1 /* gltext, Copyright (c) 2001-2014 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"
35 #include "gltrackball.h"
36 #include "textclient.h"
40 #ifdef USE_GL /* whole file */
42 #define DEF_TEXT "(default)"
43 #define DEF_PROGRAM "(default)"
44 #define DEF_SPIN "XYZ"
45 #define DEF_WANDER "True"
46 #define DEF_FACE_FRONT "True"
49 # include <sys/utsname.h>
50 #endif /* HAVE_UNAME */
52 #include "glutstroke.h"
53 #include "glut_roman.h"
54 #define GLUT_FONT (&glutStrokeRoman)
58 GLXContext *glx_context;
60 trackball_state *trackball;
62 Bool spinx, spiny, spinz;
78 static text_configuration *tps = NULL;
80 static char *text_fmt;
81 static char *program_str;
83 static Bool do_wander;
84 static Bool face_front_p;
86 static XrmOptionDescRec opts[] = {
87 { "-text", ".text", XrmoptionSepArg, 0 },
88 { "-program", ".program", XrmoptionSepArg, 0 },
89 { "-spin", ".spin", XrmoptionSepArg, 0 },
90 { "+spin", ".spin", XrmoptionNoArg, "" },
91 { "-wander", ".wander", XrmoptionNoArg, "True" },
92 { "+wander", ".wander", XrmoptionNoArg, "False" },
93 { "-front", ".faceFront", XrmoptionNoArg, "True" },
94 { "+front", ".faceFront", XrmoptionNoArg, "False" }
97 static argtype vars[] = {
98 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
99 {&program_str, "program", "Program", DEF_PROGRAM, t_String},
100 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
101 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
102 {&face_front_p, "faceFront", "FaceFront", DEF_FACE_FRONT, t_Bool},
105 ENTRYPOINT ModeSpecOpt text_opts = {countof(opts), opts, countof(vars), vars, NULL};
108 /* Window management, etc
111 reshape_text (ModeInfo *mi, int width, int height)
113 GLfloat h = (GLfloat) height / (GLfloat) width;
115 glViewport (0, 0, (GLint) width, (GLint) height);
117 glMatrixMode(GL_PROJECTION);
119 gluPerspective (30.0, 1/h, 1.0, 100.0);
121 glMatrixMode(GL_MODELVIEW);
123 gluLookAt( 0.0, 0.0, 30.0,
127 glClear(GL_COLOR_BUFFER_BIT);
132 gl_init (ModeInfo *mi)
134 text_configuration *tp = &tps[MI_SCREEN(mi)];
135 int wire = MI_IS_WIREFRAME(mi);
137 static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
141 glLightfv(GL_LIGHT0, GL_POSITION, pos);
142 glEnable(GL_CULL_FACE);
143 glEnable(GL_LIGHTING);
145 glEnable(GL_DEPTH_TEST);
148 tp->text_list = glGenLists (1);
149 glNewList (tp->text_list, GL_COMPILE);
154 /* The GLUT font only has ASCII characters in them, so do what we can to
155 convert Latin1 characters to the nearest ASCII equivalent...
158 latin1_to_ascii (char *s)
160 unsigned char *us = (unsigned char *) s;
161 const unsigned char ascii[95] = {
162 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
163 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
164 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
165 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
166 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
167 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
168 'u', 'u', 'y', 'p', 'y' };
172 *us = ascii[*us - 161];
181 parse_text (ModeInfo *mi)
183 text_configuration *tp = &tps[MI_SCREEN(mi)];
185 if (tp->text) free (tp->text);
188 if (program_str && *program_str && !!strcmp(program_str, "(default)"))
196 tp->tc = textclient_open (mi->dpy);
198 while (p < buf + sizeof(buf) - 1 &&
201 int c = textclient_getc (tp->tc);
210 if (lines == 0 && buf[0])
213 tp->text = strdup (buf);
216 else if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
221 if (uname (&uts) < 0)
223 tp->text = strdup("uname() failed");
228 if ((s = strchr(uts.nodename, '.')))
230 tp->text = (char *) malloc(strlen(uts.nodename) +
231 strlen(uts.sysname) +
232 strlen(uts.version) +
233 strlen(uts.release) + 10);
235 sprintf(tp->text, "%s\n%s %s.%s",
236 uts.nodename, uts.sysname, uts.version, uts.release);
237 # elif defined(USE_IPHONE)
238 /* "My iPhone\n iPhone4,1\n Darwin 11.0.0" */
239 sprintf(tp->text, "%s\n%s\n%s %s",
240 uts.nodename, uts.machine, uts.sysname, uts.release);
241 # elif defined(__APPLE__) /* MacOS X + XDarwin */
244 "/System/Library/CoreServices/SystemVersion.plist";
245 FILE *f = fopen (file, "r");
246 char *pbv = 0, *pn = 0, *puvv = 0;
250 while (fgets (buf, sizeof(buf)-1, f)) {
252 if (strstr(buf, S)) { \
253 fgets (buf, sizeof(buf)-1, f); \
254 if ((s = strchr (buf, '>'))) V = strdup(s+1); \
255 if ((s = strchr (V, '<'))) *s = 0; \
257 GRAB ("ProductName", pn)
258 GRAB ("ProductBuildVersion", pbv)
259 GRAB ("ProductUserVisibleVersion", puvv)
264 sprintf (tp->text, "%s\n%s\n%s\n%s",
265 uts.nodename, pn, puvv, uts.machine);
267 sprintf(tp->text, "%s\n%s %s\n%s",
268 uts.nodename, uts.sysname, uts.release, uts.machine);
271 sprintf(tp->text, "%s\n%s %s",
272 uts.nodename, uts.sysname, uts.release);
273 # endif /* special system types */
275 # else /* !HAVE_UNAME */
277 tp->text = strdup(getenv("SYS$NODE"));
279 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
281 # endif /* !HAVE_UNAME */
283 else if (!strchr (text_fmt, '%'))
285 tp->text = strdup (text_fmt);
289 time_t now = time ((time_t *) 0);
290 struct tm *tm = localtime (&now);
291 int L = strlen(text_fmt) + 100;
292 tp->text = (char *) malloc (L);
294 strftime (tp->text, L-1, text_fmt, tm);
296 sprintf (tp->text, "strftime error:\n%s", text_fmt);
300 latin1_to_ascii (tp->text);
305 text_handle_event (ModeInfo *mi, XEvent *event)
307 text_configuration *tp = &tps[MI_SCREEN(mi)];
309 if (gltrackball_event_handler (event, tp->trackball,
310 MI_WIDTH (mi), MI_HEIGHT (mi),
319 init_text (ModeInfo *mi)
321 text_configuration *tp;
325 tps = (text_configuration *)
326 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
328 fprintf(stderr, "%s: out of memory\n", progname);
333 tp = &tps[MI_SCREEN(mi)];
335 if ((tp->glx_context = init_GL(mi)) != NULL) {
337 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
341 double spin_speed = 0.5;
342 double wander_speed = 0.02;
343 double tilt_speed = 0.03;
344 double spin_accel = 0.5;
349 if (*s == 'x' || *s == 'X') tp->spinx = True;
350 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
351 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
352 else if (*s == '0') ;
356 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
363 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
364 tp->spiny ? spin_speed : 0,
365 tp->spinz ? spin_speed : 0,
367 do_wander ? wander_speed : 0,
369 tp->rot2 = (face_front_p
370 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
372 tp->trackball = gltrackball_init (False);
376 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
377 make_smooth_colormap (0, 0, 0,
378 tp->colors, &tp->ncolors,
381 /* brighter colors, please... */
382 for (i = 0; i < tp->ncolors; i++)
384 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
385 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
386 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
395 fill_character (GLUTstrokeFont font, int c, Bool wire, int *polysP)
397 GLfloat tube_width = 10;
399 const StrokeCharRec *ch;
400 const StrokeRec *stroke;
401 const CoordRec *coord;
402 StrokeFontPtr fontinfo;
405 fontinfo = (StrokeFontPtr) font;
407 if (c < 0 || c >= fontinfo->num_chars)
409 ch = &(fontinfo->ch[c]);
413 for (i = ch->num_strokes, stroke = ch->stroke;
414 i > 0; i--, stroke++) {
415 for (j = stroke->num_coords, coord = stroke->coord;
424 if (j != stroke->num_coords)
425 *polysP += tube (lx, ly, 0,
426 coord->x, coord->y, 0,
429 TUBE_FACES, smooth, False, wire);
433 /* Put a sphere at the endpoint of every line segment. Wasteful
434 on curves like "0" but necessary on corners like "4". */
438 glTranslatef (lx, ly, 0);
439 glScalef (tube_width, tube_width, tube_width);
440 *polysP += unit_sphere (TUBE_FACES, TUBE_FACES, wire);
445 return (int) (ch->right + tube_width);
452 text_extents (const char *string, int *wP, int *hP)
454 const char *s, *start;
455 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
462 if (*s == '\n' || *s == 0)
467 w += glutStrokeWidth(GLUT_FONT, *start);
472 if (w > *wP) *wP = w;
486 fill_string (const char *string, Bool wire)
489 const char *s, *start;
490 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
492 GLfloat x = 0, y = 0;
495 text_extents (string, &ow, &oh);
497 y = oh / 2 - line_height;
502 if (*s == '\n' || *s == 0)
506 const char *lstart = start;
507 const char *lend = s;
509 /* strip off whitespace at beginning and end of line
510 (since we're centering.) */
511 while (lend > lstart && isspace(lend[-1]))
513 while (lstart < lend && isspace(*lstart))
516 for (s2 = lstart; s2 < lend; s2++)
517 line_w += glutStrokeWidth (GLUT_FONT, *s2);
519 x = (-ow/2) + ((ow-line_w)/2);
520 for (s2 = lstart; s2 < lend; s2++)
523 glTranslatef(x, y, 0);
524 off = fill_character (GLUT_FONT, *s2, wire, &polys);
542 draw_text (ModeInfo *mi)
544 text_configuration *tp = &tps[MI_SCREEN(mi)];
545 Display *dpy = MI_DISPLAY(mi);
546 Window window = MI_WINDOW(mi);
547 int wire = MI_IS_WIREFRAME(mi);
549 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
550 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
552 if (!tp->glx_context)
555 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
559 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
562 tp->last_update = time ((time_t *) 0);
566 glShadeModel(GL_SMOOTH);
568 glEnable(GL_DEPTH_TEST);
569 glEnable(GL_NORMALIZE);
570 glEnable(GL_CULL_FACE);
573 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
576 glScalef(1.1, 1.1, 1.1);
577 glRotatef(current_device_rotation(), 0, 0, 1);
581 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
582 glTranslatef((x - 0.5) * 8,
586 gltrackball_rotate (tp->trackball);
591 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
592 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
593 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
594 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
598 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
599 glRotatef (x * 360, 1, 0, 0);
600 glRotatef (y * 360, 0, 1, 0);
601 glRotatef (z * 360, 0, 0, 1);
608 color[0] = tp->colors[tp->ccolor].red / 65536.0;
609 color[1] = tp->colors[tp->ccolor].green / 65536.0;
610 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
612 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
614 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
616 glScalef(0.01, 0.01, 0.01);
618 mi->polygon_count = fill_string(tp->text, wire);
622 if (mi->fps_p) do_fps (mi);
625 glXSwapBuffers(dpy, window);
629 release_text(ModeInfo * mi)
634 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
636 text_configuration *tp = &tps[MI_SCREEN(mi)];
638 textclient_close (tp->tc);
647 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)