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