http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.04.2.tar.gz
[xscreensaver] / hacks / glx / gltext.c
1 /* gltext, Copyright (c) 2001, 2002 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, "False" },
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 #  ifdef _AIX
181           sprintf(tp->text, "%s\n%s %s.%s",
182                   uts.nodename, uts.sysname, uts.version, uts.release);
183 #  else  /* !_AIX */
184           sprintf(tp->text, "%s\n%s %s",
185                   uts.nodename, uts.sysname, uts.release);
186 #  endif /* !_AIX */
187         }
188 # else  /* !HAVE_UNAME */
189 #  ifdef VMS
190       tp->text = strdup(getenv("SYS$NODE"));
191 #  else
192       tp->text = strdup("*  *\n*  *  *\nxscreensaver\n*  *  *\n*  *");
193 #  endif
194 # endif /* !HAVE_UNAME */
195     }
196   else if (!strchr (text_fmt, '%'))
197     {
198       tp->text = strdup (text_fmt);
199     }
200   else
201     {
202       time_t now = time ((time_t *) 0);
203       struct tm *tm = localtime (&now);
204       int L = strlen(text_fmt) + 100;
205       tp->text = (char *) malloc (L);
206       *tp->text = 0;
207       strftime (tp->text, L-1, text_fmt, tm);
208       if (!*tp->text)
209         sprintf (tp->text, "strftime error:\n%s", text_fmt);
210     }
211 }
212
213
214 Bool
215 text_handle_event (ModeInfo *mi, XEvent *event)
216 {
217   text_configuration *tp = &tps[MI_SCREEN(mi)];
218
219   if (event->xany.type == ButtonPress &&
220       event->xbutton.button & Button1)
221     {
222       tp->button_down_p = True;
223       gltrackball_start (tp->trackball,
224                          event->xbutton.x, event->xbutton.y,
225                          MI_WIDTH (mi), MI_HEIGHT (mi));
226       return True;
227     }
228   else if (event->xany.type == ButtonRelease &&
229            event->xbutton.button & Button1)
230     {
231       tp->button_down_p = False;
232       return True;
233     }
234   else if (event->xany.type == MotionNotify &&
235            tp->button_down_p)
236     {
237       gltrackball_track (tp->trackball,
238                          event->xmotion.x, event->xmotion.y,
239                          MI_WIDTH (mi), MI_HEIGHT (mi));
240       return True;
241     }
242
243   return False;
244 }
245
246
247 void 
248 init_text (ModeInfo *mi)
249 {
250   text_configuration *tp;
251   int i;
252
253   if (!tps) {
254     tps = (text_configuration *)
255       calloc (MI_NUM_SCREENS(mi), sizeof (text_configuration));
256     if (!tps) {
257       fprintf(stderr, "%s: out of memory\n", progname);
258       exit(1);
259     }
260
261     tp = &tps[MI_SCREEN(mi)];
262   }
263
264   tp = &tps[MI_SCREEN(mi)];
265
266   if ((tp->glx_context = init_GL(mi)) != NULL) {
267     gl_init(mi);
268     reshape_text (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
269   }
270
271   {
272     double spin_speed   = 1.0;
273     double wander_speed = 0.05;
274     double spin_accel   = 1.0;
275
276     tp->rot = make_rotator (do_spin ? spin_speed : 0,
277                             do_spin ? spin_speed : 0,
278                             do_spin ? spin_speed : 0,
279                             spin_accel,
280                             do_wander ? wander_speed : 0,
281                             True);
282     tp->trackball = gltrackball_init ();
283   }
284
285   tp->ncolors = 255;
286   tp->colors = (XColor *) calloc(tp->ncolors, sizeof(XColor));
287   make_smooth_colormap (0, 0, 0,
288                         tp->colors, &tp->ncolors,
289                         False, 0, False);
290
291   /* brighter colors, please... */
292   for (i = 0; i < tp->ncolors; i++)
293     {
294       tp->colors[i].red   = (tp->colors[i].red   / 2) + 32767;
295       tp->colors[i].green = (tp->colors[i].green / 2) + 32767;
296       tp->colors[i].blue  = (tp->colors[i].blue  / 2) + 32767;
297     }
298
299   parse_text (mi);
300
301 }
302
303
304 static int
305 fill_character (GLUTstrokeFont font, int c, Bool wire)
306 {
307   GLfloat tube_width = 10;
308
309   const StrokeCharRec *ch;
310   const StrokeRec *stroke;
311   const CoordRec *coord;
312   StrokeFontPtr fontinfo;
313   int i, j;
314
315   fontinfo = (StrokeFontPtr) font;
316
317   if (c < 0 || c >= fontinfo->num_chars)
318     return 0;
319   ch = &(fontinfo->ch[c]);
320   if (ch)
321     {
322       GLfloat lx=0, ly=0;
323       for (i = ch->num_strokes, stroke = ch->stroke;
324            i > 0; i--, stroke++) {
325         for (j = stroke->num_coords, coord = stroke->coord;
326              j > 0; j--, coord++)
327           {
328 # ifdef SMOOTH_TUBE
329             int smooth = True;
330 # else
331             int smooth = False;
332 # endif
333             if (j != stroke->num_coords)
334               tube (lx,       ly,       0,
335                     coord->x, coord->y, 0,
336                     tube_width,
337                     tube_width * 0.15,
338                     TUBE_FACES, smooth, wire);
339             lx = coord->x;
340             ly = coord->y;
341           }
342       }
343       return (int) (ch->right + tube_width/2);
344     }
345   return 0;
346 }
347
348
349 static int
350 text_extents (const char *string, int *wP, int *hP)
351 {
352   const char *s, *start;
353   int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
354   int lines = 0;
355   *wP = 0;
356   *hP = 0;
357   start = string;
358   s = start;
359   while (1)
360     if (*s == '\n' || *s == 0)
361       {
362         int w = 0;
363         while (start < s)
364           {
365             w += glutStrokeWidth(GLUT_FONT, *start);
366             start++;
367           }
368         start = s+1;
369
370         if (w > *wP) *wP = w;
371         *hP += line_height;
372         s++;
373         lines++;
374         if (*s == 0) break;
375       }
376     else
377       s++;
378
379   return lines;
380 }
381
382
383 static void
384 fill_string (const char *string, Bool wire)
385 {
386   const char *s, *start;
387   int line_height = GLUT_FONT->top - GLUT_FONT->bottom;
388   int off;
389   GLfloat x = 0, y = 0;
390   int lines;
391
392   int ow, oh;
393   lines = text_extents (string, &ow, &oh);
394
395   y = oh / 2 - line_height;
396
397   start = string;
398   s = start;
399   while (1)
400     if (*s == '\n' || *s == 0)
401       {
402         int line_w = 0;
403         const char *s2;
404         const char *lstart = start;
405         const char *lend = s;
406
407         /* strip off whitespace at beginning and end of line
408            (since we're centering.) */
409         while (lend > lstart && isspace(lend[-1]))
410           lend--;
411         while (lstart < lend && isspace(*lstart))
412           lstart++;
413
414         for (s2 = lstart; s2 < lend; s2++)
415           line_w += glutStrokeWidth (GLUT_FONT, *s2);
416
417         x = (-ow/2) + ((ow-line_w)/2);
418         for (s2 = lstart; s2 < lend; s2++)
419           {
420             glPushMatrix();
421             glTranslatef(x, y, 0);
422             off = fill_character (GLUT_FONT, *s2, wire);
423             x += off;
424             glPopMatrix();
425           }
426
427         start = s+1;
428
429         y -= line_height;
430         if (*s == 0) break;
431         s++;
432       }
433     else
434       s++;
435 }
436
437
438 void
439 draw_text (ModeInfo *mi)
440 {
441   text_configuration *tp = &tps[MI_SCREEN(mi)];
442   Display *dpy = MI_DISPLAY(mi);
443   Window window = MI_WINDOW(mi);
444   int wire = MI_IS_WIREFRAME(mi);
445
446   static GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
447   static GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
448
449   if (!tp->glx_context)
450     return;
451
452   if (strchr (text_fmt, '%'))
453     {
454       static time_t last_update = -1;
455       time_t now = time ((time_t *) 0);
456       if (now != last_update) /* do it once a second */
457         parse_text (mi);
458       last_update = now;
459     }
460
461   glShadeModel(GL_SMOOTH);
462
463   glEnable(GL_DEPTH_TEST);
464   glEnable(GL_NORMALIZE);
465   glEnable(GL_CULL_FACE);
466
467   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
468
469   glPushMatrix ();
470
471   glScalef(1.1, 1.1, 1.1);
472
473   {
474     double x, y, z;
475     get_position (tp->rot, &x, &y, &z, !tp->button_down_p);
476     glTranslatef((x - 0.5) * 8,
477                  (y - 0.5) * 8,
478                  (z - 0.5) * 8);
479
480     gltrackball_rotate (tp->trackball);
481
482     get_rotation (tp->rot, &x, &y, &z, !tp->button_down_p);
483     glRotatef (x * 360, 1.0, 0.0, 0.0);
484     glRotatef (y * 360, 0.0, 1.0, 0.0);
485     glRotatef (z * 360, 0.0, 0.0, 1.0);
486   }
487
488
489   glColor4fv (white);
490
491   color[0] = tp->colors[tp->ccolor].red   / 65536.0;
492   color[1] = tp->colors[tp->ccolor].green / 65536.0;
493   color[2] = tp->colors[tp->ccolor].blue  / 65536.0;
494   tp->ccolor++;
495   if (tp->ccolor >= tp->ncolors) tp->ccolor = 0;
496
497   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
498
499   glScalef(0.01, 0.01, 0.01);
500
501   fill_string(tp->text, wire);
502
503   glPopMatrix ();
504
505   if (mi->fps_p) do_fps (mi);
506   glFinish();
507
508   glXSwapBuffers(dpy, window);
509 }
510
511 #endif /* USE_GL */