http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / hacks / glx / starwars.c
1 /* starwars, Copyright (c) 1998-2011 Jamie Zawinski <jwz@jwz.org> and
2  * Claudio Matsuoka <claudio@helllabs.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  *
12  * Star Wars -- Phosphor meets a well-known scroller from a galaxy far,
13  *           far away.
14  *
15  * Feb 2000 Claudio Matsuoka    First version.
16  * Jan 2001 Jamie Zawinski      Rewrote large sections to add the ability to
17  *                              run a subprocess, customization of the font
18  *                              size and other parameters, etc.
19  * Feb 2001 jepler@inetnebr.com Added anti-aliased lines, and fade-to-black.
20  * Feb 2005 Jamie Zawinski      Added texture fonts.
21  *
22  *
23  * For the fanboys:
24  *
25  *     starwars -program 'cat starwars.txt' -columns 25 -no-wrap -texture
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif /* HAVE_CONFIG_H */
31
32 #include <ctype.h>
33 #include <sys/stat.h>
34
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38
39 #ifndef HAVE_COCOA
40 # include <X11/Intrinsic.h>
41 #endif
42
43
44 #define DEFAULTS "*delay:    40000     \n" \
45                  "*showFPS:  False     \n" \
46                  "*fpsTop:   True      \n" \
47                  "*font:   " DEF_FONT "\n"
48
49 # define refresh_sws 0
50 # define sws_handle_event 0
51 #undef countof
52 #define countof(x) (sizeof((x))/sizeof((*x)))
53
54 #include "xlockmore.h"
55
56 #ifdef USE_GL /* whole file */
57
58 /* Should be in <GL/glext.h> */
59 # ifndef  GL_TEXTURE_MAX_ANISOTROPY_EXT
60 #  define GL_TEXTURE_MAX_ANISOTROPY_EXT     0x84FE
61 # endif
62 # ifndef  GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
63 #  define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
64 # endif
65
66
67 #define DEF_PROGRAM    "xscreensaver-text --cols 0"  /* don't wrap */
68 #define DEF_LINES      "125"
69 #define DEF_STEPS      "35"
70 #define DEF_SPIN       "0.03"
71 #define DEF_SIZE       "-1"
72 #define DEF_COLUMNS    "-1"
73 #define DEF_LINE_WRAP  "True"
74 #define DEF_ALIGNMENT  "Center"
75 #define DEF_SMOOTH     "True"
76 #define DEF_THICK      "True"
77 #define DEF_FADE       "True"
78 #define DEF_TEXTURES   "True"
79 #define DEF_DEBUG      "False"
80
81 /* Utopia 800 needs 64 512x512 textures (4096x4096 bitmap).
82    Utopia 720 needs 16 512x512 textures (2048x2048 bitmap).
83    Utopia 480 needs 16 512x512 textures (2048x2048 bitmap).
84    Utopia 400 needs  4 512x512 textures (1024x1024 bitmap).
85    Utopia 180 needs  1 512x512 texture.
86    Times  240 needs  1 512x512 texture.
87  */
88 #define DEF_FONT       "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-iso8859-1"
89
90 #define TAB_WIDTH        8
91
92 #define MAX_THICK_LINES   25
93 #define FONT_WEIGHT       14
94 #define KEEP_ASPECT
95
96 #include "texfont.h"
97 #include "glutstroke.h"
98 #include "glut_roman.h"
99 #define GLUT_FONT (&glutStrokeRoman)
100
101 typedef struct {
102   Display *dpy;
103   GLXContext *glx_context;
104
105   GLuint text_list, star_list;
106   texture_font_data *texfont;
107   int polygon_count;
108
109   FILE *pipe;
110   XtInputId pipe_id;
111   XtIntervalId pipe_timer;
112   Time subproc_relaunch_delay;
113
114   char *buf;
115   int buf_size;
116   int buf_tail;
117
118   char **lines;
119   int total_lines;
120
121   double star_theta;
122   double char_width;
123   double line_height;
124   double font_scale;
125   double intra_line_scroll;
126
127   int line_pixel_width;   /* in font units (for wrapping text) */
128   int line_pixel_height;  /* in screen units (for computing line thickness) */
129   GLfloat line_thickness;
130
131 } sws_configuration;
132
133
134 static sws_configuration *scs = NULL;
135
136 static char *program;
137 static int max_lines;
138 static int scroll_steps;
139 static float star_spin;
140 static float font_size;
141 static int target_columns;
142 static int wrap_p;
143 static int smooth_p;
144 static int thick_p;
145 static int fade_p;
146 static int textures_p;
147 static int debug_p;
148 static char *alignment_str;
149 static int alignment;
150
151 static XrmOptionDescRec opts[] = {
152   {"-program",     ".program",   XrmoptionSepArg, 0 },
153   {"-lines",       ".lines",     XrmoptionSepArg, 0 },
154   {"-steps",       ".steps",     XrmoptionSepArg, 0 },
155   {"-spin",        ".spin",      XrmoptionSepArg, 0 },
156   {"-size",        ".size",      XrmoptionSepArg, 0 },
157   {"-columns",     ".columns",   XrmoptionSepArg, 0 },
158 /*{"-font",        ".font",      XrmoptionSepArg, 0 },*/
159   {"-fade",        ".fade",      XrmoptionNoArg,  "True"   },
160   {"-no-fade",     ".fade",      XrmoptionNoArg,  "False"  },
161   {"-textures",    ".textures",  XrmoptionNoArg,  "True"   },
162   {"-smooth",      ".smooth",    XrmoptionNoArg,  "True"   },
163   {"-no-smooth",   ".smooth",    XrmoptionNoArg,  "False"  },
164   {"-thick",       ".thick",     XrmoptionNoArg,  "True"   },
165   {"-no-thick",    ".thick",     XrmoptionNoArg,  "False"  },
166   {"-no-textures", ".textures",  XrmoptionNoArg,  "False"  },
167   {"-wrap",        ".lineWrap",  XrmoptionNoArg,  "True"   },
168   {"-no-wrap",     ".lineWrap",  XrmoptionNoArg,  "False"  },
169   {"-nowrap",      ".lineWrap",  XrmoptionNoArg,  "False"  },
170   {"-alignment",   ".alignment", XrmoptionSepArg, 0        },
171   {"-left",        ".alignment", XrmoptionNoArg,  "Left"   },
172   {"-right",       ".alignment", XrmoptionNoArg,  "Right"  },
173   {"-center",      ".alignment", XrmoptionNoArg,  "Center" },
174   {"-debug",       ".debug",     XrmoptionNoArg,  "True"   },
175 };
176
177 static argtype vars[] = {
178   {&program,        "program",   "Program",    DEF_PROGRAM,   t_String},
179   {&max_lines,      "lines",     "Integer",    DEF_LINES,     t_Int},
180   {&scroll_steps,   "steps",     "Integer",    DEF_STEPS,     t_Int},
181   {&star_spin,      "spin",      "Float",      DEF_SPIN,      t_Float},
182   {&font_size,      "size",      "Float",      DEF_SIZE,      t_Float},
183   {&target_columns, "columns",   "Integer",    DEF_COLUMNS,   t_Int},
184   {&wrap_p,         "lineWrap",  "Boolean",    DEF_LINE_WRAP, t_Bool},
185   {&alignment_str,  "alignment", "Alignment",  DEF_ALIGNMENT, t_String},
186   {&smooth_p,       "smooth",    "Boolean",    DEF_SMOOTH,    t_Bool},
187   {&thick_p,        "thick",     "Boolean",    DEF_THICK,     t_Bool},
188   {&fade_p,         "fade",      "Boolean",    DEF_FADE,      t_Bool},
189   {&textures_p,     "textures",  "Boolean",    DEF_TEXTURES,  t_Bool},
190   {&debug_p,        "debug",     "Boolean",    DEF_DEBUG,     t_Bool},
191 };
192
193 ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
194
195
196
197 /* Tabs are bad, mmmkay? */
198
199 static char *
200 untabify (const char *string)
201 {
202   const char *ostring = string;
203   char *result = (char *) malloc ((strlen(string) * 8) + 1);
204   char *out = result;
205   int col = 0;
206   while (*string)
207     {
208       if (*string == '\t')
209         {
210           do {
211             col++;
212             *out++ = ' ';
213           } while (col % TAB_WIDTH);
214           string++;
215         }
216       else if (*string == '\r' || *string == '\n')
217         {
218           *out++ = *string++;
219           col = 0;
220         }
221       else if (*string == '\010')    /* backspace */
222         {
223           if (string > ostring)
224             out--, string++;
225         }
226       else
227         {
228           *out++ = *string++;
229           col++;
230         }
231     }
232   *out = 0;
233
234   return result;
235 }
236
237 static void
238 strip (char *s, Bool leading, Bool trailing)
239 {
240   int L = strlen(s);
241   if (trailing)
242     while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
243       s[L--] = 0;
244   if (leading)
245     {
246       char *s2 = s;
247       while (*s2 == ' ' || *s2 == '\t')
248         s2++;
249       if (s == s2)
250         return;
251       while (*s2)
252         *s++ = *s2++;
253       *s = 0;
254     }
255 }
256
257
258 /* The GLUT font only has ASCII characters in them, so do what we can to
259    convert Latin1 characters to the nearest ASCII equivalent... 
260  */
261 static void
262 latin1_to_ascii (char *s)
263 {
264   unsigned char *us = (unsigned char *) s;
265   const unsigned char ascii[95] = {
266     '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
267     '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
268     '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
269     'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
270     'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
271     'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
272     'u', 'u', 'y', 'p', 'y' };
273   while (*us)
274     {
275       if (*us >= 161)
276         *us = ascii[*us - 161];
277       else if (*us > 127)
278         *us = '?';
279       us++;
280     }
281 }
282
283 \f
284 /* Subprocess.
285    (This bit mostly cribbed from phosphor.c)
286  */
287
288 static void drain_input (sws_configuration *sc);
289
290 static void
291 subproc_cb (XtPointer closure, int *source, XtInputId *id)
292 {
293   sws_configuration *sc = (sws_configuration *) closure;
294   drain_input (sc);
295 }
296
297
298 static void
299 launch_text_generator (sws_configuration *sc)
300 {
301   XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
302   char *oprogram = get_string_resource (sc->dpy, "program", "Program");
303   char *program = (char *) malloc (strlen (oprogram) + 10);
304   strcpy (program, "( ");
305   strcat (program, oprogram);
306   strcat (program, " ) 2>&1");
307
308   if ((sc->pipe = popen (program, "r")))
309     {
310       sc->pipe_id =
311         XtAppAddInput (app, fileno (sc->pipe),
312                        (XtPointer) (XtInputReadMask | XtInputExceptMask),
313                        subproc_cb, (XtPointer) sc);
314     }
315   else
316     {
317       perror (program);
318     }
319 }
320
321
322 static void
323 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
324 {
325   sws_configuration *sc = (sws_configuration *) closure;
326   if (!sc->pipe_timer) abort();
327   sc->pipe_timer = 0;
328   launch_text_generator (sc);
329 }
330
331
332 /* When the subprocess has generated some output, this reads as much as it
333    can into sc->buf at sc->buf_tail.
334  */
335 static void
336 drain_input (sws_configuration *sc)
337 {
338   XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
339   if (sc->buf_tail < sc->buf_size - 2)
340     {
341       int target = sc->buf_size - sc->buf_tail - 2;
342       int n = (sc->pipe
343                ? read (fileno (sc->pipe),
344                        (void *) (sc->buf + sc->buf_tail),
345                        target)
346                : 0);
347       if (n > 0)
348         {
349           sc->buf_tail += n;
350           sc->buf[sc->buf_tail] = 0;
351         }
352       else
353         {
354           if (sc->pipe)
355             {
356               XtRemoveInput (sc->pipe_id);
357               sc->pipe_id = 0;
358               pclose (sc->pipe);
359               sc->pipe = 0;
360             }
361
362           /* If the process didn't print a terminating newline, add one. */
363           if (sc->buf_tail > 1 &&
364               sc->buf[sc->buf_tail-1] != '\n')
365             {
366               sc->buf[sc->buf_tail++] = '\n';
367               sc->buf[sc->buf_tail] = 0;
368             }
369
370           /* Then add one more, just for giggles. */
371           sc->buf[sc->buf_tail++] = '\n';
372           sc->buf[sc->buf_tail] = 0;
373
374           /* Set up a timer to re-launch the subproc in a bit. */
375           sc->pipe_timer = XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
376                                             relaunch_generator_timer,
377                                             (XtPointer) sc);
378         }
379     }
380 }
381
382
383 static int
384 string_width (sws_configuration *sc, const char *s)
385 {
386   if (textures_p)
387     return texture_string_width (sc->texfont, s, 0);
388   else
389     return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
390 }
391
392 static int
393 char_width (sws_configuration *sc, char c)
394 {
395   char s[2];
396   s[0] = c;
397   s[1] = 0;
398   return string_width (sc, s);
399 }
400
401
402 /* Populates the sc->lines list with as many lines as are currently in
403    sc->buf (which was filled by drain_input().
404  */
405 static void
406 get_more_lines (sws_configuration *sc)
407 {
408   /* wrap anyway, if it's absurdly long. */
409   int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
410   
411   int col = 0;
412   int col_pix = 0;
413
414   char *s = sc->buf;
415   while (sc->total_lines < max_lines)
416     {
417       int cw;
418
419       if (s >= sc->buf + sc->buf_tail)
420         /* Reached end of buffer before end of line.  Bail. */
421         return;
422
423       cw = char_width (sc, *s);
424
425       if (*s == '\r' || *s == '\n' ||
426           col_pix + cw >= wrap_pix)
427         {
428           int L = s - sc->buf;
429
430           if (*s == '\r' || *s == '\n')
431             {
432               if (*s == '\r' && s[1] == '\n')  /* swallow CRLF too */
433                 *s++ = 0;
434
435               *s++ = 0;
436             }
437           else
438             {
439               /* We wrapped -- try to back up to the previous word boundary. */
440               char *s2 = s;
441               int n = 0;
442               while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
443                 s2--, n++;
444               if (s2 > sc->buf)
445                 {
446                   s = s2;
447                   *s++ = 0;
448                   L = s - sc->buf;
449                 }
450             }
451
452           sc->lines[sc->total_lines] = (char *) malloc (L+1);
453           memcpy (sc->lines[sc->total_lines], sc->buf, L);
454           sc->lines[sc->total_lines][L] = 0;
455
456           if (!textures_p)
457             latin1_to_ascii (sc->lines[sc->total_lines]);
458
459           {
460             char *t = sc->lines[sc->total_lines];
461             char *ut = untabify (t);
462             strip (ut, (alignment == 0), 1); /* if centering, strip
463                                                 leading whitespace too */
464             sc->lines[sc->total_lines] = ut;
465             free (t);
466           }
467
468           sc->total_lines++;
469
470           if (sc->buf_tail > (s - sc->buf))
471             {
472               int i = sc->buf_tail - (s - sc->buf);
473               memmove (sc->buf, s, i);
474               sc->buf_tail = i;
475               sc->buf[sc->buf_tail] = 0;
476             }
477           else
478             {
479               sc->buf_tail = 0;
480             }
481
482           sc->buf[sc->buf_tail] = 0;
483           s = sc->buf;
484           col = 0;
485           col_pix = 0;
486         }
487       else
488         {
489           col++;
490           col_pix += cw;
491           if (*s == '\t')
492             {
493               int tab_pix = TAB_WIDTH * sc->char_width;
494               col     = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
495               col_pix = tab_pix   * ((col / tab_pix)   + 1);
496             }
497           s++;
498         }
499     }
500 }
501
502
503 static void
504 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
505 {
506   const char *os = s;
507   if (!s || !*s) return;
508   glPushMatrix ();
509   glTranslatef (x, y, 0);
510
511   if (textures_p)
512     print_texture_string (sc->texfont, s);
513   else
514     while (*s)
515       glutStrokeCharacter (GLUT_FONT, *s++);
516   glPopMatrix ();
517
518   if (debug_p)
519     {
520       GLfloat w;
521       GLfloat h = sc->line_height / sc->font_scale;
522       char c[2];
523       c[1]=0;
524       s = os;
525       if (textures_p) glDisable (GL_TEXTURE_2D);
526       glLineWidth (1);
527       glColor3f (0.4, 0.4, 0.4);
528       glPushMatrix ();
529       glTranslatef (x, y, 0);
530       while (*s)
531         {
532           *c = *s++;
533           w = string_width (sc, c);
534           glBegin (GL_LINE_LOOP);
535           glVertex3f (0, 0, 0);
536           glVertex3f (w, 0, 0);
537           glVertex3f (w, h, 0);
538           glVertex3f (0, h, 0);
539           glEnd();
540           glTranslatef (w, 0, 0);
541         }
542       glPopMatrix ();
543       if (textures_p) glEnable (GL_TEXTURE_2D);
544     }
545 }
546
547
548 static void
549 grid (double width, double height, double xspacing, double yspacing, double z)
550 {
551   double x, y;
552   for (y = 0; y <= height/2; y += yspacing)
553     {
554       glBegin(GL_LINES);
555       glVertex3f(-width/2,  y, z);
556       glVertex3f( width/2,  y, z);
557       glVertex3f(-width/2, -y, z);
558       glVertex3f( width/2, -y, z);
559       glEnd();
560     }
561   for (x = 0; x <= width/2; x += xspacing)
562     {
563       glBegin(GL_LINES);
564       glVertex3f( x, -height/2, z);
565       glVertex3f( x,  height/2, z);
566       glVertex3f(-x, -height/2, z);
567       glVertex3f(-x,  height/2, z);
568       glEnd();
569     }
570
571   glBegin(GL_LINES);
572   glVertex3f(-width, 0, z);
573   glVertex3f( width, 0, z);
574   glVertex3f(0, -height, z);
575   glVertex3f(0,  height, z);
576   glEnd();
577 }
578
579 static void
580 box (double width, double height, double depth)
581 {
582   glBegin(GL_LINE_LOOP);
583   glVertex3f(-width/2,  -height/2, -depth/2);
584   glVertex3f(-width/2,   height/2, -depth/2);
585   glVertex3f( width/2,   height/2, -depth/2);
586   glVertex3f( width/2,  -height/2, -depth/2);
587   glEnd();
588   glBegin(GL_LINE_LOOP);
589   glVertex3f(-width/2,  -height/2,  depth/2);
590   glVertex3f(-width/2,   height/2,  depth/2);
591   glVertex3f( width/2,   height/2,  depth/2);
592   glVertex3f( width/2,  -height/2,  depth/2);
593   glEnd();
594   glBegin(GL_LINE_LOOP);
595   glVertex3f(-width/2,  -height/2, -depth/2);
596   glVertex3f(-width/2,  -height/2,  depth/2);
597   glVertex3f(-width/2,   height/2,  depth/2);
598   glVertex3f(-width/2,   height/2, -depth/2);
599   glEnd();
600   glBegin(GL_LINE_LOOP);
601   glVertex3f( width/2,  -height/2, -depth/2);
602   glVertex3f( width/2,  -height/2,  depth/2);
603   glVertex3f( width/2,   height/2,  depth/2);
604   glVertex3f( width/2,   height/2, -depth/2);
605   glEnd();
606
607   glBegin(GL_LINES);
608   glVertex3f(-width/2,   height/2,  depth/2);
609   glVertex3f(-width/2,  -height/2, -depth/2);
610
611   glVertex3f( width/2,   height/2,  depth/2);
612   glVertex3f( width/2,  -height/2, -depth/2);
613
614   glVertex3f(-width/2,  -height/2,  depth/2);
615   glVertex3f(-width/2,   height/2, -depth/2);
616
617   glVertex3f( width/2,  -height/2,  depth/2);
618   glVertex3f( width/2,   height/2, -depth/2);
619   glEnd();
620 }
621
622
623 /* Construct stars (number of stars is dependent on size of screen) */
624 static void
625 init_stars (ModeInfo *mi, int width, int height)
626 {
627   sws_configuration *sc = &scs[MI_SCREEN(mi)];
628   int i, j;
629   int size = (width > height ? width : height);
630   int nstars = size * size / 320;
631   int max_size = 3;
632   GLfloat inc = 0.5;
633   int steps = max_size / inc;
634
635   glDeleteLists (sc->star_list, 1);
636   sc->star_list = glGenLists (1);
637   glNewList (sc->star_list, GL_COMPILE);
638
639   glEnable(GL_POINT_SMOOTH);
640
641   for (j = 1; j <= steps; j++)
642     {
643       glPointSize(inc * j);
644       glBegin (GL_POINTS);
645       for (i = 0; i < nstars / steps; i++)
646         {
647           glColor3f (0.6 + frand(0.3),
648                      0.6 + frand(0.3),
649                      0.6 + frand(0.3));
650           glVertex2f (2 * size * (0.5 - frand(1.0)),
651                       2 * size * (0.5 - frand(1.0)));
652         }
653       glEnd ();
654     }
655   glEndList ();
656 }
657
658
659 /* Window management, etc
660  */
661 ENTRYPOINT void
662 reshape_sws (ModeInfo *mi, int width, int height)
663 {
664   sws_configuration *sc = &scs[MI_SCREEN(mi)];
665
666   /* Set up matrices for perspective text display
667    */
668   {
669     GLfloat desired_aspect = (GLfloat) 3/4;
670     int w = mi->xgwa.width;
671     int h = mi->xgwa.height;
672     int yoff = 0;
673
674 #ifdef KEEP_ASPECT
675     {
676       int h2 = w * desired_aspect;
677       yoff = (h - h2) / 2;      /* Wide window: letterbox at top and bottom. */
678       if (yoff < 0) yoff = 0;   /* Tall window: clip off the top. */
679       h = h2;
680     }
681 #endif
682
683     glMatrixMode (GL_PROJECTION);
684     glViewport (0, yoff, w, h);
685
686     glMatrixMode (GL_MODELVIEW);
687     glLoadIdentity ();
688     gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
689     gluLookAt (0.0, 0.0, 4600.0,
690                0.0, 0.0, 0.0,
691                0.0, 1.0, 0.0);
692     glRotatef (-60.0, 1.0, 0.0, 0.0);
693
694 #if 0
695     glRotatef (60.0, 1.0, 0.0, 0.0);
696     glTranslatef (260, 3200, 0);
697     glScalef (1.85, 1.85, 1);
698 #endif
699
700     /* The above gives us an arena where the bottom edge of the screen is
701        represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
702
703     /* Now let's move the origin to the front of the screen. */
704     glTranslatef (0.0, -3140, 0.0);
705
706     /* And then let's scale so that the bottom of the screen is 1.0 wide. */
707     glScalef (4200, 4200, 4200);
708   }
709
710
711   /* Compute the height in pixels of the line at the bottom of the screen. */
712   {
713     GLdouble mm[17], pm[17];
714     GLint vp[5];
715     GLfloat x = 0.5, y1 = 0, z = 0;
716     GLfloat y2 = sc->line_height;
717     GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
718
719     glGetDoublev (GL_MODELVIEW_MATRIX, mm);
720     glGetDoublev (GL_PROJECTION_MATRIX, pm);
721     glGetIntegerv (GL_VIEWPORT, vp);
722     gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
723     gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
724     sc->line_pixel_height = (wy2 - wy1);
725     glLineWidth (1);
726   }
727
728   /* Compute the best looking line thickness for the bottom line.
729    */
730   if (!thick_p)
731     sc->line_thickness = 1.0;
732   else
733     sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
734
735   if (sc->line_thickness < 1.2)
736     sc->line_thickness = 1.0;
737 }
738
739
740 static void
741 gl_init (ModeInfo *mi)
742 {
743   sws_configuration *sc = &scs[MI_SCREEN(mi)];
744
745   program = get_string_resource (mi->dpy, "program", "Program");
746
747   glDisable (GL_LIGHTING);
748   glDisable (GL_DEPTH_TEST);
749
750   if (smooth_p)
751     {
752       glEnable (GL_LINE_SMOOTH);
753       glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
754       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
755       glEnable (GL_BLEND);
756     }
757
758   sc->text_list = glGenLists (1);
759   glNewList (sc->text_list, GL_COMPILE);
760   glEndList ();
761
762   sc->star_list = glGenLists (1);
763   glNewList (sc->star_list, GL_COMPILE);
764   glEndList ();
765
766   sc->line_thickness = 1.0;
767 }
768
769
770 ENTRYPOINT void 
771 init_sws (ModeInfo *mi)
772 {
773   double font_height;
774
775   sws_configuration *sc = 0;
776
777   if (!scs) {
778     scs = (sws_configuration *)
779       calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
780     if (!scs) {
781       fprintf(stderr, "%s: out of memory\n", progname);
782       exit(1);
783     }
784   }
785
786   sc = &scs[MI_SCREEN(mi)];
787
788   sc->dpy = MI_DISPLAY(mi);
789   sc = &scs[MI_SCREEN(mi)];
790   sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
791
792   if ((sc->glx_context = init_GL(mi)) != NULL) {
793     gl_init(mi);
794     reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
795     clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
796
797     init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
798   }
799
800   if (textures_p)
801     {
802       int cw, lh;
803       sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
804       cw = texture_string_width (sc->texfont, "n", &lh);
805       sc->char_width = cw;
806       font_height = lh;
807       glEnable(GL_ALPHA_TEST);
808       glEnable (GL_TEXTURE_2D);
809
810       check_gl_error ("loading font");
811
812       /* "Anistropic filtering helps for quadrilateral-angled textures.
813          A sharper image is accomplished by interpolating and filtering
814          multiple samples from one or more mipmaps to better approximate
815          very distorted textures.  This is the next level of filtering
816          after trilinear filtering." */
817       if (smooth_p && 
818           strstr ((char *) glGetString(GL_EXTENSIONS),
819                   "GL_EXT_texture_filter_anisotropic"))
820       {
821         GLfloat anisotropic = 0.0;
822         glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
823         if (anisotropic >= 1.0)
824           glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 
825                            anisotropic);
826       }
827     }
828   else
829     {
830       font_height = GLUT_FONT->top - GLUT_FONT->bottom;
831       sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
832     }
833   
834   sc->font_scale = 1.0 / sc->char_width;
835
836
837   /* We consider a font that consumes 80 columns to be "18 points".
838
839      If neither -size nor -columns was specified, default to 60 columns
840      (which is 24 points.)
841
842      If both were specified, -columns has priority.
843    */
844   {
845     int base_col  = 80;
846     int base_size = 18;
847
848     if (target_columns <= 0 && font_size <= 0)
849       target_columns = 60;
850
851     if (target_columns > 0)
852       font_size = base_size * (base_col / (double) target_columns);
853     else if (font_size > 0)
854       target_columns = base_col * (base_size / (double) font_size);
855   }
856
857   sc->line_pixel_width = target_columns * sc->char_width;
858
859   sc->font_scale /= target_columns;
860   sc->line_height = font_height * sc->font_scale;
861
862
863   /* Buffer only two lines of text.
864      If the buffer is too big, there's a significant delay between
865      when the program launches and when the text appears, which can be
866      irritating for time-sensitive output (clock, current music, etc.)
867    */
868   sc->buf_size = target_columns * 2;
869   if (sc->buf_size < 80) sc->buf_size = 80;
870   sc->buf = (char *) calloc (1, sc->buf_size);
871
872   sc->subproc_relaunch_delay = 2 * 1000;   /* 2 seconds */
873   sc->total_lines = max_lines-1;
874
875   if (random() & 1)
876     star_spin = -star_spin;
877
878   if (!alignment_str || !*alignment_str ||
879       !strcasecmp(alignment_str, "left"))
880     alignment = -1;
881   else if (!strcasecmp(alignment_str, "center") ||
882            !strcasecmp(alignment_str, "middle"))
883     alignment = 0;
884   else if (!strcasecmp(alignment_str, "right"))
885     alignment = 1;
886   else
887     {
888       fprintf (stderr,
889                "%s: alignment must be left, center, or right, not \"%s\"\n",
890                progname, alignment_str);
891       exit (1);
892     }
893
894   launch_text_generator (sc);
895
896   /* one more reshape, after line_height has been computed */
897   reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
898 }
899
900
901 static void
902 draw_stars (ModeInfo *mi)
903 {
904   sws_configuration *sc = &scs[MI_SCREEN(mi)];
905
906   glMatrixMode (GL_PROJECTION);
907   glPushMatrix ();
908   {
909     glLoadIdentity ();
910
911     glMatrixMode (GL_MODELVIEW);
912     glPushMatrix ();
913     {
914       glLoadIdentity ();
915       glOrtho (-0.5 * MI_WIDTH(mi),  0.5 * MI_WIDTH(mi),
916                -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
917                -100.0, 100.0);
918       glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
919       if (textures_p) glDisable (GL_TEXTURE_2D);
920       glCallList (sc->star_list);
921       if (textures_p) glEnable (GL_TEXTURE_2D);
922     }
923     glPopMatrix ();
924   }
925   glMatrixMode (GL_PROJECTION);
926   glPopMatrix ();
927 }
928
929 ENTRYPOINT void
930 draw_sws (ModeInfo *mi)
931 {
932   sws_configuration *sc = &scs[MI_SCREEN(mi)];
933 /*  XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
934   Display *dpy = MI_DISPLAY(mi);
935   Window window = MI_WINDOW(mi);
936   int i;
937
938   if (!sc->glx_context)
939     return;
940
941 #if 0
942   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
943     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
944 #endif
945
946   glDrawBuffer (GL_BACK);
947   glXMakeCurrent (dpy, window, *(sc->glx_context));
948
949   glClear (GL_COLOR_BUFFER_BIT);
950
951   draw_stars (mi);
952
953   glMatrixMode (GL_MODELVIEW);
954   glPushMatrix ();
955
956   if (debug_p)
957     {
958       int i;
959       glPushMatrix ();
960       if (textures_p) glDisable (GL_TEXTURE_2D);
961       glLineWidth (1);
962       glColor3f (0.4, 0.4, 0.4);
963       glTranslatef (0,-1, 0);
964       for (i = 0; i < 16; i++)
965         {
966           box (1, 1, 1);
967           grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
968           glTranslatef(0, 1, 0);
969         }
970       if (textures_p) glEnable (GL_TEXTURE_2D);
971       glPopMatrix ();
972     }
973
974   /* Scroll to current position */
975   glTranslatef (0.0, sc->intra_line_scroll, 0.0);
976
977   glColor3f (1.0, 1.0, 0.4);
978   glCallList (sc->text_list);
979   mi->polygon_count = sc->polygon_count;
980
981   sc->intra_line_scroll += sc->line_height / scroll_steps;
982
983   if (sc->intra_line_scroll >= sc->line_height)
984     {
985       sc->intra_line_scroll = 0;
986
987       /* Drop the oldest line off the end. */
988       if (sc->lines[0])
989         free (sc->lines[0]);
990
991       /* Scroll the contents of the lines array toward 0. */
992       if (sc->total_lines > 0)
993         {
994           for (i = 1; i < sc->total_lines; i++)
995             sc->lines[i-1] = sc->lines[i];
996           sc->lines[--sc->total_lines] = 0;
997         }
998
999       /* Bring in new lines at the end. */
1000       get_more_lines (sc);
1001
1002       if (sc->total_lines < max_lines)
1003         /* Oops, we ran out of text... well, insert some blank lines
1004            here so that new text still pulls in from the bottom of
1005            the screen, isntead of just appearing. */
1006         sc->total_lines = max_lines;
1007
1008       glDeleteLists (sc->text_list, 1);
1009       sc->text_list = glGenLists (1);
1010       glNewList (sc->text_list, GL_COMPILE);
1011       sc->polygon_count = 0;
1012       glPushMatrix ();
1013       glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
1014       for (i = 0; i < sc->total_lines; i++)
1015         {
1016           double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
1017           int offscreen_lines = 2;
1018
1019           double x = -0.5;
1020           double y =  ((sc->total_lines - (i + offscreen_lines) - 1)
1021                        * sc->line_height);
1022           double xoff = 0;
1023           char *line = sc->lines[i];
1024
1025           if (debug_p)
1026             {
1027               double xx = x * 1.4;  /* a little more to the left */
1028               char n[20];
1029               sprintf(n, "%d:", i);
1030               draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
1031             }
1032
1033           if (!line || !*line)
1034             continue;
1035
1036           if (sc->line_thickness != 1 && !textures_p)
1037             {
1038               int max_thick_lines = MAX_THICK_LINES;
1039               GLfloat thinnest_line = 1.0;
1040               GLfloat thickest_line = sc->line_thickness;
1041               GLfloat range = thickest_line - thinnest_line;
1042               GLfloat thickness;
1043
1044               int j = sc->total_lines - i - 1;
1045
1046               if (j > max_thick_lines)
1047                 thickness = thinnest_line;
1048               else
1049                 thickness = (thinnest_line +
1050                              (range * ((max_thick_lines - j) /
1051                                        (GLfloat) max_thick_lines)));
1052
1053               glLineWidth (thickness);
1054             }
1055
1056           if (alignment >= 0)
1057             {
1058               int n = string_width (sc, line);
1059               xoff = 1.0 - (n * sc->font_scale);
1060             }
1061
1062           if (alignment == 0)
1063             xoff /= 2;
1064
1065           glColor3f (fade, fade, 0.5 * fade);
1066           draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1067                        line);
1068           if (textures_p)
1069             sc->polygon_count += strlen (line);
1070         }
1071       glPopMatrix ();
1072       glEndList ();
1073     }
1074
1075   glPopMatrix ();
1076
1077   if (mi->fps_p) do_fps (mi);
1078   glFinish();
1079   glXSwapBuffers(dpy, window);
1080
1081   sc->star_theta += star_spin;
1082 }
1083
1084 ENTRYPOINT void
1085 release_sws (ModeInfo *mi)
1086 {
1087   if (scs) {
1088     int screen;
1089     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1090       sws_configuration *sc = &scs[screen];
1091       if (sc->pipe_id)
1092         XtRemoveInput (sc->pipe_id);
1093       if (sc->pipe)
1094         pclose (sc->pipe);
1095       if (sc->pipe_timer)
1096         XtRemoveTimeOut (sc->pipe_timer);
1097
1098       /* #### there's more to free here */
1099     }
1100     free (scs);
1101     scs = 0;
1102   }
1103   FreeAllGL(mi);
1104 }
1105
1106
1107 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)
1108
1109 #endif /* USE_GL */