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];
506 clear_spinners (m_state *state)
509 for (i = 0; i < state->grid_width * state->grid_height; i++)
510 if (state->cells[i].spinner)
512 state->cells[i].spinner = 0;
513 state->cells[i].changed = 1;
518 static void set_mode (m_state *, m_mode);
522 init_trace (m_state *state)
524 char *s = get_string_resource (state->dpy, "tracePhone", "TracePhone");
530 state->tracing = (signed char *) malloc (strlen (s) + 1);
531 s3 = (char *) state->tracing;
533 for (s2 = s; *s2; s2++)
534 if (*s2 >= '0' && *s2 <= '9')
538 if (s3 == (char *) state->tracing)
541 for (i = 0; i < strlen((char *) state->tracing); i++)
542 state->tracing[i] = -state->tracing[i];
544 state->glyph_map = decimal_encoding;
545 state->nglyphs = countof(decimal_encoding);
550 fprintf (stderr, "%s: bad phone number: \"%s\".\n",
551 progname, s ? s : "(null)");
554 if (state->tracing) free (state->tracing);
556 set_mode (state, MATRIX);
561 init_drain (m_state *state)
565 set_cursor (state, False);
566 state->cursor_x = -1;
567 state->cursor_y = -1;
569 /* Fill the top row with empty top-feeders, to clear the screen. */
570 for (i = 0; i < state->grid_width; i++)
572 m_feeder *f = &state->feeders[i];
578 /* Turn off all the spinners, else they never go away. */
579 clear_spinners (state);
583 screen_blank_p (m_state *state)
586 for (i = 0; i < state->grid_width * state->grid_height; i++)
587 if (state->cells[i].glyph)
594 set_mode (m_state *state, m_mode mode)
596 if (mode == state->mode)
605 state->glyph_map = matrix_encoding;
606 state->nglyphs = countof(matrix_encoding);
607 flip_images (state, True);
608 init_spinners (state);
611 state->glyph_map = dna_encoding;
612 state->nglyphs = countof(dna_encoding);
613 flip_images (state, False);
616 state->glyph_map = binary_encoding;
617 state->nglyphs = countof(binary_encoding);
618 flip_images (state, False);
621 state->glyph_map = hex_encoding;
622 state->nglyphs = countof(hex_encoding);
623 flip_images (state, False);
626 state->glyph_map = ascii_encoding;
627 state->nglyphs = countof(ascii_encoding);
628 flip_images (state, False);
635 state->glyph_map = decimal_encoding;
636 state->nglyphs = countof(decimal_encoding);
637 flip_images (state, False);
641 flip_images (state, False);
661 xmatrix_init (Display *dpy, Window window)
666 m_state *state = (m_state *) calloc (sizeof(*state), 1);
669 state->window = window;
670 state->delay = get_integer_resource (dpy, "delay", "Integer");
672 XGetWindowAttributes (dpy, window, &state->xgwa);
674 state->small_p = (state->xgwa.width < 300);
676 const char *s = get_string_resource (dpy, "matrixFont", "String");
677 if (!s || !*s || !strcasecmp(s, "large"))
678 state->small_p = False;
679 else if (!strcasecmp(s, "small"))
680 state->small_p = True;
682 fprintf (stderr, "%s: matrixFont should be 'small' or 'large' not '%s'\n",
686 load_images (dpy, state);
688 gcv.foreground = get_pixel_resource(state->dpy, state->xgwa.colormap,
689 "foreground", "Foreground");
690 gcv.background = get_pixel_resource(state->dpy, state->xgwa.colormap,
691 "background", "Background");
692 state->draw_gc = XCreateGC (state->dpy, state->window,
693 GCForeground|GCBackground, &gcv);
694 gcv.foreground = gcv.background;
695 state->erase_gc = XCreateGC (state->dpy, state->window,
696 GCForeground|GCBackground, &gcv);
698 state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv);
700 /* Allocate colors for SYSTEM FAILURE box */
702 XColor boxcolors[] = {
703 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
704 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
705 { 0, 0xE0E0, 0xF7F7, 0xE0E0, DoRed|DoGreen|DoBlue, 0 },
706 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
707 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
709 for (i = 0; i < countof(boxcolors); i++)
711 if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i]))
712 state->colors[i] = boxcolors[i].pixel;
714 state->colors[i] = gcv.foreground; /* default black */
718 state->char_width = state->image_width / CHAR_COLS;
719 state->char_height = state->image_height / CHAR_ROWS;
721 state->grid_width = state->xgwa.width / state->char_width;
722 state->grid_height = state->xgwa.height / state->char_height;
724 state->grid_height++;
725 if (state->grid_width < 5) state->grid_width = 5;
726 if (state->grid_height < 5) state->grid_height = 5;
728 state->glyph_map = matrix_encoding;
729 state->nglyphs = countof(matrix_encoding);
731 state->cells = (m_cell *)
732 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
733 state->background = (m_cell *)
734 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
735 state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
737 state->density = get_integer_resource (dpy, "density", "Integer");
739 insert = get_string_resource(dpy, "insert", "Insert");
740 if (insert && !strcmp(insert, "top"))
742 state->insert_top_p = True;
743 state->insert_bottom_p = False;
745 else if (insert && !strcmp(insert, "bottom"))
747 state->insert_top_p = False;
748 state->insert_bottom_p = True;
750 else if (insert && !strcmp(insert, "both"))
752 state->insert_top_p = True;
753 state->insert_bottom_p = True;
757 if (insert && *insert)
759 "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
761 state->insert_top_p = False;
762 state->insert_bottom_p = True;
765 state->nspinners = get_integer_resource (dpy, "spinners", "Integer");
770 state->knock_knock_p = get_boolean_resource (dpy, "knockKnock", "KnockKnock");
772 state->use_pipe_p = get_boolean_resource (dpy, "usePipe", "Boolean");
774 state->buf[0] = ' '; /* spacer byte in buffer (space) */
776 state->do_fill_buff = True;
777 state->start_reveal_back_p = False;
778 state->back_text_full_p = False;
783 state->def_mode = MATRIX;
784 mode = get_string_resource (dpy, "mode", "Mode");
785 if (mode && !strcasecmp(mode, "trace"))
786 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
787 else if (mode && !strcasecmp(mode, "crack"))
788 set_mode (state, DRAIN_NMAP);
789 else if (mode && !strcasecmp(mode, "dna")){
790 set_mode (state, DNA);
791 state->def_mode = DNA;
793 else if (mode && (!strcasecmp(mode, "bin") ||
794 !strcasecmp(mode, "binary"))){
795 set_mode (state, BINARY);
796 state->def_mode = BINARY;
798 else if (mode && (!strcasecmp(mode, "hex") ||
799 !strcasecmp(mode, "hexadecimal"))){
800 set_mode (state, HEX);
801 state->def_mode = HEX;
803 else if (mode && (!strcasecmp(mode, "dec") ||
804 !strcasecmp(mode, "decimal"))){
805 set_mode (state, DEC);
806 state->def_mode = DEC;
808 else if (mode && (!strcasecmp(mode, "asc") ||
809 !strcasecmp(mode, "ascii"))){
810 set_mode (state, ASCII);
811 state->def_mode = ASCII;
813 else if (mode && !strcasecmp(mode, "pipe"))
815 set_mode (state, ASCII);
816 state->def_mode = ASCII;
817 state->use_pipe_p = True;
818 launch_text_generator (state);
820 else if (!mode || !*mode || !strcasecmp(mode, "matrix"))
821 set_mode (state, MATRIX);
824 fprintf (stderr, "%s: `mode' must be ",progname);
825 fprintf (stderr, "matrix, trace, dna, binary, ascii, hex, or pipe: ");
826 fprintf (stderr, "not `%s'\n", mode);
827 set_mode (state, MATRIX);
830 if (state->mode == MATRIX && get_boolean_resource (dpy, "trace", "Boolean"))
831 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
833 state->cursor_x = -1;
834 state->cursor_y = -1;
841 insert_glyph (m_state *state, int glyph, int x, int y)
843 Bool bottom_feeder_p = (y >= 0);
845 if (y >= state->grid_height)
850 to = &state->cells[state->grid_width * y + x];
854 for (y = state->grid_height-1; y > 0; y--)
856 from = &state->cells[state->grid_width * (y-1) + x];
857 to = &state->cells[state->grid_width * y + x];
858 to->glyph = from->glyph;
859 to->glow = from->glow;
862 to = &state->cells[x];
870 else if (bottom_feeder_p)
871 to->glow = 1 + (random() % (state->tracing ? 4 : 2));
878 place_back_char (m_state *state, char textc, int x, int y){
879 if((x>=0) && (y>=0) &&
880 (x < state->grid_width) && (y < state->grid_height)){
881 m_cell *celltmp = &state->background[state->grid_width * y + x];
882 celltmp -> glyph = char_map[(unsigned char)textc] + 1;
883 if(!celltmp->glyph || (celltmp->glyph == 3)){
884 celltmp -> glyph = char_map[32] + 1;
886 celltmp -> changed = 1;
891 place_back_text (m_state *state, char *text, int x, int y){
893 for(i=0; i<strlen(text); i++){
894 place_back_char(state, text[i], x+i, y);
899 place_back_pipe (m_state *state, char textc){
900 Bool new_line = False;
901 /* gringer pipe insert */
902 state->back_line[state->back_pos] = textc;
904 state->back_line[state->back_pos] = '\0';
907 else if ((state->back_pos > (state->grid_width - 4)) ||
908 (state->back_pos >= BUF_SIZE)){ /* off by 1? */
909 state->back_line[++state->back_pos] = '\0';
916 int startx = (state->grid_width >> 1) -
917 (strlen(state->back_line) >> 1);
918 place_back_text(state, state->back_line,
919 startx, state->back_y);
922 if(state->back_y >= (state->grid_height - 1)){
924 state->back_text_full_p = True;
925 state->start_reveal_back_p = True;
931 feed_matrix (m_state *state)
939 int L = strlen((char *) state->tracing);
943 for (i = 0; i < strlen((char *) state->tracing); i++)
944 if (state->tracing[i] > 0)
949 set_mode (state, TRACE_DONE);
950 state->typing_delay = 1000000;
955 i = 5 + (30 / (count+1)); /* how fast numbers are discovered */
957 if ((random() % i) == 0)
960 if (state->tracing[i] < 0)
961 state->tracing[i] = -state->tracing[i];
968 if ((random() % 40) == 0)
970 set_mode (state, TRACE_FAIL);
975 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
988 if((state->use_pipe_p) && (!state->back_text_full_p)){
989 place_back_pipe(state, state->buf[state->buf_done]);
990 state->buf_done = (state->buf_done + 1) % BUF_SIZE;
991 if(state->buf_done == (state->buf_pos - 1)){
992 state->do_fill_buff = True;
995 if(state->buf_done == (state->buf_pos + 1)){
996 state->do_fill_buff = False;
999 state->do_fill_buff = True;
1003 /* Update according to current feeders. */
1004 for (x = 0; x < state->grid_width; x++)
1006 m_feeder *f = &state->feeders[x];
1008 if (f->throttle) /* this is a delay tick, synced to frame. */
1012 else if (f->remaining > 0) /* how many items are in the pipe */
1016 if((state->use_pipe_p) && (!state->back_text_full_p)){
1017 rval = (int) state->buf[f->pipe_loc];
1018 if(++f->pipe_loc > (BUF_SIZE-1)){
1020 /*fill_input(state);*/
1022 rval = (rval % state->nglyphs);
1025 rval = (random() % state->nglyphs);
1027 g = state->glyph_map[rval] + 1;
1028 insert_glyph (state, g, x, f->y);
1030 if (f->y >= 0) /* bottom_feeder_p */
1033 else /* if pipe is empty, insert spaces */
1035 insert_glyph (state, 0, x, f->y);
1036 if (f->y >= 0) /* bottom_feeder_p */
1040 if ((random() % 10) == 0) /* randomly change throttle speed */
1042 f->throttle = ((random() % 5) + (random() % 5));
1049 redraw_cells (m_state *state, Bool active)
1053 Bool use_back_p = False;
1055 for (y = 0; y < state->grid_height; y++)
1056 for (x = 0; x < state->grid_width; x++)
1058 m_cell *cell = &state->cells[state->grid_width * y + x];
1059 m_cell *back = &state->background[state->grid_width * y + x];
1060 Bool cursor_p = (state->cursor_on &&
1061 x == state->cursor_x &&
1062 y == state->cursor_y);
1067 if((state->start_reveal_back_p) &&
1068 (back->glyph) && !(state->mode == TRACE_A ||
1069 state->mode == TRACE_B ||
1070 state->mode == TRACE_DONE)){
1076 /* In trace-mode, the state of each cell is random unless we have
1077 a match for this digit. */
1078 if (active && (state->mode == TRACE_A ||
1079 state->mode == TRACE_B ||
1080 state->mode == TRACE_DONE))
1082 int xx = x % strlen((char *) state->tracing);
1083 Bool dead_p = state->tracing[xx] > 0;
1085 if (y == 0 && x == xx && !use_back_p)
1086 cell->glyph = (dead_p
1087 ? state->glyph_map[state->tracing[xx]-'0'] + 1
1089 else if (y == 0 && !use_back_p)
1091 else if (!use_back_p)
1092 cell->glyph = (dead_p ? 0 :
1093 (state->glyph_map[(random()%state->nglyphs)]
1103 if (cell->glyph == 0 && !cursor_p && !use_back_p)
1104 XFillRectangle (state->dpy, state->window, state->erase_gc,
1105 x * state->char_width,
1106 y * state->char_height,
1108 state->char_height);
1111 int g = (cursor_p ? CURSOR_GLYPH : cell->glyph);
1112 int cx = (g - 1) % CHAR_COLS;
1113 int cy = (g - 1) / CHAR_COLS;
1114 int map = ((cell->glow != 0 || cell->spinner) ? GLOW_MAP :
1117 XCopyArea (state->dpy, state->images[map],
1118 state->window, state->draw_gc,
1119 cx * state->char_width,
1120 cy * state->char_height,
1123 x * state->char_width,
1124 y * state->char_height);
1129 if (cell->glow > 0 && state->mode != NMAP && !use_back_p)
1134 else if (cell->glow < 0)
1137 if (cell->spinner && active && !use_back_p)
1139 cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1);
1147 densitizer (m_state *state)
1149 /* Horrid kludge that converts percentages (density of screen coverage)
1150 to the parameter that actually controls this. I got this mapping
1151 empirically, on a 1024x768 screen. Sue me. */
1152 if (state->density < 10) return 85;
1153 else if (state->density < 15) return 60;
1154 else if (state->density < 20) return 45;
1155 else if (state->density < 25) return 25;
1156 else if (state->density < 30) return 20;
1157 else if (state->density < 35) return 15;
1158 else if (state->density < 45) return 10;
1159 else if (state->density < 50) return 8;
1160 else if (state->density < 55) return 7;
1161 else if (state->density < 65) return 5;
1162 else if (state->density < 80) return 3;
1163 else if (state->density < 90) return 2;
1169 hack_text (m_state *state)
1173 set_cursor (state, False);
1174 state->cursor_x = 0;
1175 state->cursor_y = 0;
1176 state->typing_scroll_p = False;
1177 state->typing_bold_p = False;
1178 state->typing_cursor_p = True;
1179 state->typing_stutter_p = False;
1180 state->typing_char_delay = 10000;
1181 state->typing_line_delay = 1500000;
1183 switch (state->mode)
1187 clear_spinners (state);
1188 if (state->mode == TRACE_TEXT_A)
1190 if (state->grid_width >= 52)
1192 ("Call trans opt: received. 2-19-98 13:24:18 REC:Log>\n"
1193 "Trace program: running\n");
1196 ("Call trans opt: received.\n2-19-98 13:24:18 REC:Log>\n"
1197 "Trace program: running\n");
1201 if (state->grid_width >= 52)
1203 ("Call trans opt: received. 9-18-99 14:32:21 REC:Log>\n"
1204 "WARNING: carrier anomaly\n"
1205 "Trace program: running\n");
1208 ("Call trans opt: received.\n9-18-99 14:32:21 REC:Log>\n"
1209 "WARNING: carrier anomaly\n"
1210 "Trace program: running\n");
1216 const char *s = "SYSTEM FAILURE\n";
1218 float cx = (state->grid_width - strlen(s) - 1) / 2 - 0.5;
1219 float cy = (state->grid_height / 2) - 1.3;
1224 XFillRectangle (state->dpy, state->window, state->erase_gc,
1225 cx * state->char_width,
1226 cy * state->char_height,
1227 strlen(s) * state->char_width,
1228 state->char_height * 1.6);
1230 for (i = -2; i < 3; i++)
1233 gcv.foreground = state->colors[i + 2];
1234 XChangeGC (state->dpy, state->scratch_gc, GCForeground, &gcv);
1235 XDrawRectangle (state->dpy, state->window, state->scratch_gc,
1236 cx * state->char_width - i,
1237 cy * state->char_height - i,
1238 strlen(s) * state->char_width + (2 * i),
1239 (state->char_height * 1.6) + (2 * i));
1242 /* If we don't clear these, part of the box may get overwritten */
1243 for (i = 0; i < state->grid_height * state->grid_width; i++)
1245 m_cell *cell = &state->cells[i];
1249 state->cursor_x = (state->grid_width - strlen(s) - 1) / 2;
1250 state->cursor_y = (state->grid_height / 2) - 1;
1251 if (state->cursor_x < 0) state->cursor_x = 0;
1252 if (state->cursor_y < 0) state->cursor_y = 0;
1255 state->typing_char_delay = 0;
1256 state->typing_cursor_p = False;
1262 clear_spinners (state);
1263 state->typing = ("\001Wake up, Neo...\n"
1264 "\001The Matrix has you...\n"
1265 "\001Follow the white rabbit.\n"
1267 "Knock, knock, Neo.\n");
1269 state->cursor_x = 4;
1270 state->cursor_y = 2;
1271 state->typing_char_delay = 0;
1272 state->typing_line_delay = 2000000;
1278 /* Note that what Trinity is using here is moderately accurate:
1279 She runs nmap (http://www.insecure.org/nmap/) then breaks in
1280 with a (hypothetical) program called "sshnuke" that exploits
1281 the (very real) SSHv1 CRC32 compensation attack detector bug
1282 (http://staff.washington.edu/dittrich/misc/ssh-analysis.txt).
1284 The command syntax of the power grid control software looks a
1285 lot like Cisco IOS to me. (IOS is a descendant of VMS.)
1288 clear_spinners (state);
1291 __extension__ /* don't warn about "string length is greater than
1292 the length ISO C89 compilers are required to
1298 "\001nmap -v -sS -O 10.2.2.2\n"
1299 "Starting nmap V. 2.54BETA25\n"
1300 "\010\010\010\010\010\010\010\010\010\010"
1301 "Insufficient responses for TCP sequencing (3), OS detection"
1302 " may be less accurate\n"
1303 "Interesting ports on 10.2.2.2:\n"
1304 "(The 1539 ports scanned but not shown below are in state:"
1306 "Port state service\n"
1309 "No exact OS matches for host\n"
1311 "Nmap run completed -- 1 IP address (1 host up) scanned\n"
1315 "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n"
1316 "Connecting to 10.2.2.2:ssh ... "
1319 "Attempting to exploit SSHv1 CRC32 ... "
1322 "Resetting root password to \"Z1ON0101\".\n"
1324 "System open: Access Level <9>\n"
1329 "\001ssh 10.2.2.2 -l root\n"
1331 "root@10.2.2.2's password: "
1337 "\001disable grid nodes 21 - 48\n"
1339 "\002Warning: Disabling nodes 21-48 will disconnect sector 11"
1342 "\002 ARE YOU SURE? (y/n) "
1348 "\010\002Grid Node 21 offline...\n"
1349 "\010\002Grid Node 22 offline...\n"
1350 "\010\002Grid Node 23 offline...\n"
1351 "\010\002Grid Node 24 offline...\n"
1352 "\010\002Grid Node 25 offline...\n"
1353 "\010\002Grid Node 26 offline...\n"
1354 "\010\002Grid Node 27 offline...\n"
1355 "\010\002Grid Node 28 offline...\n"
1356 "\010\002Grid Node 29 offline...\n"
1357 "\010\002Grid Node 30 offline...\n"
1358 "\010\002Grid Node 31 offline...\n"
1359 "\010\002Grid Node 32 offline...\n"
1360 "\010\002Grid Node 33 offline...\n"
1361 "\010\002Grid Node 34 offline...\n"
1362 "\010\002Grid Node 35 offline...\n"
1363 "\010\002Grid Node 36 offline...\n"
1364 "\010\002Grid Node 37 offline...\n"
1365 "\010\002Grid Node 38 offline...\n"
1366 "\010\002Grid Node 39 offline...\n"
1367 "\010\002Grid Node 40 offline...\n"
1368 "\010\002Grid Node 41 offline...\n"
1369 "\010\002Grid Node 42 offline...\n"
1370 "\010\002Grid Node 43 offline...\n"
1371 "\010\002Grid Node 44 offline...\n"
1372 "\010\002Grid Node 45 offline...\n"
1373 "\010\002Grid Node 46 offline...\n"
1374 "\010\002Grid Node 47 offline...\n"
1375 "\010\002Grid Node 48 offline...\n"
1378 "\010\010\010\010\010\010\010\010"
1381 state->cursor_x = 0;
1382 state->cursor_y = state->grid_height - 3;
1383 state->typing_scroll_p = True;
1384 state->typing_char_delay = 0;
1385 state->typing_line_delay = 20000;
1394 state->typing_left_margin = state->cursor_x;
1395 state->typing_delay = state->typing_char_delay;
1396 if (state->typing_cursor_p)
1397 set_cursor (state, True);
1401 Bool scrolled_p = False;
1402 unsigned char c, c1;
1403 int x = state->cursor_x;
1404 int y = state->cursor_y;
1407 c = ((unsigned char *) state->typing)[0];
1408 c1 = ((unsigned char *) state->typing)[1];
1410 state->typing_delay = (!c || c1 == '\n'
1411 ? state->typing_line_delay
1412 : state->typing_char_delay);
1415 state->typing_delay = 0;
1420 if (state->typing_scroll_p &&
1422 x >= state->grid_width - 1))
1424 set_cursor (state, False);
1428 if (y >= state->grid_height-1)
1431 for (yy = 0; yy < state->grid_height-2; yy++)
1432 for (xx = 0; xx < state->grid_width; xx++)
1434 int ii = yy * state->grid_width + xx;
1435 int jj = (yy+1) * state->grid_width + xx;
1436 state->cells[ii] = state->cells[jj];
1437 state->cells[ii].changed = 1;
1439 /* clear bottom row */
1440 for (xx = 0; xx < state->grid_width; xx++)
1442 int ii = yy * state->grid_width + xx;
1443 state->cells[ii].glyph = 0;
1444 state->cells[ii].changed = 1;
1446 y--; /* move back up to bottom line */
1453 if (!state->typing_scroll_p)
1456 set_cursor (state, False);
1457 x = state->typing_left_margin;
1459 /* clear the line */
1460 i = state->grid_width * y;
1461 j = i + state->grid_width;
1464 state->cells[i].glyph = 0;
1465 state->cells[i].changed = 1;
1468 state->typing_bold_p = False;
1469 state->typing_stutter_p = False;
1473 else if (c == '\010')
1474 state->typing_delay += 500000;
1476 else if (c == '\001')
1478 state->typing_stutter_p = True;
1479 state->typing_bold_p = False;
1481 else if (c == '\002')
1482 state->typing_bold_p = True;
1484 else if (x < state->grid_width-1)
1486 m_cell *cell = &state->cells[state->grid_width * y + x];
1487 cell->glyph = char_map[c] + 1;
1488 if (c == ' ' || c == '\t') cell->glyph = 0;
1490 cell->glow = (state->typing_bold_p ? 127 : 0);
1496 if (x >= state->grid_width-1)
1497 x = state->grid_width-1;
1501 if (state->typing_stutter_p)
1503 if (state->typing_delay == 0)
1504 state->typing_delay = 20000;
1506 state->typing_delay += (0xFFFFFF & ((random() % 200000) + 1));
1509 /* If there's no delay after this character, just keep going. */
1510 if (state->typing_delay == 0)
1513 if (scrolled_p || x != state->cursor_x || y != state->cursor_y)
1515 set_cursor (state, False);
1516 state->cursor_x = x;
1517 state->cursor_y = y;
1518 if (state->typing_cursor_p)
1519 set_cursor (state, True);
1526 hack_matrix (m_state *state)
1530 switch (state->mode)
1532 case TRACE_DONE: case TRACE_FAIL:
1534 case TRACE_A: case TRACE_B:
1535 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1541 /* Glow some characters. */
1542 if (!state->insert_bottom_p)
1544 int i = random() % (state->grid_width / 2);
1547 int yy = random() % state->grid_height;
1548 int xx = random() % state->grid_width;
1549 m_cell *cell = &state->cells[state->grid_width * yy + xx];
1550 if (cell->glyph && cell->glow == 0)
1552 cell->glow = random() % 10;
1558 /* Change some of the feeders. */
1559 for (x = 0; x < state->grid_width; x++)
1561 m_feeder *f = &state->feeders[x];
1562 Bool bottom_feeder_p;
1564 if (f->remaining > 0) /* never change if pipe isn't empty */
1567 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
1570 f->remaining = 3 + (random() % state->grid_height);
1571 f->throttle = ((random() % 5) + (random() % 5));
1573 if ((random() % 4) != 0)
1576 if (state->mode == TRACE_A || state->mode == TRACE_B)
1577 bottom_feeder_p = True;
1578 else if (state->insert_top_p && state->insert_bottom_p)
1579 bottom_feeder_p = (random() & 1);
1581 bottom_feeder_p = state->insert_bottom_p;
1583 if (bottom_feeder_p)
1584 f->y = random() % (state->grid_height / 2);
1589 if (state->mode == MATRIX && (! (random() % 500)))
1590 init_spinners (state);
1594 static unsigned long
1595 xmatrix_draw (Display *dpy, Window window, void *closure)
1597 m_state *state = (m_state *) closure;
1599 if (state->typing_delay > 0)
1601 state->typing_delay -= state->delay;
1602 if (state->typing_delay < 0)
1603 state->typing_delay = 0;
1604 redraw_cells (state, False);
1605 return state->delay;
1608 switch (state->mode)
1610 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1611 case TRACE_A: case TRACE_B:
1612 feed_matrix (state);
1613 hack_matrix (state);
1621 feed_matrix (state);
1622 if (screen_blank_p (state))
1624 state->typing_delay = 500000;
1625 if(state->start_reveal_back_p){
1628 state->typing_delay = 5000000;
1629 state->start_reveal_back_p = False;
1630 state->back_text_full_p = False;
1631 /* for loop to move background to foreground */
1632 for (y = 0; y < state->grid_height; y++){
1633 for (x = 0; x < state->grid_width; x++){
1634 to = &state->cells[state->grid_width * y + x];
1635 back = &state->background[state->grid_width * y + x];
1636 to->glyph = back->glyph;
1637 to->changed = back->changed;
1643 switch (state->mode)
1645 case DRAIN_TRACE_A: set_mode (state, TRACE_TEXT_A); break;
1646 case DRAIN_TRACE_B: set_mode (state, TRACE_TEXT_B); break;
1647 case DRAIN_KNOCK: set_mode (state, KNOCK); break;
1648 case DRAIN_NMAP: set_mode (state, NMAP); break;
1649 case DRAIN_MATRIX: set_mode (state, state->def_mode); break;
1650 default: abort(); break;
1656 set_mode (state, state->def_mode);
1666 if (! state->typing) /* done typing */
1668 set_cursor (state, False);
1669 switch (state->mode)
1671 case TRACE_TEXT_A: set_mode (state, TRACE_A); break;
1672 case TRACE_TEXT_B: set_mode (state, TRACE_B); break;
1673 case TRACE_FAIL: set_mode (state, state->def_mode); break;
1674 case KNOCK: set_mode (state, state->def_mode); break;
1675 case NMAP: set_mode (state, state->def_mode); break;
1676 default: abort(); break;
1684 if (state->start_reveal_back_p){
1685 set_mode (state, DRAIN_MATRIX);
1687 if (state->mode == MATRIX &&
1688 state->knock_knock_p &&
1689 (! (random() % 10000)))
1691 if (! (random() % 5))
1692 set_mode (state, DRAIN_NMAP);
1694 set_mode (state, DRAIN_KNOCK);
1697 redraw_cells (state, True);
1702 static int ndens = 0;
1703 static int tdens = 0;
1709 ((double) (state->grid_width * state->grid_height))));
1712 printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
1718 return state->delay;
1723 xmatrix_reshape (Display *dpy, Window window, void *closure,
1724 unsigned int w, unsigned int h)
1726 m_state *state = (m_state *) closure;
1727 int ow = state->grid_width;
1728 int oh = state->grid_height;
1729 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
1730 state->grid_width = state->xgwa.width / state->char_width;
1731 state->grid_height = state->xgwa.height / state->char_height;
1732 state->grid_width++;
1733 state->grid_height++;
1734 if (state->grid_width < 5) state->grid_width = 5;
1735 if (state->grid_height < 5) state->grid_height = 5;
1737 if (ow != state->grid_width ||
1738 oh != state->grid_height)
1740 m_cell *ncells = (m_cell *)
1741 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1742 m_cell *nbackground = (m_cell *)
1743 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1744 m_feeder *nfeeders = (m_feeder *)
1745 calloc (sizeof(m_feeder), state->grid_width);
1748 /* fprintf(stderr, "resize: %d x %d ==> %d x %d\n",
1749 ow, oh, state->grid_width, state->grid_height); */
1751 for (y = 0; y < oh; y++)
1752 for (x = 0; x < ow; x++)
1753 if (x < ow && x < state->grid_width &&
1754 y < oh && y < state->grid_height){
1755 ncells[y * state->grid_width + x] =
1756 state->cells[y * ow + x];
1757 nbackground[y * state->grid_width + x] =
1758 state->background[y * ow + x];
1760 free (state->cells);
1761 free (state->background);
1762 state->cells = ncells;
1763 state->background = nbackground;
1765 x = (ow < state->grid_width ? ow : state->grid_width);
1766 for (i = 0; i < x; i++)
1767 nfeeders[i] = state->feeders[i];
1768 free (state->feeders);
1769 state->feeders = nfeeders;
1774 xmatrix_event (Display *dpy, Window window, void *closure, XEvent *event)
1776 m_state *state = (m_state *) closure;
1778 if (event->xany.type == KeyPress)
1782 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1786 /*set_mode (state, DRAIN_MATRIX);*/
1788 state->back_text_full_p = True;
1789 state->start_reveal_back_p = True;
1792 case '+': case '=': case '>': case '.':
1793 state->density += 10;
1794 if (state->density > 100)
1795 state->density = 100;
1800 case '-': case '_': case '<': case ',':
1801 state->density -= 10;
1802 if (state->density < 0)
1808 case '[': case '(': case '{':
1809 state->insert_top_p = True;
1810 state->insert_bottom_p = False;
1813 case ']': case ')': case '}':
1814 state->insert_top_p = False;
1815 state->insert_bottom_p = True;
1818 case '\\': case '|':
1819 state->insert_top_p = True;
1820 state->insert_bottom_p = True;
1824 set_mode (state, DRAIN_TRACE_A);
1828 set_mode (state, DRAIN_TRACE_B);
1832 set_mode (state, DRAIN_KNOCK);
1836 set_mode (state, DRAIN_NMAP);
1848 xmatrix_free (Display *dpy, Window window, void *closure)
1850 m_state *state = (m_state *) closure;
1852 if (state->cursor_timer)
1853 XtRemoveTimeOut (state->cursor_timer);
1855 /* #### there's more to free here */
1860 static const char *xmatrix_defaults [] = {
1861 ".background: black",
1862 ".foreground: #00AA00",
1864 "*matrixFont: large",
1868 "*tracePhone: (312) 555-0690",
1872 "*knockKnock: True",
1874 "*program: xscreensaver-text",
1875 "*geometry: 800x600",
1879 static XrmOptionDescRec xmatrix_options [] = {
1880 { "-small", ".matrixFont", XrmoptionNoArg, "Small" },
1881 { "-large", ".matrixFont", XrmoptionNoArg, "Large" },
1882 { "-delay", ".delay", XrmoptionSepArg, 0 },
1883 { "-insert", ".insert", XrmoptionSepArg, 0 },
1884 { "-top", ".insert", XrmoptionNoArg, "top" },
1885 { "-bottom", ".insert", XrmoptionNoArg, "bottom" },
1886 { "-both", ".insert", XrmoptionNoArg, "both" },
1887 { "-density", ".density", XrmoptionSepArg, 0 },
1888 { "-trace", ".trace", XrmoptionNoArg, "True" },
1889 { "-no-trace", ".trace", XrmoptionNoArg, "False" },
1890 { "-crack", ".mode", XrmoptionNoArg, "crack"},
1891 { "-phone", ".tracePhone", XrmoptionSepArg, 0 },
1892 { "-mode", ".mode", XrmoptionSepArg, 0 },
1893 { "-dna", ".mode", XrmoptionNoArg, "DNA" },
1894 { "-binary", ".mode", XrmoptionNoArg, "binary" },
1895 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal"},
1896 { "-decimal", ".mode", XrmoptionNoArg, "decimal"},
1897 { "-knock-knock", ".knockKnock", XrmoptionNoArg, "True" },
1898 { "-no-knock-knock", ".knockKnock", XrmoptionNoArg, "False" },
1899 { "-ascii", ".mode", XrmoptionNoArg, "ascii"},
1900 { "-pipe", ".usePipe", XrmoptionNoArg, "True" },
1901 { "-no-pipe", ".usePipe", XrmoptionNoArg, "False" },
1902 { "-program", ".program", XrmoptionSepArg, 0 },
1906 XSCREENSAVER_MODULE ("XMatrix", xmatrix)