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