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