1 /* xscreensaver, Copyright (c) 1999-2017 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. "GLMatrix" does that.
48 * ==========================================================
56 #include "screenhack.h"
57 #include "textclient.h"
58 #include "xpm-pixmap.h"
65 # define DO_XBM /* only do mono bitmaps under real X11 */
69 # include <X11/Intrinsic.h>
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"
80 # include "images/matrix1.xbm"
81 # include "images/matrix2.xbm"
82 # include "images/matrix1b.xbm"
83 # include "images/matrix2b.xbm"
94 unsigned int glyph : 9; /* note: 9 bit characters! */
95 unsigned int changed : 1;
96 unsigned int spinner : 1;
106 #define countof(x) (sizeof(x)/sizeof(*(x)))
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
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 */
147 #define CURSOR_GLYPH 97
149 /* larger numbers should mean more variability between columns */
152 typedef enum { DRAIN_TRACE_A,
153 TRACE_TEXT_A, /* Call trans opt: received. */
154 TRACE_A, /* (31_) 5__-0_9_ */
158 TRACE_TEXT_B, /* Call trans opt: received. */
160 TRACE_FAIL, /* System Failure */
163 KNOCK, /* Wake up, Neo... */
166 NMAP, /* Starting nmap V. 2.54BETA25 */
179 XWindowAttributes xgwa;
180 GC draw_gc, erase_gc, scratch_gc;
181 int grid_width, grid_height;
182 int char_width, char_height;
189 Bool insert_top_p, insert_bottom_p;
192 m_mode def_mode; /* Mode to return to after trace etc. */
195 char buf [BUF_SIZE*2+1]; /* ring buffer */
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 */
205 signed char *tracing;
209 Bool typing_scroll_p;
210 Bool typing_cursor_p;
212 Bool typing_stutter_p;
213 int typing_left_margin;
214 int typing_char_delay;
215 int typing_line_delay;
219 int cursor_x, cursor_y;
220 XtIntervalId cursor_timer;
222 Pixmap images[CHAR_MAPS];
223 int image_width, image_height;
224 Bool images_flipped_p;
227 const int *glyph_map;
229 unsigned long colors[5];
235 load_images_1 (Display *dpy, m_state *state, int which)
237 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
238 if (!get_boolean_resource (dpy, "mono", "Boolean") &&
239 state->xgwa.depth > 1)
242 (which == 1 ? (state->small_p ? matrix1b_xpm : matrix1_xpm) :
243 (state->small_p ? matrix2b_xpm : matrix2_xpm));
245 state->images[which] =
246 xpm_data_to_pixmap (state->dpy, state->window, bits,
247 &state->image_width, &state->image_height, 0);
250 #endif /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
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);
274 load_images (Display *dpy, m_state *state)
276 load_images_1 (dpy, state, 1);
277 load_images_1 (dpy, state, 2);
282 flip_images_1 (m_state *state, int which)
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));
288 int ww = state->char_width;
289 unsigned long *row = (unsigned long *) malloc (sizeof(*row) * ww);
291 for (y = 0; y < state->image_height; y++)
293 for (x = 0; x < CHAR_COLS; x++)
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]);
302 XPutImage (state->dpy, state->images[which], state->draw_gc, im, 0, 0, 0, 0,
303 state->image_width, state->image_height);
309 flip_images (m_state *state, Bool flipped_p)
311 if (flipped_p != state->images_flipped_p)
313 state->images_flipped_p = flipped_p;
314 flip_images_1 (state, 1);
315 flip_images_1 (state, 2);
320 /* When the subprocess has generated some output, this reads as much as it
321 can into s->buf at s->buf_tail.
324 fill_input (m_state *s)
328 if(s->buf_done > s->buf_pos){
329 loadBytes = (s->buf_done - s->buf_pos) - 1;
332 loadBytes = ((BUF_SIZE - s->buf_pos) + s->buf_done) - 1;
340 int c = textclient_getc (s->tc);
341 n = (c > 0 ? 1 : -1);
342 s->buf [s->buf_pos] = (char) c;
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);
355 s->buf_pos = s->buf_pos % BUF_SIZE;
359 /* Couldn't read anything from the buffer */
360 /* Assume EOF has been reached, so start again */
361 s->do_fill_buff = True;
366 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
367 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
370 set_cursor_1 (m_state *state, Bool on)
372 Bool changed = (state->cursor_on != on);
373 state->cursor_on = on;
375 if (changed && state->cursor_x >= 0 && state->cursor_y >= 0)
377 m_cell *cell = &state->cells[state->grid_width * state->cursor_y
380 cell->changed = True;
387 set_cursor (m_state *state, Bool on)
389 if (set_cursor_1 (state, on))
391 if (state->cursor_timer)
392 XtRemoveTimeOut (state->cursor_timer);
393 state->cursor_timer = 0;
395 cursor_on_timer (state, 0);
400 cursor_off_timer (XtPointer closure, XtIntervalId *id)
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);
410 cursor_on_timer (XtPointer closure, XtIntervalId *id)
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);
421 init_spinners (m_state *state)
423 int i = state->nspinners;
427 for (y = 0; y < state->grid_height; y++)
428 for (x = 0; x < state->grid_width; x++)
430 cell = &state->cells[state->grid_width * y + x];
436 x = random() % state->grid_width;
437 y = random() % state->grid_height;
438 cell = &state->cells[state->grid_width * y + x];
445 clear_spinners (m_state *state)
448 for (i = 0; i < state->grid_width * state->grid_height; i++)
449 if (state->cells[i].spinner)
451 state->cells[i].spinner = 0;
452 state->cells[i].changed = 1;
457 static void set_mode (m_state *, m_mode);
461 init_trace (m_state *state)
463 char *s = get_string_resource (state->dpy, "tracePhone", "TracePhone");
469 state->tracing = (signed char *) malloc (strlen (s) + 1);
472 for (s2 = s; *s2; s2++)
473 if (*s2 >= '0' && *s2 <= '9')
477 if (s3 == state->tracing)
480 state->glyph_map = decimal_encoding;
481 state->nglyphs = countof(decimal_encoding);
486 fprintf (stderr, "%s: bad phone number: \"%s\".\n",
487 progname, s ? s : "(null)");
490 if (state->tracing) free (state->tracing);
492 set_mode (state, MATRIX);
497 init_drain (m_state *state)
501 set_cursor (state, False);
502 state->cursor_x = -1;
503 state->cursor_y = -1;
505 /* Fill the top row with empty top-feeders, to clear the screen. */
506 for (i = 0; i < state->grid_width; i++)
508 m_feeder *f = &state->feeders[i];
514 /* Turn off all the spinners, else they never go away. */
515 clear_spinners (state);
519 screen_blank_p (m_state *state)
522 for (i = 0; i < state->grid_width * state->grid_height; i++)
523 if (state->cells[i].glyph)
530 set_mode (m_state *state, m_mode mode)
532 if (mode == state->mode)
541 state->glyph_map = matrix_encoding;
542 state->nglyphs = countof(matrix_encoding);
543 flip_images (state, True);
544 init_spinners (state);
547 state->glyph_map = dna_encoding;
548 state->nglyphs = countof(dna_encoding);
549 flip_images (state, False);
552 state->glyph_map = binary_encoding;
553 state->nglyphs = countof(binary_encoding);
554 flip_images (state, False);
557 state->glyph_map = hex_encoding;
558 state->nglyphs = countof(hex_encoding);
559 flip_images (state, False);
562 state->glyph_map = ascii_encoding;
563 state->nglyphs = countof(ascii_encoding);
564 flip_images (state, False);
571 state->glyph_map = decimal_encoding;
572 state->nglyphs = countof(decimal_encoding);
573 flip_images (state, False);
577 flip_images (state, False);
597 xmatrix_init (Display *dpy, Window window)
602 m_state *state = (m_state *) calloc (sizeof(*state), 1);
605 state->window = window;
606 state->delay = get_integer_resource (dpy, "delay", "Integer");
608 XGetWindowAttributes (dpy, window, &state->xgwa);
610 state->small_p = (state->xgwa.width < 300);
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;
618 fprintf (stderr, "%s: matrixFont should be 'small' or 'large' not '%s'\n",
622 load_images (dpy, state);
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);
634 state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv);
636 /* Allocate colors for SYSTEM FAILURE box */
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 },
645 for (i = 0; i < countof(boxcolors); i++)
647 if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i]))
648 state->colors[i] = boxcolors[i].pixel;
650 state->colors[i] = gcv.foreground; /* default black */
654 state->char_width = state->image_width / CHAR_COLS;
655 state->char_height = state->image_height / CHAR_ROWS;
657 state->grid_width = state->xgwa.width / state->char_width;
658 state->grid_height = state->xgwa.height / state->char_height;
660 state->grid_height++;
661 if (state->grid_width < 5) state->grid_width = 5;
662 if (state->grid_height < 5) state->grid_height = 5;
664 state->glyph_map = matrix_encoding;
665 state->nglyphs = countof(matrix_encoding);
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);
673 state->density = get_integer_resource (dpy, "density", "Integer");
675 insert = get_string_resource(dpy, "insert", "Insert");
676 if (insert && !strcmp(insert, "top"))
678 state->insert_top_p = True;
679 state->insert_bottom_p = False;
681 else if (insert && !strcmp(insert, "bottom"))
683 state->insert_top_p = False;
684 state->insert_bottom_p = True;
686 else if (insert && !strcmp(insert, "both"))
688 state->insert_top_p = True;
689 state->insert_bottom_p = True;
693 if (insert && *insert)
695 "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
697 state->insert_top_p = False;
698 state->insert_bottom_p = True;
701 state->nspinners = get_integer_resource (dpy, "spinners", "Integer");
706 state->knock_knock_p = get_boolean_resource (dpy, "knockKnock", "KnockKnock");
708 state->use_pipe_p = get_boolean_resource (dpy, "usePipe", "Boolean");
710 state->buf[0] = ' '; /* spacer byte in buffer (space) */
712 state->do_fill_buff = True;
713 state->start_reveal_back_p = False;
714 state->back_text_full_p = False;
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;
729 else if (mode && (!strcasecmp(mode, "bin") ||
730 !strcasecmp(mode, "binary"))){
731 set_mode (state, BINARY);
732 state->def_mode = BINARY;
734 else if (mode && (!strcasecmp(mode, "hex") ||
735 !strcasecmp(mode, "hexadecimal"))){
736 set_mode (state, HEX);
737 state->def_mode = HEX;
739 else if (mode && (!strcasecmp(mode, "dec") ||
740 !strcasecmp(mode, "decimal"))){
741 set_mode (state, DEC);
742 state->def_mode = DEC;
744 else if (mode && (!strcasecmp(mode, "asc") ||
745 !strcasecmp(mode, "ascii"))){
746 set_mode (state, ASCII);
747 state->def_mode = ASCII;
749 else if (mode && !strcasecmp(mode, "pipe"))
751 set_mode (state, ASCII);
752 state->def_mode = ASCII;
753 state->use_pipe_p = True;
754 state->tc = textclient_open (dpy);
756 else if (!mode || !*mode || !strcasecmp(mode, "matrix"))
757 set_mode (state, MATRIX);
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);
766 if (state->mode == MATRIX && get_boolean_resource (dpy, "trace", "Boolean"))
767 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
769 state->cursor_x = -1;
770 state->cursor_y = -1;
777 insert_glyph (m_state *state, int glyph, int x, int y)
779 Bool bottom_feeder_p = (y >= 0);
781 if (y >= state->grid_height)
786 to = &state->cells[state->grid_width * y + x];
790 for (y = state->grid_height-1; y > 0; y--)
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;
798 to = &state->cells[x];
806 else if (bottom_feeder_p)
807 to->glow = 1 + (random() % (state->tracing ? 4 : 2));
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;
822 celltmp -> changed = 1;
827 place_back_text (m_state *state, char *text, int x, int y){
829 for(i=0; i<strlen(text); i++){
830 place_back_char(state, text[i], x+i, y);
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;
840 state->back_line[state->back_pos] = '\0';
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';
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);
858 if(state->back_y >= (state->grid_height - 1)){
860 state->back_text_full_p = True;
861 state->start_reveal_back_p = True;
867 feed_matrix (m_state *state)
875 int L = strlen((char *) state->tracing);
879 for (i = 0; i < strlen((char *) state->tracing); i++)
880 if (state->tracing[i] > 0)
885 set_mode (state, TRACE_DONE);
886 state->typing_delay = 1000000;
891 i = 5 + (30 / (count+1)); /* how fast numbers are discovered */
893 if ((random() % i) == 0)
896 if (state->tracing[i] < 0)
897 state->tracing[i] = -state->tracing[i];
904 if ((random() % 40) == 0)
906 set_mode (state, TRACE_FAIL);
911 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
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;
931 if(state->buf_done == (state->buf_pos + 1)){
932 state->do_fill_buff = False;
935 state->do_fill_buff = True;
939 /* Update according to current feeders. */
940 for (x = 0; x < state->grid_width; x++)
942 m_feeder *f = &state->feeders[x];
944 if (f->throttle) /* this is a delay tick, synced to frame. */
948 else if (f->remaining > 0) /* how many items are in the pipe */
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)){
956 /*fill_input(state);*/
958 rval = (rval % state->nglyphs);
961 rval = (random() % state->nglyphs);
963 g = state->glyph_map[rval] + 1;
964 insert_glyph (state, g, x, f->y);
966 if (f->y >= 0) /* bottom_feeder_p */
969 else /* if pipe is empty, insert spaces */
971 insert_glyph (state, 0, x, f->y);
972 if (f->y >= 0) /* bottom_feeder_p */
976 if ((random() % 10) == 0) /* randomly change throttle speed */
978 f->throttle = ((random() % 5) + (random() % 5));
985 redraw_cells (m_state *state, Bool active)
989 Bool use_back_p = False;
991 for (y = 0; y < state->grid_height; y++)
992 for (x = 0; x < state->grid_width; x++)
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);
1003 if((state->start_reveal_back_p) &&
1004 (back->glyph) && !(state->mode == TRACE_A ||
1005 state->mode == TRACE_B ||
1006 state->mode == TRACE_DONE)){
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))
1018 int xx = x % strlen((char *) state->tracing);
1019 Bool dead_p = state->tracing[xx] > 0;
1021 if (y == 0 && x == xx && !use_back_p)
1022 cell->glyph = (dead_p
1023 ? state->glyph_map[state->tracing[xx]-'0'] + 1
1025 else if (y == 0 && !use_back_p)
1027 else if (!use_back_p)
1028 cell->glyph = (dead_p ? 0 :
1029 (state->glyph_map[(random()%state->nglyphs)]
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,
1044 state->char_height);
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 :
1053 XCopyArea (state->dpy, state->images[map],
1054 state->window, state->draw_gc,
1055 cx * state->char_width,
1056 cy * state->char_height,
1059 x * state->char_width,
1060 y * state->char_height);
1065 if (cell->glow > 0 && state->mode != NMAP && !use_back_p)
1070 else if (cell->glow < 0)
1073 if (cell->spinner && active && !use_back_p)
1075 cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1);
1083 densitizer (m_state *state)
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;
1105 hack_text (m_state *state)
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;
1119 switch (state->mode)
1123 clear_spinners (state);
1124 if (state->mode == TRACE_TEXT_A)
1126 if (state->grid_width >= 52)
1128 ("Call trans opt: received. 2-19-98 13:24:18 REC:Log>\n"
1129 "Trace program: running\n");
1132 ("Call trans opt: received.\n2-19-98 13:24:18 REC:Log>\n"
1133 "Trace program: running\n");
1137 if (state->grid_width >= 52)
1139 ("Call trans opt: received. 9-18-99 14:32:21 REC:Log>\n"
1140 "WARNING: carrier anomaly\n"
1141 "Trace program: running\n");
1144 ("Call trans opt: received.\n9-18-99 14:32:21 REC:Log>\n"
1145 "WARNING: carrier anomaly\n"
1146 "Trace program: running\n");
1152 const char *s = "SYSTEM FAILURE\n";
1154 float cx = (state->grid_width - strlen(s) - 1) / 2 - 0.5;
1155 float cy = (state->grid_height / 2) - 1.3;
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);
1166 for (i = -2; i < 3; i++)
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));
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++)
1181 m_cell *cell = &state->cells[i];
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;
1191 state->typing_char_delay = 0;
1192 state->typing_cursor_p = False;
1198 clear_spinners (state);
1199 state->typing = ("\001Wake up, Neo...\n"
1200 "\001The Matrix has you...\n"
1201 "\001Follow the white rabbit.\n"
1203 "Knock, knock, Neo.\n");
1205 state->cursor_x = 4;
1206 state->cursor_y = 2;
1207 state->typing_char_delay = 0;
1208 state->typing_line_delay = 2000000;
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).
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.)
1224 clear_spinners (state);
1227 __extension__ /* don't warn about "string length is greater than
1228 the length ISO C89 compilers are required to
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:"
1242 "Port state service\n"
1245 "No exact OS matches for host\n"
1247 "Nmap run completed -- 1 IP address (1 host up) scanned\n"
1251 "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n"
1252 "Connecting to 10.2.2.2:ssh ... "
1255 "Attempting to exploit SSHv1 CRC32 ... "
1258 "Resetting root password to \"Z1ON0101\".\n"
1260 "System open: Access Level <9>\n"
1265 "\001ssh 10.2.2.2 -l root\n"
1267 "root@10.2.2.2's password: "
1273 "\001disable grid nodes 21 - 48\n"
1275 "\002Warning: Disabling nodes 21-48 will disconnect sector 11"
1278 "\002 ARE YOU SURE? (y/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"
1314 "\010\010\010\010\010\010\010\010"
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;
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);
1336 /* Stupid iPhone X bezel.
1337 #### This is the worst of all possible ways to do this! But how else?
1339 if (state->xgwa.width == 2436 || state->xgwa.height == 2436)
1340 switch (state->mode)
1347 int off = 5 * (state->small_p ? 2 : 1);
1348 if (state->xgwa.width > state->xgwa.height)
1350 state->typing_left_margin += off;
1351 state->cursor_x += off;
1355 state->cursor_y += off;
1364 Bool scrolled_p = False;
1365 unsigned char c, c1;
1366 int x = state->cursor_x;
1367 int y = state->cursor_y;
1370 c = ((unsigned char *) state->typing)[0];
1371 c1 = c ? ((unsigned char *) state->typing)[1] : 0;
1373 state->typing_delay = (!c || c1 == '\n'
1374 ? state->typing_line_delay
1375 : state->typing_char_delay);
1378 state->typing_delay = 0;
1383 if (state->typing_scroll_p &&
1385 x >= state->grid_width - 1))
1387 set_cursor (state, False);
1391 if (y >= state->grid_height-1)
1394 for (yy = 0; yy < state->grid_height-2; yy++)
1395 for (xx = 0; xx < state->grid_width; xx++)
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;
1402 /* clear bottom row */
1403 for (xx = 0; xx < state->grid_width; xx++)
1405 int ii = yy * state->grid_width + xx;
1406 state->cells[ii].glyph = 0;
1407 state->cells[ii].changed = 1;
1409 y--; /* move back up to bottom line */
1416 if (!state->typing_scroll_p)
1419 set_cursor (state, False);
1420 x = state->typing_left_margin;
1422 /* clear the line */
1423 i = state->grid_width * y;
1424 j = i + state->grid_width;
1427 state->cells[i].glyph = 0;
1428 state->cells[i].changed = 1;
1431 state->typing_bold_p = False;
1432 state->typing_stutter_p = False;
1436 else if (c == '\010')
1437 state->typing_delay += 500000;
1439 else if (c == '\001')
1441 state->typing_stutter_p = True;
1442 state->typing_bold_p = False;
1444 else if (c == '\002')
1445 state->typing_bold_p = True;
1447 else if (x < state->grid_width-1)
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;
1453 cell->glow = (state->typing_bold_p ? 127 : 0);
1459 if (x >= state->grid_width-1)
1460 x = state->grid_width-1;
1464 if (state->typing_stutter_p)
1466 if (state->typing_delay == 0)
1467 state->typing_delay = 20000;
1469 state->typing_delay += (0xFFFFFF & ((random() % 200000) + 1));
1472 /* If there's no delay after this character, just keep going. */
1473 if (state->typing_delay == 0)
1476 if (scrolled_p || x != state->cursor_x || y != state->cursor_y)
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);
1489 hack_matrix (m_state *state)
1493 switch (state->mode)
1495 case TRACE_DONE: case TRACE_FAIL:
1497 case TRACE_A: case TRACE_B:
1498 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1504 /* Glow some characters. */
1505 if (!state->insert_bottom_p)
1507 int i = random() % (state->grid_width / 2);
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)
1515 cell->glow = random() % 10;
1521 /* Change some of the feeders. */
1522 for (x = 0; x < state->grid_width; x++)
1524 m_feeder *f = &state->feeders[x];
1525 Bool bottom_feeder_p;
1527 if (f->remaining > 0) /* never change if pipe isn't empty */
1530 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
1533 f->remaining = 3 + (random() % state->grid_height);
1534 f->throttle = ((random() % 5) + (random() % 5));
1536 if ((random() % 4) != 0)
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);
1544 bottom_feeder_p = state->insert_bottom_p;
1546 if (bottom_feeder_p)
1547 f->y = random() % (state->grid_height / 2);
1552 if (state->mode == MATRIX && (! (random() % 500)))
1553 init_spinners (state);
1557 static unsigned long
1558 xmatrix_draw (Display *dpy, Window window, void *closure)
1560 m_state *state = (m_state *) closure;
1562 if (state->typing_delay > 0)
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;
1571 switch (state->mode)
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);
1584 feed_matrix (state);
1585 if (screen_blank_p (state))
1587 state->typing_delay = 500000;
1588 if(state->start_reveal_back_p){
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;
1606 switch (state->mode)
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;
1619 set_mode (state, state->def_mode);
1629 if (! state->typing) /* done typing */
1631 set_cursor (state, False);
1632 switch (state->mode)
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;
1647 if (state->start_reveal_back_p){
1648 set_mode (state, DRAIN_MATRIX);
1650 if (state->mode == MATRIX &&
1651 state->knock_knock_p &&
1652 (! (random() % 10000)))
1654 if (! (random() % 5))
1655 set_mode (state, DRAIN_NMAP);
1657 set_mode (state, DRAIN_KNOCK);
1660 redraw_cells (state, True);
1665 static int ndens = 0;
1666 static int tdens = 0;
1672 ((double) (state->grid_width * state->grid_height))));
1675 printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
1681 return state->delay;
1686 xmatrix_reshape (Display *dpy, Window window, void *closure,
1687 unsigned int w, unsigned int h)
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;
1700 if (ow != state->grid_width ||
1701 oh != state->grid_height)
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);
1711 /* fprintf(stderr, "resize: %d x %d ==> %d x %d\n",
1712 ow, oh, state->grid_width, state->grid_height); */
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];
1723 free (state->cells);
1724 free (state->background);
1725 state->cells = ncells;
1726 state->background = nbackground;
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;
1735 textclient_reshape (state->tc,
1738 state->grid_width - 2,
1739 state->grid_height - 1,
1744 xmatrix_event (Display *dpy, Window window, void *closure, XEvent *event)
1746 m_state *state = (m_state *) closure;
1748 if (event->xany.type == KeyPress)
1752 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1756 /*set_mode (state, DRAIN_MATRIX);*/
1758 state->back_text_full_p = True;
1759 state->start_reveal_back_p = True;
1762 case '+': case '=': case '>': case '.':
1763 state->density += 10;
1764 if (state->density > 100)
1765 state->density = 100;
1770 case '-': case '_': case '<': case ',':
1771 state->density -= 10;
1772 if (state->density < 0)
1778 case '[': case '(': case '{':
1779 state->insert_top_p = True;
1780 state->insert_bottom_p = False;
1783 case ']': case ')': case '}':
1784 state->insert_top_p = False;
1785 state->insert_bottom_p = True;
1788 case '\\': case '|':
1789 state->insert_top_p = True;
1790 state->insert_bottom_p = True;
1794 set_mode (state, DRAIN_TRACE_A);
1798 set_mode (state, DRAIN_TRACE_B);
1802 set_mode (state, DRAIN_KNOCK);
1806 set_mode (state, DRAIN_NMAP);
1814 if (screenhack_event_helper (dpy, window, event))
1816 set_mode (state, DRAIN_MATRIX);
1824 xmatrix_free (Display *dpy, Window window, void *closure)
1826 m_state *state = (m_state *) closure;
1828 textclient_close (state->tc);
1829 if (state->cursor_timer)
1830 XtRemoveTimeOut (state->cursor_timer);
1832 /* #### there's more to free here */
1837 static const char *xmatrix_defaults [] = {
1838 ".background: black",
1839 ".foreground: #00AA00",
1841 "*matrixFont: large",
1845 "*tracePhone: (312) 555-0690",
1849 "*knockKnock: True",
1852 "*program: xscreensaver-text --latin1",
1853 "*geometry: 960x720",
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 },
1884 XSCREENSAVER_MODULE ("XMatrix", xmatrix)