http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.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                 else
289                   {
290                     /* kernel source not installed?  try X... */
291                     strcpy (cmd, "cat /usr/X11R6/lib/X11/doc/README");
292                     if (!stat (cmd+4, &st))
293                       oprogram = cmd;
294                   }
295               }
296           }
297       }
298 #endif /* __linux__ */
299
300 #ifdef __APPLE__   /* MacOS X + XDarwin */
301       {
302         static int done_once = 0;
303         if (!done_once)
304           {
305             struct stat st;
306             static char *cmd = "cat /usr/X11R6/README";
307             if (!stat (cmd+4, &st))
308               oprogram = cmd;
309           }
310       }
311 #endif /* __APPLE__ */
312     }
313
314   program = (char *) malloc (strlen (oprogram) + 10);
315   strcpy (program, "( ");
316   strcat (program, oprogram);
317   strcat (program, " ) 2>&1");
318
319   if ((sc->pipe = popen (program, "r")))
320     {
321       sc->pipe_id =
322         XtAppAddInput (app, fileno (sc->pipe),
323                        (XtPointer) (XtInputReadMask | XtInputExceptMask),
324                        subproc_cb, (XtPointer) sc);
325     }
326   else
327     {
328       perror (program);
329     }
330 }
331
332
333 static void
334 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
335 {
336   sws_configuration *sc = (sws_configuration *) closure;
337   launch_text_generator (sc);
338 }
339
340
341 /* When the subprocess has generated some output, this reads as much as it
342    can into sc->buf at sc->buf_tail.
343  */
344 static void
345 drain_input (sws_configuration *sc)
346 {
347   if (sc->buf_tail < sizeof(sc->buf) - 2)
348     {
349       int target = sizeof(sc->buf) - sc->buf_tail - 2;
350       int n = read (fileno (sc->pipe),
351                     (void *) (sc->buf + sc->buf_tail),
352                     target);
353       if (n > 0)
354         {
355           sc->buf_tail += n;
356           sc->buf[sc->buf_tail] = 0;
357         }
358       else
359         {
360           XtRemoveInput (sc->pipe_id);
361           sc->pipe_id = 0;
362           pclose (sc->pipe);
363           sc->pipe = 0;
364
365           /* If the process didn't print a terminating newline, add one. */
366           if (sc->buf_tail > 1 &&
367               sc->buf[sc->buf_tail-1] != '\n')
368             {
369               sc->buf[sc->buf_tail++] = '\n';
370               sc->buf[sc->buf_tail] = 0;
371             }
372
373           /* Then add one more, just for giggles. */
374           sc->buf[sc->buf_tail++] = '\n';
375           sc->buf[sc->buf_tail] = 0;
376
377           /* Set up a timer to re-launch the subproc in a bit. */
378           XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
379                            relaunch_generator_timer,
380                            (XtPointer) sc);
381         }
382     }
383 }
384
385
386 /* Populates the sc->lines list with as many lines as are currently in
387    sc->buf (which was filled by drain_input().
388  */
389 static void
390 get_more_lines (sws_configuration *sc)
391 {
392   int col = 0;
393   char *s = sc->buf;
394   while (sc->total_lines < max_lines)
395     {
396       if (s >= sc->buf + sc->buf_tail)
397         {
398           /* Reached end of buffer before end of line.  Bail. */
399           return;
400         }
401
402       if (*s == '\r' || *s == '\n' || col > sc->columns)
403         {
404           int L = s - sc->buf;
405
406           if (*s == '\r' || *s == '\n')
407             {
408               if (*s == '\r' && s[1] == '\n')  /* swallow CRLF too */
409                 *s++ = 0;
410
411               *s++ = 0;
412             }
413           else
414             {
415               /* We wrapped -- try to back up to the previous word boundary. */
416               char *s2 = s;
417               int n = 0;
418               while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
419                 s2--, n++;
420               if (s2 > sc->buf)
421                 {
422                   s = s2;
423                   *s++ = 0;
424                   L = s - sc->buf;
425                 }
426             }
427
428           sc->lines[sc->total_lines] = (char *) malloc (L+1);
429           memcpy (sc->lines[sc->total_lines], sc->buf, L);
430           sc->lines[sc->total_lines][L] = 0;
431
432           {
433             char *t = sc->lines[sc->total_lines];
434             char *ut = untabify (t);
435             strip (ut, (alignment == 0), 1); /* if centering, strip
436                                                 leading whitespace too */
437             sc->lines[sc->total_lines] = ut;
438             free (t);
439           }
440
441           sc->total_lines++;
442
443           if (sc->buf_tail > (s - sc->buf))
444             {
445               int i = sc->buf_tail - (s - sc->buf);
446               memmove (sc->buf, s, i);
447               sc->buf_tail = i;
448               sc->buf[sc->buf_tail] = 0;
449             }
450           else
451             {
452               sc->buf_tail = 0;
453             }
454
455           sc->buf[sc->buf_tail] = 0;
456           s = sc->buf;
457           col = 0;
458         }
459       else
460         {
461           col++;
462           if (*s == '\t')
463             col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
464           s++;
465         }
466     }
467 }
468
469
470 static void
471 draw_string (int x, int y, const char *s)
472 {
473   if (!s || !*s) return;
474   glPushMatrix ();
475   glTranslatef (x, y, 0);
476
477   while (*s)
478     glutStrokeCharacter (GLUT_FONT, *s++);
479   glPopMatrix ();
480 }
481
482
483 #ifdef DEBUG
484 static void
485 grid (double width, double height, double spacing, double z)
486 {
487   double x, y;
488   for (y = 0; y <= height/2; y += spacing)
489     {
490       glBegin(GL_LINES);
491       glVertex3f(-width/2,  y, z);
492       glVertex3f( width/2,  y, z);
493       glVertex3f(-width/2, -y, z);
494       glVertex3f( width/2, -y, z);
495       glEnd();
496     }
497   for (x = 0; x <= width/2; x += spacing)
498     {
499       glBegin(GL_LINES);
500       glVertex3f( x, -height/2, z);
501       glVertex3f( x,  height/2, z);
502       glVertex3f(-x, -height/2, z);
503       glVertex3f(-x,  height/2, z);
504       glEnd();
505     }
506
507   glBegin(GL_LINES);
508   glVertex3f(-width, 0, z);
509   glVertex3f( width, 0, z);
510   glVertex3f(0, -height, z);
511   glVertex3f(0,  height, z);
512   glEnd();
513 }
514
515 static void
516 box (double width, double height, double depth)
517 {
518   glBegin(GL_LINE_LOOP);
519   glVertex3f(-width/2,  -height/2, -depth/2);
520   glVertex3f(-width/2,   height/2, -depth/2);
521   glVertex3f( width/2,   height/2, -depth/2);
522   glVertex3f( width/2,  -height/2, -depth/2);
523   glEnd();
524   glBegin(GL_LINE_LOOP);
525   glVertex3f(-width/2,  -height/2,  depth/2);
526   glVertex3f(-width/2,   height/2,  depth/2);
527   glVertex3f( width/2,   height/2,  depth/2);
528   glVertex3f( width/2,  -height/2,  depth/2);
529   glEnd();
530   glBegin(GL_LINE_LOOP);
531   glVertex3f(-width/2,  -height/2, -depth/2);
532   glVertex3f(-width/2,  -height/2,  depth/2);
533   glVertex3f(-width/2,   height/2,  depth/2);
534   glVertex3f(-width/2,   height/2, -depth/2);
535   glEnd();
536   glBegin(GL_LINE_LOOP);
537   glVertex3f( width/2,  -height/2, -depth/2);
538   glVertex3f( width/2,  -height/2,  depth/2);
539   glVertex3f( width/2,   height/2,  depth/2);
540   glVertex3f( width/2,   height/2, -depth/2);
541   glEnd();
542
543   glEnd();
544   glBegin(GL_LINES);
545   glVertex3f(-width/2,   height/2,  depth/2);
546   glVertex3f(-width/2,  -height/2, -depth/2);
547
548   glVertex3f( width/2,   height/2,  depth/2);
549   glVertex3f( width/2,  -height/2, -depth/2);
550
551   glVertex3f(-width/2,  -height/2,  depth/2);
552   glVertex3f(-width/2,   height/2, -depth/2);
553
554   glVertex3f( width/2,  -height/2,  depth/2);
555   glVertex3f( width/2,   height/2, -depth/2);
556   glEnd();
557 }
558 #endif /* DEBUG */
559
560
561 /* Construct stars (number of stars is dependent on size of screen) */
562 static void
563 init_stars (ModeInfo *mi, int width, int height)
564 {
565   sws_configuration *sc = &scs[MI_SCREEN(mi)];
566   int i, j;
567   int nstars = width * height / 320;
568   int max_size = 3;
569   GLfloat inc = 0.5;
570   int steps = max_size / inc;
571
572   glDeleteLists (sc->star_list, 1);
573   sc->star_list = glGenLists (1);
574   glNewList (sc->star_list, GL_COMPILE);
575
576   glEnable(GL_POINT_SMOOTH);
577
578   for (j = 1; j <= steps; j++)
579     {
580       glPointSize(inc * j);
581       glBegin (GL_POINTS);
582       for (i = 0; i < nstars / steps; i++)
583         {
584           glColor3f (0.6 + frand(0.3),
585                      0.6 + frand(0.3),
586                      0.6 + frand(0.3));
587           glVertex2f (2 * width  * (0.5 - frand(1.0)),
588                       2 * height * (0.5 - frand(1.0)));
589         }
590       glEnd ();
591     }
592   glEndList ();
593 }
594
595
596 /* Window management, etc
597  */
598 void
599 reshape_sws (ModeInfo *mi, int width, int height)
600 {
601   sws_configuration *sc = &scs[MI_SCREEN(mi)];
602
603   /* Set up matrices for perspective text display
604    */
605   {
606     GLfloat desired_aspect = (GLfloat) 3/4;
607     int w = mi->xgwa.width;
608     int h = mi->xgwa.height;
609
610 #ifdef KEEP_ASPECT
611     h = w * desired_aspect;
612 #endif
613
614     glMatrixMode (GL_PROJECTION);
615     glViewport (0, 0, w, h);
616
617     glMatrixMode (GL_MODELVIEW);
618     glLoadIdentity ();
619     gluPerspective (80.0, 1/desired_aspect, 10, 500000);
620     gluLookAt (0.0, 0.0, 4600.0,
621                0.0, 0.0, 0.0,
622                0.0, 1.0, 0.0);
623     glRotatef (-60.0, 1.0, 0.0, 0.0);
624
625     /* The above gives us an arena where the bottom edge of the screen is
626        represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
627
628     /* Now let's move the origin to the front of the screen. */
629     glTranslatef (0.0, -3140, 0.0);
630
631     /* And then let's scale so that the bottom of the screen is 1.0 wide. */
632     glScalef (4200, 4200, 4200);
633   }
634
635
636   /* Compute the height in pixels of the line at the bottom of the screen. */
637   {
638     GLdouble mm[17], pm[17];
639     GLint vp[5];
640     GLfloat x = 0.5, y1 = 0, z = 0;
641     GLfloat y2 = sc->line_height;
642     GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
643
644     glGetDoublev (GL_MODELVIEW_MATRIX, mm);
645     glGetDoublev (GL_PROJECTION_MATRIX, pm);
646     glGetIntegerv (GL_VIEWPORT, vp);
647     gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
648     gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
649     sc->line_pixel_height = (wy2 - wy1);
650     glLineWidth (1);
651   }
652
653   /* Compute the best looking line thickness for the bottom line.
654    */
655   if (!thick_p)
656     sc->line_thickness = 1.0;
657   else
658     sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
659
660   if (sc->line_thickness < 1.2)
661     sc->line_thickness = 1.0;
662 }
663
664
665 static void
666 gl_init (ModeInfo *mi)
667 {
668   sws_configuration *sc = &scs[MI_SCREEN(mi)];
669
670   program = get_string_resource ("program", "Program");
671
672   glDisable (GL_LIGHTING);
673   glDisable (GL_DEPTH_TEST);
674
675   if (smooth_p) 
676     {
677       glEnable (GL_LINE_SMOOTH);
678       glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
679       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
680       glEnable (GL_BLEND);
681     }
682
683   sc->text_list = glGenLists (1);
684   glNewList (sc->text_list, GL_COMPILE);
685   glEndList ();
686
687   sc->star_list = glGenLists (1);
688   glNewList (sc->star_list, GL_COMPILE);
689   glEndList ();
690
691   sc->line_thickness = 1.0;
692 }
693
694
695 void 
696 init_sws (ModeInfo *mi)
697 {
698   double font_height;
699
700   sws_configuration *sc;
701
702   if (!scs) {
703     scs = (sws_configuration *)
704       calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
705     if (!scs) {
706       fprintf(stderr, "%s: out of memory\n", progname);
707       exit(1);
708     }
709
710     sc = &scs[MI_SCREEN(mi)];
711     sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
712   }
713
714   sc = &scs[MI_SCREEN(mi)];
715
716   if ((sc->glx_context = init_GL(mi)) != NULL) {
717     gl_init(mi);
718     reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
719     init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
720   }
721
722
723   font_height = GLUT_FONT->top - GLUT_FONT->bottom;
724   sc->font_scale = 1.0 / glutStrokeWidth (GLUT_FONT, 'z');   /* 'n' seems
725                                                                 too wide */
726   if (target_columns > 0)
727     {
728       sc->columns = target_columns;
729     }
730   else
731     {
732       if (font_size <= 0)
733         font_size = BASE_FONT_SIZE;
734       sc->columns = BASE_FONT_COLUMNS * ((double) BASE_FONT_SIZE / font_size);
735     }
736
737   sc->font_scale /= sc->columns;
738   sc->line_height = font_height * sc->font_scale;
739
740
741   if (!wrap_p) sc->columns = 1000;  /* wrap anyway, if it's absurdly long. */
742
743   sc->subproc_relaunch_delay = 2 * 1000;
744   sc->total_lines = max_lines-1;
745
746   if (random() & 1)
747     star_spin = -star_spin;
748
749   if (!alignment_str || !*alignment_str ||
750       !strcasecmp(alignment_str, "left"))
751     alignment = -1;
752   else if (!strcasecmp(alignment_str, "center") ||
753            !strcasecmp(alignment_str, "middle"))
754     alignment = 0;
755   else if (!strcasecmp(alignment_str, "right"))
756     alignment = 1;
757   else
758     {
759       fprintf (stderr,
760                "%s: alignment must be left, center, or right, not \"%s\"\n",
761                progname, alignment_str);
762       exit (1);
763     }
764
765   launch_text_generator (sc);
766
767   /* one more reshape, after line_height has been computed */
768   reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
769 }
770
771
772 static void
773 draw_stars (ModeInfo *mi)
774 {
775   sws_configuration *sc = &scs[MI_SCREEN(mi)];
776
777   glMatrixMode (GL_PROJECTION);
778   glPushMatrix ();
779   {
780     glLoadIdentity ();
781
782     glMatrixMode (GL_MODELVIEW);
783     glPushMatrix ();
784     {
785       glLoadIdentity ();
786       glOrtho (-0.5 * MI_WIDTH(mi),  0.5 * MI_WIDTH(mi),
787                -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
788                -100.0, 100.0);
789       glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
790       glCallList (sc->star_list);
791     }
792     glPopMatrix ();
793   }
794   glMatrixMode (GL_PROJECTION);
795   glPopMatrix ();
796 }
797
798 void
799 draw_sws (ModeInfo *mi)
800 {
801   sws_configuration *sc = &scs[MI_SCREEN(mi)];
802   Display *dpy = MI_DISPLAY(mi);
803   Window window = MI_WINDOW(mi);
804   int i;
805
806   if (!sc->glx_context)
807     return;
808
809   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
810     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
811
812   glDrawBuffer (GL_BACK);
813   glXMakeCurrent (dpy, window, *(sc->glx_context));
814
815   glClear (GL_COLOR_BUFFER_BIT);
816
817   draw_stars (mi);
818
819   glMatrixMode (GL_MODELVIEW);
820   glPushMatrix ();
821
822 #ifdef DEBUG
823   glColor3f (0.4, 0.4, 0.4);
824   glLineWidth (1);
825   glTranslatef(0, 1, 0);
826   box (1, 1, 1);
827   glTranslatef(0, -1, 0);
828   box (1, 1, 1);
829   grid (1, 1, sc->line_height, 0);
830 #endif /* DEBUG */
831
832   /* Scroll to current position */
833   glTranslatef (0.0, sc->intra_line_scroll, 0.0);
834
835   glColor3f (1.0, 1.0, 0.4);
836   glCallList (sc->text_list);
837
838   sc->intra_line_scroll += sc->line_height / scroll_steps;
839
840   if (sc->intra_line_scroll >= sc->line_height)
841     {
842       sc->intra_line_scroll = 0;
843
844       /* Drop the oldest line off the end. */
845       if (sc->lines[0])
846         free (sc->lines[0]);
847
848       /* Scroll the contents of the lines array toward 0. */
849       if (sc->total_lines > 0)
850         {
851           for (i = 1; i < sc->total_lines; i++)
852             sc->lines[i-1] = sc->lines[i];
853           sc->lines[--sc->total_lines] = 0;
854         }
855
856       /* Bring in new lines at the end. */
857       get_more_lines (sc);
858
859       if (sc->total_lines < max_lines)
860         /* Oops, we ran out of text... well, insert some blank lines
861            here so that new text still pulls in from the bottom of
862            the screen, isntead of just appearing. */
863         sc->total_lines = max_lines;
864
865       glDeleteLists (sc->text_list, 1);
866       sc->text_list = glGenLists (1);
867       glNewList (sc->text_list, GL_COMPILE);
868       glPushMatrix ();
869       glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
870       for (i = 0; i < sc->total_lines; i++)
871         {
872           int offscreen_lines = 3;
873
874           double x = -0.5;
875           double y =  ((sc->total_lines - (i + offscreen_lines) - 1)
876                        * sc->line_height);
877           double xoff = 0;
878           char *line = sc->lines[i];
879 #ifdef DEBUG
880           char n[20];
881           sprintf(n, "%d:", i);
882           draw_string (x / sc->font_scale, y / sc->font_scale, n);
883 #endif /* DEBUG */
884           if (!line || !*line)
885             continue;
886
887           if (sc->line_thickness != 1)
888             {
889               int max_thick_lines = MAX_THICK_LINES;
890               GLfloat thinnest_line = 1.0;
891               GLfloat thickest_line = sc->line_thickness;
892               GLfloat range = thickest_line - thinnest_line;
893               GLfloat thickness;
894
895               int j = sc->total_lines - i - 1;
896
897               if (j > max_thick_lines)
898                 thickness = thinnest_line;
899               else
900                 thickness = (thinnest_line +
901                              (range * ((max_thick_lines - j) /
902                                        (GLfloat) max_thick_lines)));
903
904               glLineWidth (thickness);
905             }
906
907           if (alignment >= 0)
908             xoff = 1.0 - (glutStrokeLength(GLUT_FONT,
909                                            (unsigned char *) line)
910                           * sc->font_scale);
911           if (alignment == 0)
912             xoff /= 2;
913
914           if (fade_p)
915             {
916               double factor = 1.0 * i / sc->total_lines;
917               glColor3f (factor, factor, 0.5 * factor);
918             }
919
920           draw_string ((x + xoff) / sc->font_scale, y / sc->font_scale, line);
921         }
922       glPopMatrix ();
923       glEndList ();
924     }
925
926   glPopMatrix ();
927
928   if (mi->fps_p) do_fps (mi);
929   glFinish();
930   glXSwapBuffers(dpy, window);
931
932   sc->star_theta += star_spin;
933 }
934
935 #endif /* USE_GL */