1 /* gltext, Copyright (c) 2001, 2002, 2003, 2004 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: 10000 \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"
57 #ifdef USE_GL /* whole file */
60 # include <sys/utsname.h>
61 #endif /* HAVE_UNAME */
65 #include "glutstroke.h"
66 #include "glut_roman.h"
67 #define GLUT_FONT (&glutStrokeRoman)
71 GLXContext *glx_context;
73 trackball_state *trackball;
86 static text_configuration *tps = NULL;
88 static char *text_fmt;
90 static Bool do_wander;
92 static XrmOptionDescRec opts[] = {
93 { "-text", ".text", XrmoptionSepArg, 0 },
94 { "-spin", ".spin", XrmoptionSepArg, 0 },
95 { "+spin", ".spin", XrmoptionNoArg, "" },
96 { "-wander", ".wander", XrmoptionNoArg, "True" },
97 { "+wander", ".wander", XrmoptionNoArg, "False" }
100 static argtype vars[] = {
101 {&text_fmt, "text", "Text", DEF_TEXT, t_String},
102 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
103 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
106 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
109 /* Window management, etc
112 reshape_text (ModeInfo *mi, int width, int height)
114 GLfloat h = (GLfloat) height / (GLfloat) width;
116 glViewport (0, 0, (GLint) width, (GLint) height);
118 glMatrixMode(GL_PROJECTION);
120 gluPerspective (30.0, 1/h, 1.0, 100.0);
122 glMatrixMode(GL_MODELVIEW);
124 gluLookAt( 0.0, 0.0, 30.0,
128 glClear(GL_COLOR_BUFFER_BIT);
133 gl_init (ModeInfo *mi)
135 text_configuration *tp = &tps[MI_SCREEN(mi)];
136 int wire = MI_IS_WIREFRAME(mi);
138 static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
142 glLightfv(GL_LIGHT0, GL_POSITION, pos);
143 glEnable(GL_CULL_FACE);
144 glEnable(GL_LIGHTING);
146 glEnable(GL_DEPTH_TEST);
149 tp->text_list = glGenLists (1);
150 glNewList (tp->text_list, GL_COMPILE);
155 /* The GLUT font only has ASCII characters in them, so do what we can to
156 convert Latin1 characters to the nearest ASCII equivalent...
159 latin1_to_ascii (char *s)
161 unsigned char *us = (unsigned char *) s;
162 const unsigned char ascii[95] = {
163 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
164 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
165 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
166 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
167 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
168 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
169 'u', 'u', 'y', 'p', 'y' };
173 *us = ascii[*us - 161];
182 parse_text (ModeInfo *mi)
184 text_configuration *tp = &tps[MI_SCREEN(mi)];
186 if (tp->text) free (tp->text);
188 if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
193 if (uname (&uts) < 0)
195 tp->text = strdup("uname() failed");
200 if ((s = strchr(uts.nodename, '.')))
202 tp->text = (char *) malloc(strlen(uts.nodename) +
203 strlen(uts.sysname) +
204 strlen(uts.version) +
205 strlen(uts.release) + 10);
207 sprintf(tp->text, "%s\n%s %s.%s",
208 uts.nodename, uts.sysname, uts.version, uts.release);
209 # elif defined(__APPLE__) /* MacOS X + XDarwin */
210 sprintf(tp->text, "%s\n%s %s\n%s",
211 uts.nodename, uts.sysname, uts.release, uts.machine);
213 sprintf(tp->text, "%s\n%s %s",
214 uts.nodename, uts.sysname, uts.release);
215 # endif /* special system types */
217 # else /* !HAVE_UNAME */
219 tp->text = strdup(getenv("SYS$NODE"));
221 tp->text = strdup("* *\n* * *\nxscreensaver\n* * *\n* *");
223 # endif /* !HAVE_UNAME */
225 else if (!strchr (text_fmt, '%'))
227 tp->text = strdup (text_fmt);
231 time_t now = time ((time_t *) 0);
232 struct tm *tm = localtime (&now);
233 int L = strlen(text_fmt) + 100;
234 tp->text = (char *) malloc (L);
236 strftime (tp->text, L-1, text_fmt, tm);
238 sprintf (tp->text, "strftime error:\n%s", text_fmt);
241 latin1_to_ascii (tp->text);
246 text_handle_event (ModeInfo *mi, XEvent *event)
248 text_configuration *tp = &tps[MI_SCREEN(mi)];
250 if (event->xany.type == ButtonPress &&
251 event->xbutton.button & Button1)
253 tp->button_down_p = True;
254 gltrackball_start (tp->trackball,
255 event->xbutton.x, event->xbutton.y,
256 MI_WIDTH (mi), MI_HEIGHT (mi));
259 else if (event->xany.type == ButtonRelease &&
260 event->xbutton.button & Button1)
262 tp->button_down_p = False;
265 else if (event->xany.type == MotionNotify &&
268 gltrackball_track (tp->trackball,
269 event->xmotion.x, event->xmotion.y,
270 MI_WIDTH (mi), MI_HEIGHT (mi));
279 init_text (ModeInfo *mi)
281 text_configuration *tp;
285 tps = (text_configuration *)
286 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
288 fprintf(stderr, "%s: out of memory\n", progname);
292 tp = &tps[MI_SCREEN(mi)];
295 tp = &tps[MI_SCREEN(mi)];
297 if ((tp->glx_context = init_GL(mi)) != NULL) {
299 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
303 Bool spinx=False, spiny=False, spinz=False;
304 double spin_speed = 1.0;
305 double wander_speed = 0.05;
306 double spin_accel = 1.0;
311 if (*s == 'x' || *s == 'X') spinx = True;
312 else if (*s == 'y' || *s == 'Y') spiny = True;
313 else if (*s == 'z' || *s == 'Z') spinz = True;
317 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
324 tp->rot = make_rotator (spinx ? spin_speed : 0,
325 spiny ? spin_speed : 0,
326 spinz ? spin_speed : 0,
328 do_wander ? wander_speed : 0,
330 tp->trackball = gltrackball_init ();
334 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
335 make_smooth_colormap (0, 0, 0,
336 tp->colors, &tp->ncolors,
339 /* brighter colors, please... */
340 for (i = 0; i < tp->ncolors; i++)
342 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
343 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
344 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
353 fill_character (GLUTstrokeFont font, int c, Bool wire)
355 GLfloat tube_width = 10;
357 const StrokeCharRec *ch;
358 const StrokeRec *stroke;
359 const CoordRec *coord;
360 StrokeFontPtr fontinfo;
363 fontinfo = (StrokeFontPtr) font;
365 if (c < 0 || c >= fontinfo->num_chars)
367 ch = &(fontinfo->ch[c]);
371 for (i = ch->num_strokes, stroke = ch->stroke;
372 i > 0; i--, stroke++) {
373 for (j = stroke->num_coords, coord = stroke->coord;
381 if (j != stroke->num_coords)
383 coord->x, coord->y, 0,
386 TUBE_FACES, smooth, True, wire);
391 return (int) (ch->right + tube_width/2);
398 text_extents (const char *string, int *wP, int *hP)
400 const char *s, *start;
401 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
408 if (*s == '\n' || *s == 0)
413 w += glutStrokeWidth(GLUT_FONT, *start);
418 if (w > *wP) *wP = w;
432 fill_string (const char *string, Bool wire)
434 const char *s, *start;
435 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
437 GLfloat x = 0, y = 0;
441 lines = text_extents (string, &ow, &oh);
443 y = oh / 2 - line_height;
448 if (*s == '\n' || *s == 0)
452 const char *lstart = start;
453 const char *lend = s;
455 /* strip off whitespace at beginning and end of line
456 (since we're centering.) */
457 while (lend > lstart && isspace(lend[-1]))
459 while (lstart < lend && isspace(*lstart))
462 for (s2 = lstart; s2 < lend; s2++)
463 line_w += glutStrokeWidth (GLUT_FONT, *s2);
465 x = (-ow/2) + ((ow-line_w)/2);
466 for (s2 = lstart; s2 < lend; s2++)
469 glTranslatef(x, y, 0);
470 off = fill_character (GLUT_FONT, *s2, wire);
487 draw_text (ModeInfo *mi)
489 text_configuration *tp = &tps[MI_SCREEN(mi)];
490 Display *dpy = MI_DISPLAY(mi);
491 Window window = MI_WINDOW(mi);
492 int wire = MI_IS_WIREFRAME(mi);
494 static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
495 static GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
497 if (!tp->glx_context)
500 if (strchr (text_fmt, '%'))
502 static time_t last_update = -1;
503 time_t now = time ((time_t *) 0);
504 if (now != last_update) /* do it once a second */
509 glShadeModel(GL_SMOOTH);
511 glEnable(GL_DEPTH_TEST);
512 glEnable(GL_NORMALIZE);
513 glEnable(GL_CULL_FACE);
515 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
519 glScalef(1.1, 1.1, 1.1);
523 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
524 glTranslatef((x - 0.5) * 8,
528 gltrackball_rotate (tp->trackball);
530 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
531 glRotatef (x * 360, 1.0, 0.0, 0.0);
532 glRotatef (y * 360, 0.0, 1.0, 0.0);
533 glRotatef (z * 360, 0.0, 0.0, 1.0);
539 color[0] = tp->colors[tp->ccolor].red / 65536.0;
540 color[1] = tp->colors[tp->ccolor].green / 65536.0;
541 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
543 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
545 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
547 glScalef(0.01, 0.01, 0.01);
549 fill_string(tp->text, wire);
553 if (mi->fps_p) do_fps (mi);
556 glXSwapBuffers(dpy, window);