http://ftp.x.org/contrib/applications/xscreensaver-2.24.tar.gz
[xscreensaver] / driver / xscreensaver.c
index 451f953d3c15e8721eacf5de66468e1363398121..accfbdf596f7c9b0053a520a405ed3ace8495322 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-1995 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,8 +9,6 @@
  * implied warranty.
  */
 
-#include "version.h"
-
 /*   ========================================================================
  *   First we wait until the keyboard and mouse become idle for the specified
  *   amount of time.  We do this in one of three different ways: periodically
  *   and a few other things.  The included "xscreensaver_command" program
  *   sends these messsages.
  *
- *   If we don't have the XIdle or MIT-SCREENSAVER 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.
+ *   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
  *   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
+ *   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
+ *   times, we could just stat() those.  But the incremental benefit of
  *   implementing this is really small, so forget I said anything.
  *
  *   Debugging hints:
  *     - 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>
-#include <X11/Xmu/Error.h>
+#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 /* HAVE_XIDLE_EXTENSION */
 
-#ifdef HAVE_SAVER_EXTENSION
-#include <X11/extensions/scrnsaver.h>
-#endif /* HAVE_SAVER_EXTENSION */
-
 #include "xscreensaver.h"
+#include "version.h"
+#include "yarandom.h"
+#include "resources.h"
+#include "visual.h"
 
-#if defined(SVR4) || defined(SYSV)
-# 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, XtIntervalId *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));
-
-extern void initialize_stderr P((void));
-
-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_extension;
-Bool use_saver_extension;
-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;
-
-#ifdef HAVE_SAVER_EXTENSION
-int saver_ext_event_number = 0;
-int saver_ext_error_number = 0;
-#endif /* HAVE_SAVER_EXTENSION */
-
-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 },
   { "-cycle",             ".cycle",            XrmoptionSepArg, 0 },
-  { "-idelay",            ".initialDelay",     XrmoptionSepArg, 0 },
-  { "-visual",            ".visualID",         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" },
-  { "-ss-extension",      ".saverExtension",   XrmoptionNoArg, "on" },
-  { "-no-ss-extension",           ".saverExtension",   XrmoptionNoArg, "off" },
-  { "-lock",              ".lock",             XrmoptionNoArg, "on" },
-  { "-no-lock",                   ".lock",             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[] = {
-#include "XScreenSaver.ad.h"
+#include "XScreenSaver_ad.h"
  0
 };
 
 static void
-do_help P((void))
+do_help (saver_info *si)
 {
   printf ("\
-xscreensaver %s, copyright (c) 1991-1995 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\
+    -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\
-    -demo                      Enter interactive demo mode on startup.\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\
-    -saver-extension           Use the R6 MIT-SCREEN-SAVER server extension.\n\
-    -no-saver-extension        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\
 \n\
-Use the `xscreensaver-command' program to control a running screensaver.\n\
+The `xscreensaver' program should be left running in the background.\n\
+Use the `xscreensaver-command' program to manipulate a running xscreensaver.\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);
+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\
+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");
@@ -310,67 +228,109 @@ more details.\n\n",
 #ifdef NO_DEMO_MODE
   printf ("Support for demo mode was not enabled at compile-time.\n");
 #endif
-#if !defined(HAVE_XIDLE_EXTENSION) && !defined(HAVE_SAVER_EXTENSION)
-  printf ("Support for the XIDLE and MIT-SCREEN-SAVER server extensions\
was not\n\enabled at compile-time.\n");
-#endif /* !HAVE_XIDLE_EXTENSION && !HAVE_SAVER_EXTENSION */
+#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;
@@ -387,460 +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_extension unless it is explicitly specified */
-  if (get_string_resource ("xidleExtension", "Boolean"))
-    use_xidle_extension = get_boolean_resource ("xidleExtension", "Boolean");
-  else
-#ifdef HAVE_XIDLE_EXTENSION    /* pick a default */
-    use_xidle_extension = True;
-#else  /* !HAVE_XIDLE_EXTENSION */
-    use_xidle_extension = False;
-#endif /* !HAVE_XIDLE_EXTENSION */
-
-  /* don't set use_saver_extension unless it is explicitly specified */
-  if (get_string_resource ("saverExtension", "Boolean"))
-    use_xidle_extension = get_boolean_resource ("saverExtension", "Boolean");
-  else
-#ifdef HAVE_SAVER_EXTENSION    /* pick a default */
-    use_saver_extension = True;
-#else  /* !HAVE_SAVER_EXTENSION */
-    use_saver_extension = False;
-#endif /* !HAVE_SAVER_EXTENSION */
+  get_screenhacks (si);
 
+  if (p->debug_p)
+    {
+      XSynchronize(si->dpy, True);
+      p->verbose_p = True;
+      p->timestamp_p = True;
+      p->initial_delay = 0;
+    }
 
-  get_screenhacks ();
+  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 */
-
-
-#ifndef NO_LOCKING
-extern Bool unlock_p P((Widget));
-extern Bool lock_init P((void));
-#endif
-
-static void initialize P((int argc, char **argv));
-static void main_loop P((void));
+static void initialize (saver_info *si, int argc, char **argv);
+static void main_loop (saver_info *si);
 
-void
-main (argc, argv)
-     int argc;
-     char **argv;
+int
+main (int argc, char **argv)
 {
-  initialize (argc, argv);
-  main_loop ();
+  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 int
-saver_ehandler (dpy, error)
-     Display *dpy;
-     XErrorEvent *error;
+int
+saver_ehandler (Display *dpy, XErrorEvent *error)
 {
-  fprintf (real_stderr, "\nX error in %s:\n", progname);
+  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))
-    exit (-1);
+    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);
-}
-
-#ifdef HAVE_SAVER_EXTENSION
-
-static int
-ignore_all_errors_ehandler (dpy, error)
-     Display *dpy;
-     XErrorEvent *error;
-{
-  return 0;
-}
-
-static void
-init_saver_extension ()
-{
-  XID kill_id;
-  Atom kill_type;
-  Window root = RootWindowOfScreen (screen);
-  XScreenSaverInfo *info;
-  Pixmap blank_pix = XCreatePixmap (dpy, root, 1, 1, 1);
-
-  /* Kill off the old MIT-SCREEN-SAVER client if there is one.
-     This tends to generate X errors, though (possibly due to a bug
-     in the server extension itself?) so just ignore errors here. */
-  if (XScreenSaverGetRegistered (dpy, XScreenNumberOfScreen (screen),
-                                &kill_id, &kill_type)
-      && kill_id != blank_pix)
+  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++)
     {
-      int (*old_handler) ();
-      old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
-      XKillClient (dpy, kill_id);
-      XSync (dpy, False);
-      XSetErrorHandler (old_handler);
-    }
+      saver_screen_info *ssi = &si->screens[i];
+      ssi->global = si;
+      ssi->screen = ScreenOfDisplay (si->dpy, i);
 
-  XScreenSaverSelectInput (dpy, root, ScreenSaverNotifyMask);
+      /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
+      ssi->default_visual =
+       get_visual_resource (ssi->screen, "visualID", "VisualID", False);
 
-  XScreenSaverRegister (dpy, XScreenNumberOfScreen (screen),
-                       (XID) blank_pix, XA_PIXMAP);
-  info = XScreenSaverAllocInfo ();
+      ssi->current_visual = ssi->default_visual;
+      ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
 
-#if 0
-  /* #### I think this is noticing that the saver is on, and replacing it
-     without turning it off first. */
-  saver = info->window;
-  if (info->state == ScreenSaverOn)
-    {
-      if (info->kind != ScreenSaverExternal) 
-       {
-         XResetScreenSaver (display);
-         XActivateScreenSaver (display);
-       }
-      StartSaver ();
+      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);
     }
-#endif
 }
-#endif /* HAVE_SAVER_EXTENSION */
-
 
-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-1995 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_saver_extension)
+  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_SAVER_EXTENSION
-      if (! XScreenSaverQueryExtension (dpy,
-                                       &saver_ext_event_number,
-                                       &saver_ext_error_number))
+#ifdef HAVE_MIT_SAVER_EXTENSION
+      if (! query_mit_saver_extension (si))
        {
          fprintf (stderr,
-        "%s: %sdisplay %s does not support the MIT-SCREEN-SAVER extension.\n",
-                  progname, (verbose_p ? "## " : ""), DisplayString (dpy));
-         use_saver_extension = False;
+        "%s: display %s does not support the MIT-SCREEN-SAVER extension.\n",
+                  blurb(), DisplayString (si->dpy));
+         p->use_mit_saver_extension = False;
        }
-      else if (use_xidle_extension)
+      else if (p->use_xidle_extension)
        {
          fprintf (stderr,
-        "%s: %sMIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
-                  progname, (verbose_p ? "## " : ""));
-         use_xidle_extension = False;
+        "%s: MIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
+                  blurb());
+         p->use_xidle_extension = False;
        }
-#else  /* !HAVE_SAVER_EXTENSION */
+#else  /* !HAVE_MIT_SAVER_EXTENSION */
       fprintf (stderr,
-       "%s: %snot compiled with support for the MIT-SCREEN-SAVER extension.\n",
-              progname, (verbose_p ? "## " : ""));
-      use_saver_extension = False;
-#endif /* !HAVE_SAVER_EXTENSION */
+       "%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 (use_xidle_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: %sdisplay %s does not support the XIdle extension.\n",
-                  progname, (verbose_p ? "## " : ""), DisplayString (dpy));
-         use_xidle_extension = False;
+                  "%s: display %s does not support the XIdle extension.\n",
+                  blurb(), DisplayString (si->dpy));
+         p->use_xidle_extension = False;
        }
 #else  /* !HAVE_XIDLE_EXTENSION */
-      fprintf (stderr, "%s: %snot compiled with support for XIdle.\n",
-              progname, (verbose_p ? "## " : ""));
-      use_xidle_extension = False;
+      fprintf (stderr, "%s: not compiled with support for XIdle.\n",
+              blurb());
+      p->use_xidle_extension = False;
 #endif /* !HAVE_XIDLE_EXTENSION */
     }
 
-  init_sigchld ();
+  /* Call this only after having probed for presence of desired extension. */
+  initialize_screensaver_window (si);
 
-  disable_builtin_screensaver ();
+  init_sigchld ();
 
-#ifdef HAVE_SAVER_EXTENSION
-  if (use_saver_extension)
-    init_saver_extension ();
-#endif /* HAVE_SAVER_EXTENSION */
+  disable_builtin_screensaver (si, True);
 
-  if (verbose_p && use_saver_extension)
+  if (p->verbose_p && p->use_mit_saver_extension)
     fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
-            progname);
-  if (verbose_p && use_xidle_extension)
+            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",
-            progname);
+            blurb());
 
-  initialize_stderr ();
+  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;
 
-  if (!use_xidle_extension && !use_saver_extension)
+  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,
-                     (int) 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
@@ -853,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 ());
        }
     }
@@ -898,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;
@@ -978,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 ? "## " : ""),
-                (unsigned int) 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;