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