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