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: 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 == MotionNotify &&
272 gltrackball_track (tp->trackball,
273 event->xmotion.x, event->xmotion.y,
274 MI_WIDTH (mi), MI_HEIGHT (mi));
283 init_text (ModeInfo *mi)
285 text_configuration *tp;
288 # ifdef HAVE_SETLOCALE
289 setlocale (LC_TIME, ""); /* for strftime() calls */
293 tps = (text_configuration *)
294 calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
296 fprintf(stderr, "%s: out of memory\n", progname);
300 tp = &tps[MI_SCREEN(mi)];
303 tp = &tps[MI_SCREEN(mi)];
305 if ((tp->glx_context = init_GL(mi)) != NULL) {
307 reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
311 Bool spinx=False, spiny=False, spinz=False;
312 double spin_speed = 0.5;
313 double wander_speed = 0.02;
314 double spin_accel = 0.5;
319 if (*s == 'x' || *s == 'X') spinx = True;
320 else if (*s == 'y' || *s == 'Y') spiny = True;
321 else if (*s == 'z' || *s == 'Z') spinz = True;
325 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
332 tp->rot = make_rotator (spinx ? spin_speed : 0,
333 spiny ? spin_speed : 0,
334 spinz ? spin_speed : 0,
336 do_wander ? wander_speed : 0,
338 tp->trackball = gltrackball_init ();
342 tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
343 make_smooth_colormap (0, 0, 0,
344 tp->colors, &tp->ncolors,
347 /* brighter colors, please... */
348 for (i = 0; i < tp->ncolors; i++)
350 tp->colors[i].red = (tp->colors[i].red / 2) + 32767;
351 tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
352 tp->colors[i].blue = (tp->colors[i].blue / 2) + 32767;
361 fill_character (GLUTstrokeFont font, int c, Bool wire)
363 GLfloat tube_width = 10;
365 const StrokeCharRec *ch;
366 const StrokeRec *stroke;
367 const CoordRec *coord;
368 StrokeFontPtr fontinfo;
371 fontinfo = (StrokeFontPtr) font;
373 if (c < 0 || c >= fontinfo->num_chars)
375 ch = &(fontinfo->ch[c]);
379 for (i = ch->num_strokes, stroke = ch->stroke;
380 i > 0; i--, stroke++) {
381 for (j = stroke->num_coords, coord = stroke->coord;
389 if (j != stroke->num_coords)
391 coord->x, coord->y, 0,
394 TUBE_FACES, smooth, True, wire);
399 return (int) (ch->right + tube_width/2);
406 text_extents (const char *string, int *wP, int *hP)
408 const char *s, *start;
409 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
416 if (*s == '\n' || *s == 0)
421 w += glutStrokeWidth(GLUT_FONT, *start);
426 if (w > *wP) *wP = w;
440 fill_string (const char *string, Bool wire)
442 const char *s, *start;
443 int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
445 GLfloat x = 0, y = 0;
449 lines = text_extents (string, &ow, &oh);
451 y = oh / 2 - line_height;
456 if (*s == '\n' || *s == 0)
460 const char *lstart = start;
461 const char *lend = s;
463 /* strip off whitespace at beginning and end of line
464 (since we're centering.) */
465 while (lend > lstart && isspace(lend[-1]))
467 while (lstart < lend && isspace(*lstart))
470 for (s2 = lstart; s2 < lend; s2++)
471 line_w += glutStrokeWidth (GLUT_FONT, *s2);
473 x = (-ow/2) + ((ow-line_w)/2);
474 for (s2 = lstart; s2 < lend; s2++)
477 glTranslatef(x, y, 0);
478 off = fill_character (GLUT_FONT, *s2, wire);
495 draw_text (ModeInfo *mi)
497 text_configuration *tp = &tps[MI_SCREEN(mi)];
498 Display *dpy = MI_DISPLAY(mi);
499 Window window = MI_WINDOW(mi);
500 int wire = MI_IS_WIREFRAME(mi);
502 static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
503 static GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
505 if (!tp->glx_context)
508 if (strchr (text_fmt, '%'))
510 static time_t last_update = -1;
511 time_t now = time ((time_t *) 0);
512 if (now != last_update) /* do it once a second */
517 glShadeModel(GL_SMOOTH);
519 glEnable(GL_DEPTH_TEST);
520 glEnable(GL_NORMALIZE);
521 glEnable(GL_CULL_FACE);
523 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
527 glScalef(1.1, 1.1, 1.1);
531 get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
532 glTranslatef((x - 0.5) * 8,
536 gltrackball_rotate (tp->trackball);
538 get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
539 glRotatef (x * 360, 1.0, 0.0, 0.0);
540 glRotatef (y * 360, 0.0, 1.0, 0.0);
541 glRotatef (z * 360, 0.0, 0.0, 1.0);
547 color[0] = tp->colors[tp->ccolor].red / 65536.0;
548 color[1] = tp->colors[tp->ccolor].green / 65536.0;
549 color[2] = tp->colors[tp->ccolor].blue / 65536.0;
551 if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
553 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
555 glScalef(0.01, 0.01, 0.01);
557 fill_string(tp->text, wire);
561 if (mi->fps_p) do_fps (mi);
564 glXSwapBuffers(dpy, window);