From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / hacks / bsod.c
index ffe27c4eff16139d7dc954cb915e3bb522d32b27..ff6c359fdd092114cf4def7a7b6611838c977447 100644 (file)
@@ -1,11 +1,11 @@
-/* xscreensaver, Copyright (c) 1998-2013 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1998-2018 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * the above copyright notice appear in all copies and that both that
  * copyright notice and this permission notice appear in supporting
  * documentation.  No representations are made about the suitability of this
- * software for any purpose.  It is provided "as is" without express or 
+ * software for any purpose.  It is provided "as is" without express or
  * implied warranty.
  *
  * Blue Screen of Death: the finest in personal computer emulation.
 
 
 #include "screenhack.h"
-#include "xpm-pixmap.h"
+#include "ximage-loader.h"
 #include "apple2.h"
 
 #include <ctype.h>
+#include <time.h>
 
 #ifdef HAVE_XSHM_EXTENSION
 #include "xshm.h"
 # include <sys/utsname.h>
 #endif /* HAVE_UNAME */
 
-#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM) || defined(HAVE_COCOA)
-# define DO_XPM
-#endif
-
-#ifdef DO_XPM
-# include "images/amiga.xpm"
-# include "images/hmac.xpm"
-# include "images/osx_10_2.xpm"
-# include "images/osx_10_3.xpm"
-# include "images/android.xpm"
-#endif
-#include "images/atari.xbm"
-#include "images/mac.xbm"
-#include "images/macbomb.xbm"
-#include "images/atm.xbm"
+#include "images/gen/amiga_png.h"
+#include "images/gen/hmac_png.h"
+#include "images/gen/osx_10_2_png.h"
+#include "images/gen/osx_10_3_png.h"
+#include "images/gen/android_png.h"
+#include "images/gen/ransomware_png.h"
+#include "images/gen/atari_png.h"
+#include "images/gen/mac_png.h"
+#include "images/gen/macbomb_png.h"
+#include "images/gen/apple_png.h"
+#include "images/gen/atm_png.h"
 
 #undef countof
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
+# undef MIN
+# undef MAX
+# define MIN(A,B) ((A)<(B)?(A):(B))
+# define MAX(A,B) ((A)>(B)?(A):(B))
+
 #undef EOF
-typedef enum { EOF=0, 
-               LEFT, CENTER, RIGHT, 
-               LEFT_FULL, CENTER_FULL, RIGHT_FULL, 
+typedef enum { EOF=0,
+               LEFT, CENTER, RIGHT,
+               LEFT_FULL, CENTER_FULL, RIGHT_FULL,
                COLOR, INVERT, MOVETO, MARGINS,
-               CURSOR_BLOCK, CURSOR_LINE, RECT, COPY, PIXMAP, IMG,
+               CURSOR_BLOCK, CURSOR_LINE, RECT, LINE, COPY, PIXMAP, IMG, FONT,
                PAUSE, CHAR_DELAY, LINE_DELAY,
-               LOOP, RESET
+               LOOP, RESET, VERT_MARGINS, CROP,
+               WRAP, WORD_WRAP, TRUNCATE
 } bsod_event_type;
 
 struct bsod_event {
@@ -87,18 +90,22 @@ struct bsod_state {
   Display *dpy;
   Window window;
   XWindowAttributes xgwa;
-  XFontStruct *font;
+  XFontStruct *font, *fontA, *fontB, *fontC;
   unsigned long fg, bg;
   GC gc;
   int left_margin, right_margin;       /* for text wrapping */
-  int top_margin, bottom_margin;       /* for text scrolling */
-  Bool wrap_p;
+  int top_margin, bottom_margin;       /* for text scrolling and cropping */
+  int xoff, yoff;
+  Bool wrap_p, word_wrap_p;
+  char word_buf[80];
   Bool scroll_p;
+  Bool crop_p;                  /* If True, chops off extra text vertically */
 
-  Pixmap pixmap;               /* Source image used by BSOD_PIXMAP */
+  Pixmap pixmap, mask;         /* Source image used by BSOD_PIXMAP */
 
   int x, y;                    /* current text-drawing position */
   int current_left;            /* don't use this */
+  int last_nonwhite;
 
   int pos;                     /* position in queue */
   int queue_size;
@@ -203,6 +210,16 @@ struct bsod_state {
   (bst)->pos++; \
   } while (0)
 
+/* Set the prevailing top/bottom margins (used when scrolling and cropping text)
+ */
+#define BSOD_VERT_MARGINS(bst,top,bottom) do { \
+  ensure_queue (bst); \
+  (bst)->queue[(bst)->pos].type = VERT_MARGINS; \
+  (bst)->queue[(bst)->pos].arg1 = (void *) ((long) (top)); \
+  (bst)->queue[(bst)->pos].arg2 = (void *) ((long) (bottom)); \
+  (bst)->pos++; \
+  } while (0)
+
 /* Draw a blinking cursor; type is CURSOR_BLOCK or CURSOR_LINE.
    usec is how long 1/2 of a cycle is.  count is how many times to blink.
    (You can pass a gigantic number if this is the last thing in your mode.)
@@ -228,6 +245,19 @@ struct bsod_state {
   (bst)->pos++; \
   } while (0)
 
+/* Draw a line.
+ */
+#define BSOD_LINE(bst,x,y,x2,y2,thick) do { \
+  ensure_queue (bst); \
+  (bst)->queue[(bst)->pos].type = LINE; \
+  (bst)->queue[(bst)->pos].arg1 = (void *) ((long) (x)); \
+  (bst)->queue[(bst)->pos].arg2 = (void *) ((long) (y)); \
+  (bst)->queue[(bst)->pos].arg3 = (void *) ((long) (x2)); \
+  (bst)->queue[(bst)->pos].arg4 = (void *) ((long) (y2)); \
+  (bst)->queue[(bst)->pos].arg5 = (void *) ((long) (thick)); \
+  (bst)->pos++; \
+  } while (0)
+
 /* Copy a rect from the window, to the window.
  */
 #define BSOD_COPY(bst,srcx,srcy,w,h,tox,toy) do { \
@@ -257,7 +287,16 @@ struct bsod_state {
   (bst)->pos++; \
   } while (0)
 
-/* Jump around in the state table.  You can use this as the last thing 
+/* Switch between fonts A, B and C.
+ */
+#define BSOD_FONT(bst,n) do { \
+  ensure_queue (bst); \
+  (bst)->queue[(bst)->pos].type = FONT; \
+  (bst)->queue[(bst)->pos].arg1 = (void *) ((long) (n)); \
+  (bst)->pos++; \
+  } while (0)
+
+/* Jump around in the state table.  You can use this as the last thing
    in your state table to repeat the last N elements forever.
  */
 #define BSOD_LOOP(bst,off) do { \
@@ -275,6 +314,33 @@ struct bsod_state {
   (bst)->pos++; \
   } while (0)
 
+/* Sets the crop state, if True, will not write text below the bottom_margin
+ */
+#define BSOD_CROP(bst, state) do { \
+  ensure_queue (bst); \
+  (bst)->queue[(bst)->pos].type = CROP; \
+  (bst)->queue[(bst)->pos].arg1 = (void *) ((long) (state)); \
+  (bst)->pos++; \
+  } while (0)
+
+#define BSOD_WRAP(bst) do { \
+  ensure_queue (bst); \
+ (bst)->queue[(bst)->pos].type = WRAP; \
+  (bst)->pos++; \
+  } while (0)
+
+#define BSOD_WORD_WRAP(bst) do { \
+  ensure_queue (bst); \
+  (bst)->queue[(bst)->pos].type = WORD_WRAP; \
+  (bst)->pos++; \
+  } while (0)
+
+#define BSOD_TRUNCATE(bst) do { \
+  ensure_queue (bst); \
+  (bst)->queue[(bst)->pos].type = TRUNCATE; \
+  (bst)->pos++; \
+  } while (0)
+
 
 static void
 ensure_queue (struct bsod_state *bst)
@@ -287,10 +353,10 @@ ensure_queue (struct bsod_state *bst)
   if (n < 100) n *= 2;
   n *= 1.2;
 
-  bst->queue = (struct bsod_event *) 
+  bst->queue = (struct bsod_event *)
     realloc (bst->queue, n * sizeof(*bst->queue));
   if (!bst->queue) abort();
-  memset (bst->queue + bst->queue_size, 0, 
+  memset (bst->queue + bst->queue_size, 0,
           (n - bst->queue_size) * sizeof(*bst->queue));
   bst->queue_size = n;
 }
@@ -324,11 +390,11 @@ position_for_text (struct bsod_state *bst, const char *line)
   switch (bst->queue[bst->pos].type) {
   case LEFT:
   case LEFT_FULL:
-    bst->current_left = bst->left_margin;
+    bst->current_left = bst->left_margin + bst->xoff;
     break;
   case RIGHT:
   case RIGHT_FULL:
-    bst->x = max_width - bst->right_margin;
+    bst->x = max_width - bst->right_margin - bst->xoff;
     bst->current_left = bst->x;
     break;
   case CENTER:
@@ -337,7 +403,7 @@ position_for_text (struct bsod_state *bst, const char *line)
       int w = (bst->xgwa.width - bst->left_margin - bst->right_margin -
                max_width);
       if (w < 0) w = 0;
-      bst->x = bst->left_margin + (w / 2);
+      bst->x = bst->left_margin + bst->xoff + (w / 2);
       bst->current_left = bst->x;
       break;
     }
@@ -353,20 +419,21 @@ bst_crlf (struct bsod_state *bst)
   int lh = bst->font->ascent + bst->font->descent;
   bst->x = bst->current_left;
   if (!bst->scroll_p ||
-      bst->y + lh < bst->xgwa.height - bst->bottom_margin)
+      bst->y + lh < bst->xgwa.height - bst->bottom_margin - bst->yoff)
     bst->y += lh;
   else
     {
       int w = bst->xgwa.width  - bst->right_margin - bst->left_margin;
       int h = bst->xgwa.height - bst->top_margin - bst->bottom_margin;
       XCopyArea (bst->dpy, bst->window, bst->window, bst->gc,
-                 bst->left_margin,
-                 bst->top_margin + lh,
+                 bst->left_margin + bst->xoff,
+                 bst->top_margin + bst->yoff + lh,
                  w, h - lh,
-                 bst->left_margin,
-                 bst->top_margin);
+                 bst->left_margin + bst->xoff,
+                 bst->top_margin + bst->yoff);
       XClearArea (bst->dpy, bst->window,
-                  bst->left_margin, bst->top_margin + h - lh, w, lh, False);
+                  bst->left_margin + bst->xoff,
+                  bst->top_margin + bst->yoff + h - lh, w, lh, False);
     }
 }
 
@@ -376,19 +443,26 @@ draw_char (struct bsod_state *bst, char c)
 {
   if (!c)
     abort();
+  else if (bst->crop_p && bst->y >=
+           (bst->xgwa.height - bst->bottom_margin - bst->yoff))
+    {
+      /* reached the bottom of the drawing area, and crop_p = True */
+      return;
+    }
   else if (c == '\r')
     {
       bst->x = bst->current_left;
+      bst->last_nonwhite = bst->x;
     }
   else if (c == '\n')
     {
       if (bst->macx_eol_kludge)
         {
           /* Special case for the weird way OSX crashes print newlines... */
-          XDrawImageString (bst->dpy, bst->window, bst->gc, 
+          XDrawImageString (bst->dpy, bst->window, bst->gc,
                             bst->x, bst->y, " ", 1);
-          XDrawImageString (bst->dpy, bst->window, bst->gc, 
-                            bst->x, 
+          XDrawImageString (bst->dpy, bst->window, bst->gc,
+                            bst->x,
                             bst->y + bst->font->ascent + bst->font->descent,
                             " ", 1);
         }
@@ -400,8 +474,8 @@ draw_char (struct bsod_state *bst, char c)
                ? bst->font->per_char['n'-bst->font->min_char_or_byte2].width
                : bst->font->min_bounds.width);
       bst->x -= cw;
-      if (bst->x < bst->left_margin)
-        bst->x = bst->left_margin;
+      if (bst->x < bst->left_margin + bst->xoff)
+        bst->x = bst->left_margin + bst->xoff;
     }
   else
     {
@@ -409,13 +483,60 @@ draw_char (struct bsod_state *bst, char c)
       XCharStruct ov;
       XTextExtents (bst->font, &c, 1, &dir, &ascent, &descent, &ov);
 
-      if (bst->wrap_p && 
-          bst->x + ov.width > bst->xgwa.width - bst->right_margin)
-        bst_crlf (bst);
+      if ((bst->wrap_p || bst->word_wrap_p) &&
+          bst->x + ov.width > bst->xgwa.width - bst->right_margin - bst->xoff)
+        {
+          XCharStruct ov2;
+          int L = 0;
+
+          if (bst->word_wrap_p && *bst->word_buf)
+            {
+              L = strlen(bst->word_buf);
+              XTextExtents (bst->font, bst->word_buf, L,
+                      &dir, &ascent, &descent, &ov2);
+            }
+
+          if (L)  /* Erase the truncated wrapped word */
+            {
+              XSetForeground (bst->dpy, bst->gc, bst->bg);
+              XFillRectangle (bst->dpy, bst->window, bst->gc,
+                              bst->last_nonwhite,
+                              bst->y - bst->font->ascent,
+                              ov2.width,
+                              bst->font->ascent + bst->font->descent);
+              XSetForeground (bst->dpy, bst->gc, bst->fg);
+            }
+
+          bst_crlf (bst);
+
+          if (L)  /* Draw wrapped partial word on the next line, no delay */
+            {
+              XDrawImageString (bst->dpy, bst->window, bst->gc,
+                                bst->x, bst->y, bst->word_buf, L);
+              bst->x += ov2.width;
+              bst->last_nonwhite = bst->x;
+            }
+        }
 
-      XDrawImageString (bst->dpy, bst->window, bst->gc, 
+      XDrawImageString (bst->dpy, bst->window, bst->gc,
                         bst->x, bst->y, &c, 1);
       bst->x += ov.width;
+
+      if (bst->word_wrap_p)
+        {
+          if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+            {
+              bst->word_buf[0] = 0;
+              bst->last_nonwhite = bst->x;
+            }
+          else
+            {
+              int L = strlen (bst->word_buf);
+              if (L >= sizeof(bst->word_buf)-1) abort();
+              bst->word_buf[L] = c;
+              bst->word_buf[L+1] = 0;
+            }
+        }
     }
 }
 
@@ -443,8 +564,13 @@ bsod_pop (struct bsod_state *bst)
       if (! *s)
         {
           long delay = bst->line_delay;
+          /* Reset the string back to the beginning, in case we loop. */
+          bst->queue[bst->pos].arg2 = bst->queue[bst->pos].arg1;
+          bst->queue[bst->pos].arg3 = 0;
+          bst->queue[bst->pos].type = (bsod_event_type) 
+            bst->queue[bst->pos].arg4;
           bst->pos++;
-          bst->current_left = bst->left_margin;
+          bst->current_left = bst->left_margin + bst->xoff;
           return delay;
         }
 
@@ -453,6 +579,8 @@ bsod_pop (struct bsod_state *bst)
           position_for_text (bst, s);
           bst->queue[bst->pos].arg4 = (void *) bst->queue[bst->pos].type;
           bst->queue[bst->pos].type = LEFT;
+          bst->word_buf[0] = 0;
+          bst->last_nonwhite = bst->x;
 
           if (type == CENTER_FULL ||
               type == LEFT_FULL ||
@@ -500,6 +628,8 @@ bsod_pop (struct bsod_state *bst)
     {
       bst->x = (long) bst->queue[bst->pos].arg1;
       bst->y = (long) bst->queue[bst->pos].arg2;
+      bst->word_buf[0] = 0;
+      bst->last_nonwhite = bst->x;
       bst->pos++;
       return 0;
     }
@@ -517,6 +647,20 @@ bsod_pop (struct bsod_state *bst)
       bst->pos++;
       return 0;
     }
+  case LINE:
+    {
+      int x1 = (long) bst->queue[bst->pos].arg1;
+      int y1 = (long) bst->queue[bst->pos].arg2;
+      int x2 = (long) bst->queue[bst->pos].arg3;
+      int y2 = (long) bst->queue[bst->pos].arg4;
+      int t  = (long) bst->queue[bst->pos].arg5;
+      XGCValues gcv;
+      gcv.line_width = t;
+      XChangeGC (bst->dpy, bst->gc, GCLineWidth, &gcv);
+      XDrawLine (bst->dpy, bst->window, bst->gc, x1, y1, x2, y2);
+      bst->pos++;
+      return 0;
+    }
   case COPY:
   case PIXMAP:
     {
@@ -526,22 +670,41 @@ bsod_pop (struct bsod_state *bst)
       int h    = (long) bst->queue[bst->pos].arg4;
       int tox  = (long) bst->queue[bst->pos].arg5;
       int toy  = (long) bst->queue[bst->pos].arg6;
-      XCopyArea (bst->dpy, 
-                 (type == PIXMAP ? bst->pixmap : bst->window), 
+      if (type == PIXMAP && bst->mask)
+        {
+          XSetClipMask (bst->dpy, bst->gc, bst->mask);
+          XSetClipOrigin (bst->dpy, bst->gc, tox, toy);
+        }
+      XCopyArea (bst->dpy,
+                 (type == PIXMAP ? bst->pixmap : bst->window),
                  bst->window, bst->gc,
                  srcx, srcy, w, h, tox, toy);
+      if (type == PIXMAP && bst->mask)
+        XSetClipMask (bst->dpy, bst->gc, None);
       bst->pos++;
       return 0;
     }
   case IMG:
     {
       if (bst->img_loader) abort();
-      bst->img_loader = load_image_async_simple (0, bst->xgwa.screen, 
+      bst->img_loader = load_image_async_simple (0, bst->xgwa.screen,
                                                  bst->window, bst->window,
                                                  0, 0);
       bst->pos++;
       return 0;
     }
+  case FONT:
+    {
+      switch ((long) bst->queue[bst->pos].arg1) {
+      case 0: bst->font = bst->fontA; break;
+      case 1: bst->font = bst->fontB; break;
+      case 2: bst->font = bst->fontC; break;
+      default: abort(); break;
+      }
+      XSetFont (bst->dpy, bst->gc, bst->font->fid);
+      bst->pos++;
+      return 0;
+    }
   case PAUSE:
     {
       long delay = (long) bst->queue[bst->pos].arg1;
@@ -567,6 +730,13 @@ bsod_pop (struct bsod_state *bst)
       bst->pos++;
       return 0;
     }
+  case VERT_MARGINS:
+    {
+      bst->top_margin    = (long) bst->queue[bst->pos].arg1;
+      bst->bottom_margin = (long) bst->queue[bst->pos].arg2;
+      bst->pos++;
+      return 0;
+    }
   case CURSOR_BLOCK:
   case CURSOR_LINE:
     {
@@ -598,6 +768,24 @@ bsod_pop (struct bsod_state *bst)
 
       return delay;
     }
+  case WRAP:
+    bst->wrap_p = 1;
+    bst->word_wrap_p = 0;
+    bst->pos++;
+    return 0;
+
+  case WORD_WRAP:
+    bst->wrap_p = 0;
+    bst->word_wrap_p = 1;
+    bst->pos++;
+    return 0;
+
+  case TRUNCATE:
+    bst->wrap_p = 0;
+    bst->word_wrap_p = 0;
+    bst->pos++;
+    return 0;
+
   case LOOP:
     {
       long off = (long) bst->queue[bst->pos].arg1;
@@ -623,6 +811,12 @@ bsod_pop (struct bsod_state *bst)
       bst->pos = 0;
       return 0;
     }
+  case CROP:
+    {
+      bst->crop_p = (long) bst->queue[bst->pos].arg1;
+      bst->pos++;
+      return 0;
+    }
   case EOF:
     {
       bst->pos = -1;
@@ -642,8 +836,9 @@ make_bsod_state (Display *dpy, Window window,
   XGCValues gcv;
   struct bsod_state *bst;
   char buf1[1024], buf2[1024];
-  char buf3[1024], buf4[1024];
-  const char *font1, *font2;
+  char buf5[1024], buf6[1024];
+  char buf7[1024], buf8[1024];
+  const char *font1, *font3, *font4;
 
   bst = (struct bsod_state *) calloc (1, sizeof (*bst));
   bst->queue_size = 10;
@@ -653,14 +848,9 @@ make_bsod_state (Display *dpy, Window window,
   bst->window = window;
   XGetWindowAttributes (dpy, window, &bst->xgwa);
 
-  /* If the window is small:
-       use ".font" if it is loadable, else use ".font2".
-
-     If the window is big:
-       use ".bigFont" if it is loadable, else use ".bigFont2".
-   */
+  /* If the window is small, use ".font"; if big, ".bigFont". */
   if (
-# ifdef USE_IPHONE
+# ifdef HAVE_MOBILE
       1
 # else
       bst->xgwa.height < 640
@@ -669,34 +859,53 @@ make_bsod_state (Display *dpy, Window window,
     {
       sprintf (buf1, "%.100s.font", name);
       sprintf (buf2, "%.100s.font", class);
-      sprintf (buf3, "%.100s.font2", name);
-      sprintf (buf4, "%.100s.font2", class);
     }
   else
     {
       sprintf (buf1, "%.100s.bigFont", name);
       sprintf (buf2, "%.100s.bigFont", class);
-      sprintf (buf3, "%.100s.bigFont2", name);
-      sprintf (buf4, "%.100s.bigFont2", class);
     }
+  sprintf (buf5, "%.100s.fontB", name);
+  sprintf (buf6, "%.100s.fontB", class);
+  sprintf (buf7, "%.100s.fontC", name);
+  sprintf (buf8, "%.100s.fontC", class);
 
   font1 = get_string_resource (dpy, buf1, buf2);
-  font2 = get_string_resource (dpy, buf3, buf4);
+  font3 = get_string_resource (dpy, buf5, buf6);
+  font4 = get_string_resource (dpy, buf7, buf8);
 
-  if (font1)
-    bst->font = XLoadQueryFont (dpy, font1);
-  if (! bst->font && font2)
-    bst->font = XLoadQueryFont (dpy, font2);
-
-  /* If neither of those worked, try some defaults. */
+  /* If there was no ".mode.font" resource also look for ".font".
+     Under real X11, the wildcard does this, so this is redundant,
+     but jwxyz needs it because it doesn't implement wildcards.
+   */
+# define RES2(VAR, BUF1, BUF2) do {                          \
+    if (! VAR) {                                             \
+      VAR = get_string_resource (dpy,                        \
+                                strchr (BUF1, '.') + 1,     \
+                                strchr (BUF2, '.') + 1);    \
+    }} while(0)
+  RES2 (font1, buf1, buf2);
+  RES2 (font3, buf5, buf6);
+  RES2 (font4, buf7, buf8);
+#undef RES2
+
+  if (font1 && *font1)
+    bst->font = load_font_retry (dpy, font1);
 
-  if (! bst->font)
-    bst->font = XLoadQueryFont (dpy,"-*-courier-bold-r-*-*-*-120-*-*-m-*-*-*");
-  if (! bst->font)
-    bst->font = XLoadQueryFont (dpy, "fixed");
   if (! bst->font)
     abort();
 
+  if (font3 && *font3)
+    bst->fontB = load_font_retry (dpy, font3);
+  if (font4 && *font4)
+    bst->fontC = load_font_retry (dpy, font4);
+
+  if (! bst->fontB) bst->fontB = bst->font;
+  if (! bst->fontC) bst->fontC = bst->font;
+
+  bst->fontA = bst->font;
+
+
   gcv.font = bst->font->fid;
 
   sprintf (buf1, "%.100s.foreground", name);
@@ -709,13 +918,25 @@ make_bsod_state (Display *dpy, Window window,
                                                  buf1, buf2);
   bst->gc = XCreateGC (dpy, window, GCFont|GCForeground|GCBackground, &gcv);
 
-#ifdef HAVE_COCOA
-  jwxyz_XSetAntiAliasing (dpy, bst->gc, False);
+#ifdef HAVE_JWXYZ
+  jwxyz_XSetAntiAliasing (dpy, bst->gc, True);
 #endif
 
+# ifdef USE_IPHONE
+  /* Stupid iPhone X bezel.
+     #### This is the worst of all possible ways to do this!
+   */
+  if (bst->xgwa.width == 2436 || bst->xgwa.height == 2436) {
+    if (bst->xgwa.width > bst->xgwa.height)
+      bst->xoff = 96;
+    else
+      bst->yoff = 96;
+  }
+# endif
+
   bst->left_margin = bst->right_margin = 10;
-  bst->x = bst->left_margin;
-  bst->y = bst->font->ascent + bst->left_margin;
+  bst->x = bst->left_margin + bst->xoff;
+  bst->y = bst->font->ascent + bst->left_margin + bst->yoff;
 
   XSetWindowBackground (dpy, window, gcv.background);
   return bst;
@@ -731,6 +952,8 @@ free_bsod_state (struct bsod_state *bst)
     bst->free_cb (bst);
   if (bst->pixmap)
     XFreePixmap(bst->dpy, bst->pixmap);
+  if (bst->mask)
+    XFreePixmap(bst->dpy, bst->mask);
 
   XFreeFont (bst->dpy, bst->font);
   XFreeGC (bst->dpy, bst->gc);
@@ -752,14 +975,18 @@ free_bsod_state (struct bsod_state *bst)
 
 
 static Pixmap
-double_pixmap (Display *dpy, GC gc, Visual *visual, int depth, Pixmap pixmap,
+double_pixmap (Display *dpy, Visual *visual, int depth, Pixmap pixmap,
                int pix_w, int pix_h)
 {
   int x, y;
   Pixmap p2 = XCreatePixmap(dpy, pixmap, pix_w*2, pix_h*2, depth);
-  XImage *i1 = XGetImage(dpy, pixmap, 0, 0, pix_w, pix_h, ~0L, ZPixmap);
-  XImage *i2 = XCreateImage(dpy, visual, depth, ZPixmap, 0, 0,
-                           pix_w*2, pix_h*2, 8, 0);
+  XImage *i1 = XGetImage (dpy, pixmap, 0, 0, pix_w, pix_h, ~0L, 
+                          (depth == 1 ? XYPixmap : ZPixmap));
+  XImage *i2 = XCreateImage (dpy, visual, depth, 
+                             (depth == 1 ? XYPixmap : ZPixmap), 0, 0,
+                             pix_w*2, pix_h*2, 8, 0);
+  XGCValues gcv;
+  GC gc = XCreateGC (dpy, p2, 0, &gcv);
   i2->data = (char *) calloc(i2->height, i2->bytes_per_line);
   for (y = 0; y < pix_h; y++)
     for (x = 0; x < pix_w; x++)
@@ -773,6 +1000,7 @@ double_pixmap (Display *dpy, GC gc, Visual *visual, int depth, Pixmap pixmap,
   free(i1->data); i1->data = 0;
   XDestroyImage(i1);
   XPutImage(dpy, p2, gc, i2, 0, 0, 0, 0, i2->width, i2->height);
+  XFreeGC (dpy, gc);
   free(i2->data); i2->data = 0;
   XDestroyImage(i2);
   XFreePixmap(dpy, pixmap);
@@ -787,6 +1015,9 @@ static struct bsod_state *
 windows_31 (Display *dpy, Window window)
 {
   struct bsod_state *bst = make_bsod_state (dpy, window, "windows", "Windows");
+
+  bst->xoff = bst->left_margin = bst->right_margin = 0;
+
   BSOD_INVERT (bst);
   BSOD_TEXT   (bst, CENTER, "Windows\n");
   BSOD_INVERT (bst);
@@ -801,7 +1032,7 @@ windows_31 (Display *dpy, Window window)
                "\n");
   BSOD_TEXT   (bst, CENTER, "Press any key to continue");
 
-  bst->y = ((bst->xgwa.height -
+  bst->y = ((bst->xgwa.height - bst->yoff -
              ((bst->font->ascent + bst->font->descent) * 9))
             / 2);
 
@@ -809,11 +1040,69 @@ windows_31 (Display *dpy, Window window)
   return bst;
 }
 
+static struct bsod_state *
+vmware (Display *dpy, Window window)
+{
+  struct bsod_state *bst = make_bsod_state (dpy, window, "vmware", "VMware");
+
+  unsigned long fg = bst->fg;
+  unsigned long bg = bst->bg;
+  unsigned long fg2 = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                          "vmware.foreground2",
+                                          "vmware.foreground");
+  BSOD_COLOR (bst, fg2, bg);
+  BSOD_TEXT   (bst, LEFT,
+               "VMware ESX Server [Releasebuild-98103]\n");
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_TEXT   (bst, LEFT,
+               "PCPU 1 locked up. Failed to ack TLB invalidate.\n"
+               "frame=0x3a37d98 ip=0x625e94 cr2=0x0 cr3=0x40c66000 cr4=0x16c\n"
+               "es=0xffffffff ds=0xffffffff fs=0xffffffff gs=0xffffffff\n"
+               "eax=0xffffffff ebx=0xffffffff ecx=0xffffffff edx=0xffffffff\n"
+               "ebp=0x3a37ef4 esi=0xffffffff edi=0xffffffff err=-1 eflags=0xffffffff\n"
+               "*0:1037/helper1-4 1:1107/vmm0:Fagi 2:1121/vmware-vm 3:1122/mks:Franc\n"
+               "0x3a37ef4:[0x625e94]Panic+0x17 stack: 0x833ab4, 0x3a37f10, 0x3a37f48\n"
+               "0x3a37f04:[0x625e94]Panic+0x17 stack: 0x833ab4, 0x1, 0x14a03a0\n"
+               "0x3a37f48:[0x64bfa4]TLBDoInvalidate+0x38f stack: 0x3a37f54, 0x40, 0x2\n"
+               "0x3a37f70:[0x66da4d]XMapForceFlush+0x64 stack: 0x0, 0x4d3a, 0x0\n"
+               "0x3a37fac:[0x652b8b]helpFunc+0x2d2 stack: 0x1, 0x14a4580, 0x0\n"
+               "0x3a37ffc:[0x750902]CpuSched_StartWorld+0x109 stack: 0x0, 0x0, 0x0\n"
+               "0x3a38000:[0x0]blk_dev+0xfd76461f stack: 0x0, 0x0, 0x0\n"
+               "VMK uptime: 7:05:43:45.014 TSC: 1751259712918392\n"
+               "Starting coredump to disk\n");
+  BSOD_CHAR_DELAY (bst, 10000);                
+  BSOD_TEXT (bst, LEFT,        "using slot 1 of 1... ");
+  BSOD_CHAR_DELAY (bst, 300000);
+  BSOD_TEXT (bst, LEFT, "9876");
+  BSOD_CHAR_DELAY (bst, 3000000);
+  BSOD_TEXT (bst, LEFT, "66665");
+  BSOD_CHAR_DELAY (bst, 100000);
+  BSOD_TEXT (bst, LEFT, "4321");
+  BSOD_CHAR_DELAY (bst, 0);
+  BSOD_TEXT (bst, LEFT, "Disk dump successfull.\n"
+               "Waiting for Debugger (world 1037)\n"
+               "Debugger is listening on serial port ...\n");
+  BSOD_CHAR_DELAY (bst, 10000);
+  BSOD_TEXT (bst, LEFT, "Press Escape to enter local debugger\n");
+  BSOD_CHAR_DELAY (bst, 10000);
+  BSOD_TEXT (bst, LEFT, "Remote debugger activated. Local debugger no longer available.\n");
+
+/*  BSOD_CURSOR (bst, CURSOR_LINE, 240000, 999999);*/
+               
+/*  bst->y = ((bst->xgwa.height - bst->yoff -
+             ((bst->font->ascent + bst->font->descent) * 9))
+            / 2);*/
+
+  XClearWindow (dpy, window);
+  return bst;
+}
+
+
 
 static struct bsod_state *
 windows_nt (Display *dpy, Window window)
 {
-  struct bsod_state *bst = make_bsod_state (dpy, window, "windows", "Windows");
+  struct bsod_state *bst = make_bsod_state (dpy, window, "nt", "NT");
 
   BSOD_TEXT (bst, LEFT,
    "*** STOP: 0x0000001E (0x80000003,0x80106fc0,0x8025ea21,0xfd6829e8)\n"
@@ -905,7 +1194,7 @@ windows_me (Display *dpy, Window window)
   BSOD_CURSOR (bst, CURSOR_LINE, 120000, 999999);
 
   bst->left_margin = 40;
-  bst->y = ((bst->xgwa.height -
+  bst->y = ((bst->xgwa.height - bst->yoff -
              ((bst->font->ascent + bst->font->descent) * 3))
             / 2);
 
@@ -963,7 +1252,7 @@ windows_xp (Display *dpy, Window window)
 static struct bsod_state *
 windows_lh (Display *dpy, Window window)
 {
-  struct bsod_state *bst = 
+  struct bsod_state *bst =
     make_bsod_state (dpy, window, "windowslh", "WindowsLH");
 
   unsigned long fg = bst->fg;
@@ -999,7 +1288,8 @@ windows_lh (Display *dpy, Window window)
      "or computer\n"
      "manufacturer.\n"
      );
-  BSOD_MOVETO (bst, bst->left_margin, bst->xgwa.height - bst->font->descent);
+  BSOD_MOVETO (bst, bst->left_margin + bst->xoff,
+               bst->xgwa.height - bst->yoff - bst->font->descent);
   BSOD_COLOR (bst, bg, bg2);
   BSOD_TEXT (bst, LEFT_FULL, " SPACE=Continue\n");
 
@@ -1010,6 +1300,177 @@ windows_lh (Display *dpy, Window window)
 }
 
 
+static struct bsod_state *
+windows_10_update (Display *dpy, Window window)
+{
+  struct bsod_state *bst =
+    make_bsod_state (dpy, window, "win10", "Win10");
+  const char *fmt  = "Working on updates  %d%%";
+  char        line1[255];
+  const char *line2 = "Don't turn off your PC. This will take a while.";
+  const char *line3 = "Your PC will restart several times.";
+  int line_height = bst->fontA->ascent + bst->fontA->descent;
+  int y1 = bst->xgwa.height / 2 - line_height * 4;
+  int y2 = bst->xgwa.height - bst->yoff - line_height * 3;
+  int pct;
+
+  /* TODO: win10_spinner.gif goes at (y - line_height * 2 - 64). */
+
+  for (pct = 0; pct < 98; pct++)
+    {
+      BSOD_MOVETO (bst, 0, y1);
+      sprintf (line1, fmt, pct);
+      BSOD_TEXT (bst, CENTER, line1);
+      BSOD_MOVETO (bst, 0, y1 + line_height);
+      BSOD_TEXT (bst, CENTER, line2);
+      BSOD_MOVETO (bst, 0, y2);
+      BSOD_TEXT (bst, CENTER, line3);
+      BSOD_PAUSE (bst, 200000 + (random() % 3000000) + (random() % 3000000));
+    }
+
+  XClearWindow (dpy, window);
+  return bst;
+}
+
+
+static struct bsod_state *
+windows_10 (Display *dpy, Window window)
+{
+  struct bsod_state *bst =
+    make_bsod_state (dpy, window, "win10", "Win10");
+
+  int qr_width  = 41;
+  int qr_height = 41;
+  static const unsigned char qr_bits[] = {
+    0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,
+    0x03,0x9A,0x70,0xEE,0x80,0x01,0xFB,0x22,0xAA,0xA6,0xBE,0x01,
+    0x8B,0x8E,0x74,0xE7,0xA2,0x01,0x8B,0xEE,0x42,0xC4,0xA2,0x01,
+    0x8B,0x42,0x6E,0xED,0xA2,0x01,0xFB,0xDA,0x63,0xA6,0xBE,0x01,
+    0x03,0xAA,0xAA,0xAA,0x80,0x01,0xFF,0x8B,0xD8,0x9D,0xFF,0x01,
+    0x63,0x62,0xDA,0x1B,0x98,0x01,0x6F,0x67,0x98,0x9F,0xBC,0x01,
+    0x4F,0xCC,0x55,0x81,0x83,0x01,0xB7,0x6D,0xFF,0x68,0xB2,0x01,
+    0xC3,0x10,0x87,0x8B,0x96,0x01,0x6F,0xB1,0x91,0x58,0x94,0x01,
+    0xE3,0x36,0x88,0x84,0xB8,0x01,0x83,0x9B,0xFE,0x59,0xD7,0x01,
+    0x3B,0x74,0x98,0x5C,0xB4,0x01,0x37,0x75,0xDC,0x91,0xA6,0x01,
+    0x77,0xDE,0x01,0x54,0xBA,0x01,0xBB,0x6D,0x8B,0xB9,0xB5,0x01,
+    0x1F,0x06,0xBD,0x9B,0xB4,0x01,0xD3,0xBD,0x91,0x19,0x84,0x01,
+    0x0B,0x20,0xD8,0x91,0xB4,0x01,0x33,0x95,0xBC,0x0A,0xD5,0x01,
+    0xB3,0x60,0xDC,0xD9,0xB6,0x01,0xEF,0x77,0x18,0x09,0xA4,0x01,
+    0xA3,0xC2,0x95,0x51,0xB2,0x01,0xDF,0x63,0xDB,0xBE,0xB3,0x01,
+    0x03,0x08,0xC9,0x09,0xF0,0x01,0xFF,0xA3,0x19,0xBD,0xFB,0x01,
+    0x03,0x2E,0x84,0xA5,0xAA,0x01,0xFB,0x9A,0xFC,0x9B,0xBB,0x01,
+    0x8B,0x7E,0x9C,0x1D,0xB0,0x01,0x8B,0x6E,0x58,0xA1,0xDB,0x01,
+    0x8B,0xDA,0xD5,0x65,0xA2,0x01,0xFB,0x72,0xFB,0xE9,0xF0,0x01,
+    0x03,0x02,0x99,0x3B,0xB3,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,
+    0xFF,0xFF,0xFF,0xFF,0xFF,0x01};
+  Pixmap pixmap;
+
+  const char *lines[] = {
+    ":(\n",
+    "\n",
+    "Your PC ran into a problem and needs to restart. We're just\n",
+    "collecting some error info, and then we'll restart for you.\n",
+    "\n",
+    "\n",
+    "\n",
+    "For more information about this issue and\n",
+    "possible fixes, visit\n",
+/*  "https://www.jwz.org/xscreensaver\n",*/
+    "http://youtu.be/-RjmN9RZyr4\n",
+    "\n",
+    "If you call a support person, give them this info:\n",
+    "Stop code CRITICAL_PROCESS_DIED",
+ };
+  int i, y = 0, y0 = 0;
+  int line_height0 = bst->fontB->ascent;
+  int line_height1 = bst->fontA->ascent + bst->fontA->descent;
+  int line_height2 = bst->fontC->ascent + bst->fontC->descent;
+  int line_height = line_height0;
+  int top, left0, left;
+  int stop = 60 + (random() % 39);
+
+  if (!(random() % 7))
+    return windows_10_update (dpy, window);
+
+
+  line_height1 *= 1.3;
+  line_height2 *= 1.5;
+
+  top = ((bst->xgwa.height - bst->yoff
+          - (line_height0 * 1 +
+             line_height1 * 6 +
+             line_height2 * 6))
+         / 2);
+
+  {
+    int dir, ascent, descent;
+    XCharStruct ov;
+    const char *s = lines[2];
+    XTextExtents (bst->fontA, s, strlen(s),
+                  &dir, &ascent, &descent, &ov);
+    left = left0 = (bst->xgwa.width - ov.width) / 2;
+  }
+
+  pixmap = XCreatePixmapFromBitmapData (dpy, window, (char *) qr_bits,
+                                        qr_width, qr_height,
+                                        bst->fg, bst->bg, bst->xgwa.depth);
+  {
+    int n = 2;
+    if (bst->xgwa.width > 2560) n++;  /* Retina displays */
+    for (i = 0; i < n; i++)
+      {
+        pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
+                                pixmap, qr_width, qr_height);
+        qr_width *= 2;
+        qr_height *= 2;
+      }
+  }
+  bst->pixmap = pixmap;
+
+  y = top;
+  line_height = line_height0;
+  BSOD_FONT (bst, 1);
+  for (i = 0; i < countof(lines); i++)
+    {
+      BSOD_MOVETO (bst, left, y);
+      BSOD_TEXT (bst, LEFT, lines[i]);
+      y += line_height;
+      if (i == 0)
+        {
+          BSOD_FONT (bst, 0);
+          line_height = line_height1;
+        }
+      else if (i == 4)
+        {
+          y0 = y;
+          y += line_height / 2;
+          BSOD_PIXMAP (bst, 0, 0, qr_width, qr_height, left, y + line_height1);
+          BSOD_FONT (bst, 2);
+          line_height = line_height2;
+          left += qr_width + line_height2 / 2;
+# ifdef HAVE_MOBILE
+          y -= 14;
+# endif
+        }
+    }
+
+  left = left0;
+  BSOD_FONT (bst, 0);
+  for (i = 0; i <= stop; i++)
+    {
+      char buf[100];
+      sprintf (buf, "%d%% complete", i);
+      BSOD_MOVETO (bst, left, y0);
+      BSOD_TEXT (bst, LEFT, buf);
+      BSOD_PAUSE (bst, 85000);
+    }
+  BSOD_PAUSE (bst, 3000000);
+
+  XClearWindow (dpy, window);
+  return bst;
+}
+
+
 static struct bsod_state *
 windows_other (Display *dpy, Window window)
 {
@@ -1025,6 +1486,476 @@ windows_other (Display *dpy, Window window)
   }
 }
 
+/* Windows and Apple2 ransomware.
+ */
+static struct bsod_state *apple2ransomware (Display *, Window);
+
+static struct bsod_state *
+windows_ransomware (Display *dpy, Window window)
+{
+  struct bsod_state *bst = make_bsod_state (dpy, window,
+                                            "ransomware", "Ransomware");
+  char buf[1024];
+
+  int pix_w = 0, pix_h = 0;
+  Pixmap mask = 0;
+  Pixmap pixmap = image_data_to_pixmap (dpy, window,
+                                        ransomware_png, sizeof(ransomware_png),
+                                        &pix_w, &pix_h, &mask);
+  int i, n = 0;
+
+  /* Don't start the countdown from the start, advance the deadline by 3 - 30
+     hours */
+  int advance_deadline = (random() % 97200) + 10800;
+
+  time_t now = time(NULL);
+  const time_t stage1_deadline = now + 259200 - advance_deadline; /* 3 days */
+  const time_t stage2_deadline = now + 604800 - advance_deadline; /* 7 days */
+  char stage1_deadline_str[25], stage2_deadline_str[25];
+  char countdown_str[16];
+  int countdown_d, countdown_h, countdown_m, countdown_s, countdown_r;
+  int line_height  = bst->font->ascent + bst->font->descent;
+  int line_height1 = bst->fontA->ascent + bst->fontA->descent;
+
+  const char *currencies[] = {
+    "Blitcoin",
+    "Bitcorn",
+    "Buttcorn",
+    "clicks",
+    "clicks",
+    "Ass Pennies",
+    "Ass Pennies",
+    "Dollary-doos",
+    "Dunning-Krugerrands",
+    "Dunning-Krugerrands",
+    "Dunning-Krugerrands",
+    "Dunning-Krugerrands",
+    "Dunning-Krugerrands",
+    "Dunning-Krugerrands",
+    "gift certificates",
+    "Creepto-Currency",
+    "secret sauce",
+    "Tribbles",
+  };
+
+  const char *currency = currencies[random() % countof(currencies)];
+
+  const char *header_quips[] = {
+    "Oops, your screens have been encrypted!",
+    "Oops, your screens have been encrypted!",
+    "Oops, your screens have been encrypted!",
+    "Oops, your screens have been encrypted!",
+    "Oops, your screen have encrypted!",
+    "Oops, you're screens have been encrypted!",
+    "Oops, your screens have been encrupted!",
+    "Oops, your screens have been encrumpet!",
+    "Oops, your screens have been encrusted!",
+    "If you don't pay this ransom, then you are a theif!",
+    "Your screen was subject to the laws of mathomatics!",
+    "Oops, your screen was shaved by Occam's Razor!",
+    "Oops, your screen was perturbated by Langford's Basilisk!",
+    "Your screen is now stored as Snapchat messages!",
+    "Oops, your screen is now stored on Betamax!",
+    "Oops, your screen is now in the clown!",
+    "Oops, your screen has been deprecated!",
+    "Oops, you're screen was seized by the FBI!",
+    "All your screen was shared with your coworkers!",
+    "All your screen are belong to us.",
+    "Well actually, your screen isn't needed anymore.",
+    "u just got popped with some 0day shit!!",
+    "M'lady,",
+  };
+
+  const char *header_quip = header_quips[random() % countof(header_quips)];
+
+  /* You got this because... */
+  const char *excuse_quips[] = {
+    "all human actions are equivalent and all are on principle doomed "
+    "to failure",
+    "you hold a diverse portfolio of cryptocurrencies",
+    "you need to get in on ransomware futures at the ground floor",
+    "your flight was overbooked",
+    "you did not apply the security update for bugs NSA keys secret from "
+    "Microsoft in your Windows(R) operating system",
+    "you are bad and you should feel bad",
+    "you used the wifi at defcon",
+    "you lack official Clown Strike[TM] threaty threat technology",
+  };
+
+  const char *excuse_quip = excuse_quips[random() % countof(excuse_quips)];
+
+  /* WELL ACTUALLY, screensavers aren't really nescessary anymore because... */
+  const char *screensaver_quips[] = {
+    "I read it on hacker news",
+    "that's official Debian policy now",
+    "that is the official policy of United Airlines",
+    "they cause global warming",
+    "they lack an eternal struggle",
+    "they lack a vapid dichotomy",
+    "those electrons could be used for gold farming instead",
+    "you can make more money in art exhibitions",
+  };
+
+  const char *screensaver_quip =
+    screensaver_quips[random() % countof(screensaver_quips)];
+
+  const char *lines[] = {
+    "*What Happened To My Computer?\n",
+    "Your important pixels are paintcrypted. All of your documents, photos, ",
+    "videos, databases, icons, dick pics are not accessible because they ",
+    "have been bitblted. Maybe you are looking for a way to get them back, ",
+    "but don't waste your time. Nobody can recover your pixels without our ",
+    "pointer motion clicker services.\n",
+    "\n",
+    "*Can I Recover My Important Dick Pix?\n",
+    "Yes. We guarantee that you can recover them safely and easily. But you ",
+    "not have much time.\n",
+    "You can expose some files for free. Try it now by pressing <The Any ",
+    "Key>.\n",
+    "But if you want to unsave all your screens, then you need to pay. ",
+    "You have only 3 days to click. After that the clicks will double. ",
+    "After 7 days your pixels will be gone forever.\n",
+    "We will have free events for cheapskates who can't pay in 6 months, ",
+    "long after all the pixels are xored.\n",
+    "\n",
+    "*How do I pay?\n",
+    "Payment is accepted in ", "[C]",
+    " only. For more information, press <About ", "[C]", ">.",
+    " Please check the current price of ", "[C]", " and buy some ", "[C]",
+    ". For more information, press <How to buy ", "[C]", ">.\n",
+    "And send the correct amount to the address specified below. After your ",
+    "payment, press <Check Payment>. Best time to check: 4-6am, Mon-Fri.\n",
+    "\n",
+    "*Why Did I Get This?\n",
+    "You got this because ", "[Q]",
+    ". Also you didn't click hard enough and now Tinkerbelle is dead.\n",
+    "\n",
+    "*But Aren't Screensavers Are Necessary?\n",
+    "WELL ACTUALLY, screensavers aren't really nescessary anymore because ",
+    "[S]", ".\n",
+    "\n",
+    "Please file complaints to @POTUS on Twitter.\n",
+    "\n"
+    "\n"
+    "\n"
+    "\n",
+    "*GREETZ TO CRASH OVERRIDE AND ALSO JOEY\n",
+  };
+
+  /* Positions of UI elements. Layout:
+
+   +---------+-+---------------------------------------+
+   |   LOGO  | |               HEADER                  |
+   |         | |---------------------------------------|
+   |         | | NOTE TEXT                             |
+   |   DEAD  | |                                       |
+   |   LINE  | |                                       |
+   |  TIMERS | |                                       |
+   |         | |                                       |
+   |         | |                                       |
+   |         | |                                       |
+   |         | |                                       |
+   |         | |                                       |
+   +---------+ |                                       |
+   | LINKS   | +---------------------------------------+
+   | LINKS   | | FOOTER                                |
+   +---------+-+---------------------------------------+
+
+  The right side of the UI maximises to available width.
+  The note text maximises to available height.
+  The logo, header and timers are anchored to the top left of the window.
+  The links and footer are anchored to the bottom left of the window.
+  The entire window is a fixed 4:3 scale, with a minimum margin around it.
+  */
+
+  /* main window text */
+  unsigned long fg = bst->fg;
+  unsigned long bg = bst->bg;
+  /* ransom note */
+  unsigned long fg2 = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                          "ransomware.foreground2",
+                                          "Ransomware.Foreground");
+  unsigned long bg2 = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                          "ransomware.background2",
+                                          "Ransomware.Background");
+  /* buttons */
+  unsigned long fg3 = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                          "ransomware.foreground3",
+                                          "Ransomware.Foreground");
+  unsigned long bg3 = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                          "ransomware.background3",
+                                          "Ransomware.Background");
+  /* links */
+  unsigned long link = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                           "ransomware.link",
+                                           "Ransomware.Foreground");
+  /* headers */
+  unsigned long theader = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                              "ransomware.timerheader",
+                                              "Ransomware.Foreground");
+  int left_column_width;
+  int right_column_width;
+  int right_column_height;
+  int stage1_countdown_y, stage2_countdown_y;
+  int margin;
+  int top_height, bottom_height;
+  int x, y;
+
+  if (bst->xgwa.width > 2560) n++;  /* Retina displays */
+  for (i = 0; i < n; i++)
+    {
+      pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
+                              pixmap, pix_w, pix_h);
+      mask = double_pixmap (dpy, bst->xgwa.visual, 1,
+                            mask, pix_w, pix_h);
+      pix_w *= 2;
+      pix_h *= 2;
+    }
+
+  margin = line_height;
+  left_column_width  = MAX (pix_w, line_height1 * 8);
+  right_column_width = MIN (line_height * 40,
+                            MAX (line_height * 8,
+                                 bst->xgwa.width - left_column_width
+                                 - margin*2));
+  top_height = line_height * 2.5;
+  bottom_height = line_height * 6;
+  right_column_height = MIN (line_height * 36,
+                             bst->xgwa.height - bottom_height - top_height
+                             - line_height);
+
+  if ((bst->xgwa.width / 4) * 3 > bst->xgwa.height)
+    /* Wide screen: keep the big text box at 4:3, centered. */
+    right_column_height = MIN (right_column_height,
+                               right_column_width * 4 / 3);
+  else if (right_column_width < line_height * 30)
+    /* Tall but narrow screen: make the text box be full height. */
+    right_column_height = (bst->xgwa.height - bottom_height - top_height
+                           - line_height);
+
+  x = (bst->xgwa.width - left_column_width - right_column_width - margin) / 2;
+  y = (bst->xgwa.height - right_column_height - bottom_height) / 2;
+
+  bst->xoff = bst->left_margin = bst->right_margin = 0;
+
+  if (!(random() % 8))
+    return apple2ransomware (dpy, window);
+
+  /* Draw the main red window */
+  BSOD_INVERT (bst);
+  BSOD_RECT (bst, True, 0, 0, bst->xgwa.width, bst->xgwa.height);
+
+  if (pixmap) {
+    bst->pixmap = pixmap;
+    bst->mask = mask;
+    BSOD_PIXMAP (bst, 0, 0, pix_w, pix_h,
+                 x + (left_column_width - pix_w) / 2,
+                 y);
+  }
+
+  /* Setup deadlines */
+  strftime (stage1_deadline_str, sizeof(stage1_deadline_str),
+            "%m/%d/%Y %H:%M:%S", localtime(&stage1_deadline));
+  strftime (stage2_deadline_str, sizeof(stage1_deadline_str),
+            "%m/%d/%Y %H:%M:%S", localtime(&stage2_deadline));
+
+  BSOD_INVERT (bst);
+  /* Draw header pane */
+  BSOD_FONT (bst, 0);
+
+  BSOD_MARGINS (bst,
+                x + left_column_width + margin,
+                bst->xgwa.width -
+                (x + left_column_width + margin + right_column_width));
+  BSOD_MOVETO (bst, x + left_column_width + margin,
+               y + bst->fontA->ascent);
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_WORD_WRAP (bst);
+  BSOD_TEXT (bst, CENTER, header_quip);
+  BSOD_TRUNCATE (bst);
+
+  /* Draw left-side timers */
+  BSOD_MARGINS (bst, x, bst->xgwa.width - (x + left_column_width));
+  BSOD_MOVETO (bst, x, y + pix_h + line_height);
+  BSOD_FONT (bst, 1);
+
+  BSOD_COLOR (bst, theader, bg);
+  BSOD_TEXT (bst, CENTER, "Payment will be raised on\n");
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_TEXT (bst, CENTER, stage1_deadline_str);
+
+  stage1_countdown_y = y + pix_h + line_height + line_height1 * 3;
+  BSOD_MOVETO (bst, x, stage1_countdown_y - line_height);
+  BSOD_TEXT (bst, CENTER, "Time Left");
+
+  BSOD_COLOR (bst, theader, bg);
+  BSOD_WORD_WRAP (bst);
+  BSOD_TEXT (bst, CENTER, "\n\n\n\nYour pixels will be lost on\n");
+  BSOD_TRUNCATE (bst);
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_TEXT (bst, CENTER, stage2_deadline_str);
+
+  stage2_countdown_y = stage1_countdown_y + line_height1 * 5;
+  BSOD_MOVETO (bst, x, stage2_countdown_y - line_height);
+  BSOD_TEXT (bst, CENTER, "Time Left");
+
+  BSOD_FONT (bst, 1);
+
+  /* Draw links, but skip on small screens */
+  if (right_column_height > 425) {
+    BSOD_MOVETO (bst, x, 
+                 y + right_column_height + top_height + bottom_height
+                 - line_height1 * 5);
+    BSOD_COLOR (bst, link, bg);
+    BSOD_TEXT (bst, LEFT, "\n");
+    BSOD_TEXT (bst, LEFT, "About ");
+    BSOD_TEXT (bst, LEFT, currency);
+    BSOD_TEXT (bst, LEFT, "\n\n"
+                          "How to buy ");
+    BSOD_TEXT (bst, LEFT, currency);
+    BSOD_TEXT (bst, LEFT, "\n\n"
+                          "Contact us\n");
+  }
+
+  /* Ransom note text area */
+  BSOD_COLOR (bst, bg2, fg2);
+  BSOD_RECT (bst, True, 
+             x + left_column_width + margin,
+             y + top_height,
+             right_column_width,
+             right_column_height);
+  BSOD_MOVETO (bst,
+               x + left_column_width + margin + line_height / 2,
+               y + top_height + line_height + line_height / 2);
+  BSOD_MARGINS (bst,
+                x + left_column_width + margin + line_height / 2,
+                bst->xgwa.width - 
+                (x + left_column_width + margin + right_column_width));
+  BSOD_VERT_MARGINS (bst, 
+                     y + top_height + line_height / 2,
+                     bottom_height - line_height);
+  BSOD_INVERT (bst);
+
+  /* Write out the ransom note itself */
+  BSOD_CROP (bst, True);
+  BSOD_WORD_WRAP (bst);
+  for (i = 0; i < countof(lines); i++)
+    {
+      const char *s = lines[i];
+      if (!strcmp(s, "[C]")) s = currency;
+      else if  (!strcmp(s, "[Q]")) s = excuse_quip;
+      else if  (!strcmp(s, "[S]")) s = screensaver_quip;
+
+      if (*s == '*')
+        {
+          s++;
+          BSOD_FONT (bst, 2);
+        }
+      else
+        BSOD_FONT (bst, 0);
+
+      BSOD_TEXT (bst, LEFT, s);
+    }
+  BSOD_TRUNCATE (bst);
+  BSOD_CROP (bst, False);
+  BSOD_FONT (bst, 0);
+
+  /* Draw over any overflowing ransom text. */
+  BSOD_COLOR (bst, bg, fg);
+  BSOD_RECT (bst, True,
+             x + left_column_width + margin,
+             y + top_height + right_column_height,
+             bst->xgwa.width, bst->xgwa.height);
+  BSOD_RECT (bst, True,
+             x + left_column_width + margin + right_column_width,
+             y + top_height,
+             bst->xgwa.width, bst->xgwa.height);
+
+  /* Draw the footer */
+  BSOD_COLOR (bst, theader, bg);
+  BSOD_MOVETO (bst,
+               x + left_column_width + margin,
+               y + top_height + right_column_height + line_height * 2);
+
+  sprintf(buf, "Send $%.2f of %s to this address:\n", 101+frand(888), currency);
+  BSOD_TEXT (bst, LEFT, buf);
+  BSOD_COLOR (bst, fg2, bg2);
+
+  /* address, has some extra slashes in there because it's a fake address */
+  for (i = 0; i < 40; i++) {
+    const char *s =
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123459789";
+    buf[i] = s[random() % strlen(s)];
+  }
+  strncpy (buf, " //", 3);
+  buf[10] = '/';
+  buf[17] = '/';
+  buf[24] = '/';
+  strcpy (buf+33, " ");
+  BSOD_TEXT (bst, LEFT, buf);
+
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_TEXT (bst, LEFT, "   ");
+  BSOD_COLOR (bst, fg3, bg3);
+  BSOD_TEXT (bst, LEFT, "  Copy  ");
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_TEXT (bst, LEFT, "\n\n");
+
+  BSOD_COLOR (bst, fg3, bg3);
+  BSOD_TEXT (bst, LEFT, "  Demogrify Screen  ");
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_TEXT (bst, LEFT, "            ");
+  BSOD_COLOR (bst, fg3, bg3);
+  BSOD_TEXT (bst, LEFT, "  Check Payment  ");
+
+
+  /* Draw countdown timers */
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_FONT (bst, 0);
+  do {
+    /* First timer */
+    BSOD_MOVETO (bst, x, stage1_countdown_y);
+    BSOD_MARGINS (bst, x, bst->xgwa.width - (x + left_column_width));
+
+    countdown_r = stage1_deadline - now;
+    countdown_s = countdown_r % 60;
+    countdown_m = (countdown_r / 60) % 60;
+    countdown_h = (countdown_r / 3600) % 24;
+    countdown_d = (countdown_r / 86400);
+
+    sprintf (countdown_str, "%02d:%02d:%02d:%02d\n",
+             countdown_d, countdown_h, countdown_m, countdown_s);
+
+    BSOD_TEXT (bst, CENTER, countdown_str);
+
+    /* Second timer */
+    BSOD_MOVETO (bst, x, stage2_countdown_y);
+
+    countdown_r = stage2_deadline - now;
+    countdown_s = countdown_r % 60;
+    countdown_m = (countdown_r / 60) % 60;
+    countdown_h = (countdown_r / 3600) % 24;
+    countdown_d = (countdown_r / 86400);
+
+    sprintf (countdown_str, "%02d:%02d:%02d:%02d\n",
+             countdown_d, countdown_h, countdown_m, countdown_s);
+
+    BSOD_TEXT (bst, CENTER, countdown_str);
+
+    BSOD_PAUSE (bst, 1000000);
+    now++;
+
+    /* While the "correct" thing to do is create enough of a script to fill the
+     * stage2_deadline, this would be 7 days of "frames", which is quite a bit
+     * of memory. Instead, only fill the buffer with 1 hour of frames, which is
+     * enough to make the point before xscreensaver cycles us.
+     */
+  } while (stage1_deadline - now > 3600);
+
+  XClearWindow (dpy, window);
+  return bst;
+}
+
 
 /* As seen in Portal 2.  By jwz.
  */
@@ -1049,7 +1980,9 @@ glados (Display *dpy, Window window)
 
   int i;
 
-  bst->y = ((bst->xgwa.height -
+  bst->xoff = bst->left_margin = bst->right_margin = 0;
+
+  bst->y = ((bst->xgwa.height - bst->yoff -
              ((bst->font->ascent + bst->font->descent) * countof(panicstr)))
             / 2);
 
@@ -1110,7 +2043,7 @@ sco (Display *dpy, Window window)
      "** Press Any Key to Reboot **\n"
     );
 
-  bst->y = ((bst->xgwa.height -
+  bst->y = ((bst->xgwa.height - bst->yoff -
              ((bst->font->ascent + bst->font->descent) * 18)));
 
   XClearWindow (dpy, window);
@@ -1123,9 +2056,11 @@ sco (Display *dpy, Window window)
 static struct bsod_state *
 sparc_linux (Display *dpy, Window window)
 {
-  struct bsod_state *bst = make_bsod_state (dpy, window, "sco", "SCO");
+  struct bsod_state *bst = make_bsod_state (dpy, window,
+                                            "sparclinux", "SparcLinux");
   bst->scroll_p = True;
-  bst->y = bst->xgwa.height - bst->font->ascent - bst->font->descent;
+  bst->y = bst->xgwa.height - bst->yoff
+    - bst->font->ascent - bst->font->descent;
 
   BSOD_TEXT (bst, LEFT,
         "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
@@ -1207,7 +2142,7 @@ bsd (Display *dpy, Window window)
   BSOD_TEXT (bst, LEFT, (b ? "damn!" : "sunk!"));
   BSOD_TEXT (bst, LEFT, "\nRebooting\n");
 
-  bst->y = ((bst->xgwa.height -
+  bst->y = ((bst->xgwa.height - bst->yoff -
              ((bst->font->ascent + bst->font->descent) * 4)));
 
   XClearWindow (dpy, window);
@@ -1220,47 +2155,72 @@ amiga (Display *dpy, Window window)
 {
   struct bsod_state *bst = make_bsod_state (dpy, window, "amiga", "Amiga");
 
+  const char *guru1 ="Software failure.  Press left mouse button to continue.";
+  const char *guru2 ="Guru Meditation #00000003.00C01570";
   Pixmap pixmap = 0;
+  Pixmap mask = 0;
   int pix_w = 0, pix_h = 0;
   int height;
-  int lw = 10;
+  int lw = bst->font->ascent + bst->font->descent;
+  unsigned long fg = bst->fg;
+  unsigned long bg = bst->bg;
 
   unsigned long bg2 = get_pixel_resource (dpy, bst->xgwa.colormap,
                                           "amiga.background2",
                                           "Amiga.Background");
 
-# ifdef DO_XPM
-  pixmap = xpm_data_to_pixmap (dpy, window, (char **) amiga_hand,
-                               &pix_w, &pix_h, 0);
-# endif /* DO_XPM */
+  bst->yoff = 0;
+  bst->top_margin = bst->bottom_margin = 0;
 
-  if (pixmap && bst->xgwa.height > 600)        /* scale up the bitmap */
+  pixmap = image_data_to_pixmap (dpy, window,
+                                 amiga_png, sizeof(amiga_png),
+                                 &pix_w, &pix_h, &mask);
+
+  if (pixmap)
     {
-      pixmap = double_pixmap (dpy, bst->gc, bst->xgwa.visual, bst->xgwa.depth,
-                              pixmap, pix_w, pix_h);
-      pix_w *= 2;
-      pix_h *= 2;
+      int i, n = 0;
+      if (MIN (bst->xgwa.width, bst->xgwa.height) > 600) n++;
+      if (bst->xgwa.width > 2560) n++;  /* Retina displays */
+      for (i = 0; i < n; i++)
+        {
+          pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
+                                  pixmap, pix_w, pix_h);
+          mask = double_pixmap (dpy, bst->xgwa.visual, 1, mask, pix_w, pix_h);
+          pix_w *= 2;
+          pix_h *= 2;
+        }
     }
 
   XSetLineAttributes (dpy, bst->gc, lw, LineSolid, CapButt, JoinMiter);
 
-  height = (bst->font->ascent + bst->font->descent) * 6;
+  height = lw * 5;
+
+  bst->char_delay = bst->line_delay = 0;
 
   BSOD_PAUSE (bst, 2000000);
   BSOD_COPY (bst, 0, 0, bst->xgwa.width, bst->xgwa.height - height, 0, height);
 
-  BSOD_INVERT (bst);
-  BSOD_RECT (bst, True, 0, 0, bst->xgwa.width, height);
-  BSOD_INVERT (bst);
-  BSOD_TEXT (bst, CENTER,
-             "\n"
-             "Software failure.  Press left mouse button to continue.\n"
-             "Guru Meditation #00000003.00C01570"
-             );
-  BSOD_RECT (bst, False, lw/2, lw/2, bst->xgwa.width - lw, height);
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_RECT (bst, True, 0, 0, bst->xgwa.width, height); /* red */
+  BSOD_COLOR (bst, bg, fg);
+  BSOD_RECT (bst, True, lw/2, lw/2, bst->xgwa.width-lw, height-lw); /* black */
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_MOVETO (bst, 0, lw*2);
+  BSOD_TEXT (bst, CENTER, guru1);
+  BSOD_MOVETO (bst, 0, lw*3.5);
+  BSOD_TEXT (bst, CENTER, guru2);
   BSOD_PAUSE (bst, 1000000);
-  BSOD_INVERT (bst);
-  BSOD_LOOP (bst, -3);
+
+  BSOD_COLOR (bst, bg, fg);
+  BSOD_RECT (bst, True, 0, 0, bst->xgwa.width, height); /* black */
+  BSOD_COLOR (bst, fg, bg);
+  BSOD_MOVETO (bst, 0, lw*2);
+  BSOD_TEXT (bst, CENTER, guru1);
+  BSOD_MOVETO (bst, 0, lw*3.5);
+  BSOD_TEXT (bst, CENTER, guru2);
+  BSOD_PAUSE (bst, 1000000);
+
+  BSOD_LOOP (bst, -17);
 
   XSetWindowBackground (dpy, window, bg2);
   XClearWindow (dpy, window);
@@ -1270,7 +2230,12 @@ amiga (Display *dpy, Window window)
     {
       int x = (bst->xgwa.width - pix_w) / 2;
       int y = ((bst->xgwa.height - pix_h) / 2);
+      XSetClipMask (dpy, bst->gc, mask);
+      XSetClipOrigin (dpy, bst->gc, x, y);
       XCopyArea (dpy, pixmap, bst->window, bst->gc, 0, 0, pix_w, pix_h, x, y);
+      XSetClipMask (dpy, bst->gc, None);
+      XFreePixmap (dpy, pixmap);
+      XFreePixmap (dpy, mask);
     }
 
   bst->y += lw;
@@ -1295,17 +2260,17 @@ atari (Display *dpy, Window window)
 {
   struct bsod_state *bst = make_bsod_state (dpy, window, "atari", "Atari");
 
-  Pixmap pixmap = 0;
-  int pix_w = atari_width;
-  int pix_h = atari_height;
+  int pix_w, pix_h;
   int offset;
   int i, x, y;
+  Pixmap mask = 0;
+  Pixmap pixmap = image_data_to_pixmap (dpy, window,
+                                        atari_png, sizeof(atari_png),
+                                        &pix_w, &pix_h, &mask);
 
-  pixmap = XCreatePixmapFromBitmapData (dpy, window, (char *) atari_bits,
-                                        pix_w, pix_h,
-                                        bst->fg, bst->bg, bst->xgwa.depth);
-  pixmap = double_pixmap (dpy, bst->gc, bst->xgwa.visual, bst->xgwa.depth,
+  pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
                           pixmap, pix_w, pix_h);
+  mask = double_pixmap (dpy, bst->xgwa.visual, 1, mask, pix_w, pix_h);
   pix_w *= 2;
   pix_h *= 2;
 
@@ -1317,15 +2282,19 @@ atari (Display *dpy, Window window)
   for (i = 1; i< 7; i++)
     BSOD_COPY (bst, x, y, pix_w, pix_h, (x + (i*offset)), y);
 
-  for (; i< 10; i++) 
+  for (; i< 10; i++)
     {
       BSOD_PAUSE (bst, 1000000);
       BSOD_COPY (bst, x, y, pix_w, pix_h, (x + (i*offset)), y);
     }
 
   XClearWindow (dpy, window);
+  XSetClipMask (dpy, bst->gc, mask);
+  XSetClipOrigin (dpy, bst->gc, x, y);
   XCopyArea (dpy, pixmap, window, bst->gc, 0, 0, pix_w, pix_h, x, y);
+  XSetClipMask (dpy, bst->gc, None);
   XFreePixmap (dpy, pixmap);
+  XFreePixmap (dpy, mask);
 
   return bst;
 }
@@ -1336,23 +2305,25 @@ mac (Display *dpy, Window window)
 {
   struct bsod_state *bst = make_bsod_state (dpy, window, "mac", "Mac");
 
-  Pixmap pixmap = 0;
-  int pix_w = mac_width;
-  int pix_h = mac_height;
-  int offset = mac_height * 4;
+  int pix_w, pix_h;
   int i;
 
   const char *string = ("0 0 0 0 0 0 0 F\n"
                        "0 0 0 0 0 0 0 3");
 
-  pixmap = XCreatePixmapFromBitmapData(dpy, window, (char *) mac_bits,
-                                      mac_width, mac_height,
-                                      bst->fg, bst->bg, bst->xgwa.depth);
+  Pixmap mask = 0;
+  Pixmap pixmap = image_data_to_pixmap (dpy, window,
+                                        mac_png, sizeof(mac_png),
+                                        &pix_w, &pix_h, &mask);
+  int offset = pix_h * 4;
+
+  bst->xoff = bst->left_margin = bst->right_margin = 0;
 
   for (i = 0; i < 2; i++)
     {
-      pixmap = double_pixmap (dpy, bst->gc, bst->xgwa.visual, bst->xgwa.depth,
+      pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
                               pixmap, pix_w, pix_h);
+      mask = double_pixmap (dpy, bst->xgwa.visual, 1, mask, pix_w, pix_h);
       pix_w *= 2; pix_h *= 2;
     }
 
@@ -1363,8 +2334,12 @@ mac (Display *dpy, Window window)
   if (bst->y < 0) bst->y = 0;
 
   XClearWindow (dpy, window);
+  XSetClipMask (dpy, bst->gc, mask);
+  XSetClipOrigin (dpy, bst->gc, bst->x, bst->y);
   XCopyArea (dpy, pixmap, window, bst->gc, 0, 0, pix_w, pix_h, bst->x, bst->y);
+  XSetClipMask (dpy, bst->gc, None);
   XFreePixmap (dpy, pixmap);
+  XFreePixmap (dpy, mask);
 
   bst->y += offset + bst->font->ascent + bst->font->descent;
   BSOD_TEXT (bst, CENTER, string);
@@ -1478,6 +2453,8 @@ macsbug (Display *dpy, Window window)
                                          "macsbug.borderColor",
                                          "MacsBug.BorderColor");
 
+  bst->xoff = bst->left_margin = bst->right_margin = 0;
+
   for (s = body; *s; s++) if (*s == '\n') body_lines++;
 
   char_width = (bst->font->per_char
@@ -1488,8 +2465,8 @@ macsbug (Display *dpy, Window window)
   col_right   = char_width  * 12;  /* number of columns in `left' */
   page_bottom = line_height * 47;  /* number of lines in `left'   */
 
-  if (page_bottom > bst->xgwa.height
-    page_bottom = bst->xgwa.height;
+  if (page_bottom > bst->xgwa.height - bst->yoff)
+    page_bottom = bst->xgwa.height - bst->yoff;
 
   row_bottom = page_bottom - line_height;
   row_top    = row_bottom - (line_height * 4);
@@ -1523,15 +2500,15 @@ macsbug (Display *dpy, Window window)
   BSOD_TEXT (bst, LEFT, bottom);
 
   BSOD_RECT (bst, True, xoff + col_right, yoff, 2, page_bottom);
-  BSOD_RECT (bst, True, xoff + col_right, yoff + row_top, 
+  BSOD_RECT (bst, True, xoff + col_right, yoff + row_top,
              page_right - col_right, 1);
-  BSOD_RECT (bst, True, xoff + col_right, yoff + row_bottom, 
+  BSOD_RECT (bst, True, xoff + col_right, yoff + row_bottom,
              page_right - col_right, 1);
   BSOD_RECT (bst, False, xoff-2, yoff, page_right+4, page_bottom);
 
   BSOD_LINE_DELAY (bst, 500);
-  BSOD_MOVETO (bst, 
-               xoff + col_right + char_width, 
+  BSOD_MOVETO (bst,
+               xoff + col_right + char_width,
                yoff + body_top + line_height);
   BSOD_MARGINS (bst, xoff + col_right + char_width, yoff);
   BSOD_TEXT (bst, LEFT, body);
@@ -1558,21 +2535,39 @@ mac1 (Display *dpy, Window window)
 {
   struct bsod_state *bst = make_bsod_state (dpy, window, "mac1", "Mac1");
 
-  Pixmap pixmap = 0;
-  int pix_w = macbomb_width;
-  int pix_h = macbomb_height;
+  int pix_w, pix_h;
   int x, y;
-
-  pixmap = XCreatePixmapFromBitmapData (dpy, window, (char *) macbomb_bits,
-                                        macbomb_width, macbomb_height,
-                                        bst->fg, bst->bg, bst->xgwa.depth);
+  Pixmap mask = 0;
+  Pixmap pixmap = image_data_to_pixmap (dpy, window,
+                                        macbomb_png, sizeof(macbomb_png),
+                                        &pix_w, &pix_h, &mask);
+
+  if (pixmap && 
+      pix_w < bst->xgwa.width / 2 &&
+      pix_h < bst->xgwa.height / 2)
+    {
+      int i, n = 1;
+      if (bst->xgwa.width > 2560) n++;  /* Retina displays */
+      for (i = 0; i < n; i++)
+        {
+          pixmap = double_pixmap (dpy, bst->xgwa.visual,
+                                  bst->xgwa.depth, pixmap, pix_w, pix_h);
+          mask = double_pixmap (dpy, bst->xgwa.visual, 1, mask, pix_w, pix_h);
+          pix_w *= 2;
+          pix_h *= 2;
+        }
+    }
 
   x = (bst->xgwa.width - pix_w) / 2;
   y = (bst->xgwa.height - pix_h) / 2;
   if (y < 0) y = 0;
 
   XClearWindow (dpy, window);
+  XSetClipMask (dpy, bst->gc, mask);
+  XSetClipOrigin (dpy, bst->gc, x, y);
   XCopyArea (dpy, pixmap, window, bst->gc, 0, 0, pix_w, pix_h, x, y);
+  XSetClipMask (dpy, bst->gc, None);
+  XFreePixmap (dpy, mask);
 
   return bst;
 }
@@ -1597,20 +2592,20 @@ macx_10_0 (Display *dpy, Window window)
                                       "macx.textBackground",
                                       "MacX.TextBackground"));
 
-# ifdef DO_XPM
   {
     Pixmap pixmap = 0;
     Pixmap mask = 0;
     int x, y, pix_w, pix_h;
-    pixmap = xpm_data_to_pixmap (dpy, window, (char **) happy_mac,
-                                 &pix_w, &pix_h, &mask);
+    pixmap = image_data_to_pixmap (dpy, window,
+                                   hmac_png, sizeof(hmac_png),
+                                   &pix_w, &pix_h, &mask);
 
-# ifdef USE_IPHONE
+# ifdef HAVE_MOBILE
     if (pixmap)
       {
-        pixmap = double_pixmap (dpy, bst->gc, bst->xgwa.visual,
+        pixmap = double_pixmap (dpy, bst->xgwa.visual,
                                 bst->xgwa.depth, pixmap, pix_w, pix_h);
-        mask = double_pixmap (dpy, bst->gc, bst->xgwa.visual,
+        mask = double_pixmap (dpy, bst->xgwa.visual,
                               1, mask, pix_w, pix_h);
         pix_w *= 2;
         pix_h *= 2;
@@ -1625,8 +2620,8 @@ macx_10_0 (Display *dpy, Window window)
     XCopyArea (dpy, pixmap, window, bst->gc, 0, 0, pix_w, pix_h, x, y);
     XSetClipMask (dpy, bst->gc, None);
     XFreePixmap (dpy, pixmap);
+    XFreePixmap (dpy, mask);
   }
-#endif /* DO_XPM */
 
   bst->left_margin = 0;
   bst->right_margin = 0;
@@ -1666,27 +2661,35 @@ macx_10_0 (Display *dpy, Window window)
 }
 
 
-# ifdef DO_XPM
 static struct bsod_state *
 macx_10_2 (Display *dpy, Window window, Bool v10_3_p)
 {
   struct bsod_state *bst = make_bsod_state (dpy, window, "macx", "MacX");
 
+  Pixmap mask = 0;
   Pixmap pixmap = 0;
   int pix_w = 0, pix_h = 0;
   int x, y;
 
-  pixmap = xpm_data_to_pixmap (dpy, window, 
-                               (char **) (v10_3_p ? osx_10_3 : osx_10_2),
-                               &pix_w, &pix_h, 0);
+  if (v10_3_p)
+    pixmap = image_data_to_pixmap (dpy, window,
+                                   osx_10_3_png, sizeof(osx_10_3_png),
+                                   &pix_w, &pix_h, &mask);
+  else
+    pixmap = image_data_to_pixmap (dpy, window,
+                                   osx_10_2_png, sizeof(osx_10_2_png),
+                                   &pix_w, &pix_h, &mask);
   if (! pixmap) abort();
+  if (! mask) abort();
 
 #if 0
   if (bst->xgwa.height > 600)  /* scale up the bitmap */
     {
-      pixmap = double_pixmap (dpy, bst->gc, bst->xgwa.visual, bst->xgwa.depth,
+      pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
                               pixmap, pix_w, pix_h);
+      mask = double_pixmap (dpy, bst->xgwa.visual, 1, mask, pix_w, pix_h);
       if (! pixmap) abort();
+      if (! mask) abort();
       pix_w *= 2;
       pix_h *= 2;
     }
@@ -1696,6 +2699,7 @@ macx_10_2 (Display *dpy, Window window, Bool v10_3_p)
   BSOD_PAUSE (bst, 2000000);
 
   bst->pixmap = pixmap;
+  bst->mask = mask;
 
   x = (bst->xgwa.width - pix_w) / 2;
   y = ((bst->xgwa.height - pix_h) / 2);
@@ -1703,7 +2707,6 @@ macx_10_2 (Display *dpy, Window window, Bool v10_3_p)
 
   return bst;
 }
-# endif /* DO_XPM */
 
 
 /* 2006 Mac Mini with MacOS 10.6 failing with a bad boot drive. By jwz.
@@ -1876,28 +2879,123 @@ mac_diskfail (Display *dpy, Window window)
 }
 
 
+/* 2017 MacOS 10.12 interminable software update, by jwz.
+ */
+static struct bsod_state *
+macx_install (Display *dpy, Window window)
+{
+  struct bsod_state *bst = make_bsod_state (dpy, window, "macinstall", "MacX");
+
+  int pix_w, pix_h;
+  int x, y;
+  int bw1, bh1;
+  int bw2, bh2;
+
+  unsigned long fg = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                         "macinstall.foreground",
+                                         "Mac.Foreground");
+  unsigned long bg = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                         "macinstall.background",
+                                         "Mac.Background");
+  unsigned long fg2 = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                         "macinstall.barForeground",
+                                         "Mac.Foreground");
+  unsigned long bg2 = get_pixel_resource (dpy, bst->xgwa.colormap,
+                                         "macinstall.barBackground",
+                                         "Mac.Background");
+  char buf[1024];
+  int lh = bst->font->ascent + bst->font->descent;
+  int i, min;
+  double pct;
+
+  Pixmap mask = 0;
+  Pixmap pixmap = image_data_to_pixmap (dpy, window,
+                                        apple_png, sizeof(apple_png),
+                                        &pix_w, &pix_h, &mask);
+
+  bst->xoff = bst->left_margin = bst->right_margin = 0;
+
+  if (pixmap)
+    {
+      int i, n = 0;
+      if (bst->xgwa.width > 2560) n++;  /* Retina displays */
+      for (i = 0; i < n; i++)
+        {
+          pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
+                                  pixmap, pix_w, pix_h);
+          mask = double_pixmap (dpy, bst->xgwa.visual, 1, mask, pix_w, pix_h);
+          pix_w *= 2;
+          pix_h *= 2;
+        }
+    }
+
+  bst->pixmap = pixmap;
+  bst->mask = mask;
+
+  x = (bst->xgwa.width - pix_w) / 2;
+  y = (bst->xgwa.height) / 2  - pix_h;
+  if (y < 0) y = 0;
+
+  XSetLineAttributes (dpy, bst->gc, 1, LineSolid, CapRound, JoinMiter);
+
+  BSOD_COLOR(bst, bg, bg);
+  BSOD_RECT (bst, True, 0, 0, bst->xgwa.width, bst->xgwa.height);
+  BSOD_COLOR(bst, fg, bg);
+  BSOD_PIXMAP (bst, 0, 0, pix_w, pix_h, x, y);
+  y += pix_h * 2 - lh;
+
+  /* progress bar */
+  bw1 = pix_w * 2.5;
+  bh1 = lh * 0.66;
+  if (bh1 < 8) bh1 = 8;
+
+  x = (bst->xgwa.width - bw1) / 2;
+  BSOD_COLOR(bst, fg2, bg);
+  BSOD_LINE (bst, x,   y, x + bw1, y, bh1);
+
+  bw2 = bw1 - 1;
+  bh2 = bh1 - 4;
+  BSOD_COLOR(bst, bg2, bg);
+  BSOD_LINE (bst, x+1, y, x + bw2, y, bh2);
+
+  BSOD_COLOR(bst, fg, bg);
+  BSOD_LINE (bst, x,   y, x + 1, y, bh1);
+
+  pct = 5 + (random() % 40);
+  min = 5 + (random() % 40);
+
+  for (i = 0; i < 100; i++) {
+    pct += frand(0.3);
+    min += (random() % 3) - 1;  /* sometimes down, mostly up */
+
+    if (pct > 90) pct = 90;
+    BSOD_RECT (bst, True, x, y - bh1/2, bw1 * pct / 100, bh1);
+
+    sprintf (buf, "  Installing Software Update: about %d minutes.  ", min);
+    bst->y = y + lh * 3;
+    BSOD_TEXT (bst, CENTER, buf);
+    BSOD_PAUSE (bst, 1000000);
+  }
+
+  return bst;
+}
+
 
 static struct bsod_state *
 macx (Display *dpy, Window window)
 {
-# ifdef DO_XPM
-  switch (random() % 4) {
+  switch (1?4:random() % 5) {
   case 0: return macx_10_0 (dpy, window);        break;
   case 1: return macx_10_2 (dpy, window, False); break;
   case 2: return macx_10_2 (dpy, window, True);  break;
-  case 3: return mac_diskfail (dpy, window); break;
+  case 3: return mac_diskfail (dpy, window);     break;
+  case 4: return macx_install (dpy, window);     break;
   default: abort();
   }
-# else  /* !DO_XPM */
-  switch (random() % 2) {
-  case 0:  return macx_10_0 (dpy, window);    break;
-  default: return mac_diskfail (dpy, window); break;
-  }
-# endif /* !DO_XPM */
 }
 
 
-#ifndef HAVE_COCOA /* #### I have no idea how to implement this without
+#ifndef HAVE_JWXYZ /* #### I have no idea how to implement this without
                            real plane-masks.  I don't think it would look
                            right if done with alpha-transparency... */
 /* blit damage
@@ -1911,7 +3009,7 @@ macx (Display *dpy, Window window)
 static struct bsod_state *
 blitdamage (Display *dpy, Window window)
 {
-  struct bsod_state *bst = 
+  struct bsod_state *bst =
     make_bsod_state (dpy, window, "blitdamage", "BlitDamage");
 
   int i;
@@ -1919,48 +3017,45 @@ blitdamage (Display *dpy, Window window)
   int w, h;
   int chunk_h, chunk_w;
   int steps;
-  long gc_mask = 0;
   int src_x, src_y;
   int x, y;
-  
+
   w = bst->xgwa.width;
   h = bst->xgwa.height;
 
-  gc_mask = GCForeground;
-  
   XSetPlaneMask (dpy, bst->gc, random());
 
   steps = 50;
   chunk_w = w / (random() % 1 + 1);
   chunk_h = h / (random() % 1 + 1);
-  if (random() & 0x1000) 
+  if (random() & 0x1000)
     delta_y = random() % 600;
   if (!delta_y || (random() & 0x2000))
     delta_x = random() % 600;
-  src_x = 0; 
-  src_y = 0; 
+  src_x = 0;
+  src_y = 0;
   x = 0;
   y = 0;
-  
+
   BSOD_IMG (bst);
   for (i = 0; i < steps; i++) {
-    if (x + chunk_w > w) 
+    if (x + chunk_w > w)
       x -= w;
     else
       x += delta_x;
-    
+
     if (y + chunk_h > h)
       y -= h;
     else
       y += delta_y;
-    
+
     BSOD_COPY (bst, src_x, src_y, chunk_w, chunk_h, x, y);
     BSOD_PAUSE (bst, 1000);
   }
 
   return bst;
 }
-#endif /* !HAVE_COCOA */
+#endif /* !HAVE_JWXYZ */
 
 
 /*
@@ -2116,7 +3211,7 @@ os2 (Display *dpy, Window window)
 
 /* SPARC Solaris panic. Should look pretty authentic on Solaris boxes.
  * Anton Solovyev <solovam@earthlink.net>
- */ 
+ */
 static struct bsod_state *
 sparc_solaris (Display *dpy, Window window)
 {
@@ -2127,13 +3222,13 @@ sparc_solaris (Display *dpy, Window window)
   bst->wrap_p = True;
   bst->left_margin = bst->right_margin = bst->xgwa.width  * 0.07;
   bst->top_margin = bst->bottom_margin = bst->xgwa.height * 0.07;
-  bst->y = bst->top_margin + bst->font->ascent;
+  bst->y = bst->top_margin + bst->yoff + bst->font->ascent;
 
   BSOD_IMG (bst);
   BSOD_PAUSE (bst, 3000000);
 
   BSOD_INVERT(bst);
-  BSOD_RECT (bst, True, 
+  BSOD_RECT (bst, True,
              bst->left_margin, bst->top_margin,
              bst->xgwa.width - bst->left_margin - bst->right_margin,
              bst->xgwa.height - bst->top_margin - bst->bottom_margin);
@@ -2204,6 +3299,9 @@ linux_fsck (Display *dpy, Window window)
   int i;
   const char *sysname;
   char buf[1024];
+# ifdef HAVE_UNAME
+  struct utsname uts;
+#endif /* UNAME */
 
   const char *linux_panic[] = {
    " kernel: Unable to handle kernel paging request at virtual "
@@ -2246,7 +3344,6 @@ linux_fsck (Display *dpy, Window window)
   sysname = "linux";
 # ifdef HAVE_UNAME
   {
-    struct utsname uts;
     char *s;
     if (uname (&uts) >= 0)
       sysname = uts.nodename;
@@ -2265,7 +3362,7 @@ linux_fsck (Display *dpy, Window window)
   BSOD_CHAR_DELAY (bst, 300000);
   BSOD_TEXT (bst, LEFT, ".........\n");
   BSOD_CHAR_DELAY (bst, 0);
-  BSOD_TEXT (bst, LEFT, 
+  BSOD_TEXT (bst, LEFT,
              "xinit:  X server slow to shut down, sending KILL signal.\n"
              "waiting for server to die ");
   BSOD_CHAR_DELAY (bst, 300000);
@@ -2432,7 +3529,7 @@ linux_fsck (Display *dpy, Window window)
   if (0 == random() % 10)
     goto PANIC;
 
-  BSOD_TEXT (bst, LEFT, 
+  BSOD_TEXT (bst, LEFT,
              "Pass 3: Checking directory connectivity\n"
              "/lost+found not found.  Create? yes\n");
   BSOD_PAUSE (bst, 2000000);
@@ -2440,7 +3537,7 @@ linux_fsck (Display *dpy, Window window)
   /* Unconnected directory inode 4949 (/var/spool/squid/06/???)
      Connect to /lost+found<y>? yes
 
-     '..' in /var/spool/squid/06/08 (20351) is <The NULL inode> (0), should be 
+     '..' in /var/spool/squid/06/08 (20351) is <The NULL inode> (0), should be
      /var/spool/squid/06 (20350).
      Fix<y>? yes
 
@@ -2547,12 +3644,16 @@ linux_fsck (Display *dpy, Window window)
 static struct bsod_state *
 hppa_linux (Display *dpy, Window window)
 {
-  struct bsod_state *bst = 
+  struct bsod_state *bst =
     make_bsod_state (dpy, window, "hppalinux", "HPPALinux");
 
   int i = 0;
   const char *release, *sysname, *gccversion, *version;
   long int linedelay = 0;
+  char ss[1024];
+# ifdef HAVE_UNAME
+  struct utsname uts;
+# endif /* UNAME */
 
   __extension__
   struct { long int delay; const char *string; } linux_panic[] =
@@ -2628,7 +3729,7 @@ hppa_linux (Display *dpy, Window window)
      { -1, "Soft power switch enabled, polling @ 0xf0400804.\n" },
      { -1, "pty: 256 Unix98 ptys configured\n" },
      { -1, "Generic RTC Driver v1.07\n" },
-     { -1, "Serial: 8250/16550 driver $Revision: 1.100 $ 13 ports, "
+     { -1, "Serial: 8250/16550 driver $" "Revision: 1.100 $ 13 ports, "
            "IRQ sharing disabled\n" },
      { -1, "ttyS0 at I/O 0x3f8 (irq = 0) is a 16550A\n" },
      { -1, "ttyS1 at I/O 0x2f8 (irq = 0) is a 16550A\n" },
@@ -2693,7 +3794,6 @@ hppa_linux (Display *dpy, Window window)
   version = "#2 Mon Dec 8 06:09:27 GMT 2003";
 # ifdef HAVE_UNAME
   {
-    struct utsname uts;
     char *s;
     if (uname (&uts) >= 0)
       {
@@ -2717,8 +3817,7 @@ hppa_linux (Display *dpy, Window window)
 
   /* Insert current host name into banner on line 2 */
   {
-    char ss[1024];
-    snprintf (ss, 1024, linux_panic[1].string, 
+    snprintf (ss, 1024, linux_panic[1].string,
              release, sysname, gccversion, version);
     linux_panic[1].string = ss;
   }
@@ -2733,7 +3832,8 @@ hppa_linux (Display *dpy, Window window)
       i++;
     }
 
-  bst->y = bst->xgwa.height - bst->font->ascent - bst->font->descent;
+  bst->y = bst->xgwa.height - bst->yoff
+    - bst->font->ascent - bst->font->descent;
 
   XClearWindow(dpy, window);
   return bst;
@@ -2754,6 +3854,9 @@ vms (Display *dpy, Window window)
   char *s, *s1;
   int i;
   int arg_count;
+# ifdef HAVE_UNAME
+  struct utsname uts;
+# endif /* UNAME */
 
   __extension__
 
@@ -2826,7 +3929,6 @@ vms (Display *dpy, Window window)
   sysname = "VMS001";
 # ifdef HAVE_UNAME
   {
-    struct utsname uts;
     if (uname (&uts) >= 0)
       sysname = uts.nodename;
     s = strchr (sysname, '.');
@@ -2928,7 +4030,8 @@ hvx (Display *dpy, Window window)
 
   bst->scroll_p = True;
   bst->wrap_p = True;
-  bst->y = bst->xgwa.height - bst->bottom_margin - bst->font->ascent;
+  bst->y = bst->xgwa.height - bst->bottom_margin - bst->yoff
+    - bst->font->ascent;
 
   BSOD_CHAR_DELAY (bst, 10000);
   BSOD_TEXT (bst, LEFT,
@@ -2987,17 +4090,20 @@ hvx (Display *dpy, Window window)
 static struct bsod_state *
 hpux (Display *dpy, Window window)
 {
-  struct bsod_state *bst = make_bsod_state (dpy, window, "hvx", "HVX");
+  struct bsod_state *bst = make_bsod_state (dpy, window, "hpux", "HPUX");
   const char *sysname;
   char buf[2048];
+# ifdef HAVE_UNAME
+  struct utsname uts;
+# endif /* UNAME */
 
   bst->scroll_p = True;
-  bst->y = bst->xgwa.height - bst->bottom_margin - bst->font->ascent;
+  bst->y = bst->xgwa.height - bst->bottom_margin - bst->yoff
+    - bst->font->ascent;
 
   sysname = "HPUX";
 # ifdef HAVE_UNAME
   {
-    struct utsname uts;
     char *s;
     if (uname (&uts) >= 0)
       sysname = uts.nodename;
@@ -3089,7 +4195,7 @@ hpux (Display *dpy, Window window)
     for (i = 0; i <= steps; i++)
       {
         if (i > steps) i = steps;
-        sprintf (buf, 
+        sprintf (buf,
                "*** Dumping: %3d%% complete (%d of 40 MB) (device 64:0x2)\r",
                  i * 100 / steps,
                  i * size / steps);
@@ -3116,7 +4222,8 @@ os390 (Display *dpy, Window window)
   struct bsod_state *bst = make_bsod_state (dpy, window, "os390", "OS390");
 
   bst->scroll_p = True;
-  bst->y = bst->xgwa.height - bst->bottom_margin - bst->font->ascent;
+  bst->y = bst->xgwa.height - bst->bottom_margin - bst->yoff
+    - bst->font->ascent;
 
   BSOD_LINE_DELAY (bst, 100000);
   BSOD_TEXT (bst, LEFT,
@@ -3178,14 +4285,17 @@ tru64 (Display *dpy, Window window)
   struct bsod_state *bst = make_bsod_state (dpy, window, "tru64", "Tru64");
   const char *sysname;
   char buf[2048];
+# ifdef HAVE_UNAME
+  struct utsname uts;
+#endif /* UNAME */
 
   bst->scroll_p = True;
-  bst->y = bst->xgwa.height - bst->bottom_margin - bst->font->ascent;
+  bst->y = bst->xgwa.height - bst->bottom_margin - bst->yoff
+    - bst->font->ascent;
 
   sysname = "127.0.0.1";
 # ifdef HAVE_UNAME
   {
-    struct utsname uts;
     if (uname (&uts) >= 0)
       sysname = uts.nodename;
   }
@@ -3242,7 +4352,7 @@ tru64 (Display *dpy, Window window)
   BSOD_PAUSE (bst, 3000000);
 
   BSOD_TEXT (bst, LEFT,
-    "\n"   
+    "\n"
     "CPU 0 booting\n"
     "\n"
     "\n"
@@ -3411,7 +4521,7 @@ nvidia_draw (struct bsod_state *bst)
 
   nvs->tick++;
   if ((random() % 5) == 0)    /* change the display */
-    nvspatter (nvs->grid, nvs->rows, nvs->cols, nvs->ncolors, 
+    nvspatter (nvs->grid, nvs->rows, nvs->cols, nvs->ncolors,
                countof(nvs->bits), False);
 
   return 250000;
@@ -3467,7 +4577,7 @@ nvidia (Display *dpy, Window window)
       if (!nvs->gc1) nvs->gc1 = XCreateGC (dpy, nvs->bits[i], 0, &gcv);
 
       XSetForeground (dpy, nvs->gc1, 0);
-      XFillRectangle (dpy, nvs->bits[i], nvs->gc1, 0, 0, 
+      XFillRectangle (dpy, nvs->bits[i], nvs->gc1, 0, 0,
                       nvs->cellw, nvs->cellh);
       XSetForeground (dpy, nvs->gc1, 1);
 
@@ -3481,10 +4591,10 @@ nvidia (Display *dpy, Window window)
 
   /* Randomize the grid
    */
-  nvspatter (nvs->grid, nvs->rows, nvs->cols, nvs->ncolors, 
+  nvspatter (nvs->grid, nvs->rows, nvs->cols, nvs->ncolors,
              countof(nvs->bits), True);
   for (i = 0; i < 20; i++)
-    nvspatter (nvs->grid, nvs->rows, nvs->cols, nvs->ncolors, 
+    nvspatter (nvs->grid, nvs->rows, nvs->cols, nvs->ncolors,
                countof(nvs->bits), False);
 
   return bst;
@@ -3503,7 +4613,7 @@ nvidia (Display *dpy, Window window)
  * The Apple ][ logic and video hardware is in apple2.c. The TV is emulated by
  * analogtv.c for maximum realism
  *
- * Trevor Blackwell <tlb@tlb.org> 
+ * Trevor Blackwell <tlb@tlb.org>
  */
 
 static const char * const apple2_basic_errors[]={
@@ -3550,10 +4660,10 @@ static void a2controller_crash(apple2_sim_t *sim, int *stepno,
   if (!sim->controller_data)
     sim->controller_data = calloc(sizeof(struct mydata),1);
   mine=(struct mydata *) sim->controller_data;
-  
+
   switch(*stepno) {
   case 0:
-    
+
     a2_init_memory_active(sim);
     sim->dec->powerup = 1000.0;
 
@@ -3785,6 +4895,134 @@ apple2crash (Display *dpy, Window window)
 }
 
 
+static void
+a2controller_ransomware(apple2_sim_t *sim, int *stepno,
+                        double *next_actiontime)
+{
+  apple2_state_t *st=sim->st;
+
+  struct mydata {
+    const char *str;
+    Bool bold_p;
+  } *mine;
+
+  if (!sim->controller_data)
+    sim->controller_data = calloc(sizeof(struct mydata),1);
+  mine=(struct mydata *) sim->controller_data;
+
+  switch(*stepno) {
+  case 0:
+
+    st->gr_mode |= A2_GR_FULL;
+    a2_cls(st);
+    a2_goto(st,0,16);
+    a2_prints(st, "APPLE ][");
+    a2_goto(st,2,0);
+    *stepno = 10;
+    *next_actiontime += 2;
+    break;
+
+  case 10:
+    a2_prints(st, "READY\n\n");
+    *stepno = 11;
+    *next_actiontime += 1;
+    break;
+
+  case 11:
+    a2_goto(st, 1, 0);
+    *stepno = 12;
+    mine->str =
+      ("\n"
+       " _____________________________________\n"
+       "/                                     \\\n"
+       "! OOPS YOUR FILES HAVE BEEN ENCRYPTED !\n"
+       "!          ________________________   !\n"
+       "!         !                        !  !\n"
+       "!  [/--\\]   !  [ WHAT HAPPENED TO MY ] !  !\n"
+       "!  [!]  [!]   !  [ COMPUTER? ]           !  !\n"
+       "!  [!]  [!]   !                        !  !\n"
+       "! [######]  !  [ CAN I RECOVER MY ]    !  !\n"
+       "! [######]  !  [ FILES? ]              !  !\n"
+       "! [######]  !                        !  !\n"
+       "! [######]  !  [ HOW DO I PAY? ]       !  !\n"
+       "!         !                        !  !\n"
+       "!         !________________________!  !\n"
+       "!                                     !\n"
+       "!         BITCOIN ACCEPTED HERE       !\n"
+       "\\_____________________________________/\n"
+       "\n"
+       "\n"
+       "WAITING FOR BLOCKCHAIN..@\n"
+       "\n"
+       "PLEASE INSERT NEXT FLOPPY: "
+       );
+    break;
+
+  case 12:
+    {
+      char c = *mine->str;
+      mine->str++;
+
+      if (c == 0)
+        {
+          *next_actiontime += 30;
+          *stepno = 0;
+        }
+      else if (c == '[')
+        mine->bold_p++;
+      else if (c == ']')
+        mine->bold_p--;
+      else
+        {
+          if (c == '@')
+            {
+              c = '.';
+              *next_actiontime += 2;
+            }
+
+          if (mine->bold_p)
+            c |= 0xC0;
+          a2_printc_noscroll(st, c);
+          if (c == '.')
+            *next_actiontime += 1;
+        }
+    }
+    break;
+
+  case A2CONTROLLER_FREE:
+    return;
+  }
+}
+
+
+static int
+a2_ransomware_draw (struct bsod_state *bst)
+{
+  apple2_sim_t *sim = (apple2_sim_t *) bst->closure;
+  if (! sim) {
+    sim = apple2_start (bst->dpy, bst->window, 9999999,
+                        a2controller_ransomware);
+    bst->closure = sim;
+  }
+
+  if (! apple2_one_frame (sim)) {
+    bst->closure = 0;
+  }
+
+  return 10000;
+}
+
+static struct bsod_state *
+apple2ransomware (Display *dpy, Window window)
+{
+  struct bsod_state *bst = make_bsod_state (dpy, window, "apple2", "Apple2");
+  bst->draw_cb = a2_ransomware_draw;
+  bst->free_cb = a2_free;
+  return bst;
+}
+
+
+
 /* A crash spotted on a cash machine circa 2006, by jwz.  I didn't note
    what model it was; probably a Tranax Mini-Bank 1000 or similar vintage.
  */
@@ -3793,23 +5031,22 @@ atm (Display *dpy, Window window)
 {
   struct bsod_state *bst = make_bsod_state (dpy, window, "atm", "ATM");
 
-  Pixmap pixmap = 0;
-  int pix_w = atm_width;
-  int pix_h = atm_height;
+  int pix_w, pix_h;
   int x, y, i = 0;
   float scale = 0.48;
+  Pixmap mask = 0;
+  Pixmap pixmap = image_data_to_pixmap (dpy, window,
+                                        atm_png, sizeof(atm_png),
+                                        &pix_w, &pix_h, &mask);
 
   XClearWindow (dpy, window);
 
-  pixmap = XCreatePixmapFromBitmapData (dpy, window, (char *) atm_bits,
-                                        atm_width, atm_height,
-                                        bst->fg, bst->bg, bst->xgwa.depth);
-
-  while (pix_w <= bst->xgwa.width  * scale && 
+  while (pix_w <= bst->xgwa.width  * scale &&
          pix_h <= bst->xgwa.height * scale)
     {
-      pixmap = double_pixmap (dpy, bst->gc, bst->xgwa.visual, bst->xgwa.depth,
+      pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
                               pixmap, pix_w, pix_h);
+      mask = double_pixmap (dpy, bst->xgwa.visual, 1, mask, pix_w, pix_h);
       pix_w *= 2;
       pix_h *= 2;
       i++;
@@ -3832,9 +5069,12 @@ atm (Display *dpy, Window window)
         XDrawLine (bst->dpy, pixmap, bst->gc, 0, j, pix_w, j);
     }
 
+  XSetClipMask (dpy, bst->gc, mask);
+  XSetClipOrigin (dpy, bst->gc, x, y);
   XCopyArea (dpy, pixmap, window, bst->gc, 0, 0, pix_w, pix_h, x, y);
 
   XFreePixmap (dpy, pixmap);
+  XFreePixmap (dpy, mask);
 
   return bst;
 }
@@ -3925,22 +5165,35 @@ android (Display *dpy, Window window)
 
   int state = 0;
 
-  Pixmap pixmap = 0;
+  Pixmap pixmap = 0, mask = 0;
   int pix_w = 0, pix_h = 0;
 
-# ifdef DO_XPM
-  pixmap = xpm_data_to_pixmap (dpy, window, (char **) android_skate,
-                               &pix_w, &pix_h, 0);
+  pixmap = image_data_to_pixmap (dpy, window, 
+                                 android_png, sizeof(android_png),
+                                 &pix_w, &pix_h, &mask);
   if (! pixmap) abort();
+  {
+    int i, n = 0;
+    if (bst->xgwa.width > 2560) n++;  /* Retina displays */
+    for (i = 0; i < n; i++)
+      {
+        pixmap = double_pixmap (dpy, bst->xgwa.visual, bst->xgwa.depth,
+                                pixmap, pix_w, pix_h);
+        mask = double_pixmap (dpy, bst->xgwa.visual, 1,
+                              mask, pix_w, pix_h);
+        pix_w *= 2;
+        pix_h *= 2;
+      }
+  }
   bst->pixmap = pixmap;
-# endif /* DO_XPM */
+  bst->mask = mask;
 
   bst->left_margin = (bst->xgwa.width - (cw * 40)) / 2;
   if (bst->left_margin < 0) bst->left_margin = 0;
 
   while (1) {
     unsigned long delay =
-      ((state == 0 || 
+      ((state == 0 ||
         state == countof(lines0) ||
         state == countof(lines0) + countof(lines1) ||
         state == countof(lines0) + countof(lines1) + countof(lines2))
@@ -3952,10 +5205,11 @@ android (Display *dpy, Window window)
         BSOD_COLOR (bst, bg, bg);
         BSOD_RECT (bst, True, 0, 0, bst->xgwa.width, bst->xgwa.height);
         BSOD_COLOR (bst, bg, c1);
-        BSOD_MOVETO (bst, bst->left_margin, bst->top_margin + line_height);
+        BSOD_MOVETO (bst, bst->left_margin + bst->xoff,
+                     bst->top_margin + bst->yoff + line_height);
         BSOD_TEXT (bst, LEFT, "*** UNLOCKED ***\n");
         BSOD_COLOR (bst, c2, bg);
-        BSOD_TEXT (bst, LEFT, 
+        BSOD_TEXT (bst, LEFT,
                    "PRIMOU PVT SHIP S-OFF RL\n"
                    "HBOOT-1.17.0000\n"
                    "CPLD-None\n"
@@ -3972,7 +5226,7 @@ android (Display *dpy, Window window)
         if (pixmap)
           {
             int x = (bst->xgwa.width - pix_w) / 2;
-            int y = bst->xgwa.height - pix_h;
+            int y = bst->xgwa.height - bst->yoff - pix_h;
             BSOD_PIXMAP (bst, 0, 0, pix_w, pix_h, x, y);
           }
       }
@@ -4071,6 +5325,8 @@ static const struct {
   { "Windows",         windows_31 },
   { "NT",              windows_nt },
   { "Win2K",           windows_other },
+  { "Win10",           windows_10 },
+  { "Ransomware",      windows_ransomware },
   { "Amiga",           amiga },
   { "Mac",             mac },
   { "MacsBug",         macsbug },
@@ -4082,7 +5338,7 @@ static const struct {
   { "SparcLinux",      sparc_linux },
   { "BSD",             bsd },
   { "Atari",           atari },
-#ifndef HAVE_COCOA
+#ifndef HAVE_JWXYZ
   { "BlitDamage",      blitdamage },
 #endif
   { "Solaris",         sparc_solaris },
@@ -4098,13 +5354,15 @@ static const struct {
   { "ATM",             atm },
   { "GLaDOS",          glados },
   { "Android",         android },
+  { "VMware",          vmware },
 };
 
 
 struct driver_state {
   const char *name;
-  int only, which;
-  int delay;
+  int only, which, next_one;
+  int mode_duration;
+  int delay_remaining;
   time_t start;
   Bool debug_p, cycle_p;
   struct bsod_state *bst;
@@ -4114,7 +5372,7 @@ struct driver_state {
 static void
 hack_title (struct driver_state *dst)
 {
-# ifndef HAVE_COCOA
+# ifndef HAVE_JWXYZ
   char *oname = 0;
   XFetchName (dst->bst->dpy, dst->bst->window, &oname);
   if (oname && !strncmp (oname, "BSOD: ", 6)) {
@@ -4127,7 +5385,7 @@ hack_title (struct driver_state *dst)
     XStoreName (dst->bst->dpy, dst->bst->window, nname);
     free (nname);
   }
-# endif /* !HAVE_COCOA */
+# endif /* !HAVE_JWXYZ */
 }
 
 static void *
@@ -4136,12 +5394,13 @@ bsod_init (Display *dpy, Window window)
   struct driver_state *dst = (struct driver_state *) calloc (1, sizeof(*dst));
   char *s;
 
-  dst->delay = get_integer_resource (dpy, "delay", "Integer");
-  if (dst->delay < 3) dst->delay = 3;
+  dst->mode_duration = get_integer_resource (dpy, "delay", "Integer");
+  if (dst->mode_duration < 3) dst->mode_duration = 3;
 
   dst->debug_p = get_boolean_resource (dpy, "debug", "Boolean");
 
   dst->only = -1;
+  dst->next_one = -1;
   s = get_string_resource(dpy, "doOnly", "DoOnly");
   if (s && !strcasecmp (s, "cycle"))
     {
@@ -4177,20 +5436,36 @@ bsod_draw (Display *dpy, Window window, void *closure)
 
  AGAIN:
   now = time ((time_t *) 0);
-  time_left = dst->start + dst->delay - now;
+  time_left = dst->start + dst->mode_duration - now;
 
   if (dst->bst && dst->bst->img_loader)   /* still loading */
     {
-      dst->bst->img_loader = 
+      dst->bst->img_loader =
         load_image_async_simple (dst->bst->img_loader, 0, 0, 0, 0, 0);
       return 100000;
     }
 
+ DELAY_NOW:
+  /* Rather than returning a multi-second delay from the draw() routine,
+     meaning "don't call us again for N seconds", we quantize that down
+     to 1/10th second intervals so that it's more responsive to
+     rotate/reshape events.
+   */
+  if (dst->delay_remaining)
+    {
+      int inc = 10000;
+      int this_delay = MIN (dst->delay_remaining, inc);
+      dst->delay_remaining = MAX (0, dst->delay_remaining - inc);
+      return this_delay;
+    }
+
   if (! dst->bst && time_left > 0)     /* run completed; wait out the delay */
     {
       if (dst->debug_p)
         fprintf (stderr, "%s: %s: %d left\n", progname, dst->name, time_left);
-      return 500000;
+      dst->start = 0;
+      if (time_left > 5) time_left = 5;  /* Boooored now */
+      dst->delay_remaining = 1000000 * time_left;
     }
 
   else if (dst->bst)                   /* sub-mode currently running */
@@ -4200,12 +5475,15 @@ bsod_draw (Display *dpy, Window window, void *closure)
       if (time_left > 0)
         this_delay = bsod_pop (dst->bst);
 
-      /* XSync (dpy, False);  slows down char drawing too much on HAVE_COCOA */
+      /* XSync (dpy, False);  slows down char drawing too much on HAVE_JWXYZ */
 
       if (this_delay == 0)
         goto AGAIN;                    /* no delay, not expired: stay here */
       else if (this_delay >= 0)
-        return this_delay;             /* return; time to sleep */
+        {
+          dst->delay_remaining = this_delay;   /* return; time to sleep */
+          goto DELAY_NOW;
+        }
       else
         {                              /* sub-mode run completed or expired */
           if (dst->debug_p)
@@ -4217,43 +5495,48 @@ bsod_draw (Display *dpy, Window window, void *closure)
     }
   else                                 /* launch a new sub-mode */
     {
-      if (dst->cycle_p)
+      if (dst->next_one >= 0)
+        dst->which = dst->next_one, dst->next_one = -1;
+      else if (dst->cycle_p)
         dst->which = (dst->which + 1) % countof(all_modes);
       else if (dst->only >= 0)
         dst->which = dst->only;
       else
         {
           int count = countof(all_modes);
+          int *enabled = (int *) calloc (sizeof(*enabled), count + 1);
+          int nenabled = 0;
           int i;
 
-          for (i = 0; i < 200; i++)
+          for (i = 0; i < count; i++)
             {
               char name[100], class[100];
-              int new_mode = (random() & 0xFF) % count;
-
-              if (i < 100 && new_mode == dst->which)
-                continue;
-
-              sprintf (name,  "do%s", all_modes[new_mode].name);
-              sprintf (class, "Do%s", all_modes[new_mode].name);
-
+              sprintf (name,  "do%s", all_modes[i].name);
+              sprintf (class, "Do%s", all_modes[i].name);
               if (get_boolean_resource (dpy, name, class))
-                {
-                  dst->which = new_mode;
-                  break;
-                }
+                enabled[nenabled++] = i;
             }
 
-          if (i >= 200)
+          if (nenabled == 0)
             {
               fprintf (stderr, "%s: no display modes enabled?\n", progname);
               /* exit (-1); */
               dst->which = dst->only = 0;
             }
+          else if (nenabled == 1)
+            dst->which = enabled[0];
+          else
+            {
+              i = dst->which;
+              while (i == dst->which)
+                i = enabled[random() % nenabled];
+              dst->which = i;
+            }
+          free (enabled);
         }
-          
+
       if (dst->debug_p)
-        fprintf (stderr, "%s: %s: launch\n", progname, 
+        fprintf (stderr, "%s: %s: launch\n", progname,
                  all_modes[dst->which].name);
 
       /* Run the mode setup routine...
@@ -4270,15 +5553,18 @@ bsod_draw (Display *dpy, Window window, void *closure)
       if (dst->bst)
         {
           if (dst->debug_p)
-            fprintf (stderr, "%s: %s: queue size: %d (%d)\n", progname, 
+            fprintf (stderr, "%s: %s: queue size: %d (%d)\n", progname,
                      dst->name, dst->bst->pos, dst->bst->queue_size);
 
           hack_title (dst);
           dst->bst->pos = 0;
-          dst->bst->x = dst->bst->current_left = dst->bst->left_margin;
+          dst->bst->x = dst->bst->current_left =
+            dst->bst->left_margin + dst->bst->xoff;
 
-          if (dst->bst->y < dst->bst->top_margin + dst->bst->font->ascent)
-            dst->bst->y = dst->bst->top_margin + dst->bst->font->ascent;
+          if (dst->bst->y < 
+              dst->bst->top_margin + dst->bst->yoff + dst->bst->font->ascent)
+            dst->bst->y =
+              dst->bst->top_margin + dst->bst->yoff + dst->bst->font->ascent;
         }
     }
 
@@ -4287,7 +5573,7 @@ bsod_draw (Display *dpy, Window window, void *closure)
 
 
 static void
-bsod_reshape (Display *dpy, Window window, void *closure, 
+bsod_reshape (Display *dpy, Window window, void *closure,
               unsigned int w, unsigned int h)
 {
   struct driver_state *dst = (struct driver_state *) closure;
@@ -4300,11 +5586,13 @@ bsod_reshape (Display *dpy, Window window, void *closure,
   if (dst->debug_p)
     fprintf (stderr, "%s: %s: reshape reset\n", progname, dst->name);
 
-  /* just pick a new mode and restart when the window is resized. */
+  /* just restart this mode and restart when the window is resized. */
   if (dst->bst)
     free_bsod_state (dst->bst);
   dst->bst = 0;
   dst->start = 0;
+  dst->delay_remaining = 0;
+  dst->next_one = dst->which;
   dst->name = "none";
   XClearWindow (dpy, window);
 }
@@ -4318,18 +5606,8 @@ bsod_event (Display *dpy, Window window, void *closure, XEvent *event)
 
   /* pick a new mode and restart when mouse clicked, or certain keys typed. */
 
-  if (event->type == ButtonPress)
-    return True;
-  else if (event->type == ButtonRelease)
+  if (screenhack_event_helper (dpy, window, event))
     reset_p = True;
-  else if (event->type == KeyPress)
-    {
-      KeySym keysym;
-      char c = 0;
-      XLookupString (&event->xkey, &c, 1, &keysym, 0);
-      if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
-        reset_p = True;
-    }
 
   if (reset_p)
     {
@@ -4339,6 +5617,7 @@ bsod_event (Display *dpy, Window window, void *closure, XEvent *event)
         free_bsod_state (dst->bst);
       dst->bst = 0;
       dst->start = 0;
+      dst->delay_remaining = 0;
       dst->name = "none";
       XClearWindow (dpy, window);
       return True;
@@ -4366,6 +5645,8 @@ static const char *bsod_defaults [] = {
   "*doWindows:            True",
   "*doNT:                 True",
   "*doWin2K:              True",
+  "*doWin10:              True",
+  "*doRansomware:         True",
   "*doAmiga:              True",
   "*doMac:                True",
   "*doMacsBug:            True",
@@ -4391,11 +5672,7 @@ static const char *bsod_defaults [] = {
   "*doATM:                True",
   "*doGLaDOS:             True",
   "*doAndroid:            True",
-
-  "*font:                 9x15bold",
-  "*font2:                -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
-  "*bigFont:              -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
-  "*bigFont2:             -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
+  "*doVMware:             True",
 
   ".foreground:                   White",
   ".background:                   Black",
@@ -4403,10 +5680,26 @@ static const char *bsod_defaults [] = {
   ".windows.foreground:           White",
   ".windows.background:           #0000AA",    /* EGA color 0x01. */
 
+  ".nt.foreground:        White",
+  ".nt.background:        #0000AA",    /* EGA color 0x01. */
+
   ".windowslh.foreground:  White",
   ".windowslh.background:  #AA0000",    /* EGA color 0x04. */
   ".windowslh.background2: #AAAAAA",    /* EGA color 0x07. */
 
+  ".win10.foreground:      White",
+  ".win10.background:      #1070AA",
+
+  ".ransomware.foreground:   White",
+  ".ransomware.background:   #841212",
+  ".ransomware.foreground2:  Black",      /* ransom note */
+  ".ransomware.background2:  White",
+  ".ransomware.foreground3:  Black",      /* buttons */
+  ".ransomware.background3:  #AAAAAA",
+  ".ransomware.link:         #7BF9F6",
+  ".ransomware.timerheader:  #BDBE02",
+
+
   ".glaDOS.foreground:    White",
   ".glaDOS.background:    #0000AA",    /* EGA color 0x01. */
 
@@ -4414,14 +5707,12 @@ static const char *bsod_defaults [] = {
   ".amiga.background:     Black",
   ".amiga.background2:    White",
 
-  ".mac.foreground:       #BBFFFF",
+  ".mac.foreground:       #FFFFFF",
   ".mac.background:       Black",
 
   ".atari.foreground:     Black",
   ".atari.background:     White",
 
-  ".macsbug.font:         -*-courier-medium-r-*-*-*-80-*-*-m-*-*-*",
-  ".macsbug.bigFont:      -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".macsbug.foreground:           Black",
   ".macsbug.background:           White",
   ".macsbug.borderColor:   #AAAAAA",
@@ -4434,55 +5725,42 @@ static const char *bsod_defaults [] = {
   ".macx.textBackground:   Black",
   ".macx.background:      #888888",
 
-  ".macdisk.font:         -*-courier-bold-r-*-*-*-80-*-*-m-*-*-*",
-  ".macdisk.bigFont:      -*-courier-bold-r-*-*-*-100-*-*-m-*-*-*",
+  ".macinstall.barForeground: #C0C0C0",
+  ".macinstall.barBackground: #888888",
 
-  ".sco.font:             -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".sco.foreground:       White",
   ".sco.background:       Black",
 
-  ".hvx.font:             -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".hvx.foreground:       White",
   ".hvx.background:       Black",
 
   ".linux.foreground:      White",
   ".linux.background:      Black",
 
-  ".hppalinux.bigFont:    -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".hppalinux.foreground:  White",
   ".hppalinux.background:  Black",
 
-  ".sparclinux.bigFont:           -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".sparclinux.foreground: White",
   ".sparclinux.background: Black",
 
-  ".bsd.font:             vga",
-  ".bsd.bigFont:          -sun-console-medium-r-*-*-22-*-*-*-m-*-*-*",
-  ".bsd.bigFont2:         -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".bsd.foreground:       #c0c0c0",
   ".bsd.background:       Black",
 
-  ".solaris.font:          -sun-gallant-*-*-*-*-19-*-*-*-*-120-*-*",
   ".solaris.foreground:    Black",
   ".solaris.background:    White",
 
-  ".hpux.bigFont:         -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".hpux.foreground:      White",
   ".hpux.background:      Black",
 
-  ".os390.bigFont:        -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".os390.background:     Black",
   ".os390.foreground:     Red",
 
-  ".tru64.bigFont:        -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".tru64.foreground:     White",
   ".tru64.background:     #0000AA",    /* EGA color 0x01. */
 
-  ".vms.bigFont:          -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".vms.foreground:       White",
   ".vms.background:       Black",
 
-  ".msdos.bigFont:        -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".msdos.foreground:     White",
   ".msdos.background:     Black",
 
@@ -4502,6 +5780,10 @@ static const char *bsod_defaults [] = {
   ".android.color6:       #66AA33", /* green3 */
   ".android.color7:       #FF0000", /* red */
 
+  ".vmware.foreground:    White",
+  ".vmware.foreground2:           Yellow",
+  ".vmware.background:    #a700a8",    /* purple */
+
   "*dontClearRoot:         True",
 
   ANALOGTV_DEFAULTS
@@ -4510,16 +5792,144 @@ static const char *bsod_defaults [] = {
   "*useSHM:                True",
 #endif
 
-# ifdef USE_IPHONE
-  "*font:                 Courier-Bold 9",
-  ".amiga.font:                   Courier-Bold 12",
-  ".macsbug.font:         Courier-Bold 5",
-  ".sco.font:             Courier-Bold 9",
-  ".hvx.font:             Courier-Bold 9",
-  ".bsd.font:             Courier-Bold 9",
-  ".solaris.font:          Courier-Bold 6",
-  ".macdisk.font:          Courier-Bold 6",
-# endif
+  "*fontB:                ",
+  "*fontC:                ",
+
+# if defined(USE_IPHONE)
+
+  "*font:                 PxPlus IBM VGA8 16, Courier-Bold 14",
+  "*bigFont:              ",
+
+  ".mac.font:             Courier-Bold 18",
+  ".macsbug.font:         Courier-Bold 8",
+  ".macx.font:            Courier-Bold 14",
+  ".macdisk.font:         Courier-Bold 14",
+  ".macinstall.font:      Helvetica 12, Arial 12",
+  ".macinstall.bigFont:           Helvetica 12, Arial 12",
+  ".msdos.font:                   PxPlus IBM VGA8 32, Courier-Bold 28",
+  ".nt.font:              PxPlus IBM VGA8 12, Courier-Bold 10",
+  ".win10.font:                   Arial 12, Helvetica 12",
+  ".win10.bigFont:        Arial 12, Helvetica 12",
+  ".win10.fontB:          Arial 50, Helvetica 50",
+  ".win10.fontC:          Arial 9, Helvetica 9",
+
+  /* The real Solaris font is ../OSX/Gallant19.bdf but I don't know how
+     to convert that to a TTF, so let's use Luxi Mono instead. */
+  ".solaris.font:         Luxi Mono 12, PxPlus IBM VGA8 12, Courier Bold 12",
+
+  /* "Arial" loads "ArialMT" but "Arial Bold" does not load "Arial-BoldMT"? */
+  ".ransomware.font:         Arial 11, Helvetica 11",
+  ".ransomware.fontB:        Arial 9, Helvetica 9",
+  ".ransomware.fontC:        Arial Bold 11, Arial-BoldMT 11, Helvetica Bold 11",
+
+# elif defined(HAVE_ANDROID)
+
+  "*font:                 PxPlus IBM VGA8 16",
+  "*bigFont:              ",
+
+  ".mac.font:             -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
+  ".macsbug.font:         -*-courier-bold-r-*-*-*-80-*-*-m-*-*-*",
+  ".macx.font:            -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".macdisk.font:         -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".macinstall.font:      -*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*",
+  ".macinstall.bigFont:           -*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*",
+  ".msdos.font:                   PxPlus IBM VGA8 32",
+  ".nt.font:              PxPlus IBM VGA8 12",
+  ".solaris.font:         Luxi Mono 12, PxPlus IBM VGA8 12, Courier Bold 12",
+
+  ".win10.font:                   -*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*",
+  ".win10.bigFont:        -*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*",
+  ".win10.fontB:          -*-helvetica-medium-r-*-*-*-500-*-*-*-*-*-*",
+  ".win10.fontC:          -*-helvetica-medium-r-*-*-*-90-*-*-*-*-*-*",
+
+  ".ransomware.font:      -*-helvetica-medium-r-*-*-*-100-*-*-*-*-*-*",
+  ".ransomware.fontB:     -*-helvetica-medium-r-*-*-*-80-*-*-*-*-*-*",
+  ".ransomware.fontC:     -*-helvetica-bold-r-*-*-*-100-*-*-*-*-*-*",
+
+# elif defined(HAVE_COCOA)
+
+  "*font:                 PxPlus IBM VGA8 8,  Courier Bold 9",
+  "*bigFont:              PxPlus IBM VGA8 32, Courier Bold 24",
+
+  ".mac.font:             Monaco 10, Courier Bold 9",
+  ".mac.bigFont:          Monaco 18, Courier Bold 18",
+
+  ".macsbug.font:         Monaco 10, Courier Bold 9",
+  ".macsbug.bigFont:      Monaco 24, Courier Bold 24",
+
+  ".macx.font:            Courier Bold 9",
+  ".macx.bigFont:         Courier Bold 14",
+  ".macdisk.font:         Courier Bold 9",
+  ".macdisk.bigFont:      Courier Bold 18",
+  ".macinstall.font:      Helvetica 24, Arial 24",
+  ".macinstall.bigFont:           Helvetica 24, Arial 24",
+
+  ".hvx.bigFont:          PxPlus IBM VGA8 16, Courier Bold 14",
+  ".hppalinux.bigFont:    PxPlus IBM VGA8 16, Courier Bold 14",
+  ".linux.bigFont:        PxPlus IBM VGA8 16, Courier Bold 14",
+  ".hpux.bigFont:         PxPlus IBM VGA8 16, Courier Bold 14",
+  ".msdos.font:                   PxPlus IBM VGA8 16, Courier Bold 14",
+  ".solaris.font:         Luxi Mono 12, PxPlus IBM VGA8 12, Courier Bold 12",
+  ".solaris.bigFont:      Luxi Mono 16, PxPlus IBM VGA8 16, Courier Bold 14",
+
+  ".win10.font:                   Arial 24, Helvetica 24",
+  ".win10.bigFont:        Arial 24, Helvetica 24",
+  ".win10.fontB:          Arial 100, Helvetica 100",
+  ".win10.fontC:          Arial 16, Helvetica 16",
+
+  ".ransomware.font:         Arial 24, Helvetica 24",
+  ".ransomware.bigFont:      Arial 24, Helvetica 24",
+  ".ransomware.fontB:        Arial 16, Helvetica 16",
+  ".ransomware.fontC:        Arial Bold 24, Helvetica Bold 24",
+
+# else   /* X11 */
+
+  "*font:                 9x15bold",
+  "*bigFont:              -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
+
+  ".macsbug.font:         -*-courier-medium-r-*-*-*-80-*-*-m-*-*-*",
+  ".macsbug.bigFont:      -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+
+  ".macdisk.font:         -*-courier-bold-r-*-*-*-80-*-*-m-*-*-*",
+  ".macdisk.bigFont:      -*-courier-bold-r-*-*-*-100-*-*-m-*-*-*",
+  ".macinstall.font:      -*-helvetica-medium-r-*-*-*-180-*-*-*-*-*-*",
+  ".macinstall.bigFont:           -*-helvetica-medium-r-*-*-*-180-*-*-*-*-*-*",
+
+  ".sco.font:             -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".hvx.font:             -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".hppalinux.bigFont:    -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".sparclinux.bigFont:           -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+
+  /* Some systems might have this, but I'm not sure where it comes from: */
+  /* ".bsd.font:          -*-vga-normal-r-*-*-*-120-*-*-c-*-*-*", */
+  /* The fonts/misc/vga.pcf that comes with xdosemu has no XLFD name: */
+  ".bsd.font:             vga",
+  ".bsd.bigFont:          -*-vga-normal-r-*-*-*-220-*-*-c-*-*-*",
+
+  /* The original Solaris console font was:
+     -sun-gallant-demi-r-normal-*-*-140-*-*-c-*-*-*
+     Red Hat introduced Luxi Mono as its console font, which is similar
+     to Gallant. X.Org includes it but Debian and Fedora do not. */
+  ".solaris.font:         -*-luxi mono-medium-r-normal--*-140-*-*-m-*-*-*",
+
+  ".hpux.bigFont:         -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".os390.bigFont:        -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".tru64.bigFont:        -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".vms.bigFont:          -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".msdos.bigFont:        -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+
+  ".win10.font:                   -*-helvetica-medium-r-*-*-*-180-*-*-*-*-*-*",
+  ".win10.bigFont:        -*-helvetica-medium-r-*-*-*-180-*-*-*-*-*-*",
+  ".win10.fontB:          -*-helvetica-medium-r-*-*-*-240-*-*-*-*-*-*",
+  ".win10.fontC:          -*-helvetica-medium-r-*-*-*-140-*-*-*-*-*-*",
+
+  ".ransomware.font:      -*-helvetica-medium-r-*-*-*-180-*-*-*-*-*-*",
+  ".ransomware.bigFont:           -*-helvetica-medium-r-*-*-*-180-*-*-*-*-*-*",
+  ".ransomware.fontB:     -*-helvetica-medium-r-*-*-*-140-*-*-*-*-*-*",
+  ".ransomware.fontC:     -*-helvetica-bold-r-*-*-*-180-*-*-*-*-*-*",
+
+
+# endif  /* X11 */
 
   0
 };
@@ -4534,6 +5944,10 @@ static const XrmOptionDescRec bsod_options [] = {
   { "-no-nt",          ".doNT",                XrmoptionNoArg,  "False" },
   { "-2k",             ".doWin2K",             XrmoptionNoArg,  "True"  },
   { "-no-2k",          ".doWin2K",             XrmoptionNoArg,  "False" },
+  { "-win10",          ".doWin10",             XrmoptionNoArg,  "True"  },
+  { "-no-win10",       ".doWin10",             XrmoptionNoArg,  "False" },
+  { "-ransomware",     ".doRansomware",        XrmoptionNoArg,  "True"  },
+  { "-no-ransomware",  ".doRansomware",        XrmoptionNoArg,  "False" },
   { "-amiga",          ".doAmiga",             XrmoptionNoArg,  "True"  },
   { "-no-amiga",       ".doAmiga",             XrmoptionNoArg,  "False" },
   { "-mac",            ".doMac",               XrmoptionNoArg,  "True"  },
@@ -4584,6 +5998,8 @@ static const XrmOptionDescRec bsod_options [] = {
   { "-no-glados",      ".doGLaDOS",            XrmoptionNoArg,  "False" },
   { "-android",                ".doAndroid",           XrmoptionNoArg,  "True"  },
   { "-no-android",     ".doAndroid",           XrmoptionNoArg,  "False" },
+  { "-vmware",         ".doVMware",            XrmoptionNoArg,  "True"  },
+  { "-no-vmware",      ".doVMware",            XrmoptionNoArg,  "False" },
   ANALOGTV_OPTIONS
   { 0, 0, 0, 0 }
 };