ftp://updates.redhat.com/enterprise/2.1AS/en/os/SRPMS/xscreensaver-3.33-4.rhel21...
[xscreensaver] / hacks / glx / starwars.c
1 /*
2  * starwars, Copyright (c) 1998-2001 Jamie Zawinski <jwz@jwz.org> and
3  * Claudio Matsuoka <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  *           20010224 jepler@mail.inetnebr.com  made the lines be anti-aliased,
33  *                              made the text fade to black at the end.
34  */
35
36 #include <X11/Intrinsic.h>
37
38 extern XtAppContext app;
39
40 #define PROGCLASS       "StarWars"
41 #define HACK_INIT       init_sws
42 #define HACK_DRAW       draw_sws
43 #define HACK_RESHAPE    reshape_sws
44 #define sws_opts        xlockmore_opts
45
46 #define DEF_PROGRAM    "(default)"
47 #define DEF_LINES      "125"
48 #define DEF_STEPS      "35"
49 #define DEF_SPIN       "0.03"
50 #define DEF_FONT_SIZE  "-1"
51 #define DEF_COLUMNS    "-1"
52 #define DEF_WRAP       "True"
53 #define DEF_ALIGN      "Center"
54 #define DEF_SMOOTH     "True"
55 #define DEF_THICK      "True"
56 #define DEF_FADE       "True"
57
58 #define TAB_WIDTH        8
59
60 #define BASE_FONT_SIZE    18 /* magic */
61 #define BASE_FONT_COLUMNS 80 /* magic */
62
63 #define MAX_THICK_LINES   25
64 #define FONT_WEIGHT       14
65 #define KEEP_ASPECT
66 #undef DEBUG
67
68 #define DEFAULTS        "*delay:        40000 \n"                    \
69                         "*showFPS:      False \n"                    \
70                         "*fpsTop:       True \n"                     \
71                         "*program:      " DEF_PROGRAM           "\n" \
72                         "*lines:        " DEF_LINES             "\n" \
73                         "*spin:         " DEF_SPIN              "\n" \
74                         "*steps:        " DEF_STEPS             "\n" \
75                         "*smooth:       " DEF_SMOOTH            "\n" \
76                         "*thick:        " DEF_THICK             "\n" \
77                         "*fade:         " DEF_FADE              "\n" \
78                         "*starwars.fontSize: " DEF_FONT_SIZE    "\n" \
79                         "*starwars.columns:  " DEF_COLUMNS      "\n" \
80                         "*starwars.lineWrap: " DEF_WRAP         "\n" \
81                         "*starwars.alignment:" DEF_ALIGN        "\n"
82
83 #undef countof
84 #define countof(x) (sizeof((x))/sizeof((*x)))
85
86 #include "xlockmore.h"
87
88 #ifdef USE_GL /* whole file */
89
90 #include <GL/glu.h>
91 #include <sys/stat.h>
92 #include "glutstroke.h"
93 #include "glut_roman.h"
94 #define GLUT_FONT (&glutStrokeRoman)
95
96
97 typedef struct {
98   GLXContext *glx_context;
99
100   GLuint text_list, star_list;
101
102   FILE *pipe;
103   XtInputId pipe_id;
104   Time subproc_relaunch_delay;
105
106   char buf [1024];
107   int buf_tail;
108   char **lines;
109   int total_lines;
110   int columns;
111
112   double star_theta;
113   double line_height;
114   double font_scale;
115   double intra_line_scroll;
116
117   int line_pixel_height;
118   GLfloat line_thickness;
119
120 } sws_configuration;
121
122
123 static sws_configuration *scs = NULL;
124
125 static char *program;
126 static int max_lines;
127 static int scroll_steps;
128 static float star_spin;
129 static float font_size;
130 static int target_columns;
131 static int wrap_p;
132 static int smooth_p;
133 static int thick_p;
134 static int fade_p;
135 static char *alignment_str;
136 static int alignment;
137
138 static XrmOptionDescRec opts[] = {
139   {"-program",   ".starwars.program",  XrmoptionSepArg, (caddr_t) 0 },
140   {"-lines",     ".starwars.lines",    XrmoptionSepArg, (caddr_t) 0 },
141   {"-steps",     ".starwars.steps",    XrmoptionSepArg, (caddr_t) 0 },
142   {"-spin",      ".starwars.spin",     XrmoptionSepArg, (caddr_t) 0 },
143   {"-size",      ".starwars.fontSize", XrmoptionSepArg, (caddr_t) 0 },
144   {"-columns",   ".starwars.columns",  XrmoptionSepArg, (caddr_t) 0 },
145   {"-smooth",    ".starwars.smooth",   XrmoptionNoArg,  (caddr_t) "True" },
146   {"-no-smooth", ".starwars.smooth",   XrmoptionNoArg,  (caddr_t) "False" },
147   {"-thick",     ".starwars.thick",    XrmoptionNoArg,  (caddr_t) "True" },
148   {"-no-thick",  ".starwars.thick",    XrmoptionNoArg,  (caddr_t) "False" },
149   {"-fade",      ".starwars.fade",     XrmoptionNoArg,  (caddr_t) "True" },
150   {"-no-fade",   ".starwars.fade",     XrmoptionNoArg,  (caddr_t) "False" },
151   {"-wrap",      ".starwars.lineWrap", XrmoptionNoArg,  (caddr_t) "True" },
152   {"-no-wrap",   ".starwars.lineWrap", XrmoptionNoArg,  (caddr_t) "False" },
153   {"-nowrap",    ".starwars.lineWrap", XrmoptionNoArg,  (caddr_t) "False" },
154   {"-left",      ".starwars.alignment",XrmoptionNoArg,  (caddr_t) "Left" },
155   {"-right",     ".starwars.alignment",XrmoptionNoArg,  (caddr_t) "Right" },
156   {"-center",    ".starwars.alignment",XrmoptionNoArg,  (caddr_t) "Center" },
157 };
158
159 static argtype vars[] = {
160   {(caddr_t *) &program,        "program", "Program", DEF_PROGRAM, t_String},
161   {(caddr_t *) &max_lines,      "lines",   "Integer", DEF_LINES,   t_Int},
162   {(caddr_t *) &scroll_steps,   "steps",   "Integer", DEF_STEPS,   t_Int},
163   {(caddr_t *) &star_spin,      "spin",    "Float",   DEF_SPIN,    t_Float},
164   {(caddr_t *) &font_size,      "fontSize","Float",   DEF_STEPS,   t_Float},
165   {(caddr_t *) &target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
166   {(caddr_t *) &wrap_p,         "lineWrap","Boolean", DEF_COLUMNS, t_Bool},
167   {(caddr_t *) &alignment_str,  "alignment","Alignment",DEF_ALIGN, t_String},
168   {(caddr_t *) &smooth_p,       "smooth",  "Boolean", DEF_SMOOTH,  t_Bool},
169   {(caddr_t *) &thick_p,        "thick",   "Boolean", DEF_THICK,   t_Bool},
170   {(caddr_t *) &fade_p,         "fade",    "Boolean", DEF_FADE,    t_Bool},
171 };
172
173 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
174
175
176
177 /* Tabs are bad, mmmkay? */
178
179 static char *
180 untabify (const char *string)
181 {
182   const char *ostring = string;
183   char *result = (char *) malloc ((strlen(string) * 8) + 1);
184   char *out = result;
185   int col = 0;
186   while (*string)
187     {
188       if (*string == '\t')
189         {
190           do {
191             col++;
192             *out++ = ' ';
193           } while (col % TAB_WIDTH);
194           string++;
195         }
196       else if (*string == '\r' || *string == '\n')
197         {
198           *out++ = *string++;
199           col = 0;
200         }
201       else if (*string == '\010')    /* backspace */
202         {
203           if (string > ostring)
204             out--, string++;
205         }
206       else
207         {
208           *out++ = *string++;
209           col++;
210         }
211     }
212   *out = 0;
213
214   return result;
215 }
216
217 static void
218 strip (char *s, Bool leading, Bool trailing)
219 {
220   int L = strlen(s);
221   if (trailing)
222     while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
223       s[L--] = 0;
224   if (leading)
225     {
226       char *s2 = s;
227       while (*s2 == ' ' || *s2 == '\t')
228         s2++;
229       if (s == s2)
230         return;
231       while (*s2)
232         *s++ = *s2++;
233       *s = 0;
234     }
235 }
236
237
238 \f
239 /* Subprocess.
240    (This bit mostly cribbed from phosphor.c)
241  */
242
243 static void drain_input (sws_configuration *sc);
244
245 static void
246 subproc_cb (XtPointer closure, int *source, XtInputId *id)
247 {
248   sws_configuration *sc = (sws_configuration *) closure;
249   drain_input (sc);
250 }
251
252
253 static void
254 launch_text_generator (sws_configuration *sc)
255 {
256   char *oprogram = get_string_resource ("program", "Program");
257   char *program;
258
259   if (!strcasecmp(oprogram, "(default)"))
260     {
261 #ifdef __linux__
262       static int done_once = 0;
263       struct stat st;
264       char *cmd = "cat /usr/src/linux/README";
265       if (!(done_once++) && !stat (cmd+4, &st))
266         oprogram = cmd;
267       else
268 #endif
269         oprogram = ZIPPY_PROGRAM;
270     }
271
272  program = (char *) malloc (strlen (oprogram) + 10);
273   strcpy (program, "( ");
274   strcat (program, oprogram);
275   strcat (program, " ) 2>&1");
276
277   if ((sc->pipe = popen (program, "r")))
278     {
279       sc->pipe_id =
280         XtAppAddInput (app, fileno (sc->pipe),
281                        (XtPointer) (XtInputReadMask | XtInputExceptMask),
282                        subproc_cb, (XtPointer) sc);
283     }
284   else
285     {
286       perror (program);
287     }
288 }
289
290
291 static void
292 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
293 {
294   sws_configuration *sc = (sws_configuration *) closure;
295   launch_text_generator (sc);
296 }
297
298
299 /* When the subprocess has generated some output, this reads as much as it
300    can into sc->buf at sc->buf_tail.
301  */
302 static void
303 drain_input (sws_configuration *sc)
304 {
305   if (sc->buf_tail < sizeof(sc->buf) - 2)
306     {
307       int target = sizeof(sc->buf) - sc->buf_tail - 2;
308       int n = read (fileno (sc->pipe),
309                     (void *) (sc->buf + sc->buf_tail),
310                     target);
311       if (n > 0)
312         {
313           sc->buf_tail += n;
314           sc->buf[sc->buf_tail] = 0;
315         }
316       else
317         {
318           XtRemoveInput (sc->pipe_id);
319           sc->pipe_id = 0;
320           pclose (sc->pipe);
321           sc->pipe = 0;
322
323           /* If the process didn't print a terminating newline, add one. */
324           if (sc->buf_tail > 1 &&
325               sc->buf[sc->buf_tail-1] != '\n')
326             {
327               sc->buf[sc->buf_tail++] = '\n';
328               sc->buf[sc->buf_tail] = 0;
329             }
330
331           /* Then add one more, just for giggles. */
332           sc->buf[sc->buf_tail++] = '\n';
333           sc->buf[sc->buf_tail] = 0;
334
335           /* Set up a timer to re-launch the subproc in a bit. */
336           XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
337                            relaunch_generator_timer,
338                            (XtPointer) sc);
339         }
340     }
341 }
342
343
344 /* Populates the sc->lines list with as many lines as are currently in
345    sc->buf (which was filled by drain_input().
346  */
347 static void
348 get_more_lines (sws_configuration *sc)
349 {
350   int col = 0;
351   char *s = sc->buf;
352   while (sc->total_lines < max_lines)
353     {
354       if (s >= sc->buf + sc->buf_tail)
355         {
356           /* Reached end of buffer before end of line.  Bail. */
357           return;
358         }
359
360       if (*s == '\n' || col > sc->columns)
361         {
362           int L = s - sc->buf;
363
364           if (*s == '\n')
365             *s++ = 0;
366           else
367             {
368               /* We wrapped -- try to back up to the previous word boundary. */
369               char *s2 = s;
370               int n = 0;
371               while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
372                 s2--, n++;
373               if (s2 > sc->buf)
374                 {
375                   s = s2;
376                   *s++ = 0;
377                   L = s - sc->buf;
378                 }
379             }
380
381           sc->lines[sc->total_lines] = (char *) malloc (L+1);
382           memcpy (sc->lines[sc->total_lines], sc->buf, L);
383           sc->lines[sc->total_lines][L] = 0;
384
385           {
386             char *t = sc->lines[sc->total_lines];
387             char *ut = untabify (t);
388             strip (ut, (alignment == 0), 1); /* if centering, strip
389                                                 leading whitespace too */
390             sc->lines[sc->total_lines] = ut;
391             free (t);
392           }
393
394           sc->total_lines++;
395
396           if (sc->buf_tail > (s - sc->buf))
397             {
398               int i = sc->buf_tail - (s - sc->buf);
399               memcpy (sc->buf, s, i);
400               sc->buf_tail = i;
401               sc->buf[sc->buf_tail] = 0;
402             }
403           else
404             {
405               sc->buf_tail = 0;
406             }
407
408           sc->buf[sc->buf_tail] = 0;
409           s = sc->buf;
410           col = 0;
411         }
412       else
413         {
414           col++;
415           if (*s == '\t')
416             col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
417           s++;
418         }
419     }
420 }
421
422
423 static void
424 draw_string (int x, int y, const char *s)
425 {
426   if (!s || !*s) return;
427   glPushMatrix ();
428   glTranslatef (x, y, 0);
429
430   while (*s)
431     glutStrokeCharacter (GLUT_FONT, *s++);
432   glPopMatrix ();
433 }
434
435
436 #ifdef DEBUG
437 static void
438 grid (double width, double height, double spacing, double z)
439 {
440   double x, y;
441   for (y = 0; y <= height/2; y += spacing)
442     {
443       glBegin(GL_LINES);
444       glVertex3f(-width/2,  y, z);
445       glVertex3f( width/2,  y, z);
446       glVertex3f(-width/2, -y, z);
447       glVertex3f( width/2, -y, z);
448       glEnd();
449     }
450   for (x = 0; x <= width/2; x += spacing)
451     {
452       glBegin(GL_LINES);
453       glVertex3f( x, -height/2, z);
454       glVertex3f( x,  height/2, z);
455       glVertex3f(-x, -height/2, z);
456       glVertex3f(-x,  height/2, z);
457       glEnd();
458     }
459
460   glBegin(GL_LINES);
461   glVertex3f(-width, 0, z);
462   glVertex3f( width, 0, z);
463   glVertex3f(0, -height, z);
464   glVertex3f(0,  height, z);
465   glEnd();
466 }
467
468 static void
469 box (double width, double height, double depth)
470 {
471   glBegin(GL_LINE_LOOP);
472   glVertex3f(-width/2,  -height/2, -depth/2);
473   glVertex3f(-width/2,   height/2, -depth/2);
474   glVertex3f( width/2,   height/2, -depth/2);
475   glVertex3f( width/2,  -height/2, -depth/2);
476   glEnd();
477   glBegin(GL_LINE_LOOP);
478   glVertex3f(-width/2,  -height/2,  depth/2);
479   glVertex3f(-width/2,   height/2,  depth/2);
480   glVertex3f( width/2,   height/2,  depth/2);
481   glVertex3f( width/2,  -height/2,  depth/2);
482   glEnd();
483   glBegin(GL_LINE_LOOP);
484   glVertex3f(-width/2,  -height/2, -depth/2);
485   glVertex3f(-width/2,  -height/2,  depth/2);
486   glVertex3f(-width/2,   height/2,  depth/2);
487   glVertex3f(-width/2,   height/2, -depth/2);
488   glEnd();
489   glBegin(GL_LINE_LOOP);
490   glVertex3f( width/2,  -height/2, -depth/2);
491   glVertex3f( width/2,  -height/2,  depth/2);
492   glVertex3f( width/2,   height/2,  depth/2);
493   glVertex3f( width/2,   height/2, -depth/2);
494   glEnd();
495
496   glEnd();
497   glBegin(GL_LINES);
498   glVertex3f(-width/2,   height/2,  depth/2);
499   glVertex3f(-width/2,  -height/2, -depth/2);
500
501   glVertex3f( width/2,   height/2,  depth/2);
502   glVertex3f( width/2,  -height/2, -depth/2);
503
504   glVertex3f(-width/2,  -height/2,  depth/2);
505   glVertex3f(-width/2,   height/2, -depth/2);
506
507   glVertex3f( width/2,  -height/2,  depth/2);
508   glVertex3f( width/2,   height/2, -depth/2);
509   glEnd();
510 }
511 #endif /* DEBUG */
512
513
514 /* Construct stars (number of stars is dependent on size of screen) */
515 static void
516 init_stars (ModeInfo *mi, int width, int height)
517 {
518   sws_configuration *sc = &scs[MI_SCREEN(mi)];
519   int i, j;
520   int nstars = width * height / 320;
521   int max_size = 3;
522   GLfloat inc = 0.5;
523   int steps = max_size / inc;
524
525   glDeleteLists (sc->star_list, 1);
526   sc->star_list = glGenLists (1);
527   glNewList (sc->star_list, GL_COMPILE);
528
529   glEnable(GL_POINT_SMOOTH);
530
531   for (j = 1; j <= steps; j++)
532     {
533       glPointSize(inc * j);
534       glBegin (GL_POINTS);
535       for (i = 0; i < nstars / steps; i++)
536         {
537           glColor3f (0.6 + frand(0.3),
538                      0.6 + frand(0.3),
539                      0.6 + frand(0.3));
540           glVertex2f (2 * width  * (0.5 - frand(1.0)),
541                       2 * height * (0.5 - frand(1.0)));
542         }
543       glEnd ();
544     }
545   glEndList ();
546 }
547
548
549 /* Window management, etc
550  */
551 void
552 reshape_sws (ModeInfo *mi, int width, int height)
553 {
554   sws_configuration *sc = &scs[MI_SCREEN(mi)];
555
556   /* Set up matrices for perspective text display
557    */
558   {
559     GLfloat desired_aspect = (GLfloat) 3/4;
560     int w = mi->xgwa.width;
561     int h = mi->xgwa.height;
562
563 #ifdef KEEP_ASPECT
564     h = w * desired_aspect;
565 #endif
566
567     glMatrixMode (GL_PROJECTION);
568     glViewport (0, 0, w, h);
569
570     glMatrixMode (GL_MODELVIEW);
571     glLoadIdentity ();
572     gluPerspective (80.0, 1/desired_aspect, 10, 500000);
573     gluLookAt (0.0, 0.0, 4600.0,
574                0.0, 0.0, 0.0,
575                0.0, 1.0, 0.0);
576     glRotatef (-60.0, 1.0, 0.0, 0.0);
577
578     /* The above gives us an arena where the bottom edge of the screen is
579        represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
580
581     /* Now let's move the origin to the front of the screen. */
582     glTranslatef (0.0, -3140, 0.0);
583
584     /* And then let's scale so that the bottom of the screen is 1.0 wide. */
585     glScalef (4200, 4200, 4200);
586   }
587
588
589   /* Compute the height in pixels of the line at the bottom of the screen. */
590   {
591     GLdouble mm[17], pm[17];
592     GLint vp[5];
593     GLfloat x = 0.5, y1 = 0, z = 0;
594     GLfloat y2 = sc->line_height;
595     GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
596
597     glGetDoublev (GL_MODELVIEW_MATRIX, mm);
598     glGetDoublev (GL_PROJECTION_MATRIX, pm);
599     glGetIntegerv (GL_VIEWPORT, vp);
600     gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
601     gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
602     sc->line_pixel_height = (wy2 - wy1);
603     glLineWidth (1);
604   }
605
606   /* Compute the best looking line thickness for the bottom line.
607    */
608   if (!thick_p)
609     sc->line_thickness = 1.0;
610   else
611     sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
612
613   if (sc->line_thickness < 1.2)
614     sc->line_thickness = 1.0;
615 }
616
617
618 static void
619 gl_init (ModeInfo *mi)
620 {
621   sws_configuration *sc = &scs[MI_SCREEN(mi)];
622
623   program = get_string_resource ("program", "Program");
624
625   glDisable (GL_LIGHTING);
626   glDisable (GL_DEPTH_TEST);
627
628   if (smooth_p) 
629     {
630       glEnable (GL_LINE_SMOOTH);
631       glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
632       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
633       glEnable (GL_BLEND);
634     }
635
636   sc->text_list = glGenLists (1);
637   glNewList (sc->text_list, GL_COMPILE);
638   glEndList ();
639
640   sc->star_list = glGenLists (1);
641   glNewList (sc->star_list, GL_COMPILE);
642   glEndList ();
643
644   sc->line_thickness = 1.0;
645 }
646
647
648 void 
649 init_sws (ModeInfo *mi)
650 {
651   double font_height;
652
653   sws_configuration *sc;
654
655   if (!scs) {
656     scs = (sws_configuration *)
657       calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
658     if (!scs) {
659       fprintf(stderr, "%s: out of memory\n", progname);
660       exit(1);
661     }
662
663     sc = &scs[MI_SCREEN(mi)];
664     sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
665   }
666
667   sc = &scs[MI_SCREEN(mi)];
668
669   if ((sc->glx_context = init_GL(mi)) != NULL) {
670     gl_init(mi);
671     reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
672     init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
673   }
674
675
676   font_height = GLUT_FONT->top - GLUT_FONT->bottom;
677   sc->font_scale = 1.0 / glutStrokeWidth (GLUT_FONT, 'z');   /* 'n' seems
678                                                                 too wide */
679   if (target_columns > 0)
680     {
681       sc->columns = target_columns;
682     }
683   else
684     {
685       if (font_size <= 0)
686         font_size = BASE_FONT_SIZE;
687       sc->columns = BASE_FONT_COLUMNS * ((double) BASE_FONT_SIZE / font_size);
688     }
689
690   sc->font_scale /= sc->columns;
691   sc->line_height = font_height * sc->font_scale;
692
693
694   if (!wrap_p) sc->columns = 1000;  /* wrap anyway, if it's absurdly long. */
695
696   sc->subproc_relaunch_delay = 2 * 1000;
697   sc->total_lines = max_lines-1;
698
699   if (random() & 1)
700     star_spin = -star_spin;
701
702   if (!alignment_str || !*alignment_str ||
703       !strcasecmp(alignment_str, "left"))
704     alignment = -1;
705   else if (!strcasecmp(alignment_str, "center") ||
706            !strcasecmp(alignment_str, "middle"))
707     alignment = 0;
708   else if (!strcasecmp(alignment_str, "right"))
709     alignment = 1;
710   else
711     {
712       fprintf (stderr,
713                "%s: alignment must be left, center, or right, not \"%s\"\n",
714                progname, alignment_str);
715       exit (1);
716     }
717
718   launch_text_generator (sc);
719 }
720
721
722 static void
723 draw_stars (ModeInfo *mi)
724 {
725   sws_configuration *sc = &scs[MI_SCREEN(mi)];
726
727   glMatrixMode (GL_PROJECTION);
728   glPushMatrix ();
729   {
730     glLoadIdentity ();
731
732     glMatrixMode (GL_MODELVIEW);
733     glPushMatrix ();
734     {
735       glLoadIdentity ();
736       glOrtho (-0.5 * MI_WIDTH(mi),  0.5 * MI_WIDTH(mi),
737                -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
738                -100.0, 100.0);
739       glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
740       glCallList (sc->star_list);
741     }
742     glPopMatrix ();
743   }
744   glMatrixMode (GL_PROJECTION);
745   glPopMatrix ();
746 }
747
748 void
749 draw_sws (ModeInfo *mi)
750 {
751   sws_configuration *sc = &scs[MI_SCREEN(mi)];
752   Display *dpy = MI_DISPLAY(mi);
753   Window window = MI_WINDOW(mi);
754   int i;
755
756   if (!sc->glx_context)
757     return;
758
759   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
760     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
761
762   glDrawBuffer (GL_BACK);
763   glXMakeCurrent (dpy, window, *(sc->glx_context));
764
765   glClear (GL_COLOR_BUFFER_BIT);
766
767   draw_stars (mi);
768
769   glMatrixMode (GL_MODELVIEW);
770   glPushMatrix ();
771
772 #ifdef DEBUG
773   glColor3f (0.4, 0.4, 0.4);
774   glLineWidth (1);
775   glTranslatef(0, 1, 0);
776   box (1, 1, 1);
777   glTranslatef(0, -1, 0);
778   box (1, 1, 1);
779   grid (1, 1, sc->line_height, 0);
780 #endif /* DEBUG */
781
782   /* Scroll to current position */
783   glTranslatef (0.0, sc->intra_line_scroll, 0.0);
784
785   glColor3f (1.0, 1.0, 0.4);
786   glCallList (sc->text_list);
787
788   sc->intra_line_scroll += sc->line_height / scroll_steps;
789
790   if (sc->intra_line_scroll >= sc->line_height)
791     {
792       sc->intra_line_scroll = 0;
793
794       /* Drop the oldest line off the end. */
795       if (sc->lines[0])
796         free (sc->lines[0]);
797
798       /* Scroll the contents of the lines array toward 0. */
799       if (sc->total_lines > 0)
800         {
801           for (i = 1; i < sc->total_lines; i++)
802             sc->lines[i-1] = sc->lines[i];
803           sc->lines[--sc->total_lines] = 0;
804         }
805
806       /* Bring in new lines at the end. */
807       get_more_lines (sc);
808
809       if (sc->total_lines < max_lines)
810         /* Oops, we ran out of text... well, insert some blank lines
811            here so that new text still pulls in from the bottom of
812            the screen, isntead of just appearing. */
813         sc->total_lines = max_lines;
814
815       glDeleteLists (sc->text_list, 1);
816       sc->text_list = glGenLists (1);
817       glNewList (sc->text_list, GL_COMPILE);
818       glPushMatrix ();
819       glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
820       for (i = 0; i < sc->total_lines; i++)
821         {
822           int offscreen_lines = 3;
823
824           double x = -0.5;
825           double y =  ((sc->total_lines - (i + offscreen_lines) - 1)
826                        * sc->line_height);
827           double xoff = 0;
828           char *line = sc->lines[i];
829 #ifdef DEBUG
830           char n[20];
831           sprintf(n, "%d:", i);
832           draw_string (x / sc->font_scale, y / sc->font_scale, n);
833 #endif /* DEBUG */
834           if (!line || !*line)
835             continue;
836
837           if (sc->line_thickness != 1)
838             {
839               int max_thick_lines = MAX_THICK_LINES;
840               GLfloat thinnest_line = 1.0;
841               GLfloat thickest_line = sc->line_thickness;
842               GLfloat range = thickest_line - thinnest_line;
843               GLfloat thickness;
844
845               int j = sc->total_lines - i - 1;
846
847               if (j > max_thick_lines)
848                 thickness = thinnest_line;
849               else
850                 thickness = (thinnest_line +
851                              (range * ((max_thick_lines - j) /
852                                        (GLfloat) max_thick_lines)));
853
854               glLineWidth (thickness);
855             }
856
857           if (alignment >= 0)
858             xoff = 1.0 - (glutStrokeLength(GLUT_FONT, line) * sc->font_scale);
859           if (alignment == 0)
860             xoff /= 2;
861
862           if (fade_p)
863             {
864               double factor = 1.0 * i / sc->total_lines;
865               glColor3f (factor, factor, 0.5 * factor);
866             }
867
868           draw_string ((x + xoff) / sc->font_scale, y / sc->font_scale, line);
869         }
870       glPopMatrix ();
871       glEndList ();
872     }
873
874   glPopMatrix ();
875
876   if (mi->fps_p) do_fps (mi);
877   glFinish();
878   glXSwapBuffers(dpy, window);
879
880   sc->star_theta += star_spin;
881 }
882
883 #endif /* USE_GL */