1 /* gltext, Copyright (c) 2001-2017 Jamie Zawinski <jwz@jwz.orgq2
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 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"
35 #include "gltrackball.h"
36 #include "textclient.h"
41 #ifdef USE_GL /* whole file */
43 #define DEF_TEXT "(default)"
44 #define DEF_PROGRAM "(default)"
45 #define DEF_SCALE_FACTOR "0.01"
46 #define DEF_WANDER_SPEED "0.02"
47 #define DEF_MAX_LINES "8"
48 #define DEF_SPIN "XYZ"
49 #define DEF_WANDER "True"
50 #define DEF_FACE_FRONT "True"
51 #define DEF_USE_MONOSPACE "False"
54 # include <sys/utsname.h>
55 #endif /* HAVE_UNAME */
57 #include "glutstroke.h"
58 #include "glut_roman.h"
59 #include "glut_mroman.h"
60 #define GLUT_VARI_FONT (&glutStrokeRoman)
61 #define GLUT_MONO_FONT (&glutStrokeMonoRoman)
62 #define GLUT_FONT ((use_monospace) ? GLUT_MONO_FONT : GLUT_VARI_FONT)
66 GLXContext *glx_context;
68 trackball_state *trackball;
70 Bool spinx, spiny, spinz;
86 static text_configuration *tps = NULL;
88 static char *text_fmt;
89 static char *program_str;
90 static float scale_factor;
91 static int max_no_lines;
92 static float wander_speed;
94 static Bool do_wander;
95 static Bool face_front_p;
96 static Bool use_monospace;
98 static XrmOptionDescRec opts[] = {
99 { "-text", ".text", XrmoptionSepArg, 0 },
100 { "-program", ".program", XrmoptionSepArg, 0 },
101 { "-scale", ".scaleFactor", XrmoptionSepArg, 0 },
102 { "-maxlines", ".maxLines", XrmoptionSepArg, 0 },
103 { "-wander-speed", ".wanderSpeed", XrmoptionSepArg, 0 },
104 { "-spin", ".spin", XrmoptionSepArg, 0 },
105 { "+spin", ".spin", XrmoptionNoArg, "" },
106 { "-wander", ".wander", XrmoptionNoArg, "True" },
107 { "+wander", ".wander", XrmoptionNoArg, "False" },
108 { "-front", ".faceFront", XrmoptionNoArg, "True" },
109 { "+front", ".faceFront", XrmoptionNoArg, "False" },
110 { "-mono", ".useMonoSpace", XrmoptionNoArg, "True" },
111 { "+mono", ".useMonoSpace", XrmoptionNoArg, "False" }
114 static argtype vars[] = {
115 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
116 {&program_str, "program", "Program", DEF_PROGRAM, t_String},
117 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
118 {&scale_factor, "scaleFactor", "ScaleFactor", DEF_SCALE_FACTOR, t_Float},
119 {&max_no_lines, "maxLines", "MaxLines", DEF_MAX_LINES, t_Int},
120 {&wander_speed, "wanderSpeed", "WanderSpeed", DEF_WANDER_SPEED, t_Float},
121 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
122 {&face_front_p, "faceFront", "FaceFront", DEF_FACE_FRONT, t_Bool},
123 {&use_monospace, "useMonoSpace", "UseMonoSpace", DEF_USE_MONOSPACE, t_Bool},
126 ENTRYPOINT ModeSpecOpt text_opts = {countof(opts), opts, countof(vars), vars, NULL};
129 /* Window management, etc
132 reshape_text (ModeInfo *mi, int width, int height)
134 GLfloat h = (GLfloat) height / (GLfloat) width;
137 if (width > height * 5) { /* tiny window: show middle */
138 height = width * 9/16;
140 h = height / (GLfloat) width;
143 glViewport (0, y, (GLint) width, (GLint) height);
145 glMatrixMode(GL_PROJECTION);
147 gluPerspective (30.0, 1/h, 1.0, 100.0);
149 glMatrixMode(GL_MODELVIEW);
151 gluLookAt( 0.0, 0.0, 30.0,
155 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
157 int o = (int) current_device_rotation();
158 if (o != 0 && o != 180 && o != -180)
159 glScalef (1/h, 1/h, 1/h);
163 glClear(GL_COLOR_BUFFER_BIT);
168 gl_init (ModeInfo *mi)
170 text_configuration *tp = &tps[MI_SCREEN(mi)];
171 int wire = MI_IS_WIREFRAME(mi);
173 static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
177 glLightfv(GL_LIGHT0, GL_POSITION, pos);
178 glEnable(GL_CULL_FACE);
179 glEnable(GL_LIGHTING);
181 glEnable(GL_DEPTH_TEST);
184 tp->text_list = glGenLists (1);
185 glNewList (tp->text_list, GL_COMPILE);
191 parse_text (ModeInfo *mi)
193 text_configuration *tp = &tps[MI_SCREEN(mi)];
194 char *old = tp->text;
196 if (program_str && *program_str && !!strcmp(program_str, "(default)"))
198 int max_lines = max_no_lines;
204 tp->tc = textclient_open (mi->dpy);
206 while (p < buf + sizeof(buf) - 1 &&
209 int c = textclient_getc (tp->tc);
218 if (lines == 0 && buf[0])
221 tp->text = strdup (buf);
223 tp->reload = 7; /* Let this one linger for a few seconds */
228 else if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
233 if (uname (&uts) < 0)
235 tp->text = strdup("uname() failed");
240 if ((s = strchr(uts.nodename, '.')))
242 tp->text = (char *) malloc(strlen(uts.nodename) +
243 strlen(uts.sysname) +
244 strlen(uts.version) +
245 strlen(uts.release) + 10);
247 sprintf(tp->text, "%s\n%s %s.%s",
248 uts.nodename, uts.sysname, uts.version, uts.release);
249 # elif defined(USE_IPHONE)
250 /* "My iPhone\n iPhone4,1\n Darwin 11.0.0" */
251 sprintf(tp->text, "%s\n%s\n%s %s",
252 uts.nodename, uts.machine, uts.sysname, uts.release);
253 # elif defined(__APPLE__) /* MacOS X + XDarwin */
256 "/System/Library/CoreServices/SystemVersion.plist";
257 FILE *f = fopen (file, "r");
258 char *pbv = 0, *pn = 0, *puvv = 0;
262 while (fgets (buf, sizeof(buf)-1, f)) {
264 if (strstr(buf, S)) { \
265 fgets (buf, sizeof(buf)-1, f); \
266 if ((s = strchr (buf, '>'))) V = strdup(s+1); \
267 if ((s = strchr (V, '<'))) *s = 0; \
269 GRAB ("ProductName", pn)
270 GRAB ("ProductBuildVersion", pbv)
271 GRAB ("ProductUserVisibleVersion", puvv)
276 sprintf (tp->text, "%s\n%s\n%s\n%s",
277 uts.nodename, pn, puvv, uts.machine);
279 sprintf(tp->text, "%s\n%s %s\n%s",
280 uts.nodename, uts.sysname, uts.release, uts.machine);
283 sprintf(tp->text, "%s\n%s %s",
284 uts.nodename, uts.sysname, uts.release);
285 # endif /* special system types */
287 # else /* !HAVE_UNAME */
289 tp->text = strdup(getenv("SYS$NODE"));
291 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
293 # endif /* !HAVE_UNAME */
295 else if (!strchr (text_fmt, '%'))
297 tp->text = strdup (text_fmt);
301 time_t now = time ((time_t *) 0);
302 struct tm *tm = localtime (&now);
303 int L = strlen(text_fmt) + 100;
304 tp->text = (char *) malloc (L);
306 strftime (tp->text, L-1, text_fmt, tm);
308 sprintf (tp->text, "strftime error:\n%s", text_fmt);
313 /* The GLUT font only has ASCII characters. */
314 char *s1 = utf8_to_latin1 (tp->text, True);
319 /* If we had text before but got no text this time, hold on to the
320 old one, to avoid flickering.
322 if (old && *old && !*tp->text)
333 text_handle_event (ModeInfo *mi, XEvent *event)
335 text_configuration *tp = &tps[MI_SCREEN(mi)];
337 if (gltrackball_event_handler (event, tp->trackball,
338 MI_WIDTH (mi), MI_HEIGHT (mi),
347 init_text (ModeInfo *mi)
349 text_configuration *tp;
354 tp = &tps[MI_SCREEN(mi)];
356 if ((tp->glx_context = init_GL(mi)) != NULL) {
358 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
362 double spin_speed = 0.5;
363 double wander_speed = 0.02;
364 double tilt_speed = 0.03;
365 double spin_accel = 0.5;
370 if (*s == 'x' || *s == 'X') tp->spinx = True;
371 else if (*s == 'y' || *s == 'Y') tp->spiny = True;
372 else if (*s == 'z' || *s == 'Z') tp->spinz = True;
373 else if (*s == '0') ;
377 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
384 tp->rot = make_rotator (tp->spinx ? spin_speed : 0,
385 tp->spiny ? spin_speed : 0,
386 tp->spinz ? spin_speed : 0,
388 do_wander ? wander_speed : 0,
390 tp->rot2 = (face_front_p
391 ? make_rotator (0, 0, 0, 0, tilt_speed, True)
393 tp->trackball = gltrackball_init (False);
397 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
398 make_smooth_colormap (0, 0, 0,
399 tp->colors, &tp->ncolors,
402 /* brighter colors, please... */
403 for (i = 0; i < tp->ncolors; i++)
405 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
406 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
407 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
416 fill_character (GLUTstrokeFont font, int c, Bool wire, int *polysP)
418 GLfloat tube_width = 10;
420 const StrokeCharRec *ch;
421 const StrokeRec *stroke;
422 const CoordRec *coord;
423 StrokeFontPtr fontinfo;
426 fontinfo = (StrokeFontPtr) font;
428 if (c < 0 || c >= fontinfo->num_chars)
430 ch = &(fontinfo->ch[c]);
434 for (i = ch->num_strokes, stroke = ch->stroke;
435 i > 0; i--, stroke++) {
436 for (j = stroke->num_coords, coord = stroke->coord;
445 if (j != stroke->num_coords)
446 *polysP += tube (lx, ly, 0,
447 coord->x, coord->y, 0,
450 TUBE_FACES, smooth, False, wire);
454 /* Put a sphere at the endpoint of every line segment. Wasteful
455 on curves like "0" but necessary on corners like "4". */
459 glTranslatef (lx, ly, 0);
460 glScalef (tube_width, tube_width, tube_width);
461 *polysP += unit_sphere (TUBE_FACES, TUBE_FACES, wire);
466 return (int) (ch->right + tube_width);
473 text_extents (const char *string, int *wP, int *hP)
475 const char *s, *start;
476 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
483 if (*s == '\n' || *s == 0)
488 w += glutStrokeWidth(GLUT_FONT, *start);
493 if (w > *wP) *wP = w;
507 fill_string (const char *string, Bool wire)
510 const char *s, *start;
511 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
513 GLfloat x = 0, y = 0;
516 text_extents (string, &ow, &oh);
518 y = oh / 2 - line_height;
523 if (*s == '\n' || *s == 0)
527 const char *lstart = start;
528 const char *lend = s;
530 /* strip off whitespace at beginning and end of line
531 (since we're centering.) */
532 while (lend > lstart && isspace(lend[-1]))
534 while (lstart < lend && isspace(*lstart))
537 for (s2 = lstart; s2 < lend; s2++)
538 line_w += glutStrokeWidth (GLUT_FONT, *s2);
540 x = (-ow/2) + ((ow-line_w)/2);
541 for (s2 = lstart; s2 < lend; s2++)
544 glTranslatef(x, y, 0);
545 off = fill_character (GLUT_FONT, *s2, wire, &polys);
563 draw_text (ModeInfo *mi)
565 text_configuration *tp = &tps[MI_SCREEN(mi)];
566 Display *dpy = MI_DISPLAY(mi);
567 Window window = MI_WINDOW(mi);
568 int wire = MI_IS_WIREFRAME(mi);
570 GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
571 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
573 if (!tp->glx_context)
576 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tp->glx_context));
580 if (time ((time_t *) 0) >= tp->last_update + tp->reload)
583 tp->last_update = time ((time_t *) 0);
587 glShadeModel(GL_SMOOTH);
589 glEnable(GL_DEPTH_TEST);
590 glEnable(GL_NORMALIZE);
591 glEnable(GL_CULL_FACE);
594 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
597 glScalef(1.1, 1.1, 1.1);
598 glRotatef(current_device_rotation(), 0, 0, 1);
602 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
603 glTranslatef((x - 0.5) * 8,
607 gltrackball_rotate (tp->trackball);
612 get_position (tp->rot2, &x, &y, &z, !tp->button_down_p);
613 if (tp->spinx) glRotatef (max/2 - x*max, 1, 0, 0);
614 if (tp->spiny) glRotatef (max/2 - y*max, 0, 1, 0);
615 if (tp->spinz) glRotatef (max/2 - z*max, 0, 0, 1);
619 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
620 glRotatef (x * 360, 1, 0, 0);
621 glRotatef (y * 360, 0, 1, 0);
622 glRotatef (z * 360, 0, 0, 1);
629 color[0] = tp->colors[tp->ccolor].red / 65536.0;
630 color[1] = tp->colors[tp->ccolor].green / 65536.0;
631 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
633 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
635 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
637 glScalef(scale_factor, scale_factor, scale_factor);
639 mi->polygon_count = fill_string(tp->text, wire);
643 if (mi->fps_p) do_fps (mi);
646 glXSwapBuffers(dpy, window);
650 free_text(ModeInfo * mi)
652 text_configuration *tp = &tps[MI_SCREEN(mi)];
654 textclient_close (tp->tc);
658 XSCREENSAVER_MODULE_2 ("GLText", gltext, text)