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