http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.29.tar.gz
[xscreensaver] / hacks / glx / starwars.c
1 /*
2  * starwars, Copyright (c) 1998-2001 Jamie Zawinski <jwz@jwz.org> and
3  * Claudio Matauoka <claudio@helllabs.org>
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or 
11  * implied warranty.
12  *
13  * Star Wars -- Phosphor meets a well-known scroller from a galaxy far,
14  *           far away. Hacked by Claudio Matsuoka. Includes portions of
15  *           mjk's GLUT library, Copyright (c) 1994, 1995, 1996 by Mark J.
16  *           Kilgard. Roman simplex stroke font Copyright (c) 1989, 1990,
17  *           1991 by Sun Microsystems, Inc. and the X Consortium.
18  *
19  *      Notes:
20  *         - I tried texturized fonts but the roman simplex stroke font
21  *           was the most readable for the 80-column text from fortune.
22  *         - The proportional font is bad for text from ps(1) or w(1).
23  *         - Apparently the RIVA TNT cards for PCs don't like the stars to
24  *           be drawn in orthogonal perspective, causing unnecessary system
25  *           load.
26  *
27  *      History:
28  *           20000221 claudio   First version
29  *           20010124 jwz       Rewrote large sections to add the ability to
30  *                              run a subprocess, customization of the font
31  *                              size and other parameters, etc.
32  */
33
34 #include <X11/Intrinsic.h>
35
36 extern XtAppContext app;
37
38 #define PROGCLASS       "StarWars"
39 #define HACK_INIT       init_sws
40 #define HACK_DRAW       draw_sws
41 #define HACK_RESHAPE    reshape_sws
42 #define sws_opts        xlockmore_opts
43
44 #define DEF_PROGRAM    ZIPPY_PROGRAM
45 #define DEF_LINES      "500"
46 #define DEF_STEPS      "35"
47 #define DEF_SPIN       "0.03"
48 #define DEF_FONT_SIZE  "-1"
49 #define DEF_COLUMNS    "-1"
50 #define DEF_WRAP       "True"
51 #define DEF_ALIGN      "Center"
52
53 #define TAB_WIDTH        8
54
55 #define BASE_FONT_SIZE    18 /* magic */
56 #define BASE_FONT_COLUMNS 80 /* magic */
57
58
59 #define DEFAULTS        "*delay:        40000 \n"                    \
60                         "*showFPS:      False \n"                    \
61                         "*fpsTop:       True \n"                     \
62                         "*program:      " DEF_PROGRAM           "\n" \
63                         "*lines:        " DEF_LINES             "\n" \
64                         "*spin:         " DEF_SPIN              "\n" \
65                         "*steps:        " DEF_STEPS             "\n" \
66                         "*starwars.fontSize: " DEF_FONT_SIZE    "\n" \
67                         "*starwars.columns:  " DEF_COLUMNS      "\n" \
68                         "*starwars.lineWrap: " DEF_WRAP         "\n" \
69                         "*starwars.alignment:" DEF_ALIGN        "\n"
70
71 #undef countof
72 #define countof(x) (sizeof((x))/sizeof((*x)))
73
74 #include "xlockmore.h"
75
76 #ifdef USE_GL /* whole file */
77
78 #include <GL/glu.h>
79 #include "glutstroke.h"
80 #include "glut_roman.h"
81 #define GLUT_FONT (&glutStrokeRoman)
82
83
84 typedef struct {
85   GLXContext *glx_context;
86
87   GLuint text_list, star_list;
88
89   FILE *pipe;
90   XtInputId pipe_id;
91   Time subproc_relaunch_delay;
92
93   char buf [1024];
94   int buf_tail;
95   char **lines;
96   int total_lines;
97   int columns;
98
99   double star_theta;
100   double line_height;
101   double font_scale;
102   double intra_line_scroll;
103
104 } sws_configuration;
105
106
107 static sws_configuration *scs = NULL;
108
109 static char *program;
110 static int max_lines;
111 static int scroll_steps;
112 static float star_spin;
113 static float font_size;
114 static int target_columns;
115 static int wrap_p;
116 static char *alignment_str;
117 static int alignment;
118
119 static XrmOptionDescRec opts[] = {
120   {"-program",   ".starwars.program",  XrmoptionSepArg, (caddr_t) 0 },
121   {"-lines",     ".starwars.lines",    XrmoptionSepArg, (caddr_t) 0 },
122   {"-steps",     ".starwars.steps",    XrmoptionSepArg, (caddr_t) 0 },
123   {"-spin",      ".starwars.spin",     XrmoptionSepArg, (caddr_t) 0 },
124   {"-size",      ".starwars.fontSize", XrmoptionSepArg, (caddr_t) 0 },
125   {"-columns",   ".starwars.columns",  XrmoptionSepArg, (caddr_t) 0 },
126   {"-wrap",      ".starwars.lineWrap", XrmoptionNoArg,  (caddr_t) "True" },
127   {"-no-wrap",   ".starwars.lineWrap", XrmoptionNoArg,  (caddr_t) "False" },
128   {"-nowrap",    ".starwars.lineWrap", XrmoptionNoArg,  (caddr_t) "False" },
129   {"-left",      ".starwars.alignment",XrmoptionNoArg,  (caddr_t) "Left" },
130   {"-right",     ".starwars.alignment",XrmoptionNoArg,  (caddr_t) "Right" },
131   {"-center",    ".starwars.alignment",XrmoptionNoArg,  (caddr_t) "Center" },
132 };
133
134 static argtype vars[] = {
135   {(caddr_t *) &program,        "program", "Program", DEF_PROGRAM, t_String},
136   {(caddr_t *) &max_lines,      "lines",   "Integer", DEF_LINES,   t_Int},
137   {(caddr_t *) &scroll_steps,   "steps",   "Integer", DEF_STEPS,   t_Int},
138   {(caddr_t *) &star_spin,      "spin",    "Float",   DEF_SPIN,    t_Float},
139   {(caddr_t *) &font_size,      "fontSize","Float",   DEF_STEPS,   t_Float},
140   {(caddr_t *) &target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
141   {(caddr_t *) &wrap_p,         "lineWrap","Boolean", DEF_COLUMNS, t_Bool},
142   {(caddr_t *) &alignment_str,  "alignment","Alignment",DEF_ALIGN, t_String},
143 };
144
145 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
146
147
148
149 /* Tabs are bad, mmmkay? */
150
151 static char *
152 untabify (const char *string)
153 {
154   char *result = (char *) malloc ((strlen(string) * 8) + 1);
155   char *out = result;
156   int col = 0;
157   while (*string)
158     {
159       if (*string == '\t')
160         {
161           do {
162             col++;
163             *out++ = ' ';
164           } while (col % TAB_WIDTH);
165           string++;
166         }
167       else if (*string == '\r' || *string == '\n')
168         {
169           *out++ = *string++;
170           col = 0;
171         }
172       else
173         {
174           *out++ = *string++;
175           col++;
176         }
177     }
178   *out = 0;
179   return result;
180 }
181
182
183 \f
184 /* Subprocess.
185    (This bit mostly cribbed from phosphor.c)
186  */
187
188 static void drain_input (sws_configuration *sc);
189
190 static void
191 subproc_cb (XtPointer closure, int *source, XtInputId *id)
192 {
193   sws_configuration *sc = (sws_configuration *) closure;
194   drain_input (sc);
195 }
196
197
198 static void
199 launch_text_generator (sws_configuration *sc)
200 {
201   char *oprogram = get_string_resource ("program", "Program");
202   char *program = (char *) malloc (strlen (oprogram) + 10);
203
204   strcpy (program, "( ");
205   strcat (program, oprogram);
206   strcat (program, " ) 2>&1");
207
208   if ((sc->pipe = popen (program, "r")))
209     {
210       sc->pipe_id =
211         XtAppAddInput (app, fileno (sc->pipe),
212                        (XtPointer) (XtInputReadMask | XtInputExceptMask),
213                        subproc_cb, (XtPointer) sc);
214     }
215   else
216     {
217       perror (program);
218     }
219 }
220
221
222 static void
223 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
224 {
225   sws_configuration *sc = (sws_configuration *) closure;
226   launch_text_generator (sc);
227 }
228
229
230 /* When the subprocess has generated some output, this reads as much as it
231    can into sc->buf at sc->buf_tail.
232  */
233 static void
234 drain_input (sws_configuration *sc)
235 {
236   if (sc->buf_tail < sizeof(sc->buf) - 2)
237     {
238       int target = sizeof(sc->buf) - sc->buf_tail - 2;
239       int n = read (fileno (sc->pipe),
240                     (void *) (sc->buf + sc->buf_tail),
241                     target);
242       if (n > 0)
243         {
244           sc->buf_tail += n;
245           sc->buf[sc->buf_tail] = 0;
246         }
247       else
248         {
249           XtRemoveInput (sc->pipe_id);
250           sc->pipe_id = 0;
251           pclose (sc->pipe);
252           sc->pipe = 0;
253
254           /* If the process didn't print a terminating newline, add one. */
255           if (sc->buf_tail > 1 &&
256               sc->buf[sc->buf_tail-1] != '\n')
257             {
258               sc->buf[sc->buf_tail++] = '\n';
259               sc->buf[sc->buf_tail] = 0;
260             }
261
262           /* Then add one more, just for giggles. */
263           sc->buf[sc->buf_tail++] = '\n';
264           sc->buf[sc->buf_tail] = 0;
265
266           /* Set up a timer to re-launch the subproc in a bit. */
267           XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
268                            relaunch_generator_timer,
269                            (XtPointer) sc);
270         }
271     }
272 }
273
274
275 /* Populates the sc->lines list with as many lines as are currently in
276    sc->buf (which was filled by drain_input().
277  */
278 static void
279 get_more_lines (sws_configuration *sc)
280 {
281   int col = 0;
282   char *s = sc->buf;
283   while (sc->total_lines < max_lines)
284     {
285       if (s >= sc->buf + sc->buf_tail)
286         {
287           /* Reached end of buffer before end of line.  Bail. */
288           return;
289         }
290
291       if (*s == '\n' || col > sc->columns)
292         {
293           int L = s - sc->buf;
294
295           if (*s == '\n')
296             *s++ = 0;
297           else
298             {
299               /* We wrapped -- try to back up to the previous word boundary. */
300               char *s2 = s;
301               int n = 0;
302               while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
303                 s2--, n++;
304               if (s2 > sc->buf)
305                 {
306                   s = s2;
307                   *s++ = 0;
308                   L = s - sc->buf;
309                 }
310             }
311
312           sc->lines[sc->total_lines] = (char *) malloc (L+1);
313           memcpy (sc->lines[sc->total_lines], sc->buf, L);
314           sc->lines[sc->total_lines][L] = 0;
315
316           {
317             char *t = sc->lines[sc->total_lines];
318             char *ut = untabify (t);
319             sc->lines[sc->total_lines] = ut;
320             free (t);
321           }
322
323           sc->total_lines++;
324
325           if (sc->buf_tail > (s - sc->buf))
326             {
327               int i = sc->buf_tail - (s - sc->buf);
328               memcpy (sc->buf, s, i);
329               sc->buf_tail = i;
330               sc->buf[sc->buf_tail] = 0;
331             }
332           else
333             {
334               sc->buf_tail = 0;
335             }
336
337           sc->buf[sc->buf_tail] = 0;
338           s = sc->buf;
339           col = 0;
340         }
341       else
342         {
343           col++;
344           if (*s == '\t')
345             col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
346           s++;
347         }
348     }
349 }
350
351
352 static void
353 draw_string (int x, int y, const char *s)
354 {
355   if (!s || !*s) return;
356   glPushMatrix ();
357   glTranslatef (x, y, 0);
358   while (*s)
359     glutStrokeCharacter (GLUT_FONT, *s++);
360   glPopMatrix ();
361 }
362
363
364 #if 0
365 static void
366 grid (double width, double height, double spacing, double z)
367 {
368   double x, y;
369   for (y = 0; y <= height/2; y += spacing)
370     {
371       glBegin(GL_LINE_LOOP);
372       glVertex3f(-width/2,  y, z);
373       glVertex3f( width/2,  y, z);
374       glEnd();
375       glBegin(GL_LINE_LOOP);
376       glVertex3f(-width/2, -y, z);
377       glVertex3f( width/2, -y, z);
378       glEnd();
379     }
380   for (x = 0; x <= width/2; x += spacing)
381     {
382       glBegin(GL_LINE_LOOP);
383       glVertex3f( x, -height/2, z);
384       glVertex3f( x,  height/2, z);
385       glEnd();
386       glBegin(GL_LINE_LOOP);
387       glVertex3f(-x, -height/2, z);
388       glVertex3f(-x,  height/2, z);
389       glEnd();
390     }
391
392   glBegin(GL_LINE_LOOP);
393   glVertex3f(-width, 0, z);
394   glVertex3f( width, 0, z);
395   glEnd();
396   glBegin(GL_LINE_LOOP);
397   glVertex3f(0, -height, z);
398   glVertex3f(0,  height, z);
399   glEnd();
400 }
401
402 static void
403 box (double width, double height, double depth)
404 {
405   glBegin(GL_LINE_LOOP);
406   glVertex3f(-width/2,  -height/2, -depth/2);
407   glVertex3f(-width/2,   height/2, -depth/2);
408   glVertex3f( width/2,   height/2, -depth/2);
409   glVertex3f( width/2,  -height/2, -depth/2);
410   glEnd();
411   glBegin(GL_LINE_LOOP);
412   glVertex3f(-width/2,  -height/2,  depth/2);
413   glVertex3f(-width/2,   height/2,  depth/2);
414   glVertex3f( width/2,   height/2,  depth/2);
415   glVertex3f( width/2,  -height/2,  depth/2);
416   glEnd();
417   glBegin(GL_LINE_LOOP);
418   glVertex3f(-width/2,  -height/2, -depth/2);
419   glVertex3f(-width/2,  -height/2,  depth/2);
420   glVertex3f(-width/2,   height/2,  depth/2);
421   glVertex3f(-width/2,   height/2, -depth/2);
422   glEnd();
423   glBegin(GL_LINE_LOOP);
424   glVertex3f( width/2,  -height/2, -depth/2);
425   glVertex3f( width/2,  -height/2,  depth/2);
426   glVertex3f( width/2,   height/2,  depth/2);
427   glVertex3f( width/2,   height/2, -depth/2);
428   glEnd();
429
430   glEnd();
431   glBegin(GL_LINE_LOOP);
432   glVertex3f(-width/2,   height/2,  depth/2);
433   glVertex3f(-width/2,  -height/2, -depth/2);
434   glEnd();
435   glBegin(GL_LINE_LOOP);
436   glVertex3f( width/2,   height/2,  depth/2);
437   glVertex3f( width/2,  -height/2, -depth/2);
438   glEnd();
439   glBegin(GL_LINE_LOOP);
440   glVertex3f(-width/2,  -height/2,  depth/2);
441   glVertex3f(-width/2,   height/2, -depth/2);
442   glEnd();
443   glBegin(GL_LINE_LOOP);
444   glVertex3f( width/2,  -height/2,  depth/2);
445   glVertex3f( width/2,   height/2, -depth/2);
446   glEnd();
447 }
448 #endif /* 0 */
449
450
451 /* Window management, etc
452  */
453 void
454 reshape_sws (ModeInfo *mi, int width, int height)
455 {
456   sws_configuration *sc = &scs[MI_SCREEN(mi)];
457   static Bool stars_done = False;
458
459   glViewport (0, 0, (GLint) width, (GLint) height);
460   if (!stars_done)
461     {
462       int i;
463       int nstars = width * height / 320;
464       glDeleteLists (sc->star_list, 1);
465       sc->star_list = glGenLists (1);
466       glNewList (sc->star_list, GL_COMPILE);
467       glBegin (GL_POINTS);
468       for (i = 0; i < nstars; i++)
469         {
470           GLfloat c = 0.6 + 0.3 * random() / RAND_MAX;
471           glColor3f (c, c, c);
472           glVertex3f (2 * width  * (0.5 - 1.0 * random() / RAND_MAX),
473                       2 * height * (0.5 - 1.0 * random() / RAND_MAX),
474                       0.0);
475         }
476       glEnd ();
477       glEndList ();
478       stars_done = True;
479     }
480 }
481
482
483 static void
484 gl_init (ModeInfo *mi)
485 {
486   sws_configuration *sc = &scs[MI_SCREEN(mi)];
487
488   program = get_string_resource ("program", "Program");
489
490   glMatrixMode (GL_MODELVIEW);
491
492   glDisable (GL_LIGHTING);
493   glDisable (GL_DEPTH_TEST);
494
495   sc->text_list = glGenLists (1);
496   glNewList (sc->text_list, GL_COMPILE);
497   glEndList ();
498
499   sc->star_list = glGenLists (1);
500   glNewList (sc->star_list, GL_COMPILE);
501   glEndList ();
502 }
503
504
505 void 
506 init_sws (ModeInfo *mi)
507 {
508   double font_height;
509
510   sws_configuration *sc;
511
512   if (!scs) {
513     scs = (sws_configuration *)
514       calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
515     if (!scs) {
516       fprintf(stderr, "%s: out of memory\n", progname);
517       exit(1);
518     }
519
520     sc = &scs[MI_SCREEN(mi)];
521     sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
522   }
523
524   sc = &scs[MI_SCREEN(mi)];
525
526   if ((sc->glx_context = init_GL(mi)) != NULL) {
527     gl_init(mi);
528     reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
529   }
530
531
532
533   font_height = GLUT_FONT->top - GLUT_FONT->bottom;
534   sc->font_scale = 1.0 / glutStrokeWidth (GLUT_FONT, 'z');   /* 'n' seems
535                                                                 too wide */
536   if (target_columns > 0)
537     {
538       sc->columns = target_columns;
539     }
540   else
541     {
542       if (font_size <= 0)
543         font_size = BASE_FONT_SIZE;
544       sc->columns = BASE_FONT_COLUMNS * ((double) BASE_FONT_SIZE / font_size);
545     }
546
547   sc->font_scale /= sc->columns;
548   sc->line_height = font_height * sc->font_scale;
549
550
551   if (!wrap_p) sc->columns = 1000;  /* wrap anyway, if it's absurdly long. */
552
553   sc->subproc_relaunch_delay = 2 * 1000;
554   sc->total_lines = max_lines-1;
555   launch_text_generator (sc);
556
557   if (random() & 1)
558     star_spin = -star_spin;
559
560   if (!alignment_str || !*alignment_str ||
561       !strcasecmp(alignment_str, "left"))
562     alignment = -1;
563   else if (!strcasecmp(alignment_str, "center") ||
564            !strcasecmp(alignment_str, "middle"))
565     alignment = 0;
566   else if (!strcasecmp(alignment_str, "right"))
567     alignment = 1;
568   else
569     {
570       fprintf (stderr,
571                "%s: alignment must be left, center, or right, not \"%s\"\n",
572                progname, alignment_str);
573       exit (1);
574     }
575 }
576
577
578 void
579 draw_sws (ModeInfo *mi)
580 {
581   sws_configuration *sc = &scs[MI_SCREEN(mi)];
582   Display *dpy = MI_DISPLAY(mi);
583   Window window = MI_WINDOW(mi);
584   int i;
585
586   if (!sc->glx_context)
587     return;
588
589   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
590     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
591
592   glDrawBuffer (GL_BACK);
593   glXMakeCurrent (dpy, window, *(sc->glx_context));
594
595   glClear (GL_COLOR_BUFFER_BIT);
596
597   glPushMatrix ();
598
599   glMatrixMode (GL_PROJECTION);
600   glLoadIdentity ();
601   glOrtho (-0.5 * MI_WIDTH(mi),  0.5 * MI_WIDTH(mi),
602            -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
603            -100.0, 100.0);
604   glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
605   glCallList (sc->star_list);
606
607   glLoadIdentity ();
608   gluPerspective (80.0, 4.0/3.0, 10, 500000);
609   glMatrixMode (GL_MODELVIEW);
610   gluLookAt (0.0, 0.0, 4600.0,
611              0.0, 0.0, 0.0,
612              0.0, 1.0, 0.0);
613
614   glRotatef (-60.0, 1.0, 0.0, 0.0);
615
616   /* The above gives us an arena where the bottom edge of the screen is
617      represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
618
619   /* Now let's move the origin to the front of the screen. */
620   glTranslatef (0.0, -3140, 0.0);
621
622   /* And then let's scale so that the bottom of the screen is 1.0 wide. */
623   glScalef (4200, 4200, 4200);
624
625   /* Scroll to current position */
626   glTranslatef (0.0, sc->intra_line_scroll, 0.0);
627
628   glColor3f (1.0, 1.0, 0.4);
629   glCallList (sc->text_list);
630
631   sc->intra_line_scroll += sc->line_height / scroll_steps;
632
633   if (sc->intra_line_scroll >= sc->line_height)
634     {
635       sc->intra_line_scroll = 0;
636
637       /* Drop the oldest line off the end. */
638       if (sc->lines[0])
639         free (sc->lines[0]);
640
641       /* Scroll the contents of the lines array toward 0. */
642       if (sc->total_lines > 0)
643         {
644           for (i = 1; i < sc->total_lines; i++)
645             sc->lines[i-1] = sc->lines[i];
646           sc->lines[--sc->total_lines] = 0;
647         }
648
649       /* Bring in new lines at the end. */
650       get_more_lines (sc);
651
652       if (sc->total_lines < max_lines)
653         /* Oops, we ran out of text... well, insert some blank lines
654            here so that new text still pulls in from the bottom of
655            the screen, isntead of just appearing. */
656         sc->total_lines = max_lines;
657
658       glDeleteLists (sc->text_list, 1);
659       sc->text_list = glGenLists (1);
660       glNewList (sc->text_list, GL_COMPILE);
661       glPushMatrix ();
662       glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
663       for (i = 0; i < sc->total_lines; i++)
664         {
665           int offscreen_lines = 3;
666
667           double x = -0.5;
668           double y =  ((sc->total_lines - (i + offscreen_lines) - 1)
669                        * sc->line_height);
670           double xoff = 0;
671           char *line = sc->lines[i];
672 #if 0
673           char n[20];
674           sprintf(n, "%d:", i);
675           draw_string (x / sc->font_scale, y / sc->font_scale, n);
676 #endif
677           if (!line || !*line)
678             continue;
679
680           if (alignment >= 0)
681             xoff = 1.0 - (glutStrokeLength(GLUT_FONT, line) * sc->font_scale);
682           if (alignment == 0)
683             xoff /= 2;
684
685           draw_string ((x + xoff) / sc->font_scale, y / sc->font_scale, line);
686         }
687       glPopMatrix ();
688       glEndList ();
689     }
690
691   glPopMatrix ();
692
693   if (mi->fps_p) do_fps (mi);
694   glFinish();
695   glXSwapBuffers(dpy, window);
696
697   sc->star_theta += star_spin;
698 }
699
700 #endif /* USE_GL */