1 /* gltext, Copyright (c) 2001, 2002, 2003 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 #include <X11/Intrinsic.h>
14 extern XtAppContext app;
16 #define PROGCLASS "GLText"
17 #define HACK_INIT init_text
18 #define HACK_DRAW draw_text
19 #define HACK_RESHAPE reshape_text
20 #define HACK_HANDLE_EVENT text_handle_event
21 #define EVENT_MASK PointerMotionMask
22 #define sws_opts xlockmore_opts
24 #define DEF_TEXT "(default)"
25 #define DEF_SPIN "XYZ"
26 #define DEF_WANDER "True"
28 #define DEFAULTS "*delay: 10000 \n" \
29 "*showFPS: False \n" \
30 "*wireframe: False \n" \
31 "*spin: " DEF_SPIN "\n" \
32 "*wander: " DEF_WANDER "\n" \
33 "*text: " DEF_TEXT "\n"
36 #define SMOOTH_TUBE /* whether to have smooth or faceted tubes */
39 # define TUBE_FACES 12 /* how densely to render tubes */
46 #define countof(x) (sizeof((x))/sizeof((*x)))
48 #include "xlockmore.h"
52 #include "gltrackball.h"
57 #ifdef USE_GL /* whole file */
60 # include <sys/utsname.h>
61 #endif /* HAVE_UNAME */
65 #include "glutstroke.h"
66 #include "glut_roman.h"
67 #define GLUT_FONT (&glutStrokeRoman)
71 GLXContext *glx_context;
73 trackball_state *trackball;
86 static text_configuration *tps = NULL;
88 static char *text_fmt;
90 static Bool do_wander;
92 static XrmOptionDescRec opts[] = {
93 { "-text", ".text", XrmoptionSepArg, 0 },
94 { "-spin", ".spin", XrmoptionSepArg, 0 },
95 { "+spin", ".spin", XrmoptionNoArg, "" },
96 { "-wander", ".wander", XrmoptionNoArg, "True" },
97 { "+wander", ".wander", XrmoptionNoArg, "False" }
100 static argtype vars[] = {
101 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
102 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
103 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
106 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
109 /* Window management, etc
112 reshape_text (ModeInfo *mi, int width, int height)
114 GLfloat h = (GLfloat) height / (GLfloat) width;
116 glViewport (0, 0, (GLint) width, (GLint) height);
118 glMatrixMode(GL_PROJECTION);
120 gluPerspective (30.0, 1/h, 1.0, 100.0);
122 glMatrixMode(GL_MODELVIEW);
124 gluLookAt( 0.0, 0.0, 30.0,
128 glClear(GL_COLOR_BUFFER_BIT);
133 gl_init (ModeInfo *mi)
135 text_configuration *tp = &tps[MI_SCREEN(mi)];
136 int wire = MI_IS_WIREFRAME(mi);
138 static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
142 glLightfv(GL_LIGHT0, GL_POSITION, pos);
143 glEnable(GL_CULL_FACE);
144 glEnable(GL_LIGHTING);
146 glEnable(GL_DEPTH_TEST);
149 tp->text_list = glGenLists (1);
150 glNewList (tp->text_list, GL_COMPILE);
156 parse_text (ModeInfo *mi)
158 text_configuration *tp = &tps[MI_SCREEN(mi)];
160 if (tp->text) free (tp->text);
162 if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
167 if (uname (&uts) < 0)
169 tp->text = strdup("uname() failed");
174 if ((s = strchr(uts.nodename, '.')))
176 tp->text = (char *) malloc(strlen(uts.nodename) +
177 strlen(uts.sysname) +
178 strlen(uts.version) +
179 strlen(uts.release) + 10);
181 sprintf(tp->text, "%s\n%s %s.%s",
182 uts.nodename, uts.sysname, uts.version, uts.release);
183 # elif defined(__APPLE__) /* MacOS X + XDarwin */
184 sprintf(tp->text, "%s\n%s %s\n%s",
185 uts.nodename, uts.sysname, uts.release, uts.machine);
187 sprintf(tp->text, "%s\n%s %s",
188 uts.nodename, uts.sysname, uts.release);
189 # endif /* special system types */
191 # else /* !HAVE_UNAME */
193 tp->text = strdup(getenv("SYS$NODE"));
195 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
197 # endif /* !HAVE_UNAME */
199 else if (!strchr (text_fmt, '%'))
201 tp->text = strdup (text_fmt);
205 time_t now = time ((time_t *) 0);
206 struct tm *tm = localtime (&now);
207 int L = strlen(text_fmt) + 100;
208 tp->text = (char *) malloc (L);
210 strftime (tp->text, L-1, text_fmt, tm);
212 sprintf (tp->text, "strftime error:\n%s", text_fmt);
218 text_handle_event (ModeInfo *mi, XEvent *event)
220 text_configuration *tp = &tps[MI_SCREEN(mi)];
222 if (event->xany.type == ButtonPress &&
223 event->xbutton.button & Button1)
225 tp->button_down_p = True;
226 gltrackball_start (tp->trackball,
227 event->xbutton.x, event->xbutton.y,
228 MI_WIDTH (mi), MI_HEIGHT (mi));
231 else if (event->xany.type == ButtonRelease &&
232 event->xbutton.button & Button1)
234 tp->button_down_p = False;
237 else if (event->xany.type == MotionNotify &&
240 gltrackball_track (tp->trackball,
241 event->xmotion.x, event->xmotion.y,
242 MI_WIDTH (mi), MI_HEIGHT (mi));
251 init_text (ModeInfo *mi)
253 text_configuration *tp;
257 tps = (text_configuration *)
258 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
260 fprintf(stderr, "%s: out of memory\n", progname);
264 tp = &tps[MI_SCREEN(mi)];
267 tp = &tps[MI_SCREEN(mi)];
269 if ((tp->glx_context = init_GL(mi)) != NULL) {
271 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
275 Bool spinx=False, spiny=False, spinz=False;
276 double spin_speed = 1.0;
277 double wander_speed = 0.05;
278 double spin_accel = 1.0;
283 if (*s == 'x' || *s == 'X') spinx = True;
284 else if (*s == 'y' || *s == 'Y') spiny = True;
285 else if (*s == 'z' || *s == 'Z') spinz = True;
289 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
296 tp->rot = make_rotator (spinx ? spin_speed : 0,
297 spiny ? spin_speed : 0,
298 spinz ? spin_speed : 0,
300 do_wander ? wander_speed : 0,
302 tp->trackball = gltrackball_init ();
306 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
307 make_smooth_colormap (0, 0, 0,
308 tp->colors, &tp->ncolors,
311 /* brighter colors, please... */
312 for (i = 0; i < tp->ncolors; i++)
314 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
315 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
316 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
325 fill_character (GLUTstrokeFont font, int c, Bool wire)
327 GLfloat tube_width = 10;
329 const StrokeCharRec *ch;
330 const StrokeRec *stroke;
331 const CoordRec *coord;
332 StrokeFontPtr fontinfo;
335 fontinfo = (StrokeFontPtr) font;
337 if (c < 0 || c >= fontinfo->num_chars)
339 ch = &(fontinfo->ch[c]);
343 for (i = ch->num_strokes, stroke = ch->stroke;
344 i > 0; i--, stroke++) {
345 for (j = stroke->num_coords, coord = stroke->coord;
353 if (j != stroke->num_coords)
355 coord->x, coord->y, 0,
358 TUBE_FACES, smooth, True, wire);
363 return (int) (ch->right + tube_width/2);
370 text_extents (const char *string, int *wP, int *hP)
372 const char *s, *start;
373 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
380 if (*s == '\n' || *s == 0)
385 w += glutStrokeWidth(GLUT_FONT, *start);
390 if (w > *wP) *wP = w;
404 fill_string (const char *string, Bool wire)
406 const char *s, *start;
407 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
409 GLfloat x = 0, y = 0;
413 lines = text_extents (string, &ow, &oh);
415 y = oh / 2 - line_height;
420 if (*s == '\n' || *s == 0)
424 const char *lstart = start;
425 const char *lend = s;
427 /* strip off whitespace at beginning and end of line
428 (since we're centering.) */
429 while (lend > lstart && isspace(lend[-1]))
431 while (lstart < lend && isspace(*lstart))
434 for (s2 = lstart; s2 < lend; s2++)
435 line_w += glutStrokeWidth (GLUT_FONT, *s2);
437 x = (-ow/2) + ((ow-line_w)/2);
438 for (s2 = lstart; s2 < lend; s2++)
441 glTranslatef(x, y, 0);
442 off = fill_character (GLUT_FONT, *s2, wire);
459 draw_text (ModeInfo *mi)
461 text_configuration *tp = &tps[MI_SCREEN(mi)];
462 Display *dpy = MI_DISPLAY(mi);
463 Window window = MI_WINDOW(mi);
464 int wire = MI_IS_WIREFRAME(mi);
466 static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
467 static GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
469 if (!tp->glx_context)
472 if (strchr (text_fmt, '%'))
474 static time_t last_update = -1;
475 time_t now = time ((time_t *) 0);
476 if (now != last_update) /* do it once a second */
481 glShadeModel(GL_SMOOTH);
483 glEnable(GL_DEPTH_TEST);
484 glEnable(GL_NORMALIZE);
485 glEnable(GL_CULL_FACE);
487 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
491 glScalef(1.1, 1.1, 1.1);
495 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
496 glTranslatef((x - 0.5) * 8,
500 gltrackball_rotate (tp->trackball);
502 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
503 glRotatef (x * 360, 1.0, 0.0, 0.0);
504 glRotatef (y * 360, 0.0, 1.0, 0.0);
505 glRotatef (z * 360, 0.0, 0.0, 1.0);
511 color[0] = tp->colors[tp->ccolor].red / 65536.0;
512 color[1] = tp->colors[tp->ccolor].green / 65536.0;
513 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
515 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
517 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
519 glScalef(0.01, 0.01, 0.01);
521 fill_string(tp->text, wire);
525 if (mi->fps_p) do_fps (mi);
528 glXSwapBuffers(dpy, window);