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