http://www.archive.org/download/tucows_10294_XScreenSaver/xscreensaver-4.10.tar.gz
[xscreensaver] / hacks / xmatrix.c
index 016ec771f225f2075e1a0565e16be6275d15a44a..09fe155f846964b50d5f069446651538b5e69ddd 100644 (file)
@@ -111,9 +111,12 @@ static unsigned char char_map[256] = {
   176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191   /* 240 */
 };
 
+#define CURSOR_GLYPH 97
+
 typedef enum { TRACE0, TRACE1, TRACE2,
                KNOCK0, KNOCK1, KNOCK2, KNOCK3,
                KNOCK4, KNOCK5, KNOCK6, KNOCK7,
+               NMAP0, NMAP1,
                MATRIX, DNA, BINARY, HEX } m_mode;
 
 typedef struct {
@@ -124,8 +127,10 @@ typedef struct {
   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;
@@ -325,12 +330,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");
@@ -366,9 +374,13 @@ 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;
+  else if (mode && !strcasecmp(mode, "crack"))
+    state->mode = NMAP0;
   else if (mode && !strcasecmp(mode, "dna"))
     state->mode = DNA;
   else if (mode && !strcasecmp(mode, "binary"))
@@ -408,6 +420,8 @@ init_matrix (Display *dpy, Window window)
     }
   else if (state->mode == TRACE0)
     init_trace (state);
+  else if (state->mode == NMAP0)
+    ;
   else
     {
       flip_images (state);
@@ -500,6 +514,97 @@ 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 == TRACE2 && 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 :
+                       (cell->glow < 0) ? FADE_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)
+          {
+            cell->glow++;
+            if (cell->glow == 0)
+              cell->glyph = 0;
+            cell->changed = 1;
+          }
+
+        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)
 {
@@ -522,72 +627,447 @@ 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;
+      XEvent event;
+      XNextEvent (state->dpy, &event);
 
-    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;
+      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')
+            {
+              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;
+            }
+        }
 
-    default: abort(); break;
+      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;
+
+  if (y >= state->grid_height-1) return;
 
   while (*s)
     {
+      m_cell *cell;
+      Bool done_p = s[1] == '\000';
+
+      long_delay = done_p;
+              
       if (*s == '\n')
         {
-          i = ((i / state->grid_width) + 1) * state->grid_width;
           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 ? 8 : 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, 1000000);
+            else
+              usleep (20000);
+        }
     }
+
+  *xP = x;
+  *yP = y;
 }
 
+
+static void
+hack_text (m_state *state)
+{
+  int i;
+  int x = 0;
+  int y = 0;
+  const char *s;
+  Bool typing_delay = False;
+  Bool transmit_delay = False;
+  Bool long_delay = False;
+  Bool visible_cursor = False;
+  switch (state->mode)
+    {
+    case TRACE0: if (state->grid_width >= 52)
+                   s = "Call trans opt: received. 2-19-98 13:24:18 REC:Log>";
+                 else
+                   s = "Call trans opt: received.\n2-19-98 13:24:18 REC:Log>";
+                 transmit_delay = True;
+                 visible_cursor = True;
+                 break;
+    case TRACE1: s = "Trace program: running";
+                 transmit_delay = True;
+                 visible_cursor = True;
+                 break;
+    case KNOCK0: s = "Wake up, Neo..."; break;
+    case KNOCK1: s = ""; break;
+    case KNOCK2: s = "The Matrix has you..."; typing_delay = True; break;
+    case KNOCK3: s = ""; break;
+    case KNOCK4: s = "Follow the white rabbit."; typing_delay = True; break;
+    case KNOCK5: s = ""; break;
+    case KNOCK6: s = "Knock, knock, Neo."; break;
+    case KNOCK7:
+    case NMAP0:
+    case NMAP1:  s = ""; break;
+
+    default: abort(); break;
+    }
+
+  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;
+    }
+
+  if (state->mode == NMAP0)
+    {
+      /* 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\n"
+        "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"
+        "FFF-CONTROL> ",
+
+        "\001disable grid nodes 21 - 40\n",
+
+        "Warning: 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 j;
+
+      visible_cursor = True;
+      x = 0;
+      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, "Grid Node %d offline...\n", j);
+          hack_text_1 (state, &x, &y, buf,
+                       typing_delay, transmit_delay, long_delay,
+                       visible_cursor, True);
+
+        }
+      hack_text_1 (state, &x, &y, "\nFFF-CONTROL> ",
+                   typing_delay, transmit_delay, long_delay,
+                   visible_cursor, True);
+      return;
+    }
+  else
+    {
+      if (state->mode == TRACE0 || state->mode == TRACE1)
+        x = y = 0;
+      else
+        {
+          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);
+    }
+}
+
+
+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)
 {
@@ -614,7 +1094,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);
@@ -638,7 +1118,12 @@ roll_state (m_state *state)
     case KNOCK4: delay = 2; state->mode++; break; /* rabbit */
     case KNOCK5: delay = 4; state->mode++; break;
     case KNOCK6: delay = 4; state->mode++; break; /* knock */
+
+    case NMAP0:  delay = 4; state->mode++; break; /* knock */
+
     case KNOCK7:
+      delay = 4;
+    case NMAP1:
       state->mode = MATRIX;
       state->glyph_map = matrix_encoding;
       state->nglyphs = countof(matrix_encoding);
@@ -646,9 +1131,14 @@ 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 = NMAP0;
+          else
+            state->mode = KNOCK0;
+
           flip_images (state);
         }
       break;
@@ -661,11 +1151,8 @@ roll_state (m_state *state)
       break;
     }
 
-  if (delay)
-    {
-      XSync (state->dpy, False);
-      sleep (delay);
-    }
+  matrix_usleep (state, delay * 1000000);
+  state->cursor = NULL;
 }
 
 
@@ -679,6 +1166,7 @@ hack_matrix (m_state *state)
     case TRACE0: case TRACE1:
     case KNOCK0: case KNOCK1: case KNOCK2: case KNOCK3:
     case KNOCK4: case KNOCK5: case KNOCK6: case KNOCK7:
+    case NMAP0:  case NMAP1:
       hack_text (state);
       return;
     case TRACE2: case MATRIX: case DNA: case BINARY: case HEX:
@@ -744,88 +1232,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
@@ -854,7 +1263,7 @@ char *progclass = "XMatrix";
 
 char *defaults [] = {
   ".background:                   black",
-  ".foreground:                   green",
+  ".foreground:                   #00AA00",
   "*small:                ",
   "*delay:                10000",
   "*insert:               both",
@@ -862,6 +1271,8 @@ char *defaults [] = {
   "*tracePhone:            (212) 555-0690",
   "*spinners:             5",
   "*density:              75",
+  "*knockKnock:                   False",
+  "*geometry:             800x600",
   0
 };
 
@@ -874,10 +1285,12 @@ 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"},
+  { "-knock-knock",    ".knockKnock",          XrmoptionNoArg, "True" },
   { 0, 0, 0, 0 }
 };
 
@@ -891,7 +1304,7 @@ screenhack (Display *dpy, Window window)
     {
       draw_matrix (state);
       XSync (dpy, False);
-      screenhack_handle_events (dpy);
+      handle_events (state);
       if (delay) usleep (delay);
     }
 }