http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.31.tar.gz
[xscreensaver] / hacks / glx / gltext.c
1 /* gltext, Copyright (c) 2001 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 sws_opts        xlockmore_opts
21
22 #define DEF_TEXT        "(default)"
23 #define DEF_SPIN        "XYZ"
24 #define DEF_WANDER      "True"
25
26 #define DEFAULTS        "*delay:        10000       \n" \
27                         "*showFPS:      False       \n" \
28                         "*wireframe:    False       \n" \
29                         "*spin:       " DEF_SPIN   "\n" \
30                         "*wander:     " DEF_WANDER "\n" \
31                         "*text:       " DEF_TEXT   "\n"
32
33
34 #define SMOOTH_TUBE       /* whether to have smooth or faceted tubes */
35
36 #ifdef SMOOTH_TUBE
37 # define TUBE_FACES  12   /* how densely to render tubes */
38 #else
39 # define TUBE_FACES  8
40 #endif
41
42
43 #undef countof
44 #define countof(x) (sizeof((x))/sizeof((*x)))
45
46 #include "xlockmore.h"
47 #include "colors.h"
48 #include "tube.h"
49 #include <ctype.h>
50
51 #ifdef USE_GL /* whole file */
52
53 #ifdef HAVE_UNAME
54 # include <sys/utsname.h>
55 #endif /* HAVE_UNAME */
56
57
58 #include <GL/glu.h>
59 #include "glutstroke.h"
60 #include "glut_roman.h"
61 #define GLUT_FONT (&glutStrokeRoman)
62
63
64 typedef struct {
65   GLXContext *glx_context;
66
67   GLfloat rotx, roty, rotz;        /* current object rotation */
68   GLfloat dx, dy, dz;              /* current rotational velocity */
69   GLfloat ddx, ddy, ddz;           /* current rotational acceleration */
70   GLfloat d_max;                   /* max velocity */
71
72   GLuint text_list;
73
74   int ncolors;
75   XColor *colors;
76   int ccolor;
77
78   Bool spin_x, spin_y, spin_z;
79   char *text;
80
81 } text_configuration;
82
83 static text_configuration *tps = NULL;
84
85 static char *text_fmt;
86 static char *do_spin;
87 static Bool do_wander;
88
89 static XrmOptionDescRec opts[] = {
90   { "-text",   ".text",   XrmoptionSepArg, 0 },
91   { "-spin",   ".spin",   XrmoptionSepArg, 0 },
92   { "+spin",   ".spin",   XrmoptionNoArg, "" },
93   { "-wander", ".wander", XrmoptionNoArg, "True" },
94   { "+wander", ".wander", XrmoptionNoArg, "False" }
95 };
96
97 static argtype vars[] = {
98   {(caddr_t *) &text_fmt,  "text",   "Text",   DEF_TEXT,   t_String},
99   {(caddr_t *) &do_spin,   "spin",   "Spin",   DEF_SPIN,   t_String},
100   {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
101 };
102
103 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
104
105
106 /* Window management, etc
107  */
108 void
109 reshape_text (ModeInfo *mi, int width, int height)
110 {
111   GLfloat h = (GLfloat) height / (GLfloat) width;
112
113   glViewport (0, 0, (GLint) width, (GLint) height);
114
115   glMatrixMode(GL_PROJECTION);
116   glLoadIdentity();
117
118   gluPerspective( 30.0, 1/h, 1.0, 100.0 );
119   gluLookAt( 0.0, 0.0, 15.0,
120              0.0, 0.0, 0.0,
121              0.0, 1.0, 0.0);
122   glMatrixMode(GL_MODELVIEW);
123   glLoadIdentity();
124   glTranslatef(0.0, 0.0, -15.0);
125
126   glClear(GL_COLOR_BUFFER_BIT);
127 }
128
129
130 static void
131 gl_init (ModeInfo *mi)
132 {
133   text_configuration *tp = &tps[MI_SCREEN(mi)];
134   int wire = MI_IS_WIREFRAME(mi);
135
136   static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
137
138   if (!wire)
139     {
140       glLightfv(GL_LIGHT0, GL_POSITION, pos);
141       glEnable(GL_CULL_FACE);
142       glEnable(GL_LIGHTING);
143       glEnable(GL_LIGHT0);
144       glEnable(GL_DEPTH_TEST);
145     }
146
147   tp->text_list = glGenLists (1);
148   glNewList (tp->text_list, GL_COMPILE);
149   glEndList ();
150 }
151
152
153 /* lifted from lament.c */
154 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
155 #define RANDSIGN() ((random() & 1) ? 1 : -1)
156
157 static void
158 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
159 {
160   double ppos = *pos;
161
162   /* tick position */
163   if (ppos < 0)
164     ppos = -(ppos + *v);
165   else
166     ppos += *v;
167
168   if (ppos > 1.0)
169     ppos -= 1.0;
170   else if (ppos < 0)
171     ppos += 1.0;
172
173   if (ppos < 0) abort();
174   if (ppos > 1.0) abort();
175   *pos = (*pos > 0 ? ppos : -ppos);
176
177   /* accelerate */
178   *v += *dv;
179
180   /* clamp velocity */
181   if (*v > max_v || *v < -max_v)
182     {
183       *dv = -*dv;
184     }
185   /* If it stops, start it going in the other direction. */
186   else if (*v < 0)
187     {
188       if (random() % 4)
189         {
190           *v = 0;
191
192           /* keep going in the same direction */
193           if (random() % 2)
194             *dv = 0;
195           else if (*dv < 0)
196             *dv = -*dv;
197         }
198       else
199         {
200           /* reverse gears */
201           *v = -*v;
202           *dv = -*dv;
203           *pos = -*pos;
204         }
205     }
206
207   /* Alter direction of rotational acceleration randomly. */
208   if (! (random() % 120))
209     *dv = -*dv;
210
211   /* Change acceleration very occasionally. */
212   if (! (random() % 200))
213     {
214       if (*dv == 0)
215         *dv = 0.00001;
216       else if (random() & 1)
217         *dv *= 1.2;
218       else
219         *dv *= 0.8;
220     }
221 }
222
223
224 static void
225 parse_text (ModeInfo *mi)
226 {
227   text_configuration *tp = &tps[MI_SCREEN(mi)];
228
229   if (tp->text) free (tp->text);
230
231   if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
232     {
233 # ifdef HAVE_UNAME
234       struct utsname uts;
235
236       if (uname (&uts) < 0)
237         {
238           tp->text = strdup("uname() failed");
239         }
240       else
241         {
242           char *s;
243           if ((s = strchr(uts.nodename, '.')))
244             *s = 0;
245           tp->text = (char *) malloc(strlen(uts.nodename) +
246                                      strlen(uts.sysname) +
247                                      strlen(uts.version) +
248                                      strlen(uts.release) + 10);
249 #  ifdef _AIX
250           sprintf(tp->text, "%s\n%s %s.%s",
251                   uts.nodename, uts.sysname, uts.version, uts.release);
252 #  else  /* !_AIX */
253           sprintf(tp->text, "%s\n%s %s",
254                   uts.nodename, uts.sysname, uts.release);
255 #  endif /* !_AIX */
256         }
257 # else  /* !HAVE_UNAME */
258 #  ifdef VMS
259       tp->text = strdup(getenv("SYS$NODE"));
260 #  else
261       tp->text = strdup("*  *\n*  *  *\nxscreensaver\n*  *  *\n*  *");
262 #  endif
263 # endif /* !HAVE_UNAME */
264     }
265   else if (!strchr (text_fmt, '%'))
266     {
267       tp->text = strdup (text_fmt);
268     }
269   else
270     {
271       time_t now = time ((time_t *) 0);
272       struct tm *tm = localtime (&now);
273       int L = strlen(text_fmt) + 100;
274       tp->text = (char *) malloc (L);
275       *tp->text = 0;
276       strftime (tp->text, L-1, text_fmt, tm);
277       if (!*tp->text)
278         sprintf (tp->text, "strftime error:\n%s", text_fmt);
279     }
280 }
281
282
283 void 
284 init_text (ModeInfo *mi)
285 {
286   text_configuration *tp;
287
288   if (!tps) {
289     tps = (text_configuration *)
290       calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
291     if (!tps) {
292       fprintf(stderr, "%s: out of memory\n", progname);
293       exit(1);
294     }
295
296     tp = &tps[MI_SCREEN(mi)];
297   }
298
299   tp = &tps[MI_SCREEN(mi)];
300
301   if ((tp->glx_context = init_GL(mi)) != NULL) {
302     gl_init(mi);
303     reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
304   }
305
306   tp->rotx = frand(1.0) * RANDSIGN();
307   tp->roty = frand(1.0) * RANDSIGN();
308   tp->rotz = frand(1.0) * RANDSIGN();
309
310   /* bell curve from 0-6 degrees, avg 3 */
311   tp->dx = (frand(1) + frand(1) + frand(1)) / (360/2);
312   tp->dy = (frand(1) + frand(1) + frand(1)) / (360/2);
313   tp->dz = (frand(1) + frand(1) + frand(1)) / (360/2);
314
315   tp->d_max = tp->dx * 2;
316
317   tp->ddx = 0.00006 + frand(0.00003);
318   tp->ddy = 0.00006 + frand(0.00003);
319   tp->ddz = 0.00006 + frand(0.00003);
320
321
322   tp->ncolors = 255;
323   tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
324   make_smooth_colormap (0, 0, 0,
325                         tp->colors, &tp->ncolors,
326                         False, 0, False);
327
328   parse_text (mi);
329
330   {
331     char *s = do_spin;
332     while (*s)
333       {
334         if      (*s == 'x' || *s == 'X') tp->spin_x = 1;
335         else if (*s == 'y' || *s == 'Y') tp->spin_y = 1;
336         else if (*s == 'z' || *s == 'Z') tp->spin_z = 1;
337         else
338           {
339             fprintf (stderr,
340          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
341                      progname, do_spin);
342             exit (1);
343           }
344         s++;
345       }
346   }
347
348 }
349
350
351 static int
352 fill_character (GLUTstrokeFont font, int c, Bool wire)
353 {
354   GLfloat tube_width = 10;
355
356   const StrokeCharRec *ch;
357   const StrokeRec *stroke;
358   const CoordRec *coord;
359   StrokeFontPtr fontinfo;
360   int i, j;
361
362   fontinfo = (StrokeFontPtr) font;
363
364   if (c < 0 || c >= fontinfo->num_chars)
365     return 0;
366   ch = &(fontinfo->ch[c]);
367   if (ch)
368     {
369       GLfloat lx, ly;
370       for (i = ch->num_strokes, stroke = ch->stroke;
371            i > 0; i--, stroke++) {
372         for (j = stroke->num_coords, coord = stroke->coord;
373              j > 0; j--, coord++)
374           {
375 # ifdef SMOOTH_TUBE
376             int smooth = True;
377 # else
378             int smooth = False;
379 # endif
380             if (j != stroke->num_coords)
381               tube (lx,       ly,       0,
382                     coord->x, coord->y, 0,
383                     tube_width,
384                     tube_width * 0.15,
385                     TUBE_FACES, smooth, wire);
386             lx = coord->x;
387             ly = coord->y;
388           }
389       }
390       return (int) (ch->right + tube_width/2);
391     }
392   return 0;
393 }
394
395
396 static int
397 text_extents (const char *string, int *wP, int *hP)
398 {
399   const char *s, *start;
400   int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
401   int lines = 0;
402   *wP = 0;
403   *hP = 0;
404   start = string;
405   s = start;
406   while (1)
407     if (*s == '\n' || *s == 0)
408       {
409         int w = 0;
410         while (start < s)
411           {
412             w += glutStrokeWidth(GLUT_FONT, *start);
413             start++;
414           }
415         start = s+1;
416
417         if (w > *wP) *wP = w;
418         *hP += line_height;
419         s++;
420         lines++;
421         if (*s == 0) break;
422       }
423     else
424       s++;
425
426   return lines;
427 }
428
429
430 static void
431 fill_string (const char *string, Bool wire)
432 {
433   const char *s, *start;
434   int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
435   int off;
436   GLfloat x = 0, y = 0;
437   int lines;
438
439   int ow, oh;
440   lines = text_extents (string, &ow, &oh);
441
442   y = oh / 2 - line_height;
443
444   start = string;
445   s = start;
446   while (1)
447     if (*s == '\n' || *s == 0)
448       {
449         int line_w = 0;
450         const char *s2;
451         const char *lstart = start;
452         const char *lend = s;
453
454         /* strip off whitespace at beginning and end of line
455            (since we're centering.) */
456         while (lend > lstart && isspace(lend[-1]))
457           lend--;
458         while (lstart < lend && isspace(*lstart))
459           lstart++;
460
461         for (s2 = lstart; s2 < lend; s2++)
462           line_w += glutStrokeWidth (GLUT_FONT, *s2);
463
464         x = (-ow/2) + ((ow-line_w)/2);
465         for (s2 = lstart; s2 < lend; s2++)
466           {
467             glPushMatrix();
468             glTranslatef(x, y, 0);
469             off = fill_character (GLUT_FONT, *s2, wire);
470             x += off;
471             glPopMatrix();
472           }
473
474         start = s+1;
475
476         y -= line_height;
477         if (*s == 0) break;
478         s++;
479       }
480     else
481       s++;
482 }
483
484
485 void
486 draw_text (ModeInfo *mi)
487 {
488   text_configuration *tp = &tps[MI_SCREEN(mi)];
489   Display *dpy = MI_DISPLAY(mi);
490   Window window = MI_WINDOW(mi);
491   int wire = MI_IS_WIREFRAME(mi);
492
493   static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
494   static GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
495
496   if (!tp->glx_context)
497     return;
498
499   if (strchr (text_fmt, '%'))
500     {
501       static time_t last_update = -1;
502       time_t now = time ((time_t *) 0);
503       if (now != last_update) /* do it once a second */
504         parse_text (mi);
505       last_update = now;
506     }
507
508   glShadeModel(GL_SMOOTH);
509
510   glEnable(GL_DEPTH_TEST);
511   glEnable(GL_NORMALIZE);
512   glEnable(GL_CULL_FACE);
513
514   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
515
516   glPushMatrix ();
517
518   glScalef(1.1, 1.1, 1.1);
519
520   {
521     GLfloat x, y, z;
522
523     if (do_wander)
524       {
525         static int frame = 0;
526
527 #       define SINOID(SCALE,SIZE) \
528         ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
529
530         x = SINOID(0.031, 9.0);
531         y = SINOID(0.023, 9.0);
532         z = SINOID(0.017, 9.0);
533         frame++;
534         glTranslatef(x, y, z);
535       }
536
537     if (tp->spin_x || tp->spin_y || tp->spin_z)
538       {
539         x = tp->rotx;
540         y = tp->roty;
541         z = tp->rotz;
542         if (x < 0) x = 1 - (x + 1);
543         if (y < 0) y = 1 - (y + 1);
544         if (z < 0) z = 1 - (z + 1);
545
546         if (tp->spin_x) glRotatef(x * 360, 1.0, 0.0, 0.0);
547         if (tp->spin_y) glRotatef(y * 360, 0.0, 1.0, 0.0);
548         if (tp->spin_z) glRotatef(z * 360, 0.0, 0.0, 1.0);
549
550         rotate(&tp->rotx, &tp->dx, &tp->ddx, tp->d_max);
551         rotate(&tp->roty, &tp->dy, &tp->ddy, tp->d_max);
552         rotate(&tp->rotz, &tp->dz, &tp->ddz, tp->d_max);
553       }
554   }
555
556   glColor4fv (white);
557
558   color[0] = tp->colors[tp->ccolor].red   / 65536.0;
559   color[1] = tp->colors[tp->ccolor].green / 65536.0;
560   color[2] = tp->colors[tp->ccolor].blue  / 65536.0;
561   tp->ccolor++;
562   if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
563
564   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
565
566   glScalef(0.01, 0.01, 0.01);
567
568   fill_string(tp->text, wire);
569
570   glPopMatrix ();
571
572   if (mi->fps_p) do_fps (mi);
573   glFinish();
574
575   glXSwapBuffers(dpy, window);
576 }
577
578 #endif /* USE_GL */