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