1 /* xscreensaver, Copyright (c) 1999, 2001 Jamie Zawinski <jwz@jwz.org>
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
11 * Matrix -- simulate the text scrolls from the movie "The Matrix".
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
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.
23 * ==========================================================
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.
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...
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.
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.
48 * ==========================================================
56 #include "screenhack.h"
57 #include "xpm-pixmap.h"
64 # define DO_XBM /* only do mono bitmaps under real X11 */
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"
75 # include "images/matrix1.xbm"
76 # include "images/matrix2.xbm"
77 # include "images/matrix1b.xbm"
78 # include "images/matrix2b.xbm"
82 # include <X11/Xatom.h>
83 # include <X11/Intrinsic.h>
87 # include <sys/ioctl.h>
94 #endif /* HAVE_FORKPTY */
104 unsigned int glyph : 9; /* note: 9 bit characters! */
105 unsigned int changed : 1;
106 unsigned int spinner : 1;
116 #define countof(x) (sizeof(x)/sizeof(*(x)))
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
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 */
157 #define CURSOR_GLYPH 97
159 /* larger numbers should mean more variability between columns */
162 typedef enum { DRAIN_TRACE_A,
163 TRACE_TEXT_A, /* Call trans opt: received. */
164 TRACE_A, /* (31_) 5__-0_9_ */
168 TRACE_TEXT_B, /* Call trans opt: received. */
170 TRACE_FAIL, /* System Failure */
173 KNOCK, /* Wake up, Neo... */
176 NMAP, /* Starting nmap V. 2.54BETA25 */
189 XWindowAttributes xgwa;
190 GC draw_gc, erase_gc, scratch_gc;
191 int grid_width, grid_height;
192 int char_width, char_height;
199 Bool insert_top_p, insert_bottom_p;
202 m_mode def_mode; /* Mode to return to after trace etc. */
207 Bool input_available_p;
208 Time subproc_relaunch_delay;
209 char buf [BUF_SIZE*2+1]; /* ring buffer */
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 */
220 signed char *tracing;
224 Bool typing_scroll_p;
225 Bool typing_cursor_p;
227 Bool typing_stutter_p;
228 int typing_left_margin;
229 int typing_char_delay;
230 int typing_line_delay;
234 int cursor_x, cursor_y;
235 XtIntervalId cursor_timer;
237 Pixmap images[CHAR_MAPS];
238 int image_width, image_height;
239 Bool images_flipped_p;
242 const int *glyph_map;
244 unsigned long colors[5];
250 load_images_1 (Display *dpy, m_state *state, int which)
252 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
253 if (!get_boolean_resource (dpy, "mono", "Boolean") &&
254 state->xgwa.depth > 1)
257 (which == 1 ? (state->small_p ? matrix1b_xpm : matrix1_xpm) :
258 (state->small_p ? matrix2b_xpm : matrix2_xpm));
260 state->images[which] =
261 xpm_data_to_pixmap (state->dpy, state->window, bits,
262 &state->image_width, &state->image_height, 0);
265 #endif /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
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);
289 load_images (Display *dpy, m_state *state)
291 load_images_1 (dpy, state, 1);
292 load_images_1 (dpy, state, 2);
297 flip_images_1 (m_state *state, int which)
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));
303 int ww = state->char_width;
304 unsigned long *row = (unsigned long *) malloc (sizeof(*row) * ww);
306 for (y = 0; y < state->image_height; y++)
308 for (x = 0; x < CHAR_COLS; x++)
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]);
317 XPutImage (state->dpy, state->images[which], state->draw_gc, im, 0, 0, 0, 0,
318 state->image_width, state->image_height);
324 flip_images (m_state *state, Bool flipped_p)
326 if (flipped_p != state->images_flipped_p)
328 state->images_flipped_p = flipped_p;
329 flip_images_1 (state, 1);
330 flip_images_1 (state, 2);
335 subproc_cb (XtPointer closure, int *source, XtInputId *id)
337 m_state *state = (m_state *) closure;
338 state->input_available_p = True;
342 launch_text_generator (m_state *state)
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");
351 if ((state->pipe = popen (program, "r")))
354 XtAppAddInput (app, fileno (state->pipe),
355 (XtPointer) (XtInputReadMask | XtInputExceptMask),
356 subproc_cb, (XtPointer) state);
366 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
368 m_state *state = (m_state *) closure;
369 launch_text_generator (state);
372 /* When the subprocess has generated some output, this reads as much as it
373 can into s->buf at s->buf_tail.
377 fill_input (m_state *s)
379 XtAppContext app = XtDisplayToApplicationContext (s->dpy);
382 if(s->buf_done > s->buf_pos){
383 loadBytes = (s->buf_done - s->buf_pos) - 1;
386 loadBytes = ((BUF_SIZE - s->buf_pos) + s->buf_done) - 1;
388 if (! s->pipe) return;
389 if (! s->input_available_p) return;
390 s->input_available_p = False;
393 n = read (fileno (s->pipe),
394 (void *) (s->buf+s->buf_pos),
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);
406 s->buf_pos = s->buf_pos % BUF_SIZE;
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);
418 /* Set up a timer to re-launch the subproc in a bit. */
419 XtAppAddTimeOut (app, s->subproc_relaunch_delay,
420 relaunch_generator_timer,
427 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
428 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
431 set_cursor_1 (m_state *state, Bool on)
433 Bool changed = (state->cursor_on != on);
434 state->cursor_on = on;
436 if (changed && state->cursor_x >= 0 && state->cursor_y >= 0)
438 m_cell *cell = &state->cells[state->grid_width * state->cursor_y
441 cell->changed = True;
448 set_cursor (m_state *state, Bool on)
450 if (set_cursor_1 (state, on))
452 if (state->cursor_timer)
453 XtRemoveTimeOut (state->cursor_timer);
454 state->cursor_timer = 0;
456 cursor_on_timer (state, 0);
461 cursor_off_timer (XtPointer closure, XtIntervalId *id)
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);
471 cursor_on_timer (XtPointer closure, XtIntervalId *id)
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);
482 init_spinners (m_state *state)
484 int i = state->nspinners;
488 for (y = 0; y < state->grid_height; y++)
489 for (x = 0; x < state->grid_width; x++)
491 cell = &state->cells[state->grid_width * y + x];
497 x = random() % state->grid_width;
498 y = random() % state->grid_height;
499 cell = &state->cells[state->grid_width * y + x];
505 static void set_mode (m_state *, m_mode);
509 init_trace (m_state *state)
511 char *s = get_string_resource (state->dpy, "tracePhone", "TracePhone");
517 state->tracing = (signed char *) malloc (strlen (s) + 1);
518 s3 = (char *) state->tracing;
520 for (s2 = s; *s2; s2++)
521 if (*s2 >= '0' && *s2 <= '9')
525 if (s3 == (char *) state->tracing)
528 for (i = 0; i < strlen((char *) state->tracing); i++)
529 state->tracing[i] = -state->tracing[i];
531 state->glyph_map = decimal_encoding;
532 state->nglyphs = countof(decimal_encoding);
537 fprintf (stderr, "%s: bad phone number: \"%s\".\n",
538 progname, s ? s : "(null)");
541 if (state->tracing) free (state->tracing);
543 set_mode (state, MATRIX);
548 init_drain (m_state *state)
552 set_cursor (state, False);
553 state->cursor_x = -1;
554 state->cursor_y = -1;
556 /* Fill the top row with empty top-feeders, to clear the screen. */
557 for (i = 0; i < state->grid_width; i++)
559 m_feeder *f = &state->feeders[i];
565 /* Turn off all the spinners, else they never go away. */
566 for (i = 0; i < state->grid_width * state->grid_height; i++)
567 if (state->cells[i].spinner)
569 state->cells[i].spinner = 0;
570 state->cells[i].changed = 1;
575 screen_blank_p (m_state *state)
578 for (i = 0; i < state->grid_width * state->grid_height; i++)
579 if (state->cells[i].glyph)
586 set_mode (m_state *state, m_mode mode)
588 if (mode == state->mode)
597 state->glyph_map = matrix_encoding;
598 state->nglyphs = countof(matrix_encoding);
599 flip_images (state, True);
600 init_spinners (state);
603 state->glyph_map = dna_encoding;
604 state->nglyphs = countof(dna_encoding);
605 flip_images (state, False);
608 state->glyph_map = binary_encoding;
609 state->nglyphs = countof(binary_encoding);
610 flip_images (state, False);
613 state->glyph_map = hex_encoding;
614 state->nglyphs = countof(hex_encoding);
615 flip_images (state, False);
618 state->glyph_map = ascii_encoding;
619 state->nglyphs = countof(ascii_encoding);
620 flip_images (state, False);
627 state->glyph_map = decimal_encoding;
628 state->nglyphs = countof(decimal_encoding);
629 flip_images (state, False);
633 flip_images (state, False);
653 xmatrix_init (Display *dpy, Window window)
658 m_state *state = (m_state *) calloc (sizeof(*state), 1);
661 state->window = window;
662 state->delay = get_integer_resource (dpy, "delay", "Integer");
664 XGetWindowAttributes (dpy, window, &state->xgwa);
666 state->small_p = (state->xgwa.width < 300);
668 const char *s = get_string_resource (dpy, "matrixFont", "String");
669 if (!s || !*s || !strcasecmp(s, "large"))
670 state->small_p = False;
671 else if (!strcasecmp(s, "small"))
672 state->small_p = True;
674 fprintf (stderr, "%s: matrixFont should be 'small' or 'large' not '%s'\n",
678 load_images (dpy, state);
680 gcv.foreground = get_pixel_resource(state->dpy, state->xgwa.colormap,
681 "foreground", "Foreground");
682 gcv.background = get_pixel_resource(state->dpy, state->xgwa.colormap,
683 "background", "Background");
684 state->draw_gc = XCreateGC (state->dpy, state->window,
685 GCForeground|GCBackground, &gcv);
686 gcv.foreground = gcv.background;
687 state->erase_gc = XCreateGC (state->dpy, state->window,
688 GCForeground|GCBackground, &gcv);
690 state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv);
692 /* Allocate colors for SYSTEM FAILURE box */
694 XColor boxcolors[] = {
695 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
696 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
697 { 0, 0xE0E0, 0xF7F7, 0xE0E0, DoRed|DoGreen|DoBlue, 0 },
698 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
699 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
701 for (i = 0; i < countof(boxcolors); i++)
703 if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i]))
704 state->colors[i] = boxcolors[i].pixel;
706 state->colors[i] = gcv.foreground; /* default black */
710 state->char_width = state->image_width / CHAR_COLS;
711 state->char_height = state->image_height / CHAR_ROWS;
713 state->grid_width = state->xgwa.width / state->char_width;
714 state->grid_height = state->xgwa.height / state->char_height;
716 state->grid_height++;
717 if (state->grid_width < 5) state->grid_width = 5;
718 if (state->grid_height < 5) state->grid_height = 5;
720 state->glyph_map = matrix_encoding;
721 state->nglyphs = countof(matrix_encoding);
723 state->cells = (m_cell *)
724 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
725 state->background = (m_cell *)
726 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
727 state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
729 state->density = get_integer_resource (dpy, "density", "Integer");
731 insert = get_string_resource(dpy, "insert", "Insert");
732 if (insert && !strcmp(insert, "top"))
734 state->insert_top_p = True;
735 state->insert_bottom_p = False;
737 else if (insert && !strcmp(insert, "bottom"))
739 state->insert_top_p = False;
740 state->insert_bottom_p = True;
742 else if (insert && !strcmp(insert, "both"))
744 state->insert_top_p = True;
745 state->insert_bottom_p = True;
749 if (insert && *insert)
751 "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
753 state->insert_top_p = False;
754 state->insert_bottom_p = True;
757 state->nspinners = get_integer_resource (dpy, "spinners", "Integer");
762 state->knock_knock_p = get_boolean_resource (dpy, "knockKnock", "KnockKnock");
764 state->use_pipe_p = get_boolean_resource (dpy, "usePipe", "Boolean");
766 state->buf[0] = ' '; /* spacer byte in buffer (space) */
768 state->do_fill_buff = True;
769 state->start_reveal_back_p = False;
770 state->back_text_full_p = False;
775 state->def_mode = MATRIX;
776 mode = get_string_resource (dpy, "mode", "Mode");
777 if (mode && !strcasecmp(mode, "trace"))
778 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
779 else if (mode && !strcasecmp(mode, "crack"))
780 set_mode (state, DRAIN_NMAP);
781 else if (mode && !strcasecmp(mode, "dna")){
782 set_mode (state, DNA);
783 state->def_mode = DNA;
785 else if (mode && (!strcasecmp(mode, "bin") ||
786 !strcasecmp(mode, "binary"))){
787 set_mode (state, BINARY);
788 state->def_mode = BINARY;
790 else if (mode && (!strcasecmp(mode, "hex") ||
791 !strcasecmp(mode, "hexadecimal"))){
792 set_mode (state, HEX);
793 state->def_mode = HEX;
795 else if (mode && (!strcasecmp(mode, "dec") ||
796 !strcasecmp(mode, "decimal"))){
797 set_mode (state, DEC);
798 state->def_mode = DEC;
800 else if (mode && (!strcasecmp(mode, "asc") ||
801 !strcasecmp(mode, "ascii"))){
802 set_mode (state, ASCII);
803 state->def_mode = ASCII;
805 else if (mode && !strcasecmp(mode, "pipe"))
807 set_mode (state, ASCII);
808 state->def_mode = ASCII;
809 state->use_pipe_p = True;
810 launch_text_generator (state);
812 else if (!mode || !*mode || !strcasecmp(mode, "matrix"))
813 set_mode (state, MATRIX);
816 fprintf (stderr, "%s: `mode' must be ",progname);
817 fprintf (stderr, "matrix, trace, dna, binary, ascii, hex, or pipe: ");
818 fprintf (stderr, "not `%s'\n", mode);
819 set_mode (state, MATRIX);
822 if (state->mode == MATRIX && get_boolean_resource (dpy, "trace", "Boolean"))
823 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
825 state->cursor_x = -1;
826 state->cursor_y = -1;
833 insert_glyph (m_state *state, int glyph, int x, int y)
835 Bool bottom_feeder_p = (y >= 0);
837 if (y >= state->grid_height)
842 to = &state->cells[state->grid_width * y + x];
846 for (y = state->grid_height-1; y > 0; y--)
848 from = &state->cells[state->grid_width * (y-1) + x];
849 to = &state->cells[state->grid_width * y + x];
850 to->glyph = from->glyph;
851 to->glow = from->glow;
854 to = &state->cells[x];
862 else if (bottom_feeder_p)
863 to->glow = 1 + (random() % (state->tracing ? 4 : 2));
870 place_back_char (m_state *state, char textc, int x, int y){
871 if((x>=0) && (y>=0) &&
872 (x < state->grid_width) && (y < state->grid_height)){
873 m_cell *celltmp = &state->background[state->grid_width * y + x];
874 celltmp -> glyph = char_map[(unsigned char)textc] + 1;
875 if(!celltmp->glyph || (celltmp->glyph == 3)){
876 celltmp -> glyph = char_map[32] + 1;
878 celltmp -> changed = 1;
883 place_back_text (m_state *state, char *text, int x, int y){
885 for(i=0; i<strlen(text); i++){
886 place_back_char(state, text[i], x+i, y);
891 place_back_pipe (m_state *state, char textc){
892 Bool new_line = False;
893 /* gringer pipe insert */
894 state->back_line[state->back_pos] = textc;
896 state->back_line[state->back_pos] = '\0';
899 else if ((state->back_pos > (state->grid_width - 4)) ||
900 (state->back_pos >= BUF_SIZE)){ /* off by 1? */
901 state->back_line[++state->back_pos] = '\0';
908 int startx = (state->grid_width >> 1) -
909 (strlen(state->back_line) >> 1);
910 place_back_text(state, state->back_line,
911 startx, state->back_y);
914 if(state->back_y >= (state->grid_height - 1)){
916 state->back_text_full_p = True;
917 state->start_reveal_back_p = True;
923 feed_matrix (m_state *state)
931 int L = strlen((char *) state->tracing);
935 for (i = 0; i < strlen((char *) state->tracing); i++)
936 if (state->tracing[i] > 0)
941 set_mode (state, TRACE_DONE);
942 state->typing_delay = 1000000;
947 i = 5 + (30 / (count+1)); /* how fast numbers are discovered */
949 if ((random() % i) == 0)
952 if (state->tracing[i] < 0)
953 state->tracing[i] = -state->tracing[i];
960 if ((random() % 40) == 0)
962 set_mode (state, TRACE_FAIL);
967 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
980 if((state->use_pipe_p) && (!state->back_text_full_p)){
981 place_back_pipe(state, state->buf[state->buf_done]);
982 state->buf_done = (state->buf_done + 1) % BUF_SIZE;
983 if(state->buf_done == (state->buf_pos - 1)){
984 state->do_fill_buff = True;
987 if(state->buf_done == (state->buf_pos + 1)){
988 state->do_fill_buff = False;
991 state->do_fill_buff = True;
995 /* Update according to current feeders. */
996 for (x = 0; x < state->grid_width; x++)
998 m_feeder *f = &state->feeders[x];
1000 if (f->throttle) /* this is a delay tick, synced to frame. */
1004 else if (f->remaining > 0) /* how many items are in the pipe */
1008 if((state->use_pipe_p) && (!state->back_text_full_p)){
1009 rval = (int) state->buf[f->pipe_loc];
1010 if(++f->pipe_loc > (BUF_SIZE-1)){
1012 /*fill_input(state);*/
1014 rval = (rval % state->nglyphs);
1017 rval = (random() % state->nglyphs);
1019 g = state->glyph_map[rval] + 1;
1020 insert_glyph (state, g, x, f->y);
1022 if (f->y >= 0) /* bottom_feeder_p */
1025 else /* if pipe is empty, insert spaces */
1027 insert_glyph (state, 0, x, f->y);
1028 if (f->y >= 0) /* bottom_feeder_p */
1032 if ((random() % 10) == 0) /* randomly change throttle speed */
1034 f->throttle = ((random() % 5) + (random() % 5));
1041 redraw_cells (m_state *state, Bool active)
1045 Bool use_back_p = False;
1047 for (y = 0; y < state->grid_height; y++)
1048 for (x = 0; x < state->grid_width; x++)
1050 m_cell *cell = &state->cells[state->grid_width * y + x];
1051 m_cell *back = &state->background[state->grid_width * y + x];
1052 Bool cursor_p = (state->cursor_on &&
1053 x == state->cursor_x &&
1054 y == state->cursor_y);
1059 if((state->start_reveal_back_p) &&
1060 (back->glyph) && !(state->mode == TRACE_A ||
1061 state->mode == TRACE_B ||
1062 state->mode == TRACE_DONE)){
1068 /* In trace-mode, the state of each cell is random unless we have
1069 a match for this digit. */
1070 if (active && (state->mode == TRACE_A ||
1071 state->mode == TRACE_B ||
1072 state->mode == TRACE_DONE))
1074 int xx = x % strlen((char *) state->tracing);
1075 Bool dead_p = state->tracing[xx] > 0;
1077 if (y == 0 && x == xx && !use_back_p)
1078 cell->glyph = (dead_p
1079 ? state->glyph_map[state->tracing[xx]-'0'] + 1
1081 else if (y == 0 && !use_back_p)
1083 else if (!use_back_p)
1084 cell->glyph = (dead_p ? 0 :
1085 (state->glyph_map[(random()%state->nglyphs)]
1095 if (cell->glyph == 0 && !cursor_p && !use_back_p)
1096 XFillRectangle (state->dpy, state->window, state->erase_gc,
1097 x * state->char_width,
1098 y * state->char_height,
1100 state->char_height);
1103 int g = (cursor_p ? CURSOR_GLYPH : cell->glyph);
1104 int cx = (g - 1) % CHAR_COLS;
1105 int cy = (g - 1) / CHAR_COLS;
1106 int map = ((cell->glow != 0 || cell->spinner) ? GLOW_MAP :
1109 XCopyArea (state->dpy, state->images[map],
1110 state->window, state->draw_gc,
1111 cx * state->char_width,
1112 cy * state->char_height,
1115 x * state->char_width,
1116 y * state->char_height);
1121 if (cell->glow > 0 && state->mode != NMAP && !use_back_p)
1126 else if (cell->glow < 0)
1129 if (cell->spinner && active && !use_back_p)
1131 cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1);
1139 densitizer (m_state *state)
1141 /* Horrid kludge that converts percentages (density of screen coverage)
1142 to the parameter that actually controls this. I got this mapping
1143 empirically, on a 1024x768 screen. Sue me. */
1144 if (state->density < 10) return 85;
1145 else if (state->density < 15) return 60;
1146 else if (state->density < 20) return 45;
1147 else if (state->density < 25) return 25;
1148 else if (state->density < 30) return 20;
1149 else if (state->density < 35) return 15;
1150 else if (state->density < 45) return 10;
1151 else if (state->density < 50) return 8;
1152 else if (state->density < 55) return 7;
1153 else if (state->density < 65) return 5;
1154 else if (state->density < 80) return 3;
1155 else if (state->density < 90) return 2;
1161 hack_text (m_state *state)
1165 set_cursor (state, False);
1166 state->cursor_x = 0;
1167 state->cursor_y = 0;
1168 state->typing_scroll_p = False;
1169 state->typing_bold_p = False;
1170 state->typing_cursor_p = True;
1171 state->typing_stutter_p = False;
1172 state->typing_char_delay = 10000;
1173 state->typing_line_delay = 1500000;
1175 switch (state->mode)
1179 if (state->mode == TRACE_TEXT_A)
1181 if (state->grid_width >= 52)
1183 ("Call trans opt: received. 2-19-98 13:24:18 REC:Log>\n"
1184 "Trace program: running\n");
1187 ("Call trans opt: received.\n2-19-98 13:24:18 REC:Log>\n"
1188 "Trace program: running\n");
1192 if (state->grid_width >= 52)
1194 ("Call trans opt: received. 9-18-99 14:32:21 REC:Log>\n"
1195 "WARNING: carrier anomaly\n"
1196 "Trace program: running\n");
1199 ("Call trans opt: received.\n9-18-99 14:32:21 REC:Log>\n"
1200 "WARNING: carrier anomaly\n"
1201 "Trace program: running\n");
1207 const char *s = "SYSTEM FAILURE\n";
1209 float cx = (state->grid_width - strlen(s) - 1) / 2 - 0.5;
1210 float cy = (state->grid_height / 2) - 1.3;
1215 XFillRectangle (state->dpy, state->window, state->erase_gc,
1216 cx * state->char_width,
1217 cy * state->char_height,
1218 strlen(s) * state->char_width,
1219 state->char_height * 1.6);
1221 for (i = -2; i < 3; i++)
1224 gcv.foreground = state->colors[i + 2];
1225 XChangeGC (state->dpy, state->scratch_gc, GCForeground, &gcv);
1226 XDrawRectangle (state->dpy, state->window, state->scratch_gc,
1227 cx * state->char_width - i,
1228 cy * state->char_height - i,
1229 strlen(s) * state->char_width + (2 * i),
1230 (state->char_height * 1.6) + (2 * i));
1233 /* If we don't clear these, part of the box may get overwritten */
1234 for (i = 0; i < state->grid_height * state->grid_width; i++)
1236 m_cell *cell = &state->cells[i];
1240 state->cursor_x = (state->grid_width - strlen(s) - 1) / 2;
1241 state->cursor_y = (state->grid_height / 2) - 1;
1242 if (state->cursor_x < 0) state->cursor_x = 0;
1243 if (state->cursor_y < 0) state->cursor_y = 0;
1246 state->typing_char_delay = 0;
1247 state->typing_cursor_p = False;
1253 state->typing = ("\001Wake up, Neo...\n"
1254 "\001The Matrix has you...\n"
1255 "\001Follow the white rabbit.\n"
1257 "Knock, knock, Neo.\n");
1259 state->cursor_x = 4;
1260 state->cursor_y = 2;
1261 state->typing_char_delay = 0;
1262 state->typing_line_delay = 2000000;
1268 /* Note that what Trinity is using here is moderately accurate:
1269 She runs nmap (http://www.insecure.org/nmap/) then breaks in
1270 with a (hypothetical) program called "sshnuke" that exploits
1271 the (very real) SSHv1 CRC32 compensation attack detector bug
1272 (http://staff.washington.edu/dittrich/misc/ssh-analysis.txt).
1274 The command syntax of the power grid control software looks a
1275 lot like Cisco IOS to me. (IOS is a descendant of VMS.)
1280 __extension__ /* don't warn about "string length is greater than
1281 the length ISO C89 compilers are required to
1287 "\001nmap -v -sS -O 10.2.2.2\n"
1288 "Starting nmap V. 2.54BETA25\n"
1289 "\010\010\010\010\010\010\010\010\010\010"
1290 "Insufficient responses for TCP sequencing (3), OS detection"
1291 " may be less accurate\n"
1292 "Interesting ports on 10.2.2.2:\n"
1293 "(The 1539 ports scanned but not shown below are in state:"
1295 "Port state service\n"
1298 "No exact OS matches for host\n"
1300 "Nmap run completed -- 1 IP address (1 host up) scanned\n"
1304 "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n"
1305 "Connecting to 10.2.2.2:ssh ... "
1308 "Attempting to exploit SSHv1 CRC32 ... "
1311 "Resetting root password to \"Z1ON0101\".\n"
1313 "System open: Access Level <9>\n"
1318 "\001ssh 10.2.2.2 -l root\n"
1320 "root@10.2.2.2's password: "
1326 "\001disable grid nodes 21 - 48\n"
1328 "\002Warning: Disabling nodes 21-48 will disconnect sector 11"
1331 "\002 ARE YOU SURE? (y/n) "
1337 "\010\002Grid Node 21 offline...\n"
1338 "\010\002Grid Node 22 offline...\n"
1339 "\010\002Grid Node 23 offline...\n"
1340 "\010\002Grid Node 24 offline...\n"
1341 "\010\002Grid Node 25 offline...\n"
1342 "\010\002Grid Node 26 offline...\n"
1343 "\010\002Grid Node 27 offline...\n"
1344 "\010\002Grid Node 28 offline...\n"
1345 "\010\002Grid Node 29 offline...\n"
1346 "\010\002Grid Node 30 offline...\n"
1347 "\010\002Grid Node 31 offline...\n"
1348 "\010\002Grid Node 32 offline...\n"
1349 "\010\002Grid Node 33 offline...\n"
1350 "\010\002Grid Node 34 offline...\n"
1351 "\010\002Grid Node 35 offline...\n"
1352 "\010\002Grid Node 36 offline...\n"
1353 "\010\002Grid Node 37 offline...\n"
1354 "\010\002Grid Node 38 offline...\n"
1355 "\010\002Grid Node 39 offline...\n"
1356 "\010\002Grid Node 40 offline...\n"
1357 "\010\002Grid Node 41 offline...\n"
1358 "\010\002Grid Node 42 offline...\n"
1359 "\010\002Grid Node 43 offline...\n"
1360 "\010\002Grid Node 44 offline...\n"
1361 "\010\002Grid Node 45 offline...\n"
1362 "\010\002Grid Node 46 offline...\n"
1363 "\010\002Grid Node 47 offline...\n"
1364 "\010\002Grid Node 48 offline...\n"
1367 "\010\010\010\010\010\010\010\010"
1370 state->cursor_x = 0;
1371 state->cursor_y = state->grid_height - 3;
1372 state->typing_scroll_p = True;
1373 state->typing_char_delay = 0;
1374 state->typing_line_delay = 20000;
1383 state->typing_left_margin = state->cursor_x;
1384 state->typing_delay = state->typing_char_delay;
1385 if (state->typing_cursor_p)
1386 set_cursor (state, True);
1390 Bool scrolled_p = False;
1391 unsigned char c, c1;
1392 int x = state->cursor_x;
1393 int y = state->cursor_y;
1396 c = ((unsigned char *) state->typing)[0];
1397 c1 = ((unsigned char *) state->typing)[1];
1399 state->typing_delay = (!c || c1 == '\n'
1400 ? state->typing_line_delay
1401 : state->typing_char_delay);
1404 state->typing_delay = 0;
1409 if (state->typing_scroll_p &&
1411 x >= state->grid_width - 1))
1413 set_cursor (state, False);
1417 if (y >= state->grid_height-1)
1420 for (yy = 0; yy < state->grid_height-2; yy++)
1421 for (xx = 0; xx < state->grid_width; xx++)
1423 int ii = yy * state->grid_width + xx;
1424 int jj = (yy+1) * state->grid_width + xx;
1425 state->cells[ii] = state->cells[jj];
1426 state->cells[ii].changed = 1;
1428 /* clear bottom row */
1429 for (xx = 0; xx < state->grid_width; xx++)
1431 int ii = yy * state->grid_width + xx;
1432 state->cells[ii].glyph = 0;
1433 state->cells[ii].changed = 1;
1435 y--; /* move back up to bottom line */
1442 if (!state->typing_scroll_p)
1445 set_cursor (state, False);
1446 x = state->typing_left_margin;
1448 /* clear the line */
1449 i = state->grid_width * y;
1450 j = i + state->grid_width;
1453 state->cells[i].glyph = 0;
1454 state->cells[i].changed = 1;
1457 state->typing_bold_p = False;
1458 state->typing_stutter_p = False;
1462 else if (c == '\010')
1463 state->typing_delay += 500000;
1465 else if (c == '\001')
1467 state->typing_stutter_p = True;
1468 state->typing_bold_p = False;
1470 else if (c == '\002')
1471 state->typing_bold_p = True;
1473 else if (x < state->grid_width-1)
1475 m_cell *cell = &state->cells[state->grid_width * y + x];
1476 cell->glyph = char_map[c] + 1;
1477 if (c == ' ' || c == '\t') cell->glyph = 0;
1479 cell->glow = (state->typing_bold_p ? 127 : 0);
1485 if (x >= state->grid_width-1)
1486 x = state->grid_width-1;
1490 if (state->typing_stutter_p)
1492 if (state->typing_delay == 0)
1493 state->typing_delay = 20000;
1495 state->typing_delay += (0xFFFFFF & ((random() % 200000) + 1));
1498 /* If there's no delay after this character, just keep going. */
1499 if (state->typing_delay == 0)
1502 if (scrolled_p || x != state->cursor_x || y != state->cursor_y)
1504 set_cursor (state, False);
1505 state->cursor_x = x;
1506 state->cursor_y = y;
1507 if (state->typing_cursor_p)
1508 set_cursor (state, True);
1515 hack_matrix (m_state *state)
1519 switch (state->mode)
1521 case TRACE_DONE: case TRACE_FAIL:
1523 case TRACE_A: case TRACE_B:
1524 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1530 /* Glow some characters. */
1531 if (!state->insert_bottom_p)
1533 int i = random() % (state->grid_width / 2);
1536 int yy = random() % state->grid_height;
1537 int xx = random() % state->grid_width;
1538 m_cell *cell = &state->cells[state->grid_width * yy + xx];
1539 if (cell->glyph && cell->glow == 0)
1541 cell->glow = random() % 10;
1547 /* Change some of the feeders. */
1548 for (x = 0; x < state->grid_width; x++)
1550 m_feeder *f = &state->feeders[x];
1551 Bool bottom_feeder_p;
1553 if (f->remaining > 0) /* never change if pipe isn't empty */
1556 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
1559 f->remaining = 3 + (random() % state->grid_height);
1560 f->throttle = ((random() % 5) + (random() % 5));
1562 if ((random() % 4) != 0)
1565 if (state->mode == TRACE_A || state->mode == TRACE_B)
1566 bottom_feeder_p = True;
1567 if (state->insert_top_p && state->insert_bottom_p)
1568 bottom_feeder_p = (random() & 1);
1570 bottom_feeder_p = state->insert_bottom_p;
1572 if (bottom_feeder_p)
1573 f->y = random() % (state->grid_height / 2);
1578 if (state->mode == MATRIX && (! (random() % 500)))
1579 init_spinners (state);
1583 static unsigned long
1584 xmatrix_draw (Display *dpy, Window window, void *closure)
1586 m_state *state = (m_state *) closure;
1588 if (state->typing_delay > 0)
1590 state->typing_delay -= state->delay;
1591 if (state->typing_delay < 0)
1592 state->typing_delay = 0;
1593 redraw_cells (state, False);
1594 return state->delay;
1597 switch (state->mode)
1599 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1600 case TRACE_A: case TRACE_B:
1601 feed_matrix (state);
1602 hack_matrix (state);
1610 feed_matrix (state);
1611 if (screen_blank_p (state))
1613 state->typing_delay = 500000;
1614 if(state->start_reveal_back_p){
1617 state->typing_delay = 5000000;
1618 state->start_reveal_back_p = False;
1619 state->back_text_full_p = False;
1620 /* for loop to move background to foreground */
1621 for (y = 0; y < state->grid_height; y++){
1622 for (x = 0; x < state->grid_width; x++){
1623 to = &state->cells[state->grid_width * y + x];
1624 back = &state->background[state->grid_width * y + x];
1625 to->glyph = back->glyph;
1626 to->changed = back->changed;
1632 switch (state->mode)
1634 case DRAIN_TRACE_A: set_mode (state, TRACE_TEXT_A); break;
1635 case DRAIN_TRACE_B: set_mode (state, TRACE_TEXT_B); break;
1636 case DRAIN_KNOCK: set_mode (state, KNOCK); break;
1637 case DRAIN_NMAP: set_mode (state, NMAP); break;
1638 case DRAIN_MATRIX: set_mode (state, state->def_mode); break;
1639 default: abort(); break;
1645 set_mode (state, state->def_mode);
1655 if (! state->typing) /* done typing */
1657 set_cursor (state, False);
1658 switch (state->mode)
1660 case TRACE_TEXT_A: set_mode (state, TRACE_A); break;
1661 case TRACE_TEXT_B: set_mode (state, TRACE_B); break;
1662 case TRACE_FAIL: set_mode (state, state->def_mode); break;
1663 case KNOCK: set_mode (state, state->def_mode); break;
1664 case NMAP: set_mode (state, state->def_mode); break;
1665 default: abort(); break;
1673 if (state->start_reveal_back_p){
1674 set_mode (state, DRAIN_MATRIX);
1676 if (state->mode == MATRIX &&
1677 state->knock_knock_p &&
1678 (! (random() % 10000)))
1680 if (! (random() % 5))
1681 set_mode (state, DRAIN_NMAP);
1683 set_mode (state, DRAIN_KNOCK);
1686 redraw_cells (state, True);
1691 static int ndens = 0;
1692 static int tdens = 0;
1698 ((double) (state->grid_width * state->grid_height))));
1701 printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
1707 return state->delay;
1712 xmatrix_reshape (Display *dpy, Window window, void *closure,
1713 unsigned int w, unsigned int h)
1715 m_state *state = (m_state *) closure;
1716 int ow = state->grid_width;
1717 int oh = state->grid_height;
1718 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
1719 state->grid_width = state->xgwa.width / state->char_width;
1720 state->grid_height = state->xgwa.height / state->char_height;
1721 state->grid_width++;
1722 state->grid_height++;
1723 if (state->grid_width < 5) state->grid_width = 5;
1724 if (state->grid_height < 5) state->grid_height = 5;
1726 if (ow != state->grid_width ||
1727 oh != state->grid_height)
1729 m_cell *ncells = (m_cell *)
1730 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1731 m_cell *nbackground = (m_cell *)
1732 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1733 m_feeder *nfeeders = (m_feeder *)
1734 calloc (sizeof(m_feeder), state->grid_width);
1737 /* fprintf(stderr, "resize: %d x %d ==> %d x %d\n",
1738 ow, oh, state->grid_width, state->grid_height); */
1740 for (y = 0; y < oh; y++)
1741 for (x = 0; x < ow; x++)
1742 if (x < ow && x < state->grid_width &&
1743 y < oh && y < state->grid_height){
1744 ncells[y * state->grid_width + x] =
1745 state->cells[y * ow + x];
1746 nbackground[y * state->grid_width + x] =
1747 state->background[y * ow + x];
1749 free (state->cells);
1750 free (state->background);
1751 state->cells = ncells;
1752 state->background = nbackground;
1754 x = (ow < state->grid_width ? ow : state->grid_width);
1755 for (i = 0; i < x; i++)
1756 nfeeders[i] = state->feeders[i];
1757 free (state->feeders);
1758 state->feeders = nfeeders;
1763 xmatrix_event (Display *dpy, Window window, void *closure, XEvent *event)
1765 m_state *state = (m_state *) closure;
1767 if (event->xany.type == KeyPress)
1771 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1775 /*set_mode (state, DRAIN_MATRIX);*/
1777 state->back_text_full_p = True;
1778 state->start_reveal_back_p = True;
1781 case '+': case '=': case '>': case '.':
1782 state->density += 10;
1783 if (state->density > 100)
1784 state->density = 100;
1789 case '-': case '_': case '<': case ',':
1790 state->density -= 10;
1791 if (state->density < 0)
1797 case '[': case '(': case '{':
1798 state->insert_top_p = True;
1799 state->insert_bottom_p = False;
1802 case ']': case ')': case '}':
1803 state->insert_top_p = False;
1804 state->insert_bottom_p = True;
1807 case '\\': case '|':
1808 state->insert_top_p = True;
1809 state->insert_bottom_p = True;
1813 set_mode (state, DRAIN_TRACE_A);
1817 set_mode (state, DRAIN_TRACE_B);
1821 set_mode (state, DRAIN_KNOCK);
1825 set_mode (state, DRAIN_NMAP);
1837 xmatrix_free (Display *dpy, Window window, void *closure)
1839 m_state *state = (m_state *) closure;
1841 if (state->cursor_timer)
1842 XtRemoveTimeOut (state->cursor_timer);
1844 /* #### there's more to free here */
1849 static const char *xmatrix_defaults [] = {
1850 ".background: black",
1851 ".foreground: #00AA00",
1852 "*matrixFont: large",
1856 "*tracePhone: (312) 555-0690",
1860 "*knockKnock: True",
1862 "*program: xscreensaver-text",
1863 "*geometry: 800x600",
1867 static XrmOptionDescRec xmatrix_options [] = {
1868 { "-small", ".matrixFont", XrmoptionNoArg, "Small" },
1869 { "-large", ".matrixFont", XrmoptionNoArg, "Large" },
1870 { "-delay", ".delay", XrmoptionSepArg, 0 },
1871 { "-insert", ".insert", XrmoptionSepArg, 0 },
1872 { "-top", ".insert", XrmoptionNoArg, "top" },
1873 { "-bottom", ".insert", XrmoptionNoArg, "bottom" },
1874 { "-both", ".insert", XrmoptionNoArg, "both" },
1875 { "-density", ".density", XrmoptionSepArg, 0 },
1876 { "-trace", ".trace", XrmoptionNoArg, "True" },
1877 { "-no-trace", ".trace", XrmoptionNoArg, "False" },
1878 { "-crack", ".mode", XrmoptionNoArg, "crack"},
1879 { "-phone", ".tracePhone", XrmoptionSepArg, 0 },
1880 { "-mode", ".mode", XrmoptionSepArg, 0 },
1881 { "-dna", ".mode", XrmoptionNoArg, "DNA" },
1882 { "-binary", ".mode", XrmoptionNoArg, "binary" },
1883 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal"},
1884 { "-decimal", ".mode", XrmoptionNoArg, "decimal"},
1885 { "-knock-knock", ".knockKnock", XrmoptionNoArg, "True" },
1886 { "-no-knock-knock", ".knockKnock", XrmoptionNoArg, "False" },
1887 { "-ascii", ".mode", XrmoptionNoArg, "ascii"},
1888 { "-pipe", ".usePipe", XrmoptionNoArg, "True" },
1889 { "-no-pipe", ".usePipe", XrmoptionNoArg, "False" },
1890 { "-program", ".program", XrmoptionSepArg, 0 },
1894 XSCREENSAVER_MODULE ("XMatrix", xmatrix)