1 /* xscreensaver, Copyright (c) 1999-2014 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);
470 s3 = (char *) state->tracing;
472 for (s2 = s; *s2; s2++)
473 if (*s2 >= '0' && *s2 <= '9')
477 if (s3 == (char *) state->tracing)
480 for (i = 0; i < strlen((char *) state->tracing); i++)
481 state->tracing[i] = -state->tracing[i];
483 state->glyph_map = decimal_encoding;
484 state->nglyphs = countof(decimal_encoding);
489 fprintf (stderr, "%s: bad phone number: \"%s\".\n",
490 progname, s ? s : "(null)");
493 if (state->tracing) free (state->tracing);
495 set_mode (state, MATRIX);
500 init_drain (m_state *state)
504 set_cursor (state, False);
505 state->cursor_x = -1;
506 state->cursor_y = -1;
508 /* Fill the top row with empty top-feeders, to clear the screen. */
509 for (i = 0; i < state->grid_width; i++)
511 m_feeder *f = &state->feeders[i];
517 /* Turn off all the spinners, else they never go away. */
518 clear_spinners (state);
522 screen_blank_p (m_state *state)
525 for (i = 0; i < state->grid_width * state->grid_height; i++)
526 if (state->cells[i].glyph)
533 set_mode (m_state *state, m_mode mode)
535 if (mode == state->mode)
544 state->glyph_map = matrix_encoding;
545 state->nglyphs = countof(matrix_encoding);
546 flip_images (state, True);
547 init_spinners (state);
550 state->glyph_map = dna_encoding;
551 state->nglyphs = countof(dna_encoding);
552 flip_images (state, False);
555 state->glyph_map = binary_encoding;
556 state->nglyphs = countof(binary_encoding);
557 flip_images (state, False);
560 state->glyph_map = hex_encoding;
561 state->nglyphs = countof(hex_encoding);
562 flip_images (state, False);
565 state->glyph_map = ascii_encoding;
566 state->nglyphs = countof(ascii_encoding);
567 flip_images (state, False);
574 state->glyph_map = decimal_encoding;
575 state->nglyphs = countof(decimal_encoding);
576 flip_images (state, False);
580 flip_images (state, False);
600 xmatrix_init (Display *dpy, Window window)
605 m_state *state = (m_state *) calloc (sizeof(*state), 1);
608 state->window = window;
609 state->delay = get_integer_resource (dpy, "delay", "Integer");
611 XGetWindowAttributes (dpy, window, &state->xgwa);
613 state->small_p = (state->xgwa.width < 300);
615 const char *s = get_string_resource (dpy, "matrixFont", "String");
616 if (!s || !*s || !strcasecmp(s, "large"))
617 state->small_p = False;
618 else if (!strcasecmp(s, "small"))
619 state->small_p = True;
621 fprintf (stderr, "%s: matrixFont should be 'small' or 'large' not '%s'\n",
625 load_images (dpy, state);
627 gcv.foreground = get_pixel_resource(state->dpy, state->xgwa.colormap,
628 "foreground", "Foreground");
629 gcv.background = get_pixel_resource(state->dpy, state->xgwa.colormap,
630 "background", "Background");
631 state->draw_gc = XCreateGC (state->dpy, state->window,
632 GCForeground|GCBackground, &gcv);
633 gcv.foreground = gcv.background;
634 state->erase_gc = XCreateGC (state->dpy, state->window,
635 GCForeground|GCBackground, &gcv);
637 state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv);
639 /* Allocate colors for SYSTEM FAILURE box */
641 XColor boxcolors[] = {
642 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
643 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
644 { 0, 0xE0E0, 0xF7F7, 0xE0E0, DoRed|DoGreen|DoBlue, 0 },
645 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
646 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
648 for (i = 0; i < countof(boxcolors); i++)
650 if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i]))
651 state->colors[i] = boxcolors[i].pixel;
653 state->colors[i] = gcv.foreground; /* default black */
657 state->char_width = state->image_width / CHAR_COLS;
658 state->char_height = state->image_height / CHAR_ROWS;
660 state->grid_width = state->xgwa.width / state->char_width;
661 state->grid_height = state->xgwa.height / state->char_height;
663 state->grid_height++;
664 if (state->grid_width < 5) state->grid_width = 5;
665 if (state->grid_height < 5) state->grid_height = 5;
667 state->glyph_map = matrix_encoding;
668 state->nglyphs = countof(matrix_encoding);
670 state->cells = (m_cell *)
671 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
672 state->background = (m_cell *)
673 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
674 state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
676 state->density = get_integer_resource (dpy, "density", "Integer");
678 insert = get_string_resource(dpy, "insert", "Insert");
679 if (insert && !strcmp(insert, "top"))
681 state->insert_top_p = True;
682 state->insert_bottom_p = False;
684 else if (insert && !strcmp(insert, "bottom"))
686 state->insert_top_p = False;
687 state->insert_bottom_p = True;
689 else if (insert && !strcmp(insert, "both"))
691 state->insert_top_p = True;
692 state->insert_bottom_p = True;
696 if (insert && *insert)
698 "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
700 state->insert_top_p = False;
701 state->insert_bottom_p = True;
704 state->nspinners = get_integer_resource (dpy, "spinners", "Integer");
709 state->knock_knock_p = get_boolean_resource (dpy, "knockKnock", "KnockKnock");
711 state->use_pipe_p = get_boolean_resource (dpy, "usePipe", "Boolean");
713 state->buf[0] = ' '; /* spacer byte in buffer (space) */
715 state->do_fill_buff = True;
716 state->start_reveal_back_p = False;
717 state->back_text_full_p = False;
722 state->def_mode = MATRIX;
723 mode = get_string_resource (dpy, "mode", "Mode");
724 if (mode && !strcasecmp(mode, "trace"))
725 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
726 else if (mode && !strcasecmp(mode, "crack"))
727 set_mode (state, DRAIN_NMAP);
728 else if (mode && !strcasecmp(mode, "dna")){
729 set_mode (state, DNA);
730 state->def_mode = DNA;
732 else if (mode && (!strcasecmp(mode, "bin") ||
733 !strcasecmp(mode, "binary"))){
734 set_mode (state, BINARY);
735 state->def_mode = BINARY;
737 else if (mode && (!strcasecmp(mode, "hex") ||
738 !strcasecmp(mode, "hexadecimal"))){
739 set_mode (state, HEX);
740 state->def_mode = HEX;
742 else if (mode && (!strcasecmp(mode, "dec") ||
743 !strcasecmp(mode, "decimal"))){
744 set_mode (state, DEC);
745 state->def_mode = DEC;
747 else if (mode && (!strcasecmp(mode, "asc") ||
748 !strcasecmp(mode, "ascii"))){
749 set_mode (state, ASCII);
750 state->def_mode = ASCII;
752 else if (mode && !strcasecmp(mode, "pipe"))
754 set_mode (state, ASCII);
755 state->def_mode = ASCII;
756 state->use_pipe_p = True;
757 state->tc = textclient_open (dpy);
759 else if (!mode || !*mode || !strcasecmp(mode, "matrix"))
760 set_mode (state, MATRIX);
763 fprintf (stderr, "%s: `mode' must be ",progname);
764 fprintf (stderr, "matrix, trace, dna, binary, ascii, hex, or pipe: ");
765 fprintf (stderr, "not `%s'\n", mode);
766 set_mode (state, MATRIX);
769 if (state->mode == MATRIX && get_boolean_resource (dpy, "trace", "Boolean"))
770 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
772 state->cursor_x = -1;
773 state->cursor_y = -1;
780 insert_glyph (m_state *state, int glyph, int x, int y)
782 Bool bottom_feeder_p = (y >= 0);
784 if (y >= state->grid_height)
789 to = &state->cells[state->grid_width * y + x];
793 for (y = state->grid_height-1; y > 0; y--)
795 from = &state->cells[state->grid_width * (y-1) + x];
796 to = &state->cells[state->grid_width * y + x];
797 to->glyph = from->glyph;
798 to->glow = from->glow;
801 to = &state->cells[x];
809 else if (bottom_feeder_p)
810 to->glow = 1 + (random() % (state->tracing ? 4 : 2));
817 place_back_char (m_state *state, char textc, int x, int y){
818 if((x>=0) && (y>=0) &&
819 (x < state->grid_width) && (y < state->grid_height)){
820 m_cell *celltmp = &state->background[state->grid_width * y + x];
821 celltmp -> glyph = char_map[(unsigned char)textc] + 1;
822 if(!celltmp->glyph || (celltmp->glyph == 3)){
823 celltmp -> glyph = char_map[32] + 1;
825 celltmp -> changed = 1;
830 place_back_text (m_state *state, char *text, int x, int y){
832 for(i=0; i<strlen(text); i++){
833 place_back_char(state, text[i], x+i, y);
838 place_back_pipe (m_state *state, char textc){
839 Bool new_line = False;
840 /* gringer pipe insert */
841 state->back_line[state->back_pos] = textc;
843 state->back_line[state->back_pos] = '\0';
846 else if ((state->back_pos > (state->grid_width - 4)) ||
847 (state->back_pos >= BUF_SIZE)){ /* off by 1? */
848 state->back_line[++state->back_pos] = '\0';
855 int startx = (state->grid_width >> 1) -
856 (strlen(state->back_line) >> 1);
857 place_back_text(state, state->back_line,
858 startx, state->back_y);
861 if(state->back_y >= (state->grid_height - 1)){
863 state->back_text_full_p = True;
864 state->start_reveal_back_p = True;
870 feed_matrix (m_state *state)
878 int L = strlen((char *) state->tracing);
882 for (i = 0; i < strlen((char *) state->tracing); i++)
883 if (state->tracing[i] > 0)
888 set_mode (state, TRACE_DONE);
889 state->typing_delay = 1000000;
894 i = 5 + (30 / (count+1)); /* how fast numbers are discovered */
896 if ((random() % i) == 0)
899 if (state->tracing[i] < 0)
900 state->tracing[i] = -state->tracing[i];
907 if ((random() % 40) == 0)
909 set_mode (state, TRACE_FAIL);
914 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
927 if((state->use_pipe_p) && (!state->back_text_full_p)){
928 place_back_pipe(state, state->buf[state->buf_done]);
929 state->buf_done = (state->buf_done + 1) % BUF_SIZE;
930 if(state->buf_done == (state->buf_pos - 1)){
931 state->do_fill_buff = True;
934 if(state->buf_done == (state->buf_pos + 1)){
935 state->do_fill_buff = False;
938 state->do_fill_buff = True;
942 /* Update according to current feeders. */
943 for (x = 0; x < state->grid_width; x++)
945 m_feeder *f = &state->feeders[x];
947 if (f->throttle) /* this is a delay tick, synced to frame. */
951 else if (f->remaining > 0) /* how many items are in the pipe */
955 if((state->use_pipe_p) && (!state->back_text_full_p)){
956 rval = (int) state->buf[f->pipe_loc];
957 if(++f->pipe_loc > (BUF_SIZE-1)){
959 /*fill_input(state);*/
961 rval = (rval % state->nglyphs);
964 rval = (random() % state->nglyphs);
966 g = state->glyph_map[rval] + 1;
967 insert_glyph (state, g, x, f->y);
969 if (f->y >= 0) /* bottom_feeder_p */
972 else /* if pipe is empty, insert spaces */
974 insert_glyph (state, 0, x, f->y);
975 if (f->y >= 0) /* bottom_feeder_p */
979 if ((random() % 10) == 0) /* randomly change throttle speed */
981 f->throttle = ((random() % 5) + (random() % 5));
988 redraw_cells (m_state *state, Bool active)
992 Bool use_back_p = False;
994 for (y = 0; y < state->grid_height; y++)
995 for (x = 0; x < state->grid_width; x++)
997 m_cell *cell = &state->cells[state->grid_width * y + x];
998 m_cell *back = &state->background[state->grid_width * y + x];
999 Bool cursor_p = (state->cursor_on &&
1000 x == state->cursor_x &&
1001 y == state->cursor_y);
1006 if((state->start_reveal_back_p) &&
1007 (back->glyph) && !(state->mode == TRACE_A ||
1008 state->mode == TRACE_B ||
1009 state->mode == TRACE_DONE)){
1015 /* In trace-mode, the state of each cell is random unless we have
1016 a match for this digit. */
1017 if (active && (state->mode == TRACE_A ||
1018 state->mode == TRACE_B ||
1019 state->mode == TRACE_DONE))
1021 int xx = x % strlen((char *) state->tracing);
1022 Bool dead_p = state->tracing[xx] > 0;
1024 if (y == 0 && x == xx && !use_back_p)
1025 cell->glyph = (dead_p
1026 ? state->glyph_map[state->tracing[xx]-'0'] + 1
1028 else if (y == 0 && !use_back_p)
1030 else if (!use_back_p)
1031 cell->glyph = (dead_p ? 0 :
1032 (state->glyph_map[(random()%state->nglyphs)]
1042 if (cell->glyph == 0 && !cursor_p && !use_back_p)
1043 XFillRectangle (state->dpy, state->window, state->erase_gc,
1044 x * state->char_width,
1045 y * state->char_height,
1047 state->char_height);
1050 int g = (cursor_p ? CURSOR_GLYPH : cell->glyph);
1051 int cx = (g - 1) % CHAR_COLS;
1052 int cy = (g - 1) / CHAR_COLS;
1053 int map = ((cell->glow != 0 || cell->spinner) ? GLOW_MAP :
1056 XCopyArea (state->dpy, state->images[map],
1057 state->window, state->draw_gc,
1058 cx * state->char_width,
1059 cy * state->char_height,
1062 x * state->char_width,
1063 y * state->char_height);
1068 if (cell->glow > 0 && state->mode != NMAP && !use_back_p)
1073 else if (cell->glow < 0)
1076 if (cell->spinner && active && !use_back_p)
1078 cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1);
1086 densitizer (m_state *state)
1088 /* Horrid kludge that converts percentages (density of screen coverage)
1089 to the parameter that actually controls this. I got this mapping
1090 empirically, on a 1024x768 screen. Sue me. */
1091 if (state->density < 10) return 85;
1092 else if (state->density < 15) return 60;
1093 else if (state->density < 20) return 45;
1094 else if (state->density < 25) return 25;
1095 else if (state->density < 30) return 20;
1096 else if (state->density < 35) return 15;
1097 else if (state->density < 45) return 10;
1098 else if (state->density < 50) return 8;
1099 else if (state->density < 55) return 7;
1100 else if (state->density < 65) return 5;
1101 else if (state->density < 80) return 3;
1102 else if (state->density < 90) return 2;
1108 hack_text (m_state *state)
1112 set_cursor (state, False);
1113 state->cursor_x = 0;
1114 state->cursor_y = 0;
1115 state->typing_scroll_p = False;
1116 state->typing_bold_p = False;
1117 state->typing_cursor_p = True;
1118 state->typing_stutter_p = False;
1119 state->typing_char_delay = 10000;
1120 state->typing_line_delay = 1500000;
1122 switch (state->mode)
1126 clear_spinners (state);
1127 if (state->mode == TRACE_TEXT_A)
1129 if (state->grid_width >= 52)
1131 ("Call trans opt: received. 2-19-98 13:24:18 REC:Log>\n"
1132 "Trace program: running\n");
1135 ("Call trans opt: received.\n2-19-98 13:24:18 REC:Log>\n"
1136 "Trace program: running\n");
1140 if (state->grid_width >= 52)
1142 ("Call trans opt: received. 9-18-99 14:32:21 REC:Log>\n"
1143 "WARNING: carrier anomaly\n"
1144 "Trace program: running\n");
1147 ("Call trans opt: received.\n9-18-99 14:32:21 REC:Log>\n"
1148 "WARNING: carrier anomaly\n"
1149 "Trace program: running\n");
1155 const char *s = "SYSTEM FAILURE\n";
1157 float cx = (state->grid_width - strlen(s) - 1) / 2 - 0.5;
1158 float cy = (state->grid_height / 2) - 1.3;
1163 XFillRectangle (state->dpy, state->window, state->erase_gc,
1164 cx * state->char_width,
1165 cy * state->char_height,
1166 strlen(s) * state->char_width,
1167 state->char_height * 1.6);
1169 for (i = -2; i < 3; i++)
1172 gcv.foreground = state->colors[i + 2];
1173 XChangeGC (state->dpy, state->scratch_gc, GCForeground, &gcv);
1174 XDrawRectangle (state->dpy, state->window, state->scratch_gc,
1175 cx * state->char_width - i,
1176 cy * state->char_height - i,
1177 strlen(s) * state->char_width + (2 * i),
1178 (state->char_height * 1.6) + (2 * i));
1181 /* If we don't clear these, part of the box may get overwritten */
1182 for (i = 0; i < state->grid_height * state->grid_width; i++)
1184 m_cell *cell = &state->cells[i];
1188 state->cursor_x = (state->grid_width - strlen(s) - 1) / 2;
1189 state->cursor_y = (state->grid_height / 2) - 1;
1190 if (state->cursor_x < 0) state->cursor_x = 0;
1191 if (state->cursor_y < 0) state->cursor_y = 0;
1194 state->typing_char_delay = 0;
1195 state->typing_cursor_p = False;
1201 clear_spinners (state);
1202 state->typing = ("\001Wake up, Neo...\n"
1203 "\001The Matrix has you...\n"
1204 "\001Follow the white rabbit.\n"
1206 "Knock, knock, Neo.\n");
1208 state->cursor_x = 4;
1209 state->cursor_y = 2;
1210 state->typing_char_delay = 0;
1211 state->typing_line_delay = 2000000;
1217 /* Note that what Trinity is using here is moderately accurate:
1218 She runs nmap (http://www.insecure.org/nmap/) then breaks in
1219 with a (hypothetical) program called "sshnuke" that exploits
1220 the (very real) SSHv1 CRC32 compensation attack detector bug
1221 (http://staff.washington.edu/dittrich/misc/ssh-analysis.txt).
1223 The command syntax of the power grid control software looks a
1224 lot like Cisco IOS to me. (IOS is a descendant of VMS.)
1227 clear_spinners (state);
1230 __extension__ /* don't warn about "string length is greater than
1231 the length ISO C89 compilers are required to
1237 "\001nmap -v -sS -O 10.2.2.2\n"
1238 "Starting nmap V. 2.54BETA25\n"
1239 "\010\010\010\010\010\010\010\010\010\010"
1240 "Insufficient responses for TCP sequencing (3), OS detection"
1241 " may be less accurate\n"
1242 "Interesting ports on 10.2.2.2:\n"
1243 "(The 1539 ports scanned but not shown below are in state:"
1245 "Port state service\n"
1248 "No exact OS matches for host\n"
1250 "Nmap run completed -- 1 IP address (1 host up) scanned\n"
1254 "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n"
1255 "Connecting to 10.2.2.2:ssh ... "
1258 "Attempting to exploit SSHv1 CRC32 ... "
1261 "Resetting root password to \"Z1ON0101\".\n"
1263 "System open: Access Level <9>\n"
1268 "\001ssh 10.2.2.2 -l root\n"
1270 "root@10.2.2.2's password: "
1276 "\001disable grid nodes 21 - 48\n"
1278 "\002Warning: Disabling nodes 21-48 will disconnect sector 11"
1281 "\002 ARE YOU SURE? (y/n) "
1287 "\010\002Grid Node 21 offline...\n"
1288 "\010\002Grid Node 22 offline...\n"
1289 "\010\002Grid Node 23 offline...\n"
1290 "\010\002Grid Node 24 offline...\n"
1291 "\010\002Grid Node 25 offline...\n"
1292 "\010\002Grid Node 26 offline...\n"
1293 "\010\002Grid Node 27 offline...\n"
1294 "\010\002Grid Node 28 offline...\n"
1295 "\010\002Grid Node 29 offline...\n"
1296 "\010\002Grid Node 30 offline...\n"
1297 "\010\002Grid Node 31 offline...\n"
1298 "\010\002Grid Node 32 offline...\n"
1299 "\010\002Grid Node 33 offline...\n"
1300 "\010\002Grid Node 34 offline...\n"
1301 "\010\002Grid Node 35 offline...\n"
1302 "\010\002Grid Node 36 offline...\n"
1303 "\010\002Grid Node 37 offline...\n"
1304 "\010\002Grid Node 38 offline...\n"
1305 "\010\002Grid Node 39 offline...\n"
1306 "\010\002Grid Node 40 offline...\n"
1307 "\010\002Grid Node 41 offline...\n"
1308 "\010\002Grid Node 42 offline...\n"
1309 "\010\002Grid Node 43 offline...\n"
1310 "\010\002Grid Node 44 offline...\n"
1311 "\010\002Grid Node 45 offline...\n"
1312 "\010\002Grid Node 46 offline...\n"
1313 "\010\002Grid Node 47 offline...\n"
1314 "\010\002Grid Node 48 offline...\n"
1317 "\010\010\010\010\010\010\010\010"
1320 state->cursor_x = 0;
1321 state->cursor_y = state->grid_height - 3;
1322 state->typing_scroll_p = True;
1323 state->typing_char_delay = 0;
1324 state->typing_line_delay = 20000;
1333 state->typing_left_margin = state->cursor_x;
1334 state->typing_delay = state->typing_char_delay;
1335 if (state->typing_cursor_p)
1336 set_cursor (state, True);
1340 Bool scrolled_p = False;
1341 unsigned char c, c1;
1342 int x = state->cursor_x;
1343 int y = state->cursor_y;
1346 c = ((unsigned char *) state->typing)[0];
1347 c1 = c ? ((unsigned char *) state->typing)[1] : 0;
1349 state->typing_delay = (!c || c1 == '\n'
1350 ? state->typing_line_delay
1351 : state->typing_char_delay);
1354 state->typing_delay = 0;
1359 if (state->typing_scroll_p &&
1361 x >= state->grid_width - 1))
1363 set_cursor (state, False);
1367 if (y >= state->grid_height-1)
1370 for (yy = 0; yy < state->grid_height-2; yy++)
1371 for (xx = 0; xx < state->grid_width; xx++)
1373 int ii = yy * state->grid_width + xx;
1374 int jj = (yy+1) * state->grid_width + xx;
1375 state->cells[ii] = state->cells[jj];
1376 state->cells[ii].changed = 1;
1378 /* clear bottom row */
1379 for (xx = 0; xx < state->grid_width; xx++)
1381 int ii = yy * state->grid_width + xx;
1382 state->cells[ii].glyph = 0;
1383 state->cells[ii].changed = 1;
1385 y--; /* move back up to bottom line */
1392 if (!state->typing_scroll_p)
1395 set_cursor (state, False);
1396 x = state->typing_left_margin;
1398 /* clear the line */
1399 i = state->grid_width * y;
1400 j = i + state->grid_width;
1403 state->cells[i].glyph = 0;
1404 state->cells[i].changed = 1;
1407 state->typing_bold_p = False;
1408 state->typing_stutter_p = False;
1412 else if (c == '\010')
1413 state->typing_delay += 500000;
1415 else if (c == '\001')
1417 state->typing_stutter_p = True;
1418 state->typing_bold_p = False;
1420 else if (c == '\002')
1421 state->typing_bold_p = True;
1423 else if (x < state->grid_width-1)
1425 m_cell *cell = &state->cells[state->grid_width * y + x];
1426 cell->glyph = char_map[c] + 1;
1427 if (c == ' ' || c == '\t') cell->glyph = 0;
1429 cell->glow = (state->typing_bold_p ? 127 : 0);
1435 if (x >= state->grid_width-1)
1436 x = state->grid_width-1;
1440 if (state->typing_stutter_p)
1442 if (state->typing_delay == 0)
1443 state->typing_delay = 20000;
1445 state->typing_delay += (0xFFFFFF & ((random() % 200000) + 1));
1448 /* If there's no delay after this character, just keep going. */
1449 if (state->typing_delay == 0)
1452 if (scrolled_p || x != state->cursor_x || y != state->cursor_y)
1454 set_cursor (state, False);
1455 state->cursor_x = x;
1456 state->cursor_y = y;
1457 if (state->typing_cursor_p)
1458 set_cursor (state, True);
1465 hack_matrix (m_state *state)
1469 switch (state->mode)
1471 case TRACE_DONE: case TRACE_FAIL:
1473 case TRACE_A: case TRACE_B:
1474 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1480 /* Glow some characters. */
1481 if (!state->insert_bottom_p)
1483 int i = random() % (state->grid_width / 2);
1486 int yy = random() % state->grid_height;
1487 int xx = random() % state->grid_width;
1488 m_cell *cell = &state->cells[state->grid_width * yy + xx];
1489 if (cell->glyph && cell->glow == 0)
1491 cell->glow = random() % 10;
1497 /* Change some of the feeders. */
1498 for (x = 0; x < state->grid_width; x++)
1500 m_feeder *f = &state->feeders[x];
1501 Bool bottom_feeder_p;
1503 if (f->remaining > 0) /* never change if pipe isn't empty */
1506 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
1509 f->remaining = 3 + (random() % state->grid_height);
1510 f->throttle = ((random() % 5) + (random() % 5));
1512 if ((random() % 4) != 0)
1515 if (state->mode == TRACE_A || state->mode == TRACE_B)
1516 bottom_feeder_p = True;
1517 else if (state->insert_top_p && state->insert_bottom_p)
1518 bottom_feeder_p = (random() & 1);
1520 bottom_feeder_p = state->insert_bottom_p;
1522 if (bottom_feeder_p)
1523 f->y = random() % (state->grid_height / 2);
1528 if (state->mode == MATRIX && (! (random() % 500)))
1529 init_spinners (state);
1533 static unsigned long
1534 xmatrix_draw (Display *dpy, Window window, void *closure)
1536 m_state *state = (m_state *) closure;
1538 if (state->typing_delay > 0)
1540 state->typing_delay -= state->delay;
1541 if (state->typing_delay < 0)
1542 state->typing_delay = 0;
1543 redraw_cells (state, False);
1544 return state->delay;
1547 switch (state->mode)
1549 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1550 case TRACE_A: case TRACE_B:
1551 feed_matrix (state);
1552 hack_matrix (state);
1560 feed_matrix (state);
1561 if (screen_blank_p (state))
1563 state->typing_delay = 500000;
1564 if(state->start_reveal_back_p){
1567 state->typing_delay = 5000000;
1568 state->start_reveal_back_p = False;
1569 state->back_text_full_p = False;
1570 /* for loop to move background to foreground */
1571 for (y = 0; y < state->grid_height; y++){
1572 for (x = 0; x < state->grid_width; x++){
1573 to = &state->cells[state->grid_width * y + x];
1574 back = &state->background[state->grid_width * y + x];
1575 to->glyph = back->glyph;
1576 to->changed = back->changed;
1582 switch (state->mode)
1584 case DRAIN_TRACE_A: set_mode (state, TRACE_TEXT_A); break;
1585 case DRAIN_TRACE_B: set_mode (state, TRACE_TEXT_B); break;
1586 case DRAIN_KNOCK: set_mode (state, KNOCK); break;
1587 case DRAIN_NMAP: set_mode (state, NMAP); break;
1588 case DRAIN_MATRIX: set_mode (state, state->def_mode); break;
1589 default: abort(); break;
1595 set_mode (state, state->def_mode);
1605 if (! state->typing) /* done typing */
1607 set_cursor (state, False);
1608 switch (state->mode)
1610 case TRACE_TEXT_A: set_mode (state, TRACE_A); break;
1611 case TRACE_TEXT_B: set_mode (state, TRACE_B); break;
1612 case TRACE_FAIL: set_mode (state, state->def_mode); break;
1613 case KNOCK: set_mode (state, state->def_mode); break;
1614 case NMAP: set_mode (state, state->def_mode); break;
1615 default: abort(); break;
1623 if (state->start_reveal_back_p){
1624 set_mode (state, DRAIN_MATRIX);
1626 if (state->mode == MATRIX &&
1627 state->knock_knock_p &&
1628 (! (random() % 10000)))
1630 if (! (random() % 5))
1631 set_mode (state, DRAIN_NMAP);
1633 set_mode (state, DRAIN_KNOCK);
1636 redraw_cells (state, True);
1641 static int ndens = 0;
1642 static int tdens = 0;
1648 ((double) (state->grid_width * state->grid_height))));
1651 printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
1657 return state->delay;
1662 xmatrix_reshape (Display *dpy, Window window, void *closure,
1663 unsigned int w, unsigned int h)
1665 m_state *state = (m_state *) closure;
1666 int ow = state->grid_width;
1667 int oh = state->grid_height;
1668 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
1669 state->grid_width = state->xgwa.width / state->char_width;
1670 state->grid_height = state->xgwa.height / state->char_height;
1671 state->grid_width++;
1672 state->grid_height++;
1673 if (state->grid_width < 5) state->grid_width = 5;
1674 if (state->grid_height < 5) state->grid_height = 5;
1676 if (ow != state->grid_width ||
1677 oh != state->grid_height)
1679 m_cell *ncells = (m_cell *)
1680 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1681 m_cell *nbackground = (m_cell *)
1682 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1683 m_feeder *nfeeders = (m_feeder *)
1684 calloc (sizeof(m_feeder), state->grid_width);
1687 /* fprintf(stderr, "resize: %d x %d ==> %d x %d\n",
1688 ow, oh, state->grid_width, state->grid_height); */
1690 for (y = 0; y < oh; y++)
1691 for (x = 0; x < ow; x++)
1692 if (x < ow && x < state->grid_width &&
1693 y < oh && y < state->grid_height){
1694 ncells[y * state->grid_width + x] =
1695 state->cells[y * ow + x];
1696 nbackground[y * state->grid_width + x] =
1697 state->background[y * ow + x];
1699 free (state->cells);
1700 free (state->background);
1701 state->cells = ncells;
1702 state->background = nbackground;
1704 x = (ow < state->grid_width ? ow : state->grid_width);
1705 for (i = 0; i < x; i++)
1706 nfeeders[i] = state->feeders[i];
1707 free (state->feeders);
1708 state->feeders = nfeeders;
1711 textclient_reshape (state->tc,
1714 state->grid_width - 2,
1715 state->grid_height - 1,
1720 xmatrix_event (Display *dpy, Window window, void *closure, XEvent *event)
1722 m_state *state = (m_state *) closure;
1724 if (event->xany.type == KeyPress)
1728 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1732 /*set_mode (state, DRAIN_MATRIX);*/
1734 state->back_text_full_p = True;
1735 state->start_reveal_back_p = True;
1738 case '+': case '=': case '>': case '.':
1739 state->density += 10;
1740 if (state->density > 100)
1741 state->density = 100;
1746 case '-': case '_': case '<': case ',':
1747 state->density -= 10;
1748 if (state->density < 0)
1754 case '[': case '(': case '{':
1755 state->insert_top_p = True;
1756 state->insert_bottom_p = False;
1759 case ']': case ')': case '}':
1760 state->insert_top_p = False;
1761 state->insert_bottom_p = True;
1764 case '\\': case '|':
1765 state->insert_top_p = True;
1766 state->insert_bottom_p = True;
1770 set_mode (state, DRAIN_TRACE_A);
1774 set_mode (state, DRAIN_TRACE_B);
1778 set_mode (state, DRAIN_KNOCK);
1782 set_mode (state, DRAIN_NMAP);
1790 if (screenhack_event_helper (dpy, window, event))
1792 set_mode (state, DRAIN_MATRIX);
1800 xmatrix_free (Display *dpy, Window window, void *closure)
1802 m_state *state = (m_state *) closure;
1804 textclient_close (state->tc);
1805 if (state->cursor_timer)
1806 XtRemoveTimeOut (state->cursor_timer);
1808 /* #### there's more to free here */
1813 static const char *xmatrix_defaults [] = {
1814 ".background: black",
1815 ".foreground: #00AA00",
1817 "*matrixFont: large",
1821 "*tracePhone: (312) 555-0690",
1825 "*knockKnock: True",
1828 "*program: xscreensaver-text --latin1",
1829 "*geometry: 960x720",
1833 static XrmOptionDescRec xmatrix_options [] = {
1834 { "-small", ".matrixFont", XrmoptionNoArg, "Small" },
1835 { "-large", ".matrixFont", XrmoptionNoArg, "Large" },
1836 { "-delay", ".delay", XrmoptionSepArg, 0 },
1837 { "-insert", ".insert", XrmoptionSepArg, 0 },
1838 { "-top", ".insert", XrmoptionNoArg, "top" },
1839 { "-bottom", ".insert", XrmoptionNoArg, "bottom" },
1840 { "-both", ".insert", XrmoptionNoArg, "both" },
1841 { "-density", ".density", XrmoptionSepArg, 0 },
1842 { "-trace", ".trace", XrmoptionNoArg, "True" },
1843 { "-no-trace", ".trace", XrmoptionNoArg, "False" },
1844 { "-crack", ".mode", XrmoptionNoArg, "crack"},
1845 { "-phone", ".tracePhone", XrmoptionSepArg, 0 },
1846 { "-mode", ".mode", XrmoptionSepArg, 0 },
1847 { "-dna", ".mode", XrmoptionNoArg, "DNA" },
1848 { "-binary", ".mode", XrmoptionNoArg, "binary" },
1849 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal"},
1850 { "-decimal", ".mode", XrmoptionNoArg, "decimal"},
1851 { "-knock-knock", ".knockKnock", XrmoptionNoArg, "True" },
1852 { "-no-knock-knock", ".knockKnock", XrmoptionNoArg, "False" },
1853 { "-ascii", ".mode", XrmoptionNoArg, "ascii"},
1854 { "-pipe", ".usePipe", XrmoptionNoArg, "True" },
1855 { "-no-pipe", ".usePipe", XrmoptionNoArg, "False" },
1856 { "-program", ".program", XrmoptionSepArg, 0 },
1860 XSCREENSAVER_MODULE ("XMatrix", xmatrix)