weston-editor --help works
[profile/ivi/weston-ivi-shell.git] / clients / glmatrix.c
1 /* glmatrix, Copyright (c) 2003, 2004 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * GLMatrix -- simulate the text scrolls from the movie "The Matrix".
12  *
13  * This program does a 3D rendering of the dropping characters that
14  * appeared in the title sequences of the movies.  See also `xmatrix'
15  * for a simulation of what the computer monitors actually *in* the
16  * movie did.
17  */
18
19 #define DEFAULTS        "*delay:        30000         \n" \
20                         "*showFPS:      False         \n" \
21                         "*wireframe:    False         \n" \
22
23 # define refresh_matrix 0
24 # define release_matrix 0
25 #undef countof
26 #define countof(x) (sizeof((x))/sizeof((*x)))
27
28 #undef BELLRAND
29 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
30
31 #include "wscreensaver-glue.h"
32
33 #ifdef __GNUC__
34   __extension__  /* don't warn about "string length is greater than the length
35                     ISO C89 compilers are required to support" when including
36                     the following XPM file... */
37 #endif
38 #include "matrix3.xpm"
39
40
41 #define DEF_SPEED       "1.0"
42 #define DEF_DENSITY     "20"
43 #define DEF_CLOCK       "False"
44 #define DEF_FOG         "True"
45 #define DEF_WAVES       "True"
46 #define DEF_ROTATE      "True"
47 #define DEF_TEXTURE     "True"
48 #define DEF_MODE        "Matrix"
49 #define DEF_TIMEFMT     " %l%M%p "
50
51
52 #define CHAR_COLS 16
53 #define CHAR_ROWS 13
54
55 static const int matrix_encoding[] = {
56     16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
57 # if 0
58     192, 193, 194, 195, 196, 197, 198, 199,
59     200, 201, 202, 203, 204, 205, 206, 207
60 # else
61     160, 161, 162, 163, 164, 165, 166, 167,
62     168, 169, 170, 171, 172, 173, 174, 175
63 # endif
64   };
65 static const int decimal_encoding[]  = {
66   16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
67 static const int hex_encoding[] = {
68   16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 };
69 static const int binary_encoding[] = { 16, 17 };
70 static const int dna_encoding[]    = { 33, 35, 39, 52 };
71
72 static const unsigned char char_map[256] = {
73    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /*   0 */
74    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /*  16 */
75     0,  1,  2, 96,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  /*  32 */
76    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,  /*  48 */
77    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,  /*  64 */
78    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,  /*  80 */
79    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  /*  96 */
80    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,  /* 112 */
81    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /* 128 */
82    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /* 144 */
83    96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,  /* 160 */
84   112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,  /* 176 */
85   128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,  /* 192 */
86   144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,  /* 208 */
87 #if 0
88   160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,  /* 224 */
89   176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191   /* 240 */
90 #else /* see spank_image() */
91    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /* 224 */
92    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,  /* 240 */
93 #endif
94 };
95
96 #define CURSOR_GLYPH 97
97
98 /* #define DEBUG */
99
100 #define GRID_SIZE  70     /* width and height of the arena */
101 #define GRID_DEPTH 35     /* depth of the arena */
102 #define WAVE_SIZE  22     /* periodicity of color (brightness) waves */
103 #define SPLASH_RATIO 0.7  /* ratio of GRID_DEPTH where chars hit the screen */
104
105 static const struct { GLfloat x, y; } nice_views[] = {
106   {  0,     0 },
107   {  0,   -20 },     /* this is a list of viewer rotations that look nice. */
108   {  0,    20 },     /* every now and then we switch to a new one.         */
109   { 25,     0 },     /* (but we only use the first one at start-up.)       */
110   {-25,     0 },
111   { 25,    20 },
112   {-25,    20 },
113   { 25,   -20 },
114   {-25,   -20 },
115
116   { 10,     0 },
117   {-10,     0 },
118   {  0,     0 },  /* prefer these */
119   {  0,     0 },
120   {  0,     0 },
121   {  0,     0 },
122   {  0,     0 },
123 };
124
125
126 typedef struct {
127   GLfloat x, y, z;        /* position of strip */
128   GLfloat dx, dy, dz;     /* velocity of strip */
129
130   Bool erasing_p;         /* Whether this strip is on its way out. */
131
132   int spinner_glyph;      /* the bottommost glyph -- the feeder */
133   GLfloat spinner_y;      /* where on the strip the bottom glyph is */
134   GLfloat spinner_speed;  /* how fast the bottom glyph drops */
135
136   int glyphs[GRID_SIZE];  /* the other glyphs on the strip, which will be
137                              revealed by the dropping spinner.
138                              0 means no glyph; negative means "spinner".
139                              If non-zero, real value is abs(G)-1. */
140
141   Bool highlight[GRID_SIZE];
142                           /* some glyphs may be highlighted */
143   
144   int spin_speed;         /* Rotate all spinners every this-many frames */
145   int spin_tick;          /* frame counter */
146
147   int wave_position;      /* Waves of brightness wash down the strip. */
148   int wave_speed;         /* every this-many frames. */
149   int wave_tick;          /* frame counter. */
150
151 } strip;
152
153
154 typedef struct {
155   GLXContext *glx_context;
156   Bool button_down_p;
157   GLuint texture;
158   int nstrips;
159   strip *strips;
160   const int *glyph_map;
161   int nglyphs;
162   GLfloat tex_char_width, tex_char_height;
163
164   /* auto-tracking direction of view */
165   int last_view, target_view;
166   GLfloat view_x, view_y;
167   int view_steps, view_tick;
168   Bool auto_tracking_p;
169   int track_tick;
170
171   int real_char_rows;
172   GLfloat brightness_ramp[WAVE_SIZE];
173
174 } matrix_configuration;
175
176 static matrix_configuration *mps = NULL;
177
178 static GLfloat speed = 1.0;
179 static GLfloat density = 20.0;
180 static Bool do_clock;
181 static char *timefmt;
182 static Bool do_fog = 1;
183 static Bool do_waves;
184 static Bool do_rotate = 1;
185 static Bool do_texture = 1;
186 static char *mode_str;
187
188 #if 0
189 static XrmOptionDescRec opts[] = {
190   { "-speed",       ".speed",     XrmoptionSepArg, 0 },
191   { "-density",     ".density",   XrmoptionSepArg, 0 },
192   { "-mode",        ".mode",      XrmoptionSepArg, 0 },
193   { "-binary",      ".mode",      XrmoptionNoArg, "binary"      },
194   { "-hexadecimal", ".mode",      XrmoptionNoArg, "hexadecimal" },
195   { "-decimal",     ".mode",      XrmoptionNoArg, "decimal"     },
196   { "-dna",         ".mode",      XrmoptionNoArg, "dna"         },
197   { "-clock",       ".clock",     XrmoptionNoArg, "True"  },
198   { "+clock",       ".clock",     XrmoptionNoArg, "False" },
199   { "-timefmt",     ".timefmt",   XrmoptionSepArg, 0  },
200   { "-fog",         ".fog",       XrmoptionNoArg, "True"  },
201   { "+fog",         ".fog",       XrmoptionNoArg, "False" },
202   { "-waves",       ".waves",     XrmoptionNoArg, "True"  },
203   { "+waves",       ".waves",     XrmoptionNoArg, "False" },
204   { "-rotate",      ".rotate",    XrmoptionNoArg, "True"  },
205   { "+rotate",      ".rotate",    XrmoptionNoArg, "False" },
206   {"-texture",      ".texture",   XrmoptionNoArg, "True"  },
207   {"+texture",      ".texture",   XrmoptionNoArg, "False" },
208 };
209
210 static argtype vars[] = {
211   {&mode_str,   "mode",       "Mode",    DEF_MODE,      t_String},
212   {&speed,      "speed",      "Speed",   DEF_SPEED,     t_Float},
213   {&density,    "density",    "Density", DEF_DENSITY,   t_Float},
214   {&do_clock,   "clock",      "Clock",   DEF_CLOCK,     t_Bool},
215   {&timefmt,    "timefmt",    "Timefmt", DEF_TIMEFMT,   t_String},
216   {&do_fog,     "fog",        "Fog",     DEF_FOG,       t_Bool},
217   {&do_waves,   "waves",      "Waves",   DEF_WAVES,     t_Bool},
218   {&do_rotate,  "rotate",     "Rotate",  DEF_ROTATE,    t_Bool},
219   {&do_texture, "texture",    "Texture", DEF_TEXTURE,   t_Bool},
220 };
221
222 ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL};
223 #endif
224
225 /* Re-randomize the state of one strip.
226  */
227 static void
228 reset_strip (ModeInfo *mi, strip *s)
229 {
230   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
231   int i;
232   Bool time_displayed_p = False;  /* never display time twice in one strip */
233
234   memset (s, 0, sizeof(*s));
235   s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2));
236   s->y = (GLfloat) (GRID_SIZE/2 + BELLRAND(0.5));      /* shift top slightly */
237   s->z = (GLfloat) (GRID_DEPTH * 0.2) - frand (GRID_DEPTH * 0.7);
238   s->spinner_y = 0;
239
240   s->dx = 0;
241 /*  s->dx = ((BELLRAND(0.01) - 0.005) * speed); */
242   s->dy = 0;
243   s->dz = (BELLRAND(0.02) * speed);
244
245   s->spinner_speed = (BELLRAND(0.3) * speed);
246
247   s->spin_speed = (int) BELLRAND(2.0 / speed) + 1;
248   s->spin_tick  = 0;
249
250   s->wave_position = 0;
251   s->wave_speed = (int) BELLRAND(3.0 / speed) + 1;
252   s->wave_tick  = 0;
253
254   for (i = 0; i < GRID_SIZE; i++)
255     if (do_clock &&
256         !time_displayed_p &&
257         (i < GRID_SIZE-5) &&   /* display approx. once per 5 strips */
258         !(random() % (GRID_SIZE-5)*5))
259       {
260         unsigned int j;
261         char text[80];
262         time_t now = time ((time_t *) 0);
263         struct tm *tm = localtime (&now);
264         strftime (text, sizeof(text)-1, timefmt, tm);
265
266         /* render time into the strip */
267         for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++)
268           {
269             s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1;
270             s->highlight[i] = True;
271           }
272
273         time_displayed_p = True;        
274       }
275     else
276       {
277         int draw_p = (random() % 7);
278         int spin_p = (draw_p && !(random() % 20));
279         int g = (draw_p
280                  ? mp->glyph_map[(random() % mp->nglyphs)] + 1
281                  : 0);
282         if (spin_p) g = -g;
283         s->glyphs[i] = g;
284         s->highlight[i] = False;
285       }
286
287   s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
288 }
289
290
291 /* Animate the strip one step.  Reset if it has reached the bottom.
292  */
293 static void
294 tick_strip (ModeInfo *mi, strip *s)
295 {
296   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
297   int i;
298
299   if (mp->button_down_p)
300     return;
301
302   s->x += s->dx;
303   s->y += s->dy;
304   s->z += s->dz;
305
306   if (s->z > GRID_DEPTH * SPLASH_RATIO)  /* splashed into screen */
307     {
308       reset_strip (mi, s);
309       return;
310     }
311
312   s->spinner_y += s->spinner_speed;
313   if (s->spinner_y >= GRID_SIZE)
314     {
315       if (s->erasing_p)
316         {
317           reset_strip (mi, s);
318           return;
319         }
320       else
321         {
322           s->erasing_p = True;
323           s->spinner_y = 0;
324           s->spinner_speed /= 2;  /* erase it slower than we drew it */
325         }
326     }
327
328   /* Spin the spinners. */
329   s->spin_tick++;
330   if (s->spin_tick > s->spin_speed)
331     {
332       s->spin_tick = 0;
333       s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1);
334       for (i = 0; i < GRID_SIZE; i++)
335         if (s->glyphs[i] < 0)
336           {
337             s->glyphs[i] = -(mp->glyph_map[(random() % mp->nglyphs)] + 1);
338             if (! (random() % 800))  /* sometimes they stop spinning */
339               s->glyphs[i] = -s->glyphs[i];
340           }
341     }
342
343   /* Move the color (brightness) wave. */
344   s->wave_tick++;
345   if (s->wave_tick > s->wave_speed)
346     {
347       s->wave_tick = 0;
348       s->wave_position++;
349       if (s->wave_position >= WAVE_SIZE)
350         s->wave_position = 0;
351     }
352 }
353
354
355 /* Draw a single character at the given position and brightness.
356  */
357 static void
358 draw_glyph (ModeInfo *mi, int glyph, Bool highlight,
359             GLfloat x, GLfloat y, GLfloat z,
360             GLfloat brightness)
361 {
362   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
363   int wire = MI_IS_WIREFRAME(mi);
364   GLfloat w = mp->tex_char_width;
365   GLfloat h = mp->tex_char_height;
366   GLfloat cx = 0, cy = 0;
367   GLfloat S = 1;
368   Bool spinner_p = (glyph < 0);
369
370   if (glyph == 0) abort();
371   if (glyph < 0) glyph = -glyph;
372
373   if (spinner_p)
374     brightness *= 1.5;
375
376   if (!do_texture)
377     {
378       S  = 0.8;
379       x += 0.1;
380       y += 0.1;
381     }
382   else
383     {
384       int ccx = ((glyph - 1) % CHAR_COLS);
385       int ccy = ((glyph - 1) / CHAR_COLS);
386
387       cx = ccx * w;
388       cy = (mp->real_char_rows - ccy - 1) * h;
389
390       if (do_fog)
391         {
392           GLfloat depth;
393           depth = (z / GRID_DEPTH) + 0.5;  /* z ratio from back/front      */
394           depth = 0.2 + (depth * 0.8);     /* scale to range [0.2 - 1.0]   */
395           brightness *= depth;             /* so no row goes all black.    */
396         }
397     }
398
399   {
400     GLfloat r, g, b, a;
401
402     if (highlight)
403       brightness *= 2;
404
405     if (!do_texture && !spinner_p)
406       r = b = 0, g = 1;
407     else
408       r = g = b = 1;
409
410     a = brightness;
411
412     /* If the glyph is very close to the screen (meaning it is very large,
413        and is about to splash into the screen and vanish) then start fading
414        it out, proportional to how close to the glass it is.
415     */
416     if (z > GRID_DEPTH/2)
417       {
418         GLfloat ratio = ((z - GRID_DEPTH/2) /
419                          ((GRID_DEPTH * SPLASH_RATIO) - GRID_DEPTH/2));
420         int i = ratio * WAVE_SIZE;
421
422         if (i < 0) i = 0;
423         else if (i >= WAVE_SIZE) i = WAVE_SIZE-1; 
424
425         a *= mp->brightness_ramp[i];
426       }
427
428     glColor4f (r,g,b,a);
429   }
430
431   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
432   glNormal3f (0, 0, 1);
433   glTexCoord2f (cx,   cy);   glVertex3f (x,   y,   z);
434   glTexCoord2f (cx+w, cy);   glVertex3f (x+S, y,   z);
435   glTexCoord2f (cx+w, cy+h); glVertex3f (x+S, y+S, z);
436   glTexCoord2f (cx,   cy+h); glVertex3f (x,   y+S, z);
437   glEnd ();
438
439   if (wire && spinner_p)
440     {
441       glBegin (GL_LINES);
442       glVertex3f (x,   y,   z);
443       glVertex3f (x+S, y+S, z);
444       glVertex3f (x,   y+S, z);
445       glVertex3f (x+S, y,   z);
446       glEnd();
447     }
448
449   mi->polygon_count++;
450 }
451
452
453 /* Draw all the visible glyphs in the strip.
454  */
455 static void
456 draw_strip (ModeInfo *mi, strip *s)
457 {
458   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
459   int i;
460   for (i = 0; i < GRID_SIZE; i++)
461     {
462       int g = s->glyphs[i];
463       Bool below_p = (s->spinner_y >= i);
464
465       if (s->erasing_p)
466         below_p = !below_p;
467
468       if (g && below_p)       /* don't draw cells below the spinner */
469         {
470           GLfloat brightness;
471           if (!do_waves)
472             brightness = 1.0;
473           else
474             {
475               int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position))
476                                    % WAVE_SIZE);
477               brightness = mp->brightness_ramp[j];
478             }
479
480           draw_glyph (mi, g, s->highlight[i],
481                       s->x, s->y - i, s->z, brightness);
482         }
483     }
484
485   if (!s->erasing_p)
486     draw_glyph (mi, s->spinner_glyph, False,
487                 s->x, s->y - s->spinner_y, s->z, 1.0);
488 }
489
490
491 /* qsort comparator for sorting strips by z position */
492 static int
493 cmp_strips (const void *aa, const void *bb)
494 {
495   const strip *a = *(strip **) aa;
496   const strip *b = *(strip **) bb;
497   return ((int) (a->z * 10000) -
498           (int) (b->z * 10000));
499 }
500
501
502 /* Auto-tracking
503  */
504
505 static void
506 auto_track_init (ModeInfo *mi)
507 {
508   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
509   mp->last_view = 0;
510   mp->target_view = 0;
511   mp->view_x = nice_views[mp->last_view].x;
512   mp->view_y = nice_views[mp->last_view].y;
513   mp->view_steps = 100;
514   mp->view_tick = 0;
515   mp->auto_tracking_p = False;
516 }
517
518
519 static void
520 auto_track (ModeInfo *mi)
521 {
522   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
523
524   if (! do_rotate)
525     return;
526   if (mp->button_down_p)
527     return;
528
529   /* if we're not moving, maybe start moving.  Otherwise, do nothing. */
530   if (! mp->auto_tracking_p)
531     {
532       if (++mp->track_tick < 20/speed) return;
533       mp->track_tick = 0;
534       if (! (random() % 20))
535         mp->auto_tracking_p = True;
536       else
537         return;
538     }
539
540
541   {
542     GLfloat ox = nice_views[mp->last_view].x;
543     GLfloat oy = nice_views[mp->last_view].y;
544     GLfloat tx = nice_views[mp->target_view].x;
545     GLfloat ty = nice_views[mp->target_view].y;
546
547     /* move from A to B with sinusoidal deltas, so that it doesn't jerk
548        to a stop. */
549     GLfloat th = sin ((M_PI / 2) * (double) mp->view_tick / mp->view_steps);
550
551     mp->view_x = (ox + ((tx - ox) * th));
552     mp->view_y = (oy + ((ty - oy) * th));
553     mp->view_tick++;
554
555   if (mp->view_tick >= mp->view_steps)
556     {
557       mp->view_tick = 0;
558       mp->view_steps = (350.0 / speed);
559       mp->last_view = mp->target_view;
560       mp->target_view = (random() % (countof(nice_views) - 1)) + 1;
561       mp->auto_tracking_p = False;
562     }
563   }
564 }
565
566
567 /* Window management, etc
568  */
569 ENTRYPOINT void
570 reshape_matrix (ModeInfo *mi, int width, int height)
571 {
572   GLfloat h = (GLfloat) height / (GLfloat) width;
573
574   glViewport (0, 0, (GLint) width, (GLint) height);
575
576   glMatrixMode(GL_PROJECTION);
577   glLoadIdentity();
578   gluPerspective (80.0, 1/h, 1.0, 100);
579
580   glMatrixMode(GL_MODELVIEW);
581   glLoadIdentity();
582   gluLookAt( 0.0, 0.0, 25.0,
583              0.0, 0.0, 0.0,
584              0.0, 1.0, 0.0);
585 }
586
587
588 #if 0
589 ENTRYPOINT Bool
590 matrix_handle_event (ModeInfo *mi, XEvent *event)
591 {
592   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
593
594   if (event->xany.type == ButtonPress &&
595       event->xbutton.button == Button1)
596     {
597       mp->button_down_p = True;
598       return True;
599     }
600   else if (event->xany.type == ButtonRelease &&
601            event->xbutton.button == Button1)
602     {
603       mp->button_down_p = False;
604       return True;
605     }
606
607   return False;
608 }
609 #endif
610
611 #if 0
612 static Bool
613 bigendian (void)
614 {
615   union { int i; char c[sizeof(int)]; } u;
616   u.i = 1;
617   return !u.c[0];
618 }
619 #endif
620
621
622 /* The image with the characters in it is 512x598, meaning that it needs to
623    be copied into a 512x1024 texture.  But some machines can't handle textures
624    that large...  And it turns out that we aren't using most of the characters
625    in that image anyway, since this program doesn't do anything that makes use
626    of the full range of Latin1 characters.  So... this function tosses out the
627    last 32 of the Latin1 characters, resulting in a 512x506 image, which we
628    can then stuff in a 512x512 texture.  Voila.
629
630    If this hack ever grows into something that displays full Latin1 text,
631    well then, Something Else Will Need To Be Done.
632  */
633 static void
634 spank_image (matrix_configuration *mp, XImage *xi)
635 {
636   int ch = xi->height / CHAR_ROWS;
637   int cut = 2;
638   unsigned char *bits = (unsigned char *) xi->data;
639   unsigned char *from, *to, *s, *end;
640   int L = xi->bytes_per_line * ch;
641 /*  int i;*/
642
643   /* Copy row 12 into 10 (which really means, copy 2 into 0,
644      since texture data is upside down.).
645   */
646   to   = bits + (L * cut);
647   from = bits;
648   end  = from + L;
649   s    = from;
650   while (s < end)
651     *to++ = *s++;
652
653   /* Then, pull all the bits down by 2 rows.
654    */
655   to   = bits;
656   from = bits + (L * cut);
657   end  = bits + (L * CHAR_ROWS);
658   s    = from;
659   while (s < end)
660     *to++ = *s++;
661
662   /* And clear out the rest, for good measure.
663    */
664   from = bits + (L * (CHAR_ROWS - cut));
665   end  = bits + (L * CHAR_ROWS);
666   s    = from;
667   while (s < end)
668     *s++ = 0;
669
670   xi->height -= (cut * ch);
671   mp->real_char_rows -= cut;
672
673 # if 0
674   /* Finally, pull the map indexes back to match the new bits.
675    */
676   for (i = 0; i < countof(matrix_encoding); i++)
677     if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut)))
678       matrix_encoding[i] -= (cut * CHAR_COLS);
679 # endif
680 }
681
682
683 static void
684 load_textures (ModeInfo *mi, Bool flip_p)
685 {
686   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
687   XImage *xi;
688   int x, y;
689   int cw, ch;
690   int orig_w, orig_h;
691
692   /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
693      So we waste some padding rows to round up.
694    */
695   xi = xpm_to_ximage (matrix3_xpm);
696   orig_w = xi->width;
697   orig_h = xi->height;
698   mp->real_char_rows = CHAR_ROWS;
699   spank_image (mp, xi);
700
701   if (xi->height != 512 && xi->height != 1024)
702     {
703       xi->height = (xi->height < 512 ? 512 : 1024);
704       xi->data = realloc (xi->data, xi->height * xi->bytes_per_line);
705       if (!xi->data)
706         {
707           fprintf(stderr, "%s: out of memory\n", progname);
708           exit(1);
709         }
710     }
711
712   if (xi->width != 512) abort();
713   if (xi->height != 512 && xi->height != 1024) abort();
714
715   /* char size in pixels */
716   cw = orig_w / CHAR_COLS;
717   ch = orig_h / CHAR_ROWS;
718
719   /* char size in ratio of final (padded) texture size */
720   mp->tex_char_width  = (GLfloat) cw / xi->width;
721   mp->tex_char_height = (GLfloat) ch / xi->height;
722
723   /* Flip each character's bits horizontally -- we could also just do this
724      by reversing the texture coordinates on the quads, but on some systems
725      that slows things down a lot.
726    */
727   if (flip_p)
728     {
729       int xx, col;
730       unsigned long buf[100];
731       for (y = 0; y < xi->height; y++)
732         for (col = 0, xx = 0; col < CHAR_COLS; col++, xx += cw)
733           {
734             for (x = 0; x < cw; x++)
735               buf[x] = XGetPixel (xi, xx+x, y);
736             for (x = 0; x < cw; x++)
737               XPutPixel (xi, xx+x, y, buf[cw-x-1]);
738           }
739     }
740
741   /* The pixmap is a color image with no transparency.  Set the texture's
742      alpha to be the green channel, and set the green channel to be 100%.
743    */
744   {
745     int rpos, gpos, bpos, apos;  /* bitfield positions */
746 #if 0
747     /* #### Cherub says that the little-endian case must be taken on MacOSX,
748             or else the colors/alpha are the wrong way around.  How can
749             that be the case?
750      */
751     if (bigendian())
752       rpos = 24, gpos = 16, bpos =  8, apos =  0;
753     else
754 #endif
755       rpos =  0, gpos =  8, bpos = 16, apos = 24;
756
757     for (y = 0; y < xi->height; y++)
758       for (x = 0; x < xi->width; x++)
759         {
760           unsigned long p = XGetPixel (xi, x, y);
761           unsigned char r = (p >> rpos) & 0xFF;
762           unsigned char g = (p >> gpos) & 0xFF;
763           unsigned char b = (p >> bpos) & 0xFF;
764           unsigned char a = g;
765           g = 0xFF;
766           p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos);
767           XPutPixel (xi, x, y, p);
768         }
769   }
770
771   /* Now load the texture into GL.
772    */
773   clear_gl_error();
774   glGenTextures (1, &mp->texture);
775
776   glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
777   glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);
778   glBindTexture (GL_TEXTURE_2D, mp->texture);
779   check_gl_error ("texture init");
780   glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA,
781                 GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
782   {
783     char buf[255];
784     sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
785     check_gl_error (buf);
786   }
787
788   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
789   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
790
791   /* I'd expect CLAMP to be the thing to do here, but oddly, we get a
792      faint solid green border around the texture if it is *not* REPEAT!
793   */
794   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
795   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
796
797   glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
798   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
799   check_gl_error ("texture param");
800
801   XDestroyImage (xi);
802 }
803
804
805 ENTRYPOINT void 
806 init_matrix (ModeInfo *mi)
807 {
808   matrix_configuration *mp;
809   int wire = MI_IS_WIREFRAME(mi);
810   Bool flip_p = 0;
811   int i;
812
813   if (wire)
814     do_texture = False;
815
816   if (!mps) {
817     mps = (matrix_configuration *)
818       calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration));
819     if (!mps) {
820       fprintf(stderr, "%s: out of memory\n", progname);
821       exit(1);
822     }
823   }
824
825   mp = &mps[MI_SCREEN(mi)];
826   mp->glx_context = init_GL(mi);
827
828   if (!mode_str || !*mode_str || !strcasecmp(mode_str, "matrix"))
829     {
830       flip_p = 1;
831       mp->glyph_map = matrix_encoding;
832       mp->nglyphs   = countof(matrix_encoding);
833     }
834   else if (!strcasecmp (mode_str, "dna"))
835     {
836       flip_p = 0;
837       mp->glyph_map = dna_encoding;
838       mp->nglyphs   = countof(dna_encoding);
839     }
840   else if (!strcasecmp (mode_str, "bin") ||
841            !strcasecmp (mode_str, "binary"))
842     {
843       flip_p = 0;
844       mp->glyph_map = binary_encoding;
845       mp->nglyphs   = countof(binary_encoding);
846     }
847   else if (!strcasecmp (mode_str, "hex") ||
848            !strcasecmp (mode_str, "hexadecimal"))
849     {
850       flip_p = 0;
851       mp->glyph_map = hex_encoding;
852       mp->nglyphs   = countof(hex_encoding);
853     }
854   else if (!strcasecmp (mode_str, "dec") ||
855            !strcasecmp (mode_str, "decimal"))
856     {
857       flip_p = 0;
858       mp->glyph_map = decimal_encoding;
859       mp->nglyphs   = countof(decimal_encoding);
860     }
861   else
862     {
863       fprintf (stderr,
864            "%s: `mode' must be matrix, dna, binary, or hex: not `%s'\n",
865                progname, mode_str);
866       exit (1);
867     }
868
869   reshape_matrix (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
870
871   glShadeModel(GL_SMOOTH);
872
873   glDisable(GL_DEPTH_TEST);
874   glDisable(GL_CULL_FACE);
875   glEnable(GL_NORMALIZE);
876
877   if (do_texture)
878     {
879       load_textures (mi, flip_p);
880       glEnable(GL_TEXTURE_2D);
881       glEnable(GL_BLEND);
882
883       /* Jeff Epler points out:
884          By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are
885          added to each other, so that a bright glyph with a darker one
886          in front is a little brighter than the bright glyph alone.
887        */
888       glBlendFunc (GL_SRC_ALPHA, GL_ONE);
889     }
890
891   /* to scale coverage-percent to strips, this number looks about right... */
892   mp->nstrips = (int) (density * 2.2);
893   if      (mp->nstrips < 1)    mp->nstrips = 1;
894   else if (mp->nstrips > 2000) mp->nstrips = 2000;
895
896
897   mp->strips = calloc (mp->nstrips, sizeof(strip));
898   for (i = 0; i < mp->nstrips; i++)
899     {
900       strip *s = &mp->strips[i];
901       reset_strip (mi, s);
902
903       /* If we start all strips from zero at once, then the first few seconds
904          of the animation are much denser than normal.  So instead, set all
905          the initial strips to erase-mode with random starting positions.
906          As these die off at random speeds and are re-created, we'll get a
907          more consistent density. */
908       s->erasing_p = True;
909       s->spinner_y = frand(GRID_SIZE);
910       memset (s->glyphs, 0, sizeof(s->glyphs));  /* no visible glyphs */
911     }
912
913   /* Compute the brightness ramp.
914    */
915   for (i = 0; i < WAVE_SIZE; i++)
916     {
917       GLfloat j = ((WAVE_SIZE - i) / (GLfloat) (WAVE_SIZE - 1));
918       j *= (M_PI / 2);       /* j ranges from 0.0 - PI/2  */
919       j = sin (j);           /* j ranges from 0.0 - 1.0   */
920       j = 0.2 + (j * 0.8);   /* j ranges from 0.2 - 1.0   */
921       mp->brightness_ramp[i] = j;
922       /* printf("%2d %8.2f\n", i, j); */
923     }
924
925
926   auto_track_init (mi);
927 }
928
929
930 #ifdef DEBUG
931
932 static void
933 draw_grid (ModeInfo *mi)
934 {
935   if (!MI_IS_WIREFRAME(mi))
936     {
937       glDisable(GL_TEXTURE_2D);
938       glDisable(GL_BLEND);
939     }
940   glPushMatrix();
941
942   glColor3f(1, 1, 1);
943   glBegin(GL_LINES);
944   glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
945   glVertex3f(0, -GRID_SIZE, 0); glVertex3f(0, GRID_SIZE, 0);
946   glEnd();
947   glBegin(GL_LINE_LOOP);
948   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
949   glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2, 0);
950   glVertex3f( GRID_SIZE/2,  GRID_SIZE/2, 0);
951   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
952   glEnd();
953   glBegin(GL_LINE_LOOP);
954   glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
955   glVertex3f(-GRID_SIZE/2, GRID_SIZE/2,  GRID_DEPTH/2);
956   glVertex3f( GRID_SIZE/2, GRID_SIZE/2,  GRID_DEPTH/2);
957   glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
958   glEnd();
959   glBegin(GL_LINE_LOOP);
960   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
961   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
962   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
963   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
964   glEnd();
965   glBegin(GL_LINES);
966   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
967   glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2, -GRID_DEPTH/2);
968   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
969   glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2,  GRID_DEPTH/2);
970   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
971   glVertex3f( GRID_SIZE/2,  GRID_SIZE/2, -GRID_DEPTH/2);
972   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
973   glVertex3f( GRID_SIZE/2,  GRID_SIZE/2,  GRID_DEPTH/2);
974   glEnd();
975   glPopMatrix();
976   if (!MI_IS_WIREFRAME(mi))
977     {
978       glEnable(GL_TEXTURE_2D);
979       glEnable(GL_BLEND);
980     }
981 }
982 #endif /* DEBUG */
983
984
985 ENTRYPOINT void
986 draw_matrix (ModeInfo *mi)
987 {
988   matrix_configuration *mp = &mps[MI_SCREEN(mi)];
989   int i;
990
991   if (!mp->glx_context)
992     return;
993
994   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
995
996   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
997
998   glPushMatrix ();
999
1000   if (do_rotate)
1001     {
1002       glRotatef (mp->view_x, 1, 0, 0);
1003       glRotatef (mp->view_y, 0, 1, 0);
1004     }
1005
1006 #ifdef DEBUG
1007 # if 0
1008   glScalef(0.5, 0.5, 0.5);
1009 # endif
1010 # if 0
1011   glRotatef(-30, 0, 1, 0); 
1012 # endif
1013   draw_grid (mi);
1014 #endif
1015
1016   mi->polygon_count = 0;
1017
1018   /* Render (and tick) each strip, starting at the back
1019      (draw the ones farthest from the camera first, to make
1020      the alpha transparency work out right.)
1021    */
1022   {
1023     strip **sorted = malloc (mp->nstrips * sizeof(*sorted));
1024     for (i = 0; i < mp->nstrips; i++)
1025       sorted[i] = &mp->strips[i];
1026     qsort (sorted, i, sizeof(*sorted), cmp_strips);
1027
1028     for (i = 0; i < mp->nstrips; i++)
1029       {
1030         strip *s = sorted[i];
1031         tick_strip (mi, s);
1032         draw_strip (mi, s);
1033       }
1034     free (sorted);
1035   }
1036
1037   auto_track (mi);
1038
1039 #if 0
1040   glBegin(GL_QUADS);
1041   glColor3f(1,1,1);
1042   glTexCoord2f (0,0);  glVertex3f(-15,-15,0);
1043   glTexCoord2f (0,1);  glVertex3f(-15,15,0);
1044   glTexCoord2f (1,1);  glVertex3f(15,15,0);
1045   glTexCoord2f (1,0);  glVertex3f(15,-15,0);
1046   glEnd();
1047 #endif
1048
1049   glPopMatrix ();
1050
1051   if (mi->fps_p) do_fps (mi);
1052   glFinish();
1053
1054   glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
1055 }
1056
1057 WL_EXPORT struct wscreensaver_plugin glmatrix_screensaver = {
1058         "GLMatrix",
1059         init_matrix,
1060         draw_matrix,
1061         reshape_matrix
1062 };