http://nanonyme.dy.fi/mirrors/hvl/distfiles/xscreensaver/xscreensaver-5.03.tar.gz
[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 #ifdef HAVE_UNISTD_H
53 # include <unistd.h>
54 #endif
55
56 #include "screenhack.h"
57 #include "xpm-pixmap.h"
58 #include <stdio.h>
59 #include <sys/wait.h>
60
61 #ifdef HAVE_COCOA
62 # define HAVE_XPM
63 #else
64 # define DO_XBM     /* only do mono bitmaps under real X11 */
65 #endif
66
67 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
68 # include "images/matrix1.xpm"
69 # include "images/matrix2.xpm"
70 # include "images/matrix1b.xpm"
71 # include "images/matrix2b.xpm"
72 #endif
73
74 #ifdef DO_XBM
75 # include "images/matrix1.xbm"
76 # include "images/matrix2.xbm"
77 # include "images/matrix1b.xbm"
78 # include "images/matrix2b.xbm"
79 #endif /* DO_XBM */
80
81 #ifndef HAVE_COCOA
82 # include <X11/Xatom.h>
83 # include <X11/Intrinsic.h>
84 #endif
85
86 #ifdef HAVE_FORKPTY
87 # include <sys/ioctl.h>
88 # ifdef HAVE_PTY_H
89 #  include <pty.h>
90 # endif
91 # ifdef HAVE_UTIL_H
92 #  include <util.h>
93 # endif
94 #endif /* HAVE_FORKPTY */
95
96 #define CHAR_COLS 16
97 #define CHAR_ROWS 13
98 #define CHAR_MAPS 3
99 #define PLAIN_MAP 1
100 #define GLOW_MAP  2
101
102 typedef struct {
103            int glow    : 8;
104   unsigned int glyph   : 9;  /* note: 9 bit characters! */
105   unsigned int changed : 1;
106   unsigned int spinner : 1;
107 } m_cell;
108
109 typedef struct {
110   int pipe_loc;
111   int remaining;
112   int throttle;
113   int y;
114 } m_feeder;
115
116 #define countof(x) (sizeof(x)/sizeof(*(x)))
117
118 static const int matrix_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
119                                        192, 193, 194, 195, 196, 197, 198, 199,
120                                        200, 201, 202, 203, 204, 205, 206, 207 };
121 static const int decimal_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25};
122 static const int hex_encoding[]     = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
123                                         33, 34, 35, 36, 37, 38 };
124 static const int binary_encoding[] = { 16, 17 };
125 static const int dna_encoding[]    = { 33, 35, 39, 52 };
126 static const int ascii_encoding[]  = {
127     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
128     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
129     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 
130    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 
131    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 
132    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 
133    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 
134    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 
135    96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
136   112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
137 };
138 static const unsigned char char_map[256] = {
139     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  /*   0 */
140     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  /*  16 */
141     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  /*  32 */
142    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,  /*  48 */
143    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,  /*  64 */
144    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,  /*  80 */
145    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  /*  96 */
146    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,  /* 112 */
147     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  /* 128 */
148     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  /* 144 */
149    96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,  /* 160 */
150   112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,  /* 176 */
151   128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,  /* 192 */
152   144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,  /* 208 */
153   160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,  /* 224 */
154   176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191   /* 240 */
155 };
156
157 #define CURSOR_GLYPH 97
158
159 /* larger numbers should mean more variability between columns */
160 #define BUF_SIZE 200
161
162 typedef enum { DRAIN_TRACE_A,
163                TRACE_TEXT_A,            /* Call trans opt: received. */
164                TRACE_A,                 /* (31_) 5__-0_9_ */
165                TRACE_DONE,
166
167                DRAIN_TRACE_B,
168                TRACE_TEXT_B,            /* Call trans opt: received. */
169                TRACE_B,
170                TRACE_FAIL,              /* System Failure */
171
172                DRAIN_KNOCK,
173                KNOCK,                   /* Wake up, Neo... */
174
175                DRAIN_NMAP,
176                NMAP,                    /* Starting nmap V. 2.54BETA25 */
177
178                DRAIN_MATRIX,
179                MATRIX,
180                DNA,
181                BINARY,
182                DEC,
183                HEX,
184                ASCII } m_mode;
185
186 typedef struct {
187   Display *dpy;
188   Window window;
189   XWindowAttributes xgwa;
190   GC draw_gc, erase_gc, scratch_gc;
191   int grid_width, grid_height;
192   int char_width, char_height;
193   m_cell *cells;
194   m_feeder *feeders;
195   int nspinners;
196   Bool knock_knock_p;
197   Bool small_p;
198   Bool insert_top_p, insert_bottom_p;
199   Bool use_pipe_p;
200   m_mode mode;
201
202   pid_t pid;
203   FILE *pipe;
204   XtInputId pipe_id;
205   Bool input_available_p;
206   Time subproc_relaunch_delay;
207   char buf [BUF_SIZE*2+1]; /* twice because this is a ring buffer */
208
209   Bool do_fill_buff;
210   int buf_done;
211   int buf_pos;
212
213   signed char *tracing;
214   int density;
215
216   const char *typing;
217   Bool typing_scroll_p;
218   Bool typing_cursor_p;
219   Bool typing_bold_p;
220   Bool typing_stutter_p;
221   int typing_left_margin;
222   int typing_char_delay;
223   int typing_line_delay;
224   int typing_delay;
225
226   Bool cursor_on;
227   int cursor_x, cursor_y;
228   XtIntervalId cursor_timer;
229
230   Pixmap images[CHAR_MAPS];
231   int image_width, image_height;
232   Bool images_flipped_p;
233
234   int nglyphs;
235   const int *glyph_map;
236
237   unsigned long colors[5];
238   int delay;
239 } m_state;
240
241
242 static void
243 load_images_1 (Display *dpy, m_state *state, int which)
244 {
245 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
246   if (!get_boolean_resource (dpy, "mono", "Boolean") &&
247       state->xgwa.depth > 1)
248     {
249       char **bits =
250         (which == 1 ? (state->small_p ? matrix1b_xpm : matrix1_xpm) :
251          (state->small_p ? matrix2b_xpm : matrix2_xpm));
252
253       state->images[which] =
254         xpm_data_to_pixmap (state->dpy, state->window, bits,
255                             &state->image_width, &state->image_height, 0);
256     }
257   else
258 #endif /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
259     {
260 #ifdef DO_XBM
261       unsigned long fg, bg;
262       state->image_width  = (state->small_p ? matrix1b_width :matrix1_width);
263       state->image_height = (state->small_p ? matrix1b_height:matrix1_height);
264       fg = get_pixel_resource(state->dpy, state->xgwa.colormap,
265                               "foreground", "Foreground");
266       bg = get_pixel_resource(state->dpy, state->xgwa.colormap,
267                               "background", "Background");
268       state->images[which] =
269         XCreatePixmapFromBitmapData (state->dpy, state->window, (char *)
270                 (which == 1 ? (state->small_p ? matrix1b_bits :matrix1_bits) :
271                               (state->small_p ? matrix2b_bits :matrix2_bits)),
272                                      state->image_width, state->image_height,
273                                      bg, fg, state->xgwa.depth);
274 #else  /* !DO_XBM */
275       abort();
276 #endif /* !DO_XBM */
277     }
278 }
279
280
281 static void
282 load_images (Display *dpy, m_state *state)
283 {
284   load_images_1 (dpy, state, 1);
285   load_images_1 (dpy, state, 2);
286 }
287
288
289 static void
290 flip_images_1 (m_state *state, int which)
291 {
292   XImage *im = XGetImage (state->dpy, state->images[which], 0, 0,
293                           state->image_width, state->image_height,
294                           ~0L, (state->xgwa.depth > 1 ? ZPixmap : XYPixmap));
295   int x, y, xx;
296   int ww = state->char_width;
297   unsigned long *row = (unsigned long *) malloc (sizeof(*row) * ww);
298
299   for (y = 0; y < state->image_height; y++)
300     {
301       for (x = 0; x < CHAR_COLS; x++)
302         {
303           for (xx = 0; xx < ww; xx++)
304             row[xx] = XGetPixel (im, (x * ww) + xx, y);
305           for (xx = 0; xx < ww; xx++)
306             XPutPixel (im, (x * ww) + xx, y, row[ww - xx - 1]);
307         }
308     }
309
310   XPutImage (state->dpy, state->images[which], state->draw_gc, im, 0, 0, 0, 0,
311              state->image_width, state->image_height);
312   XDestroyImage (im);
313   free (row);
314 }
315
316 static void
317 flip_images (m_state *state, Bool flipped_p)
318 {
319   if (flipped_p != state->images_flipped_p)
320     {
321       state->images_flipped_p = flipped_p;
322       flip_images_1 (state, 1);
323       flip_images_1 (state, 2);
324     }
325 }
326
327 static void
328 subproc_cb (XtPointer closure, int *source, XtInputId *id)
329 {
330   m_state *state = (m_state *) closure;
331   state->input_available_p = True;
332 }
333
334 static void
335 launch_text_generator (m_state *state)
336 {
337   XtAppContext app = XtDisplayToApplicationContext (state->dpy);
338   char *oprogram = get_string_resource (state->dpy, "program", "Program");
339   char *program = (char *) malloc (strlen (oprogram) + 10);
340   strcpy (program, "( ");
341   strcat (program, oprogram);
342   strcat (program, " ) 2>&1");
343
344   if ((state->pipe = popen (program, "r")))
345     {
346       state->pipe_id =
347         XtAppAddInput (app, fileno (state->pipe),
348                        (XtPointer) (XtInputReadMask | XtInputExceptMask),
349                        subproc_cb, (XtPointer) state);
350     }
351   else
352     {
353       perror (program);
354     }
355 }
356
357
358 static void
359 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
360 {
361   m_state *state = (m_state *) closure;
362   launch_text_generator (state);
363 }
364
365 /* When the subprocess has generated some output, this reads as much as it
366    can into s->buf at s->buf_tail.
367  */
368
369 static void
370 fill_input (m_state *s)
371 {
372   XtAppContext app = XtDisplayToApplicationContext (s->dpy);
373   int n;
374
375   if (! s->pipe) return;
376   if (! s->input_available_p) return;
377   s->input_available_p = False;
378
379   n = read (fileno (s->pipe),
380             (void *) (s->buf+s->buf_pos),
381             BUF_SIZE);
382   if (n > 0)
383     {
384       if (n == BUF_SIZE){
385         /* if one read wasn't enough to fill the whole buffer, then 
386            read again. This might need some work if it's too intensive. */
387         s->do_fill_buff = False;
388       }
389       s->buf_pos = (s->buf_pos + n);
390       if(s->buf_pos > BUF_SIZE){
391         /* just in case areas overlap */
392         memmove(s->buf,s->buf+BUF_SIZE,s->buf_pos-BUF_SIZE);
393       }
394       s->buf_pos = s->buf_pos % BUF_SIZE;
395       /*      s->input_available_p = True;*/
396     }
397   else
398     {
399       s->do_fill_buff = True;
400       XtRemoveInput (s->pipe_id);
401       s->pipe_id = 0;
402       pclose (s->pipe);
403       s->pipe = 0;
404
405       /* Set up a timer to re-launch the subproc in a bit. */
406       XtAppAddTimeOut (app, s->subproc_relaunch_delay,
407                        relaunch_generator_timer,
408                        (XtPointer) s);
409     }
410 }
411
412
413
414 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
415 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
416
417 static Bool
418 set_cursor_1 (m_state *state, Bool on)
419 {
420   Bool changed = (state->cursor_on != on);
421   state->cursor_on = on;
422
423   if (changed && state->cursor_x >= 0 && state->cursor_y >= 0)
424     {
425       m_cell *cell = &state->cells[state->grid_width * state->cursor_y
426                                    + state->cursor_x];
427       cell->glow = 0;
428       cell->changed = True;
429     }
430   return changed;
431 }
432
433
434 static void
435 set_cursor (m_state *state, Bool on)
436 {
437   if (set_cursor_1 (state, on))
438     {
439       if (state->cursor_timer)
440         XtRemoveTimeOut (state->cursor_timer);
441       state->cursor_timer = 0;
442       if (on)
443         cursor_on_timer (state, 0);
444     }
445 }
446
447 static void
448 cursor_off_timer (XtPointer closure, XtIntervalId *id)
449 {
450   m_state *state = (m_state *) closure;
451   XtAppContext app = XtDisplayToApplicationContext (state->dpy);
452   set_cursor_1 (state, False);
453   state->cursor_timer = XtAppAddTimeOut (app, 333,
454                                          cursor_on_timer, closure);
455 }
456
457 static void
458 cursor_on_timer (XtPointer closure, XtIntervalId *id)
459 {
460   m_state *state = (m_state *) closure;
461   XtAppContext app = XtDisplayToApplicationContext (state->dpy);
462   set_cursor_1 (state, True);
463   state->cursor_timer = XtAppAddTimeOut (app, 666,
464                                          cursor_off_timer, closure);
465 }
466
467
468 static void
469 init_spinners (m_state *state)
470 {
471   int i = state->nspinners;
472   int x, y;
473   m_cell *cell;
474
475   for (y = 0; y < state->grid_height; y++)
476     for (x = 0; x < state->grid_width; x++)
477       {
478         cell = &state->cells[state->grid_width * y + x];
479         cell->spinner = 0;
480       }
481
482   while (--i > 0)
483     {
484       x = random() % state->grid_width;
485       y = random() % state->grid_height;
486       cell = &state->cells[state->grid_width * y + x];
487       cell->spinner = 1;
488     }
489 }
490
491
492 static void set_mode (m_state *, m_mode);
493
494
495 static void
496 init_trace (m_state *state)
497 {
498   char *s = get_string_resource (state->dpy, "tracePhone", "TracePhone");
499   char *s2, *s3;
500   int i;
501   if (!s)
502     goto FAIL;
503
504   state->tracing = (signed char *) malloc (strlen (s) + 1);
505   s3 = (char *) state->tracing;
506
507   for (s2 = s; *s2; s2++)
508     if (*s2 >= '0' && *s2 <= '9')
509       *s3++ = *s2;
510   *s3 = 0;
511
512   if (s3 == (char *) state->tracing)
513     goto FAIL;
514
515   for (i = 0; i < strlen((char *) state->tracing); i++)
516     state->tracing[i] = -state->tracing[i];
517
518   state->glyph_map = decimal_encoding;
519   state->nglyphs = countof(decimal_encoding);
520
521   return;
522
523  FAIL:
524   fprintf (stderr, "%s: bad phone number: \"%s\".\n",
525            progname, s ? s : "(null)");
526
527   if (s) free (s);
528   if (state->tracing) free (state->tracing);
529   state->tracing = 0;
530   set_mode (state, MATRIX);
531 }
532
533
534 static void
535 init_drain (m_state *state)
536 {
537   int i;
538
539   set_cursor (state, False);
540   state->cursor_x = -1;
541   state->cursor_y = -1;
542
543   /* Fill the top row with empty top-feeders, to clear the screen. */
544   for (i = 0; i < state->grid_width; i++)
545     {
546       m_feeder *f = &state->feeders[i];
547       f->y = -1;
548       f->remaining = 0;
549       f->throttle = 0;
550       f->pipe_loc = BUF_SIZE-1;
551     }
552
553   /* Turn off all the spinners, else they never go away. */
554   for (i = 0; i < state->grid_width * state->grid_height; i++)
555     if (state->cells[i].spinner)
556       {
557         state->cells[i].spinner = 0;
558         state->cells[i].changed = 1;
559       }
560 }
561
562 static Bool
563 screen_blank_p (m_state *state)
564 {
565   int i;
566   for (i = 0; i < state->grid_width * state->grid_height; i++)
567     if (state->cells[i].glyph)
568       return False;
569   return True;
570 }
571
572
573 static void
574 set_mode (m_state *state, m_mode mode)
575 {
576   if (mode == state->mode)
577     return;
578
579   state->mode = mode;
580   state->typing = 0;
581
582   switch (mode)
583     {
584     case MATRIX:
585       state->glyph_map = matrix_encoding;
586       state->nglyphs = countof(matrix_encoding);
587       flip_images (state, True);
588       init_spinners (state);
589       break;
590     case DNA:
591       state->glyph_map = dna_encoding;
592       state->nglyphs = countof(dna_encoding);
593       flip_images (state, False);
594       break;
595     case BINARY:
596       state->glyph_map = binary_encoding;
597       state->nglyphs = countof(binary_encoding);
598       flip_images (state, False);
599       break;
600     case HEX:
601       state->glyph_map = hex_encoding;
602       state->nglyphs = countof(hex_encoding);
603       flip_images (state, False);
604       break;
605     case ASCII:
606       state->glyph_map = ascii_encoding;
607       state->nglyphs = countof(ascii_encoding);
608       flip_images (state, False);
609       break;
610     case DEC:
611     case TRACE_A:
612     case TRACE_B:
613     case NMAP:
614     case KNOCK:
615       state->glyph_map = decimal_encoding;
616       state->nglyphs = countof(decimal_encoding);
617       flip_images (state, False);
618       break;
619     case TRACE_TEXT_A: 
620     case TRACE_TEXT_B:
621       flip_images (state, False);
622       init_trace (state);
623       break;
624     case DRAIN_TRACE_A:
625     case DRAIN_TRACE_B:
626     case DRAIN_KNOCK:
627     case DRAIN_NMAP:
628     case DRAIN_MATRIX:
629       init_drain (state);
630       break;
631     case TRACE_DONE:
632     case TRACE_FAIL:
633       break;
634     default:
635       abort();
636     }
637 }
638
639
640 static void *
641 xmatrix_init (Display *dpy, Window window)
642 {
643   XGCValues gcv;
644   char *insert, *mode;
645   int i;
646   m_state *state = (m_state *) calloc (sizeof(*state), 1);
647
648   state->dpy = dpy;
649   state->window = window;
650   state->delay = get_integer_resource (dpy, "delay", "Integer");
651
652   XGetWindowAttributes (dpy, window, &state->xgwa);
653
654   state->small_p = (state->xgwa.width < 300);
655   {
656     const char *s = get_string_resource (dpy, "matrixFont", "String");
657     if (!s || !*s || !strcasecmp(s, "large"))
658       state->small_p = False;
659     else if (!strcasecmp(s, "small"))
660       state->small_p = True;
661     else
662       fprintf (stderr, "%s: matrixFont should be 'small' or 'large' not '%s'\n",
663                progname, s);
664   }
665
666   load_images (dpy, state);
667
668   gcv.foreground = get_pixel_resource(state->dpy, state->xgwa.colormap,
669                                       "foreground", "Foreground");
670   gcv.background = get_pixel_resource(state->dpy, state->xgwa.colormap,
671                                       "background", "Background");
672   state->draw_gc = XCreateGC (state->dpy, state->window,
673                               GCForeground|GCBackground, &gcv);
674   gcv.foreground = gcv.background;
675   state->erase_gc = XCreateGC (state->dpy, state->window,
676                                GCForeground|GCBackground, &gcv);
677
678   state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv);
679
680   /* Allocate colors for SYSTEM FAILURE box */
681   {
682     XColor boxcolors[] = {
683       { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
684       { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
685       { 0, 0xE0E0, 0xF7F7, 0xE0E0, DoRed|DoGreen|DoBlue, 0 },
686       { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
687       { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
688     };
689   for (i = 0; i < countof(boxcolors); i++)
690     {
691       if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i]))
692         state->colors[i] = boxcolors[i].pixel;
693       else
694         state->colors[i] = gcv.foreground;  /* default black */
695     }
696   }
697
698   state->char_width =  state->image_width  / CHAR_COLS;
699   state->char_height = state->image_height / CHAR_ROWS;
700
701   state->grid_width  = state->xgwa.width  / state->char_width;
702   state->grid_height = state->xgwa.height / state->char_height;
703   state->grid_width++;
704   state->grid_height++;
705   if (state->grid_width  < 5) state->grid_width  = 5;
706   if (state->grid_height < 5) state->grid_height = 5;
707
708   state->glyph_map = matrix_encoding;
709   state->nglyphs = countof(matrix_encoding);
710
711   state->cells = (m_cell *)
712     calloc (sizeof(m_cell), state->grid_width * state->grid_height);
713   state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
714
715   state->density = get_integer_resource (dpy, "density", "Integer");
716
717   insert = get_string_resource(dpy, "insert", "Insert");
718   if (insert && !strcmp(insert, "top"))
719     {
720       state->insert_top_p = True;
721       state->insert_bottom_p = False;
722     }
723   else if (insert && !strcmp(insert, "bottom"))
724     {
725       state->insert_top_p = False;
726       state->insert_bottom_p = True;
727     }
728   else if (insert && !strcmp(insert, "both"))
729     {
730       state->insert_top_p = True;
731       state->insert_bottom_p = True;
732     }
733   else
734     {
735       if (insert && *insert)
736         fprintf (stderr,
737                  "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
738                  progname, insert);
739       state->insert_top_p = False;
740       state->insert_bottom_p = True;
741     }
742
743   state->nspinners = get_integer_resource (dpy, "spinners", "Integer");
744
745   if (insert)
746     free (insert);
747
748   state->knock_knock_p = get_boolean_resource (dpy, "knockKnock", "KnockKnock");
749
750   state->use_pipe_p = get_boolean_resource (dpy, "usePipe", "Boolean");
751   state->buf_done = 0;
752   state->do_fill_buff = True;
753
754   launch_text_generator (state);
755
756   state->mode = -1;
757   mode = get_string_resource (dpy, "mode", "Mode");
758   if (mode && !strcasecmp(mode, "trace"))
759     set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
760   else if (mode && !strcasecmp(mode, "crack"))
761     set_mode (state, DRAIN_NMAP);
762   else if (mode && !strcasecmp(mode, "dna"))
763     set_mode (state, DNA);
764   else if (mode && (!strcasecmp(mode, "bin") ||
765                     !strcasecmp(mode, "binary")))
766     set_mode (state, BINARY);
767   else if (mode && (!strcasecmp(mode, "hex") ||
768                     !strcasecmp(mode, "hexadecimal")))
769     set_mode (state, HEX);
770   else if (mode && (!strcasecmp(mode, "dec") ||
771                     !strcasecmp(mode, "decimal")))
772     set_mode (state, DEC);
773   else if (mode && (!strcasecmp(mode, "asc") ||
774                     !strcasecmp(mode, "ascii")))
775     set_mode (state, ASCII);
776   else if (!mode || !*mode || !strcasecmp(mode, "matrix"))
777     set_mode (state, MATRIX);
778   else if (!mode || !*mode || !strcasecmp(mode, "pipe"))
779     {
780       set_mode (state, ASCII);
781       state->use_pipe_p = True;
782     }
783   else
784     {
785       fprintf (stderr, "%s: `mode' must be ",progname);
786       fprintf (stderr, "matrix, trace, dna, binary, ascii, or hex: ");
787       fprintf (stderr, "not `%s'\n", mode);
788       set_mode (state, MATRIX);
789     }
790
791   if (state->mode == MATRIX && get_boolean_resource (dpy, "trace", "Boolean"))
792     set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
793
794   state->cursor_x = -1;
795   state->cursor_y = -1;
796
797   return state;
798 }
799
800
801 static void
802 insert_glyph (m_state *state, int glyph, int x, int y)
803 {
804   Bool bottom_feeder_p = (y >= 0);
805   m_cell *from, *to;
806
807   if (y >= state->grid_height)
808     return;
809
810   if (bottom_feeder_p)
811     {
812       to = &state->cells[state->grid_width * y + x];
813     }
814   else
815     {
816       for (y = state->grid_height-1; y > 0; y--)
817         {
818           from = &state->cells[state->grid_width * (y-1) + x];
819           to   = &state->cells[state->grid_width * y     + x];
820           to->glyph   = from->glyph;
821           to->glow    = from->glow;
822           to->changed = 1;
823         }
824       to = &state->cells[x];
825     }
826
827   to->glyph = glyph;
828   to->changed = 1;
829
830   if (!to->glyph)
831     ;
832   else if (bottom_feeder_p)
833     to->glow = 1 + (random() % (state->tracing ? 4 : 2));
834   else
835     to->glow = 0;
836 }
837
838
839 static void
840 feed_matrix (m_state *state)
841 {
842   int x;
843
844   switch (state->mode)
845     {
846     case TRACE_A:
847         {
848           int L = strlen((char *) state->tracing);
849           int count = 0;
850           int i;
851
852           for (i = 0; i < strlen((char *) state->tracing); i++)
853             if (state->tracing[i] > 0) 
854               count++;
855
856           if (count >= L)
857             {
858               set_mode (state, TRACE_DONE);
859               state->typing_delay = 1000000;
860               return;
861             }
862           else
863             {
864               i = 5 + (30 / (count+1)); /* how fast numbers are discovered */
865
866               if ((random() % i) == 0)
867                 {
868                   i = random() % L;
869                   if (state->tracing[i] < 0)
870                     state->tracing[i] = -state->tracing[i];
871                 }
872             }
873         }
874       break;
875
876     case TRACE_B:
877       if ((random() % 40) == 0)
878         {
879           set_mode (state, TRACE_FAIL);
880           return;
881         }
882       break;
883
884     case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII: 
885     case DRAIN_TRACE_A:
886     case DRAIN_TRACE_B:
887     case DRAIN_KNOCK:
888     case DRAIN_NMAP:
889     case DRAIN_MATRIX:
890       break;
891
892     default:
893       abort();
894     }
895
896   /*get input*/
897   if(state->use_pipe_p){
898     state->buf_done = (state->buf_done + 1) % BUF_SIZE;
899     if(state->buf_done == 0){
900       state->do_fill_buff = True;
901     }
902     if(state->do_fill_buff){
903       fill_input(state);
904     }
905   }
906
907   /* Update according to current feeders. */
908   for (x = 0; x < state->grid_width; x++)
909     {
910       m_feeder *f = &state->feeders[x];
911
912       if (f->throttle)          /* this is a delay tick, synced to frame. */
913         {
914           f->throttle--;
915         }
916       else if (f->remaining > 0)        /* how many items are in the pipe */
917         {
918           int g;
919           long rval;
920           if(state->use_pipe_p){
921             rval = (int) state->buf[f->pipe_loc];
922             if(++f->pipe_loc > (BUF_SIZE-1)){
923               f->pipe_loc = 0;
924               /*fill_input(state);*/
925             }
926             rval = (rval % state->nglyphs);
927           }
928           else{
929             rval = (random() % state->nglyphs);
930           }
931           g = state->glyph_map[rval] + 1;
932           insert_glyph (state, g, x, f->y);
933           f->remaining--;
934           if (f->y >= 0)  /* bottom_feeder_p */
935             f->y++;
936         }
937       else                              /* if pipe is empty, insert spaces */
938         {
939           insert_glyph (state, 0, x, f->y);
940           if (f->y >= 0)  /* bottom_feeder_p */
941             f->y++;
942         }
943
944       if ((random() % 10) == 0)         /* randomly change throttle speed */
945         {
946           f->throttle = ((random() % 5) + (random() % 5));
947         }
948     }
949 }
950
951
952 static void
953 redraw_cells (m_state *state, Bool active)
954 {
955   int x, y;
956   int count = 0;
957
958   for (y = 0; y < state->grid_height; y++)
959     for (x = 0; x < state->grid_width; x++)
960       {
961         m_cell *cell = &state->cells[state->grid_width * y + x];
962         Bool cursor_p = (state->cursor_on &&
963                          x == state->cursor_x && 
964                          y == state->cursor_y);
965
966         if (cell->glyph)
967           count++;
968
969         /* In trace-mode, the state of each cell is random unless we have
970            a match for this digit. */
971         if (active && (state->mode == TRACE_A || 
972                        state->mode == TRACE_B ||
973                        state->mode == TRACE_DONE))
974           {
975             int xx = x % strlen((char *) state->tracing);
976             Bool dead_p = state->tracing[xx] > 0;
977
978             if (y == 0 && x == xx)
979               cell->glyph = (dead_p
980                              ? state->glyph_map[state->tracing[xx]-'0'] + 1
981                              : 0);
982             else if (y == 0)
983               cell->glyph = 0;
984             else
985               cell->glyph = (dead_p ? 0 :
986                              (state->glyph_map[(random()%state->nglyphs)]
987                               + 1));
988
989             cell->changed = 1;
990           }
991
992         if (!cell->changed)
993           continue;
994
995         if (cell->glyph == 0 && !cursor_p)
996           XFillRectangle (state->dpy, state->window, state->erase_gc,
997                           x * state->char_width,
998                           y * state->char_height,
999                           state->char_width,
1000                           state->char_height);
1001         else
1002           {
1003             int g = (cursor_p ? CURSOR_GLYPH : cell->glyph);
1004             int cx = (g - 1) % CHAR_COLS;
1005             int cy = (g - 1) / CHAR_COLS;
1006             int map = ((cell->glow != 0 || cell->spinner) ? GLOW_MAP :
1007                        PLAIN_MAP);
1008
1009             XCopyArea (state->dpy, state->images[map],
1010                        state->window, state->draw_gc,
1011                        cx * state->char_width,
1012                        cy * state->char_height,
1013                        state->char_width,
1014                        state->char_height,
1015                        x * state->char_width,
1016                        y * state->char_height);
1017           }
1018
1019         cell->changed = 0;
1020
1021         if (cell->glow > 0 && state->mode != NMAP)
1022           {
1023             cell->glow--;
1024             cell->changed = 1;
1025           }
1026         else if (cell->glow < 0)
1027           abort();
1028
1029         if (cell->spinner && active)
1030           {
1031             cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1);
1032             cell->changed = 1;
1033           }
1034       }
1035 }
1036
1037
1038 static int
1039 densitizer (m_state *state)
1040 {
1041   /* Horrid kludge that converts percentages (density of screen coverage)
1042      to the parameter that actually controls this.  I got this mapping
1043      empirically, on a 1024x768 screen.  Sue me. */
1044   if      (state->density < 10) return 85;
1045   else if (state->density < 15) return 60;
1046   else if (state->density < 20) return 45;
1047   else if (state->density < 25) return 25;
1048   else if (state->density < 30) return 20;
1049   else if (state->density < 35) return 15;
1050   else if (state->density < 45) return 10;
1051   else if (state->density < 50) return 8;
1052   else if (state->density < 55) return 7;
1053   else if (state->density < 65) return 5;
1054   else if (state->density < 80) return 3;
1055   else if (state->density < 90) return 2;
1056   else return 1;
1057 }
1058
1059
1060 static void
1061 hack_text (m_state *state)
1062 {
1063   if (!state->typing)
1064     {
1065       set_cursor (state, False);
1066       state->cursor_x = 0;
1067       state->cursor_y = 0;
1068       state->typing_scroll_p = False;
1069       state->typing_bold_p = False;
1070       state->typing_cursor_p = True;
1071       state->typing_stutter_p = False;
1072       state->typing_char_delay = 10000;
1073       state->typing_line_delay = 1500000;
1074
1075       switch (state->mode)
1076         {
1077         case TRACE_TEXT_A:
1078         case TRACE_TEXT_B:
1079           if (state->mode == TRACE_TEXT_A)
1080             {
1081               if (state->grid_width >= 52)
1082                 state->typing =
1083                   ("Call trans opt: received. 2-19-98 13:24:18 REC:Log>\n"
1084                    "Trace program: running\n");
1085               else
1086                 state->typing =
1087                   ("Call trans opt: received.\n2-19-98 13:24:18 REC:Log>\n"
1088                    "Trace program: running\n");
1089             }
1090           else
1091             {
1092               if (state->grid_width >= 52)
1093                 state->typing =
1094                   ("Call trans opt: received. 9-18-99 14:32:21 REC:Log>\n"
1095                    "WARNING: carrier anomaly\n"
1096                    "Trace program: running\n");
1097               else
1098                 state->typing =
1099                   ("Call trans opt: received.\n9-18-99 14:32:21 REC:Log>\n"
1100                    "WARNING: carrier anomaly\n"
1101                    "Trace program: running\n");
1102             }
1103           break;
1104
1105         case TRACE_FAIL:
1106           {
1107             const char *s = "SYSTEM FAILURE\n";
1108             int i;
1109             float cx = (state->grid_width - strlen(s) - 1) / 2 - 0.5;
1110             float cy = (state->grid_height / 2) - 1.3;
1111
1112             if (cy < 0) cy = 0;
1113             if (cx < 0) cx = 0;
1114
1115             XFillRectangle (state->dpy, state->window, state->erase_gc,
1116                             cx * state->char_width,
1117                             cy * state->char_height,
1118                             strlen(s) * state->char_width,
1119                             state->char_height * 1.6);
1120
1121             for (i = -2; i < 3; i++)
1122               {
1123                 XGCValues gcv;
1124                 gcv.foreground = state->colors[i + 2];
1125                 XChangeGC (state->dpy, state->scratch_gc, GCForeground, &gcv);
1126                 XDrawRectangle (state->dpy, state->window, state->scratch_gc,
1127                                 cx * state->char_width - i,
1128                                 cy * state->char_height - i,
1129                                 strlen(s) * state->char_width + (2 * i),
1130                                 (state->char_height * 1.6) + (2 * i));
1131               }
1132
1133             /* If we don't clear these, part of the box may get overwritten */
1134             for (i = 0; i < state->grid_height * state->grid_width; i++)
1135               {
1136                 m_cell *cell = &state->cells[i];
1137                 cell->changed = 0;
1138               }
1139
1140             state->cursor_x = (state->grid_width - strlen(s) - 1) / 2;
1141             state->cursor_y = (state->grid_height / 2) - 1;
1142             if (state->cursor_x < 0) state->cursor_x = 0;
1143             if (state->cursor_y < 0) state->cursor_y = 0;
1144
1145             state->typing = s;
1146             state->typing_char_delay = 0;
1147             state->typing_cursor_p = False;
1148           }
1149           break;
1150
1151         case KNOCK:
1152           {
1153             state->typing = ("\001Wake up, Neo...\n"
1154                              "\001The Matrix has you...\n"
1155                              "\001Follow the white rabbit.\n"
1156                              "\n"
1157                              "Knock, knock, Neo.\n");
1158
1159             state->cursor_x = 4;
1160             state->cursor_y = 2;
1161             state->typing_char_delay = 0;
1162             state->typing_line_delay = 2000000;
1163           }
1164           break;
1165
1166         case NMAP:
1167           {
1168             /* Note that what Trinity is using here is moderately accurate:
1169                She runs nmap (http://www.insecure.org/nmap/) then breaks in
1170                with a (hypothetical) program called "sshnuke" that exploits
1171                the (very real) SSHv1 CRC32 compensation attack detector bug
1172                (http://staff.washington.edu/dittrich/misc/ssh-analysis.txt).
1173
1174                The command syntax of the power grid control software looks a
1175                lot like Cisco IOS to me.  (IOS is a descendant of VMS.)
1176             */
1177
1178             state->typing =
1179 # ifdef __GNUC__
1180             __extension__  /* don't warn about "string length is greater than
1181                               the length ISO C89 compilers are required to
1182                               support"... */
1183
1184 # endif
1185               ("# "
1186                "\010\010\010\010"
1187                "\001nmap 10.2.2.2\n"
1188                "Starting nmap V. 2.54BETA25\n"
1189                "\010\010\010\010\010\010\010\010\010\010"
1190                "Insufficient responses for TCP sequencing (3), OS detection"
1191                " may be less accurate\n"
1192                "Interesting ports on 10.2.2.2:\n"
1193                "(The 1538 ports scanned but not shown below are in state:"
1194                " filtered)\n"
1195                "Port       state       service\n"
1196                "22/tcp     open        ssh\n"
1197                "\n"
1198                "No exact OS matches for host\n"
1199                "\n"
1200                "Nmap run completed -- 1 IP address (1 host up) scanned\n"
1201                "# "
1202
1203                "\010\010\010\010"
1204                "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n"
1205                "Connecting to 10.2.2.2:ssh ... "
1206                "\010\010"
1207                "successful.\n"
1208                "Attempting to exploit SSHv1 CRC32 ... "
1209                "\010\010\010\010"
1210                "successful.\n"
1211                "Resetting root password to \"Z1ON0101\".\n"
1212                "\010\010"
1213                "System open: Access Level <9>\n"
1214
1215                "# "
1216                "\010\010"
1217
1218                "\001ssh 10.2.2.2 -l root\n"
1219                "\010\010"
1220                "root@10.2.2.2's password: "
1221                "\010\010\n"
1222                "\010\010\n"
1223                "RRF-CONTROL> "
1224
1225                "\010\010"
1226                "\001disable grid nodes 21 - 48\n"
1227                "\n"
1228                "\002Warning: Disabling nodes 21-48 will disconnect sector 11"
1229                " (28 nodes)\n"
1230                "\n"
1231                "\002         ARE YOU SURE? (y/n) "
1232
1233                "\010\010"
1234                "\001y\n"
1235                "\n"
1236                "\n"
1237                "\010\002Grid Node 21 offline...\n"
1238                "\010\002Grid Node 22 offline...\n"
1239                "\010\002Grid Node 23 offline...\n"
1240                "\010\002Grid Node 24 offline...\n"
1241                "\010\002Grid Node 25 offline...\n"
1242                "\010\002Grid Node 26 offline...\n"
1243                "\010\002Grid Node 27 offline...\n"
1244                "\010\002Grid Node 28 offline...\n"
1245                "\010\002Grid Node 29 offline...\n"
1246                "\010\002Grid Node 30 offline...\n"
1247                "\010\002Grid Node 31 offline...\n"
1248                "\010\002Grid Node 32 offline...\n"
1249                "\010\002Grid Node 33 offline...\n"
1250                "\010\002Grid Node 34 offline...\n"
1251                "\010\002Grid Node 35 offline...\n"
1252                "\010\002Grid Node 36 offline...\n"
1253                "\010\002Grid Node 37 offline...\n"
1254                "\010\002Grid Node 38 offline...\n"
1255                "\010\002Grid Node 39 offline...\n"
1256                "\010\002Grid Node 40 offline...\n"
1257                "\010\002Grid Node 41 offline...\n"
1258                "\010\002Grid Node 42 offline...\n"
1259                "\010\002Grid Node 43 offline...\n"
1260                "\010\002Grid Node 44 offline...\n"
1261                "\010\002Grid Node 45 offline...\n"
1262                "\010\002Grid Node 46 offline...\n"
1263                "\010\002Grid Node 47 offline...\n"
1264                "\010\002Grid Node 48 offline...\n"
1265                "\010\010"
1266                "\nRRF-CONTROL> "
1267                "\010\010\010\010\010\010\010\010"
1268                );
1269
1270             state->cursor_x = 0;
1271             state->cursor_y = state->grid_height - 3;
1272             state->typing_scroll_p = True;
1273             state->typing_char_delay = 0;
1274             state->typing_line_delay = 20000;
1275           }
1276           break;
1277
1278         default:
1279           abort();
1280           break;
1281         }
1282
1283       state->typing_left_margin = state->cursor_x;
1284       state->typing_delay = state->typing_char_delay;
1285       if (state->typing_cursor_p)
1286         set_cursor (state, True);
1287     }
1288   else
1289     {
1290       Bool scrolled_p = False;
1291       unsigned char c, c1;
1292       int x = state->cursor_x;
1293       int y = state->cursor_y;
1294
1295     AGAIN:
1296       c  = ((unsigned char *) state->typing)[0];
1297       c1 = ((unsigned char *) state->typing)[1];
1298
1299       state->typing_delay = (!c || c1 == '\n'
1300                              ? state->typing_line_delay
1301                              : state->typing_char_delay);
1302       if (! c)
1303         {
1304           state->typing_delay = 0;
1305           state->typing = 0;
1306           return;
1307         }
1308
1309       if (state->typing_scroll_p &&
1310           (c == '\n' || 
1311            x >= state->grid_width - 1))
1312         {
1313           set_cursor (state, False);
1314           x = 0;
1315           y++;
1316
1317           if (y >= state->grid_height-1)
1318             {
1319               int xx, yy;
1320               for (yy = 0; yy < state->grid_height-2; yy++)
1321                 for (xx = 0; xx < state->grid_width; xx++)
1322                   {
1323                     int ii = yy     * state->grid_width + xx;
1324                     int jj = (yy+1) * state->grid_width + xx;
1325                     state->cells[ii] = state->cells[jj];
1326                     state->cells[ii].changed = 1;
1327                   }
1328               /* clear bottom row */
1329               for (xx = 0; xx < state->grid_width; xx++)
1330                 {
1331                   int ii = yy * state->grid_width + xx;
1332                   state->cells[ii].glyph   = 0;
1333                   state->cells[ii].changed = 1;
1334                 }
1335               y--;  /* move back up to bottom line */
1336               scrolled_p = True;
1337             }
1338         }
1339
1340       if (c == '\n')
1341         {
1342           if (!state->typing_scroll_p)
1343             {
1344               int i, j;
1345               set_cursor (state, False);
1346               x = state->typing_left_margin;
1347
1348               /* clear the line */
1349               i = state->grid_width * y;
1350               j = i + state->grid_width;
1351               for (; i < j; i++)
1352                 {
1353                   state->cells[i].glyph   = 0;
1354                   state->cells[i].changed = 1;
1355                 }
1356             }
1357           state->typing_bold_p = False;
1358           state->typing_stutter_p = False;
1359           scrolled_p = True;
1360         }
1361
1362       else if (c == '\010')
1363         state->typing_delay += 500000;
1364
1365       else if (c == '\001')
1366         {
1367           state->typing_stutter_p = True;
1368           state->typing_bold_p = False;
1369         }
1370       else if (c == '\002')
1371         state->typing_bold_p = True;
1372
1373       else if (x < state->grid_width-1)
1374         {
1375           m_cell *cell = &state->cells[state->grid_width * y + x];
1376           cell->glyph = char_map[c] + 1;
1377           if (c == ' ' || c == '\t') cell->glyph = 0;
1378           cell->changed = 1;
1379           cell->glow = (state->typing_bold_p ? 127 : 0);
1380         }
1381
1382       if (c >= ' ')
1383         x++;
1384
1385       if (x >= state->grid_width-1)
1386         x = state->grid_width-1;
1387
1388       state->typing++;
1389
1390       if (state->typing_stutter_p)
1391         {
1392           if (state->typing_delay == 0)
1393             state->typing_delay = 20000;
1394           if (random() % 3)
1395             state->typing_delay += (0xFFFFFF & ((random() % 200000) + 1));
1396         }
1397
1398       /* If there's no delay after this character, just keep going. */
1399       if (state->typing_delay == 0)
1400         goto AGAIN;
1401
1402       if (scrolled_p || x != state->cursor_x || y != state->cursor_y)
1403         {
1404           set_cursor (state, False);
1405           state->cursor_x = x;
1406           state->cursor_y = y;
1407           if (state->typing_cursor_p)
1408             set_cursor (state, True);
1409         }
1410     }
1411 }
1412
1413
1414 static void
1415 hack_matrix (m_state *state)
1416 {
1417   int x;
1418
1419   switch (state->mode)
1420     {
1421     case TRACE_DONE: case TRACE_FAIL:
1422       return;
1423     case TRACE_A: case TRACE_B:
1424     case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1425       break;
1426     default:
1427       abort(); break;
1428     }
1429
1430   /* Glow some characters. */
1431   if (!state->insert_bottom_p)
1432     {
1433       int i = random() % (state->grid_width / 2);
1434       while (--i > 0)
1435         {
1436           int yy = random() % state->grid_height;
1437           int xx = random() % state->grid_width;
1438           m_cell *cell = &state->cells[state->grid_width * yy + xx];
1439           if (cell->glyph && cell->glow == 0)
1440             {
1441               cell->glow = random() % 10;
1442               cell->changed = 1;
1443             }
1444         }
1445     }
1446
1447   /* Change some of the feeders. */
1448   for (x = 0; x < state->grid_width; x++)
1449     {
1450       m_feeder *f = &state->feeders[x];
1451       Bool bottom_feeder_p;
1452
1453       if (f->remaining > 0)     /* never change if pipe isn't empty */
1454         continue;
1455
1456       if ((random() % densitizer(state)) != 0) /* then change N% of the time */
1457         continue;
1458
1459       f->remaining = 3 + (random() % state->grid_height);
1460       f->throttle = ((random() % 5) + (random() % 5));
1461
1462       if ((random() % 4) != 0)
1463         f->remaining = 0;
1464
1465       if (state->mode == TRACE_A || state->mode == TRACE_B)
1466         bottom_feeder_p = True;
1467       if (state->insert_top_p && state->insert_bottom_p)
1468         bottom_feeder_p = (random() & 1);
1469       else
1470         bottom_feeder_p = state->insert_bottom_p;
1471
1472       if (bottom_feeder_p)
1473         f->y = random() % (state->grid_height / 2);
1474       else
1475         f->y = -1;
1476     }
1477
1478   if (state->mode == MATRIX && (! (random() % 500)))
1479     init_spinners (state);
1480 }
1481
1482
1483 static unsigned long
1484 xmatrix_draw (Display *dpy, Window window, void *closure)
1485 {
1486   m_state *state = (m_state *) closure;
1487
1488   if (state->typing_delay > 0)
1489     {
1490       state->typing_delay -= state->delay;
1491       if (state->typing_delay < 0)
1492         state->typing_delay = 0;
1493       redraw_cells (state, False);
1494       return state->delay;
1495     }
1496
1497   switch (state->mode)
1498     {
1499     case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1500     case TRACE_A: case TRACE_B:
1501       feed_matrix (state);
1502       hack_matrix (state);
1503       break;
1504
1505     case DRAIN_TRACE_A:
1506     case DRAIN_TRACE_B:
1507     case DRAIN_KNOCK:
1508     case DRAIN_NMAP:
1509     case DRAIN_MATRIX:
1510       feed_matrix (state);
1511       if (screen_blank_p (state))
1512         {
1513           state->typing_delay = 500000;
1514           switch (state->mode)
1515             {
1516             case DRAIN_TRACE_A: set_mode (state, TRACE_TEXT_A); break;
1517             case DRAIN_TRACE_B: set_mode (state, TRACE_TEXT_B); break;
1518             case DRAIN_KNOCK:   set_mode (state, KNOCK);        break;
1519             case DRAIN_NMAP:    set_mode (state, NMAP);         break;
1520             case DRAIN_MATRIX:  set_mode (state, MATRIX);       break;
1521             default:            abort();                        break;
1522             }
1523         }
1524       break;
1525
1526     case TRACE_DONE:
1527       set_mode (state, MATRIX);
1528       break;
1529
1530     case TRACE_TEXT_A: 
1531     case TRACE_TEXT_B: 
1532     case TRACE_FAIL: 
1533     case KNOCK: 
1534     case NMAP:
1535       hack_text (state);
1536
1537       if (! state->typing)  /* done typing */
1538         {
1539           set_cursor (state, False);
1540           switch (state->mode)
1541             {
1542             case TRACE_TEXT_A: set_mode (state, TRACE_A); break;
1543             case TRACE_TEXT_B: set_mode (state, TRACE_B); break;
1544             case TRACE_FAIL:   set_mode (state, MATRIX);  break;
1545             case KNOCK:        set_mode (state, MATRIX);  break;
1546             case NMAP:         set_mode (state, MATRIX);  break;
1547             default:           abort();                   break;
1548             }
1549         }
1550       break;
1551
1552     default:
1553       abort();
1554     }
1555
1556   if (state->mode == MATRIX &&
1557       state->knock_knock_p &&
1558       (! (random() % 10000)))
1559     {
1560       if (! (random() % 5))
1561         set_mode (state, DRAIN_NMAP);
1562       else
1563         set_mode (state, DRAIN_KNOCK);
1564     }
1565
1566   redraw_cells (state, True);
1567
1568 #if 0
1569   {
1570     static int i = 0;
1571     static int ndens = 0;
1572     static int tdens = 0;
1573     i++;
1574     if (i > 50)
1575       {
1576         int dens = (100.0 *
1577                     (((double)count) /
1578                      ((double) (state->grid_width * state->grid_height))));
1579         tdens += dens;
1580         ndens++;
1581         printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
1582         i = 0;
1583       }
1584   }
1585 #endif
1586
1587   return state->delay;
1588 }
1589
1590
1591 static void
1592 xmatrix_reshape (Display *dpy, Window window, void *closure, 
1593                  unsigned int w, unsigned int h)
1594 {
1595   m_state *state = (m_state *) closure;
1596   int ow = state->grid_width;
1597   int oh = state->grid_height;
1598   XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
1599   state->grid_width  = state->xgwa.width  / state->char_width;
1600   state->grid_height = state->xgwa.height / state->char_height;
1601   state->grid_width++;
1602   state->grid_height++;
1603   if (state->grid_width  < 5) state->grid_width  = 5;
1604   if (state->grid_height < 5) state->grid_height = 5;
1605
1606   if (ow != state->grid_width ||
1607       oh != state->grid_height)
1608     {
1609       m_cell *ncells = (m_cell *)
1610         calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1611       m_feeder *nfeeders = (m_feeder *)
1612         calloc (sizeof(m_feeder), state->grid_width);
1613       int x, y, i;
1614
1615       /* fprintf(stderr, "resize: %d x %d  ==>  %d x %d\n",
1616          ow, oh, state->grid_width, state->grid_height); */
1617
1618       for (y = 0; y < oh; y++)
1619         for (x = 0; x < ow; x++)
1620           if (x < ow && x < state->grid_width &&
1621               y < oh && y < state->grid_height)
1622             ncells[y * state->grid_width + x] =
1623               state->cells[y * ow + x];
1624       free (state->cells);
1625       state->cells = ncells;
1626
1627       x = (ow < state->grid_width ? ow : state->grid_width);
1628       for (i = 0; i < x; i++)
1629         nfeeders[i] = state->feeders[i];
1630       free (state->feeders);
1631       state->feeders = nfeeders;
1632     }
1633 }
1634
1635 static Bool
1636 xmatrix_event (Display *dpy, Window window, void *closure, XEvent *event)
1637 {
1638   m_state *state = (m_state *) closure;
1639
1640  if (event->xany.type == KeyPress)
1641    {
1642      KeySym keysym;
1643      char c = 0;
1644      XLookupString (&event->xkey, &c, 1, &keysym, 0);
1645      switch (c)
1646        {
1647        case '0':
1648          set_mode (state, DRAIN_MATRIX);
1649          return True;
1650
1651        case '+': case '=': case '>': case '.':
1652          state->density += 10;
1653          if (state->density > 100)
1654            state->density = 100;
1655          else
1656            return True;
1657          break;
1658
1659        case '-': case '_': case '<': case ',':
1660          state->density -= 10;
1661          if (state->density < 0)
1662            state->density = 0;
1663          else
1664            return True;
1665          break;
1666
1667        case '[': case '(': case '{':
1668          state->insert_top_p    = True;
1669          state->insert_bottom_p = False;
1670          return True;
1671
1672        case ']': case ')': case '}':
1673          state->insert_top_p    = False;
1674          state->insert_bottom_p = True;
1675          return True;
1676
1677        case '\\': case '|':
1678          state->insert_top_p    = True;
1679          state->insert_bottom_p = True;
1680          return True;
1681
1682        case 't':
1683          set_mode (state, DRAIN_TRACE_A);
1684          return True;
1685
1686        case 'T':
1687          set_mode (state, DRAIN_TRACE_B);
1688          return True;
1689
1690        case 'k':
1691          set_mode (state, DRAIN_KNOCK);
1692          return True;
1693
1694        case 'c':
1695          set_mode (state, DRAIN_NMAP);
1696          return True;
1697
1698        default:
1699          return False;
1700        }
1701    }
1702
1703   return False;
1704 }
1705
1706 static void
1707 xmatrix_free (Display *dpy, Window window, void *closure)
1708 {
1709   m_state *state = (m_state *) closure;
1710
1711   if (state->cursor_timer)
1712     XtRemoveTimeOut (state->cursor_timer);
1713
1714   /* #### there's more to free here */
1715
1716   free (state);
1717 }
1718
1719
1720 \f
1721 static const char *xmatrix_defaults [] = {
1722   ".background:            black",
1723   ".foreground:            #00AA00",
1724   "*matrixFont:            large",
1725   "*delay:                 10000",
1726   "*insert:                both",
1727   "*mode:                  Matrix",
1728   "*tracePhone:            (312) 555-0690",
1729   "*spinners:              5",
1730   "*density:               75",
1731   "*trace:                 True",
1732   "*knockKnock:            True",
1733   "*usePipe:               False",
1734   "*program:               xscreensaver-text",
1735   "*geometry:              800x600",
1736   0
1737 };
1738
1739 static XrmOptionDescRec xmatrix_options [] = {
1740   { "-small",           ".matrixFont",          XrmoptionNoArg, "Small" },
1741   { "-large",           ".matrixFont",          XrmoptionNoArg, "Large" },
1742   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
1743   { "-insert",          ".insert",              XrmoptionSepArg, 0 },
1744   { "-top",             ".insert",              XrmoptionNoArg, "top" },
1745   { "-bottom",          ".insert",              XrmoptionNoArg, "bottom" },
1746   { "-both",            ".insert",              XrmoptionNoArg, "both" },
1747   { "-density",         ".density",             XrmoptionSepArg, 0 },
1748   { "-trace",           ".trace",               XrmoptionNoArg, "True" },
1749   { "-no-trace",        ".trace",               XrmoptionNoArg, "False" },
1750   { "-crack",           ".mode",                XrmoptionNoArg, "crack"},
1751   { "-phone",           ".tracePhone",          XrmoptionSepArg, 0 },
1752   { "-mode",            ".mode",                XrmoptionSepArg, 0 },
1753   { "-dna",             ".mode",                XrmoptionNoArg, "DNA" },
1754   { "-binary",          ".mode",                XrmoptionNoArg, "binary" },
1755   { "-hexadecimal",     ".mode",                XrmoptionNoArg, "hexadecimal"},
1756   { "-decimal",         ".mode",                XrmoptionNoArg, "decimal"},
1757   { "-knock-knock",     ".knockKnock",          XrmoptionNoArg, "True" },
1758   { "-no-knock-knock",  ".knockKnock",          XrmoptionNoArg, "False" },
1759   { "-ascii",           ".mode",                XrmoptionNoArg, "ascii"},
1760   { "-pipe",            ".usePipe",             XrmoptionNoArg, "True" },
1761   { "-no-pipe",         ".usePipe",             XrmoptionNoArg, "False" },
1762   { "-program",         ".program",             XrmoptionSepArg, 0 },
1763   { 0, 0, 0, 0 }
1764 };
1765
1766 XSCREENSAVER_MODULE ("XMatrix", xmatrix)