X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fxmatrix.c;h=dacf312c6549e3e5d9d7449f736aeee69f3dc695;hb=96bdd7cf6ea60c418a76921acaf0e34d6f5be930;hp=1a6bebf158c8b857a436e5298e4e20abd6d1ad41;hpb=8eb2873d7054e705c4e83f22d18c40946a9e2529;p=xscreensaver diff --git a/hacks/xmatrix.c b/hacks/xmatrix.c index 1a6bebf1..dacf312c 100644 --- a/hacks/xmatrix.c +++ b/hacks/xmatrix.c @@ -12,9 +12,12 @@ * * The movie people distribute their own Windows/Mac screensaver that does * a similar thing, so I wrote one for Unix. However, that version (the - * Windows/Mac version at http://www.whatisthematrix.com/) doesn't match my - * memory of what the screens in the movie looked like, so my `xmatrix' - * does things differently. + * Windows/Mac version at http://www.whatisthematrix.com/) doesn't match + * what the computer screens in the movie looked like, so my `xmatrix' does + * things differently. + * + * See also my `glmatrix' program, which does a 3D rendering of the similar + * effect that appeared in the title sequence of the movies. * * * ========================================================== @@ -37,35 +40,35 @@ * washed out. They also definitely scrolled a * character at a time, not a pixel at a time. * + * And keep in mind that this program emulates the + * behavior of the computer screens that were visible + * in the movies -- not the behavior of the effects in + * the title sequences. + * * ========================================================== * */ #include "screenhack.h" +#include "xpm-pixmap.h" #include #include -#ifdef HAVE_XPM -# include -# include "images/matrix0.xpm" +#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM) # include "images/matrix1.xpm" # include "images/matrix2.xpm" -# include "images/matrix0b.xpm" # include "images/matrix1b.xpm" # include "images/matrix2b.xpm" #endif -#include "images/matrix0.xbm" #include "images/matrix1.xbm" #include "images/matrix2.xbm" -#include "images/matrix0b.xbm" #include "images/matrix1b.xbm" #include "images/matrix2b.xbm" #define CHAR_COLS 16 #define CHAR_ROWS 13 #define CHAR_MAPS 3 -#define FADE_MAP 0 #define PLAIN_MAP 1 #define GLOW_MAP 2 @@ -111,21 +114,24 @@ static unsigned char char_map[256] = { 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */ }; -typedef enum { TRACE0, TRACE1, TRACE2, - KNOCK0, KNOCK1, KNOCK2, KNOCK3, - KNOCK4, KNOCK5, KNOCK6, KNOCK7, - MATRIX, DNA, BINARY, HEX } m_mode; +#define CURSOR_GLYPH 97 + +typedef enum { TRACEA1, TRACEA2, + TRACEB1, TRACEB2, SYSTEMFAILURE, + KNOCK, NMAP, MATRIX, DNA, BINARY, DEC, HEX } m_mode; typedef struct { Display *dpy; Window window; XWindowAttributes xgwa; - GC draw_gc, erase_gc; + GC draw_gc, erase_gc, scratch_gc; int grid_width, grid_height; int char_width, char_height; m_cell *cells; + m_cell *cursor; m_feeder *feeders; int nspinners; + Bool knock_knock_p; Bool small_p; Bool insert_top_p, insert_bottom_p; m_mode mode; @@ -138,65 +144,38 @@ typedef struct { int nglyphs; int *glyph_map; + unsigned long colors[5]; } m_state; static void load_images_1 (m_state *state, int which) { -#ifdef HAVE_XPM +#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM) if (!get_boolean_resource ("mono", "Boolean") && state->xgwa.depth > 1) { - - - XpmAttributes xpmattrs; - int result; - xpmattrs.valuemask = 0; - -# ifdef XpmCloseness - xpmattrs.valuemask |= XpmCloseness; - xpmattrs.closeness = 40000; -# endif -# ifdef XpmVisual - xpmattrs.valuemask |= XpmVisual; - xpmattrs.visual = state->xgwa.visual; -# endif -# ifdef XpmDepth - xpmattrs.valuemask |= XpmDepth; - xpmattrs.depth = state->xgwa.depth; -# endif -# ifdef XpmColormap - xpmattrs.valuemask |= XpmColormap; - xpmattrs.colormap = state->xgwa.colormap; -# endif - - result = XpmCreatePixmapFromData (state->dpy, state->window, - (which == 0 ? (state->small_p ? matrix0b_xpm : matrix0_xpm) : - which == 1 ? (state->small_p ? matrix1b_xpm : matrix1_xpm) : - (state->small_p ? matrix2b_xpm : matrix2_xpm)), - &state->images[which], 0 /* mask */, - &xpmattrs); - if (!state->images || (result != XpmSuccess && result != XpmColorError)) - state->images[which] = 0; - - state->image_width = xpmattrs.width; - state->image_height = xpmattrs.height; + char **bits = + (which == 1 ? (state->small_p ? matrix1b_xpm : matrix1_xpm) : + (state->small_p ? matrix2b_xpm : matrix2_xpm)); + + state->images[which] = + xpm_data_to_pixmap (state->dpy, state->window, bits, + &state->image_width, &state->image_height, 0); } else -#endif /* !HAVE_XPM */ +#endif /* !HAVE_XPM && !HAVE_GDK_PIXBUF */ { unsigned long fg, bg; - state->image_width = (state->small_p ? matrix0b_width :matrix0_width); - state->image_height = (state->small_p ? matrix0b_height:matrix0_height); + state->image_width = (state->small_p ? matrix1b_width :matrix1_width); + state->image_height = (state->small_p ? matrix1b_height:matrix1_height); fg = get_pixel_resource("foreground", "Foreground", state->dpy, state->xgwa.colormap); bg = get_pixel_resource("background", "Background", state->dpy, state->xgwa.colormap); state->images[which] = XCreatePixmapFromBitmapData (state->dpy, state->window, (char *) - (which == 0 ? (state->small_p ? matrix0b_bits :matrix0_bits) : - which == 1 ? (state->small_p ? matrix1b_bits :matrix1_bits) : + (which == 1 ? (state->small_p ? matrix1b_bits :matrix1_bits) : (state->small_p ? matrix2b_bits :matrix2_bits)), state->image_width, state->image_height, bg, fg, state->xgwa.depth); @@ -207,7 +186,6 @@ load_images_1 (m_state *state, int which) static void load_images (m_state *state) { - load_images_1 (state, 0); load_images_1 (state, 1); load_images_1 (state, 2); } @@ -243,7 +221,6 @@ flip_images_1 (m_state *state, int which) static void flip_images (m_state *state) { - flip_images_1 (state, 0); flip_images_1 (state, 1); flip_images_1 (state, 2); } @@ -317,6 +294,7 @@ init_matrix (Display *dpy, Window window) { XGCValues gcv; char *insert, *mode; + int i; m_state *state = (m_state *) calloc (sizeof(*state), 1); state->dpy = dpy; @@ -344,6 +322,26 @@ init_matrix (Display *dpy, Window window) state->erase_gc = XCreateGC (state->dpy, state->window, GCForeground|GCBackground, &gcv); + state->scratch_gc = XCreateGC (state->dpy, state->window, 0, &gcv); + + /* Allocate colors for SYSTEM FAILURE box */ + { + XColor boxcolors[] = { + { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 }, + { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 }, + { 0, 0xE0E0, 0xF7F7, 0xE0E0, DoRed|DoGreen|DoBlue, 0 }, + { 0, 0x5A5A, 0xD2D2, 0x5A5A, DoRed|DoGreen|DoBlue, 0 }, + { 0, 0x0808, 0x1E1E, 0x0808, DoRed|DoGreen|DoBlue, 0 }, + }; + for (i = 0; i < countof(boxcolors); i++) + { + if (XAllocColor (state->dpy, state->xgwa.colormap, &boxcolors[i])) + state->colors[i] = boxcolors[i].pixel; + else + state->colors[i] = gcv.foreground; /* default black */ + } + } + state->char_width = state->image_width / CHAR_COLS; state->char_height = state->image_height / CHAR_ROWS; @@ -351,12 +349,15 @@ init_matrix (Display *dpy, Window window) state->grid_height = state->xgwa.height / state->char_height; state->grid_width++; state->grid_height++; + if (state->grid_width < 5) state->grid_width = 5; + if (state->grid_height < 5) state->grid_height = 5; state->glyph_map = matrix_encoding; state->nglyphs = countof(matrix_encoding); state->cells = (m_cell *) calloc (sizeof(m_cell), state->grid_width * state->grid_height); + state->cursor = NULL; state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width); state->density = get_integer_resource ("density", "Integer"); @@ -392,16 +393,24 @@ init_matrix (Display *dpy, Window window) if (insert) free (insert); + state->knock_knock_p = get_boolean_resource ("knockKnock", "KnockKnock"); + mode = get_string_resource ("mode", "Mode"); if (mode && !strcasecmp(mode, "trace")) - state->mode = TRACE0; + state->mode = (((random() % 3) == 0) ? TRACEB1 : TRACEA1); + else if (mode && !strcasecmp(mode, "crack")) + state->mode = NMAP; else if (mode && !strcasecmp(mode, "dna")) state->mode = DNA; - else if (mode && !strcasecmp(mode, "binary")) + else if (mode && (!strcasecmp(mode, "bin") || + !strcasecmp(mode, "binary"))) state->mode = BINARY; else if (mode && (!strcasecmp(mode, "hex") || !strcasecmp(mode, "hexadecimal"))) state->mode = HEX; + else if (mode && (!strcasecmp(mode, "dec") || + !strcasecmp(mode, "decimal"))) + state->mode = DEC; else if (!mode || !*mode || !strcasecmp(mode, "matrix")) state->mode = MATRIX; else @@ -412,32 +421,35 @@ init_matrix (Display *dpy, Window window) state->mode = MATRIX; } - if (state->mode == DNA) + switch (state->mode) { + case DNA: state->glyph_map = dna_encoding; state->nglyphs = countof(dna_encoding); - } - else if (state->mode == BINARY) - { + break; + case BINARY: state->glyph_map = binary_encoding; state->nglyphs = countof(binary_encoding); - } - else if (state->mode == HEX) - { - state->glyph_map = hex_encoding; - state->nglyphs = countof(hex_encoding); - } - else if (state->mode == HEX) - { + break; + case DEC: + state->glyph_map = decimal_encoding; + state->nglyphs = countof(decimal_encoding); + break; + case HEX: state->glyph_map = hex_encoding; state->nglyphs = countof(hex_encoding); - } - else if (state->mode == TRACE0) - init_trace (state); - else - { + break; + case TRACEA1: case TRACEB1: + init_trace (state); + break; + case NMAP: + break; + case MATRIX: flip_images (state); init_spinners (state); + break; + default: + abort(); } return state; @@ -476,7 +488,7 @@ insert_glyph (m_state *state, int glyph, int x, int y) if (!to->glyph) ; else if (bottom_feeder_p) - to->glow = 1 + (random() % 2); + to->glow = 1 + (random() % (state->tracing ? 4 : 2)); else to->glow = 0; } @@ -489,7 +501,8 @@ feed_matrix (m_state *state) switch (state->mode) { - case TRACE2: case MATRIX: case DNA: case BINARY: case HEX: + case TRACEA2: case TRACEB2: + case MATRIX: case DNA: case BINARY: case DEC: case HEX: break; default: return; @@ -526,6 +539,91 @@ feed_matrix (m_state *state) } } + +static void +redraw_cells (m_state *state, Bool active) +{ + int x, y; + int count = 0; + + for (y = 0; y < state->grid_height; y++) + for (x = 0; x < state->grid_width; x++) + { + m_cell *cell = &state->cells[state->grid_width * y + x]; + + if (cell->glyph) + count++; + + if ((state->mode == TRACEA2 || state->mode == TRACEB2) && active) + { + int xx = x % strlen(state->tracing); + Bool dead_p = state->tracing[xx] > 0; + + if (y == 0 && x == xx) + cell->glyph = (dead_p + ? state->glyph_map[state->tracing[xx]-'0'] + 1 + : 0); + else if (y == 0) + cell->glyph = 0; + else + cell->glyph = (dead_p ? 0 : + (state->glyph_map[(random()%state->nglyphs)] + + 1)); + + cell->changed = 1; + } + + if (!cell->changed) + continue; + + if (cell->glyph == 0 && cell != state->cursor) + XFillRectangle (state->dpy, state->window, state->erase_gc, + x * state->char_width, + y * state->char_height, + state->char_width, + state->char_height); + else + { + int g = (cell == state->cursor ? CURSOR_GLYPH : cell->glyph); + int cx = (g - 1) % CHAR_COLS; + int cy = (g - 1) / CHAR_COLS; + int map = ((cell->glow != 0 || cell->spinner) ? GLOW_MAP : + PLAIN_MAP); + + XCopyArea (state->dpy, state->images[map], + state->window, state->draw_gc, + cx * state->char_width, + cy * state->char_height, + state->char_width, + state->char_height, + x * state->char_width, + y * state->char_height); + } + + cell->changed = 0; + + if (cell->glow > 0) + { + cell->glow--; + cell->changed = 1; + } + else if (cell->glow < 0) + abort(); + + if (cell->spinner && active) + { + cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1); + cell->changed = 1; + } + } + + if (state->cursor) + { + state->cursor->changed = 1; + } +} + + static int densitizer (m_state *state) { @@ -548,89 +646,593 @@ densitizer (m_state *state) } +static void drain_matrix (m_state *); + static void -hack_text (m_state *state) +handle_events (m_state *state) { - int i; - int x = 0; - const char *s; - switch (state->mode) + XSync (state->dpy, False); + while (XPending (state->dpy)) { - case TRACE0: s = "Call trans opt: received.\n" - "2-19-98 13:24:18 REC:Log>_"; break; - case TRACE1: s = "Trace program: running_"; break; - - case KNOCK0: s = "Wake up, Neo..."; break; - case KNOCK1: s = ""; break; - case KNOCK2: s = "The Matrix has you..."; break; - case KNOCK3: s = ""; break; - case KNOCK4: s = "Follow the white rabbit..."; break; - case KNOCK5: s = ""; break; - case KNOCK6: s = "Knock knock, Neo."; break; - case KNOCK7: s = ""; break; - - default: abort(); break; + XEvent event; + XNextEvent (state->dpy, &event); + + if (event.xany.type == ConfigureNotify) + { + int ow = state->grid_width; + int oh = state->grid_height; + XGetWindowAttributes (state->dpy, state->window, &state->xgwa); + state->grid_width = state->xgwa.width / state->char_width; + state->grid_height = state->xgwa.height / state->char_height; + state->grid_width++; + state->grid_height++; + if (state->grid_width < 5) state->grid_width = 5; + if (state->grid_height < 5) state->grid_height = 5; + + if (ow != state->grid_width || + oh != state->grid_height) + { + m_cell *ncells = (m_cell *) + calloc (sizeof(m_cell), + state->grid_width * state->grid_height); + m_feeder *nfeeders = (m_feeder *) + calloc (sizeof(m_feeder), state->grid_width); + int x, y, i; + + /* fprintf(stderr, "resize: %d x %d ==> %d x %d\n", + ow, oh, state->grid_width, state->grid_height); */ + + for (y = 0; y < oh; y++) + for (x = 0; x < ow; x++) + if (x < ow && x < state->grid_width && + y < oh && y < state->grid_height) + ncells[y * state->grid_width + x] = + state->cells[y * ow + x]; + free (state->cells); + state->cells = ncells; + + x = (ow < state->grid_width ? ow : state->grid_width); + for (i = 0; i < x; i++) + nfeeders[i] = state->feeders[i]; + free (state->feeders); + state->feeders = nfeeders; + } + } + else if (event.xany.type == KeyPress) + { + KeySym keysym; + char c = 0; + XLookupString (&event.xkey, &c, 1, &keysym, 0); + if (c == '0' && !state->tracing) + { + drain_matrix (state); + return; + } + else if (c == '+' || c == '=' || c == '>' || c == '.') + { + state->density += 10; + if (state->density > 100) + state->density = 100; + else + return; + } + else if (c == '-' || c == '_' || c == '<' || c == ',') + { + state->density -= 10; + if (state->density < 0) + state->density = 0; + else + return; + } + else if (c == '[' || c == '(' || c == '{') + { + state->insert_top_p = True; + state->insert_bottom_p = False; + return; + } + else if (c == ']' || c == ')' || c == '}') + { + state->insert_top_p = False; + state->insert_bottom_p = True; + return; + } + else if (c == '\\' || c == '|') + { + state->insert_top_p = True; + state->insert_bottom_p = True; + return; + } + else if ((c == 't' || c == 'T') && state->mode == MATRIX) + { + state->mode = (c == 't' ? TRACEA1 : TRACEB1); + flip_images (state); + init_trace (state); + return; + } + else if ((c == 'c' || c == 'k') && state->mode == MATRIX) + { + drain_matrix (state); + state->mode = (c == 'c' ? NMAP : KNOCK); + flip_images (state); + return; + } + } + + screenhack_handle_event (state->dpy, &event); } +} - for (i = 0; i < state->grid_height * state->grid_width; i++) + +static void +matrix_usleep (m_state *state, unsigned long delay) +{ + if (!delay) return; + + if (state->cursor) { - m_cell *cell = &state->cells[i]; - cell->changed = (cell->glyph != 0); - cell->glyph = 0; + int blink_delay = 333000; + int tot_delay = 0; + m_cell *cursor = state->cursor; + while (tot_delay < delay) + { + if (state->cursor) + { + usleep (blink_delay * 2); + tot_delay += (2 * blink_delay); + state->cursor = NULL; + } + else + { + usleep (blink_delay); + tot_delay += blink_delay; + state->cursor = cursor; + } + cursor->changed = 1; + redraw_cells (state, False); + XSync (state->dpy, False); + handle_events (state); + } } - - if (state->mode == TRACE0 || state->mode == TRACE1) - i = 0; else { - int y; - x = ((int)state->grid_width - (int)strlen(s)) / 2; - y = (state->grid_height / 2) - 1; - if (y < 0) y = 0; - if (x < 0) x = 0; - i = (y * state->grid_width) + x; + XSync (state->dpy, False); + handle_events (state); + usleep (delay); } +} + + +static void +hack_text_1 (m_state *state, + int *xP, int *yP, + const char *s, + Bool typing_delay, + Bool transmit_delay, + Bool long_delay, + Bool visible_cursor, + Bool scroll_p) +{ + int x = *xP; + int y = *yP; + int i = state->grid_width * y + x; + Bool glow_p = False; + int long_delay_usecs = 1000000; + + if (long_delay == -1) + long_delay = 0, long_delay_usecs /= 6; + + if (y >= state->grid_height-1) return; while (*s) { - if (*s == '\n') + m_cell *cell; + Bool done_p = s[1] == '\000'; + + long_delay = done_p; + + if (*s == '\n' || x >= state->grid_width - 1) { - i = ((i / state->grid_width) + 1) * state->grid_width; + if (*s != '\n') + s--; x = 0; + y++; + i = state->grid_width * y + x; + + if (scroll_p) + { + int xx, yy; + for (yy = 0; yy < state->grid_height-1; yy++) + for (xx = 0; xx < state->grid_width; xx++) + { + int ii = yy * state->grid_width + xx; + int jj = (yy+1) * state->grid_width + xx; + state->cells[ii] = state->cells[jj]; + state->cells[ii].changed = 1; + } + /* clear bottom row */ + for (xx = 0; xx < state->grid_width; xx++) + { + int ii = yy * state->grid_width + xx; + state->cells[ii].glyph = 0; + state->cells[ii].changed = 1; + } + y--; /* move it back */ + i = state->grid_width * y + x; + } + + if (y >= state->grid_height) return; + + cell = &state->cells[i]; + if (visible_cursor) + { + cell->changed = 1; + state->cursor = cell; + } } + else if (*s == '\010') + ; + else if (*s == '\002') + glow_p = True; else { - m_cell *cell = &state->cells[i]; + cell = &state->cells[i]; if (x < state->grid_width-1) { cell->glyph = char_map[(unsigned char) *s] + 1; if (*s == ' ' || *s == '\t') cell->glyph = 0; cell->changed = 1; + cell->glow = (glow_p ? 100 : 0); + if (visible_cursor) + { + m_cell *next = &state->cells[i + 1]; + next->changed = 1; + state->cursor = next; + } i++; } x++; } s++; + if (typing_delay || transmit_delay || long_delay) + { + redraw_cells (state, False); + XSync (state->dpy, False); + handle_events (state); + if (typing_delay) + { + usleep (50000); + if (typing_delay && 0 == random() % 3) + usleep (0xFFFFFF & ((random() % 250000) + 1)); + } + else + if (long_delay) + matrix_usleep (state, long_delay_usecs); + else + usleep (20000); + } } + + *xP = x; + *yP = y; } + +static void +zero_cells (m_state *state) +{ + int i; + for (i = 0; i < state->grid_height * state->grid_width; i++) + { + m_cell *cell = &state->cells[i]; + cell->changed = (cell->glyph != 0); + cell->glyph = 0; + cell->glow = 0; + cell->spinner = 0; + } +} + + +static void +hack_text (m_state *state) +{ + Bool typing_delay = False; + Bool transmit_delay = False; + Bool long_delay = False; + Bool visible_cursor = False; + + switch (state->mode) + { + case KNOCK: + { + const char *blocks[] = { + "Wake up, Neo...", + "The Matrix has you...", + "Follow the white rabbit.", + " ", + "Knock, knock, Neo." + }; + int nblocks = countof(blocks); + int j; + typing_delay = True; + transmit_delay = False; + long_delay = False; + visible_cursor = True; + for (j = 0; j < nblocks; j++) + { + int x = 3; + int y = 2; + const char *s = blocks[j]; + if (!s[0] || !s[1]) typing_delay = False; + zero_cells (state); + hack_text_1 (state, &x, &y, s, + typing_delay, transmit_delay, -1, + visible_cursor, True); + matrix_usleep (state, 2000000); + } + } + break; + + case TRACEA1: case TRACEB1: + { + const char *blocks[10]; + int j, n = 0; + + if (state->mode == TRACEA1) + blocks[n++] = + (state->grid_width >= 52 + ? "Call trans opt: received. 2-19-98 13:24:18 REC:Log>" + : "Call trans opt: received.\n2-19-98 13:24:18 REC:Log>"); + else + blocks[n++] = + (state->grid_width >= 52 + ? "Call trans opt: received. 9-18-99 14:32:21 REC:Log>" + : "Call trans opt: received.\n9-18-99 14:32:21 REC:Log>"); + + if (state->mode == TRACEB1) + blocks[n++] = "WARNING: carrier anomaly"; + blocks[n++] = "Trace program: running"; + + typing_delay = False; + transmit_delay = True; + long_delay = True; + visible_cursor = True; + for (j = 0; j < n; j++) + { + const char *s = blocks[j]; + int x = 0; + int y = 0; + zero_cells (state); + hack_text_1 (state, &x, &y, s, + typing_delay, transmit_delay, long_delay, + visible_cursor, True); + } + matrix_usleep (state, 1000000); + } + break; + + case SYSTEMFAILURE: + { + const char *s = "SYSTEM FAILURE"; + int i; + float cx = ((int)state->grid_width - (int)strlen(s)) / 2 - 0.5; + float cy = (state->grid_height / 2) - 1.3; + int x, y; + + if (cy < 0) cy = 0; + if (cx < 0) cx = 0; + + XFillRectangle (state->dpy, state->window, state->erase_gc, + cx * state->char_width, + cy * state->char_height, + (strlen(s) + 1) * state->char_width, + state->char_height * 1.6); + + for (i = -2; i < 3; i++) + { + XGCValues gcv; + gcv.foreground = state->colors[i + 2]; + XChangeGC (state->dpy, state->scratch_gc, GCForeground, &gcv); + XDrawRectangle (state->dpy, state->window, state->scratch_gc, + cx * state->char_width - i, + cy * state->char_height - i, + (strlen(s) + 1) * state->char_width + (2 * i), + (state->char_height * 1.6) + (2 * i)); + } + + /* If we don't clear these out, part of the box may get overwritten */ + for (i = 0; i < state->grid_height * state->grid_width; i++) + { + m_cell *cell = &state->cells[i]; + cell->changed = 0; + } + + x = ((int)state->grid_width - (int)strlen(s)) / 2; + y = (state->grid_height / 2) - 1; + if (y < 0) y = 0; + if (x < 0) x = 0; + hack_text_1 (state, &x, &y, s, + typing_delay, transmit_delay, long_delay, + visible_cursor, False); + } + break; + + case NMAP: + { + /* Note that what Trinity is using here is moderately accurate: + She runs nmap (http://www.insecure.org/nmap/) then breaks in + with a (hypothetical) program called "sshnuke" that exploits + the (very real) SSHv1 CRC32 compensation attack detector bug + (http://staff.washington.edu/dittrich/misc/ssh-analysis.txt). + + The command syntax of the power grid control software looks a + lot like Cisco IOS to me. (IOS is a descendant of VMS.) + */ + const char *blocks[] = { + "# ", + + "\001nmap 10.2.2.2\n", + "Starting nmap V. 2.54BETA25\n" + + "\010", "\010", "\010", + + "Insufficient responses for TCP sequencing (3), OS detection " + "may be less accurate\n" + "Interesting ports on 10.2.2.2:\n" + "(The 1538 ports scanned but not shown below are in state: " + "filtered)\n" + "Port state service\n" + "22/tcp open ssh\n" + "\n" + "No exact OS matches for host\n" + "\n" + "Nmap run completed -- 1 IP address (1 host up) scanned\n" + "# ", + + "\001sshnuke 10.2.2.2 -rootpw=\"Z1ON0101\"\n", + + "Connecting to 10.2.2.2:ssh ... ", + + "successful.\n" + "Attempting to exploit SSHv1 CRC32 ... ", + + "successful.\n" + "Resetting root password to \"Z1ON0101\".\n", + + "System open: Access Level <9>\n" + "# ", + + "\001ssh 10.2.2.2 -l root\n", + + "root@10.2.2.2's password: ", + + "\001\010\010\010\010\010\010\010\010\n", + + "\n" + "RRF-CONTROL> ", + + "\001disable grid nodes 21 - 40\n", + + "\002Warning: Disabling nodes 21-40 will disconnect sector 11 " + "(27 nodes)\n" + "\n" + "\002 ARE YOU SURE? (y/n) ", + + "\001\010\010y\n", + "\n" + }; + + int nblocks = countof(blocks); + int y = state->grid_height - 2; + int x, j; + + visible_cursor = True; + x = 0; + zero_cells (state); + for (j = 0; j < nblocks; j++) + { + const char *s = blocks[j]; + typing_delay = (*s == '\001'); + if (typing_delay) s++; + + long_delay = False; + hack_text_1 (state, &x, &y, s, + typing_delay, transmit_delay, long_delay, + visible_cursor, True); + } + + typing_delay = False; + long_delay = False; + for (j = 21; j <= 40; j++) + { + char buf[100]; + sprintf (buf, "\002Grid Node %d offline...\n", j); + hack_text_1 (state, &x, &y, buf, + typing_delay, transmit_delay, -1, + visible_cursor, True); + + } + long_delay = True; + hack_text_1 (state, &x, &y, "\nRRF-CONTROL> ", + typing_delay, transmit_delay, long_delay, + visible_cursor, True); + + /* De-glow all cells before draining them... */ + for (j = 0; j < state->grid_height * state->grid_width; j++) + { + m_cell *cell = &state->cells[j]; + cell->changed = (cell->glow != 0); + cell->glow = 0; + } + } + break; + + default: + abort(); + break; + } +} + + +static void +drain_matrix (m_state *state) +{ + int delay = get_integer_resource ("delay", "Integer"); + int i; + + /* Fill the top row with empty top-feeders, to clear the screen. */ + for (i = 0; i < state->grid_width; i++) + { + m_feeder *f = &state->feeders[i]; + f->y = -1; + f->remaining = 0; + f->throttle = 0; + } + + /* Turn off all the spinners, else they never go away. */ + for (i = 0; i < state->grid_width * state->grid_height; i++) + if (state->cells[i].spinner) + { + state->cells[i].spinner = 0; + state->cells[i].changed = 1; + } + + /* Run the machine until there are no live cells left. */ + while (1) + { + Bool any_cells_p = False; + for (i = 0; i < state->grid_width * state->grid_height; i++) + if (state->cells[i].glyph) + { + any_cells_p = True; + goto FOUND; + } + FOUND: + if (! any_cells_p) + return; + + feed_matrix (state); + redraw_cells (state, True); + XSync (state->dpy, False); + handle_events (state); + if (delay) usleep (delay); + } +} + + static void roll_state (m_state *state) { int delay = 0; switch (state->mode) { - case TRACE0: - delay = 3; - state->mode = TRACE1; + case TRACEA1: + state->mode = TRACEA2; break; - - case TRACE1: - delay = 2; - state->mode = TRACE2; + case TRACEB1: + state->mode = TRACEB2; break; - case TRACE2: + case TRACEA2: { Bool any = False; int i; @@ -640,7 +1242,7 @@ roll_state (m_state *state) if (!any) { XSync (state->dpy, False); - sleep (3); + matrix_usleep (state, 3000000); state->mode = MATRIX; state->glyph_map = matrix_encoding; state->nglyphs = countof(matrix_encoding); @@ -648,7 +1250,7 @@ roll_state (m_state *state) free (state->tracing); state->tracing = 0; } - else if ((random() % 10) == 0) + else if ((random() % 20) == 0) /* how fast numbers are discovered */ { int x = random() % strlen(state->tracing); if (state->tracing[x] < 0) @@ -657,14 +1259,44 @@ roll_state (m_state *state) break; } - case KNOCK0: delay = 1; state->mode++; break; /* wake */ - case KNOCK1: delay = 4; state->mode++; break; - case KNOCK2: delay = 2; state->mode++; break; /* has */ - case KNOCK3: delay = 4; state->mode++; break; - case KNOCK4: delay = 2; state->mode++; break; /* rabbit */ - case KNOCK5: delay = 4; state->mode++; break; - case KNOCK6: delay = 4; state->mode++; break; /* knock */ - case KNOCK7: + case TRACEB2: + { + /* reversed logic from TRACEA2 */ + Bool any = False; + int i; + for (i = 0; i < strlen(state->tracing); i++) + if (state->tracing[i] > 0) any = True; + + if ((random() % 15) == 0) { + if (any) + state->mode = SYSTEMFAILURE; + else + { + int x = random() % strlen(state->tracing); + if (state->tracing[x] < 0) + state->tracing[x] = -state->tracing[x]; + } + } + break; + } + + case SYSTEMFAILURE: + XSync (state->dpy, False); + matrix_usleep (state, 6000000); + state->mode = MATRIX; + state->glyph_map = matrix_encoding; + state->nglyphs = countof(matrix_encoding); + flip_images (state); + drain_matrix (state); + matrix_usleep (state, 2000000); + if (state->tracing) { + free (state->tracing); + state->tracing = 0; + } + break; + + case KNOCK: + case NMAP: state->mode = MATRIX; state->glyph_map = matrix_encoding; state->nglyphs = countof(matrix_encoding); @@ -672,14 +1304,19 @@ roll_state (m_state *state) break; case MATRIX: - if (! (random() % 5000)) + if (state->knock_knock_p && (! (random() % 3500))) { - state->mode = KNOCK0; + drain_matrix (state); + if (! (random() % 5)) + state->mode = NMAP; + else + state->mode = KNOCK; + flip_images (state); } break; - case DNA: case BINARY: case HEX: + case DNA: case BINARY: case DEC: case HEX: break; default: @@ -687,11 +1324,8 @@ roll_state (m_state *state) break; } - if (delay) - { - XSync (state->dpy, False); - sleep (delay); - } + matrix_usleep (state, delay * 1000000); + state->cursor = NULL; } @@ -702,12 +1336,12 @@ hack_matrix (m_state *state) switch (state->mode) { - case TRACE0: case TRACE1: - case KNOCK0: case KNOCK1: case KNOCK2: case KNOCK3: - case KNOCK4: case KNOCK5: case KNOCK6: case KNOCK7: + case TRACEA1: case TRACEB1: case SYSTEMFAILURE: + case KNOCK: case NMAP: hack_text (state); return; - case TRACE2: case MATRIX: case DNA: case BINARY: case HEX: + case TRACEA2: case TRACEB2: + case MATRIX: case DNA: case BINARY: case DEC: case HEX: break; default: abort(); break; @@ -748,7 +1382,7 @@ hack_matrix (m_state *state) if ((random() % 4) != 0) f->remaining = 0; - if (state->mode == TRACE2) + if (state->mode == TRACEA2 || state->mode == TRACEB2) bottom_feeder_p = True; if (state->insert_top_p && state->insert_bottom_p) bottom_feeder_p = (random() & 1); @@ -761,7 +1395,7 @@ hack_matrix (m_state *state) f->y = -1; } - if (!state->mode == TRACE2 && + if (!state->mode == TRACEA2 && !state->mode == TRACEB2 && ! (random() % 500)) init_spinners (state); } @@ -770,88 +1404,9 @@ hack_matrix (m_state *state) static void draw_matrix (m_state *state) { - int x, y; - int count = 0; - feed_matrix (state); hack_matrix (state); - - for (y = 0; y < state->grid_height; y++) - for (x = 0; x < state->grid_width; x++) - { - m_cell *cell = &state->cells[state->grid_width * y + x]; - - if (cell->glyph) - count++; - - if (state->mode == TRACE2) - { - int xx = x % strlen(state->tracing); - Bool dead_p = state->tracing[xx] > 0; - - if (y == 0 && x == xx) - cell->glyph = (dead_p - ? state->glyph_map[state->tracing[xx]-'0'] + 1 - : 0); - else if (y == 0) - cell->glyph = 0; - else - cell->glyph = (dead_p ? 0 : - (state->glyph_map[(random()%state->nglyphs)] - + 1)); - - cell->changed = 1; - } - - if (!cell->changed) - continue; - - if (cell->glyph == 0) - XFillRectangle (state->dpy, state->window, state->erase_gc, - x * state->char_width, - y * state->char_height, - state->char_width, - state->char_height); - else - { - int cx = (cell->glyph - 1) % CHAR_COLS; - int cy = (cell->glyph - 1) / CHAR_COLS; - int map = ((cell->glow > 0 || cell->spinner) ? GLOW_MAP : - (cell->glow == 0) ? PLAIN_MAP : - GLOW_MAP); - - XCopyArea (state->dpy, state->images[map], - state->window, state->draw_gc, - cx * state->char_width, - cy * state->char_height, - state->char_width, - state->char_height, - x * state->char_width, - y * state->char_height); - } - - cell->changed = 0; - - if (cell->glow > 0) - { - cell->glow--; - cell->changed = 1; - } - else if (cell->glow < 0) - { - cell->glow++; - if (cell->glow == 0) - cell->glyph = 0; - cell->changed = 1; - } - - if (cell->spinner) - { - cell->glyph = (state->glyph_map[(random()%state->nglyphs)] + 1); - cell->changed = 1; - } - } - + redraw_cells (state, True); roll_state (state); #if 0 @@ -880,14 +1435,16 @@ char *progclass = "XMatrix"; char *defaults [] = { ".background: black", - ".foreground: green", + ".foreground: #00AA00", "*small: ", "*delay: 10000", "*insert: both", "*mode: Matrix", - "*tracePhone: (212) 555-0690", + "*tracePhone: (312) 555-0690", "*spinners: 5", "*density: 75", + "*knockKnock: False", + "*geometry: 800x600", 0 }; @@ -900,10 +1457,13 @@ XrmOptionDescRec options [] = { { "-both", ".insert", XrmoptionNoArg, "both" }, { "-density", ".density", XrmoptionSepArg, 0 }, { "-trace", ".mode", XrmoptionNoArg, "trace" }, + { "-crack", ".mode", XrmoptionNoArg, "crack"}, { "-phone", ".tracePhone", XrmoptionSepArg, 0 }, { "-dna", ".mode", XrmoptionNoArg, "DNA" }, { "-binary", ".mode", XrmoptionNoArg, "binary" }, { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal"}, + { "-decimal", ".mode", XrmoptionNoArg, "decimal"}, + { "-knock-knock", ".knockKnock", XrmoptionNoArg, "True" }, { 0, 0, 0, 0 } }; @@ -917,7 +1477,7 @@ screenhack (Display *dpy, Window window) { draw_matrix (state); XSync (dpy, False); - screenhack_handle_events (dpy); + handle_events (state); if (delay) usleep (delay); } }