ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / glx / gltext.c
1 /* gltext, Copyright (c) 2001, 2002, 2003, 2004 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:        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"
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   {&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},
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 /* The GLUT font only has ASCII characters in them, so do what we can to
156    convert Latin1 characters to the nearest ASCII equivalent... 
157  */
158 static void
159 latin1_to_ascii (char *s)
160 {
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' };
170   while (*us)
171     {
172       if (*us >= 161)
173         *us = ascii[*us - 161];
174       else if (*us > 127)
175         *us = '?';
176       us++;
177     }
178 }
179
180
181 static void
182 parse_text (ModeInfo *mi)
183 {
184   text_configuration *tp = &tps[MI_SCREEN(mi)];
185
186   if (tp->text) free (tp->text);
187
188   if (!text_fmt || !*text_fmt || !strcmp(text_fmt, "(default)"))
189     {
190 # ifdef HAVE_UNAME
191       struct utsname uts;
192
193       if (uname (&uts) < 0)
194         {
195           tp->text = strdup("uname() failed");
196         }
197       else
198         {
199           char *s;
200           if ((s = strchr(uts.nodename, '.')))
201             *s = 0;
202           tp->text = (char *) malloc(strlen(uts.nodename) +
203                                      strlen(uts.sysname) +
204                                      strlen(uts.version) +
205                                      strlen(uts.release) + 10);
206 #  if defined(_AIX)
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);
212 #  else
213           sprintf(tp->text, "%s\n%s %s",
214                   uts.nodename, uts.sysname, uts.release);
215 #  endif /* special system types */
216         }
217 # else  /* !HAVE_UNAME */
218 #  ifdef VMS
219       tp->text = strdup(getenv("SYS$NODE"));
220 #  else
221       tp->text = strdup("*  *\n*  *  *\nxscreensaver\n*  *  *\n*  *");
222 #  endif
223 # endif /* !HAVE_UNAME */
224     }
225   else if (!strchr (text_fmt, '%'))
226     {
227       tp->text = strdup (text_fmt);
228     }
229   else
230     {
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);
235       *tp->text = 0;
236       strftime (tp->text, L-1, text_fmt, tm);
237       if (!*tp->text)
238         sprintf (tp->text, "strftime error:\n%s", text_fmt);
239     }
240
241   latin1_to_ascii (tp->text);
242 }
243
244
245 Bool
246 text_handle_event (ModeInfo *mi, XEvent *event)
247 {
248   text_configuration *tp = &tps[MI_SCREEN(mi)];
249
250   if (event->xany.type == ButtonPress &&
251       event->xbutton.button & Button1)
252     {
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));
257       return True;
258     }
259   else if (event->xany.type == ButtonRelease &&
260            event->xbutton.button & Button1)
261     {
262       tp->button_down_p = False;
263       return True;
264     }
265   else if (event->xany.type == MotionNotify &&
266            tp->button_down_p)
267     {
268       gltrackball_track (tp->trackball,
269                          event->xmotion.x, event->xmotion.y,
270                          MI_WIDTH (mi), MI_HEIGHT (mi));
271       return True;
272     }
273
274   return False;
275 }
276
277
278 void 
279 init_text (ModeInfo *mi)
280 {
281   text_configuration *tp;
282   int i;
283
284   if (!tps) {
285     tps = (text_configuration *)
286       calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
287     if (!tps) {
288       fprintf(stderr, "%s: out of memory\n", progname);
289       exit(1);
290     }
291
292     tp = &tps[MI_SCREEN(mi)];
293   }
294
295   tp = &tps[MI_SCREEN(mi)];
296
297   if ((tp->glx_context = init_GL(mi)) != NULL) {
298     gl_init(mi);
299     reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
300   }
301
302   {
303     Bool spinx=False, spiny=False, spinz=False;
304     double spin_speed   = 0.5;
305     double wander_speed = 0.02;
306     double spin_accel   = 0.5;
307
308     char *s = do_spin;
309     while (*s)
310       {
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;
314         else
315           {
316             fprintf (stderr,
317          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
318                      progname, do_spin);
319             exit (1);
320           }
321         s++;
322       }
323
324     tp->rot = make_rotator (spinx ? spin_speed : 0,
325                             spiny ? spin_speed : 0,
326                             spinz ? spin_speed : 0,
327                             spin_accel,
328                             do_wander ? wander_speed : 0,
329                             False);
330     tp->trackball = gltrackball_init ();
331   }
332
333   tp->ncolors = 255;
334   tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
335   make_smooth_colormap (0, 0, 0,
336                         tp->colors, &tp->ncolors,
337                         False, 0, False);
338
339   /* brighter colors, please... */
340   for (i = 0; i < tp->ncolors; i++)
341     {
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;
345     }
346
347   parse_text (mi);
348
349 }
350
351
352 static int
353 fill_character (GLUTstrokeFont font, int c, Bool wire)
354 {
355   GLfloat tube_width = 10;
356
357   const StrokeCharRec *ch;
358   const StrokeRec *stroke;
359   const CoordRec *coord;
360   StrokeFontPtr fontinfo;
361   int i, j;
362
363   fontinfo = (StrokeFontPtr) font;
364
365   if (c < 0 || c >= fontinfo->num_chars)
366     return 0;
367   ch = &(fontinfo->ch[c]);
368   if (ch)
369     {
370       GLfloat lx=0, ly=0;
371       for (i = ch->num_strokes, stroke = ch->stroke;
372            i > 0; i--, stroke++) {
373         for (j = stroke->num_coords, coord = stroke->coord;
374              j > 0; j--, coord++)
375           {
376 # ifdef SMOOTH_TUBE
377             int smooth = True;
378 # else
379             int smooth = False;
380 # endif
381             if (j != stroke->num_coords)
382               tube (lx,       ly,       0,
383                     coord->x, coord->y, 0,
384                     tube_width,
385                     tube_width * 0.15,
386                     TUBE_FACES, smooth, True, wire);
387             lx = coord->x;
388             ly = coord->y;
389           }
390       }
391       return (int) (ch->right + tube_width/2);
392     }
393   return 0;
394 }
395
396
397 static int
398 text_extents (const char *string, int *wP, int *hP)
399 {
400   const char *s, *start;
401   int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
402   int lines = 0;
403   *wP = 0;
404   *hP = 0;
405   start = string;
406   s = start;
407   while (1)
408     if (*s == '\n' || *s == 0)
409       {
410         int w = 0;
411         while (start < s)
412           {
413             w += glutStrokeWidth(GLUT_FONT, *start);
414             start++;
415           }
416         start = s+1;
417
418         if (w > *wP) *wP = w;
419         *hP += line_height;
420         lines++;
421         if (*s == 0) break;
422         s++;
423       }
424     else
425       s++;
426
427   return lines;
428 }
429
430
431 static void
432 fill_string (const char *string, Bool wire)
433 {
434   const char *s, *start;
435   int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
436   int off;
437   GLfloat x = 0, y = 0;
438   int lines;
439
440   int ow, oh;
441   lines = text_extents (string, &ow, &oh);
442
443   y = oh / 2 - line_height;
444
445   start = string;
446   s = start;
447   while (1)
448     if (*s == '\n' || *s == 0)
449       {
450         int line_w = 0;
451         const char *s2;
452         const char *lstart = start;
453         const char *lend = s;
454
455         /* strip off whitespace at beginning and end of line
456            (since we're centering.) */
457         while (lend > lstart && isspace(lend[-1]))
458           lend--;
459         while (lstart < lend && isspace(*lstart))
460           lstart++;
461
462         for (s2 = lstart; s2 < lend; s2++)
463           line_w += glutStrokeWidth (GLUT_FONT, *s2);
464
465         x = (-ow/2) + ((ow-line_w)/2);
466         for (s2 = lstart; s2 < lend; s2++)
467           {
468             glPushMatrix();
469             glTranslatef(x, y, 0);
470             off = fill_character (GLUT_FONT, *s2, wire);
471             x += off;
472             glPopMatrix();
473           }
474
475         start = s+1;
476
477         y -= line_height;
478         if (*s == 0) break;
479         s++;
480       }
481     else
482       s++;
483 }
484
485
486 void
487 draw_text (ModeInfo *mi)
488 {
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);
493
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};
496
497   if (!tp->glx_context)
498     return;
499
500   if (strchr (text_fmt, '%'))
501     {
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 */
505         parse_text (mi);
506       last_update = now;
507     }
508
509   glShadeModel(GL_SMOOTH);
510
511   glEnable(GL_DEPTH_TEST);
512   glEnable(GL_NORMALIZE);
513   glEnable(GL_CULL_FACE);
514
515   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
516
517   glPushMatrix ();
518
519   glScalef(1.1, 1.1, 1.1);
520
521   {
522     double x, y, z;
523     get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
524     glTranslatef((x - 0.5) * 8,
525                  (y - 0.5) * 8,
526                  (z - 0.5) * 8);
527
528     gltrackball_rotate (tp->trackball);
529
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);
534   }
535
536
537   glColor4fv (white);
538
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;
542   tp->ccolor++;
543   if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
544
545   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
546
547   glScalef(0.01, 0.01, 0.01);
548
549   fill_string(tp->text, wire);
550
551   glPopMatrix ();
552
553   if (mi->fps_p) do_fps (mi);
554   glFinish();
555
556   glXSwapBuffers(dpy, window);
557 }
558
559 #endif /* USE_GL */