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