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