http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / glx / gltext.c
1 /* gltext, Copyright (c) 2001, 2002, 2003 Jamie Zawinski <jwz@jwz.org>
2  *
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 
9  * implied warranty.
10  */
11
12 #include <X11/Intrinsic.h>
13
14 extern XtAppContext app;
15
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
23
24 #define DEF_TEXT        "(default)"
25 #define DEF_SPIN        "XYZ"
26 #define DEF_WANDER      "True"
27
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"
34
35
36 #define SMOOTH_TUBE       /* whether to have smooth or faceted tubes */
37
38 #ifdef SMOOTH_TUBE
39 # define TUBE_FACES  12   /* how densely to render tubes */
40 #else
41 # define TUBE_FACES  8
42 #endif
43
44
45 #undef countof
46 #define countof(x) (sizeof((x))/sizeof((*x)))
47
48 #include "xlockmore.h"
49 #include "colors.h"
50 #include "tube.h"
51 #include "rotator.h"
52 #include "gltrackball.h"
53 #include <time.h>
54 #include <sys/time.h>
55 #include <ctype.h>
56
57 #ifdef USE_GL /* whole file */
58
59 #ifdef HAVE_UNAME
60 # include <sys/utsname.h>
61 #endif /* HAVE_UNAME */
62
63
64 #include <GL/glu.h>
65 #include "glutstroke.h"
66 #include "glut_roman.h"
67 #define GLUT_FONT (&glutStrokeRoman)
68
69
70 typedef struct {
71   GLXContext *glx_context;
72   rotator *rot;
73   trackball_state *trackball;
74   Bool button_down_p;
75
76   GLuint text_list;
77
78   int ncolors;
79   XColor *colors;
80   int ccolor;
81
82   char *text;
83
84 } text_configuration;
85
86 static text_configuration *tps = NULL;
87
88 static char *text_fmt;
89 static char *do_spin;
90 static Bool do_wander;
91
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" }
98 };
99
100 static argtype vars[] = {
101   {(caddr_t *) &text_fmt,  "text",   "Text",   DEF_TEXT,   t_String},
102   {(caddr_t *) &do_spin,   "spin",   "Spin",   DEF_SPIN,   t_String},
103   {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
104 };
105
106 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
107
108
109 /* Window management, etc
110  */
111 void
112 reshape_text (ModeInfo *mi, int width, int height)
113 {
114   GLfloat h = (GLfloat) height / (GLfloat) width;
115
116   glViewport (0, 0, (GLint) width, (GLint) height);
117
118   glMatrixMode(GL_PROJECTION);
119   glLoadIdentity();
120   gluPerspective (30.0, 1/h, 1.0, 100.0);
121
122   glMatrixMode(GL_MODELVIEW);
123   glLoadIdentity();
124   gluLookAt( 0.0, 0.0, 30.0,
125              0.0, 0.0, 0.0,
126              0.0, 1.0, 0.0);
127
128   glClear(GL_COLOR_BUFFER_BIT);
129 }
130
131
132 static void
133 gl_init (ModeInfo *mi)
134 {
135   text_configuration *tp = &tps[MI_SCREEN(mi)];
136   int wire = MI_IS_WIREFRAME(mi);
137
138   static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
139
140   if (!wire)
141     {
142       glLightfv(GL_LIGHT0, GL_POSITION, pos);
143       glEnable(GL_CULL_FACE);
144       glEnable(GL_LIGHTING);
145       glEnable(GL_LIGHT0);
146       glEnable(GL_DEPTH_TEST);
147     }
148
149   tp->text_list = glGenLists (1);
150   glNewList (tp->text_list, GL_COMPILE);
151   glEndList ();
152 }
153
154
155 static void
156 parse_text (ModeInfo *mi)
157 {
158   text_configuration *tp = &tps[MI_SCREEN(mi)];
159
160   if (tp->text) free (tp->text);
161
162   if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
163     {
164 # ifdef HAVE_UNAME
165       struct utsname uts;
166
167       if (uname (&uts) < 0)
168         {
169           tp->text = strdup("uname() failed");
170         }
171       else
172         {
173           char *s;
174           if ((s = strchr(uts.nodename, '.')))
175             *s = 0;
176           tp->text = (char *) malloc(strlen(uts.nodename) +
177                                      strlen(uts.sysname) +
178                                      strlen(uts.version) +
179                                      strlen(uts.release) + 10);
180 #  if defined(_AIX)
181           sprintf(tp->text, "%s\n%s %s.%s",
182                   uts.nodename, uts.sysname, uts.version, uts.release);
183 #  elif defined(__APPLE__)  /* MacOS X + XDarwin */
184           sprintf(tp->text, "%s\n%s %s\n%s",
185                   uts.nodename, uts.sysname, uts.release, uts.machine);
186 #  else
187           sprintf(tp->text, "%s\n%s %s",
188                   uts.nodename, uts.sysname, uts.release);
189 #  endif /* special system types */
190         }
191 # else  /* !HAVE_UNAME */
192 #  ifdef VMS
193       tp->text = strdup(getenv("SYS$NODE"));
194 #  else
195       tp->text = strdup("*  *\n*  *  *\nxscreensaver\n*  *  *\n*  *");
196 #  endif
197 # endif /* !HAVE_UNAME */
198     }
199   else if (!strchr (text_fmt, '%'))
200     {
201       tp->text = strdup (text_fmt);
202     }
203   else
204     {
205       time_t now = time ((time_t *) 0);
206       struct tm *tm = localtime (&now);
207       int L = strlen(text_fmt) + 100;
208       tp->text = (char *) malloc (L);
209       *tp->text = 0;
210       strftime (tp->text, L-1, text_fmt, tm);
211       if (!*tp->text)
212         sprintf (tp->text, "strftime error:\n%s", text_fmt);
213     }
214 }
215
216
217 Bool
218 text_handle_event (ModeInfo *mi, XEvent *event)
219 {
220   text_configuration *tp = &tps[MI_SCREEN(mi)];
221
222   if (event->xany.type == ButtonPress &&
223       event->xbutton.button & Button1)
224     {
225       tp->button_down_p = True;
226       gltrackball_start (tp->trackball,
227                          event->xbutton.x, event->xbutton.y,
228                          MI_WIDTH (mi), MI_HEIGHT (mi));
229       return True;
230     }
231   else if (event->xany.type == ButtonRelease &&
232            event->xbutton.button & Button1)
233     {
234       tp->button_down_p = False;
235       return True;
236     }
237   else if (event->xany.type == MotionNotify &&
238            tp->button_down_p)
239     {
240       gltrackball_track (tp->trackball,
241                          event->xmotion.x, event->xmotion.y,
242                          MI_WIDTH (mi), MI_HEIGHT (mi));
243       return True;
244     }
245
246   return False;
247 }
248
249
250 void 
251 init_text (ModeInfo *mi)
252 {
253   text_configuration *tp;
254   int i;
255
256   if (!tps) {
257     tps = (text_configuration *)
258       calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
259     if (!tps) {
260       fprintf(stderr, "%s: out of memory\n", progname);
261       exit(1);
262     }
263
264     tp = &tps[MI_SCREEN(mi)];
265   }
266
267   tp = &tps[MI_SCREEN(mi)];
268
269   if ((tp->glx_context = init_GL(mi)) != NULL) {
270     gl_init(mi);
271     reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
272   }
273
274   {
275     Bool spinx=False, spiny=False, spinz=False;
276     double spin_speed   = 1.0;
277     double wander_speed = 0.05;
278     double spin_accel   = 1.0;
279
280     char *s = do_spin;
281     while (*s)
282       {
283         if      (*s == 'x' || *s == 'X') spinx = True;
284         else if (*s == 'y' || *s == 'Y') spiny = True;
285         else if (*s == 'z' || *s == 'Z') spinz = True;
286         else
287           {
288             fprintf (stderr,
289          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
290                      progname, do_spin);
291             exit (1);
292           }
293         s++;
294       }
295
296     tp->rot = make_rotator (spinx ? spin_speed : 0,
297                             spiny ? spin_speed : 0,
298                             spinz ? spin_speed : 0,
299                             spin_accel,
300                             do_wander ? wander_speed : 0,
301                             False);
302     tp->trackball = gltrackball_init ();
303   }
304
305   tp->ncolors = 255;
306   tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
307   make_smooth_colormap (0, 0, 0,
308                         tp->colors, &tp->ncolors,
309                         False, 0, False);
310
311   /* brighter colors, please... */
312   for (i = 0; i < tp->ncolors; i++)
313     {
314       tp->colors[i].red   = (tp->colors[i].red   / 2) + 32767;
315       tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
316       tp->colors[i].blue  = (tp->colors[i].blue  / 2) + 32767;
317     }
318
319   parse_text (mi);
320
321 }
322
323
324 static int
325 fill_character (GLUTstrokeFont font, int c, Bool wire)
326 {
327   GLfloat tube_width = 10;
328
329   const StrokeCharRec *ch;
330   const StrokeRec *stroke;
331   const CoordRec *coord;
332   StrokeFontPtr fontinfo;
333   int i, j;
334
335   fontinfo = (StrokeFontPtr) font;
336
337   if (c < 0 || c >= fontinfo->num_chars)
338     return 0;
339   ch = &(fontinfo->ch[c]);
340   if (ch)
341     {
342       GLfloat lx=0, ly=0;
343       for (i = ch->num_strokes, stroke = ch->stroke;
344            i > 0; i--, stroke++) {
345         for (j = stroke->num_coords, coord = stroke->coord;
346              j > 0; j--, coord++)
347           {
348 # ifdef SMOOTH_TUBE
349             int smooth = True;
350 # else
351             int smooth = False;
352 # endif
353             if (j != stroke->num_coords)
354               tube (lx,       ly,       0,
355                     coord->x, coord->y, 0,
356                     tube_width,
357                     tube_width * 0.15,
358                     TUBE_FACES, smooth, True, wire);
359             lx = coord->x;
360             ly = coord->y;
361           }
362       }
363       return (int) (ch->right + tube_width/2);
364     }
365   return 0;
366 }
367
368
369 static int
370 text_extents (const char *string, int *wP, int *hP)
371 {
372   const char *s, *start;
373   int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
374   int lines = 0;
375   *wP = 0;
376   *hP = 0;
377   start = string;
378   s = start;
379   while (1)
380     if (*s == '\n' || *s == 0)
381       {
382         int w = 0;
383         while (start < s)
384           {
385             w += glutStrokeWidth(GLUT_FONT, *start);
386             start++;
387           }
388         start = s+1;
389
390         if (w > *wP) *wP = w;
391         *hP += line_height;
392         lines++;
393         if (*s == 0) break;
394         s++;
395       }
396     else
397       s++;
398
399   return lines;
400 }
401
402
403 static void
404 fill_string (const char *string, Bool wire)
405 {
406   const char *s, *start;
407   int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
408   int off;
409   GLfloat x = 0, y = 0;
410   int lines;
411
412   int ow, oh;
413   lines = text_extents (string, &ow, &oh);
414
415   y = oh / 2 - line_height;
416
417   start = string;
418   s = start;
419   while (1)
420     if (*s == '\n' || *s == 0)
421       {
422         int line_w = 0;
423         const char *s2;
424         const char *lstart = start;
425         const char *lend = s;
426
427         /* strip off whitespace at beginning and end of line
428            (since we're centering.) */
429         while (lend > lstart && isspace(lend[-1]))
430           lend--;
431         while (lstart < lend && isspace(*lstart))
432           lstart++;
433
434         for (s2 = lstart; s2 < lend; s2++)
435           line_w += glutStrokeWidth (GLUT_FONT, *s2);
436
437         x = (-ow/2) + ((ow-line_w)/2);
438         for (s2 = lstart; s2 < lend; s2++)
439           {
440             glPushMatrix();
441             glTranslatef(x, y, 0);
442             off = fill_character (GLUT_FONT, *s2, wire);
443             x += off;
444             glPopMatrix();
445           }
446
447         start = s+1;
448
449         y -= line_height;
450         if (*s == 0) break;
451         s++;
452       }
453     else
454       s++;
455 }
456
457
458 void
459 draw_text (ModeInfo *mi)
460 {
461   text_configuration *tp = &tps[MI_SCREEN(mi)];
462   Display *dpy = MI_DISPLAY(mi);
463   Window window = MI_WINDOW(mi);
464   int wire = MI_IS_WIREFRAME(mi);
465
466   static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
467   static GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
468
469   if (!tp->glx_context)
470     return;
471
472   if (strchr (text_fmt, '%'))
473     {
474       static time_t last_update = -1;
475       time_t now = time ((time_t *) 0);
476       if (now != last_update) /* do it once a second */
477         parse_text (mi);
478       last_update = now;
479     }
480
481   glShadeModel(GL_SMOOTH);
482
483   glEnable(GL_DEPTH_TEST);
484   glEnable(GL_NORMALIZE);
485   glEnable(GL_CULL_FACE);
486
487   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
488
489   glPushMatrix ();
490
491   glScalef(1.1, 1.1, 1.1);
492
493   {
494     double x, y, z;
495     get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
496     glTranslatef((x - 0.5) * 8,
497                  (y - 0.5) * 8,
498                  (z - 0.5) * 8);
499
500     gltrackball_rotate (tp->trackball);
501
502     get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
503     glRotatef (x * 360, 1.0, 0.0, 0.0);
504     glRotatef (y * 360, 0.0, 1.0, 0.0);
505     glRotatef (z * 360, 0.0, 0.0, 1.0);
506   }
507
508
509   glColor4fv (white);
510
511   color[0] = tp->colors[tp->ccolor].red   / 65536.0;
512   color[1] = tp->colors[tp->ccolor].green / 65536.0;
513   color[2] = tp->colors[tp->ccolor].blue  / 65536.0;
514   tp->ccolor++;
515   if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
516
517   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
518
519   glScalef(0.01, 0.01, 0.01);
520
521   fill_string(tp->text, wire);
522
523   glPopMatrix ();
524
525   if (mi->fps_p) do_fps (mi);
526   glFinish();
527
528   glXSwapBuffers(dpy, window);
529 }
530
531 #endif /* USE_GL */