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