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;
347 s->do_fill_buff = False;
348 s->buf_pos = (s->buf_pos + n);
349 if(s->buf_pos > BUF_SIZE){
350 /* copy to start of buffer */
351 /* areas shouldn't overlap, but just in case, use memmove */
352 memmove(s->buf,s->buf+BUF_SIZE,s->buf_pos-BUF_SIZE);
354 s->buf_pos = s->buf_pos % BUF_SIZE;
358 /* Couldn't read anything from the buffer */
359 /* Assume EOF has been reached, so start again */
360 s->do_fill_buff = True;
365 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
366 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
369 set_cursor_1 (m_state *state, Bool on)
371 Bool changed = (state->cursor_on != on);
372 state->cursor_on = on;
374 if (changed && state->cursor_x >= 0 && state->cursor_y >= 0)
376 m_cell *cell = &state->cells[state->grid_width * state->cursor_y
379 cell->changed = True;
386 set_cursor (m_state *state, Bool on)
388 if (set_cursor_1 (state, on))
390 if (state->cursor_timer)
391 XtRemoveTimeOut (state->cursor_timer);
392 state->cursor_timer = 0;
394 cursor_on_timer (state, 0);
399 cursor_off_timer (XtPointer closure, XtIntervalId *id)
401 m_state *state = (m_state *) closure;
402 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
403 set_cursor_1 (state, False);
404 state->cursor_timer = XtAppAddTimeOut (app, 333,
405 cursor_on_timer, closure);
409 cursor_on_timer (XtPointer closure, XtIntervalId *id)
411 m_state *state = (m_state *) closure;
412 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
413 set_cursor_1 (state, True);
414 state->cursor_timer = XtAppAddTimeOut (app, 666,
415 cursor_off_timer, closure);
420 init_spinners (m_state *state)
422 int i = state->nspinners;
426 for (y = 0; y < state->grid_height; y++)
427 for (x = 0; x < state->grid_width; x++)
429 cell = &state->cells[state->grid_width * y + x];
435 x = random() % state->grid_width;
436 y = random() % state->grid_height;
437 cell = &state->cells[state->grid_width * y + x];
444 clear_spinners (m_state *state)
447 for (i = 0; i < state->grid_width * state->grid_height; i++)
448 if (state->cells[i].spinner)
450 state->cells[i].spinner = 0;
451 state->cells[i].changed = 1;
456 static void set_mode (m_state *, m_mode);
460 init_trace (m_state *state)
462 char *s = get_string_resource (state->dpy, "tracePhone", "TracePhone");
468 state->tracing = (signed char *) malloc (strlen (s) + 1);
469 s3 = (char *) state->tracing;
471 for (s2 = s; *s2; s2++)
472 if (*s2 >= '0' && *s2 <= '9')
476 if (s3 == (char *) state->tracing)
479 for (i = 0; i < strlen((char *) state->tracing); i++)
480 state->tracing[i] = -state->tracing[i];
482 state->glyph_map = decimal_encoding;
483 state->nglyphs = countof(decimal_encoding);
488 fprintf (stderr, "%s: bad phone number: \"%s\".\n",
489 progname, s ? s : "(null)");
492 if (state->tracing) free (state->tracing);
494 set_mode (state, MATRIX);
499 init_drain (m_state *state)
503 set_cursor (state, False);
504 state->cursor_x = -1;
505 state->cursor_y = -1;
507 /* Fill the top row with empty top-feeders, to clear the screen. */
508 for (i = 0; i < state->grid_width; i++)
510 m_feeder *f = &state->feeders[i];
516 /* Turn off all the spinners, else they never go away. */
517 clear_spinners (state);
521 screen_blank_p (m_state *state)
524 for (i = 0; i < state->grid_width * state->grid_height; i++)
525 if (state->cells[i].glyph)
532 set_mode (m_state *state, m_mode mode)
534 if (mode == state->mode)
543 state->glyph_map = matrix_encoding;
544 state->nglyphs = countof(matrix_encoding);
545 flip_images (state, True);
546 init_spinners (state);
549 state->glyph_map = dna_encoding;
550 state->nglyphs = countof(dna_encoding);
551 flip_images (state, False);
554 state->glyph_map = binary_encoding;
555 state->nglyphs = countof(binary_encoding);
556 flip_images (state, False);
559 state->glyph_map = hex_encoding;
560 state->nglyphs = countof(hex_encoding);
561 flip_images (state, False);
564 state->glyph_map = ascii_encoding;
565 state->nglyphs = countof(ascii_encoding);
566 flip_images (state, False);
573 state->glyph_map = decimal_encoding;
574 state->nglyphs = countof(decimal_encoding);
575 flip_images (state, False);
579 flip_images (state, False);
599 xmatrix_init (Display *dpy, Window window)
604 m_state *state = (m_state *) calloc (sizeof(*state), 1);
607 state->window = window;
608 state->delay = get_integer_resource (dpy, "delay", "Integer");
610 XGetWindowAttributes (dpy, window, &state->xgwa);
612 state->small_p = (state->xgwa.width < 300);
614 const char *s = get_string_resource (dpy, "matrixFont", "String");
615 if (!s || !*s || !strcasecmp(s, "large"))
616 state->small_p = False;
617 else if (!strcasecmp(s, "small"))
618 state->small_p = True;
620 fprintf (stderr, "%s: matrixFont should be 'small' or 'large' not '%s'\n",
624 load_images (dpy, state);
626 gcv.foreground = get_pixel_resource(state->dpy, state->xgwa.colormap,
627 "foreground", "Foreground");
628 gcv.background = get_pixel_resource(state->dpy, state->xgwa.colormap,
629 "background", "Background");
630 state->draw_gc = XCreateGC (state->dpy, state->window,
631 GCForeground|GCBackground, &gcv);
632 gcv.foreground = gcv.background;
633 state->erase_gc = XCreateGC (state->dpy, state->window,
634 GCForeground|GCBackground, &gcv);
636 state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv);
638 /* Allocate colors for SYSTEM FAILURE box */
640 XColor boxcolors[] = {
641 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
642 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
643 { 0, 0xE0E0, 0xF7F7, 0xE0E0, DoRed|DoGreen|DoBlue, 0 },
644 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
645 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
647 for (i = 0; i < countof(boxcolors); i++)
649 if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i]))
650 state->colors[i] = boxcolors[i].pixel;
652 state->colors[i] = gcv.foreground; /* default black */
656 state->char_width = state->image_width / CHAR_COLS;
657 state->char_height = state->image_height / CHAR_ROWS;
659 state->grid_width = state->xgwa.width / state->char_width;
660 state->grid_height = state->xgwa.height / state->char_height;
662 state->grid_height++;
663 if (state->grid_width < 5) state->grid_width = 5;
664 if (state->grid_height < 5) state->grid_height = 5;
666 state->glyph_map = matrix_encoding;
667 state->nglyphs = countof(matrix_encoding);
669 state->cells = (m_cell *)
670 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
671 state->background = (m_cell *)
672 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
673 state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
675 state->density = get_integer_resource (dpy, "density", "Integer");
677 insert = get_string_resource(dpy, "insert", "Insert");
678 if (insert && !strcmp(insert, "top"))
680 state->insert_top_p = True;
681 state->insert_bottom_p = False;
683 else if (insert && !strcmp(insert, "bottom"))
685 state->insert_top_p = False;
686 state->insert_bottom_p = True;
688 else if (insert && !strcmp(insert, "both"))
690 state->insert_top_p = True;
691 state->insert_bottom_p = True;
695 if (insert && *insert)
697 "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
699 state->insert_top_p = False;
700 state->insert_bottom_p = True;
703 state->nspinners = get_integer_resource (dpy, "spinners", "Integer");
708 state->knock_knock_p = get_boolean_resource (dpy, "knockKnock", "KnockKnock");
710 state->use_pipe_p = get_boolean_resource (dpy, "usePipe", "Boolean");
712 state->buf[0] = ' '; /* spacer byte in buffer (space) */
714 state->do_fill_buff = True;
715 state->start_reveal_back_p = False;
716 state->back_text_full_p = False;
721 state->def_mode = MATRIX;
722 mode = get_string_resource (dpy, "mode", "Mode");
723 if (mode && !strcasecmp(mode, "trace"))
724 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
725 else if (mode && !strcasecmp(mode, "crack"))
726 set_mode (state, DRAIN_NMAP);
727 else if (mode && !strcasecmp(mode, "dna")){
728 set_mode (state, DNA);
729 state->def_mode = DNA;
731 else if (mode && (!strcasecmp(mode, "bin") ||
732 !strcasecmp(mode, "binary"))){
733 set_mode (state, BINARY);
734 state->def_mode = BINARY;
736 else if (mode && (!strcasecmp(mode, "hex") ||
737 !strcasecmp(mode, "hexadecimal"))){
738 set_mode (state, HEX);
739 state->def_mode = HEX;
741 else if (mode && (!strcasecmp(mode, "dec") ||
742 !strcasecmp(mode, "decimal"))){
743 set_mode (state, DEC);
744 state->def_mode = DEC;
746 else if (mode && (!strcasecmp(mode, "asc") ||
747 !strcasecmp(mode, "ascii"))){
748 set_mode (state, ASCII);
749 state->def_mode = ASCII;
751 else if (mode && !strcasecmp(mode, "pipe"))
753 set_mode (state, ASCII);
754 state->def_mode = ASCII;
755 state->use_pipe_p = True;
756 state->tc = textclient_open (dpy);
758 else if (!mode || !*mode || !strcasecmp(mode, "matrix"))
759 set_mode (state, MATRIX);
762 fprintf (stderr, "%s: `mode' must be ",progname);
763 fprintf (stderr, "matrix, trace, dna, binary, ascii, hex, or pipe: ");
764 fprintf (stderr, "not `%s'\n", mode);
765 set_mode (state, MATRIX);
768 if (state->mode == MATRIX && get_boolean_resource (dpy, "trace", "Boolean"))
769 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
771 state->cursor_x = -1;
772 state->cursor_y = -1;
779 insert_glyph (m_state *state, int glyph, int x, int y)
781 Bool bottom_feeder_p = (y >= 0);
783 if (y >= state->grid_height)
788 to = &state->cells[state->grid_width * y + x];
792 for (y = state->grid_height-1; y > 0; y--)
794 from = &state->cells[state->grid_width * (y-1) + x];
795 to = &state->cells[state->grid_width * y + x];
796 to->glyph = from->glyph;
797 to->glow = from->glow;
800 to = &state->cells[x];
808 else if (bottom_feeder_p)
809 to->glow = 1 + (random() % (state->tracing ? 4 : 2));
816 place_back_char (m_state *state, char textc, int x, int y){
817 if((x>=0) && (y>=0) &&
818 (x < state->grid_width) && (y < state->grid_height)){
819 m_cell *celltmp = &state->background[state->grid_width * y + x];
820 celltmp -> glyph = char_map[(unsigned char)textc] + 1;
821 if(!celltmp->glyph || (celltmp->glyph == 3)){
822 celltmp -> glyph = char_map[32] + 1;
824 celltmp -> changed = 1;
829 place_back_text (m_state *state, char *text, int x, int y){
831 for(i=0; i<strlen(text); i++){
832 place_back_char(state, text[i], x+i, y);
837 place_back_pipe (m_state *state, char textc){
838 Bool new_line = False;
839 /* gringer pipe insert */
840 state->back_line[state->back_pos] = textc;
842 state->back_line[state->back_pos] = '\0';
845 else if ((state->back_pos > (state->grid_width - 4)) ||
846 (state->back_pos >= BUF_SIZE)){ /* off by 1? */
847 state->back_line[++state->back_pos] = '\0';
854 int startx = (state->grid_width >> 1) -
855 (strlen(state->back_line) >> 1);
856 place_back_text(state, state->back_line,
857 startx, state->back_y);
860 if(state->back_y >= (state->grid_height - 1)){
862 state->back_text_full_p = True;
863 state->start_reveal_back_p = True;
869 feed_matrix (m_state *state)
877 int L = strlen((char *) state->tracing);
881 for (i = 0; i < strlen((char *) state->tracing); i++)
882 if (state->tracing[i] > 0)
887 set_mode (state, TRACE_DONE);
888 state->typing_delay = 1000000;
893 i = 5 + (30 / (count+1)); /* how fast numbers are discovered */
895 if ((random() % i) == 0)
898 if (state->tracing[i] < 0)
899 state->tracing[i] = -state->tracing[i];
906 if ((random() % 40) == 0)
908 set_mode (state, TRACE_FAIL);
913 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
926 if((state->use_pipe_p) && (!state->back_text_full_p)){
927 place_back_pipe(state, state->buf[state->buf_done]);
928 state->buf_done = (state->buf_done + 1) % BUF_SIZE;
929 if(state->buf_done == (state->buf_pos - 1)){
930 state->do_fill_buff = True;
933 if(state->buf_done == (state->buf_pos + 1)){
934 state->do_fill_buff = False;
937 state->do_fill_buff = True;
941 /* Update according to current feeders. */
942 for (x = 0; x < state->grid_width; x++)
944 m_feeder *f = &state->feeders[x];
946 if (f->throttle) /* this is a delay tick, synced to frame. */
950 else if (f->remaining > 0) /* how many items are in the pipe */
954 if((state->use_pipe_p) && (!state->back_text_full_p)){
955 rval = (int) state->buf[f->pipe_loc];
956 if(++f->pipe_loc > (BUF_SIZE-1)){
958 /*fill_input(state);*/
960 rval = (rval % state->nglyphs);
963 rval = (random() % state->nglyphs);
965 g = state->glyph_map[rval] + 1;
966 insert_glyph (state, g, x, f->y);
968 if (f->y >= 0) /* bottom_feeder_p */
971 else /* if pipe is empty, insert spaces */
973 insert_glyph (state, 0, x, f->y);
974 if (f->y >= 0) /* bottom_feeder_p */
978 if ((random() % 10) == 0) /* randomly change throttle speed */
980 f->throttle = ((random() % 5) + (random() % 5));
987 redraw_cells (m_state *state, Bool active)
991 Bool use_back_p = False;
993 for (y = 0; y < state->grid_height; y++)
994 for (x = 0; x < state->grid_width; x++)
996 m_cell *cell = &state->cells[state->grid_width * y + x];
997 m_cell *back = &state->background[state->grid_width * y + x];
998 Bool cursor_p = (state->cursor_on &&
999 x == state->cursor_x &&
1000 y == state->cursor_y);
1005 if((state->start_reveal_back_p) &&
1006 (back->glyph) && !(state->mode == TRACE_A ||
1007 state->mode == TRACE_B ||
1008 state->mode == TRACE_DONE)){
1014 /* In trace-mode, the state of each cell is random unless we have
1015 a match for this digit. */
1016 if (active && (state->mode == TRACE_A ||
1017 state->mode == TRACE_B ||
1018 state->mode == TRACE_DONE))
1020 int xx = x % strlen((char *) state->tracing);
1021 Bool dead_p = state->tracing[xx] > 0;
1023 if (y == 0 && x == xx && !use_back_p)
1024 cell->glyph = (dead_p
1025 ? state->glyph_map[state->tracing[xx]-'0'] + 1
1027 else if (y == 0 && !use_back_p)
1029 else if (!use_back_p)
1030 cell->glyph = (dead_p ? 0 :
1031 (state->glyph_map[(random()%state->nglyphs)]
1041 if (cell->glyph == 0 && !cursor_p && !use_back_p)
1042 XFillRectangle (state->dpy, state->window, state->erase_gc,
1043 x * state->char_width,
1044 y * state->char_height,
1046 state->char_height);
1049 int g = (cursor_p ? CURSOR_GLYPH : cell->glyph);
1050 int cx = (g - 1) % CHAR_COLS;
1051 int cy = (g - 1) / CHAR_COLS;
1052 int map = ((cell->glow != 0 || cell->spinner) ? GLOW_MAP :
1055 XCopyArea (state->dpy, state->images[map],
1056 state->window, state->draw_gc,
1057 cx * state->char_width,
1058 cy * state->char_height,
1061 x * state->char_width,
1062 y * state->char_height);
1067 if (cell->glow > 0 && state->mode != NMAP && !use_back_p)
1072 else if (cell->glow < 0)
1075 if (cell->spinner && active && !use_back_p)
1077 cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1);
1085 densitizer (m_state *state)
1087 /* Horrid kludge that converts percentages (density of screen coverage)
1088 to the parameter that actually controls this. I got this mapping
1089 empirically, on a 1024x768 screen. Sue me. */
1090 if (state->density < 10) return 85;
1091 else if (state->density < 15) return 60;
1092 else if (state->density < 20) return 45;
1093 else if (state->density < 25) return 25;
1094 else if (state->density < 30) return 20;
1095 else if (state->density < 35) return 15;
1096 else if (state->density < 45) return 10;
1097 else if (state->density < 50) return 8;
1098 else if (state->density < 55) return 7;
1099 else if (state->density < 65) return 5;
1100 else if (state->density < 80) return 3;
1101 else if (state->density < 90) return 2;
1107 hack_text (m_state *state)
1111 set_cursor (state, False);
1112 state->cursor_x = 0;
1113 state->cursor_y = 0;
1114 state->typing_scroll_p = False;
1115 state->typing_bold_p = False;
1116 state->typing_cursor_p = True;
1117 state->typing_stutter_p = False;
1118 state->typing_char_delay = 10000;
1119 state->typing_line_delay = 1500000;
1121 switch (state->mode)
1125 clear_spinners (state);
1126 if (state->mode == TRACE_TEXT_A)
1128 if (state->grid_width >= 52)
1130 ("Call trans opt: received. 2-19-98 13:24:18 REC:Log>\n"
1131 "Trace program: running\n");
1134 ("Call trans opt: received.\n2-19-98 13:24:18 REC:Log>\n"
1135 "Trace program: running\n");
1139 if (state->grid_width >= 52)
1141 ("Call trans opt: received. 9-18-99 14:32:21 REC:Log>\n"
1142 "WARNING: carrier anomaly\n"
1143 "Trace program: running\n");
1146 ("Call trans opt: received.\n9-18-99 14:32:21 REC:Log>\n"
1147 "WARNING: carrier anomaly\n"
1148 "Trace program: running\n");
1154 const char *s = "SYSTEM FAILURE\n";
1156 float cx = (state->grid_width - strlen(s) - 1) / 2 - 0.5;
1157 float cy = (state->grid_height / 2) - 1.3;
1162 XFillRectangle (state->dpy, state->window, state->erase_gc,
1163 cx * state->char_width,
1164 cy * state->char_height,
1165 strlen(s) * state->char_width,
1166 state->char_height * 1.6);
1168 for (i = -2; i < 3; i++)
1171 gcv.foreground = state->colors[i + 2];
1172 XChangeGC (state->dpy, state->scratch_gc, GCForeground, &gcv);
1173 XDrawRectangle (state->dpy, state->window, state->scratch_gc,
1174 cx * state->char_width - i,
1175 cy * state->char_height - i,
1176 strlen(s) * state->char_width + (2 * i),
1177 (state->char_height * 1.6) + (2 * i));
1180 /* If we don't clear these, part of the box may get overwritten */
1181 for (i = 0; i < state->grid_height * state->grid_width; i++)
1183 m_cell *cell = &state->cells[i];
1187 state->cursor_x = (state->grid_width - strlen(s) - 1) / 2;
1188 state->cursor_y = (state->grid_height / 2) - 1;
1189 if (state->cursor_x < 0) state->cursor_x = 0;
1190 if (state->cursor_y < 0) state->cursor_y = 0;
1193 state->typing_char_delay = 0;
1194 state->typing_cursor_p = False;
1200 clear_spinners (state);
1201 state->typing = ("\001Wake up, Neo...\n"
1202 "\001The Matrix has you...\n"
1203 "\001Follow the white rabbit.\n"
1205 "Knock, knock, Neo.\n");
1207 state->cursor_x = 4;
1208 state->cursor_y = 2;
1209 state->typing_char_delay = 0;
1210 state->typing_line_delay = 2000000;
1216 /* Note that what Trinity is using here is moderately accurate:
1217 She runs nmap (http://www.insecure.org/nmap/) then breaks in
1218 with a (hypothetical) program called "sshnuke" that exploits
1219 the (very real) SSHv1 CRC32 compensation attack detector bug
1220 (http://staff.washington.edu/dittrich/misc/ssh-analysis.txt).
1222 The command syntax of the power grid control software looks a
1223 lot like Cisco IOS to me. (IOS is a descendant of VMS.)
1226 clear_spinners (state);
1229 __extension__ /* don't warn about "string length is greater than
1230 the length ISO C89 compilers are required to
1236 "\001nmap -v -sS -O 10.2.2.2\n"
1237 "Starting nmap V. 2.54BETA25\n"
1238 "\010\010\010\010\010\010\010\010\010\010"
1239 "Insufficient responses for TCP sequencing (3), OS detection"
1240 " may be less accurate\n"
1241 "Interesting ports on 10.2.2.2:\n"
1242 "(The 1539 ports scanned but not shown below are in state:"
1244 "Port state service\n"
1247 "No exact OS matches for host\n"
1249 "Nmap run completed -- 1 IP address (1 host up) scanned\n"
1253 "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n"
1254 "Connecting to 10.2.2.2:ssh ... "
1257 "Attempting to exploit SSHv1 CRC32 ... "
1260 "Resetting root password to \"Z1ON0101\".\n"
1262 "System open: Access Level <9>\n"
1267 "\001ssh 10.2.2.2 -l root\n"
1269 "root@10.2.2.2's password: "
1275 "\001disable grid nodes 21 - 48\n"
1277 "\002Warning: Disabling nodes 21-48 will disconnect sector 11"
1280 "\002 ARE YOU SURE? (y/n) "
1286 "\010\002Grid Node 21 offline...\n"
1287 "\010\002Grid Node 22 offline...\n"
1288 "\010\002Grid Node 23 offline...\n"
1289 "\010\002Grid Node 24 offline...\n"
1290 "\010\002Grid Node 25 offline...\n"
1291 "\010\002Grid Node 26 offline...\n"
1292 "\010\002Grid Node 27 offline...\n"
1293 "\010\002Grid Node 28 offline...\n"
1294 "\010\002Grid Node 29 offline...\n"
1295 "\010\002Grid Node 30 offline...\n"
1296 "\010\002Grid Node 31 offline...\n"
1297 "\010\002Grid Node 32 offline...\n"
1298 "\010\002Grid Node 33 offline...\n"
1299 "\010\002Grid Node 34 offline...\n"
1300 "\010\002Grid Node 35 offline...\n"
1301 "\010\002Grid Node 36 offline...\n"
1302 "\010\002Grid Node 37 offline...\n"
1303 "\010\002Grid Node 38 offline...\n"
1304 "\010\002Grid Node 39 offline...\n"
1305 "\010\002Grid Node 40 offline...\n"
1306 "\010\002Grid Node 41 offline...\n"
1307 "\010\002Grid Node 42 offline...\n"
1308 "\010\002Grid Node 43 offline...\n"
1309 "\010\002Grid Node 44 offline...\n"
1310 "\010\002Grid Node 45 offline...\n"
1311 "\010\002Grid Node 46 offline...\n"
1312 "\010\002Grid Node 47 offline...\n"
1313 "\010\002Grid Node 48 offline...\n"
1316 "\010\010\010\010\010\010\010\010"
1319 state->cursor_x = 0;
1320 state->cursor_y = state->grid_height - 3;
1321 state->typing_scroll_p = True;
1322 state->typing_char_delay = 0;
1323 state->typing_line_delay = 20000;
1332 state->typing_left_margin = state->cursor_x;
1333 state->typing_delay = state->typing_char_delay;
1334 if (state->typing_cursor_p)
1335 set_cursor (state, True);
1339 Bool scrolled_p = False;
1340 unsigned char c, c1;
1341 int x = state->cursor_x;
1342 int y = state->cursor_y;
1345 c = ((unsigned char *) state->typing)[0];
1346 c1 = c ? ((unsigned char *) state->typing)[1] : 0;
1348 state->typing_delay = (!c || c1 == '\n'
1349 ? state->typing_line_delay
1350 : state->typing_char_delay);
1353 state->typing_delay = 0;
1358 if (state->typing_scroll_p &&
1360 x >= state->grid_width - 1))
1362 set_cursor (state, False);
1366 if (y >= state->grid_height-1)
1369 for (yy = 0; yy < state->grid_height-2; yy++)
1370 for (xx = 0; xx < state->grid_width; xx++)
1372 int ii = yy * state->grid_width + xx;
1373 int jj = (yy+1) * state->grid_width + xx;
1374 state->cells[ii] = state->cells[jj];
1375 state->cells[ii].changed = 1;
1377 /* clear bottom row */
1378 for (xx = 0; xx < state->grid_width; xx++)
1380 int ii = yy * state->grid_width + xx;
1381 state->cells[ii].glyph = 0;
1382 state->cells[ii].changed = 1;
1384 y--; /* move back up to bottom line */
1391 if (!state->typing_scroll_p)
1394 set_cursor (state, False);
1395 x = state->typing_left_margin;
1397 /* clear the line */
1398 i = state->grid_width * y;
1399 j = i + state->grid_width;
1402 state->cells[i].glyph = 0;
1403 state->cells[i].changed = 1;
1406 state->typing_bold_p = False;
1407 state->typing_stutter_p = False;
1411 else if (c == '\010')
1412 state->typing_delay += 500000;
1414 else if (c == '\001')
1416 state->typing_stutter_p = True;
1417 state->typing_bold_p = False;
1419 else if (c == '\002')
1420 state->typing_bold_p = True;
1422 else if (x < state->grid_width-1)
1424 m_cell *cell = &state->cells[state->grid_width * y + x];
1425 cell->glyph = char_map[c] + 1;
1426 if (c == ' ' || c == '\t') cell->glyph = 0;
1428 cell->glow = (state->typing_bold_p ? 127 : 0);
1434 if (x >= state->grid_width-1)
1435 x = state->grid_width-1;
1439 if (state->typing_stutter_p)
1441 if (state->typing_delay == 0)
1442 state->typing_delay = 20000;
1444 state->typing_delay += (0xFFFFFF & ((random() % 200000) + 1));
1447 /* If there's no delay after this character, just keep going. */
1448 if (state->typing_delay == 0)
1451 if (scrolled_p || x != state->cursor_x || y != state->cursor_y)
1453 set_cursor (state, False);
1454 state->cursor_x = x;
1455 state->cursor_y = y;
1456 if (state->typing_cursor_p)
1457 set_cursor (state, True);
1464 hack_matrix (m_state *state)
1468 switch (state->mode)
1470 case TRACE_DONE: case TRACE_FAIL:
1472 case TRACE_A: case TRACE_B:
1473 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1479 /* Glow some characters. */
1480 if (!state->insert_bottom_p)
1482 int i = random() % (state->grid_width / 2);
1485 int yy = random() % state->grid_height;
1486 int xx = random() % state->grid_width;
1487 m_cell *cell = &state->cells[state->grid_width * yy + xx];
1488 if (cell->glyph && cell->glow == 0)
1490 cell->glow = random() % 10;
1496 /* Change some of the feeders. */
1497 for (x = 0; x < state->grid_width; x++)
1499 m_feeder *f = &state->feeders[x];
1500 Bool bottom_feeder_p;
1502 if (f->remaining > 0) /* never change if pipe isn't empty */
1505 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
1508 f->remaining = 3 + (random() % state->grid_height);
1509 f->throttle = ((random() % 5) + (random() % 5));
1511 if ((random() % 4) != 0)
1514 if (state->mode == TRACE_A || state->mode == TRACE_B)
1515 bottom_feeder_p = True;
1516 else if (state->insert_top_p && state->insert_bottom_p)
1517 bottom_feeder_p = (random() & 1);
1519 bottom_feeder_p = state->insert_bottom_p;
1521 if (bottom_feeder_p)
1522 f->y = random() % (state->grid_height / 2);
1527 if (state->mode == MATRIX && (! (random() % 500)))
1528 init_spinners (state);
1532 static unsigned long
1533 xmatrix_draw (Display *dpy, Window window, void *closure)
1535 m_state *state = (m_state *) closure;
1537 if (state->typing_delay > 0)
1539 state->typing_delay -= state->delay;
1540 if (state->typing_delay < 0)
1541 state->typing_delay = 0;
1542 redraw_cells (state, False);
1543 return state->delay;
1546 switch (state->mode)
1548 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
1549 case TRACE_A: case TRACE_B:
1550 feed_matrix (state);
1551 hack_matrix (state);
1559 feed_matrix (state);
1560 if (screen_blank_p (state))
1562 state->typing_delay = 500000;
1563 if(state->start_reveal_back_p){
1566 state->typing_delay = 5000000;
1567 state->start_reveal_back_p = False;
1568 state->back_text_full_p = False;
1569 /* for loop to move background to foreground */
1570 for (y = 0; y < state->grid_height; y++){
1571 for (x = 0; x < state->grid_width; x++){
1572 to = &state->cells[state->grid_width * y + x];
1573 back = &state->background[state->grid_width * y + x];
1574 to->glyph = back->glyph;
1575 to->changed = back->changed;
1581 switch (state->mode)
1583 case DRAIN_TRACE_A: set_mode (state, TRACE_TEXT_A); break;
1584 case DRAIN_TRACE_B: set_mode (state, TRACE_TEXT_B); break;
1585 case DRAIN_KNOCK: set_mode (state, KNOCK); break;
1586 case DRAIN_NMAP: set_mode (state, NMAP); break;
1587 case DRAIN_MATRIX: set_mode (state, state->def_mode); break;
1588 default: abort(); break;
1594 set_mode (state, state->def_mode);
1604 if (! state->typing) /* done typing */
1606 set_cursor (state, False);
1607 switch (state->mode)
1609 case TRACE_TEXT_A: set_mode (state, TRACE_A); break;
1610 case TRACE_TEXT_B: set_mode (state, TRACE_B); break;
1611 case TRACE_FAIL: set_mode (state, state->def_mode); break;
1612 case KNOCK: set_mode (state, state->def_mode); break;
1613 case NMAP: set_mode (state, state->def_mode); break;
1614 default: abort(); break;
1622 if (state->start_reveal_back_p){
1623 set_mode (state, DRAIN_MATRIX);
1625 if (state->mode == MATRIX &&
1626 state->knock_knock_p &&
1627 (! (random() % 10000)))
1629 if (! (random() % 5))
1630 set_mode (state, DRAIN_NMAP);
1632 set_mode (state, DRAIN_KNOCK);
1635 redraw_cells (state, True);
1640 static int ndens = 0;
1641 static int tdens = 0;
1647 ((double) (state->grid_width * state->grid_height))));
1650 printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
1656 return state->delay;
1661 xmatrix_reshape (Display *dpy, Window window, void *closure,
1662 unsigned int w, unsigned int h)
1664 m_state *state = (m_state *) closure;
1665 int ow = state->grid_width;
1666 int oh = state->grid_height;
1667 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
1668 state->grid_width = state->xgwa.width / state->char_width;
1669 state->grid_height = state->xgwa.height / state->char_height;
1670 state->grid_width++;
1671 state->grid_height++;
1672 if (state->grid_width < 5) state->grid_width = 5;
1673 if (state->grid_height < 5) state->grid_height = 5;
1675 if (ow != state->grid_width ||
1676 oh != state->grid_height)
1678 m_cell *ncells = (m_cell *)
1679 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1680 m_cell *nbackground = (m_cell *)
1681 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
1682 m_feeder *nfeeders = (m_feeder *)
1683 calloc (sizeof(m_feeder), state->grid_width);
1686 /* fprintf(stderr, "resize: %d x %d ==> %d x %d\n",
1687 ow, oh, state->grid_width, state->grid_height); */
1689 for (y = 0; y < oh; y++)
1690 for (x = 0; x < ow; x++)
1691 if (x < ow && x < state->grid_width &&
1692 y < oh && y < state->grid_height){
1693 ncells[y * state->grid_width + x] =
1694 state->cells[y * ow + x];
1695 nbackground[y * state->grid_width + x] =
1696 state->background[y * ow + x];
1698 free (state->cells);
1699 free (state->background);
1700 state->cells = ncells;
1701 state->background = nbackground;
1703 x = (ow < state->grid_width ? ow : state->grid_width);
1704 for (i = 0; i < x; i++)
1705 nfeeders[i] = state->feeders[i];
1706 free (state->feeders);
1707 state->feeders = nfeeders;
1710 textclient_reshape (state->tc,
1713 state->grid_width - 2,
1714 state->grid_height - 1,
1719 xmatrix_event (Display *dpy, Window window, void *closure, XEvent *event)
1721 m_state *state = (m_state *) closure;
1723 if (event->xany.type == KeyPress)
1727 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1731 /*set_mode (state, DRAIN_MATRIX);*/
1733 state->back_text_full_p = True;
1734 state->start_reveal_back_p = True;
1737 case '+': case '=': case '>': case '.':
1738 state->density += 10;
1739 if (state->density > 100)
1740 state->density = 100;
1745 case '-': case '_': case '<': case ',':
1746 state->density -= 10;
1747 if (state->density < 0)
1753 case '[': case '(': case '{':
1754 state->insert_top_p = True;
1755 state->insert_bottom_p = False;
1758 case ']': case ')': case '}':
1759 state->insert_top_p = False;
1760 state->insert_bottom_p = True;
1763 case '\\': case '|':
1764 state->insert_top_p = True;
1765 state->insert_bottom_p = True;
1769 set_mode (state, DRAIN_TRACE_A);
1773 set_mode (state, DRAIN_TRACE_B);
1777 set_mode (state, DRAIN_KNOCK);
1781 set_mode (state, DRAIN_NMAP);
1789 if (screenhack_event_helper (dpy, window, event))
1791 set_mode (state, DRAIN_MATRIX);
1799 xmatrix_free (Display *dpy, Window window, void *closure)
1801 m_state *state = (m_state *) closure;
1803 textclient_close (state->tc);
1804 if (state->cursor_timer)
1805 XtRemoveTimeOut (state->cursor_timer);
1807 /* #### there's more to free here */
1812 static const char *xmatrix_defaults [] = {
1813 ".background: black",
1814 ".foreground: #00AA00",
1816 "*matrixFont: large",
1820 "*tracePhone: (312) 555-0690",
1824 "*knockKnock: True",
1827 "*program: xscreensaver-text",
1828 "*geometry: 960x720",
1832 static XrmOptionDescRec xmatrix_options [] = {
1833 { "-small", ".matrixFont", XrmoptionNoArg, "Small" },
1834 { "-large", ".matrixFont", XrmoptionNoArg, "Large" },
1835 { "-delay", ".delay", XrmoptionSepArg, 0 },
1836 { "-insert", ".insert", XrmoptionSepArg, 0 },
1837 { "-top", ".insert", XrmoptionNoArg, "top" },
1838 { "-bottom", ".insert", XrmoptionNoArg, "bottom" },
1839 { "-both", ".insert", XrmoptionNoArg, "both" },
1840 { "-density", ".density", XrmoptionSepArg, 0 },
1841 { "-trace", ".trace", XrmoptionNoArg, "True" },
1842 { "-no-trace", ".trace", XrmoptionNoArg, "False" },
1843 { "-crack", ".mode", XrmoptionNoArg, "crack"},
1844 { "-phone", ".tracePhone", XrmoptionSepArg, 0 },
1845 { "-mode", ".mode", XrmoptionSepArg, 0 },
1846 { "-dna", ".mode", XrmoptionNoArg, "DNA" },
1847 { "-binary", ".mode", XrmoptionNoArg, "binary" },
1848 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal"},
1849 { "-decimal", ".mode", XrmoptionNoArg, "decimal"},
1850 { "-knock-knock", ".knockKnock", XrmoptionNoArg, "True" },
1851 { "-no-knock-knock", ".knockKnock", XrmoptionNoArg, "False" },
1852 { "-ascii", ".mode", XrmoptionNoArg, "ascii"},
1853 { "-pipe", ".usePipe", XrmoptionNoArg, "True" },
1854 { "-no-pipe", ".usePipe", XrmoptionNoArg, "False" },
1855 { "-program", ".program", XrmoptionSepArg, 0 },
1859 XSCREENSAVER_MODULE ("XMatrix", xmatrix)