1 /* gltext, Copyright (c) 2001-2008 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 #define DEFAULTS "*delay: 20000 \n" \
13 "*showFPS: False \n" \
14 "*wireframe: False \n" \
16 # define refresh_text 0
17 # define release_text 0
18 #define SMOOTH_TUBE /* whether to have smooth or faceted tubes */
21 # define TUBE_FACES 12 /* how densely to render tubes */
28 #define countof(x) (sizeof((x))/sizeof((*x)))
30 #include "xlockmore.h"
34 #include "gltrackball.h"
38 #ifdef USE_GL /* whole file */
40 #define DEF_TEXT "(default)"
41 #define DEF_PROGRAM "(default)"
42 #define DEF_SPIN "XYZ"
43 #define DEF_WANDER "True"
44 #define DEF_FACE_FRONT "True"
47 # include <sys/utsname.h>
48 #endif /* HAVE_UNAME */
50 #include "glutstroke.h"
51 #include "glut_roman.h"
52 #define GLUT_FONT (&glutStrokeRoman)
56 GLXContext *glx_context;
58 trackball_state *trackball;
60 Bool spinx, spiny, spinz;
75 static text_configuration *tps = NULL;
77 static char *text_fmt;
78 static char *program_str;
80 static Bool do_wander;
81 static Bool face_front_p;
83 static XrmOptionDescRec opts[] = {
84 { "-text", ".text", XrmoptionSepArg, 0 },
85 { "-program", ".program", XrmoptionSepArg, 0 },
86 { "-spin", ".spin", XrmoptionSepArg, 0 },
87 { "+spin", ".spin", XrmoptionNoArg, "" },
88 { "-wander", ".wander", XrmoptionNoArg, "True" },
89 { "+wander", ".wander", XrmoptionNoArg, "False" },
90 { "-front", ".faceFront", XrmoptionNoArg, "True" },
91 { "+front", ".faceFront", XrmoptionNoArg, "False" }
94 static argtype vars[] = {
95 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
96 {&program_str, "program", "Program", DEF_PROGRAM, t_String},
97 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
98 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
99 {&face_front_p, "faceFront", "FaceFront", DEF_FACE_FRONT, t_Bool},
102 ENTRYPOINT ModeSpecOpt text_opts = {countof(opts), opts, countof(vars), vars, NULL};
105 /* Window management, etc
108 reshape_text (ModeInfo *mi, int width, int height)
110 GLfloat h = (GLfloat) height / (GLfloat) width;
112 glViewport (0, 0, (GLint) width, (GLint) height);
114 glMatrixMode(GL_PROJECTION);
116 gluPerspective (30.0, 1/h, 1.0, 100.0);
118 glMatrixMode(GL_MODELVIEW);
120 gluLookAt( 0.0, 0.0, 30.0,
124 glClear(GL_COLOR_BUFFER_BIT);
129 gl_init (ModeInfo *mi)
131 text_configuration *tp = &tps[MI_SCREEN(mi)];
132 int wire = MI_IS_WIREFRAME(mi);
134 static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
138 glLightfv(GL_LIGHT0, GL_POSITION, pos);
139 glEnable(GL_CULL_FACE);
140 glEnable(GL_LIGHTING);
142 glEnable(GL_DEPTH_TEST);
145 tp->text_list = glGenLists (1);
146 glNewList (tp->text_list, GL_COMPILE);
151 /* The GLUT font only has ASCII characters in them, so do what we can to
152 convert Latin1 characters to the nearest ASCII equivalent...
155 latin1_to_ascii (char *s)
157 unsigned char *us = (unsigned char *) s;
158 const unsigned char ascii[95] = {
159 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
160 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
161 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
162 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
163 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
164 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
165 'u', 'u', 'y', 'p', 'y' };
169 *us = ascii[*us - 161];
178 parse_text (ModeInfo *mi)
180 text_configuration *tp = &tps[MI_SCREEN(mi)];
182 if (tp->text) free (tp->text);
184 if (program_str && *program_str && !!strcmp(program_str, "(default)"))
189 sprintf (buf, "( %.900s ) 2>&1", program_str);
190 p = popen (buf, "r");
192 sprintf (buf, "error running '%.900s'", program_str);
196 char *end = out + sizeof(buf) - 1;
199 n = fread (out, 1, end - out, p);
207 /* Truncate it to 10 lines */
210 for (i = 0; i < 10; i++)
211 if (s && (s = strchr (s, '\n')))
216 tp->text = strdup (buf);
219 else if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
224 if (uname (&uts) < 0)
226 tp->text = strdup("uname() failed");
231 if ((s = strchr(uts.nodename, '.')))
233 tp->text = (char *) malloc(strlen(uts.nodename) +
234 strlen(uts.sysname) +
235 strlen(uts.version) +
236 strlen(uts.release) + 10);
238 sprintf(tp->text, "%s\n%s %s.%s",
239 uts.nodename, uts.sysname, uts.version, uts.release);
240 # elif defined(__APPLE__) /* MacOS X + XDarwin */
243 "/System/Library/CoreServices/SystemVersion.plist";
244 FILE *f = fopen (file, "r");
245 char *pbv = 0, *pn = 0, *puvv = 0;
249 while (fgets (buf, sizeof(buf)-1, f)) {
251 if (strstr(buf, S)) { \
252 fgets (buf, sizeof(buf)-1, f); \
253 if ((s = strchr (buf, '>'))) V = strdup(s+1); \
254 if ((s = strchr (V, '<'))) *s = 0; \
256 GRAB ("ProductName", pn)
257 GRAB ("ProductBuildVersion", pbv)
258 GRAB ("ProductUserVisibleVersion", puvv)
263 sprintf (tp->text, "%s\n%s\n%s\n%s",
264 uts.nodename, pn, puvv, uts.machine);
266 sprintf(tp->text, "%s\n%s %s\n%s",
267 uts.nodename, uts.sysname, uts.release, uts.machine);
270 sprintf(tp->text, "%s\n%s %s",
271 uts.nodename, uts.sysname, uts.release);
272 # endif /* special system types */
274 # else /* !HAVE_UNAME */
276 tp->text = strdup(getenv("SYS$NODE"));
278 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
280 # endif /* !HAVE_UNAME */
282 else if (!strchr (text_fmt, '%'))
284 tp->text = strdup (text_fmt);
288 time_t now = time ((time_t *) 0);
289 struct tm *tm = localtime (&now);
290 int L = strlen(text_fmt) + 100;
291 tp->text = (char *) malloc (L);
293 strftime (tp->text, L-1, text_fmt, tm);
295 sprintf (tp->text, "strftime error:\n%s", text_fmt);
299 latin1_to_ascii (tp->text);
304 text_handle_event (ModeInfo *mi, XEvent *event)
306 text_configuration *tp = &tps[MI_SCREEN(mi)];
308 if (event->xany.type == ButtonPress &&
309 event->xbutton.button == Button1)
311 tp->button_down_p = True;
312 gltrackball_start (tp->trackball,
313 event->xbutton.x, event->xbutton.y,
314 MI_WIDTH (mi), MI_HEIGHT (mi));
317 else if (event->xany.type == ButtonRelease &&
318 event->xbutton.button == Button1)
320 tp->button_down_p = False;
323 else if (event->xany.type == ButtonPress &&
324 (event->xbutton.button == Button4 ||
325 event->xbutton.button == Button5 ||
326 event->xbutton.button == Button6 ||
327 event->xbutton.button == Button7))
329 gltrackball_mousewheel (tp->trackball, event->xbutton.button, 10,
330 !!event->xbutton.state);
333 else if (event->xany.type == MotionNotify &&
336 gltrackball_track (tp->trackball,
337 event->xmotion.x, event->xmotion.y,
338 MI_WIDTH (mi), MI_HEIGHT (mi));
347 init_text (ModeInfo *mi)
349 text_configuration *tp;
353 tps = (text_configuration *)
354 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
356 fprintf(stderr, "%s: out of memory\n", progname);
360 tp = &tps[MI_SCREEN(mi)];
363 tp = &tps[MI_SCREEN(mi)];
365 if ((tp->glx_context = init_GL(mi)) != NULL) {
367 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
371 double spin_speed = 0.5;
372 double wander_speed = 0.02;
373 double tilt_speed = 0.03;
374 double spin_accel = 0.5;
379 if (*s == 'x' || *s == 'X') tp->spinx = True;
380 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
381 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
382 else if (*s == '0') ;
386 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
393 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
394 tp->spiny ? spin_speed : 0,
395 tp->spinz ? spin_speed : 0,
397 do_wander ? wander_speed : 0,
399 tp->rot2 = (face_front_p
400 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
402 tp->trackball = gltrackball_init ();
406 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
407 make_smooth_colormap (0, 0, 0,
408 tp->colors, &tp->ncolors,
411 /* brighter colors, please... */
412 for (i = 0; i < tp->ncolors; i++)
414 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
415 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
416 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
425 fill_character (GLUTstrokeFont font, int c, Bool wire, int *polysP)
427 GLfloat tube_width = 10;
429 const StrokeCharRec *ch;
430 const StrokeRec *stroke;
431 const CoordRec *coord;
432 StrokeFontPtr fontinfo;
435 fontinfo = (StrokeFontPtr) font;
437 if (c < 0 || c >= fontinfo->num_chars)
439 ch = &(fontinfo->ch[c]);
443 for (i = ch->num_strokes, stroke = ch->stroke;
444 i > 0; i--, stroke++) {
445 for (j = stroke->num_coords, coord = stroke->coord;
453 if (j != stroke->num_coords)
454 *polysP += tube (lx, ly, 0,
455 coord->x, coord->y, 0,
458 TUBE_FACES, smooth, True, wire);
463 return (int) (ch->right + tube_width);
470 text_extents (const char *string, int *wP, int *hP)
472 const char *s, *start;
473 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
480 if (*s == '\n' || *s == 0)
485 w += glutStrokeWidth(GLUT_FONT, *start);
490 if (w > *wP) *wP = w;
504 fill_string (const char *string, Bool wire)
507 const char *s, *start;
508 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
510 GLfloat x = 0, y = 0;
514 lines = text_extents (string, &ow, &oh);
516 y = oh / 2 - line_height;
521 if (*s == '\n' || *s == 0)
525 const char *lstart = start;
526 const char *lend = s;
528 /* strip off whitespace at beginning and end of line
529 (since we're centering.) */
530 while (lend > lstart && isspace(lend[-1]))
532 while (lstart < lend && isspace(*lstart))
535 for (s2 = lstart; s2 < lend; s2++)
536 line_w += glutStrokeWidth (GLUT_FONT, *s2);
538 x = (-ow/2) + ((ow-line_w)/2);
539 for (s2 = lstart; s2 < lend; s2++)
542 glTranslatef(x, y, 0);
543 off = fill_character (GLUT_FONT, *s2, wire, &polys);
561 draw_text (ModeInfo *mi)
563 text_configuration *tp = &tps[MI_SCREEN(mi)];
564 Display *dpy = MI_DISPLAY(mi);
565 Window window = MI_WINDOW(mi);
566 int wire = MI_IS_WIREFRAME(mi);
568 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
569 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
571 if (!tp->glx_context)
574 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
578 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
581 tp->last_update = time ((time_t *) 0);
585 glShadeModel(GL_SMOOTH);
587 glEnable(GL_DEPTH_TEST);
588 glEnable(GL_NORMALIZE);
589 glEnable(GL_CULL_FACE);
592 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
596 glScalef(1.1, 1.1, 1.1);
600 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
601 glTranslatef((x - 0.5) * 8,
605 gltrackball_rotate (tp->trackball);
610 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
611 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
612 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
613 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
617 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
618 glRotatef (x * 360, 1, 0, 0);
619 glRotatef (y * 360, 0, 1, 0);
620 glRotatef (z * 360, 0, 0, 1);
627 color[0] = tp->colors[tp->ccolor].red / 65536.0;
628 color[1] = tp->colors[tp->ccolor].green / 65536.0;
629 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
631 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
633 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
635 glScalef(0.01, 0.01, 0.01);
637 mi->polygon_count = fill_string(tp->text, wire);
641 if (mi->fps_p) do_fps (mi);
644 glXSwapBuffers(dpy, window);
647 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)