1 /* gltext, Copyright (c) 2001-2006 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"
40 #endif /* HAVE_LOCALE_H */
42 #ifdef USE_GL /* whole file */
45 # define DEF_TEXT "%A%n%d %b %Y%n%r"
47 # define DEF_TEXT "(default)"
50 #define DEF_PROGRAM "(default)"
51 #define DEF_SPIN "XYZ"
52 #define DEF_WANDER "True"
53 #define DEF_FRONT "True"
56 # include <sys/utsname.h>
57 #endif /* HAVE_UNAME */
59 #include "glutstroke.h"
60 #include "glut_roman.h"
61 #define GLUT_FONT (&glutStrokeRoman)
65 GLXContext *glx_context;
67 trackball_state *trackball;
69 Bool spinx, spiny, spinz;
84 static text_configuration *tps = NULL;
86 static char *text_fmt;
87 static char *program_str;
89 static Bool do_wander;
90 static Bool face_front_p;
92 static XrmOptionDescRec opts[] = {
93 { "-text", ".text", XrmoptionSepArg, 0 },
94 { "-program", ".program", XrmoptionSepArg, 0 },
95 { "-spin", ".spin", XrmoptionSepArg, 0 },
96 { "+spin", ".spin", XrmoptionNoArg, "" },
97 { "-wander", ".wander", XrmoptionNoArg, "True" },
98 { "+wander", ".wander", XrmoptionNoArg, "False" },
99 { "-front", ".faceFront", XrmoptionNoArg, "True" },
100 { "+front", ".faceFront", XrmoptionNoArg, "False" }
103 static argtype vars[] = {
104 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
105 {&program_str, "program", "Program", DEF_PROGRAM, t_String},
106 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
107 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
108 {&face_front_p, "faceFront", "FaceFront", DEF_FRONT, t_Bool},
111 ENTRYPOINT ModeSpecOpt text_opts = {countof(opts), opts, countof(vars), vars, NULL};
114 /* Window management, etc
117 reshape_text (ModeInfo *mi, int width, int height)
119 GLfloat h = (GLfloat) height / (GLfloat) width;
121 glViewport (0, 0, (GLint) width, (GLint) height);
123 glMatrixMode(GL_PROJECTION);
125 gluPerspective (30.0, 1/h, 1.0, 100.0);
127 glMatrixMode(GL_MODELVIEW);
129 gluLookAt( 0.0, 0.0, 30.0,
133 glClear(GL_COLOR_BUFFER_BIT);
138 gl_init (ModeInfo *mi)
140 text_configuration *tp = &tps[MI_SCREEN(mi)];
141 int wire = MI_IS_WIREFRAME(mi);
143 static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
147 glLightfv(GL_LIGHT0, GL_POSITION, pos);
148 glEnable(GL_CULL_FACE);
149 glEnable(GL_LIGHTING);
151 glEnable(GL_DEPTH_TEST);
154 tp->text_list = glGenLists (1);
155 glNewList (tp->text_list, GL_COMPILE);
160 /* The GLUT font only has ASCII characters in them, so do what we can to
161 convert Latin1 characters to the nearest ASCII equivalent...
164 latin1_to_ascii (char *s)
166 unsigned char *us = (unsigned char *) s;
167 const unsigned char ascii[95] = {
168 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
169 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
170 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
171 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
172 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
173 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
174 'u', 'u', 'y', 'p', 'y' };
178 *us = ascii[*us - 161];
187 parse_text (ModeInfo *mi)
189 text_configuration *tp = &tps[MI_SCREEN(mi)];
191 if (tp->text) free (tp->text);
193 if (program_str && *program_str && !!strcmp(program_str, "(default)"))
198 sprintf (buf, "( %.900s ) 2>&1", program_str);
199 p = popen (buf, "r");
201 sprintf (buf, "error running '%.900s'", program_str);
205 char *end = out + sizeof(buf) - 1;
208 n = fread (out, 1, end - out, p);
216 /* Truncate it to 10 lines */
219 for (i = 0; i < 10; i++)
220 if (s && (s = strchr (s, '\n')))
225 tp->text = strdup (buf);
228 else if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
233 if (uname (&uts) < 0)
235 tp->text = strdup("uname() failed");
240 if ((s = strchr(uts.nodename, '.')))
242 tp->text = (char *) malloc(strlen(uts.nodename) +
243 strlen(uts.sysname) +
244 strlen(uts.version) +
245 strlen(uts.release) + 10);
247 sprintf(tp->text, "%s\n%s %s.%s",
248 uts.nodename, uts.sysname, uts.version, uts.release);
249 # elif defined(__APPLE__) /* MacOS X + XDarwin */
250 sprintf(tp->text, "%s\n%s %s\n%s",
251 uts.nodename, uts.sysname, uts.release, uts.machine);
253 sprintf(tp->text, "%s\n%s %s",
254 uts.nodename, uts.sysname, uts.release);
255 # endif /* special system types */
257 # else /* !HAVE_UNAME */
259 tp->text = strdup(getenv("SYS$NODE"));
261 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
263 # endif /* !HAVE_UNAME */
265 else if (!strchr (text_fmt, '%'))
267 tp->text = strdup (text_fmt);
271 time_t now = time ((time_t *) 0);
272 struct tm *tm = localtime (&now);
273 int L = strlen(text_fmt) + 100;
274 tp->text = (char *) malloc (L);
276 strftime (tp->text, L-1, text_fmt, tm);
278 sprintf (tp->text, "strftime error:\n%s", text_fmt);
282 latin1_to_ascii (tp->text);
287 text_handle_event (ModeInfo *mi, XEvent *event)
289 text_configuration *tp = &tps[MI_SCREEN(mi)];
291 if (event->xany.type == ButtonPress &&
292 event->xbutton.button == Button1)
294 tp->button_down_p = True;
295 gltrackball_start (tp->trackball,
296 event->xbutton.x, event->xbutton.y,
297 MI_WIDTH (mi), MI_HEIGHT (mi));
300 else if (event->xany.type == ButtonRelease &&
301 event->xbutton.button == Button1)
303 tp->button_down_p = False;
306 else if (event->xany.type == ButtonPress &&
307 (event->xbutton.button == Button4 ||
308 event->xbutton.button == Button5))
310 gltrackball_mousewheel (tp->trackball, event->xbutton.button, 10,
311 !!event->xbutton.state);
314 else if (event->xany.type == MotionNotify &&
317 gltrackball_track (tp->trackball,
318 event->xmotion.x, event->xmotion.y,
319 MI_WIDTH (mi), MI_HEIGHT (mi));
328 init_text (ModeInfo *mi)
330 text_configuration *tp;
333 /* setlocale (LC_TIME, "") only refers to environment:
337 # ifdef HAVE_SETLOCALE
338 setlocale (LC_TIME, ""); /* for strftime() calls */
343 tps = (text_configuration *)
344 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
346 fprintf(stderr, "%s: out of memory\n", progname);
350 tp = &tps[MI_SCREEN(mi)];
353 tp = &tps[MI_SCREEN(mi)];
355 if ((tp->glx_context = init_GL(mi)) != NULL) {
357 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
361 double spin_speed = 0.5;
362 double wander_speed = 0.02;
363 double tilt_speed = 0.03;
364 double spin_accel = 0.5;
369 if (*s == 'x' || *s == 'X') tp->spinx = True;
370 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
371 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
372 else if (*s == '0') ;
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 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
557 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
559 if (!tp->glx_context)
562 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
566 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
569 tp->last_update = time ((time_t *) 0);
573 glShadeModel(GL_SMOOTH);
575 glEnable(GL_DEPTH_TEST);
576 glEnable(GL_NORMALIZE);
577 glEnable(GL_CULL_FACE);
580 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
584 glScalef(1.1, 1.1, 1.1);
588 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
589 glTranslatef((x - 0.5) * 8,
593 gltrackball_rotate (tp->trackball);
598 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
599 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
600 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
601 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
605 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
606 glRotatef (x * 360, 1, 0, 0);
607 glRotatef (y * 360, 0, 1, 0);
608 glRotatef (z * 360, 0, 0, 1);
615 color[0] = tp->colors[tp->ccolor].red / 65536.0;
616 color[1] = tp->colors[tp->ccolor].green / 65536.0;
617 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
619 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
621 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
623 glScalef(0.01, 0.01, 0.01);
625 fill_string(tp->text, wire);
629 if (mi->fps_p) do_fps (mi);
632 glXSwapBuffers(dpy, window);
635 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)