1 /* xscreensaver, Copyright (c) 1999-2012 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;
339 char c = textclient_getc (s->tc);
340 n = (c > 0 ? 1 : -1);
341 s->buf [s->buf_pos] = c;
345 s->do_fill_buff = False;
346 s->buf_pos = (s->buf_pos + n);
347 if(s->buf_pos > BUF_SIZE){
348 /* copy to start of buffer */
349 /* areas shouldn't overlap, but just in case, use memmove */
350 memmove(s->buf,s->buf+BUF_SIZE,s->buf_pos-BUF_SIZE);
352 s->buf_pos = s->buf_pos % BUF_SIZE;
356 /* Couldn't read anything from the buffer */
357 /* Assume EOF has been reached, so start again */
358 s->do_fill_buff = True;
363 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
364 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
367 set_cursor_1 (m_state *state, Bool on)
369 Bool changed = (state->cursor_on != on);
370 state->cursor_on = on;
372 if (changed && state->cursor_x >= 0 && state->cursor_y >= 0)
374 m_cell *cell = &state->cells[state->grid_width * state->cursor_y
377 cell->changed = True;
384 set_cursor (m_state *state, Bool on)
386 if (set_cursor_1 (state, on))
388 if (state->cursor_timer)
389 XtRemoveTimeOut (state->cursor_timer);
390 state->cursor_timer = 0;
392 cursor_on_timer (state, 0);
397 cursor_off_timer (XtPointer closure, XtIntervalId *id)
399 m_state *state = (m_state *) closure;
400 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
401 set_cursor_1 (state, False);
402 state->cursor_timer = XtAppAddTimeOut (app, 333,
403 cursor_on_timer, closure);
407 cursor_on_timer (XtPointer closure, XtIntervalId *id)
409 m_state *state = (m_state *) closure;
410 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
411 set_cursor_1 (state, True);
412 state->cursor_timer = XtAppAddTimeOut (app, 666,
413 cursor_off_timer, closure);
418 init_spinners (m_state *state)
420 int i = state->nspinners;
424 for (y = 0; y < state->grid_height; y++)
425 for (x = 0; x < state->grid_width; x++)
427 cell = &state->cells[state->grid_width * y + x];
433 x = random() % state->grid_width;
434 y = random() % state->grid_height;
435 cell = &state->cells[state->grid_width * y + x];
442 clear_spinners (m_state *state)
445 for (i = 0; i < state->grid_width * state->grid_height; i++)
446 if (state->cells[i].spinner)
448 state->cells[i].spinner = 0;
449 state->cells[i].changed = 1;
454 static void set_mode (m_state *, m_mode);
458 init_trace (m_state *state)
460 char *s = get_string_resource (state->dpy, "tracePhone", "TracePhone");
466 state->tracing = (signed char *) malloc (strlen (s) + 1);
467 s3 = (char *) state->tracing;
469 for (s2 = s; *s2; s2++)
470 if (*s2 >= '0' && *s2 <= '9')
474 if (s3 == (char *) state->tracing)
477 for (i = 0; i < strlen((char *) state->tracing); i++)
478 state->tracing[i] = -state->tracing[i];
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);
1337 Bool scrolled_p = False;
1338 unsigned char c, c1;
1339 int x = state->cursor_x;
1340 int y = state->cursor_y;
1343 c = ((unsigned char *) state->typing)[0];
1344 c1 = ((unsigned char *) state->typing)[1];
1346 state->typing_delay = (!c || c1 == '\n'
1347 ? state->typing_line_delay
1348 : state->typing_char_delay);
1351 state->typing_delay = 0;
1356 if (state->typing_scroll_p &&
1358 x >= state->grid_width - 1))
1360 set_cursor (state, False);
1364 if (y >= state->grid_height-1)
1367 for (yy = 0; yy < state->grid_height-2; yy++)
1368 for (xx = 0; xx < state->grid_width; xx++)
1370 int ii = yy * state->grid_width + xx;
1371 int jj = (yy+1) * state->grid_width + xx;
1372 state->cells[ii] = state->cells[jj];
1373 state->cells[ii].changed = 1;
1375 /* clear bottom row */
1376 for (xx = 0; xx < state->grid_width; xx++)
1378 int ii = yy * state->grid_width + xx;
1379 state->cells[ii].glyph = 0;
1380 state->cells[ii].changed = 1;
1382 y--; /* move back up to bottom line */
1389 if (!state->typing_scroll_p)
1392 set_cursor (state, False);
1393 x = state->typing_left_margin;
1395 /* clear the line */
1396 i = state->grid_width * y;
1397 j = i + state->grid_width;
1400 state->cells[i].glyph = 0;
1401 state->cells[i].changed = 1;
1404 state->typing_bold_p = False;
1405 state->typing_stutter_p = False;
1409 else if (c == '\010')
1410 state->typing_delay += 500000;
1412 else if (c == '\001')
1414 state->typing_stutter_p = True;
1415 state->typing_bold_p = False;
1417 else if (c == '\002')
1418 state->typing_bold_p = True;
1420 else if (x < state->grid_width-1)
1422 m_cell *cell = &state->cells[state->grid_width * y + x];
1423 cell->glyph = char_map[c] + 1;
1424 if (c == ' ' || c == '\t') cell->glyph = 0;
1426 cell->glow = (state->typing_bold_p ? 127 : 0);
1432 if (x >= state->grid_width-1)
1433 x = state->grid_width-1;
1437 if (state->typing_stutter_p)
1439 if (state->typing_delay == 0)
1440 state->typing_delay = 20000;
1442 state->typing_delay += (0xFFFFFF & ((random() % 200000) + 1));
1445 /* If there's no delay after this character, just keep going. */
1446 if (state->typing_delay == 0)
1449 if (scrolled_p || x != state->cursor_x || y != state->cursor_y)
1451 set_cursor (state, False);
1452 state->cursor_x = x;
1453 state->cursor_y = y;
1454 if (state->typing_cursor_p)
1455 set_cursor (state, True);
1462 hack_matrix (m_state *state)
1466 switch (state->mode)
1468 case TRACE_DONE: case TRACE_FAIL:
1470 case TRACE_A: case TRACE_B:
1471 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1477 /* Glow some characters. */
1478 if (!state->insert_bottom_p)
1480 int i = random() % (state->grid_width / 2);
1483 int yy = random() % state->grid_height;
1484 int xx = random() % state->grid_width;
1485 m_cell *cell = &state->cells[state->grid_width * yy + xx];
1486 if (cell->glyph && cell->glow == 0)
1488 cell->glow = random() % 10;
1494 /* Change some of the feeders. */
1495 for (x = 0; x < state->grid_width; x++)
1497 m_feeder *f = &state->feeders[x];
1498 Bool bottom_feeder_p;
1500 if (f->remaining > 0) /* never change if pipe isn't empty */
1503 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
1506 f->remaining = 3 + (random() % state->grid_height);
1507 f->throttle = ((random() % 5) + (random() % 5));
1509 if ((random() % 4) != 0)
1512 if (state->mode == TRACE_A || state->mode == TRACE_B)
1513 bottom_feeder_p = True;
1514 else if (state->insert_top_p && state->insert_bottom_p)
1515 bottom_feeder_p = (random() & 1);
1517 bottom_feeder_p = state->insert_bottom_p;
1519 if (bottom_feeder_p)
1520 f->y = random() % (state->grid_height / 2);
1525 if (state->mode == MATRIX && (! (random() % 500)))
1526 init_spinners (state);
1530 static unsigned long
1531 xmatrix_draw (Display *dpy, Window window, void *closure)
1533 m_state *state = (m_state *) closure;
1535 if (state->typing_delay > 0)
1537 state->typing_delay -= state->delay;
1538 if (state->typing_delay < 0)
1539 state->typing_delay = 0;
1540 redraw_cells (state, False);
1541 return state->delay;
1544 switch (state->mode)
1546 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1547 case TRACE_A: case TRACE_B:
1548 feed_matrix (state);
1549 hack_matrix (state);
1557 feed_matrix (state);
1558 if (screen_blank_p (state))
1560 state->typing_delay = 500000;
1561 if(state->start_reveal_back_p){
1564 state->typing_delay = 5000000;
1565 state->start_reveal_back_p = False;
1566 state->back_text_full_p = False;
1567 /* for loop to move background to foreground */
1568 for (y = 0; y < state->grid_height; y++){
1569 for (x = 0; x < state->grid_width; x++){
1570 to = &state->cells[state->grid_width * y + x];
1571 back = &state->background[state->grid_width * y + x];
1572 to->glyph = back->glyph;
1573 to->changed = back->changed;
1579 switch (state->mode)
1581 case DRAIN_TRACE_A: set_mode (state, TRACE_TEXT_A); break;
1582 case DRAIN_TRACE_B: set_mode (state, TRACE_TEXT_B); break;
1583 case DRAIN_KNOCK: set_mode (state, KNOCK); break;
1584 case DRAIN_NMAP: set_mode (state, NMAP); break;
1585 case DRAIN_MATRIX: set_mode (state, state->def_mode); break;
1586 default: abort(); break;
1592 set_mode (state, state->def_mode);
1602 if (! state->typing) /* done typing */
1604 set_cursor (state, False);
1605 switch (state->mode)
1607 case TRACE_TEXT_A: set_mode (state, TRACE_A); break;
1608 case TRACE_TEXT_B: set_mode (state, TRACE_B); break;
1609 case TRACE_FAIL: set_mode (state, state->def_mode); break;
1610 case KNOCK: set_mode (state, state->def_mode); break;
1611 case NMAP: set_mode (state, state->def_mode); break;
1612 default: abort(); break;
1620 if (state->start_reveal_back_p){
1621 set_mode (state, DRAIN_MATRIX);
1623 if (state->mode == MATRIX &&
1624 state->knock_knock_p &&
1625 (! (random() % 10000)))
1627 if (! (random() % 5))
1628 set_mode (state, DRAIN_NMAP);
1630 set_mode (state, DRAIN_KNOCK);
1633 redraw_cells (state, True);
1638 static int ndens = 0;
1639 static int tdens = 0;
1645 ((double) (state->grid_width * state->grid_height))));
1648 printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
1654 return state->delay;
1659 xmatrix_reshape (Display *dpy, Window window, void *closure,
1660 unsigned int w, unsigned int h)
1662 m_state *state = (m_state *) closure;
1663 int ow = state->grid_width;
1664 int oh = state->grid_height;
1665 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
1666 state->grid_width = state->xgwa.width / state->char_width;
1667 state->grid_height = state->xgwa.height / state->char_height;
1668 state->grid_width++;
1669 state->grid_height++;
1670 if (state->grid_width < 5) state->grid_width = 5;
1671 if (state->grid_height < 5) state->grid_height = 5;
1673 if (ow != state->grid_width ||
1674 oh != state->grid_height)
1676 m_cell *ncells = (m_cell *)
1677 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1678 m_cell *nbackground = (m_cell *)
1679 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1680 m_feeder *nfeeders = (m_feeder *)
1681 calloc (sizeof(m_feeder), state->grid_width);
1684 /* fprintf(stderr, "resize: %d x %d ==> %d x %d\n",
1685 ow, oh, state->grid_width, state->grid_height); */
1687 for (y = 0; y < oh; y++)
1688 for (x = 0; x < ow; x++)
1689 if (x < ow && x < state->grid_width &&
1690 y < oh && y < state->grid_height){
1691 ncells[y * state->grid_width + x] =
1692 state->cells[y * ow + x];
1693 nbackground[y * state->grid_width + x] =
1694 state->background[y * ow + x];
1696 free (state->cells);
1697 free (state->background);
1698 state->cells = ncells;
1699 state->background = nbackground;
1701 x = (ow < state->grid_width ? ow : state->grid_width);
1702 for (i = 0; i < x; i++)
1703 nfeeders[i] = state->feeders[i];
1704 free (state->feeders);
1705 state->feeders = nfeeders;
1708 textclient_reshape (state->tc,
1711 state->grid_width - 2,
1712 state->grid_height - 1);
1716 xmatrix_event (Display *dpy, Window window, void *closure, XEvent *event)
1718 m_state *state = (m_state *) closure;
1720 if (event->xany.type == KeyPress)
1724 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1728 /*set_mode (state, DRAIN_MATRIX);*/
1730 state->back_text_full_p = True;
1731 state->start_reveal_back_p = True;
1734 case '+': case '=': case '>': case '.':
1735 state->density += 10;
1736 if (state->density > 100)
1737 state->density = 100;
1742 case '-': case '_': case '<': case ',':
1743 state->density -= 10;
1744 if (state->density < 0)
1750 case '[': case '(': case '{':
1751 state->insert_top_p = True;
1752 state->insert_bottom_p = False;
1755 case ']': case ')': case '}':
1756 state->insert_top_p = False;
1757 state->insert_bottom_p = True;
1760 case '\\': case '|':
1761 state->insert_top_p = True;
1762 state->insert_bottom_p = True;
1766 set_mode (state, DRAIN_TRACE_A);
1770 set_mode (state, DRAIN_TRACE_B);
1774 set_mode (state, DRAIN_KNOCK);
1778 set_mode (state, DRAIN_NMAP);
1790 xmatrix_free (Display *dpy, Window window, void *closure)
1792 m_state *state = (m_state *) closure;
1794 textclient_close (state->tc);
1795 if (state->cursor_timer)
1796 XtRemoveTimeOut (state->cursor_timer);
1798 /* #### there's more to free here */
1803 static const char *xmatrix_defaults [] = {
1804 ".background: black",
1805 ".foreground: #00AA00",
1807 "*matrixFont: large",
1811 "*tracePhone: (312) 555-0690",
1815 "*knockKnock: True",
1818 "*program: xscreensaver-text",
1819 "*geometry: 800x600",
1823 static XrmOptionDescRec xmatrix_options [] = {
1824 { "-small", ".matrixFont", XrmoptionNoArg, "Small" },
1825 { "-large", ".matrixFont", XrmoptionNoArg, "Large" },
1826 { "-delay", ".delay", XrmoptionSepArg, 0 },
1827 { "-insert", ".insert", XrmoptionSepArg, 0 },
1828 { "-top", ".insert", XrmoptionNoArg, "top" },
1829 { "-bottom", ".insert", XrmoptionNoArg, "bottom" },
1830 { "-both", ".insert", XrmoptionNoArg, "both" },
1831 { "-density", ".density", XrmoptionSepArg, 0 },
1832 { "-trace", ".trace", XrmoptionNoArg, "True" },
1833 { "-no-trace", ".trace", XrmoptionNoArg, "False" },
1834 { "-crack", ".mode", XrmoptionNoArg, "crack"},
1835 { "-phone", ".tracePhone", XrmoptionSepArg, 0 },
1836 { "-mode", ".mode", XrmoptionSepArg, 0 },
1837 { "-dna", ".mode", XrmoptionNoArg, "DNA" },
1838 { "-binary", ".mode", XrmoptionNoArg, "binary" },
1839 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal"},
1840 { "-decimal", ".mode", XrmoptionNoArg, "decimal"},
1841 { "-knock-knock", ".knockKnock", XrmoptionNoArg, "True" },
1842 { "-no-knock-knock", ".knockKnock", XrmoptionNoArg, "False" },
1843 { "-ascii", ".mode", XrmoptionNoArg, "ascii"},
1844 { "-pipe", ".usePipe", XrmoptionNoArg, "True" },
1845 { "-no-pipe", ".usePipe", XrmoptionNoArg, "False" },
1846 { "-program", ".program", XrmoptionSepArg, 0 },
1850 XSCREENSAVER_MODULE ("XMatrix", xmatrix)