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"
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))
306 gltrackball_mousewheel (tp->trackball, event->xbutton.button, 10,
307 !!event->xbutton.state);
310 else if (event->xany.type == MotionNotify &&
313 gltrackball_track (tp->trackball,
314 event->xmotion.x, event->xmotion.y,
315 MI_WIDTH (mi), MI_HEIGHT (mi));
324 init_text (ModeInfo *mi)
326 text_configuration *tp;
330 tps = (text_configuration *)
331 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
333 fprintf(stderr, "%s: out of memory\n", progname);
337 tp = &tps[MI_SCREEN(mi)];
340 tp = &tps[MI_SCREEN(mi)];
342 if ((tp->glx_context = init_GL(mi)) != NULL) {
344 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
348 double spin_speed = 0.5;
349 double wander_speed = 0.02;
350 double tilt_speed = 0.03;
351 double spin_accel = 0.5;
356 if (*s == 'x' || *s == 'X') tp->spinx = True;
357 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
358 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
359 else if (*s == '0') ;
363 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
370 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
371 tp->spiny ? spin_speed : 0,
372 tp->spinz ? spin_speed : 0,
374 do_wander ? wander_speed : 0,
376 tp->rot2 = (face_front_p
377 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
379 tp->trackball = gltrackball_init ();
383 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
384 make_smooth_colormap (0, 0, 0,
385 tp->colors, &tp->ncolors,
388 /* brighter colors, please... */
389 for (i = 0; i < tp->ncolors; i++)
391 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
392 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
393 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
402 fill_character (GLUTstrokeFont font, int c, Bool wire)
404 GLfloat tube_width = 10;
406 const StrokeCharRec *ch;
407 const StrokeRec *stroke;
408 const CoordRec *coord;
409 StrokeFontPtr fontinfo;
412 fontinfo = (StrokeFontPtr) font;
414 if (c < 0 || c >= fontinfo->num_chars)
416 ch = &(fontinfo->ch[c]);
420 for (i = ch->num_strokes, stroke = ch->stroke;
421 i > 0; i--, stroke++) {
422 for (j = stroke->num_coords, coord = stroke->coord;
430 if (j != stroke->num_coords)
432 coord->x, coord->y, 0,
435 TUBE_FACES, smooth, True, wire);
440 return (int) (ch->right + tube_width);
447 text_extents (const char *string, int *wP, int *hP)
449 const char *s, *start;
450 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
457 if (*s == '\n' || *s == 0)
462 w += glutStrokeWidth(GLUT_FONT, *start);
467 if (w > *wP) *wP = w;
481 fill_string (const char *string, Bool wire)
483 const char *s, *start;
484 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
486 GLfloat x = 0, y = 0;
490 lines = text_extents (string, &ow, &oh);
492 y = oh / 2 - line_height;
497 if (*s == '\n' || *s == 0)
501 const char *lstart = start;
502 const char *lend = s;
504 /* strip off whitespace at beginning and end of line
505 (since we're centering.) */
506 while (lend > lstart && isspace(lend[-1]))
508 while (lstart < lend && isspace(*lstart))
511 for (s2 = lstart; s2 < lend; s2++)
512 line_w += glutStrokeWidth (GLUT_FONT, *s2);
514 x = (-ow/2) + ((ow-line_w)/2);
515 for (s2 = lstart; s2 < lend; s2++)
518 glTranslatef(x, y, 0);
519 off = fill_character (GLUT_FONT, *s2, wire);
536 draw_text (ModeInfo *mi)
538 text_configuration *tp = &tps[MI_SCREEN(mi)];
539 Display *dpy = MI_DISPLAY(mi);
540 Window window = MI_WINDOW(mi);
541 int wire = MI_IS_WIREFRAME(mi);
543 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
544 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
546 if (!tp->glx_context)
549 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
553 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
556 tp->last_update = time ((time_t *) 0);
560 glShadeModel(GL_SMOOTH);
562 glEnable(GL_DEPTH_TEST);
563 glEnable(GL_NORMALIZE);
564 glEnable(GL_CULL_FACE);
567 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
571 glScalef(1.1, 1.1, 1.1);
575 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
576 glTranslatef((x - 0.5) * 8,
580 gltrackball_rotate (tp->trackball);
585 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
586 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
587 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
588 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
592 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
593 glRotatef (x * 360, 1, 0, 0);
594 glRotatef (y * 360, 0, 1, 0);
595 glRotatef (z * 360, 0, 0, 1);
602 color[0] = tp->colors[tp->ccolor].red / 65536.0;
603 color[1] = tp->colors[tp->ccolor].green / 65536.0;
604 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
606 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
608 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
610 glScalef(0.01, 0.01, 0.01);
612 fill_string(tp->text, wire);
616 if (mi->fps_p) do_fps (mi);
619 glXSwapBuffers(dpy, window);
622 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)