From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / fliptext.c
1 /*
2  * fliptext, Copyright (c) 2005-2015 Jamie Zawinski <jwz@jwz.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
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif /* HAVE_CONFIG_H */
16
17 #define DEF_FONT       "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-*-*"
18 #define DEF_COLOR      "#00CCFF"
19
20 #define DEFAULTS "*delay:        10000      \n" \
21                  "*showFPS:      False      \n" \
22                  "*wireframe:    False      \n" \
23                  "*usePty:       False      \n" \
24                  "*texFontCacheSize: 60     \n" \
25                  "*font:       " DEF_FONT  "\n" \
26                  ".foreground: " DEF_COLOR "\n" \
27                  "*program: xscreensaver-text --cols 0"  /* don't wrap */
28
29 # define refresh_fliptext 0
30 # define release_fliptext 0
31 # define fliptext_handle_event 0
32 #undef countof
33 #define countof(x) (sizeof((x))/sizeof((*x)))
34
35 #undef BELLRAND
36 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
37
38 #include "xlockmore.h"
39 #include "texfont.h"
40 #include "textclient.h"
41
42 #ifdef USE_GL /* whole file */
43
44 /* Should be in <GL/glext.h> */
45 # ifndef  GL_TEXTURE_MAX_ANISOTROPY_EXT
46 #  define GL_TEXTURE_MAX_ANISOTROPY_EXT     0x84FE
47 # endif
48 # ifndef  GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
49 #  define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
50 # endif
51
52
53 #define DEF_LINES      "8"
54 #define DEF_FONT_SIZE  "20"
55 #define DEF_COLUMNS    "80"
56 #define DEF_ALIGNMENT  "random"
57 #define DEF_SPEED       "1.0"
58 #define TAB_WIDTH        8
59
60 #define FONT_WEIGHT       14
61 #define KEEP_ASPECT
62
63 typedef enum { NEW, HESITATE, IN, LINGER, OUT, DEAD } line_state;
64 typedef enum { SCROLL_BOTTOM, SCROLL_TOP, SPIN } line_anim_type;
65
66 typedef struct { GLfloat x, y, z; } XYZ;
67
68 typedef struct {
69   char *text;
70   GLfloat width, height;        /* size */
71   XYZ from, to, current;        /* start, end, and current position */
72   GLfloat fth, tth, cth;        /* rotation around Z */
73
74   int cluster_size;             /* how many lines in this cluster */
75   int cluster_pos;              /* position of this line in the cluster */
76
77   line_state state;             /* current motion model */
78   int step, steps;              /* progress along this path */
79   GLfloat color[4];
80
81 } line;
82
83
84 typedef struct {
85   Display *dpy;
86   GLXContext *glx_context;
87
88   texture_font_data *texfont;
89   text_data *tc;
90
91   int alignment;
92
93   char *buf;
94   int buf_size;
95   int buf_tail;
96
97   int char_width;         /* in font units */
98   int line_height;        /* in font units */
99   double font_scale;      /* convert font units to display units */
100
101   int font_wrap_pixels;   /* in font units (for wrapping text) */
102
103   int top_margin, bottom_margin;
104   int left_margin, right_margin;
105
106   int nlines;
107   int lines_size;
108   line **lines;
109
110   line_anim_type anim_type;
111   XYZ in, mid, out;
112   XYZ rotation;
113   GLfloat color[4];
114
115 } fliptext_configuration;
116
117
118 static fliptext_configuration *scs = NULL;
119
120 static int max_lines, min_lines;
121 static float font_size;
122 static int target_columns;
123 static char *alignment_str;
124 static int alignment_random_p;
125 static GLfloat speed;
126
127 static XrmOptionDescRec opts[] = {
128   {"-lines",       ".lines",     XrmoptionSepArg, 0 },
129   {"-size",        ".fontSize",  XrmoptionSepArg, 0 },
130   {"-columns",     ".columns",   XrmoptionSepArg, 0 },
131   {"-speed",       ".speed",     XrmoptionSepArg, 0 },
132 /*{"-font",        ".font",      XrmoptionSepArg, 0 },*/
133   {"-alignment",   ".alignment", XrmoptionSepArg, 0 },
134   {"-left",        ".alignment", XrmoptionNoArg,  "Left"   },
135   {"-right",       ".alignment", XrmoptionNoArg,  "Right"  },
136   {"-center",      ".alignment", XrmoptionNoArg,  "Center" },
137 };
138
139 static argtype vars[] = {
140   {&max_lines,      "lines",     "Integer",    DEF_LINES,     t_Int},
141   {&font_size,      "fontSize",  "Float",      DEF_FONT_SIZE, t_Float},
142   {&target_columns, "columns",   "Integer",    DEF_COLUMNS,   t_Int},
143   {&alignment_str,  "alignment", "Alignment",  DEF_ALIGNMENT, t_String},
144   {&speed,          "speed",     "Speed",      DEF_SPEED,     t_Float},
145 };
146
147 ENTRYPOINT ModeSpecOpt fliptext_opts = {countof(opts), opts, countof(vars), vars, NULL};
148
149
150
151 /* Tabs are bad, mmmkay? */
152
153 static char *
154 untabify (const char *string)
155 {
156   const char *ostring = string;
157   char *result = (char *) malloc ((strlen(string) * 8) + 1);
158   char *out = result;
159   int col = 0;
160   while (*string)
161     {
162       if (*string == '\t')
163         {
164           do {
165             col++;
166             *out++ = ' ';
167           } while (col % TAB_WIDTH);
168           string++;
169         }
170       else if (*string == '\r' || *string == '\n')
171         {
172           *out++ = *string++;
173           col = 0;
174         }
175       else if (*string == '\010')    /* backspace */
176         {
177           if (string > ostring)
178             out--, string++;
179         }
180       else
181         {
182           *out++ = *string++;
183           col++;
184         }
185     }
186   *out = 0;
187
188   return result;
189 }
190
191 static void
192 strip (char *s, Bool leading, Bool trailing)
193 {
194   int L = strlen(s);
195   if (trailing)
196     while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
197       s[L--] = 0;
198   if (leading)
199     {
200       char *s2 = s;
201       while (*s2 == ' ' || *s2 == '\t')
202         s2++;
203       if (s == s2)
204         return;
205       while (*s2)
206         *s++ = *s2++;
207       *s = 0;
208     }
209 }
210
211
212 static int
213 char_width (fliptext_configuration *sc, char c)
214 {
215   XCharStruct e;
216   char s[2];
217   s[0] = c;
218   s[1] = 0;
219   texture_string_metrics (sc->texfont, s, &e, 0, 0);
220   return e.width;
221 }
222
223
224 /* Returns a single line of text from the output buffer of the subprocess,
225    taking into account wrapping, centering, etc.  Returns 0 if no complete
226    line is currently available.
227  */
228 static char *
229 get_one_line (fliptext_configuration *sc)
230 {
231   char *result = 0;
232   int wrap_pix = sc->font_wrap_pixels;
233   int col = 0;
234   int col_pix = 0;
235   char *s = sc->buf;
236   int target = sc->buf_size - sc->buf_tail - 2;
237
238   /* Fill as much as we can into sc->buf, but stop at newline.
239    */
240   while (target > 0)
241     {
242       int c = textclient_getc (sc->tc);
243       if (c <= 0)
244         break;
245       sc->buf[sc->buf_tail++] = (char) c;
246       sc->buf[sc->buf_tail] = 0;
247       target--;
248       if (c == '\r' || c == '\n')
249         break;
250     }
251
252   while (!result)
253     {
254       int cw;
255
256       if (s >= sc->buf + sc->buf_tail)
257         /* Reached end of buffer before end of line.  Bail. */
258         return 0;
259
260       cw = char_width (sc, *s);
261
262       if (*s == '\r' || *s == '\n' ||
263           col_pix + cw >= wrap_pix)
264         {
265           int L = s - sc->buf;
266
267           if (*s == '\r' || *s == '\n')
268             {
269               if (*s == '\r' && s[1] == '\n')  /* swallow CRLF too */
270                 *s++ = 0;
271
272               *s++ = 0;
273             }
274           else
275             {
276               /* We wrapped -- try to back up to the previous word boundary. */
277               char *s2 = s;
278               int n = 0;
279               while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
280                 s2--, n++;
281               if (s2 > sc->buf)
282                 {
283                   s = s2;
284                   *s++ = 0;
285                   L = s - sc->buf;
286                 }
287             }
288
289           if (result) abort();
290           result = (char *) malloc (L+1);
291           memcpy (result, sc->buf, L);
292           result[L] = 0;
293
294           {
295             char *t = result;
296             char *ut = untabify (t);
297             strip (ut, (sc->alignment == 0), 1); /* if centering, strip
298                                                     leading whitespace too */
299             result = ut;
300             free (t);
301           }
302
303           if (sc->buf_tail > (s - sc->buf))
304             {
305               int i = sc->buf_tail - (s - sc->buf);
306               memmove (sc->buf, s, i);
307               sc->buf_tail = i;
308               sc->buf[sc->buf_tail] = 0;
309             }
310           else
311             {
312               sc->buf_tail = 0;
313             }
314
315           sc->buf[sc->buf_tail] = 0;
316           s = sc->buf;
317           col = 0;
318           col_pix = 0;
319         }
320       else
321         {
322           col++;
323           col_pix += cw;
324           if (*s == '\t')
325             {
326               int tab_pix = TAB_WIDTH * sc->char_width;
327               col     = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
328               col_pix = tab_pix   * ((col / tab_pix)   + 1);
329             }
330           s++;
331         }
332     }
333
334   return result;
335 }
336
337
338 static Bool
339 blank_p (const char *s)
340 {
341   for (; *s; s++)
342     if (*s != ' ' && *s != '\t' && *s != '\r' && *s != '\n')
343       return False;
344   return True;
345 }
346
347 /* Reads some text from the subprocess, and creates and returns a `line'
348    object.  Adds that object to the lines list.   Returns 0 if no text
349    available yet.
350
351    If skip_blanks_p, then keep trying for new lines of text until we
352    get one that is not empty.
353  */
354 static line *
355 make_line (fliptext_configuration *sc, Bool skip_blanks_p)
356 {
357   XCharStruct e;
358   line *ln;
359   char *s;
360
361  AGAIN:
362   s = get_one_line (sc);
363   if (s && skip_blanks_p && blank_p (s))
364     {
365       free (s);
366       goto AGAIN;
367     }
368
369   if (!s) return 0;
370
371   ln = (line *) calloc (1, sizeof(*ln));
372   ln->text = s;
373   ln->state = NEW;
374   texture_string_metrics (sc->texfont, s, &e, 0, 0);
375   ln->width = sc->font_scale * e.width;
376   ln->height = sc->font_scale * sc->line_height;
377
378   memcpy (ln->color, sc->color, sizeof(ln->color));
379
380   sc->nlines++;
381   if (sc->lines_size <= sc->nlines)
382     {
383       sc->lines_size = (sc->lines_size * 1.2) + sc->nlines;
384       sc->lines = (line **)
385         realloc (sc->lines, sc->lines_size * sizeof(*sc->lines));
386       if (! sc->lines)
387         {
388           fprintf (stderr, "%s: out of memory (%d lines)\n",
389                    progname, sc->lines_size);
390           exit (1);
391         }
392     }
393
394   sc->lines[sc->nlines-1] = ln;
395   return ln;
396 }
397
398
399 /* frees the object and removes it from the list.
400  */
401 static void
402 free_line (fliptext_configuration *sc, line *line)
403 {
404   int i;
405   for (i = 0; i < sc->nlines; i++)
406     if (sc->lines[i] == line)
407       break;
408   if (i == sc->nlines) abort();
409   for (; i < sc->nlines-1; i++)
410     sc->lines[i] = sc->lines[i+1];
411   sc->lines[i] = 0;
412   sc->nlines--;
413
414   free (line->text);
415   free (line);
416 }
417
418
419 static void
420 draw_line (ModeInfo *mi, line *line)
421 {
422   int wire = MI_IS_WIREFRAME(mi);
423   fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
424
425   if (! line->text || !*line->text ||
426       line->state == NEW || line->state == HESITATE || line->state == DEAD)
427     return;
428
429   glPushMatrix();
430   glTranslatef (line->current.x, line->current.y, line->current.z);
431
432   glRotatef (line->cth, 0, 1, 0);
433
434   if (sc->alignment == 1)
435     glTranslatef (-line->width, 0, 0);
436   else if (sc->alignment == 0)
437     glTranslatef (-line->width/2, 0, 0);
438
439   glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
440
441   glColor4f (line->color[0], line->color[1], line->color[2], line->color[3]);
442
443   if (!wire)
444     print_texture_string (sc->texfont, line->text);
445   else
446     {
447       int w, h;
448       char *s = line->text;
449       char c[2];
450       c[1]=0;
451       glDisable (GL_TEXTURE_2D);
452       glColor3f (0.4, 0.4, 0.4);
453       while (*s)
454         {
455           XCharStruct e;
456           *c = *s++;
457           texture_string_metrics (sc->texfont, c, &e, 0, 0);
458           w = e.width;
459           h = e.ascent + e.descent;
460           glBegin (GL_LINE_LOOP);
461           glVertex3f (0, 0, 0);
462           glVertex3f (w, 0, 0);
463           glVertex3f (w, h, 0);
464           glVertex3f (0, h, 0);
465           glEnd();
466           glTranslatef (w, 0, 0);
467         }
468     }
469
470 #if 0
471   glDisable (GL_TEXTURE_2D);
472   glColor3f (0.4, 0.4, 0.4);
473   glBegin (GL_LINE_LOOP);
474   glVertex3f (0, 0, 0);
475   glVertex3f (line->width/sc->font_scale, 0, 0);
476   glVertex3f (line->width/sc->font_scale, line->height/sc->font_scale, 0);
477   glVertex3f (0, line->height/sc->font_scale, 0);
478   glEnd();
479   if (!wire) glEnable (GL_TEXTURE_2D);
480 #endif
481
482   glPopMatrix();
483
484   mi->polygon_count += strlen (line->text);
485 }
486
487 static void
488 tick_line (fliptext_configuration *sc, line *line)
489 {
490   int stagger = 30;            /* frames of delay between line spin-outs */
491   int slide   = 600;           /* frames in a slide in/out */
492   int linger  = 0;             /* frames to pause with no motion */
493   double i, ii;
494
495   if (line->state >= DEAD) abort();
496   if (++line->step >= line->steps)
497     {
498       line->state++;
499       line->step = 0;
500
501       if (linger == 0 && line->state == LINGER)
502         line->state++;
503
504       if (sc->anim_type != SPIN)
505         stagger *= 2;
506
507       switch (line->state)
508         {
509         case HESITATE:                  /* entering state HESITATE */
510           switch (sc->anim_type)
511             {
512             case SPIN:
513               line->steps = (line->cluster_pos * stagger);
514               break;
515             case SCROLL_TOP:
516               line->steps = stagger * (line->cluster_size - line->cluster_pos);
517               break;
518             case SCROLL_BOTTOM:
519               line->steps = stagger * line->cluster_pos;
520               break;
521             default:
522               abort();
523             }
524           break;
525
526         case IN:
527           line->color[3] = 0;
528           switch (sc->anim_type)
529             {
530             case SCROLL_BOTTOM:         /* entering state BOTTOM IN */
531               line->from = sc->in;
532               line->to   = sc->mid;
533               line->from.y = (sc->bottom_margin -
534                               (line->height *
535                                (line->cluster_pos + 1)));
536               line->to.y += (line->height *
537                              ((line->cluster_size/2.0) - line->cluster_pos));
538               line->steps = slide;
539               break;
540
541             case SCROLL_TOP:            /* entering state TOP IN */
542               line->from = sc->in;
543               line->to   = sc->mid;
544               line->from.y = (sc->top_margin +
545                               (line->height *
546                                (line->cluster_size - line->cluster_pos)));
547               line->to.y += (line->height *
548                              ((line->cluster_size/2.0) - line->cluster_pos));
549               line->steps = slide;
550               break;
551
552             case SPIN:                  /* entering state SPIN IN */
553               line->from = sc->in;
554               line->to   = sc->mid;
555               line->to.y += (line->height *
556                              ((line->cluster_size/2.0) - line->cluster_pos));
557               line->from.y += (line->height *
558                                ((line->cluster_size/2.0) - line->cluster_pos));
559
560               line->fth = 270;
561               line->tth = 0;
562               line->steps = slide;
563               break;
564
565             default:
566               abort();
567             }
568           break;
569
570         case OUT:
571           switch (sc->anim_type)
572             {
573             case SCROLL_BOTTOM:         /* entering state BOTTOM OUT */
574               line->from = line->to;
575               line->to   = sc->out;
576               line->to.y = (sc->top_margin +
577                             (line->height *
578                              (line->cluster_size - line->cluster_pos)));
579               line->steps = slide;
580               break;
581
582             case SCROLL_TOP:            /* entering state TOP OUT */
583               line->from = line->to;
584               line->to   = sc->out;
585               line->to.y = (sc->bottom_margin -
586                             (line->height *
587                              (line->cluster_pos + 1)));
588               line->steps = slide;
589               break;
590
591             case SPIN:                  /* entering state SPIN OUT */
592               line->from = line->to;
593               line->to   = sc->out;
594               line->to.y += (line->height *
595                              ((line->cluster_size/2.0) - line->cluster_pos));
596
597               line->fth = line->tth;
598               line->tth = -270;
599               line->steps = slide;
600               break;
601
602             default:
603               abort();
604             }
605           break;
606
607         case LINGER:
608           line->from = line->to;
609           line->steps = linger;
610           break;
611
612         default:
613           break;
614         }
615
616       line->steps /= speed;
617     }
618
619   switch (line->state)
620     {
621     case IN:
622     case OUT:
623       i = (double) line->step / line->steps;
624
625       /* Move along the path exponentially, slow side towards the middle. */
626       if (line->state == OUT)
627         ii = i * i;
628       else
629         ii = 1 - ((1-i) * (1-i));
630
631       line->current.x = line->from.x + (ii * (line->to.x - line->from.x));
632       line->current.y = line->from.y + (ii * (line->to.y - line->from.y));
633       line->current.z = line->from.z + (ii * (line->to.z - line->from.z));
634       line->cth = line->fth + (ii * (line->tth - line->fth));
635
636       if (line->state == OUT) ii = 1-ii;
637       line->color[3] = sc->color[3] * ii;
638       break;
639
640     case HESITATE:
641     case LINGER:
642     case DEAD:
643       break;
644     default:
645       abort();
646     }
647 }
648
649
650 /* Start a new cluster of lines going.
651    Pick their anim type, and in, mid, and out positions.
652  */
653 static void
654 reset_lines (ModeInfo *mi)
655 {
656   fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
657   int i;
658   line *prev = 0;
659   GLfloat minx, maxx, miny, maxy, minz, maxz, maxw, maxh;
660
661   sc->rotation.x = 5 - BELLRAND(10);
662   sc->rotation.y = 5 - BELLRAND(10);
663   sc->rotation.z = 5 - BELLRAND(10);
664
665   switch (random() % 8)
666     {
667     case 0:  sc->anim_type = SCROLL_TOP;    break;
668     case 1:  sc->anim_type = SCROLL_BOTTOM; break;
669     default: sc->anim_type = SPIN;          break;
670     }
671
672   minx = sc->left_margin  * 0.9;
673   maxx = sc->right_margin * 0.9;
674
675   miny = sc->bottom_margin * 0.9;
676   maxy = sc->top_margin    * 0.9;
677
678   minz = sc->left_margin  * 5;
679   maxz = sc->right_margin * 2;
680
681   maxw = sc->font_wrap_pixels * sc->font_scale;
682   maxh = max_lines * sc->line_height * sc->font_scale;
683  
684   if (maxw > maxx - minx)
685     maxw = maxx - minx;
686   if (maxh > maxy - miny)
687     maxh = maxy - miny;
688       
689   if (alignment_random_p)
690     sc->alignment = (random() % 3) - 1;
691
692   if      (sc->alignment == -1) maxx -= maxw;
693   else if (sc->alignment ==  1) minx += maxw;
694   else                          minx += maxw/2, maxx -= maxw/2;
695
696   miny += maxh/2;
697   maxy -= maxh/2;
698
699   sc->mid.x = minx + frand (maxx - minx);
700   if (sc->anim_type == SPIN)
701     sc->mid.y = miny + BELLRAND (maxy - miny);
702   else
703     sc->mid.y = miny + frand (maxy - miny);
704
705   sc->in.x  = BELLRAND(sc->right_margin * 2) - sc->right_margin;
706   sc->out.x = BELLRAND(sc->right_margin * 2) - sc->right_margin;
707
708   sc->in.y  = miny + frand(maxy - miny);
709   sc->out.y = miny + frand(maxy - miny);
710
711   sc->in.z  = minz + frand(maxz - minz);
712   sc->out.z = minz + frand(maxz - minz);
713
714   sc->mid.z = 0;
715
716   if (sc->anim_type == SPIN && sc->in.z  > 0) sc->in.z  /= 4;
717   if (sc->anim_type == SPIN && sc->out.z > 0) sc->out.z /= 4;
718
719   for (i = 0; i < max_lines; i++)
720     {
721       line *line = make_line (sc, (i == 0));
722       if (!line) break;                 /* no text available */
723       if (i >= min_lines &&
724           (!line->text || !*line->text))        /* blank after min */
725         break;
726     }
727
728   for (i = 0; i < sc->nlines; i++)
729     {
730       line *line = sc->lines[i];
731       if (!prev)
732         {
733           line->from.y = sc->bottom_margin;
734           line->to.y   = 0;
735         }
736       else
737         {
738           line->from.y = prev->from.y - prev->height;
739           line->to.y   = prev->to.y   - prev->height;
740         }
741       line->cluster_pos = i;
742       line->cluster_size = sc->nlines;
743       prev = line;
744     }
745 }
746
747
748 static void
749 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
750 {
751   XColor c;
752   if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
753     {
754       fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
755       exit (1);
756     }
757   a[0] = c.red   / 65536.0;
758   a[1] = c.green / 65536.0;
759   a[2] = c.blue  / 65536.0;
760   a[3] = 1.0;
761 }
762
763
764 /* Window management, etc
765  */
766 ENTRYPOINT void
767 reshape_fliptext (ModeInfo *mi, int width, int height)
768 {
769   fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
770   GLfloat h = (GLfloat) height / (GLfloat) width;
771
772   glViewport (0, 0, (GLint) width, (GLint) height);
773
774   glMatrixMode(GL_PROJECTION);
775   glLoadIdentity();
776   gluPerspective (60.0, 1/h, 0.01, 100.0);
777
778   glMatrixMode(GL_MODELVIEW);
779   glLoadIdentity();
780   gluLookAt( 0.0, 0.0, 2.6,
781              0.0, 0.0, 0.0,
782              0.0, 1.0, 0.0);
783
784   glClear(GL_COLOR_BUFFER_BIT);
785
786   sc->right_margin = sc->top_margin / h;
787   sc->left_margin = -sc->right_margin;
788 }
789
790
791 static void free_fliptext (ModeInfo *mi);
792
793
794 ENTRYPOINT void 
795 init_fliptext (ModeInfo *mi)
796 {
797   int wire = MI_IS_WIREFRAME(mi);
798
799   fliptext_configuration *sc;
800
801   MI_INIT(mi, scs, free_fliptext);
802
803   sc = &scs[MI_SCREEN(mi)];
804   sc->lines = (line **) calloc (max_lines+1, sizeof(char *));
805
806   sc->dpy = MI_DISPLAY(mi);
807
808   if ((sc->glx_context = init_GL(mi)) != NULL) {
809     reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
810     clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
811   }
812
813   {
814     XCharStruct e;
815     int cw, lh, ascent, descent;
816     sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
817     check_gl_error ("loading font");
818     texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent);
819     cw = e.width;
820     lh = ascent + descent;
821     sc->char_width = cw;
822     sc->line_height = lh;
823   }
824
825   if (!wire)
826     {
827       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
828       glEnable (GL_BLEND);
829       glEnable (GL_ALPHA_TEST);
830       glEnable (GL_TEXTURE_2D);
831
832       /* "Anistropic filtering helps for quadrilateral-angled textures.
833          A sharper image is accomplished by interpolating and filtering
834          multiple samples from one or more mipmaps to better approximate
835          very distorted textures.  This is the next level of filtering
836          after trilinear filtering." */
837       if (strstr ((char *) glGetString(GL_EXTENSIONS),
838                   "GL_EXT_texture_filter_anisotropic"))
839       {
840         GLfloat anisotropic = 0.0;
841         glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
842         if (anisotropic >= 1.0)
843           glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 
844                            anisotropic);
845       }
846     }
847   
848   /* The default font is (by fiat) "18 points".
849      Interpret the user's font size request relative to that.
850    */
851   sc->font_scale = 3 * (font_size / 18.0);
852
853   if (target_columns <= 2) target_columns = 2;
854
855   /* Figure out what the wrap column should be, in font-coordinate pixels.
856      Compute it from the given -columns value, but don't let it be wider
857      than the screen.
858    */
859   {
860     GLfloat maxw = 110 * sc->line_height / sc->font_scale;  /* magic... */
861     sc->font_wrap_pixels = target_columns * sc->char_width;
862     if (sc->font_wrap_pixels > maxw ||
863         sc->font_wrap_pixels <= 0)
864       sc->font_wrap_pixels = maxw;
865   }
866
867   sc->buf_size = target_columns * max_lines;
868   sc->buf = (char *) calloc (1, sc->buf_size);
869
870   alignment_random_p = False;
871   if (!alignment_str || !*alignment_str ||
872       !strcasecmp(alignment_str, "left"))
873     sc->alignment = -1;
874   else if (!strcasecmp(alignment_str, "center") ||
875            !strcasecmp(alignment_str, "middle"))
876     sc->alignment = 0;
877   else if (!strcasecmp(alignment_str, "right"))
878     sc->alignment = 1;
879   else if (!strcasecmp(alignment_str, "random"))
880     sc->alignment = -1, alignment_random_p = True;
881
882   else
883     {
884       fprintf (stderr,
885                "%s: alignment must be left/center/right/random, not \"%s\"\n",
886                progname, alignment_str);
887       exit (1);
888     }
889
890   sc->tc = textclient_open (sc->dpy);
891
892   if (max_lines < 1) max_lines = 1;
893   min_lines = max_lines * 0.66;
894   if (min_lines > max_lines - 3) min_lines = max_lines - 4;
895   if (min_lines < 1) min_lines = 1;
896
897   parse_color (mi, "foreground",
898                get_string_resource(mi->dpy, "foreground", "Foreground"),
899                sc->color);
900
901   sc->top_margin = (sc->char_width * 100);
902   sc->bottom_margin = -sc->top_margin;
903   reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi));  /* compute left/right */
904 }
905
906
907 ENTRYPOINT void
908 draw_fliptext (ModeInfo *mi)
909 {
910   fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
911 /*  XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
912   Display *dpy = MI_DISPLAY(mi);
913   Window window = MI_WINDOW(mi);
914   int i;
915
916   if (!sc->glx_context)
917     return;
918
919   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sc->glx_context));
920
921 #if 0
922   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
923     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
924 #endif
925
926   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
927
928   mi->polygon_count = 0;
929
930   glPushMatrix();
931   glRotatef(current_device_rotation(), 0, 0, 1);
932   {
933     GLfloat s = 3.0 / (sc->top_margin - sc->bottom_margin);
934     glScalef(s, s, s);
935   }
936
937   glRotatef (sc->rotation.x, 1, 0, 0);
938   glRotatef (sc->rotation.y, 0, 1, 0);
939   glRotatef (sc->rotation.z, 0, 0, 1);
940
941 #if 0
942   glDisable (GL_TEXTURE_2D);
943   glColor3f (1,1,1);
944   glBegin (GL_LINE_LOOP);
945   glVertex3f (sc->left_margin,  sc->top_margin, 0);
946   glVertex3f (sc->right_margin, sc->top_margin, 0);
947   glVertex3f (sc->right_margin, sc->bottom_margin, 0);
948   glVertex3f (sc->left_margin,  sc->bottom_margin, 0);
949   glEnd();
950   glBegin (GL_LINES);
951   glVertex3f (sc->in.x,  sc->top_margin,    sc->in.z);
952   glVertex3f (sc->in.x,  sc->bottom_margin, sc->in.z);
953   glVertex3f (sc->mid.x, sc->top_margin,    sc->mid.z);
954   glVertex3f (sc->mid.x, sc->bottom_margin, sc->mid.z);
955   glVertex3f (sc->out.x, sc->top_margin,    sc->out.z);
956   glVertex3f (sc->out.x, sc->bottom_margin, sc->out.z);
957   glEnd();
958   glEnable (GL_TEXTURE_2D);
959 #endif
960
961   for (i = 0; i < sc->nlines; i++)
962     {
963       line *line = sc->lines[i];
964       draw_line (mi, line);
965       tick_line (sc, line);
966     }
967
968   for (i = sc->nlines-1; i >= 0; i--)
969     {
970       line *line = sc->lines[i];
971       if (line->state == DEAD)
972         free_line (sc, line);
973     }
974
975   if (sc->nlines == 0)
976     reset_lines (mi);
977
978   glPopMatrix();
979
980   if (mi->fps_p) do_fps (mi);
981   glFinish();
982   glXSwapBuffers(dpy, window);
983 }
984
985 static void
986 free_fliptext (ModeInfo *mi)
987 {
988   fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
989   if (sc->tc)
990     textclient_close (sc->tc);
991   free(sc->lines);
992
993   /* #### there's more to free here */
994 }
995
996 XSCREENSAVER_MODULE ("FlipText", fliptext)
997
998 #endif /* USE_GL */