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" \
17 # define refresh_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"
35 #include "textclient.h"
39 #ifdef USE_GL /* whole file */
41 #define DEF_TEXT "(default)"
42 #define DEF_PROGRAM "(default)"
43 #define DEF_SPIN "XYZ"
44 #define DEF_WANDER "True"
45 #define DEF_FACE_FRONT "True"
48 # include <sys/utsname.h>
49 #endif /* HAVE_UNAME */
51 #include "glutstroke.h"
52 #include "glut_roman.h"
53 #define GLUT_FONT (&glutStrokeRoman)
57 GLXContext *glx_context;
59 trackball_state *trackball;
61 Bool spinx, spiny, spinz;
77 static text_configuration *tps = NULL;
79 static char *text_fmt;
80 static char *program_str;
82 static Bool do_wander;
83 static Bool face_front_p;
85 static XrmOptionDescRec opts[] = {
86 { "-text", ".text", XrmoptionSepArg, 0 },
87 { "-program", ".program", XrmoptionSepArg, 0 },
88 { "-spin", ".spin", XrmoptionSepArg, 0 },
89 { "+spin", ".spin", XrmoptionNoArg, "" },
90 { "-wander", ".wander", XrmoptionNoArg, "True" },
91 { "+wander", ".wander", XrmoptionNoArg, "False" },
92 { "-front", ".faceFront", XrmoptionNoArg, "True" },
93 { "+front", ".faceFront", XrmoptionNoArg, "False" }
96 static argtype vars[] = {
97 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
98 {&program_str, "program", "Program", DEF_PROGRAM, t_String},
99 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
100 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
101 {&face_front_p, "faceFront", "FaceFront", DEF_FACE_FRONT, t_Bool},
104 ENTRYPOINT ModeSpecOpt text_opts = {countof(opts), opts, countof(vars), vars, NULL};
107 /* Window management, etc
110 reshape_text (ModeInfo *mi, int width, int height)
112 GLfloat h = (GLfloat) height / (GLfloat) width;
114 glViewport (0, 0, (GLint) width, (GLint) height);
116 glMatrixMode(GL_PROJECTION);
118 gluPerspective (30.0, 1/h, 1.0, 100.0);
120 glMatrixMode(GL_MODELVIEW);
122 gluLookAt( 0.0, 0.0, 30.0,
126 glClear(GL_COLOR_BUFFER_BIT);
131 gl_init (ModeInfo *mi)
133 text_configuration *tp = &tps[MI_SCREEN(mi)];
134 int wire = MI_IS_WIREFRAME(mi);
136 static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
140 glLightfv(GL_LIGHT0, GL_POSITION, pos);
141 glEnable(GL_CULL_FACE);
142 glEnable(GL_LIGHTING);
144 glEnable(GL_DEPTH_TEST);
147 tp->text_list = glGenLists (1);
148 glNewList (tp->text_list, GL_COMPILE);
153 /* The GLUT font only has ASCII characters in them, so do what we can to
154 convert Latin1 characters to the nearest ASCII equivalent...
157 latin1_to_ascii (char *s)
159 unsigned char *us = (unsigned char *) s;
160 const unsigned char ascii[95] = {
161 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
162 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
163 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
164 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
165 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
166 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
167 'u', 'u', 'y', 'p', 'y' };
171 *us = ascii[*us - 161];
180 parse_text (ModeInfo *mi)
182 text_configuration *tp = &tps[MI_SCREEN(mi)];
184 if (tp->text) free (tp->text);
187 if (program_str && *program_str && !!strcmp(program_str, "(default)"))
195 tp->tc = textclient_open (mi->dpy);
197 while (p < buf + sizeof(buf) - 1 &&
200 char c = textclient_getc (tp->tc);
209 if (lines == 0 && buf[0])
212 tp->text = strdup (buf);
215 else if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
220 if (uname (&uts) < 0)
222 tp->text = strdup("uname() failed");
227 if ((s = strchr(uts.nodename, '.')))
229 tp->text = (char *) malloc(strlen(uts.nodename) +
230 strlen(uts.sysname) +
231 strlen(uts.version) +
232 strlen(uts.release) + 10);
234 sprintf(tp->text, "%s\n%s %s.%s",
235 uts.nodename, uts.sysname, uts.version, uts.release);
236 # elif defined(__APPLE__) /* MacOS X + XDarwin */
239 "/System/Library/CoreServices/SystemVersion.plist";
240 FILE *f = fopen (file, "r");
241 char *pbv = 0, *pn = 0, *puvv = 0;
245 while (fgets (buf, sizeof(buf)-1, f)) {
247 if (strstr(buf, S)) { \
248 fgets (buf, sizeof(buf)-1, f); \
249 if ((s = strchr (buf, '>'))) V = strdup(s+1); \
250 if ((s = strchr (V, '<'))) *s = 0; \
252 GRAB ("ProductName", pn)
253 GRAB ("ProductBuildVersion", pbv)
254 GRAB ("ProductUserVisibleVersion", puvv)
259 sprintf (tp->text, "%s\n%s\n%s\n%s",
260 uts.nodename, pn, puvv, uts.machine);
262 sprintf(tp->text, "%s\n%s %s\n%s",
263 uts.nodename, uts.sysname, uts.release, uts.machine);
266 sprintf(tp->text, "%s\n%s %s",
267 uts.nodename, uts.sysname, uts.release);
268 # endif /* special system types */
270 # else /* !HAVE_UNAME */
272 tp->text = strdup(getenv("SYS$NODE"));
274 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
276 # endif /* !HAVE_UNAME */
278 else if (!strchr (text_fmt, '%'))
280 tp->text = strdup (text_fmt);
284 time_t now = time ((time_t *) 0);
285 struct tm *tm = localtime (&now);
286 int L = strlen(text_fmt) + 100;
287 tp->text = (char *) malloc (L);
289 strftime (tp->text, L-1, text_fmt, tm);
291 sprintf (tp->text, "strftime error:\n%s", text_fmt);
295 latin1_to_ascii (tp->text);
300 text_handle_event (ModeInfo *mi, XEvent *event)
302 text_configuration *tp = &tps[MI_SCREEN(mi)];
304 if (event->xany.type == ButtonPress &&
305 event->xbutton.button == Button1)
307 tp->button_down_p = True;
308 gltrackball_start (tp->trackball,
309 event->xbutton.x, event->xbutton.y,
310 MI_WIDTH (mi), MI_HEIGHT (mi));
313 else if (event->xany.type == ButtonRelease &&
314 event->xbutton.button == Button1)
316 tp->button_down_p = False;
319 else if (event->xany.type == ButtonPress &&
320 (event->xbutton.button == Button4 ||
321 event->xbutton.button == Button5 ||
322 event->xbutton.button == Button6 ||
323 event->xbutton.button == Button7))
325 gltrackball_mousewheel (tp->trackball, event->xbutton.button, 10,
326 !!event->xbutton.state);
329 else if (event->xany.type == MotionNotify &&
332 gltrackball_track (tp->trackball,
333 event->xmotion.x, event->xmotion.y,
334 MI_WIDTH (mi), MI_HEIGHT (mi));
343 init_text (ModeInfo *mi)
345 text_configuration *tp;
349 tps = (text_configuration *)
350 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
352 fprintf(stderr, "%s: out of memory\n", progname);
357 tp = &tps[MI_SCREEN(mi)];
359 if ((tp->glx_context = init_GL(mi)) != NULL) {
361 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
365 double spin_speed = 0.5;
366 double wander_speed = 0.02;
367 double tilt_speed = 0.03;
368 double spin_accel = 0.5;
373 if (*s == 'x' || *s == 'X') tp->spinx = True;
374 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
375 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
376 else if (*s == '0') ;
380 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
387 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
388 tp->spiny ? spin_speed : 0,
389 tp->spinz ? spin_speed : 0,
391 do_wander ? wander_speed : 0,
393 tp->rot2 = (face_front_p
394 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
396 tp->trackball = gltrackball_init ();
400 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
401 make_smooth_colormap (0, 0, 0,
402 tp->colors, &tp->ncolors,
405 /* brighter colors, please... */
406 for (i = 0; i < tp->ncolors; i++)
408 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
409 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
410 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
419 fill_character (GLUTstrokeFont font, int c, Bool wire, int *polysP)
421 GLfloat tube_width = 10;
423 const StrokeCharRec *ch;
424 const StrokeRec *stroke;
425 const CoordRec *coord;
426 StrokeFontPtr fontinfo;
429 fontinfo = (StrokeFontPtr) font;
431 if (c < 0 || c >= fontinfo->num_chars)
433 ch = &(fontinfo->ch[c]);
437 for (i = ch->num_strokes, stroke = ch->stroke;
438 i > 0; i--, stroke++) {
439 for (j = stroke->num_coords, coord = stroke->coord;
447 if (j != stroke->num_coords)
448 *polysP += tube (lx, ly, 0,
449 coord->x, coord->y, 0,
452 TUBE_FACES, smooth, True, wire);
457 return (int) (ch->right + tube_width);
464 text_extents (const char *string, int *wP, int *hP)
466 const char *s, *start;
467 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
474 if (*s == '\n' || *s == 0)
479 w += glutStrokeWidth(GLUT_FONT, *start);
484 if (w > *wP) *wP = w;
498 fill_string (const char *string, Bool wire)
501 const char *s, *start;
502 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
504 GLfloat x = 0, y = 0;
507 text_extents (string, &ow, &oh);
509 y = oh / 2 - line_height;
514 if (*s == '\n' || *s == 0)
518 const char *lstart = start;
519 const char *lend = s;
521 /* strip off whitespace at beginning and end of line
522 (since we're centering.) */
523 while (lend > lstart && isspace(lend[-1]))
525 while (lstart < lend && isspace(*lstart))
528 for (s2 = lstart; s2 < lend; s2++)
529 line_w += glutStrokeWidth (GLUT_FONT, *s2);
531 x = (-ow/2) + ((ow-line_w)/2);
532 for (s2 = lstart; s2 < lend; s2++)
535 glTranslatef(x, y, 0);
536 off = fill_character (GLUT_FONT, *s2, wire, &polys);
554 draw_text (ModeInfo *mi)
556 text_configuration *tp = &tps[MI_SCREEN(mi)];
557 Display *dpy = MI_DISPLAY(mi);
558 Window window = MI_WINDOW(mi);
559 int wire = MI_IS_WIREFRAME(mi);
561 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
562 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
564 if (!tp->glx_context)
567 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
571 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
574 tp->last_update = time ((time_t *) 0);
578 glShadeModel(GL_SMOOTH);
580 glEnable(GL_DEPTH_TEST);
581 glEnable(GL_NORMALIZE);
582 glEnable(GL_CULL_FACE);
585 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
588 glScalef(1.1, 1.1, 1.1);
592 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
593 glTranslatef((x - 0.5) * 8,
597 gltrackball_rotate (tp->trackball);
598 glRotatef(current_device_rotation(), 0, 0, 1);
603 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
604 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
605 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
606 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
610 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
611 glRotatef (x * 360, 1, 0, 0);
612 glRotatef (y * 360, 0, 1, 0);
613 glRotatef (z * 360, 0, 0, 1);
620 color[0] = tp->colors[tp->ccolor].red / 65536.0;
621 color[1] = tp->colors[tp->ccolor].green / 65536.0;
622 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
624 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
626 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
628 glScalef(0.01, 0.01, 0.01);
630 mi->polygon_count = fill_string(tp->text, wire);
634 if (mi->fps_p) do_fps (mi);
637 glXSwapBuffers(dpy, window);
641 release_text(ModeInfo * mi)
646 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
648 text_configuration *tp = &tps[MI_SCREEN(mi)];
650 textclient_close (tp->tc);
659 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)