1 /* xscreensaver, Copyright (c) 1999-2015 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Matrix -- simulate the text scrolls from the movie "The Matrix".
13 * The movie people distribute their own Windows/Mac screensaver that does
14 * a similar thing, so I wrote one for Unix. However, that version (the
15 * Windows/Mac version at http://www.whatisthematrix.com/) doesn't match
16 * what the computer screens in the movie looked like, so my `xmatrix' does
19 * See also my `glmatrix' program, which does a 3D rendering of the similar
20 * effect that appeared in the title sequence of the movies.
23 * ==========================================================
27 * People just love to hack on this one. I get sent
28 * patches to this all the time saying, ``here, I made
29 * it better!'' Mostly this hasn't been true.
31 * If you've made changes to xmatrix, when you send me
32 * your patch, please explain, in English, both *what*
33 * your changes are, and *why* you think those changes
34 * make this screensaver behave more like the displays
35 * in the movie did. I'd rather not have to read your
36 * diffs to try and figure that out for myself...
38 * In particular, note that the characters in the movie
39 * were, in fact, low resolution and somewhat blurry/
40 * washed out. They also definitely scrolled a
41 * character at a time, not a pixel at a time.
43 * And keep in mind that this program emulates the
44 * behavior of the computer screens that were visible
45 * in the movies -- not the behavior of the effects in
46 * the title sequences. "GLMatrix" does that.
48 * ==========================================================
56 #include "screenhack.h"
57 #include "textclient.h"
58 #include "xpm-pixmap.h"
65 # define DO_XBM /* only do mono bitmaps under real X11 */
69 # include <X11/Intrinsic.h>
72 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
73 # include "images/matrix1.xpm"
74 # include "images/matrix2.xpm"
75 # include "images/matrix1b.xpm"
76 # include "images/matrix2b.xpm"
80 # include "images/matrix1.xbm"
81 # include "images/matrix2.xbm"
82 # include "images/matrix1b.xbm"
83 # include "images/matrix2b.xbm"
94 unsigned int glyph : 9; /* note: 9 bit characters! */
95 unsigned int changed : 1;
96 unsigned int spinner : 1;
106 #define countof(x) (sizeof(x)/sizeof(*(x)))
108 static const int matrix_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
109 192, 193, 194, 195, 196, 197, 198, 199,
110 200, 201, 202, 203, 204, 205, 206, 207 };
111 static const int decimal_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25};
112 static const int hex_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
113 33, 34, 35, 36, 37, 38 };
114 static const int binary_encoding[] = { 16, 17 };
115 static const int dna_encoding[] = { 33, 35, 39, 52 };
116 static const int ascii_encoding[] = {
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
120 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
121 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
122 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
123 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
124 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
125 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
126 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
128 static const unsigned char char_map[256] = {
129 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0 */
130 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 16 */
131 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */
132 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */
133 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */
134 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */
135 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */
136 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */
137 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 128 */
138 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 144 */
139 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */
140 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */
141 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */
142 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */
143 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */
144 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */
147 #define CURSOR_GLYPH 97
149 /* larger numbers should mean more variability between columns */
152 typedef enum { DRAIN_TRACE_A,
153 TRACE_TEXT_A, /* Call trans opt: received. */
154 TRACE_A, /* (31_) 5__-0_9_ */
158 TRACE_TEXT_B, /* Call trans opt: received. */
160 TRACE_FAIL, /* System Failure */
163 KNOCK, /* Wake up, Neo... */
166 NMAP, /* Starting nmap V. 2.54BETA25 */
179 XWindowAttributes xgwa;
180 GC draw_gc, erase_gc, scratch_gc;
181 int grid_width, grid_height;
182 int char_width, char_height;
189 Bool insert_top_p, insert_bottom_p;
192 m_mode def_mode; /* Mode to return to after trace etc. */
195 char buf [BUF_SIZE*2+1]; /* ring buffer */
199 Bool start_reveal_back_p; /* start reveal process for pipe */
200 Bool back_text_full_p; /* is the pipe buffer (background) full ? */
201 char back_line [BUF_SIZE*2+1]; /* line buffer for background */
202 int back_pos; /* background line buffer position */
205 signed char *tracing;
209 Bool typing_scroll_p;
210 Bool typing_cursor_p;
212 Bool typing_stutter_p;
213 int typing_left_margin;
214 int typing_char_delay;
215 int typing_line_delay;
219 int cursor_x, cursor_y;
220 XtIntervalId cursor_timer;
222 Pixmap images[CHAR_MAPS];
223 int image_width, image_height;
224 Bool images_flipped_p;
227 const int *glyph_map;
229 unsigned long colors[5];
235 load_images_1 (Display *dpy, m_state *state, int which)
237 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
238 if (!get_boolean_resource (dpy, "mono", "Boolean") &&
239 state->xgwa.depth > 1)
242 (which == 1 ? (state->small_p ? matrix1b_xpm : matrix1_xpm) :
243 (state->small_p ? matrix2b_xpm : matrix2_xpm));
245 state->images[which] =
246 xpm_data_to_pixmap (state->dpy, state->window, bits,
247 &state->image_width, &state->image_height, 0);
250 #endif /* !HAVE_XPM && !HAVE_GDK_PIXBUF */
253 unsigned long fg, bg;
254 state->image_width = (state->small_p ? matrix1b_width :matrix1_width);
255 state->image_height = (state->small_p ? matrix1b_height:matrix1_height);
256 fg = get_pixel_resource(state->dpy, state->xgwa.colormap,
257 "foreground", "Foreground");
258 bg = get_pixel_resource(state->dpy, state->xgwa.colormap,
259 "background", "Background");
260 state->images[which] =
261 XCreatePixmapFromBitmapData (state->dpy, state->window, (char *)
262 (which == 1 ? (state->small_p ? matrix1b_bits :matrix1_bits) :
263 (state->small_p ? matrix2b_bits :matrix2_bits)),
264 state->image_width, state->image_height,
265 bg, fg, state->xgwa.depth);
274 load_images (Display *dpy, m_state *state)
276 load_images_1 (dpy, state, 1);
277 load_images_1 (dpy, state, 2);
282 flip_images_1 (m_state *state, int which)
284 XImage *im = XGetImage (state->dpy, state->images[which], 0, 0,
285 state->image_width, state->image_height,
286 ~0L, (state->xgwa.depth > 1 ? ZPixmap : XYPixmap));
288 int ww = state->char_width;
289 unsigned long *row = (unsigned long *) malloc (sizeof(*row) * ww);
291 for (y = 0; y < state->image_height; y++)
293 for (x = 0; x < CHAR_COLS; x++)
295 for (xx = 0; xx < ww; xx++)
296 row[xx] = XGetPixel (im, (x * ww) + xx, y);
297 for (xx = 0; xx < ww; xx++)
298 XPutPixel (im, (x * ww) + xx, y, row[ww - xx - 1]);
302 XPutImage (state->dpy, state->images[which], state->draw_gc, im, 0, 0, 0, 0,
303 state->image_width, state->image_height);
309 flip_images (m_state *state, Bool flipped_p)
311 if (flipped_p != state->images_flipped_p)
313 state->images_flipped_p = flipped_p;
314 flip_images_1 (state, 1);
315 flip_images_1 (state, 2);
320 /* When the subprocess has generated some output, this reads as much as it
321 can into s->buf at s->buf_tail.
324 fill_input (m_state *s)
328 if(s->buf_done > s->buf_pos){
329 loadBytes = (s->buf_done - s->buf_pos) - 1;
332 loadBytes = ((BUF_SIZE - s->buf_pos) + s->buf_done) - 1;
340 int c = textclient_getc (s->tc);
341 n = (c > 0 ? 1 : -1);
342 s->buf [s->buf_pos] = (char) c;
348 s->do_fill_buff = False;
349 s->buf_pos = (s->buf_pos + n);
350 if(s->buf_pos > BUF_SIZE){
351 /* copy to start of buffer */
352 /* areas shouldn't overlap, but just in case, use memmove */
353 memmove(s->buf,s->buf+BUF_SIZE,s->buf_pos-BUF_SIZE);
355 s->buf_pos = s->buf_pos % BUF_SIZE;
359 /* Couldn't read anything from the buffer */
360 /* Assume EOF has been reached, so start again */
361 s->do_fill_buff = True;
366 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
367 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
370 set_cursor_1 (m_state *state, Bool on)
372 Bool changed = (state->cursor_on != on);
373 state->cursor_on = on;
375 if (changed && state->cursor_x >= 0 && state->cursor_y >= 0)
377 m_cell *cell = &state->cells[state->grid_width * state->cursor_y
380 cell->changed = True;
387 set_cursor (m_state *state, Bool on)
389 if (set_cursor_1 (state, on))
391 if (state->cursor_timer)
392 XtRemoveTimeOut (state->cursor_timer);
393 state->cursor_timer = 0;
395 cursor_on_timer (state, 0);
400 cursor_off_timer (XtPointer closure, XtIntervalId *id)
402 m_state *state = (m_state *) closure;
403 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
404 set_cursor_1 (state, False);
405 state->cursor_timer = XtAppAddTimeOut (app, 333,
406 cursor_on_timer, closure);
410 cursor_on_timer (XtPointer closure, XtIntervalId *id)
412 m_state *state = (m_state *) closure;
413 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
414 set_cursor_1 (state, True);
415 state->cursor_timer = XtAppAddTimeOut (app, 666,
416 cursor_off_timer, closure);
421 init_spinners (m_state *state)
423 int i = state->nspinners;
427 for (y = 0; y < state->grid_height; y++)
428 for (x = 0; x < state->grid_width; x++)
430 cell = &state->cells[state->grid_width * y + x];
436 x = random() % state->grid_width;
437 y = random() % state->grid_height;
438 cell = &state->cells[state->grid_width * y + x];
445 clear_spinners (m_state *state)
448 for (i = 0; i < state->grid_width * state->grid_height; i++)
449 if (state->cells[i].spinner)
451 state->cells[i].spinner = 0;
452 state->cells[i].changed = 1;
457 static void set_mode (m_state *, m_mode);
461 init_trace (m_state *state)
463 char *s = get_string_resource (state->dpy, "tracePhone", "TracePhone");
469 state->tracing = (signed char *) malloc (strlen (s) + 1);
472 for (s2 = s; *s2; s2++)
473 if (*s2 >= '0' && *s2 <= '9')
477 if (s3 == state->tracing)
480 state->glyph_map = decimal_encoding;
481 state->nglyphs = countof(decimal_encoding);
486 fprintf (stderr, "%s: bad phone number: \"%s\".\n",
487 progname, s ? s : "(null)");
490 if (state->tracing) free (state->tracing);
492 set_mode (state, MATRIX);
497 init_drain (m_state *state)
501 set_cursor (state, False);
502 state->cursor_x = -1;
503 state->cursor_y = -1;
505 /* Fill the top row with empty top-feeders, to clear the screen. */
506 for (i = 0; i < state->grid_width; i++)
508 m_feeder *f = &state->feeders[i];
514 /* Turn off all the spinners, else they never go away. */
515 clear_spinners (state);
519 screen_blank_p (m_state *state)
522 for (i = 0; i < state->grid_width * state->grid_height; i++)
523 if (state->cells[i].glyph)
530 set_mode (m_state *state, m_mode mode)
532 if (mode == state->mode)
541 state->glyph_map = matrix_encoding;
542 state->nglyphs = countof(matrix_encoding);
543 flip_images (state, True);
544 init_spinners (state);
547 state->glyph_map = dna_encoding;
548 state->nglyphs = countof(dna_encoding);
549 flip_images (state, False);
552 state->glyph_map = binary_encoding;
553 state->nglyphs = countof(binary_encoding);
554 flip_images (state, False);
557 state->glyph_map = hex_encoding;
558 state->nglyphs = countof(hex_encoding);
559 flip_images (state, False);
562 state->glyph_map = ascii_encoding;
563 state->nglyphs = countof(ascii_encoding);
564 flip_images (state, False);
571 state->glyph_map = decimal_encoding;
572 state->nglyphs = countof(decimal_encoding);
573 flip_images (state, False);
577 flip_images (state, False);
597 xmatrix_init (Display *dpy, Window window)
602 m_state *state = (m_state *) calloc (sizeof(*state), 1);
605 state->window = window;
606 state->delay = get_integer_resource (dpy, "delay", "Integer");
608 XGetWindowAttributes (dpy, window, &state->xgwa);
610 state->small_p = (state->xgwa.width < 300);
612 const char *s = get_string_resource (dpy, "matrixFont", "String");
613 if (!s || !*s || !strcasecmp(s, "large"))
614 state->small_p = False;
615 else if (!strcasecmp(s, "small"))
616 state->small_p = True;
618 fprintf (stderr, "%s: matrixFont should be 'small' or 'large' not '%s'\n",
622 load_images (dpy, state);
624 gcv.foreground = get_pixel_resource(state->dpy, state->xgwa.colormap,
625 "foreground", "Foreground");
626 gcv.background = get_pixel_resource(state->dpy, state->xgwa.colormap,
627 "background", "Background");
628 state->draw_gc = XCreateGC (state->dpy, state->window,
629 GCForeground|GCBackground, &gcv);
630 gcv.foreground = gcv.background;
631 state->erase_gc = XCreateGC (state->dpy, state->window,
632 GCForeground|GCBackground, &gcv);
634 state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv);
636 /* Allocate colors for SYSTEM FAILURE box */
638 XColor boxcolors[] = {
639 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
640 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
641 { 0, 0xE0E0, 0xF7F7, 0xE0E0, DoRed|DoGreen|DoBlue, 0 },
642 { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 },
643 { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 },
645 for (i = 0; i < countof(boxcolors); i++)
647 if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i]))
648 state->colors[i] = boxcolors[i].pixel;
650 state->colors[i] = gcv.foreground; /* default black */
654 state->char_width = state->image_width / CHAR_COLS;
655 state->char_height = state->image_height / CHAR_ROWS;
657 state->grid_width = state->xgwa.width / state->char_width;
658 state->grid_height = state->xgwa.height / state->char_height;
660 state->grid_height++;
661 if (state->grid_width < 5) state->grid_width = 5;
662 if (state->grid_height < 5) state->grid_height = 5;
664 state->glyph_map = matrix_encoding;
665 state->nglyphs = countof(matrix_encoding);
667 state->cells = (m_cell *)
668 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
669 state->background = (m_cell *)
670 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
671 state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
673 state->density = get_integer_resource (dpy, "density", "Integer");
675 insert = get_string_resource(dpy, "insert", "Insert");
676 if (insert && !strcmp(insert, "top"))
678 state->insert_top_p = True;
679 state->insert_bottom_p = False;
681 else if (insert && !strcmp(insert, "bottom"))
683 state->insert_top_p = False;
684 state->insert_bottom_p = True;
686 else if (insert && !strcmp(insert, "both"))
688 state->insert_top_p = True;
689 state->insert_bottom_p = True;
693 if (insert && *insert)
695 "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
697 state->insert_top_p = False;
698 state->insert_bottom_p = True;
701 state->nspinners = get_integer_resource (dpy, "spinners", "Integer");
706 state->knock_knock_p = get_boolean_resource (dpy, "knockKnock", "KnockKnock");
708 state->use_pipe_p = get_boolean_resource (dpy, "usePipe", "Boolean");
710 state->buf[0] = ' '; /* spacer byte in buffer (space) */
712 state->do_fill_buff = True;
713 state->start_reveal_back_p = False;
714 state->back_text_full_p = False;
719 state->def_mode = MATRIX;
720 mode = get_string_resource (dpy, "mode", "Mode");
721 if (mode && !strcasecmp(mode, "trace"))
722 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
723 else if (mode && !strcasecmp(mode, "crack"))
724 set_mode (state, DRAIN_NMAP);
725 else if (mode && !strcasecmp(mode, "dna")){
726 set_mode (state, DNA);
727 state->def_mode = DNA;
729 else if (mode && (!strcasecmp(mode, "bin") ||
730 !strcasecmp(mode, "binary"))){
731 set_mode (state, BINARY);
732 state->def_mode = BINARY;
734 else if (mode && (!strcasecmp(mode, "hex") ||
735 !strcasecmp(mode, "hexadecimal"))){
736 set_mode (state, HEX);
737 state->def_mode = HEX;
739 else if (mode && (!strcasecmp(mode, "dec") ||
740 !strcasecmp(mode, "decimal"))){
741 set_mode (state, DEC);
742 state->def_mode = DEC;
744 else if (mode && (!strcasecmp(mode, "asc") ||
745 !strcasecmp(mode, "ascii"))){
746 set_mode (state, ASCII);
747 state->def_mode = ASCII;
749 else if (mode && !strcasecmp(mode, "pipe"))
751 set_mode (state, ASCII);
752 state->def_mode = ASCII;
753 state->use_pipe_p = True;
754 state->tc = textclient_open (dpy);
756 else if (!mode || !*mode || !strcasecmp(mode, "matrix"))
757 set_mode (state, MATRIX);
760 fprintf (stderr, "%s: `mode' must be ",progname);
761 fprintf (stderr, "matrix, trace, dna, binary, ascii, hex, or pipe: ");
762 fprintf (stderr, "not `%s'\n", mode);
763 set_mode (state, MATRIX);
766 if (state->mode == MATRIX && get_boolean_resource (dpy, "trace", "Boolean"))
767 set_mode (state, ((random() % 3) ? TRACE_TEXT_A : TRACE_TEXT_B));
769 state->cursor_x = -1;
770 state->cursor_y = -1;
777 insert_glyph (m_state *state, int glyph, int x, int y)
779 Bool bottom_feeder_p = (y >= 0);
781 if (y >= state->grid_height)
786 to = &state->cells[state->grid_width * y + x];
790 for (y = state->grid_height-1; y > 0; y--)
792 from = &state->cells[state->grid_width * (y-1) + x];
793 to = &state->cells[state->grid_width * y + x];
794 to->glyph = from->glyph;
795 to->glow = from->glow;
798 to = &state->cells[x];
806 else if (bottom_feeder_p)
807 to->glow = 1 + (random() % (state->tracing ? 4 : 2));
814 place_back_char (m_state *state, char textc, int x, int y){
815 if((x>=0) && (y>=0) &&
816 (x < state->grid_width) && (y < state->grid_height)){
817 m_cell *celltmp = &state->background[state->grid_width * y + x];
818 celltmp -> glyph = char_map[(unsigned char)textc] + 1;
819 if(!celltmp->glyph || (celltmp->glyph == 3)){
820 celltmp -> glyph = char_map[32] + 1;
822 celltmp -> changed = 1;
827 place_back_text (m_state *state, char *text, int x, int y){
829 for(i=0; i<strlen(text); i++){
830 place_back_char(state, text[i], x+i, y);
835 place_back_pipe (m_state *state, char textc){
836 Bool new_line = False;
837 /* gringer pipe insert */
838 state->back_line[state->back_pos] = textc;
840 state->back_line[state->back_pos] = '\0';
843 else if ((state->back_pos > (state->grid_width - 4)) ||
844 (state->back_pos >= BUF_SIZE)){ /* off by 1? */
845 state->back_line[++state->back_pos] = '\0';
852 int startx = (state->grid_width >> 1) -
853 (strlen(state->back_line) >> 1);
854 place_back_text(state, state->back_line,
855 startx, state->back_y);
858 if(state->back_y >= (state->grid_height - 1)){
860 state->back_text_full_p = True;
861 state->start_reveal_back_p = True;
867 feed_matrix (m_state *state)
875 int L = strlen((char *) state->tracing);
879 for (i = 0; i < strlen((char *) state->tracing); i++)
880 if (state->tracing[i] > 0)
885 set_mode (state, TRACE_DONE);
886 state->typing_delay = 1000000;
891 i = 5 + (30 / (count+1)); /* how fast numbers are discovered */
893 if ((random() % i) == 0)
896 if (state->tracing[i] < 0)
897 state->tracing[i] = -state->tracing[i];
904 if ((random() % 40) == 0)
906 set_mode (state, TRACE_FAIL);
911 case MATRIX: case DNA: case BINARY: case DEC: case HEX: case ASCII:
924 if((state->use_pipe_p) && (!state->back_text_full_p)){
925 place_back_pipe(state, state->buf[state->buf_done]);
926 state->buf_done = (state->buf_done + 1) % BUF_SIZE;
927 if(state->buf_done == (state->buf_pos - 1)){
928 state->do_fill_buff = True;
931 if(state->buf_done == (state->buf_pos + 1)){
932 state->do_fill_buff = False;
935 state->do_fill_buff = True;
939 /* Update according to current feeders. */
940 for (x = 0; x < state->grid_width; x++)
942 m_feeder *f = &state->feeders[x];
944 if (f->throttle) /* this is a delay tick, synced to frame. */
948 else if (f->remaining > 0) /* how many items are in the pipe */
952 if((state->use_pipe_p) && (!state->back_text_full_p)){
953 rval = (int) state->buf[f->pipe_loc];
954 if(++f->pipe_loc > (BUF_SIZE-1)){
956 /*fill_input(state);*/
958 rval = (rval % state->nglyphs);
961 rval = (random() % state->nglyphs);
963 g = state->glyph_map[rval] + 1;
964 insert_glyph (state, g, x, f->y);
966 if (f->y >= 0) /* bottom_feeder_p */
969 else /* if pipe is empty, insert spaces */
971 insert_glyph (state, 0, x, f->y);
972 if (f->y >= 0) /* bottom_feeder_p */
976 if ((random() % 10) == 0) /* randomly change throttle speed */
978 f->throttle = ((random() % 5) + (random() % 5));
985 redraw_cells (m_state *state, Bool active)
989 Bool use_back_p = False;
991 for (y = 0; y < state->grid_height; y++)
992 for (x = 0; x < state->grid_width; x++)
994 m_cell *cell = &state->cells[state->grid_width * y + x];
995 m_cell *back = &state->background[state->grid_width * y + x];
996 Bool cursor_p = (state->cursor_on &&
997 x == state->cursor_x &&
998 y == state->cursor_y);
1003 if((state->start_reveal_back_p) &&
1004 (back->glyph) && !(state->mode == TRACE_A ||
1005 state->mode == TRACE_B ||
1006 state->mode == TRACE_DONE)){
1012 /* In trace-mode, the state of each cell is random unless we have
1013 a match for this digit. */
1014 if (active && (state->mode == TRACE_A ||
1015 state->mode == TRACE_B ||
1016 state->mode == TRACE_DONE))
1018 int xx = x % strlen((char *) state->tracing);
1019 Bool dead_p = state->tracing[xx] > 0;
1021 if (y == 0 && x == xx && !use_back_p)
1022 cell->glyph = (dead_p
1023 ? state->glyph_map[state->tracing[xx]-'0'] + 1
1025 else if (y == 0 && !use_back_p)
1027 else if (!use_back_p)
1028 cell->glyph = (dead_p ? 0 :
1029 (state->glyph_map[(random()%state->nglyphs)]
1039 if (cell->glyph == 0 && !cursor_p && !use_back_p)
1040 XFillRectangle (state->dpy, state->window, state->erase_gc,
1041 x * state->char_width,
1042 y * state->char_height,
1044 state->char_height);
1047 int g = (cursor_p ? CURSOR_GLYPH : cell->glyph);
1048 int cx = (g - 1) % CHAR_COLS;
1049 int cy = (g - 1) / CHAR_COLS;
1050 int map = ((cell->glow != 0 || cell->spinner) ? GLOW_MAP :
1053 XCopyArea (state->dpy, state->images[map],
1054 state->window, state->draw_gc,
1055 cx * state->char_width,
1056 cy * state->char_height,
1059 x * state->char_width,
1060 y * state->char_height);
1065 if (cell->glow > 0 && state->mode != NMAP && !use_back_p)
1070 else if (cell->glow < 0)
1073 if (cell->spinner && active && !use_back_p)
1075 cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1);
1083 densitizer (m_state *state)
1085 /* Horrid kludge that converts percentages (density of screen coverage)
1086 to the parameter that actually controls this. I got this mapping
1087 empirically, on a 1024x768 screen. Sue me. */
1088 if (state->density < 10) return 85;
1089 else if (state->density < 15) return 60;
1090 else if (state->density < 20) return 45;
1091 else if (state->density < 25) return 25;
1092 else if (state->density < 30) return 20;
1093 else if (state->density < 35) return 15;
1094 else if (state->density < 45) return 10;
1095 else if (state->density < 50) return 8;
1096 else if (state->density < 55) return 7;
1097 else if (state->density < 65) return 5;
1098 else if (state->density < 80) return 3;
1099 else if (state->density < 90) return 2;
1105 hack_text (m_state *state)
1109 set_cursor (state, False);
1110 state->cursor_x = 0;
1111 state->cursor_y = 0;
1112 state->typing_scroll_p = False;
1113 state->typing_bold_p = False;
1114 state->typing_cursor_p = True;
1115 state->typing_stutter_p = False;
1116 state->typing_char_delay = 10000;
1117 state->typing_line_delay = 1500000;
1119 switch (state->mode)
1123 clear_spinners (state);
1124 if (state->mode == TRACE_TEXT_A)
1126 if (state->grid_width >= 52)
1128 ("Call trans opt: received. 2-19-98 13:24:18 REC:Log>\n"
1129 "Trace program: running\n");
1132 ("Call trans opt: received.\n2-19-98 13:24:18 REC:Log>\n"
1133 "Trace program: running\n");
1137 if (state->grid_width >= 52)
1139 ("Call trans opt: received. 9-18-99 14:32:21 REC:Log>\n"
1140 "WARNING: carrier anomaly\n"
1141 "Trace program: running\n");
1144 ("Call trans opt: received.\n9-18-99 14:32:21 REC:Log>\n"
1145 "WARNING: carrier anomaly\n"
1146 "Trace program: running\n");
1152 const char *s = "SYSTEM FAILURE\n";
1154 float cx = (state->grid_width - strlen(s) - 1) / 2 - 0.5;
1155 float cy = (state->grid_height / 2) - 1.3;
1160 XFillRectangle (state->dpy, state->window, state->erase_gc,
1161 cx * state->char_width,
1162 cy * state->char_height,
1163 strlen(s) * state->char_width,
1164 state->char_height * 1.6);
1166 for (i = -2; i < 3; i++)
1169 gcv.foreground = state->colors[i + 2];
1170 XChangeGC (state->dpy, state->scratch_gc, GCForeground, &gcv);
1171 XDrawRectangle (state->dpy, state->window, state->scratch_gc,
1172 cx * state->char_width - i,
1173 cy * state->char_height - i,
1174 strlen(s) * state->char_width + (2 * i),
1175 (state->char_height * 1.6) + (2 * i));
1178 /* If we don't clear these, part of the box may get overwritten */
1179 for (i = 0; i < state->grid_height * state->grid_width; i++)
1181 m_cell *cell = &state->cells[i];
1185 state->cursor_x = (state->grid_width - strlen(s) - 1) / 2;
1186 state->cursor_y = (state->grid_height / 2) - 1;
1187 if (state->cursor_x < 0) state->cursor_x = 0;
1188 if (state->cursor_y < 0) state->cursor_y = 0;
1191 state->typing_char_delay = 0;
1192 state->typing_cursor_p = False;
1198 clear_spinners (state);
1199 state->typing = ("\001Wake up, Neo...\n"
1200 "\001The Matrix has you...\n"
1201 "\001Follow the white rabbit.\n"
1203 "Knock, knock, Neo.\n");
1205 state->cursor_x = 4;
1206 state->cursor_y = 2;
1207 state->typing_char_delay = 0;
1208 state->typing_line_delay = 2000000;
1214 /* Note that what Trinity is using here is moderately accurate:
1215 She runs nmap (http://www.insecure.org/nmap/) then breaks in
1216 with a (hypothetical) program called "sshnuke" that exploits
1217 the (very real) SSHv1 CRC32 compensation attack detector bug
1218 (http://staff.washington.edu/dittrich/misc/ssh-analysis.txt).
1220 The command syntax of the power grid control software looks a
1221 lot like Cisco IOS to me. (IOS is a descendant of VMS.)
1224 clear_spinners (state);
1227 __extension__ /* don't warn about "string length is greater than
1228 the length ISO C89 compilers are required to
1234 "\001nmap -v -sS -O 10.2.2.2\n"
1235 "Starting nmap V. 2.54BETA25\n"
1236 "\010\010\010\010\010\010\010\010\010\010"
1237 "Insufficient responses for TCP sequencing (3), OS detection"
1238 " may be less accurate\n"
1239 "Interesting ports on 10.2.2.2:\n"
1240 "(The 1539 ports scanned but not shown below are in state:"
1242 "Port state service\n"
1245 "No exact OS matches for host\n"
1247 "Nmap run completed -- 1 IP address (1 host up) scanned\n"
1251 "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n"
1252 "Connecting to 10.2.2.2:ssh ... "
1255 "Attempting to exploit SSHv1 CRC32 ... "
1258 "Resetting root password to \"Z1ON0101\".\n"
1260 "System open: Access Level <9>\n"
1265 "\001ssh 10.2.2.2 -l root\n"
1267 "root@10.2.2.2's password: "
1273 "\001disable grid nodes 21 - 48\n"
1275 "\002Warning: Disabling nodes 21-48 will disconnect sector 11"
1278 "\002 ARE YOU SURE? (y/n) "
1284 "\010\002Grid Node 21 offline...\n"
1285 "\010\002Grid Node 22 offline...\n"
1286 "\010\002Grid Node 23 offline...\n"
1287 "\010\002Grid Node 24 offline...\n"
1288 "\010\002Grid Node 25 offline...\n"
1289 "\010\002Grid Node 26 offline...\n"
1290 "\010\002Grid Node 27 offline...\n"
1291 "\010\002Grid Node 28 offline...\n"
1292 "\010\002Grid Node 29 offline...\n"
1293 "\010\002Grid Node 30 offline...\n"
1294 "\010\002Grid Node 31 offline...\n"
1295 "\010\002Grid Node 32 offline...\n"
1296 "\010\002Grid Node 33 offline...\n"
1297 "\010\002Grid Node 34 offline...\n"
1298 "\010\002Grid Node 35 offline...\n"
1299 "\010\002Grid Node 36 offline...\n"
1300 "\010\002Grid Node 37 offline...\n"
1301 "\010\002Grid Node 38 offline...\n"
1302 "\010\002Grid Node 39 offline...\n"
1303 "\010\002Grid Node 40 offline...\n"
1304 "\010\002Grid Node 41 offline...\n"
1305 "\010\002Grid Node 42 offline...\n"
1306 "\010\002Grid Node 43 offline...\n"
1307 "\010\002Grid Node 44 offline...\n"
1308 "\010\002Grid Node 45 offline...\n"
1309 "\010\002Grid Node 46 offline...\n"
1310 "\010\002Grid Node 47 offline...\n"
1311 "\010\002Grid Node 48 offline...\n"
1314 "\010\010\010\010\010\010\010\010"
1317 state->cursor_x = 0;
1318 state->cursor_y = state->grid_height - 3;
1319 state->typing_scroll_p = True;
1320 state->typing_char_delay = 0;
1321 state->typing_line_delay = 20000;
1330 state->typing_left_margin = state->cursor_x;
1331 state->typing_delay = state->typing_char_delay;
1332 if (state->typing_cursor_p)
1333 set_cursor (state, True);
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 = c ? ((unsigned char *) state->typing)[1] : 0;
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,
1717 xmatrix_event (Display *dpy, Window window, void *closure, XEvent *event)
1719 m_state *state = (m_state *) closure;
1721 if (event->xany.type == KeyPress)
1725 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1729 /*set_mode (state, DRAIN_MATRIX);*/
1731 state->back_text_full_p = True;
1732 state->start_reveal_back_p = True;
1735 case '+': case '=': case '>': case '.':
1736 state->density += 10;
1737 if (state->density > 100)
1738 state->density = 100;
1743 case '-': case '_': case '<': case ',':
1744 state->density -= 10;
1745 if (state->density < 0)
1751 case '[': case '(': case '{':
1752 state->insert_top_p = True;
1753 state->insert_bottom_p = False;
1756 case ']': case ')': case '}':
1757 state->insert_top_p = False;
1758 state->insert_bottom_p = True;
1761 case '\\': case '|':
1762 state->insert_top_p = True;
1763 state->insert_bottom_p = True;
1767 set_mode (state, DRAIN_TRACE_A);
1771 set_mode (state, DRAIN_TRACE_B);
1775 set_mode (state, DRAIN_KNOCK);
1779 set_mode (state, DRAIN_NMAP);
1787 if (screenhack_event_helper (dpy, window, event))
1789 set_mode (state, DRAIN_MATRIX);
1797 xmatrix_free (Display *dpy, Window window, void *closure)
1799 m_state *state = (m_state *) closure;
1801 textclient_close (state->tc);
1802 if (state->cursor_timer)
1803 XtRemoveTimeOut (state->cursor_timer);
1805 /* #### there's more to free here */
1810 static const char *xmatrix_defaults [] = {
1811 ".background: black",
1812 ".foreground: #00AA00",
1814 "*matrixFont: large",
1818 "*tracePhone: (312) 555-0690",
1822 "*knockKnock: True",
1825 "*program: xscreensaver-text --latin1",
1826 "*geometry: 960x720",
1830 static XrmOptionDescRec xmatrix_options [] = {
1831 { "-small", ".matrixFont", XrmoptionNoArg, "Small" },
1832 { "-large", ".matrixFont", XrmoptionNoArg, "Large" },
1833 { "-delay", ".delay", XrmoptionSepArg, 0 },
1834 { "-insert", ".insert", XrmoptionSepArg, 0 },
1835 { "-top", ".insert", XrmoptionNoArg, "top" },
1836 { "-bottom", ".insert", XrmoptionNoArg, "bottom" },
1837 { "-both", ".insert", XrmoptionNoArg, "both" },
1838 { "-density", ".density", XrmoptionSepArg, 0 },
1839 { "-trace", ".trace", XrmoptionNoArg, "True" },
1840 { "-no-trace", ".trace", XrmoptionNoArg, "False" },
1841 { "-crack", ".mode", XrmoptionNoArg, "crack"},
1842 { "-phone", ".tracePhone", XrmoptionSepArg, 0 },
1843 { "-mode", ".mode", XrmoptionSepArg, 0 },
1844 { "-dna", ".mode", XrmoptionNoArg, "DNA" },
1845 { "-binary", ".mode", XrmoptionNoArg, "binary" },
1846 { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal"},
1847 { "-decimal", ".mode", XrmoptionNoArg, "decimal"},
1848 { "-knock-knock", ".knockKnock", XrmoptionNoArg, "True" },
1849 { "-no-knock-knock", ".knockKnock", XrmoptionNoArg, "False" },
1850 { "-ascii", ".mode", XrmoptionNoArg, "ascii"},
1851 { "-pipe", ".usePipe", XrmoptionNoArg, "True" },
1852 { "-no-pipe", ".usePipe", XrmoptionNoArg, "False" },
1853 { "-program", ".program", XrmoptionSepArg, 0 },
1857 XSCREENSAVER_MODULE ("XMatrix", xmatrix)