1 /* gltext, Copyright (c) 2001-2005 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_PROGRAM "(default)"
26 #define DEF_SPIN "XYZ"
27 #define DEF_WANDER "True"
28 #define DEF_FRONT "False"
30 #define DEFAULTS "*delay: 20000 \n" \
31 "*showFPS: False \n" \
32 "*wireframe: False \n" \
34 #define SMOOTH_TUBE /* whether to have smooth or faceted tubes */
37 # define TUBE_FACES 12 /* how densely to render tubes */
44 #define countof(x) (sizeof((x))/sizeof((*x)))
46 #include "xlockmore.h"
50 #include "gltrackball.h"
57 #endif /* HAVE_LOCALE_H */
59 #ifdef USE_GL /* whole file */
62 # include <sys/utsname.h>
63 #endif /* HAVE_UNAME */
67 #include "glutstroke.h"
68 #include "glut_roman.h"
69 #define GLUT_FONT (&glutStrokeRoman)
73 GLXContext *glx_context;
75 trackball_state *trackball;
77 Bool spinx, spiny, spinz;
90 static text_configuration *tps = NULL;
92 static char *text_fmt;
93 static char *program_str;
95 static Bool do_wander;
96 static Bool face_front_p;
98 static XrmOptionDescRec opts[] = {
99 { "-text", ".text", XrmoptionSepArg, 0 },
100 { "-program", ".program", XrmoptionSepArg, 0 },
101 { "-spin", ".spin", XrmoptionSepArg, 0 },
102 { "+spin", ".spin", XrmoptionNoArg, "" },
103 { "-wander", ".wander", XrmoptionNoArg, "True" },
104 { "+wander", ".wander", XrmoptionNoArg, "False" },
105 { "-front", ".faceFront", XrmoptionNoArg, "True" },
106 { "+front", ".faceFront", XrmoptionNoArg, "False" }
109 static argtype vars[] = {
110 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
111 {&program_str, "program", "Program", DEF_PROGRAM, t_String},
112 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
113 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
114 {&face_front_p, "faceFront", "FaceFront", DEF_FRONT, t_Bool},
117 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
120 /* Window management, etc
123 reshape_text (ModeInfo *mi, int width, int height)
125 GLfloat h = (GLfloat) height / (GLfloat) width;
127 glViewport (0, 0, (GLint) width, (GLint) height);
129 glMatrixMode(GL_PROJECTION);
131 gluPerspective (30.0, 1/h, 1.0, 100.0);
133 glMatrixMode(GL_MODELVIEW);
135 gluLookAt( 0.0, 0.0, 30.0,
139 glClear(GL_COLOR_BUFFER_BIT);
144 gl_init (ModeInfo *mi)
146 text_configuration *tp = &tps[MI_SCREEN(mi)];
147 int wire = MI_IS_WIREFRAME(mi);
149 static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
153 glLightfv(GL_LIGHT0, GL_POSITION, pos);
154 glEnable(GL_CULL_FACE);
155 glEnable(GL_LIGHTING);
157 glEnable(GL_DEPTH_TEST);
160 tp->text_list = glGenLists (1);
161 glNewList (tp->text_list, GL_COMPILE);
166 /* The GLUT font only has ASCII characters in them, so do what we can to
167 convert Latin1 characters to the nearest ASCII equivalent...
170 latin1_to_ascii (char *s)
172 unsigned char *us = (unsigned char *) s;
173 const unsigned char ascii[95] = {
174 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
175 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
176 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
177 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
178 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
179 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
180 'u', 'u', 'y', 'p', 'y' };
184 *us = ascii[*us - 161];
193 parse_text (ModeInfo *mi)
195 text_configuration *tp = &tps[MI_SCREEN(mi)];
197 if (tp->text) free (tp->text);
199 if (program_str && *program_str && !!strcmp(program_str, "(default)"))
204 sprintf (buf, "( %.900s ) 2>&1", program_str);
205 p = popen (buf, "r");
207 sprintf (buf, "error running '%.900s'", program_str);
211 char *end = out + sizeof(buf) - 1;
214 n = fread (out, 1, end - out, p);
222 /* Truncate it to 10 lines */
225 for (i = 0; i < 10; i++)
226 if (s && (s = strchr (s, '\n')))
231 tp->text = strdup (buf);
234 else if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
239 if (uname (&uts) < 0)
241 tp->text = strdup("uname() failed");
246 if ((s = strchr(uts.nodename, '.')))
248 tp->text = (char *) malloc(strlen(uts.nodename) +
249 strlen(uts.sysname) +
250 strlen(uts.version) +
251 strlen(uts.release) + 10);
253 sprintf(tp->text, "%s\n%s %s.%s",
254 uts.nodename, uts.sysname, uts.version, uts.release);
255 # elif defined(__APPLE__) /* MacOS X + XDarwin */
256 sprintf(tp->text, "%s\n%s %s\n%s",
257 uts.nodename, uts.sysname, uts.release, uts.machine);
259 sprintf(tp->text, "%s\n%s %s",
260 uts.nodename, uts.sysname, uts.release);
261 # endif /* special system types */
263 # else /* !HAVE_UNAME */
265 tp->text = strdup(getenv("SYS$NODE"));
267 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
269 # endif /* !HAVE_UNAME */
271 else if (!strchr (text_fmt, '%'))
273 tp->text = strdup (text_fmt);
277 time_t now = time ((time_t *) 0);
278 struct tm *tm = localtime (&now);
279 int L = strlen(text_fmt) + 100;
280 tp->text = (char *) malloc (L);
282 strftime (tp->text, L-1, text_fmt, tm);
284 sprintf (tp->text, "strftime error:\n%s", text_fmt);
288 latin1_to_ascii (tp->text);
293 text_handle_event (ModeInfo *mi, XEvent *event)
295 text_configuration *tp = &tps[MI_SCREEN(mi)];
297 if (event->xany.type == ButtonPress &&
298 event->xbutton.button == Button1)
300 tp->button_down_p = True;
301 gltrackball_start (tp->trackball,
302 event->xbutton.x, event->xbutton.y,
303 MI_WIDTH (mi), MI_HEIGHT (mi));
306 else if (event->xany.type == ButtonRelease &&
307 event->xbutton.button == Button1)
309 tp->button_down_p = False;
312 else if (event->xany.type == ButtonPress &&
313 (event->xbutton.button == Button4 ||
314 event->xbutton.button == Button5))
316 gltrackball_mousewheel (tp->trackball, event->xbutton.button, 10,
317 !!event->xbutton.state);
320 else if (event->xany.type == MotionNotify &&
323 gltrackball_track (tp->trackball,
324 event->xmotion.x, event->xmotion.y,
325 MI_WIDTH (mi), MI_HEIGHT (mi));
334 init_text (ModeInfo *mi)
336 text_configuration *tp;
339 # ifdef HAVE_SETLOCALE
340 setlocale (LC_TIME, ""); /* for strftime() calls */
344 tps = (text_configuration *)
345 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
347 fprintf(stderr, "%s: out of memory\n", progname);
351 tp = &tps[MI_SCREEN(mi)];
354 tp = &tps[MI_SCREEN(mi)];
356 if ((tp->glx_context = init_GL(mi)) != NULL) {
358 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
362 double spin_speed = 0.5;
363 double wander_speed = 0.02;
364 double tilt_speed = 0.03;
365 double spin_accel = 0.5;
370 if (*s == 'x' || *s == 'X') tp->spinx = True;
371 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
372 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
376 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
383 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
384 tp->spiny ? spin_speed : 0,
385 tp->spinz ? spin_speed : 0,
387 do_wander ? wander_speed : 0,
389 tp->rot2 = (face_front_p
390 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
392 tp->trackball = gltrackball_init ();
396 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
397 make_smooth_colormap (0, 0, 0,
398 tp->colors, &tp->ncolors,
401 /* brighter colors, please... */
402 for (i = 0; i < tp->ncolors; i++)
404 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
405 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
406 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
415 fill_character (GLUTstrokeFont font, int c, Bool wire)
417 GLfloat tube_width = 10;
419 const StrokeCharRec *ch;
420 const StrokeRec *stroke;
421 const CoordRec *coord;
422 StrokeFontPtr fontinfo;
425 fontinfo = (StrokeFontPtr) font;
427 if (c < 0 || c >= fontinfo->num_chars)
429 ch = &(fontinfo->ch[c]);
433 for (i = ch->num_strokes, stroke = ch->stroke;
434 i > 0; i--, stroke++) {
435 for (j = stroke->num_coords, coord = stroke->coord;
443 if (j != stroke->num_coords)
445 coord->x, coord->y, 0,
448 TUBE_FACES, smooth, True, wire);
453 return (int) (ch->right + tube_width);
460 text_extents (const char *string, int *wP, int *hP)
462 const char *s, *start;
463 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
470 if (*s == '\n' || *s == 0)
475 w += glutStrokeWidth(GLUT_FONT, *start);
480 if (w > *wP) *wP = w;
494 fill_string (const char *string, Bool wire)
496 const char *s, *start;
497 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
499 GLfloat x = 0, y = 0;
503 lines = text_extents (string, &ow, &oh);
505 y = oh / 2 - line_height;
510 if (*s == '\n' || *s == 0)
514 const char *lstart = start;
515 const char *lend = s;
517 /* strip off whitespace at beginning and end of line
518 (since we're centering.) */
519 while (lend > lstart && isspace(lend[-1]))
521 while (lstart < lend && isspace(*lstart))
524 for (s2 = lstart; s2 < lend; s2++)
525 line_w += glutStrokeWidth (GLUT_FONT, *s2);
527 x = (-ow/2) + ((ow-line_w)/2);
528 for (s2 = lstart; s2 < lend; s2++)
531 glTranslatef(x, y, 0);
532 off = fill_character (GLUT_FONT, *s2, wire);
549 draw_text (ModeInfo *mi)
551 text_configuration *tp = &tps[MI_SCREEN(mi)];
552 Display *dpy = MI_DISPLAY(mi);
553 Window window = MI_WINDOW(mi);
554 int wire = MI_IS_WIREFRAME(mi);
556 static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
557 static GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
559 if (!tp->glx_context)
564 static time_t last_update = 0;
565 if (time ((time_t *) 0) >= last_update + tp->reload)
568 last_update = time ((time_t *) 0);
572 glShadeModel(GL_SMOOTH);
574 glEnable(GL_DEPTH_TEST);
575 glEnable(GL_NORMALIZE);
576 glEnable(GL_CULL_FACE);
578 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
582 glScalef(1.1, 1.1, 1.1);
586 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
587 glTranslatef((x - 0.5) * 8,
591 gltrackball_rotate (tp->trackball);
596 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
597 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
598 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
599 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
603 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
604 glRotatef (x * 360, 1, 0, 0);
605 glRotatef (y * 360, 0, 1, 0);
606 glRotatef (z * 360, 0, 0, 1);
613 color[0] = tp->colors[tp->ccolor].red / 65536.0;
614 color[1] = tp->colors[tp->ccolor].green / 65536.0;
615 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
617 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
619 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
621 glScalef(0.01, 0.01, 0.01);
623 fill_string(tp->text, wire);
627 if (mi->fps_p) do_fps (mi);
630 glXSwapBuffers(dpy, window);