1 /* gltext, Copyright (c) 2001-2005 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 #include <X11/Intrinsic.h>
14 extern XtAppContext app;
16 #define PROGCLASS "GLText"
17 #define HACK_INIT init_text
18 #define HACK_DRAW draw_text
19 #define HACK_RESHAPE reshape_text
20 #define HACK_HANDLE_EVENT text_handle_event
21 #define EVENT_MASK PointerMotionMask
22 #define sws_opts xlockmore_opts
24 #define DEF_TEXT "(default)"
25 #define DEF_SPIN "XYZ"
26 #define DEF_WANDER "True"
28 #define DEFAULTS "*delay: 20000 \n" \
29 "*showFPS: False \n" \
30 "*wireframe: False \n" \
31 "*spin: " DEF_SPIN "\n" \
32 "*wander: " DEF_WANDER "\n" \
33 "*text: " DEF_TEXT "\n"
36 #define SMOOTH_TUBE /* whether to have smooth or faceted tubes */
39 # define TUBE_FACES 12 /* how densely to render tubes */
46 #define countof(x) (sizeof((x))/sizeof((*x)))
48 #include "xlockmore.h"
52 #include "gltrackball.h"
59 #endif /* HAVE_LOCALE_H */
61 #ifdef USE_GL /* whole file */
64 # include <sys/utsname.h>
65 #endif /* HAVE_UNAME */
69 #include "glutstroke.h"
70 #include "glut_roman.h"
71 #define GLUT_FONT (&glutStrokeRoman)
75 GLXContext *glx_context;
77 trackball_state *trackball;
90 static text_configuration *tps = NULL;
92 static char *text_fmt;
94 static Bool do_wander;
96 static XrmOptionDescRec opts[] = {
97 { "-text", ".text", XrmoptionSepArg, 0 },
98 { "-spin", ".spin", XrmoptionSepArg, 0 },
99 { "+spin", ".spin", XrmoptionNoArg, "" },
100 { "-wander", ".wander", XrmoptionNoArg, "True" },
101 { "+wander", ".wander", XrmoptionNoArg, "False" }
104 static argtype vars[] = {
105 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
106 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
107 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
110 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
113 /* Window management, etc
116 reshape_text (ModeInfo *mi, int width, int height)
118 GLfloat h = (GLfloat) height / (GLfloat) width;
120 glViewport (0, 0, (GLint) width, (GLint) height);
122 glMatrixMode(GL_PROJECTION);
124 gluPerspective (30.0, 1/h, 1.0, 100.0);
126 glMatrixMode(GL_MODELVIEW);
128 gluLookAt( 0.0, 0.0, 30.0,
132 glClear(GL_COLOR_BUFFER_BIT);
137 gl_init (ModeInfo *mi)
139 text_configuration *tp = &tps[MI_SCREEN(mi)];
140 int wire = MI_IS_WIREFRAME(mi);
142 static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
146 glLightfv(GL_LIGHT0, GL_POSITION, pos);
147 glEnable(GL_CULL_FACE);
148 glEnable(GL_LIGHTING);
150 glEnable(GL_DEPTH_TEST);
153 tp->text_list = glGenLists (1);
154 glNewList (tp->text_list, GL_COMPILE);
159 /* The GLUT font only has ASCII characters in them, so do what we can to
160 convert Latin1 characters to the nearest ASCII equivalent...
163 latin1_to_ascii (char *s)
165 unsigned char *us = (unsigned char *) s;
166 const unsigned char ascii[95] = {
167 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
168 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
169 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
170 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
171 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
172 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
173 'u', 'u', 'y', 'p', 'y' };
177 *us = ascii[*us - 161];
186 parse_text (ModeInfo *mi)
188 text_configuration *tp = &tps[MI_SCREEN(mi)];
190 if (tp->text) free (tp->text);
192 if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
197 if (uname (&uts) < 0)
199 tp->text = strdup("uname() failed");
204 if ((s = strchr(uts.nodename, '.')))
206 tp->text = (char *) malloc(strlen(uts.nodename) +
207 strlen(uts.sysname) +
208 strlen(uts.version) +
209 strlen(uts.release) + 10);
211 sprintf(tp->text, "%s\n%s %s.%s",
212 uts.nodename, uts.sysname, uts.version, uts.release);
213 # elif defined(__APPLE__) /* MacOS X + XDarwin */
214 sprintf(tp->text, "%s\n%s %s\n%s",
215 uts.nodename, uts.sysname, uts.release, uts.machine);
217 sprintf(tp->text, "%s\n%s %s",
218 uts.nodename, uts.sysname, uts.release);
219 # endif /* special system types */
221 # else /* !HAVE_UNAME */
223 tp->text = strdup(getenv("SYS$NODE"));
225 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
227 # endif /* !HAVE_UNAME */
229 else if (!strchr (text_fmt, '%'))
231 tp->text = strdup (text_fmt);
235 time_t now = time ((time_t *) 0);
236 struct tm *tm = localtime (&now);
237 int L = strlen(text_fmt) + 100;
238 tp->text = (char *) malloc (L);
240 strftime (tp->text, L-1, text_fmt, tm);
242 sprintf (tp->text, "strftime error:\n%s", text_fmt);
245 latin1_to_ascii (tp->text);
250 text_handle_event (ModeInfo *mi, XEvent *event)
252 text_configuration *tp = &tps[MI_SCREEN(mi)];
254 if (event->xany.type == ButtonPress &&
255 event->xbutton.button == Button1)
257 tp->button_down_p = True;
258 gltrackball_start (tp->trackball,
259 event->xbutton.x, event->xbutton.y,
260 MI_WIDTH (mi), MI_HEIGHT (mi));
263 else if (event->xany.type == ButtonRelease &&
264 event->xbutton.button == Button1)
266 tp->button_down_p = False;
269 else if (event->xany.type == ButtonPress &&
270 (event->xbutton.button == Button4 ||
271 event->xbutton.button == Button5))
273 gltrackball_mousewheel (tp->trackball, event->xbutton.button, 10,
274 !!event->xbutton.state);
277 else if (event->xany.type == MotionNotify &&
280 gltrackball_track (tp->trackball,
281 event->xmotion.x, event->xmotion.y,
282 MI_WIDTH (mi), MI_HEIGHT (mi));
291 init_text (ModeInfo *mi)
293 text_configuration *tp;
296 # ifdef HAVE_SETLOCALE
297 setlocale (LC_TIME, ""); /* for strftime() calls */
301 tps = (text_configuration *)
302 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
304 fprintf(stderr, "%s: out of memory\n", progname);
308 tp = &tps[MI_SCREEN(mi)];
311 tp = &tps[MI_SCREEN(mi)];
313 if ((tp->glx_context = init_GL(mi)) != NULL) {
315 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
319 Bool spinx=False, spiny=False, spinz=False;
320 double spin_speed = 0.5;
321 double wander_speed = 0.02;
322 double spin_accel = 0.5;
327 if (*s == 'x' || *s == 'X') spinx = True;
328 else if (*s == 'y' || *s == 'Y') spiny = True;
329 else if (*s == 'z' || *s == 'Z') spinz = True;
333 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
340 tp->rot = make_rotator (spinx ? spin_speed : 0,
341 spiny ? spin_speed : 0,
342 spinz ? spin_speed : 0,
344 do_wander ? wander_speed : 0,
346 tp->trackball = gltrackball_init ();
350 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
351 make_smooth_colormap (0, 0, 0,
352 tp->colors, &tp->ncolors,
355 /* brighter colors, please... */
356 for (i = 0; i < tp->ncolors; i++)
358 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
359 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
360 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
369 fill_character (GLUTstrokeFont font, int c, Bool wire)
371 GLfloat tube_width = 10;
373 const StrokeCharRec *ch;
374 const StrokeRec *stroke;
375 const CoordRec *coord;
376 StrokeFontPtr fontinfo;
379 fontinfo = (StrokeFontPtr) font;
381 if (c < 0 || c >= fontinfo->num_chars)
383 ch = &(fontinfo->ch[c]);
387 for (i = ch->num_strokes, stroke = ch->stroke;
388 i > 0; i--, stroke++) {
389 for (j = stroke->num_coords, coord = stroke->coord;
397 if (j != stroke->num_coords)
399 coord->x, coord->y, 0,
402 TUBE_FACES, smooth, True, wire);
407 return (int) (ch->right + tube_width);
414 text_extents (const char *string, int *wP, int *hP)
416 const char *s, *start;
417 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
424 if (*s == '\n' || *s == 0)
429 w += glutStrokeWidth(GLUT_FONT, *start);
434 if (w > *wP) *wP = w;
448 fill_string (const char *string, Bool wire)
450 const char *s, *start;
451 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
453 GLfloat x = 0, y = 0;
457 lines = text_extents (string, &ow, &oh);
459 y = oh / 2 - line_height;
464 if (*s == '\n' || *s == 0)
468 const char *lstart = start;
469 const char *lend = s;
471 /* strip off whitespace at beginning and end of line
472 (since we're centering.) */
473 while (lend > lstart && isspace(lend[-1]))
475 while (lstart < lend && isspace(*lstart))
478 for (s2 = lstart; s2 < lend; s2++)
479 line_w += glutStrokeWidth (GLUT_FONT, *s2);
481 x = (-ow/2) + ((ow-line_w)/2);
482 for (s2 = lstart; s2 < lend; s2++)
485 glTranslatef(x, y, 0);
486 off = fill_character (GLUT_FONT, *s2, wire);
503 draw_text (ModeInfo *mi)
505 text_configuration *tp = &tps[MI_SCREEN(mi)];
506 Display *dpy = MI_DISPLAY(mi);
507 Window window = MI_WINDOW(mi);
508 int wire = MI_IS_WIREFRAME(mi);
510 static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
511 static GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
513 if (!tp->glx_context)
516 if (strchr (text_fmt, '%'))
518 static time_t last_update = -1;
519 time_t now = time ((time_t *) 0);
520 if (now != last_update) /* do it once a second */
525 glShadeModel(GL_SMOOTH);
527 glEnable(GL_DEPTH_TEST);
528 glEnable(GL_NORMALIZE);
529 glEnable(GL_CULL_FACE);
531 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
535 glScalef(1.1, 1.1, 1.1);
539 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
540 glTranslatef((x - 0.5) * 8,
544 gltrackball_rotate (tp->trackball);
546 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
547 glRotatef (x * 360, 1.0, 0.0, 0.0);
548 glRotatef (y * 360, 0.0, 1.0, 0.0);
549 glRotatef (z * 360, 0.0, 0.0, 1.0);
555 color[0] = tp->colors[tp->ccolor].red / 65536.0;
556 color[1] = tp->colors[tp->ccolor].green / 65536.0;
557 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
559 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
561 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
563 glScalef(0.01, 0.01, 0.01);
565 fill_string(tp->text, wire);
569 if (mi->fps_p) do_fps (mi);
572 glXSwapBuffers(dpy, window);