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