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