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 */
41 # define DEF_TEXT "%A%n%d %b %Y%n%r"
43 # define DEF_TEXT "(default)"
46 #define DEF_PROGRAM "(default)"
47 #define DEF_SPIN "XYZ"
48 #define DEF_WANDER "True"
49 #define DEF_FRONT "True"
52 # include <sys/utsname.h>
53 #endif /* HAVE_UNAME */
55 #include "glutstroke.h"
56 #include "glut_roman.h"
57 #define GLUT_FONT (&glutStrokeRoman)
61 GLXContext *glx_context;
63 trackball_state *trackball;
65 Bool spinx, spiny, spinz;
80 static text_configuration *tps = NULL;
82 static char *text_fmt;
83 static char *program_str;
85 static Bool do_wander;
86 static Bool face_front_p;
88 static XrmOptionDescRec opts[] = {
89 { "-text", ".text", XrmoptionSepArg, 0 },
90 { "-program", ".program", XrmoptionSepArg, 0 },
91 { "-spin", ".spin", XrmoptionSepArg, 0 },
92 { "+spin", ".spin", XrmoptionNoArg, "" },
93 { "-wander", ".wander", XrmoptionNoArg, "True" },
94 { "+wander", ".wander", XrmoptionNoArg, "False" },
95 { "-front", ".faceFront", XrmoptionNoArg, "True" },
96 { "+front", ".faceFront", XrmoptionNoArg, "False" }
99 static argtype vars[] = {
100 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
101 {&program_str, "program", "Program", DEF_PROGRAM, t_String},
102 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
103 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
104 {&face_front_p, "faceFront", "FaceFront", DEF_FRONT, t_Bool},
107 ENTRYPOINT ModeSpecOpt text_opts = {countof(opts), opts, countof(vars), vars, NULL};
110 /* Window management, etc
113 reshape_text (ModeInfo *mi, int width, int height)
115 GLfloat h = (GLfloat) height / (GLfloat) width;
117 glViewport (0, 0, (GLint) width, (GLint) height);
119 glMatrixMode(GL_PROJECTION);
121 gluPerspective (30.0, 1/h, 1.0, 100.0);
123 glMatrixMode(GL_MODELVIEW);
125 gluLookAt( 0.0, 0.0, 30.0,
129 glClear(GL_COLOR_BUFFER_BIT);
134 gl_init (ModeInfo *mi)
136 text_configuration *tp = &tps[MI_SCREEN(mi)];
137 int wire = MI_IS_WIREFRAME(mi);
139 static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
143 glLightfv(GL_LIGHT0, GL_POSITION, pos);
144 glEnable(GL_CULL_FACE);
145 glEnable(GL_LIGHTING);
147 glEnable(GL_DEPTH_TEST);
150 tp->text_list = glGenLists (1);
151 glNewList (tp->text_list, GL_COMPILE);
156 /* The GLUT font only has ASCII characters in them, so do what we can to
157 convert Latin1 characters to the nearest ASCII equivalent...
160 latin1_to_ascii (char *s)
162 unsigned char *us = (unsigned char *) s;
163 const unsigned char ascii[95] = {
164 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
165 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
166 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
167 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
168 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
169 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
170 'u', 'u', 'y', 'p', 'y' };
174 *us = ascii[*us - 161];
183 parse_text (ModeInfo *mi)
185 text_configuration *tp = &tps[MI_SCREEN(mi)];
187 if (tp->text) free (tp->text);
189 if (program_str && *program_str && !!strcmp(program_str, "(default)"))
194 sprintf (buf, "( %.900s ) 2>&1", program_str);
195 p = popen (buf, "r");
197 sprintf (buf, "error running '%.900s'", program_str);
201 char *end = out + sizeof(buf) - 1;
204 n = fread (out, 1, end - out, p);
212 /* Truncate it to 10 lines */
215 for (i = 0; i < 10; i++)
216 if (s && (s = strchr (s, '\n')))
221 tp->text = strdup (buf);
224 else if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
229 if (uname (&uts) < 0)
231 tp->text = strdup("uname() failed");
236 if ((s = strchr(uts.nodename, '.')))
238 tp->text = (char *) malloc(strlen(uts.nodename) +
239 strlen(uts.sysname) +
240 strlen(uts.version) +
241 strlen(uts.release) + 10);
243 sprintf(tp->text, "%s\n%s %s.%s",
244 uts.nodename, uts.sysname, uts.version, uts.release);
245 # elif defined(__APPLE__) /* MacOS X + XDarwin */
246 sprintf(tp->text, "%s\n%s %s\n%s",
247 uts.nodename, uts.sysname, uts.release, uts.machine);
249 sprintf(tp->text, "%s\n%s %s",
250 uts.nodename, uts.sysname, uts.release);
251 # endif /* special system types */
253 # else /* !HAVE_UNAME */
255 tp->text = strdup(getenv("SYS$NODE"));
257 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
259 # endif /* !HAVE_UNAME */
261 else if (!strchr (text_fmt, '%'))
263 tp->text = strdup (text_fmt);
267 time_t now = time ((time_t *) 0);
268 struct tm *tm = localtime (&now);
269 int L = strlen(text_fmt) + 100;
270 tp->text = (char *) malloc (L);
272 strftime (tp->text, L-1, text_fmt, tm);
274 sprintf (tp->text, "strftime error:\n%s", text_fmt);
278 latin1_to_ascii (tp->text);
283 text_handle_event (ModeInfo *mi, XEvent *event)
285 text_configuration *tp = &tps[MI_SCREEN(mi)];
287 if (event->xany.type == ButtonPress &&
288 event->xbutton.button == Button1)
290 tp->button_down_p = True;
291 gltrackball_start (tp->trackball,
292 event->xbutton.x, event->xbutton.y,
293 MI_WIDTH (mi), MI_HEIGHT (mi));
296 else if (event->xany.type == ButtonRelease &&
297 event->xbutton.button == Button1)
299 tp->button_down_p = False;
302 else if (event->xany.type == ButtonPress &&
303 (event->xbutton.button == Button4 ||
304 event->xbutton.button == Button5 ||
305 event->xbutton.button == Button6 ||
306 event->xbutton.button == Button7))
308 gltrackball_mousewheel (tp->trackball, event->xbutton.button, 10,
309 !!event->xbutton.state);
312 else if (event->xany.type == MotionNotify &&
315 gltrackball_track (tp->trackball,
316 event->xmotion.x, event->xmotion.y,
317 MI_WIDTH (mi), MI_HEIGHT (mi));
326 init_text (ModeInfo *mi)
328 text_configuration *tp;
332 tps = (text_configuration *)
333 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
335 fprintf(stderr, "%s: out of memory\n", progname);
339 tp = &tps[MI_SCREEN(mi)];
342 tp = &tps[MI_SCREEN(mi)];
344 if ((tp->glx_context = init_GL(mi)) != NULL) {
346 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
350 double spin_speed = 0.5;
351 double wander_speed = 0.02;
352 double tilt_speed = 0.03;
353 double spin_accel = 0.5;
358 if (*s == 'x' || *s == 'X') tp->spinx = True;
359 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
360 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
361 else if (*s == '0') ;
365 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
372 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
373 tp->spiny ? spin_speed : 0,
374 tp->spinz ? spin_speed : 0,
376 do_wander ? wander_speed : 0,
378 tp->rot2 = (face_front_p
379 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
381 tp->trackball = gltrackball_init ();
385 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
386 make_smooth_colormap (0, 0, 0,
387 tp->colors, &tp->ncolors,
390 /* brighter colors, please... */
391 for (i = 0; i < tp->ncolors; i++)
393 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
394 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
395 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
404 fill_character (GLUTstrokeFont font, int c, Bool wire)
406 GLfloat tube_width = 10;
408 const StrokeCharRec *ch;
409 const StrokeRec *stroke;
410 const CoordRec *coord;
411 StrokeFontPtr fontinfo;
414 fontinfo = (StrokeFontPtr) font;
416 if (c < 0 || c >= fontinfo->num_chars)
418 ch = &(fontinfo->ch[c]);
422 for (i = ch->num_strokes, stroke = ch->stroke;
423 i > 0; i--, stroke++) {
424 for (j = stroke->num_coords, coord = stroke->coord;
432 if (j != stroke->num_coords)
434 coord->x, coord->y, 0,
437 TUBE_FACES, smooth, True, wire);
442 return (int) (ch->right + tube_width);
449 text_extents (const char *string, int *wP, int *hP)
451 const char *s, *start;
452 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
459 if (*s == '\n' || *s == 0)
464 w += glutStrokeWidth(GLUT_FONT, *start);
469 if (w > *wP) *wP = w;
483 fill_string (const char *string, Bool wire)
485 const char *s, *start;
486 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
488 GLfloat x = 0, y = 0;
492 lines = text_extents (string, &ow, &oh);
494 y = oh / 2 - line_height;
499 if (*s == '\n' || *s == 0)
503 const char *lstart = start;
504 const char *lend = s;
506 /* strip off whitespace at beginning and end of line
507 (since we're centering.) */
508 while (lend > lstart && isspace(lend[-1]))
510 while (lstart < lend && isspace(*lstart))
513 for (s2 = lstart; s2 < lend; s2++)
514 line_w += glutStrokeWidth (GLUT_FONT, *s2);
516 x = (-ow/2) + ((ow-line_w)/2);
517 for (s2 = lstart; s2 < lend; s2++)
520 glTranslatef(x, y, 0);
521 off = fill_character (GLUT_FONT, *s2, wire);
538 draw_text (ModeInfo *mi)
540 text_configuration *tp = &tps[MI_SCREEN(mi)];
541 Display *dpy = MI_DISPLAY(mi);
542 Window window = MI_WINDOW(mi);
543 int wire = MI_IS_WIREFRAME(mi);
545 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
546 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
548 if (!tp->glx_context)
551 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
555 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
558 tp->last_update = time ((time_t *) 0);
562 glShadeModel(GL_SMOOTH);
564 glEnable(GL_DEPTH_TEST);
565 glEnable(GL_NORMALIZE);
566 glEnable(GL_CULL_FACE);
569 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
573 glScalef(1.1, 1.1, 1.1);
577 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
578 glTranslatef((x - 0.5) * 8,
582 gltrackball_rotate (tp->trackball);
587 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
588 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
589 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
590 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
594 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
595 glRotatef (x * 360, 1, 0, 0);
596 glRotatef (y * 360, 0, 1, 0);
597 glRotatef (z * 360, 0, 0, 1);
604 color[0] = tp->colors[tp->ccolor].red / 65536.0;
605 color[1] = tp->colors[tp->ccolor].green / 65536.0;
606 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
608 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
610 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
612 glScalef(0.01, 0.01, 0.01);
614 fill_string(tp->text, wire);
618 if (mi->fps_p) do_fps (mi);
621 glXSwapBuffers(dpy, window);
624 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)