1 /* gltext, Copyright (c) 2001-2012 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 char 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 (event->xany.type == ButtonPress &&
310 event->xbutton.button == Button1)
312 tp->button_down_p = True;
313 gltrackball_start (tp->trackball,
314 event->xbutton.x, event->xbutton.y,
315 MI_WIDTH (mi), MI_HEIGHT (mi));
318 else if (event->xany.type == ButtonRelease &&
319 event->xbutton.button == Button1)
321 tp->button_down_p = False;
324 else if (event->xany.type == ButtonPress &&
325 (event->xbutton.button == Button4 ||
326 event->xbutton.button == Button5 ||
327 event->xbutton.button == Button6 ||
328 event->xbutton.button == Button7))
330 gltrackball_mousewheel (tp->trackball, event->xbutton.button, 10,
331 !!event->xbutton.state);
334 else if (event->xany.type == MotionNotify &&
337 gltrackball_track (tp->trackball,
338 event->xmotion.x, event->xmotion.y,
339 MI_WIDTH (mi), MI_HEIGHT (mi));
348 init_text (ModeInfo *mi)
350 text_configuration *tp;
354 tps = (text_configuration *)
355 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
357 fprintf(stderr, "%s: out of memory\n", progname);
362 tp = &tps[MI_SCREEN(mi)];
364 if ((tp->glx_context = init_GL(mi)) != NULL) {
366 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
370 double spin_speed = 0.5;
371 double wander_speed = 0.02;
372 double tilt_speed = 0.03;
373 double spin_accel = 0.5;
378 if (*s == 'x' || *s == 'X') tp->spinx = True;
379 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
380 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
381 else if (*s == '0') ;
385 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
392 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
393 tp->spiny ? spin_speed : 0,
394 tp->spinz ? spin_speed : 0,
396 do_wander ? wander_speed : 0,
398 tp->rot2 = (face_front_p
399 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
401 tp->trackball = gltrackball_init ();
405 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
406 make_smooth_colormap (0, 0, 0,
407 tp->colors, &tp->ncolors,
410 /* brighter colors, please... */
411 for (i = 0; i < tp->ncolors; i++)
413 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
414 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
415 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
424 fill_character (GLUTstrokeFont font, int c, Bool wire, int *polysP)
426 GLfloat tube_width = 10;
428 const StrokeCharRec *ch;
429 const StrokeRec *stroke;
430 const CoordRec *coord;
431 StrokeFontPtr fontinfo;
434 fontinfo = (StrokeFontPtr) font;
436 if (c < 0 || c >= fontinfo->num_chars)
438 ch = &(fontinfo->ch[c]);
442 for (i = ch->num_strokes, stroke = ch->stroke;
443 i > 0; i--, stroke++) {
444 for (j = stroke->num_coords, coord = stroke->coord;
453 if (j != stroke->num_coords)
454 *polysP += tube (lx, ly, 0,
455 coord->x, coord->y, 0,
458 TUBE_FACES, smooth, False, wire);
462 /* Put a sphere at the endpoint of every line segment. Wasteful
463 on curves like "0" but necessary on corners like "4". */
467 glTranslatef (lx, ly, 0);
468 glScalef (tube_width, tube_width, tube_width);
469 *polysP += unit_sphere (TUBE_FACES, TUBE_FACES, wire);
474 return (int) (ch->right + tube_width);
481 text_extents (const char *string, int *wP, int *hP)
483 const char *s, *start;
484 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
491 if (*s == '\n' || *s == 0)
496 w += glutStrokeWidth(GLUT_FONT, *start);
501 if (w > *wP) *wP = w;
515 fill_string (const char *string, Bool wire)
518 const char *s, *start;
519 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
521 GLfloat x = 0, y = 0;
524 text_extents (string, &ow, &oh);
526 y = oh / 2 - line_height;
531 if (*s == '\n' || *s == 0)
535 const char *lstart = start;
536 const char *lend = s;
538 /* strip off whitespace at beginning and end of line
539 (since we're centering.) */
540 while (lend > lstart && isspace(lend[-1]))
542 while (lstart < lend && isspace(*lstart))
545 for (s2 = lstart; s2 < lend; s2++)
546 line_w += glutStrokeWidth (GLUT_FONT, *s2);
548 x = (-ow/2) + ((ow-line_w)/2);
549 for (s2 = lstart; s2 < lend; s2++)
552 glTranslatef(x, y, 0);
553 off = fill_character (GLUT_FONT, *s2, wire, &polys);
571 draw_text (ModeInfo *mi)
573 text_configuration *tp = &tps[MI_SCREEN(mi)];
574 Display *dpy = MI_DISPLAY(mi);
575 Window window = MI_WINDOW(mi);
576 int wire = MI_IS_WIREFRAME(mi);
578 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
579 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
581 if (!tp->glx_context)
584 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
588 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
591 tp->last_update = time ((time_t *) 0);
595 glShadeModel(GL_SMOOTH);
597 glEnable(GL_DEPTH_TEST);
598 glEnable(GL_NORMALIZE);
599 glEnable(GL_CULL_FACE);
602 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
605 glScalef(1.1, 1.1, 1.1);
606 glRotatef(current_device_rotation(), 0, 0, 1);
610 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
611 glTranslatef((x - 0.5) * 8,
615 gltrackball_rotate (tp->trackball);
620 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
621 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
622 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
623 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
627 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
628 glRotatef (x * 360, 1, 0, 0);
629 glRotatef (y * 360, 0, 1, 0);
630 glRotatef (z * 360, 0, 0, 1);
637 color[0] = tp->colors[tp->ccolor].red / 65536.0;
638 color[1] = tp->colors[tp->ccolor].green / 65536.0;
639 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
641 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
643 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
645 glScalef(0.01, 0.01, 0.01);
647 mi->polygon_count = fill_string(tp->text, wire);
651 if (mi->fps_p) do_fps (mi);
654 glXSwapBuffers(dpy, window);
658 release_text(ModeInfo * mi)
663 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
665 text_configuration *tp = &tps[MI_SCREEN(mi)];
667 textclient_close (tp->tc);
676 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)