http://svn.poeml.de/viewvc/ppc/src-unpacked/xscreensaver/xscreensaver-4.12.tar.bz2...
[xscreensaver] / hacks / xmatrix.c
1 /* xscreensaver, Copyright (c) 1999, 2001 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  * Matrix -- simulate the text scrolls from the movie "The Matrix".
12  *
13  * The movie people distribute their own Windows/Mac screensaver that does
14  * a similar thing, so I wrote one for Unix.  However, that version (the
15  * Windows/Mac version at http://www.whatisthematrix.com/) doesn't match
16  * what the computer screens in the movie looked like, so my `xmatrix' does
17  * things differently.
18  *
19  * See also my `glmatrix' program, which does a 3D rendering of the similar
20  * effect that appeared in the title sequence of the movies.
21  *
22  *
23  *     ==========================================================
24  *
25  *         NOTE:
26  *
27  *         People just love to hack on this one.  I get sent
28  *         patches to this all the time saying, ``here, I made
29  *         it better!''  Mostly this hasn't been true.
30  *
31  *         If you've made changes to xmatrix, when you send me
32  *         your patch, please explain, in English, both *what*
33  *         your changes are, and *why* you think those changes
34  *         make this screensaver behave more like the displays
35  *         in the movie did.  I'd rather not have to read your
36  *         diffs to try and figure that out for myself...
37  *
38  *         In particular, note that the characters in the movie
39  *         were, in fact, low resolution and somewhat blurry/
40  *         washed out.  They also definitely scrolled a
41  *         character at a time, not a pixel at a time.
42  *
43  *         And keep in mind that this program emulates the
44  *         behavior of the computer screens that were visible
45  *         in the movies -- not the behavior of the effects in
46  *         the title sequences.
47  *
48  *     ==========================================================
49  *
50  */
51
52 #include "screenhack.h"
53 #include "xpm-pixmap.h"
54 #include <stdio.h>
55 #include <X11/Xutil.h>
56
57 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
58 # include "images/matrix1.xpm"
59 # include "images/matrix2.xpm"
60 # include "images/matrix1b.xpm"
61 # include "images/matrix2b.xpm"
62 #endif
63
64 #include "images/matrix1.xbm"
65 #include "images/matrix2.xbm"
66 #include "images/matrix1b.xbm"
67 #include "images/matrix2b.xbm"
68
69 #define CHAR_COLS 16
70 #define CHAR_ROWS 13
71 #define CHAR_MAPS 3
72 #define PLAIN_MAP 1
73 #define GLOW_MAP  2
74
75 typedef struct {
76            int glow    : 8;
77   unsigned int glyph   : 9;  /* note: 9 bit characters! */
78   unsigned int changed : 1;
79   unsigned int spinner : 1;
80 } m_cell;
81
82 typedef struct {
83   int remaining;
84   int throttle;
85   int y;
86 } m_feeder;
87
88 #define countof(x) (sizeof(x)/sizeof(*(x)))
89
90 static int matrix_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
91                                  192, 193, 194, 195, 196, 197, 198, 199,
92                                  200, 201, 202, 203, 204, 205, 206, 207 };
93 static int decimal_encoding[]  = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
94 static int hex_encoding[]      = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
95                                    33, 34, 35, 36, 37, 38 };
96 static int binary_encoding[] = { 16, 17 };
97 static int dna_encoding[]    = { 33, 35, 39, 52 };
98 static unsigned char char_map[256] = {
99     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  /*   0 */
100     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  /*  16 */
101     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  /*  32 */
102    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,  /*  48 */
103    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,  /*  64 */
104    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,  /*  80 */
105    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  /*  96 */
106    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,  /* 112 */
107     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  /* 128 */
108     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  /* 144 */
109    96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,  /* 160 */
110   112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,  /* 176 */
111   128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,  /* 192 */
112   144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,  /* 208 */
113   160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,  /* 224 */
114   176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191   /* 240 */
115 };
116
117 #define CURSOR_GLYPH 97
118
119 typedef enum { TRACEA1, TRACEA2,
120                TRACEB1, TRACEB2, SYSTEMFAILURE,
121                KNOCK, NMAP, MATRIX, DNA, BINARY, DEC, HEX } m_mode;
122
123 typedef struct {
124   Display *dpy;
125   Window window;
126   XWindowAttributes xgwa;
127   GC draw_gc, erase_gc, scratch_gc;
128   int grid_width, grid_height;
129   int char_width, char_height;
130   m_cell *cells;
131   m_cell *cursor;
132   m_feeder *feeders;
133   int nspinners;
134   Bool knock_knock_p;
135   Bool small_p;
136   Bool insert_top_p, insert_bottom_p;
137   m_mode mode;
138   signed char *tracing;
139   int density;
140
141   Pixmap images[CHAR_MAPS];
142   int image_width, image_height;
143
144   int nglyphs;
145   int *glyph_map;
146
147   unsigned long colors[5];
148 } m_state;
149
150
151 static void
152 load_images_1 (m_state *state, int which)
153 {
154 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
155   if (!get_boolean_resource ("mono", "Boolean") &&
156       state->xgwa.depth > 1)
157     {
158       char **bits =
159         (which == 1 ? (state->small_p ? matrix1b_xpm : matrix1_xpm) :
160          (state->small_p ? matrix2b_xpm : matrix2_xpm));
161
162       state->images[which] =
163         xpm_data_to_pixmap (state->dpy, state->window, bits,
164                             &state->image_width, &state->image_height, 0);
165     }
166   else
167 #endif /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
168     {
169       unsigned long fg, bg;
170       state->image_width  = (state->small_p ? matrix1b_width :matrix1_width);
171       state->image_height = (state->small_p ? matrix1b_height:matrix1_height);
172       fg = get_pixel_resource("foreground", "Foreground",
173                               state->dpy, state->xgwa.colormap);
174       bg = get_pixel_resource("background", "Background",
175                               state->dpy, state->xgwa.colormap);
176       state->images[which] =
177         XCreatePixmapFromBitmapData (state->dpy, state->window, (char *)
178                 (which == 1 ? (state->small_p ? matrix1b_bits :matrix1_bits) :
179                               (state->small_p ? matrix2b_bits :matrix2_bits)),
180                                      state->image_width, state->image_height,
181                                      bg, fg, state->xgwa.depth);
182     }
183 }
184
185
186 static void
187 load_images (m_state *state)
188 {
189   load_images_1 (state, 1);
190   load_images_1 (state, 2);
191 }
192
193
194 static void
195 flip_images_1 (m_state *state, int which)
196 {
197   XImage *im = XGetImage (state->dpy, state->images[which], 0, 0,
198                           state->image_width, state->image_height,
199                           ~0L, (state->xgwa.depth > 1 ? ZPixmap : XYPixmap));
200   int x, y, xx;
201   int ww = state->char_width;
202   unsigned long *row = (unsigned long *) malloc (sizeof(*row) * ww);
203
204   for (y = 0; y < state->image_height; y++)
205     {
206       for (x = 0; x < CHAR_COLS; x++)
207         {
208           for (xx = 0; xx < ww; xx++)
209             row[xx] = XGetPixel (im, (x * ww) + xx, y);
210           for (xx = 0; xx < ww; xx++)
211             XPutPixel (im, (x * ww) + xx, y, row[ww - xx - 1]);
212         }
213     }
214
215   XPutImage (state->dpy, state->images[which], state->draw_gc, im, 0, 0, 0, 0,
216              state->image_width, state->image_height);
217   XDestroyImage (im);
218   free (row);
219 }
220
221 static void
222 flip_images (m_state *state)
223 {
224   flip_images_1 (state, 1);
225   flip_images_1 (state, 2);
226 }
227
228
229 static void
230 init_spinners (m_state *state)
231 {
232   int i = state->nspinners;
233   int x, y;
234   m_cell *cell;
235
236   for (y = 0; y < state->grid_height; y++)
237     for (x = 0; x < state->grid_width; x++)
238       {
239         cell = &state->cells[state->grid_width * y + x];
240         cell->spinner = 0;
241       }
242
243   while (--i > 0)
244     {
245       x = random() % state->grid_width;
246       y = random() % state->grid_height;
247       cell = &state->cells[state->grid_width * y + x];
248       cell->spinner = 1;
249     }
250 }
251
252
253 static void
254 init_trace (m_state *state)
255 {
256   char *s = get_string_resource ("tracePhone", "TracePhone");
257   char *s2, *s3;
258   int i;
259   if (!s)
260     goto FAIL;
261
262   state->tracing = (signed char *) malloc (strlen (s) + 1);
263   s3 = (char *) state->tracing;
264
265   for (s2 = s; *s2; s2++)
266     if (*s2 >= '0' && *s2 <= '9')
267       *s3++ = *s2;
268   *s3 = 0;
269
270   if (s3 == (char *) state->tracing)
271     goto FAIL;
272
273   for (i = 0; i < strlen((char *) state->tracing); i++)
274     state->tracing[i] = -state->tracing[i];
275
276   state->glyph_map = decimal_encoding;
277   state->nglyphs = countof(decimal_encoding);
278
279   return;
280
281  FAIL:
282   fprintf (stderr, "%s: bad phone number: \"%s\".\n",
283            progname, s ? s : "(null)");
284
285   if (s) free (s);
286   if (state->tracing) free (state->tracing);
287   state->tracing = 0;
288   state->mode = MATRIX;
289 }
290
291
292 static m_state *
293 init_matrix (Display *dpy, Window window)
294 {
295   XGCValues gcv;
296   char *insert, *mode;
297   int i;
298   m_state *state = (m_state *) calloc (sizeof(*state), 1);
299
300   state->dpy = dpy;
301   state->window = window;
302
303   XGetWindowAttributes (dpy, window, &state->xgwa);
304
305   {
306     const char *s = get_string_resource ("small", "Boolean");
307     if (s && *s)
308       state->small_p = get_boolean_resource ("small", "Boolean");
309     else
310       state->small_p = (state->xgwa.width < 300);
311   }
312
313   load_images (state);
314
315   gcv.foreground = get_pixel_resource("foreground", "Foreground",
316                                       state->dpy, state->xgwa.colormap);
317   gcv.background = get_pixel_resource("background", "Background",
318                                       state->dpy, state->xgwa.colormap);
319   state->draw_gc = XCreateGC (state->dpy, state->window,
320                               GCForeground|GCBackground, &gcv);
321   gcv.foreground = gcv.background;
322   state->erase_gc = XCreateGC (state->dpy, state->window,
323                                GCForeground|GCBackground, &gcv);
324
325   state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv);
326
327   /* Allocate colors for SYSTEM FAILURE box */
328   {
329     XColor boxcolors[] = {
330       { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
331       { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
332       { 0, 0xE0E0, 0xF7F7, 0xE0E0, DoRed|DoGreen|DoBlue, 0 },
333       { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
334       { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
335     };
336   for (i = 0; i < countof(boxcolors); i++)
337     {
338       if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i]))
339         state->colors[i] = boxcolors[i].pixel;
340       else
341         state->colors[i] = gcv.foreground;  /* default black */
342     }
343   }
344
345   state->char_width =  state->image_width  / CHAR_COLS;
346   state->char_height = state->image_height / CHAR_ROWS;
347
348   state->grid_width  = state->xgwa.width  / state->char_width;
349   state->grid_height = state->xgwa.height / state->char_height;
350   state->grid_width++;
351   state->grid_height++;
352   if (state->grid_width  < 5) state->grid_width  = 5;
353   if (state->grid_height < 5) state->grid_height = 5;
354
355   state->glyph_map = matrix_encoding;
356   state->nglyphs = countof(matrix_encoding);
357
358   state->cells = (m_cell *)
359     calloc (sizeof(m_cell), state->grid_width * state->grid_height);
360   state->cursor = NULL;
361   state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
362
363   state->density = get_integer_resource ("density", "Integer");
364
365   insert = get_string_resource("insert", "Insert");
366   if (insert && !strcmp(insert, "top"))
367     {
368       state->insert_top_p = True;
369       state->insert_bottom_p = False;
370     }
371   else if (insert && !strcmp(insert, "bottom"))
372     {
373       state->insert_top_p = False;
374       state->insert_bottom_p = True;
375     }
376   else if (insert && !strcmp(insert, "both"))
377     {
378       state->insert_top_p = True;
379       state->insert_bottom_p = True;
380     }
381   else
382     {
383       if (insert && *insert)
384         fprintf (stderr,
385                  "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
386                  progname, insert);
387       state->insert_top_p = False;
388       state->insert_bottom_p = True;
389     }
390
391   state->nspinners = get_integer_resource ("spinners", "Integer");
392
393   if (insert)
394     free (insert);
395
396   state->knock_knock_p = get_boolean_resource ("knockKnock", "KnockKnock");
397
398   mode = get_string_resource ("mode", "Mode");
399   if (mode && !strcasecmp(mode, "trace"))
400     state->mode = (((random() % 3) == 0) ? TRACEB1 : TRACEA1);
401   else if (mode && !strcasecmp(mode, "crack"))
402     state->mode = NMAP;
403   else if (mode && !strcasecmp(mode, "dna"))
404     state->mode = DNA;
405   else if (mode && (!strcasecmp(mode, "bin") ||
406                     !strcasecmp(mode, "binary")))
407     state->mode = BINARY;
408   else if (mode && (!strcasecmp(mode, "hex") ||
409                     !strcasecmp(mode, "hexadecimal")))
410     state->mode = HEX;
411   else if (mode && (!strcasecmp(mode, "dec") ||
412                     !strcasecmp(mode, "decimal")))
413     state->mode = DEC;
414   else if (!mode || !*mode || !strcasecmp(mode, "matrix"))
415     state->mode = MATRIX;
416   else
417     {
418       fprintf (stderr,
419            "%s: `mode' must be matrix, trace, dna, binary, or hex: not `%s'\n",
420                progname, mode);
421       state->mode = MATRIX;
422     }
423
424   switch (state->mode)
425     {
426     case DNA:
427       state->glyph_map = dna_encoding;
428       state->nglyphs = countof(dna_encoding);
429       break;
430     case BINARY:
431       state->glyph_map = binary_encoding;
432       state->nglyphs = countof(binary_encoding);
433       break;
434     case DEC:
435       state->glyph_map = decimal_encoding;
436       state->nglyphs = countof(decimal_encoding);
437       break;
438     case HEX:
439       state->glyph_map = hex_encoding;
440       state->nglyphs = countof(hex_encoding);
441       break;
442     case TRACEA1: case TRACEB1:
443       init_trace (state);
444       break;
445     case NMAP:
446       break;
447     case MATRIX:
448       flip_images (state);
449       init_spinners (state);
450       break;
451     default:
452       abort();
453     }
454
455   return state;
456 }
457
458
459 static void
460 insert_glyph (m_state *state, int glyph, int x, int y)
461 {
462   Bool bottom_feeder_p = (y >= 0);
463   m_cell *from, *to;
464
465   if (y >= state->grid_height)
466     return;
467
468   if (bottom_feeder_p)
469     {
470       to = &state->cells[state->grid_width * y + x];
471     }
472   else
473     {
474       for (y = state->grid_height-1; y > 0; y--)
475         {
476           from = &state->cells[state->grid_width * (y-1) + x];
477           to   = &state->cells[state->grid_width * y     + x];
478           to->glyph   = from->glyph;
479           to->glow    = from->glow;
480           to->changed = 1;
481         }
482       to = &state->cells[x];
483     }
484
485   to->glyph = glyph;
486   to->changed = 1;
487
488   if (!to->glyph)
489     ;
490   else if (bottom_feeder_p)
491     to->glow = 1 + (random() % (state->tracing ? 4 : 2));
492   else
493     to->glow = 0;
494 }
495
496
497 static void
498 feed_matrix (m_state *state)
499 {
500   int x;
501
502   switch (state->mode)
503     {
504     case TRACEA2: case TRACEB2:
505     case MATRIX: case DNA: case BINARY: case DEC: case HEX:
506       break;
507     default:
508       return;
509     }
510
511   /* Update according to current feeders. */
512   for (x = 0; x < state->grid_width; x++)
513     {
514       m_feeder *f = &state->feeders[x];
515
516       if (f->throttle)          /* this is a delay tick, synced to frame. */
517         {
518           f->throttle--;
519         }
520       else if (f->remaining > 0)        /* how many items are in the pipe */
521         {
522           int g = state->glyph_map[(random() % state->nglyphs)] + 1;
523           insert_glyph (state, g, x, f->y);
524           f->remaining--;
525           if (f->y >= 0)  /* bottom_feeder_p */
526             f->y++;
527         }
528       else                              /* if pipe is empty, insert spaces */
529         {
530           insert_glyph (state, 0, x, f->y);
531           if (f->y >= 0)  /* bottom_feeder_p */
532             f->y++;
533         }
534
535       if ((random() % 10) == 0)         /* randomly change throttle speed */
536         {
537           f->throttle = ((random() % 5) + (random() % 5));
538         }
539     }
540 }
541
542
543 static void
544 redraw_cells (m_state *state, Bool active)
545 {
546   int x, y;
547   int count = 0;
548
549   for (y = 0; y < state->grid_height; y++)
550     for (x = 0; x < state->grid_width; x++)
551       {
552         m_cell *cell = &state->cells[state->grid_width * y + x];
553
554         if (cell->glyph)
555           count++;
556
557         if ((state->mode == TRACEA2 || state->mode == TRACEB2) && active)
558           {
559             int xx = x % strlen((char *) state->tracing);
560             Bool dead_p = state->tracing[xx] > 0;
561
562             if (y == 0 && x == xx)
563               cell->glyph = (dead_p
564                              ? state->glyph_map[state->tracing[xx]-'0'] + 1
565                              : 0);
566             else if (y == 0)
567               cell->glyph = 0;
568             else
569               cell->glyph = (dead_p ? 0 :
570                              (state->glyph_map[(random()%state->nglyphs)]
571                               + 1));
572
573             cell->changed = 1;
574           }
575
576         if (!cell->changed)
577           continue;
578
579         if (cell->glyph == 0 && cell != state->cursor)
580           XFillRectangle (state->dpy, state->window, state->erase_gc,
581                           x * state->char_width,
582                           y * state->char_height,
583                           state->char_width,
584                           state->char_height);
585         else
586           {
587             int g = (cell == state->cursor ? CURSOR_GLYPH : cell->glyph);
588             int cx = (g - 1) % CHAR_COLS;
589             int cy = (g - 1) / CHAR_COLS;
590             int map = ((cell->glow != 0 || cell->spinner) ? GLOW_MAP :
591                        PLAIN_MAP);
592
593             XCopyArea (state->dpy, state->images[map],
594                        state->window, state->draw_gc,
595                        cx * state->char_width,
596                        cy * state->char_height,
597                        state->char_width,
598                        state->char_height,
599                        x * state->char_width,
600                        y * state->char_height);
601           }
602
603         cell->changed = 0;
604
605         if (cell->glow > 0)
606           {
607             cell->glow--;
608             cell->changed = 1;
609           }
610         else if (cell->glow < 0)
611           abort();
612
613         if (cell->spinner && active)
614           {
615             cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1);
616             cell->changed = 1;
617           }
618       }
619
620   if (state->cursor)
621     {
622       state->cursor->changed = 1;
623     }
624 }
625
626
627 static int
628 densitizer (m_state *state)
629 {
630   /* Horrid kludge that converts percentages (density of screen coverage)
631      to the parameter that actually controls this.  I got this mapping
632      empirically, on a 1024x768 screen.  Sue me. */
633   if      (state->density < 10) return 85;
634   else if (state->density < 15) return 60;
635   else if (state->density < 20) return 45;
636   else if (state->density < 25) return 25;
637   else if (state->density < 30) return 20;
638   else if (state->density < 35) return 15;
639   else if (state->density < 45) return 10;
640   else if (state->density < 50) return 8;
641   else if (state->density < 55) return 7;
642   else if (state->density < 65) return 5;
643   else if (state->density < 80) return 3;
644   else if (state->density < 90) return 2;
645   else return 1;
646 }
647
648
649 static void drain_matrix (m_state *);
650
651 static void
652 handle_events (m_state *state)
653 {
654   XSync (state->dpy, False);
655   while (XPending (state->dpy))
656     {
657       XEvent event;
658       XNextEvent (state->dpy, &event);
659
660       if (event.xany.type == ConfigureNotify)
661         {
662           int ow = state->grid_width;
663           int oh = state->grid_height;
664           XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
665           state->grid_width  = state->xgwa.width  / state->char_width;
666           state->grid_height = state->xgwa.height / state->char_height;
667           state->grid_width++;
668           state->grid_height++;
669           if (state->grid_width  < 5) state->grid_width  = 5;
670           if (state->grid_height < 5) state->grid_height = 5;
671
672           if (ow != state->grid_width ||
673               oh != state->grid_height)
674             {
675               m_cell *ncells = (m_cell *)
676                 calloc (sizeof(m_cell),
677                         state->grid_width * state->grid_height);
678               m_feeder *nfeeders = (m_feeder *)
679                 calloc (sizeof(m_feeder), state->grid_width);
680               int x, y, i;
681
682               /* fprintf(stderr, "resize: %d x %d  ==>  %d x %d\n",
683                         ow, oh, state->grid_width, state->grid_height); */
684
685               for (y = 0; y < oh; y++)
686                 for (x = 0; x < ow; x++)
687                   if (x < ow && x < state->grid_width &&
688                       y < oh && y < state->grid_height)
689                     ncells[y * state->grid_width + x] =
690                       state->cells[y * ow + x];
691               free (state->cells);
692               state->cells = ncells;
693
694               x = (ow < state->grid_width ? ow : state->grid_width);
695               for (i = 0; i < x; i++)
696                 nfeeders[i] = state->feeders[i];
697               free (state->feeders);
698               state->feeders = nfeeders;
699             }
700         }
701       else if (event.xany.type == KeyPress)
702         {
703           KeySym keysym;
704           char c = 0;
705           XLookupString (&event.xkey, &c, 1, &keysym, 0);
706           if (c == '0' && !state->tracing)
707             {
708               drain_matrix (state);
709               return;
710             }
711           else if (c == '+' || c == '=' || c == '>' || c == '.')
712             {
713               state->density += 10;
714               if (state->density > 100)
715                 state->density = 100;
716               else
717                 return;
718             }
719           else if (c == '-' || c == '_' || c == '<' || c == ',')
720             {
721               state->density -= 10;
722               if (state->density < 0)
723                 state->density = 0;
724               else
725                 return;
726             }
727           else if (c == '[' || c == '(' || c == '{')
728             {
729               state->insert_top_p    = True;
730               state->insert_bottom_p = False;
731               return;
732             }
733           else if (c == ']' || c == ')' || c == '}')
734             {
735               state->insert_top_p    = False;
736               state->insert_bottom_p = True;
737               return;
738             }
739           else if (c == '\\' || c == '|')
740             {
741               state->insert_top_p    = True;
742               state->insert_bottom_p = True;
743               return;
744             }
745           else if ((c == 't' || c == 'T') && state->mode == MATRIX)
746             {
747               state->mode = (c == 't' ? TRACEA1 : TRACEB1);
748               flip_images (state);
749               init_trace (state);
750               return;
751             }
752           else if ((c == 'c' || c == 'k') && state->mode == MATRIX)
753             {
754               drain_matrix (state);
755               state->mode = (c == 'c' ? NMAP : KNOCK);
756               flip_images (state);
757               return;
758             }
759         }
760
761       screenhack_handle_event (state->dpy, &event);
762     }
763 }
764
765
766 static void
767 matrix_usleep (m_state *state, unsigned long delay)
768 {
769   if (!delay) return;
770
771   if (state->cursor)
772     {
773       int blink_delay = 333000;
774       int tot_delay = 0;
775       m_cell *cursor = state->cursor;
776       while (tot_delay < delay)
777         {
778           if (state->cursor)
779             {
780               usleep (blink_delay * 2);
781               tot_delay += (2 * blink_delay);
782               state->cursor = NULL;
783             }
784           else
785             {
786               usleep (blink_delay);
787               tot_delay += blink_delay;
788               state->cursor = cursor;
789             }
790           cursor->changed = 1;
791           redraw_cells (state, False);
792           XSync (state->dpy, False);
793           handle_events (state);
794         }
795     }
796   else
797     {
798       XSync (state->dpy, False);
799       handle_events (state);
800       usleep (delay);
801     }
802 }
803
804
805 static void
806 hack_text_1 (m_state *state,
807              int *xP, int *yP,
808              const char *s,
809              Bool typing_delay,
810              Bool transmit_delay,
811              Bool long_delay,
812              Bool visible_cursor,
813              Bool scroll_p)
814 {
815   int x = *xP;
816   int y = *yP;
817   int i = state->grid_width * y + x;
818   Bool glow_p = False;
819   int long_delay_usecs = 1000000;
820
821   if (long_delay == -1)
822     long_delay = 0, long_delay_usecs /= 6;
823
824   if (y >= state->grid_height-1) return;
825
826   while (*s)
827     {
828       m_cell *cell;
829       Bool done_p = s[1] == '\000';
830
831       long_delay = done_p;
832               
833       if (*s == '\n' || x >= state->grid_width - 1)
834         {
835           if (*s != '\n')
836             s--;
837           x = 0;
838           y++;
839           i = state->grid_width * y + x;
840
841           if (scroll_p)
842             {
843               int xx, yy;
844               for (yy = 0; yy < state->grid_height-1; yy++)
845                 for (xx = 0; xx < state->grid_width; xx++)
846                   {
847                     int ii = yy     * state->grid_width + xx;
848                     int jj = (yy+1) * state->grid_width + xx;
849                     state->cells[ii] = state->cells[jj];
850                     state->cells[ii].changed = 1;
851                   }
852               /* clear bottom row */
853               for (xx = 0; xx < state->grid_width; xx++)
854                 {
855                   int ii = yy * state->grid_width + xx;
856                   state->cells[ii].glyph   = 0;
857                   state->cells[ii].changed = 1;
858                 }
859               y--;  /* move it back */
860               i = state->grid_width * y + x;
861             }
862
863           if (y >= state->grid_height) return;
864
865           cell = &state->cells[i];
866           if (visible_cursor)
867             {
868               cell->changed = 1;
869               state->cursor = cell;
870             }
871         }
872       else if (*s == '\010')
873         ;
874       else if (*s == '\002')
875         glow_p = True;
876       else
877         {
878           cell = &state->cells[i];
879           if (x < state->grid_width-1)
880             {
881               cell->glyph = char_map[(unsigned char) *s] + 1;
882               if (*s == ' ' || *s == '\t') cell->glyph = 0;
883               cell->changed = 1;
884               cell->glow = (glow_p ? 100 : 0);
885               if (visible_cursor)
886                 {
887                   m_cell *next = &state->cells[i + 1];
888                   next->changed = 1;
889                   state->cursor = next;
890                 }
891               i++;
892             }
893           x++;
894         }
895       s++;
896       if (typing_delay || transmit_delay || long_delay)
897         {
898           redraw_cells (state, False);
899           XSync (state->dpy, False);
900           handle_events (state);
901           if (typing_delay)
902             {
903               usleep (50000);
904               if (typing_delay && 0 == random() % 3)
905                 usleep (0xFFFFFF & ((random() % 250000) + 1));
906             }
907           else
908             if (long_delay)
909               matrix_usleep (state, long_delay_usecs);
910             else
911               usleep (20000);
912         }
913     }
914
915   *xP = x;
916   *yP = y;
917 }
918
919
920 static void
921 zero_cells (m_state *state)
922 {
923   int i;
924   for (i = 0; i < state->grid_height * state->grid_width; i++)
925     {
926       m_cell *cell = &state->cells[i];
927       cell->changed = (cell->glyph != 0);
928       cell->glyph   = 0;
929       cell->glow    = 0;
930       cell->spinner = 0;
931     }
932 }
933
934
935 static void
936 hack_text (m_state *state)
937 {
938   Bool typing_delay = False;
939   Bool transmit_delay = False;
940   Bool long_delay = False;
941   Bool visible_cursor = False;
942
943   switch (state->mode)
944     {
945     case KNOCK:
946       {
947         const char *blocks[] = {
948           "Wake up, Neo...",
949           "The Matrix has you...",
950           "Follow the white rabbit.",
951           " ",
952           "Knock, knock, Neo."
953         };
954         int nblocks = countof(blocks);
955         int j;
956         typing_delay = True;
957         transmit_delay = False;
958         long_delay = False;
959         visible_cursor = True;
960         for (j = 0; j < nblocks; j++)
961           {
962             int x = 3;
963             int y = 2;
964             const char *s = blocks[j];
965             if (!s[0] || !s[1]) typing_delay = False;
966             zero_cells (state);
967             hack_text_1 (state, &x, &y, s,
968                          typing_delay, transmit_delay, -1,
969                          visible_cursor, True);
970             matrix_usleep (state, 2000000);
971           }
972       }
973       break;
974
975     case TRACEA1: case TRACEB1:
976       {
977         const char *blocks[10];
978         int j, n = 0;
979
980         if (state->mode == TRACEA1)
981           blocks[n++] =
982             (state->grid_width >= 52
983              ?  "Call trans opt: received. 2-19-98 13:24:18 REC:Log>"
984              : "Call trans opt: received.\n2-19-98 13:24:18 REC:Log>");
985         else
986           blocks[n++] =
987             (state->grid_width >= 52
988              ?  "Call trans opt: received. 9-18-99 14:32:21 REC:Log>"
989              : "Call trans opt: received.\n9-18-99 14:32:21 REC:Log>");
990
991         if (state->mode == TRACEB1)
992           blocks[n++] = "WARNING: carrier anomaly";
993         blocks[n++] = "Trace program: running";
994
995         typing_delay = False;
996         transmit_delay = True;
997         long_delay = True;
998         visible_cursor = True;
999         for (j = 0; j < n; j++)
1000           {
1001             const char *s = blocks[j];
1002             int x = 0;
1003             int y = 0;
1004             zero_cells (state);
1005             hack_text_1 (state, &x, &y, s,
1006                          typing_delay, transmit_delay, long_delay,
1007                          visible_cursor, True);
1008           }
1009         matrix_usleep (state, 1000000);
1010       }
1011       break;
1012
1013     case SYSTEMFAILURE:
1014       {
1015         const char *s = "SYSTEM FAILURE";
1016         int i;
1017         float cx = ((int)state->grid_width - (int)strlen(s)) / 2 - 0.5;
1018         float cy = (state->grid_height / 2) - 1.3;
1019         int x, y;
1020
1021         if (cy < 0) cy = 0;
1022         if (cx < 0) cx = 0;
1023
1024         XFillRectangle (state->dpy, state->window, state->erase_gc,
1025                         cx * state->char_width,
1026                         cy * state->char_height,
1027                         (strlen(s) + 1) * state->char_width,
1028                         state->char_height * 1.6);
1029
1030         for (i = -2; i < 3; i++)
1031           {
1032             XGCValues gcv;
1033             gcv.foreground = state->colors[i + 2];
1034             XChangeGC (state->dpy, state->scratch_gc, GCForeground, &gcv);
1035             XDrawRectangle (state->dpy, state->window, state->scratch_gc,
1036                             cx * state->char_width - i,
1037                             cy * state->char_height - i,
1038                             (strlen(s) + 1) * state->char_width + (2 * i),
1039                             (state->char_height * 1.6) + (2 * i));
1040           }
1041
1042         /* If we don't clear these out, part of the box may get overwritten */
1043         for (i = 0; i < state->grid_height * state->grid_width; i++)
1044           {
1045             m_cell *cell = &state->cells[i];
1046             cell->changed = 0;
1047           }
1048
1049         x = ((int)state->grid_width - (int)strlen(s)) / 2;
1050         y = (state->grid_height / 2) - 1;
1051         if (y < 0) y = 0;
1052         if (x < 0) x = 0;
1053         hack_text_1 (state, &x, &y, s,
1054                      typing_delay, transmit_delay, long_delay,
1055                      visible_cursor, False);
1056       }
1057     break;
1058
1059     case NMAP:
1060       {
1061         /* Note that what Trinity is using here is moderately accurate:
1062            She runs nmap (http://www.insecure.org/nmap/) then breaks in
1063            with a (hypothetical) program called "sshnuke" that exploits
1064            the (very real) SSHv1 CRC32 compensation attack detector bug
1065            (http://staff.washington.edu/dittrich/misc/ssh-analysis.txt).
1066
1067            The command syntax of the power grid control software looks a
1068            lot like Cisco IOS to me.  (IOS is a descendant of VMS.)
1069         */
1070         const char *blocks[] = {
1071           "# ",
1072
1073           "\001nmap 10.2.2.2\n",
1074           "Starting nmap V. 2.54BETA25\n"
1075
1076           "\010", "\010", "\010",
1077
1078           "Insufficient responses for TCP sequencing (3), OS detection "
1079           "may be less accurate\n"
1080           "Interesting ports on 10.2.2.2:\n"
1081           "(The 1538 ports scanned but not shown below are in state: "
1082           "filtered)\n"
1083           "Port       state       service\n"
1084           "22/tcp     open        ssh\n"
1085           "\n"
1086           "No exact OS matches for host\n"
1087           "\n"
1088           "Nmap run completed -- 1 IP address (1 host up) scanned\n"
1089           "# ",
1090
1091           "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n",
1092
1093           "Connecting to 10.2.2.2:ssh ... ",
1094
1095           "successful.\n"
1096           "Attempting to exploit SSHv1 CRC32 ... ",
1097
1098           "successful.\n"
1099           "Resetting root password to \"Z1ON0101\".\n",
1100
1101           "System open: Access Level <9>\n"
1102           "# ",
1103
1104           "\001ssh 10.2.2.2 -l root\n",
1105
1106           "root@10.2.2.2's password: ",
1107
1108           "\001\010\010\010\010\010\010\010\010\n",
1109
1110           "\n"
1111           "RRF-CONTROL> ",
1112
1113           "\001disable grid nodes 21 - 48\n",
1114
1115           "\002Warning: Disabling nodes 21-48 will disconnect sector 11 "
1116           "(27 nodes)\n"
1117           "\n"
1118           "\002         ARE YOU SURE? (y/n) ",
1119
1120           "\001\010\010y\n",
1121           "\n"
1122         };
1123
1124         int nblocks = countof(blocks);
1125         int y = state->grid_height - 2;
1126         int x, j;
1127
1128         visible_cursor = True;
1129         x = 0;
1130         zero_cells (state);
1131         for (j = 0; j < nblocks; j++)
1132           {
1133             const char *s = blocks[j];
1134             typing_delay = (*s == '\001');
1135             if (typing_delay) s++;
1136
1137             long_delay = False;
1138             hack_text_1 (state, &x, &y, s,
1139                          typing_delay, transmit_delay, long_delay,
1140                          visible_cursor, True);
1141           }
1142
1143         typing_delay = False;
1144         long_delay = False;
1145         for (j = 21; j <= 48; j++)
1146           {
1147             char buf[100];
1148             sprintf (buf, "\002Grid Node %d offline...\n", j);
1149             hack_text_1 (state, &x, &y, buf,
1150                          typing_delay, transmit_delay, -1,
1151                          visible_cursor, True);
1152
1153           }
1154         long_delay = True;
1155         hack_text_1 (state, &x, &y, "\nRRF-CONTROL> ",
1156                      typing_delay, transmit_delay, long_delay,
1157                      visible_cursor, True);
1158
1159         /* De-glow all cells before draining them... */
1160         for (j = 0; j < state->grid_height * state->grid_width; j++)
1161           {
1162             m_cell *cell = &state->cells[j];
1163             cell->changed = (cell->glow != 0);
1164             cell->glow = 0;
1165           }
1166       }
1167     break;
1168
1169   default:
1170     abort();
1171     break;
1172   }
1173 }
1174
1175
1176 static void
1177 drain_matrix (m_state *state)
1178 {
1179   int delay = get_integer_resource ("delay", "Integer");
1180   int i;
1181
1182   /* Fill the top row with empty top-feeders, to clear the screen. */
1183   for (i = 0; i < state->grid_width; i++)
1184     {
1185       m_feeder *f = &state->feeders[i];
1186       f->y = -1;
1187       f->remaining = 0;
1188       f->throttle = 0;
1189     }
1190
1191   /* Turn off all the spinners, else they never go away. */
1192   for (i = 0; i < state->grid_width * state->grid_height; i++)
1193     if (state->cells[i].spinner)
1194       {
1195         state->cells[i].spinner = 0;
1196         state->cells[i].changed = 1;
1197       }
1198
1199   /* Run the machine until there are no live cells left. */
1200   while (1)
1201     {
1202       Bool any_cells_p = False;
1203       for (i = 0; i < state->grid_width * state->grid_height; i++)
1204         if (state->cells[i].glyph)
1205           {
1206             any_cells_p = True;
1207             goto FOUND;
1208           }
1209     FOUND:
1210       if (! any_cells_p)
1211         return;
1212
1213       feed_matrix (state);
1214       redraw_cells (state, True);
1215       XSync (state->dpy, False);
1216       handle_events (state);
1217       if (delay) usleep (delay);
1218     }
1219 }
1220
1221
1222 static void
1223 roll_state (m_state *state)
1224 {
1225   int delay = 0;
1226   switch (state->mode)
1227     {
1228     case TRACEA1:
1229       state->mode = TRACEA2;
1230       break;
1231     case TRACEB1:
1232       state->mode = TRACEB2;
1233       break;
1234
1235     case TRACEA2:
1236       {
1237         Bool any = False;
1238         int i;
1239         for (i = 0; i < strlen((char *) state->tracing); i++)
1240           if (state->tracing[i] < 0) any = True;
1241
1242         if (!any)
1243           {
1244             XSync (state->dpy, False);
1245             matrix_usleep (state, 3000000);
1246             state->mode = MATRIX;
1247             state->glyph_map = matrix_encoding;
1248             state->nglyphs = countof(matrix_encoding);
1249             flip_images (state);
1250             free (state->tracing);
1251             state->tracing = 0;
1252           }
1253         else if ((random() % 20) == 0)  /* how fast numbers are discovered */
1254           {
1255             int x = random() % strlen((char *) state->tracing);
1256             if (state->tracing[x] < 0)
1257               state->tracing[x] = -state->tracing[x];
1258           }
1259         break;
1260       }
1261
1262     case TRACEB2:
1263       {
1264         /* reversed logic from TRACEA2 */
1265         Bool any = False;
1266         int i;
1267         for (i = 0; i < strlen((char *) state->tracing); i++)
1268           if (state->tracing[i] > 0) any = True;
1269
1270         if ((random() % 15) == 0) {
1271           if (any)
1272             state->mode = SYSTEMFAILURE;
1273           else
1274             {
1275               int x = random() % strlen((char *) state->tracing);
1276               if (state->tracing[x] < 0)
1277                 state->tracing[x] = -state->tracing[x];
1278             }
1279         }
1280         break;
1281       }
1282
1283     case SYSTEMFAILURE:
1284       XSync (state->dpy, False);
1285       matrix_usleep (state, 6000000);
1286       state->mode = MATRIX;
1287       state->glyph_map = matrix_encoding;
1288       state->nglyphs = countof(matrix_encoding);
1289       flip_images (state);
1290       drain_matrix (state);
1291       matrix_usleep (state, 2000000);
1292       if (state->tracing) {
1293         free (state->tracing);
1294         state->tracing = 0;
1295       }
1296       break;
1297
1298     case KNOCK:
1299     case NMAP:
1300       state->mode = MATRIX;
1301       state->glyph_map = matrix_encoding;
1302       state->nglyphs = countof(matrix_encoding);
1303       flip_images (state);
1304       break;
1305
1306     case MATRIX:
1307       if (state->knock_knock_p && (! (random() % 3500)))
1308         {
1309           drain_matrix (state);
1310           if (! (random() % 5))
1311             state->mode = NMAP;
1312           else
1313             state->mode = KNOCK;
1314
1315           flip_images (state);
1316         }
1317       break;
1318
1319     case DNA: case BINARY: case DEC: case HEX:
1320       break;
1321
1322     default:
1323       abort();
1324       break;
1325     }
1326
1327   matrix_usleep (state, delay * 1000000);
1328   state->cursor = NULL;
1329 }
1330
1331
1332 static void
1333 hack_matrix (m_state *state)
1334 {
1335   int x;
1336
1337   switch (state->mode)
1338     {
1339     case TRACEA1: case TRACEB1: case SYSTEMFAILURE:
1340     case KNOCK: case NMAP:
1341       hack_text (state);
1342       return;
1343     case TRACEA2: case TRACEB2:
1344     case MATRIX: case DNA: case BINARY: case DEC: case HEX:
1345       break;
1346     default:
1347       abort(); break;
1348     }
1349
1350   /* Glow some characters. */
1351   if (!state->insert_bottom_p)
1352     {
1353       int i = random() % (state->grid_width / 2);
1354       while (--i > 0)
1355         {
1356           int yy = random() % state->grid_height;
1357           int xx = random() % state->grid_width;
1358           m_cell *cell = &state->cells[state->grid_width * yy + xx];
1359           if (cell->glyph && cell->glow == 0)
1360             {
1361               cell->glow = random() % 10;
1362               cell->changed = 1;
1363             }
1364         }
1365     }
1366
1367   /* Change some of the feeders. */
1368   for (x = 0; x < state->grid_width; x++)
1369     {
1370       m_feeder *f = &state->feeders[x];
1371       Bool bottom_feeder_p;
1372
1373       if (f->remaining > 0)     /* never change if pipe isn't empty */
1374         continue;
1375
1376       if ((random() % densitizer(state)) != 0) /* then change N% of the time */
1377         continue;
1378
1379       f->remaining = 3 + (random() % state->grid_height);
1380       f->throttle = ((random() % 5) + (random() % 5));
1381
1382       if ((random() % 4) != 0)
1383         f->remaining = 0;
1384
1385       if (state->mode == TRACEA2 || state->mode == TRACEB2)
1386         bottom_feeder_p = True;
1387       if (state->insert_top_p && state->insert_bottom_p)
1388         bottom_feeder_p = (random() & 1);
1389       else
1390         bottom_feeder_p = state->insert_bottom_p;
1391
1392       if (bottom_feeder_p)
1393         f->y = random() % (state->grid_height / 2);
1394       else
1395         f->y = -1;
1396     }
1397
1398   if (!state->mode == TRACEA2 && !state->mode == TRACEB2 &&
1399       ! (random() % 500))
1400     init_spinners (state);
1401 }
1402
1403
1404 static void
1405 draw_matrix (m_state *state)
1406 {
1407   feed_matrix (state);
1408   hack_matrix (state);
1409   redraw_cells (state, True);
1410   roll_state (state);
1411
1412 #if 0
1413   {
1414     static int i = 0;
1415     static int ndens = 0;
1416     static int tdens = 0;
1417     i++;
1418     if (i > 50)
1419       {
1420         int dens = (100.0 *
1421                     (((double)count) /
1422                      ((double) (state->grid_width * state->grid_height))));
1423         tdens += dens;
1424         ndens++;
1425         printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
1426         i = 0;
1427       }
1428   }
1429 #endif
1430
1431 }
1432
1433 \f
1434 char *progclass = "XMatrix";
1435
1436 char *defaults [] = {
1437   ".background:            black",
1438   ".foreground:            #00AA00",
1439   "*small:                 ",
1440   "*delay:                 10000",
1441   "*insert:                both",
1442   "*mode:                  Matrix",
1443   "*tracePhone:            (312) 555-0690",
1444   "*spinners:              5",
1445   "*density:               75",
1446   "*knockKnock:            False",
1447   "*geometry:              800x600",
1448   0
1449 };
1450
1451 XrmOptionDescRec options [] = {
1452   { "-small",           ".small",               XrmoptionNoArg, "True" },
1453   { "-large",           ".small",               XrmoptionNoArg, "False" },
1454   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
1455   { "-top",             ".insert",              XrmoptionNoArg, "top" },
1456   { "-bottom",          ".insert",              XrmoptionNoArg, "bottom" },
1457   { "-both",            ".insert",              XrmoptionNoArg, "both" },
1458   { "-density",         ".density",             XrmoptionSepArg, 0 },
1459   { "-trace",           ".mode",                XrmoptionNoArg, "trace" },
1460   { "-crack",           ".mode",                XrmoptionNoArg, "crack"},
1461   { "-phone",           ".tracePhone",          XrmoptionSepArg, 0 },
1462   { "-dna",             ".mode",                XrmoptionNoArg, "DNA" },
1463   { "-binary",          ".mode",                XrmoptionNoArg, "binary" },
1464   { "-hexadecimal",     ".mode",                XrmoptionNoArg, "hexadecimal"},
1465   { "-decimal",         ".mode",                XrmoptionNoArg, "decimal"},
1466   { "-knock-knock",     ".knockKnock",          XrmoptionNoArg, "True" },
1467   { 0, 0, 0, 0 }
1468 };
1469
1470
1471 void
1472 screenhack (Display *dpy, Window window)
1473 {
1474   m_state *state = init_matrix (dpy, window);
1475   int delay = get_integer_resource ("delay", "Integer");
1476   while (1)
1477     {
1478       draw_matrix (state);
1479       XSync (dpy, False);
1480       handle_events (state);
1481       if (delay) usleep (delay);
1482     }
1483 }