http://ftp.x.org/contrib/applications/xscreensaver-2.24.tar.gz
[xscreensaver] / driver / xscreensaver.c
old mode 100755 (executable)
new mode 100644 (file)
index 77c06e0..accfbdf
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-1994 Jamie Zawinski <jwz@mcom.com>
+/* xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@netscape.com>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -9,14 +9,15 @@
  * implied warranty.
  */
 
-#include "version.h"
-
 /*   ========================================================================
  *   First we wait until the keyboard and mouse become idle for the specified
- *   amount of time.  We do this by periodically checking with the XIdle
- *   server extension.
+ *   amount of time.  We do this in one of three different ways: periodically
+ *   checking with the XIdle server extension; selecting key and mouse events
+ *   on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension
+ *   to send us a "you are idle" event.
  *
- *   Then, we map a full screen black window.  
+ *   Then, we map a full screen black window (or, in the case of the 
+ *   MIT-SCREEN-SAVER extension, use the one it gave us.)
  *
  *   We place a __SWM_VROOT property on this window, so that newly-started
  *   clients will think that this window is a "virtual root" window.
  *   and a few other things.  The included "xscreensaver_command" program
  *   sends these messsages.
  *
- *   If we don't have the XIdle extension, then we do the XAutoLock trick:
- *   notice every window that gets created, and wait 30 seconds or so until
- *   its creating process has settled down, and then select KeyPress events on
- *   those windows which already select for KeyPress events.  It's important
- *   that we not select KeyPress on windows which don't select them, because
- *   that would interfere with event propagation.  This will break if any
- *   program changes its event mask to contain KeyRelease or PointerMotion
- *   more than 30 seconds after creating the window, but that's probably
- *   pretty rare.
+ *   If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
+ *   extensions, then we do the XAutoLock trick: notice every window that
+ *   gets created, and wait 30 seconds or so until its creating process has
+ *   settled down, and then select KeyPress events on those windows which
+ *   already select for KeyPress events.  It's important that we not select
+ *   KeyPress on windows which don't select them, because that would
+ *   interfere with event propagation.  This will break if any program
+ *   changes its event mask to contain KeyRelease or PointerMotion more than
+ *   30 seconds after creating the window, but that's probably pretty rare.
  *   
  *   The reason that we can't select KeyPresses on windows that don't have
  *   them already is that, when dispatching a KeyPress event, X finds the
  *   "client changing event mask" problem that the KeyPress events hack does.
  *   I think polling is more reliable.
  *
- *   None of this crap happens if we're using the XIdle extension, so install
- *   it if the description above sounds just too flaky to live.  It is, but
- *   those are your choices.
+ *   None of this crap happens if we're using one of the extensions, so install
+ *   one of them if the description above sounds just too flaky to live.  It
+ *   is, but those are your choices.
  *
- *   A third idle-detection option could be implement (but is not): when running
- *   on the console display ($DISPLAY is `localhost`:0) and we're on a machine
- *   where /dev/tty and /dev/mouse have reasonable last-modification times, we
- *   could just stat those.  But the incremental benefit of implementing this
- *   is really small, so forget I said anything.
+ *   A third idle-detection option could be implemented (but is not): when
+ *   running on the console display ($DISPLAY is `localhost`:0) and we're on a
+ *   machine where /dev/tty and /dev/mouse have reasonable last-modification
+ *   times, we could just stat() those.  But the incremental benefit of
+ *   implementing this is really small, so forget I said anything.
  *
  *   Debugging hints:
  *     - Have a second terminal handy.
  *     - Be careful where you set your breakpoints, you don't want this to
  *       stop under the debugger with the keyboard grabbed or the blackout
  *       window exposed.
- *     - you probably can't set breakpoints in functions that are called on
+ *     - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
+ *       to keep your emacs window alive even when xscreensaver has grabbed.
+ *     - Go read the code related to `debug_p'.
+ *     - You probably can't set breakpoints in functions that are called on
  *       the other side of a call to fork() -- if your clients are dying 
  *       with signal 5, Trace/BPT Trap, you're losing in this way.
- *     - If you aren't using XIdle, don't leave this stopped under the
- *       debugger for very long, or the X input buffer will get huge because
- *       of the keypress events it's selecting for.  This can make your X
- *       server wedge with "no more input buffers."
+ *     - If you aren't using a server extension, don't leave this stopped
+ *       under the debugger for very long, or the X input buffer will get
+ *       huge because of the keypress events it's selecting for.  This can
+ *       make your X server wedge with "no more input buffers."
  *       
- *   ========================================================================
- */
+ * ======================================================================== */
 
-#if __STDC__
-#include <stdlib.h>
-#include <unistd.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
 #endif
 
 #include <stdio.h>
+#include <ctype.h>
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 #include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
 #include <X11/Xos.h>
-
-#ifdef HAVE_XIDLE
+#ifdef HAVE_XMU
+# ifndef VMS
+#  include <X11/Xmu/Error.h>
+# else  /* !VMS */
+#  include <Xmu/Error.h>
+# endif /* !VMS */
+#else  /* !HAVE_XMU */
+# include "xmu.h"
+#endif /* !HAVE_XMU */
+
+#ifdef HAVE_XIDLE_EXTENSION
 #include <X11/extensions/xidle.h>
-#endif
+#endif /* HAVE_XIDLE_EXTENSION */
 
 #include "xscreensaver.h"
+#include "version.h"
+#include "yarandom.h"
+#include "resources.h"
+#include "visual.h"
 
-#if defined(SVR4) || defined(SYSV) || defined(VMS)
-# define srandom(i) srand((unsigned int)(i))
-#else
-# ifndef __linux
-extern void srandom P((int));          /* srand() is in stdlib.h... */
-# endif
-#endif
-
-extern char *get_string_resource P((char *, char *));
-extern Bool get_boolean_resource P((char *, char *));
-extern int get_integer_resource P((char *, char *));
-extern unsigned int get_minutes_resource P((char *, char *));
-extern unsigned int get_seconds_resource P((char *, char *));
-
-extern Visual *get_visual_resource P((Display *, char *, char *));
-extern int get_visual_depth P((Display *, Visual *));
-
-extern void notice_events_timer P((XtPointer closure, void *timer));
-extern void cycle_timer P((void *junk1, XtPointer junk2));
-extern void activate_lock_timer P((void *junk1, XtPointer junk2));
-extern void sleep_until_idle P((Bool until_idle_p));
-
-extern void ensure_no_screensaver_running P((void));
-extern void initialize_screensaver_window P((void));
-extern void disable_builtin_screensaver P((void));
-
-extern void hack_environment P((void));
-extern void grab_keyboard_and_mouse P((void));
-extern void ungrab_keyboard_and_mouse P((void));
-
-extern void save_argv P((int argc, char **argv));
-
-
-char *screensaver_version;
-char *progname;
-char *progclass;
-XrmDatabase db;
-
-XtAppContext app;
-
-Display *dpy;
-Screen *screen;
-Visual *visual;
-int visual_depth;
+saver_info *global_si_kludge = 0;      /* I hate C so much... */
 
-Widget toplevel_shell;
+char *progname = 0;
+char *progclass = 0;
+XrmDatabase db = 0;
 
-Time lock_timeout;
 
-extern Time timeout;
-extern Time cycle;
-#ifndef NO_LOCKING
-extern Time passwd_timeout;
-#endif
-extern Time pointer_timeout;
-extern Time notice_events_timeout;
-extern XtIntervalId lock_id, cycle_id;
-
-Bool use_xidle;
-Bool verbose_p;
-Bool lock_p, locked_p;
-
-extern char **screenhacks;
-extern int screenhacks_count;
-extern char *shell;
-extern int nice_inferior;
-extern Window screensaver_window;
-extern Cursor cursor;
-extern Colormap cmap, cmap2;
-extern Bool fade_p, unfade_p;
-extern int fade_seconds, fade_ticks;
-extern Bool install_cmap_p;
-extern Bool locking_disabled_p;
-extern char *nolock_reason;
-extern Bool demo_mode_p;
-extern Bool dbox_up_p;
-extern int next_mode_p;
-
-static time_t initial_delay;
-
-extern Atom XA_VROOT, XA_XSETROOT_ID;
-extern Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
-
-static Atom XA_SCREENSAVER;
 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK;
 
-#ifdef NO_MOTIF /* kludge */
-Bool demo_mode_p = 0;
-Bool dbox_up_p = 0;
-#ifndef NO_LOCKING
-Time passwd_timeout = 0;
-#endif
-#endif
-
-\f
-#ifdef NO_DEMO_MODE
-# define demo_mode() abort()
-#else
-extern void demo_mode P((void));
-#endif
 \f
 static XrmOptionDescRec options [] = {
-  { "-timeout",                ".timeout",     XrmoptionSepArg, 0 },
-  { "-idelay",         ".initialDelay",XrmoptionSepArg, 0 },
-  { "-cycle",          ".cycle",       XrmoptionSepArg, 0 },
-  { "-visual",         ".visualID",    XrmoptionSepArg, 0 },
-  { "-lock-timeout",   ".lockTimeout", XrmoptionSepArg, 0 },
-  { "-verbose",                ".verbose",     XrmoptionNoArg, "on" },
-  { "-silent",         ".verbose",     XrmoptionNoArg, "off" },
-  { "-xidle",          ".xidle",       XrmoptionNoArg, "on" },
-  { "-no-xidle",       ".xidle",       XrmoptionNoArg, "off" },
-  { "-lock",           ".lock",        XrmoptionNoArg, "on" },
-  { "-no-lock",                ".lock",        XrmoptionNoArg, "off" }
+  { "-timeout",                   ".timeout",          XrmoptionSepArg, 0 },
+  { "-cycle",             ".cycle",            XrmoptionSepArg, 0 },
+  { "-lock-mode",         ".lock",             XrmoptionNoArg, "on" },
+  { "-no-lock-mode",      ".lock",             XrmoptionNoArg, "off" },
+  { "-lock-timeout",      ".lockTimeout",      XrmoptionSepArg, 0 },
+  { "-visual",            ".visualID",         XrmoptionSepArg, 0 },
+  { "-install",                   ".installColormap",  XrmoptionNoArg, "on" },
+  { "-no-install",        ".installColormap",  XrmoptionNoArg, "off" },
+  { "-verbose",                   ".verbose",          XrmoptionNoArg, "on" },
+  { "-silent",            ".verbose",          XrmoptionNoArg, "off" },
+  { "-xidle-extension",           ".xidleExtension",   XrmoptionNoArg, "on" },
+  { "-no-xidle-extension", ".xidleExtension",  XrmoptionNoArg, "off" },
+  { "-mit-extension",     ".mitSaverExtension",XrmoptionNoArg, "on" },
+  { "-no-mit-extension",   ".mitSaverExtension",XrmoptionNoArg, "off" },
+  { "-sgi-extension",     ".sgiSaverExtension",XrmoptionNoArg, "on" },
+  { "-no-sgi-extension",   ".sgiSaverExtension",XrmoptionNoArg, "off" },
+  { "-idelay",            ".initialDelay",     XrmoptionSepArg, 0 },
+  { "-nice",              ".nice",             XrmoptionSepArg, 0 }
 };
 
 static char *defaults[] = {
-#ifndef VMS
-#include "XScreenSaver.ad.h"
-#else
 #include "XScreenSaver_ad.h"
-#endif
  0
 };
 
 static void
-do_help P((void))
+do_help (saver_info *si)
 {
   printf ("\
-xscreensaver %s, copyright (c) 1991-1994 by Jamie Zawinski <jwz@mcom.com>.\n\
+xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@netscape.com>\n\
 The standard Xt command-line options are accepted; other options include:\n\
 \n\
-    -timeout <minutes>         when the screensaver should activate\n\
-    -cycle <minutes>           how long to let each hack run\n\
-    -idelay <seconds>          how long to sleep before startup\n\
-    -demo                      enter interactive demo mode on startup\n\
-    -verbose                   be loud\n\
-    -silent                    don't\n\
-    -xidle                     use the XIdle server extension\n\
-    -no-xidle                  don't\n\
-    -lock                      require a password before deactivating\n\
-    -no-lock                   don't\n\
-    -lock-timeout <minutes>    grace period before locking; default 0\n\
-    -help                      this message\n\
+    -timeout <minutes>         When the screensaver should activate.\n\
+    -cycle <minutes>           How long to let each hack run.\n\
+    -lock-mode                 Require a password before deactivating.\n\
+    -no-lock-mode              Don't.\n\
+    -lock-timeout <minutes>    Grace period before locking; default 0.\n\
+    -visual <id-or-class>      Which X visual to run on.\n\
+    -install                   Install a private colormap.\n\
+    -no-install                Don't.\n\
+    -verbose                   Be loud.\n\
+    -silent                    Don't.\n\
+    -mit-extension             Use the R6 MIT_SCREEN_SAVER server extension.\n\
+    -no-mit-extension          Don't.\n\
+    -sgi-extension             Use the SGI SCREEN-SAVER server extension.\n\
+    -no-sgi-extension          Don't.\n\
+    -xidle-extension           Use the R5 XIdle server extension.\n\
+    -no-xidle-extension        Don't.\n\
+    -help                      This message.\n\
+\n\
+The `xscreensaver' program should be left running in the background.\n\
+Use the `xscreensaver-command' program to manipulate a running xscreensaver.\n\
 \n\
-Use the `xscreensaver-command' program to control a running screensaver.\n\
+The `*programs' resource controls which graphics demos will be launched by\n\
+the screensaver.  See `man xscreensaver' or the web page for more details.\n\
 \n\
-The *programs, *colorPrograms, and *monoPrograms resources control which\n\
-graphics demos will be launched by the screensaver.  See the man page for\n\
-more details.\n\n",
-         screensaver_version);
+For updates, check http://people.netscape.com/jwz/xscreensaver/\n\n",
+         si->version);
 
 #ifdef NO_LOCKING
-  printf("Support for locking was not enabled at compile-time.\n");
+  printf ("Support for locking was not enabled at compile-time.\n");
 #endif
 #ifdef NO_DEMO_MODE
-  printf("Support for demo mode was not enabled at compile-time.\n");
-#endif
-#ifndef HAVE_XIDLE
-  printf("Support for the XIdle extension was not enabled at compile-time.\n");
+  printf ("Support for demo mode was not enabled at compile-time.\n");
 #endif
+#if !defined(HAVE_XIDLE_EXTENSION) && !defined(HAVE_MIT_SAVER_EXTENSION) && !defined(HAVE_SGI_SAVER_EXTENSION)
+  printf ("Support for the XIDLE, SCREEN_SAVER, and MIT-SCREEN-SAVER server\
+ extensions\nwas not enabled at compile-time.\n");
+#endif /* !HAVE_XIDLE_EXTENSION && !HAVE_MIT_SAVER_EXTENSION && !HAVE_SGI_SAVER_EXTENSION */
 
   fflush (stdout);
   exit (1);
 }
 
 
+static char *
+reformat_hack(const char *hack)
+{
+  int i;
+  const char *in = hack;
+  int indent = 13;
+  char *h2 = (char *) malloc(strlen(in) + indent + 2);
+  char *out = h2;
+
+  while (isspace(*in)) in++;           /* skip whitespace */
+  while (*in && !isspace(*in) && *in != ':')
+    *out++ = *in++;                    /* snarf first token */
+  while (isspace(*in)) in++;           /* skip whitespace */
+
+  if (*in == ':')
+    *out++ = *in++;                    /* copy colon */
+  else
+    {
+      in = hack;
+      out = h2;                                /* reset to beginning */
+    }
+
+  *out = 0;
+
+  while (isspace(*in)) in++;           /* skip whitespace */
+  for (i = strlen(h2); i < indent; i++)        /* indent */
+    *out++ = ' ';
+
+  while (*in) *out++ = *in++;          /* copy rest of line */
+  *out = 0;
+
+  return h2;
+}
+
+
 static void
-get_screenhacks P((void))
+get_screenhacks (saver_info *si)
 {
-  char *data[3];
-  int i, hacks_size = 10;
+  saver_preferences *p = &si->prefs;
+  int i = 0;
+  int hacks_size = 60;
+  int size;
+  char *d;
+
+  d = get_string_resource ("monoPrograms", "MonoPrograms");
+  if (d && !*d) { free(d); d = 0; }
+  if (!d)
+    d = get_string_resource ("colorPrograms", "ColorPrograms");
+  if (d && !*d) { free(d); d = 0; }
+
+  if (d)
+    {
+      fprintf (stderr,
+       "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
+       see the manual for details.\n", blurb());
+      free(d);
+    }
 
-  data[0] = get_string_resource ("programs", "Programs");
-  data[1] = ((CellsOfScreen (screen) <= 2)
-            ? get_string_resource ("monoPrograms", "MonoPrograms")
-            : get_string_resource ("colorPrograms", "ColorPrograms"));
-  data[2] = 0;
-  if (! data[0]) data[0] = data[1], data[1] = 0;
+  d = get_string_resource ("programs", "Programs");
 
-  screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
-  screenhacks_count = 0;
+  size = d ? strlen (d) : 0;
+  p->screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
+  p->screenhacks_count = 0;
 
-  for (i = 0; data[i]; i++)
+  while (i < size)
     {
-      int j = 0;
-      char *d = data [i];
-      int size = strlen (d);
-      while (j < size)
+      int end, start = i;
+      if (d[i] == ' ' || d[i] == '\t' || d[i] == '\n' || d[i] == 0)
        {
-         int end, start = j;
-         if (d[j] == ' ' || d[j] == '\t' || d[j] == '\n' || d[j] == 0)
-           {
-             j++;
-             continue;
-           }
-         if (hacks_size <= screenhacks_count)
-           screenhacks = (char **) realloc (screenhacks,
-                                            (hacks_size = hacks_size * 2) *
-                                            sizeof (char *));
-         screenhacks [screenhacks_count++] = d + j;
-         while (d[j] != 0 && d[j] != '\n')
-           j++;
-         end = j;
-         while (j > start && (d[j-1] == ' ' || d[j-1] == '\t'))
-           j--;
-         d[j] = 0;
-         j = end + 1;
+         i++;
+         continue;
        }
+      if (hacks_size <= p->screenhacks_count)
+       p->screenhacks = (char **) realloc (p->screenhacks,
+                                           (hacks_size = hacks_size * 2) *
+                                           sizeof (char *));
+      p->screenhacks [p->screenhacks_count++] = d + i;
+      while (d[i] != 0 && d[i] != '\n')
+       i++;
+      end = i;
+      while (i > start && (d[i-1] == ' ' || d[i-1] == '\t'))
+       i--;
+      d[i] = 0;
+      i = end + 1;
     }
 
   /* shrink all whitespace to one space, for the benefit of the "demo"
      mode display.  We only do this when we can easily tell that the
      whitespace is not significant (no shell metachars).
    */
-  for (i = 0; i < screenhacks_count; i++)
+  for (i = 0; i < p->screenhacks_count; i++)
     {
-      char *s = screenhacks [i];
+      char *s = p->screenhacks [i];
       char *s2;
       int L = strlen (s);
       int j, k;
@@ -367,342 +347,584 @@ get_screenhacks P((void))
              for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
                k++;
              if (k > 0)
-               for (s2 = s + j + 1; *s2; s2++)
-                 s2 [0] = s2 [k];
+               {
+                 for (s2 = s+j+1; s2[k]; s2++)
+                   *s2 = s2[k];
+                 *s2 = 0;
+               }
              break;
            }
        }
     DONE:
-      ;
+      p->screenhacks[i] = reformat_hack(s);  /* mallocs */
     }
 
-  if (screenhacks_count)
+  if (p->screenhacks_count)
     {
       /* Shrink down the screenhacks array to be only as big as it needs to.
         This doesn't really matter at all. */
-      screenhacks = (char **)
-       realloc (screenhacks, ((screenhacks_count + 1) * sizeof(char *)));
-      screenhacks [screenhacks_count] = 0;
+      p->screenhacks = (char **)
+       realloc (p->screenhacks, ((p->screenhacks_count + 1) *
+                                 sizeof(char *)));
+      p->screenhacks [p->screenhacks_count] = 0;
     }
   else
     {
-      free (screenhacks);
-      screenhacks = 0;
+      free (p->screenhacks);
+      p->screenhacks = 0;
     }
 }
 
 
+static Bool blurb_timestamp_p = False;   /* kludge */
+
+
 static void
-get_resources P((void))
+get_resources (saver_info *si)
 {
-  /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
-  visual         = get_visual_resource (dpy, "visualID", "VisualID");
-  timeout         = 1000 * get_minutes_resource ("timeout", "Time");
-  cycle           = 1000 * get_minutes_resource ("cycle",   "Time");
-  lock_timeout   = 1000 * get_minutes_resource ("lockTimeout", "Time");
-  nice_inferior   = get_integer_resource ("nice", "Nice");
-  verbose_p       = get_boolean_resource ("verbose", "Boolean");
-  lock_p          = get_boolean_resource ("lock", "Boolean");
-  install_cmap_p  = get_boolean_resource ("installColormap", "Boolean");
-  fade_p         = get_boolean_resource ("fade", "Boolean");
-  unfade_p       = get_boolean_resource ("unfade", "Boolean");
-  fade_seconds   = get_seconds_resource ("fadeSeconds", "Time");
-  fade_ticks     = get_integer_resource ("fadeTicks", "Integer");
-  shell           = get_string_resource ("bourneShell", "BourneShell");
-  initial_delay   = get_seconds_resource ("initialDelay", "Time");
-  pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
-  notice_events_timeout = 1000 * get_seconds_resource ("windowCreationTimeout",
-                                                      "Time");
+  char *s;
+  saver_preferences *p = &si->prefs;
+
+  p->verbose_p     = get_boolean_resource ("verbose", "Boolean");
+  p->timestamp_p    = get_boolean_resource ("timestamp", "Boolean");
+  p->lock_p        = get_boolean_resource ("lock", "Boolean");
+  p->fade_p        = get_boolean_resource ("fade", "Boolean");
+  p->unfade_p      = get_boolean_resource ("unfade", "Boolean");
+  p->fade_seconds   = get_seconds_resource ("fadeSeconds", "Time");
+  p->fade_ticks            = get_integer_resource ("fadeTicks", "Integer");
+  p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
+  p->nice_inferior  = get_integer_resource ("nice", "Nice");
+
+  p->initial_delay  = get_seconds_resource ("initialDelay", "Time");
+  p->timeout        = 1000 * get_minutes_resource ("timeout", "Time");
+  p->lock_timeout   = 1000 * get_minutes_resource ("lockTimeout", "Time");
+  p->cycle          = 1000 * get_minutes_resource ("cycle", "Time");
+
 #ifndef NO_LOCKING
-  passwd_timeout  = 1000 * get_seconds_resource ("passwdTimeout", "Time");
-  if (passwd_timeout == 0) passwd_timeout = 30000;
+  p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
 #endif
-  if (timeout < 10000) timeout = 10000;
-  if (cycle != 0 && cycle < 2000) cycle = 2000;
-  if (pointer_timeout == 0) pointer_timeout = 5000;
-  if (notice_events_timeout == 0) notice_events_timeout = 10000;
-  if (fade_seconds == 0 || fade_ticks == 0) fade_p = False;
-  if (! fade_p) unfade_p = False;
 
-  visual_depth = get_visual_depth (dpy, visual);
+  p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
+  p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
+                                                      "Time");
+  p->shell = get_string_resource ("bourneShell", "BourneShell");
+
 
-  if (visual_depth <= 1 || CellsOfScreen (screen) <= 2)
-    install_cmap_p = False;
+  /* don't set use_xidle_extension unless it is explicitly specified */
+  if ((s = get_string_resource ("xidleExtension", "Boolean")))
+    p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
+  else
+#ifdef HAVE_XIDLE_EXTENSION            /* pick a default */
+    p->use_xidle_extension = True;     /* if we have it, use it */
+#else  /* !HAVE_XIDLE_EXTENSION */
+    p->use_xidle_extension = False;
+#endif /* !HAVE_XIDLE_EXTENSION */
+  if (s) free (s);
+
+  /* don't set use_mit_extension unless it is explicitly specified */
+  if ((s = get_string_resource ("mitSaverExtension", "Boolean")))
+    p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
+                                                      "Boolean");
+  else
+#ifdef HAVE_MIT_SAVER_EXTENSION                /* pick a default */
+    p->use_mit_saver_extension = False;        /* Default false, because it sucks */
+#else  /* !HAVE_MIT_SAVER_EXTENSION */
+    p->use_mit_saver_extension = False;
+#endif /* !HAVE_MIT_SAVER_EXTENSION */
+  if (s) free (s);
+
+
+  /* don't set use_mit_extension unless it is explicitly specified */
+  if ((s = get_string_resource ("sgiSaverExtension", "Boolean")))
+    p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
+                                                      "Boolean");
+  else
+#ifdef HAVE_SGI_SAVER_EXTENSION                /* pick a default */
+    p->use_sgi_saver_extension = True; /* if we have it, use it */
+#else  /* !HAVE_SGI_SAVER_EXTENSION */
+    p->use_sgi_saver_extension = False;
+#endif /* !HAVE_SGI_SAVER_EXTENSION */
+  if (s) free (s);
+
+
+  /* Throttle the various timeouts to reasonable values.
+   */
+#ifndef NO_LOCKING
+  if (p->passwd_timeout == 0) p->passwd_timeout = 30000;        /* 30 secs */
+#endif
+  if (p->timeout < 10000) p->timeout = 10000;                   /* 10 secs */
+  if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000;        /*  2 secs */
+  if (p->pointer_timeout == 0) p->pointer_timeout = 5000;       /*  5 secs */
+  if (p->notice_events_timeout == 0)
+    p->notice_events_timeout = 10000;                           /* 10 secs */
+  if (p->fade_seconds == 0 || p->fade_ticks == 0)
+    p->fade_p = False;
+  if (! p->fade_p) p->unfade_p = False;
+
+  p->watchdog_timeout = p->cycle;
+  if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000;          /* 30 secs */
+  if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /*  1 hr */
 
 #ifdef NO_LOCKING
-  locking_disabled_p = True;
-  nolock_reason = "not compiled with locking support";
-  if (lock_p)
+  si->locking_disabled_p = True;
+  si->nolock_reason = "not compiled with locking support";
+  if (p->lock_p)
     {
-      lock_p = False;
-      fprintf (stderr, "%s: %snot compiled with support for locking.\n",
-              progname, (verbose_p ? "## " : ""));
+      p->lock_p = False;
+      fprintf (stderr, "%s: not compiled with support for locking.\n",
+              blurb());
     }
 #else  /* ! NO_LOCKING */
-  if (lock_p && locking_disabled_p)
+  if (p->lock_p && si->locking_disabled_p)
     {
-      fprintf (stderr, "%s: %slocking is disabled (%s).\n", progname,
-              (verbose_p ? "## " : ""), nolock_reason);
-      lock_p = False;
+      fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
+              si->nolock_reason);
+      p->lock_p = False;
     }
 #endif /* ! NO_LOCKING */
 
-  /* don't set use_xidle unless it is explicitly specified */
-  if (get_string_resource ("xidle", "Boolean"))
-    use_xidle = get_boolean_resource ("xidle", "Boolean");
-  else
-#ifdef HAVE_XIDLE      /* pick a default */
-    use_xidle = True;
-#else
-    use_xidle = False;
-#endif
-  get_screenhacks ();
+  get_screenhacks (si);
+
+  if (p->debug_p)
+    {
+      XSynchronize(si->dpy, True);
+      p->verbose_p = True;
+      p->timestamp_p = True;
+      p->initial_delay = 0;
+    }
+
+  blurb_timestamp_p = p->timestamp_p;
 }
 
+
 char *
-timestring P((void))
+timestring (void)
 {
-  long now = time ((time_t *) 0);
+  time_t now = time ((time_t *) 0);
   char *str = (char *) ctime (&now);
   char *nl = (char *) strchr (str, '\n');
   if (nl) *nl = 0; /* take off that dang newline */
   return str;
 }
 
-#ifdef NO_SETUID
-# define hack_uid()
-# define hack_uid_warn()
-#else /* !NO_SETUID */
-extern void hack_uid P((void));
-extern void hack_uid_warn P((void));
-#endif /* NO_SETUID */
-
+static void initialize (saver_info *si, int argc, char **argv);
+static void main_loop (saver_info *si);
 
-#ifndef NO_LOCKING
-extern Bool unlock_p P((Widget));
-extern Bool lock_init P((void));
-#endif
+int
+main (int argc, char **argv)
+{
+  saver_info si;
+  memset(&si, 0, sizeof(si));
+  global_si_kludge = &si;      /* I hate C so much... */
+  initialize (&si, argc, argv);
+  main_loop (&si);             /* doesn't return */
+  return 0;
+}
 
-static void initialize P((int argc, char **argv));
-static void main_loop P((void));
 
-#ifndef VMS
-void
-#else
 int
-#endif
-main (argc, argv)
-     int argc;
-     char **argv;
+saver_ehandler (Display *dpy, XErrorEvent *error)
 {
-  initialize (argc, argv);
-  main_loop ();
+  saver_info *si = global_si_kludge;   /* I hate C so much... */
+
+  fprintf (real_stderr, "\nX error in %s:\n", blurb());
+  if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
+    saver_exit (si, -1);
+  else
+    fprintf (real_stderr, " (nonfatal.)\n");
+  return 0;
 }
 
 
+const char *
+blurb (void)
+{
+  if (!blurb_timestamp_p)
+    return progname;
+  else
+    {
+      static char buf[255];
+      time_t now = time ((time_t *) 0);
+      char *ct = (char *) ctime (&now);
+      int n = strlen(progname);
+      if (n > 100) n = 99;
+      strncpy(buf, progname, n);
+      buf[n++] = ':';
+      buf[n++] = ' ';
+      strncpy(buf+n, ct+11, 8);
+      strcpy(buf+n+9, ": ");
+      return buf;
+    }
+}
+
 static void
-#if __STDC__
-initialize_connection (int argc, char **argv)
-#else
-initialize_connection (argc, argv)
-     int argc;
-     char **argv;
-#endif
+initialize_connection (saver_info *si, int argc, char **argv)
 {
-  toplevel_shell = XtAppInitialize (&app, progclass,
+  int i;
+  Widget toplevel_shell;
+
+  /* The X resource database blows up if argv[0] has a "." in it. */
+  {
+    char *s = argv[0];
+    while ((s = strchr (s, '.')))
+      *s = '_';
+  }
+
+  toplevel_shell = XtAppInitialize (&si->app, progclass,
                                    options, XtNumber (options),
                                    &argc, argv, defaults, 0, 0);
 
-  dpy = XtDisplay (toplevel_shell);
-  screen = XtScreen (toplevel_shell);
-  db = XtDatabase (dpy);
-  XtGetApplicationNameAndClass (dpy, &progname, &progclass);
+  si->dpy = XtDisplay (toplevel_shell);
+  si->db = XtDatabase (si->dpy);
+  XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
+
+  if(strlen(progname)  > 100) progname [99] = 0;  /* keep it short. */
+
+  db = si->db; /* resources.c needs this */
 
   if (argc == 2 && !strcmp (argv[1], "-help"))
-    do_help ();
+    do_help (si);
+
+  else if (argc == 2 && !strcmp (argv[1], "-debug"))
+    si->prefs.debug_p = True;  /* no resource for this one, out of paranoia. */
+
   else if (argc > 1)
     {
-      fprintf (stderr, "%s: unknown option %s\n", progname, argv [1]);
+      const char *s = argv[1];
+      fprintf (stderr, "%s: unknown option \"%s\".  Try \"-help\".\n",
+              blurb(), s);
+
+      if (s[0] == '-' && s[1] == '-') s++;
+      if (!strcmp (s, "-activate") ||
+         !strcmp (s, "-deactivate") ||
+         !strcmp (s, "-cycle") ||
+         !strcmp (s, "-next") ||
+         !strcmp (s, "-prev") ||
+         !strcmp (s, "-exit") ||
+         !strcmp (s, "-restart") ||
+         !strcmp (s, "-demo") ||
+         !strcmp (s, "-lock") ||
+         !strcmp (s, "-version") ||
+         !strcmp (s, "-time"))
+       {
+         fprintf (stderr, "\n\
+    However, %s is an option to the `xscreensaver-command' program.\n\
+    The `xscreensaver' program is a daemon that runs in the background.\n\
+    You control a running xscreensaver process by sending it messages\n\
+    with `xscreensaver-command'.  See the man pages for details,\n\
+    or check the web page: http://people.netscape.com/jwz/xscreensaver/\n\n",
+                  s);
+
+         /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
+            suggest that explicitly. */
+         if (!strcmp (s, "-lock"))
+           fprintf (stderr, "\
+    Or perhaps you meant either the \"-lock-mode\" or the\n\
+    \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
+       }
+
       exit (1);
     }
-  get_resources ();
-  hack_uid_warn ();
-  hack_environment ();
-  XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
-  XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
-  XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION", False);
-  XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
-  XA_XSETROOT_ID = XInternAtom (dpy, "_XSETROOT_ID", False);
-  XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
-  XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
-  XA_RESTART = XInternAtom (dpy, "RESTART", False);
-  XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
-  XA_NEXT = XInternAtom (dpy, "NEXT", False);
-  XA_PREV = XInternAtom (dpy, "PREV", False);
-  XA_EXIT = XInternAtom (dpy, "EXIT", False);
-  XA_DEMO = XInternAtom (dpy, "DEMO", False);
-  XA_LOCK = XInternAtom (dpy, "LOCK", False);
+  get_resources (si);
+#ifndef NO_SETUID
+  hack_uid_warn (si);
+#endif /* NO_SETUID */
+  XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
+  XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
+  XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
+  XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
+  XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
+  XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
+  XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
+  XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
+  XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
+  XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
+  XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
+  XA_PREV = XInternAtom (si->dpy, "PREV", False);
+  XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
+  XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
+  XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
+
+  si->nscreens = ScreenCount(si->dpy);
+  si->screens = (saver_screen_info *)
+    calloc(sizeof(saver_screen_info), si->nscreens);
+
+  si->default_screen = &si->screens[DefaultScreen(si->dpy)];
+
+  for (i = 0; i < si->nscreens; i++)
+    {
+      saver_screen_info *ssi = &si->screens[i];
+      ssi->global = si;
+      ssi->screen = ScreenOfDisplay (si->dpy, i);
+
+      /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
+      ssi->default_visual =
+       get_visual_resource (ssi->screen, "visualID", "VisualID", False);
+
+      ssi->current_visual = ssi->default_visual;
+      ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
+
+      if (ssi == si->default_screen)
+       /* Since this is the default screen, use the one already created. */
+       ssi->toplevel_shell = toplevel_shell;
+      else
+       /* Otherwise, each screen must have its own unmapped root widget. */
+       ssi->toplevel_shell =
+         XtVaAppCreateShell(progname, progclass, applicationShellWidgetClass,
+                            si->dpy,
+                            XtNscreen, ssi->screen,
+                            XtNvisual, ssi->current_visual,
+                            XtNdepth,  visual_depth(ssi->screen,
+                                                    ssi->current_visual),
+                            0);
+    }
 }
 
-extern void init_sigchld P((void));
 
 static void
-initialize (argc, argv)
-     int argc;
-     char **argv;
+initialize (saver_info *si, int argc, char **argv)
 {
+  int i;
+  saver_preferences *p = &si->prefs;
   Bool initial_demo_mode_p = False;
-  screensaver_version = (char *) malloc (5);
-  memcpy (screensaver_version, screensaver_id + 17, 4);
-  screensaver_version [4] = 0;
+  si->version = (char *) malloc (5);
+  memcpy (si->version, screensaver_id + 17, 4);
+  si->version [4] = 0;
   progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
 
+  if(strlen(progname) > 100) progname[99] = 0;  /* keep it short. */
+
 #ifdef NO_LOCKING
-  locking_disabled_p = True;
-  nolock_reason = "not compiled with locking support";
-#else
-  locking_disabled_p = False;
-  if (! lock_init ())  /* before hack_uid() for proper permissions */
+  si->locking_disabled_p = True;
+  si->nolock_reason = "not compiled with locking support";
+#else  /* !NO_LOCKING */
+  si->locking_disabled_p = False;
+
+# ifdef SCO
+  set_auth_parameters(argc, argv);
+# endif /* SCO */
+
+  if (! lock_init (argc, argv))        /* before hack_uid() for proper permissions */
     {
-      locking_disabled_p = True;
-      nolock_reason = "error getting password";
+      si->locking_disabled_p = True;
+      si->nolock_reason = "error getting password";
     }
-#endif
+#endif  /* !NO_LOCKING */
+
+#ifndef NO_SETUID
+  hack_uid (si);
+#endif /* NO_SETUID */
 
-  hack_uid ();
   progclass = "XScreenSaver";
 
-  /* remove -demo switch before saving argv */
-  {
-    int i;
-    for (i = 1; i < argc; i++)
-      while (!strcmp ("-demo", argv [i]))
-       {
-         int j;
-         initial_demo_mode_p = True;
-         for (j = i; j < argc; j++)
-           argv [j] = argv [j+1];
-         argv [j] = 0;
-         argc--;
-         if (argc <= i) break;
-       }
-  }
+  /* remove -initial-demo-mode switch before saving argv */
+  for (i = 1; i < argc; i++)
+    while (!strcmp ("-initial-demo-mode", argv [i]))
+      {
+       int j;
+       initial_demo_mode_p = True;
+       for (j = i; j < argc; j++)
+         argv [j] = argv [j+1];
+       argv [j] = 0;
+       argc--;
+       if (argc <= i) break;
+      }
   save_argv (argc, argv);
-  initialize_connection (argc, argv);
-  ensure_no_screensaver_running ();
+  initialize_connection (si, argc, argv);
 
-  if (verbose_p)
+  if (p->verbose_p)
     printf ("\
-%s %s, copyright (c) 1991-1994 by Jamie Zawinski <jwz@mcom.com>.\n\
- pid = %d.\n", progname, screensaver_version, getpid ());
-  ensure_no_screensaver_running ();
-
-  demo_mode_p = initial_demo_mode_p;
-  screensaver_window = 0;
-  cursor = 0;
-  initialize_screensaver_window ();
+%s %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@netscape.com>\n\
+ pid = %d.\n", progname, si->version, (int) getpid ());
+
+  
+  for (i = 0; i < si->nscreens; i++)
+    if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
+      exit (1);
+
+  hack_environment (si);
+
+  si->demo_mode_p = initial_demo_mode_p;
   srandom ((int) time ((time_t *) 0));
-  cycle_id = 0;
-  lock_id = 0;
-  locked_p = False;
 
-  if (use_xidle)
+  if (p->use_sgi_saver_extension)
+    {
+#ifdef HAVE_SGI_SAVER_EXTENSION
+      if (! query_sgi_saver_extension (si))
+       {
+         fprintf (stderr,
+        "%s: display %s does not support the SGI SCREEN_SAVER extension.\n",
+                  blurb(), DisplayString (si->dpy));
+         p->use_sgi_saver_extension = False;
+       }
+      else if (p->use_mit_saver_extension)
+       {
+         fprintf (stderr, "%s: SGI SCREEN_SAVER extension used instead\
+ of MIT-SCREEN-SAVER extension.\n",
+                  blurb());
+         p->use_mit_saver_extension = False;
+       }
+      else if (p->use_xidle_extension)
+       {
+         fprintf (stderr,
+        "%s: SGI SCREEN_SAVER extension used instead of XIDLE extension.\n",
+                  blurb());
+         p->use_xidle_extension = False;
+       }
+#else  /* !HAVE_MIT_SAVER_EXTENSION */
+      fprintf (stderr,
+       "%s: not compiled with support for the SGI SCREEN_SAVER extension.\n",
+              blurb());
+      p->use_sgi_saver_extension = False;
+#endif /* !HAVE_SGI_SAVER_EXTENSION */
+    }
+
+  if (p->use_mit_saver_extension)
     {
-#ifdef HAVE_XIDLE
+#ifdef HAVE_MIT_SAVER_EXTENSION
+      if (! query_mit_saver_extension (si))
+       {
+         fprintf (stderr,
+        "%s: display %s does not support the MIT-SCREEN-SAVER extension.\n",
+                  blurb(), DisplayString (si->dpy));
+         p->use_mit_saver_extension = False;
+       }
+      else if (p->use_xidle_extension)
+       {
+         fprintf (stderr,
+        "%s: MIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
+                  blurb());
+         p->use_xidle_extension = False;
+       }
+#else  /* !HAVE_MIT_SAVER_EXTENSION */
+      fprintf (stderr,
+       "%s: not compiled with support for the MIT-SCREEN-SAVER extension.\n",
+              blurb());
+      p->use_mit_saver_extension = False;
+#endif /* !HAVE_MIT_SAVER_EXTENSION */
+    }
+
+  if (p->use_xidle_extension)
+    {
+#ifdef HAVE_XIDLE_EXTENSION
       int first_event, first_error;
-      if (! XidleQueryExtension (dpy, &first_event, &first_error))
+      if (! XidleQueryExtension (si->dpy, &first_event, &first_error))
        {
          fprintf (stderr,
                   "%s: display %s does not support the XIdle extension.\n",
-                  progname, DisplayString (dpy));
-         use_xidle = 0;
+                  blurb(), DisplayString (si->dpy));
+         p->use_xidle_extension = False;
        }
-#else
+#else  /* !HAVE_XIDLE_EXTENSION */
       fprintf (stderr, "%s: not compiled with support for XIdle.\n",
-              progname);
-      use_xidle = 0;
-#endif
+              blurb());
+      p->use_xidle_extension = False;
+#endif /* !HAVE_XIDLE_EXTENSION */
     }
 
+  /* Call this only after having probed for presence of desired extension. */
+  initialize_screensaver_window (si);
+
   init_sigchld ();
 
-  disable_builtin_screensaver ();
+  disable_builtin_screensaver (si, True);
+
+  if (p->verbose_p && p->use_mit_saver_extension)
+    fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
+            blurb());
+  if (p->verbose_p && p->use_sgi_saver_extension)
+    fprintf (stderr, "%s: using SGI SCREEN_SAVER server extension.\n",
+            blurb());
+  if (p->verbose_p && p->use_xidle_extension)
+    fprintf (stderr, "%s: using XIdle server extension.\n",
+            blurb());
+
+  initialize_stderr (si);
+  XSetErrorHandler (saver_ehandler);
 
   if (initial_demo_mode_p)
     /* If the user wants demo mode, don't wait around before doing it. */
-    initial_delay = 0;
+    p->initial_delay = 0;
 
-#ifdef HAVE_XIDLE
-  if (! use_xidle)
-#endif
+  if (!p->use_xidle_extension &&
+      !p->use_mit_saver_extension &&
+      !p->use_sgi_saver_extension)
     {
-      if (initial_delay)
+      if (p->initial_delay)
        {
-         if (verbose_p)
+         if (p->verbose_p)
            {
-             printf ("%s: waiting for %d second%s...", progname,
-                     initial_delay, (initial_delay == 1 ? "" : "s"));
+             printf ("%s: waiting for %d second%s...", blurb(),
+                     (int) p->initial_delay,
+                     (p->initial_delay == 1 ? "" : "s"));
              fflush (stdout);
            }
-         sleep (initial_delay);
-         if (verbose_p)
+         sleep (p->initial_delay);
+         if (p->verbose_p)
            printf (" done.\n");
        }
-      if (verbose_p)
+      if (p->verbose_p)
        {
-         printf ("%s: selecting events on extant windows...", progname);
+         printf ("%s: selecting events on extant windows...", blurb());
          fflush (stdout);
        }
-      notice_events_timer ((XtPointer)
-                          RootWindowOfScreen (XtScreen (toplevel_shell)),
-                          0);
-      if (verbose_p)
+
+      /* Select events on the root windows of every screen.  This also selects
+        for window creation events, so that new subwindows will be noticed.
+       */
+      for (i = 0; i < si->nscreens; i++)
+       start_notice_events_timer (si,
+                                  RootWindowOfScreen (si->screens[i].screen));
+
+      if (p->verbose_p)
        printf (" done.\n");
     }
 }
 
-
-extern void suspend_screenhack P((Bool suspend_p));
-
 static void
-main_loop ()
+main_loop (saver_info *si)
 {
+  saver_preferences *p = &si->prefs;
   while (1)
     {
-      if (! demo_mode_p)
-       sleep_until_idle (True);
+      if (! si->demo_mode_p)
+       sleep_until_idle (si, True);
 
-      if (demo_mode_p)
-       demo_mode ();
+#ifndef NO_DEMO_MODE
+      if (si->demo_mode_p)
+       demo_mode (si);
       else
+#endif /* !NO_DEMO_MODE */
        {
-         if (verbose_p)
-           printf ("%s: user is idle; waking up at %s.\n", progname,
+         if (p->verbose_p)
+           printf ("%s: user is idle; waking up at %s.\n", blurb(),
                    timestring());
-         blank_screen ();
-         spawn_screenhack (True);
-         if (cycle)
-           cycle_id = XtAppAddTimeOut (app, cycle, (XtPointer)cycle_timer, 0);
+         blank_screen (si);
+         spawn_screenhack (si, True);
+         if (p->cycle)
+           si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
+                                           (XtPointer) si);
 
 #ifndef NO_LOCKING
-         if (lock_p && lock_timeout == 0)
-           locked_p = True;
-         if (lock_p && !locked_p)
+         if (p->lock_p && p->lock_timeout == 0)
+           si->locked_p = True;
+         if (p->lock_p && !si->locked_p)
            /* locked_p might be true already because of ClientMessage */
-           lock_id = XtAppAddTimeOut (app,lock_timeout,
-                                      (XtPointer)activate_lock_timer,0);
-#endif
+           si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
+                                          activate_lock_timer,
+                                          (XtPointer) si);
+#endif /* !NO_LOCKING */
 
        PASSWD_INVALID:
 
-         sleep_until_idle (False); /* until not idle */
+         sleep_until_idle (si, False); /* until not idle */
 
 #ifndef NO_LOCKING
-         if (locked_p)
+         if (si->locked_p)
            {
              Bool val;
-             if (locking_disabled_p) abort ();
-             dbox_up_p = True;
+             if (si->locking_disabled_p) abort ();
+             si->dbox_up_p = True;
 
              /* We used to ungrab the keyboard here, before calling unlock_p()
                 to pop up the dialog box.  This left the keyboard ungrabbed
@@ -715,43 +937,55 @@ main_loop ()
                 just after the server is grabbed, closing this window
                 entirely.
               */
-             /* ungrab_keyboard_and_mouse (); */
-
-             suspend_screenhack (True);
-             XUndefineCursor (dpy, screensaver_window);
-             if (verbose_p)
-               printf ("%s: prompting for password.\n", progname);
-             val = unlock_p (toplevel_shell);
-             if (verbose_p && val == False)
-               printf ("%s: password incorrect!\n", progname);
-             dbox_up_p = False;
-             XDefineCursor (dpy, screensaver_window, cursor);
-             suspend_screenhack (False);
-
-             /* I think this grab is now redundant, but it shouldn't hurt. */
-             grab_keyboard_and_mouse ();
+             /* ungrab_keyboard_and_mouse (si); */
+
+             {
+               saver_screen_info *ssi = si->default_screen;
+               suspend_screenhack (si, True);
+               XUndefineCursor (si->dpy, ssi->screensaver_window);
+               if (p->verbose_p)
+                 printf ("%s: prompting for password.\n", blurb());
+               val = unlock_p (si);
+               if (p->verbose_p && val == False)
+                 printf ("%s: password incorrect!\n", blurb());
+               si->dbox_up_p = False;
+               XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
+               suspend_screenhack (si, False);
+
+               /* I think this grab is now redundant, but it shouldn't hurt.
+                */
+               if (!si->demo_mode_p)
+                 grab_keyboard_and_mouse (si, ssi->screensaver_window,
+                                          ssi->cursor);
+             }
 
              if (! val)
                goto PASSWD_INVALID;
-             locked_p = False;
+             si->locked_p = False;
            }
-#endif
-         unblank_screen ();
-         kill_screenhack ();
-         if (cycle_id)
+#endif /* !NO_LOCKING */
+
+         /* Let's kill it before unblanking, to get it to stop drawing as
+            soon as possible... */
+         kill_screenhack (si);
+         unblank_screen (si);
+
+         if (si->cycle_id)
            {
-             XtRemoveTimeOut (cycle_id);
-             cycle_id = 0;
+             XtRemoveTimeOut (si->cycle_id);
+             si->cycle_id = 0;
            }
+
 #ifndef NO_LOCKING
-         if (lock_id)
+         if (si->lock_id)
            {
-             XtRemoveTimeOut (lock_id);
-             lock_id = 0;
+             XtRemoveTimeOut (si->lock_id);
+             si->lock_id = 0;
            }
-#endif
-         if (verbose_p)
-           printf ("%s: user is active; going to sleep at %s.\n", progname,
+#endif /* !NO_LOCKING */
+
+         if (p->verbose_p)
+           printf ("%s: user is active; going to sleep at %s.\n", blurb(),
                    timestring ());
        }
     }
@@ -760,79 +994,95 @@ main_loop ()
 \f
 
 Bool
-handle_clientmessage (event, until_idle_p)
-     XEvent *event;
-     Bool until_idle_p;
+handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
 {
-  Atom type;
+  saver_preferences *p = &si->prefs;
+  Atom type = 0;
   if (event->xclient.message_type != XA_SCREENSAVER)
     {
       char *str;
-      str = XGetAtomName (dpy, event->xclient.message_type);
-      fprintf (stderr, "%s: %sunrecognised ClientMessage type %s received\n",
-              progname, (verbose_p ? "## " : ""),
-              (str ? str : "(null)"));
+      str = XGetAtomName (si->dpy, event->xclient.message_type);
+      fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
+              blurb(), (str ? str : "(null)"));
       if (str) XFree (str);
+      return False;
     }
   if (event->xclient.format != 32)
     {
-      fprintf (stderr, "%s: %sClientMessage of format %d received, not 32\n",
-              progname, (verbose_p ? "## " : ""), event->xclient.format);
+      fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
+              blurb(), event->xclient.format);
+      return False;
     }
+
   type = event->xclient.data.l[0];
   if (type == XA_ACTIVATE)
     {
       if (until_idle_p)
        {
-         if (verbose_p)
-           printf ("%s: ACTIVATE ClientMessage received.\n", progname);
-         return True;
+         if (p->verbose_p)
+           printf ("%s: ACTIVATE ClientMessage received.\n", blurb());
+         if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
+           {
+             XForceScreenSaver (si->dpy, ScreenSaverActive);
+             return False;
+           }
+         else
+           {
+             return True;
+           }
        }
       fprintf (stderr,
-              "%s: %sClientMessage ACTIVATE received while already active.\n",
-              progname, (verbose_p ? "## " : ""));
+              "%s: ClientMessage ACTIVATE received while already active.\n",
+              blurb());
     }
   else if (type == XA_DEACTIVATE)
     {
       if (! until_idle_p)
        {
-         if (verbose_p)
-           printf ("%s: DEACTIVATE ClientMessage received.\n", progname);
-         return True;
+         if (p->verbose_p)
+           printf ("%s: DEACTIVATE ClientMessage received.\n", blurb());
+         if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
+           {
+             XForceScreenSaver (si->dpy, ScreenSaverReset);
+             return False;
+           }
+         else
+           {
+             return True;
+           }
        }
       fprintf (stderr,
-              "%s: %sClientMessage DEACTIVATE received while inactive.\n",
-              progname, (verbose_p ? "## " : ""));
+              "%s: ClientMessage DEACTIVATE received while inactive.\n",
+              blurb());
     }
   else if (type == XA_CYCLE)
     {
       if (! until_idle_p)
        {
-         if (verbose_p)
-           printf ("%s: CYCLE ClientMessage received.\n", progname);
-         if (cycle_id)
-           XtRemoveTimeOut (cycle_id);
-         cycle_id = 0;
-         cycle_timer (0, 0);
+         if (p->verbose_p)
+           printf ("%s: CYCLE ClientMessage received.\n", blurb());
+         if (si->cycle_id)
+           XtRemoveTimeOut (si->cycle_id);
+         si->cycle_id = 0;
+         cycle_timer ((XtPointer) si, 0);
          return False;
        }
-      fprintf (stderr,
-              "%s: %sClientMessage CYCLE received while inactive.\n",
-              progname, (verbose_p ? "## " : ""));
+      fprintf (stderr, "%s: ClientMessage CYCLE received while inactive.\n",
+              blurb());
     }
   else if (type == XA_NEXT || type == XA_PREV)
     {
-      if (verbose_p)
-       printf ("%s: %s ClientMessage received.\n", progname,
+      if (p->verbose_p)
+       printf ("%s: %s ClientMessage received.\n", blurb(),
                (type == XA_NEXT ? "NEXT" : "PREV"));
-      next_mode_p = 1 + (type == XA_PREV);
+      si->next_mode_p = 1 + (type == XA_PREV);
 
       if (! until_idle_p)
        {
-         if (cycle_id)
-           XtRemoveTimeOut (cycle_id);
-         cycle_id = 0;
-         cycle_timer (0, 0);
+         if (si->cycle_id)
+           XtRemoveTimeOut (si->cycle_id);
+         si->cycle_id = 0;
+         cycle_timer ((XtPointer) si, 0);
        }
       else
        return True;
@@ -840,107 +1090,121 @@ handle_clientmessage (event, until_idle_p)
   else if (type == XA_EXIT)
     {
       /* Ignore EXIT message if the screen is locked. */
-      if (until_idle_p || !locked_p)
+      if (until_idle_p || !si->locked_p)
        {
-         if (verbose_p)
-           printf ("%s: EXIT ClientMessage received.\n", progname);
+         if (p->verbose_p)
+           printf ("%s: EXIT ClientMessage received.\n", blurb());
          if (! until_idle_p)
            {
-             unblank_screen ();
-             kill_screenhack ();
-             XSync (dpy, False);
+             unblank_screen (si);
+             kill_screenhack (si);
+             XSync (si->dpy, False);
            }
-         exit (0);
+         saver_exit (si, 0);
        }
       else
-       fprintf (stderr, "%s: %sEXIT ClientMessage received while locked.\n",
-                progname, (verbose_p ? "## " : ""));
+       fprintf (stderr, "%s: EXIT ClientMessage received while locked.\n",
+                blurb());
     }
   else if (type == XA_RESTART)
     {
       /* The RESTART message works whether the screensaver is active or not,
         unless the screen is locked, in which case it doesn't work.
        */
-      if (until_idle_p || !locked_p)
+      if (until_idle_p || !si->locked_p)
        {
-         if (verbose_p)
-           printf ("%s: RESTART ClientMessage received.\n", progname);
+         if (p->verbose_p)
+           printf ("%s: RESTART ClientMessage received.\n", blurb());
          if (! until_idle_p)
            {
-             unblank_screen ();
-             kill_screenhack ();
-             XSync (dpy, False);
+             unblank_screen (si);
+             kill_screenhack (si);
+             XSync (si->dpy, False);
            }
-         restart_process ();
+
+         /* make sure error message shows up before exit. */
+         if (real_stderr && stderr != real_stderr)
+           dup2 (fileno(real_stderr), fileno(stderr));
+
+         restart_process (si);
+         exit (1);     /* shouldn't get here; but if restarting didn't work,
+                          make this command be the same as EXIT. */
        }
       else
-       fprintf(stderr, "%s: %sRESTART ClientMessage received while locked.\n",
-               progname, (verbose_p ? "## " : ""));
+       fprintf(stderr, "%s: RESTART ClientMessage received while locked.\n",
+               blurb());
     }
   else if (type == XA_DEMO)
     {
 #ifdef NO_DEMO_MODE
-      fprintf (stderr,
-              "%s: %snot compiled with support for DEMO mode\n",
-              progname, (verbose_p ? "## " : ""));
+      fprintf (stderr, "%s: not compiled with support for DEMO mode\n",
+              blurb());
 #else
       if (until_idle_p)
        {
-         if (verbose_p)
-           printf ("%s: DEMO ClientMessage received.\n", progname);
-         demo_mode_p = True;
+         if (p->verbose_p)
+           printf ("%s: DEMO ClientMessage received.\n", blurb());
+         si->demo_mode_p = True;
          return True;
        }
       fprintf (stderr,
-              "%s: %sDEMO ClientMessage received while active.\n",
-              progname, (verbose_p ? "## " : ""));
+              "%s: DEMO ClientMessage received while active.\n", blurb());
 #endif
     }
   else if (type == XA_LOCK)
     {
 #ifdef NO_LOCKING
-      fprintf (stderr, "%s: %snot compiled with support for LOCK mode\n",
-              progname, (verbose_p ? "## " : ""));
+      fprintf (stderr, "%s: not compiled with support for LOCK mode\n",
+              blurb());
 #else
-      if (locking_disabled_p)
+      if (si->locking_disabled_p)
        fprintf (stderr,
-              "%s: %sLOCK ClientMessage received, but locking is disabled.\n",
-                progname, (verbose_p ? "## " : ""));
-      else if (locked_p)
+              "%s: LOCK ClientMessage received, but locking is disabled.\n",
+                blurb());
+      else if (si->locked_p)
        fprintf (stderr,
-              "%s: %sLOCK ClientMessage received while already locked.\n",
-                progname, (verbose_p ? "## " : ""));
+              "%s: LOCK ClientMessage received while already locked.\n",
+                blurb());
       else
        {
-         locked_p = True;
-         if (verbose_p) 
+         si->locked_p = True;
+         if (p->verbose_p) 
            printf ("%s: LOCK ClientMessage received;%s locking.\n",
-                   progname, until_idle_p ? " activating and" : "");
+                   blurb(), until_idle_p ? " activating and" : "");
 
-         if (lock_id)  /* we're doing it now, so lose the timeout */
+         if (si->lock_id)      /* we're doing it now, so lose the timeout */
            {
-             XtRemoveTimeOut (lock_id);
-             lock_id = 0;
+             XtRemoveTimeOut (si->lock_id);
+             si->lock_id = 0;
            }
 
          if (until_idle_p)
-           return True;
+           {
+             if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
+               {
+                 XForceScreenSaver (si->dpy, ScreenSaverActive);
+                 return False;
+               }
+             else
+               {
+                 return True;
+               }
+           }
        }
 #endif
     }
   else
     {
       char *str;
-      str = XGetAtomName (dpy, type);
+      str = (type ? XGetAtomName(si->dpy, type) : 0);
       if (str)
        fprintf (stderr,
-                "%s: %sunrecognised screensaver ClientMessage %s received\n",
-                progname, (verbose_p ? "## " : ""), str);
+                "%s: unrecognised screensaver ClientMessage %s received\n",
+                blurb(), str);
       else
        fprintf (stderr,
-                "%s: %sunrecognised screensaver ClientMessage 0x%x received\n",
-                progname, (verbose_p ? "## " : ""),
-                event->xclient.data.l[0]);
+               "%s: unrecognised screensaver ClientMessage 0x%x received\n",
+                blurb(), (unsigned int) event->xclient.data.l[0]);
       if (str) XFree (str);
     }
   return False;