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