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);
361 tp = &tps[MI_SCREEN(mi)];
363 if ((tp->glx_context = init_GL(mi)) != NULL) {
365 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
369 double spin_speed = 0.5;
370 double wander_speed = 0.02;
371 double tilt_speed = 0.03;
372 double spin_accel = 0.5;
377 if (*s == 'x' || *s == 'X') tp->spinx = True;
378 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
379 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
380 else if (*s == '0') ;
384 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
391 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
392 tp->spiny ? spin_speed : 0,
393 tp->spinz ? spin_speed : 0,
395 do_wander ? wander_speed : 0,
397 tp->rot2 = (face_front_p
398 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
400 tp->trackball = gltrackball_init ();
404 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
405 make_smooth_colormap (0, 0, 0,
406 tp->colors, &tp->ncolors,
409 /* brighter colors, please... */
410 for (i = 0; i < tp->ncolors; i++)
412 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
413 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
414 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
423 fill_character (GLUTstrokeFont font, int c, Bool wire, int *polysP)
425 GLfloat tube_width = 10;
427 const StrokeCharRec *ch;
428 const StrokeRec *stroke;
429 const CoordRec *coord;
430 StrokeFontPtr fontinfo;
433 fontinfo = (StrokeFontPtr) font;
435 if (c < 0 || c >= fontinfo->num_chars)
437 ch = &(fontinfo->ch[c]);
441 for (i = ch->num_strokes, stroke = ch->stroke;
442 i > 0; i--, stroke++) {
443 for (j = stroke->num_coords, coord = stroke->coord;
451 if (j != stroke->num_coords)
452 *polysP += tube (lx, ly, 0,
453 coord->x, coord->y, 0,
456 TUBE_FACES, smooth, True, wire);
461 return (int) (ch->right + tube_width);
468 text_extents (const char *string, int *wP, int *hP)
470 const char *s, *start;
471 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
478 if (*s == '\n' || *s == 0)
483 w += glutStrokeWidth(GLUT_FONT, *start);
488 if (w > *wP) *wP = w;
502 fill_string (const char *string, Bool wire)
505 const char *s, *start;
506 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
508 GLfloat x = 0, y = 0;
511 text_extents (string, &ow, &oh);
513 y = oh / 2 - line_height;
518 if (*s == '\n' || *s == 0)
522 const char *lstart = start;
523 const char *lend = s;
525 /* strip off whitespace at beginning and end of line
526 (since we're centering.) */
527 while (lend > lstart && isspace(lend[-1]))
529 while (lstart < lend && isspace(*lstart))
532 for (s2 = lstart; s2 < lend; s2++)
533 line_w += glutStrokeWidth (GLUT_FONT, *s2);
535 x = (-ow/2) + ((ow-line_w)/2);
536 for (s2 = lstart; s2 < lend; s2++)
539 glTranslatef(x, y, 0);
540 off = fill_character (GLUT_FONT, *s2, wire, &polys);
558 draw_text (ModeInfo *mi)
560 text_configuration *tp = &tps[MI_SCREEN(mi)];
561 Display *dpy = MI_DISPLAY(mi);
562 Window window = MI_WINDOW(mi);
563 int wire = MI_IS_WIREFRAME(mi);
565 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
566 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
568 if (!tp->glx_context)
571 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
575 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
578 tp->last_update = time ((time_t *) 0);
582 glShadeModel(GL_SMOOTH);
584 glEnable(GL_DEPTH_TEST);
585 glEnable(GL_NORMALIZE);
586 glEnable(GL_CULL_FACE);
589 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
593 glScalef(1.1, 1.1, 1.1);
597 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
598 glTranslatef((x - 0.5) * 8,
602 gltrackball_rotate (tp->trackball);
607 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
608 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
609 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
610 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
614 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
615 glRotatef (x * 360, 1, 0, 0);
616 glRotatef (y * 360, 0, 1, 0);
617 glRotatef (z * 360, 0, 0, 1);
624 color[0] = tp->colors[tp->ccolor].red / 65536.0;
625 color[1] = tp->colors[tp->ccolor].green / 65536.0;
626 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
628 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
630 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
632 glScalef(0.01, 0.01, 0.01);
634 mi->polygon_count = fill_string(tp->text, wire);
638 if (mi->fps_p) do_fps (mi);
641 glXSwapBuffers(dpy, window);
644 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)