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