http://ftp.nluug.nl/pub/os/Linux/distr/pardusrepo/sources/xscreensaver-5.02.tar.gz
[xscreensaver] / driver / xscreensaver.c
index dda587a12f81c2da057bdff4c884b2760224a66e..3f0c7409edb20e9a5a062f2731cccb8819282e0d 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-1999 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2006 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  *   on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension
  *   to send us a "you are idle" event.
  *
  *   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 (or, in the case of the 
- *   MIT-SCREEN-SAVER extension, use the one it gave us.)
+ *   Then, we map a full screen black window.
  *
  *   We place a __SWM_VROOT property on this window, so that newly-started
  *
  *   We place a __SWM_VROOT property on this window, so that newly-started
- *   clients will think that this window is a "virtual root" window.
+ *   clients will think that this window is a "virtual root" window (as per
+ *   the logic in the historical "vroot.h" header.)
  *
  *   If there is an existing "virtual root" window (one that already had
  *   an __SWM_VROOT property) then we remove that property from that window.
  *
  *   If there is an existing "virtual root" window (one that already had
  *   an __SWM_VROOT property) then we remove that property from that window.
  *   When they are, we kill the inferior process, unmap the window, and restore
  *   the __SWM_VROOT property to the real virtual root window if there was one.
  *
  *   When they are, we kill the inferior process, unmap the window, and restore
  *   the __SWM_VROOT property to the real virtual root window if there was one.
  *
- *   While we are waiting, we also set up timers so that, after a certain 
- *   amount of time has passed, we can start a different screenhack.  We do
- *   this by killing the running child process with SIGTERM, and then starting
- *   a new one in the same way.
+ *   On multi-screen systems, we do the above on each screen, and start
+ *   multiple programs, each with a different value of $DISPLAY.
+ *
+ *   On Xinerama systems, we do a similar thing, but instead create multiple
+ *   windows on the (only) display, and tell the subprocess which one to use
+ *   via the $XSCREENSAVER_WINDOW environment variable -- this trick requires
+ *   a recent (Aug 2003) revision of vroot.h.
+ *
+ *   While we are waiting for user activity, we also set up timers so that,
+ *   after a certain amount of time has passed, we can start a different
+ *   screenhack.  We do this by killing the running child process with
+ *   SIGTERM, and then starting a new one in the same way.
  *
  *   If there was a real virtual root, meaning that we removed the __SWM_VROOT
  *   property from it, meaning we must (absolutely must) restore it before we
  *
  *   If there was a real virtual root, meaning that we removed the __SWM_VROOT
  *   property from it, meaning we must (absolutely must) restore it before we
@@ -57,9 +65,9 @@
  *   can really fuck up the world by killing this process with "kill -9".
  *
  *   This program accepts ClientMessages of type SCREENSAVER; these messages
  *   can really fuck up the world by killing this process with "kill -9".
  *
  *   This program accepts ClientMessages of type SCREENSAVER; these messages
- *   may contain the atom ACTIVATE or DEACTIVATE, meaning to turn the 
+ *   may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the 
  *   screensaver on or off now, regardless of the idleness of the user,
  *   screensaver on or off now, regardless of the idleness of the user,
- *   and a few other things.  The included "xscreensaver_command" program
+ *   and a few other things.  The included "xscreensaver-command" program
  *   sends these messsages.
  *
  *   If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
  *   sends these messsages.
  *
  *   If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
@@ -70,8 +78,9 @@
  *   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
  *   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.
- *   
+ *   30 seconds after creating the window, but such programs do not seem to
+ *   occur in nature (I've never seen it happen in all these years.)
+ *
  *   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
  *   lowest (leafmost) window in the hierarchy on which *any* client selects
  *   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
  *   lowest (leafmost) window in the hierarchy on which *any* client selects
  *   "client changing event mask" problem that the KeyPress events hack does.
  *   I think polling is more reliable.
  *
  *   "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 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.
+ *   On systems with /proc/interrupts (Linux) we poll that file and note when
+ *   the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
+ *   (There is no reliable way, using /proc/interrupts, to detect non-PS/2
+ *   mice, so it doesn't help for serial or USB mice.)
+ *
+ *   None of this crap happens if we're using one of the extensions.  Sadly,
+ *   the XIdle extension hasn't been available for many years; the SGI
+ *   extension only exists on SGIs; and the MIT extension, while widely
+ *   deployed, is garbage in several ways.
  *
  *   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
  *
  *   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
  *       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
  *       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.
+ *       the other side of a call to fork() -- if your subprocesses are
+ *       dying with signal 5, Trace/BPT Trap, you're losing in this way.
  *     - 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 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."
- *       
+ *
  * ======================================================================== */
 
 #ifdef HAVE_CONFIG_H
  * ======================================================================== */
 
 #ifdef HAVE_CONFIG_H
 #include <stdio.h>
 #include <ctype.h>
 #include <X11/Xlib.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <X11/Xlib.h>
+
+#include <X11/Xlibint.h>
+
 #include <X11/Xatom.h>
 #include <X11/Intrinsic.h>
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
 #include <X11/Xos.h>
 #include <X11/Xatom.h>
 #include <X11/Intrinsic.h>
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
 #include <X11/Xos.h>
+#include <time.h>
+#include <sys/time.h>
 #include <netdb.h>     /* for gethostbyname() */
 #include <netdb.h>     /* for gethostbyname() */
+#include <sys/types.h>
+#include <pwd.h>
 #ifdef HAVE_XMU
 # ifndef VMS
 #  include <X11/Xmu/Error.h>
 #ifdef HAVE_XMU
 # ifndef VMS
 #  include <X11/Xmu/Error.h>
 # include <X11/extensions/xidle.h>
 #endif /* HAVE_XIDLE_EXTENSION */
 
 # include <X11/extensions/xidle.h>
 #endif /* HAVE_XIDLE_EXTENSION */
 
+#ifdef HAVE_XINERAMA
+# include <X11/extensions/Xinerama.h>
+#endif /* HAVE_XINERAMA */
+
 #include "xscreensaver.h"
 #include "version.h"
 #include "yarandom.h"
 #include "resources.h"
 #include "visual.h"
 #include "usleep.h"
 #include "xscreensaver.h"
 #include "version.h"
 #include "yarandom.h"
 #include "resources.h"
 #include "visual.h"
 #include "usleep.h"
+#include "auth.h"
 
 saver_info *global_si_kludge = 0;      /* I hate C so much... */
 
 
 saver_info *global_si_kludge = 0;      /* I hate C so much... */
 
@@ -162,46 +189,30 @@ XrmDatabase db = 0;
 
 static Atom XA_SCREENSAVER_RESPONSE;
 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
 
 static Atom XA_SCREENSAVER_RESPONSE;
 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
-static Atom XA_EXIT, XA_RESTART, XA_LOCK, XA_SELECT;
-Atom XA_DEMO, XA_PREFS;
+static Atom XA_RESTART, XA_SELECT;
+static Atom XA_THROTTLE, XA_UNTHROTTLE;
+Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
 
 \f
 static XrmOptionDescRec options [] = {
 
 \f
 static XrmOptionDescRec options [] = {
-  { "-timeout",                   ".timeout",          XrmoptionSepArg, 0 },
-  { "-cycle",             ".cycle",            XrmoptionSepArg, 0 },
-  { "-lock-mode",         ".lock",             XrmoptionNoArg, "on" },
-  { "-no-lock-mode",      ".lock",             XrmoptionNoArg, "off" },
-  { "-no-lock",                   ".lock",             XrmoptionNoArg, "off" },
-  { "-lock-timeout",      ".lockTimeout",      XrmoptionSepArg, 0 },
-  { "-lock-vts",          ".lockVTs",          XrmoptionNoArg, "on" },
-  { "-no-lock-vts",       ".lockVTs",          XrmoptionNoArg, "off" },
-  { "-visual",            ".visualID",         XrmoptionSepArg, 0 },
-  { "-install",                   ".installColormap",  XrmoptionNoArg, "on" },
-  { "-no-install",        ".installColormap",  XrmoptionNoArg, "off" },
+
   { "-verbose",                   ".verbose",          XrmoptionNoArg, "on" },
   { "-silent",            ".verbose",          XrmoptionNoArg, "off" },
   { "-verbose",                   ".verbose",          XrmoptionNoArg, "on" },
   { "-silent",            ".verbose",          XrmoptionNoArg, "off" },
-  { "-timestamp",         ".timestamp",        XrmoptionNoArg, "on" },
-  { "-capture-stderr",    ".captureStderr",    XrmoptionNoArg, "on" },
-  { "-no-capture-stderr",  ".captureStderr",   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" },
-  { "-proc-interrupts",           ".procInterrupts",   XrmoptionNoArg, "on" },
-  { "-no-proc-interrupts", ".procInterrupts",  XrmoptionNoArg, "off" },
-  { "-splash",            ".splash",           XrmoptionNoArg, "on" },
-  { "-no-splash",         ".splash",           XrmoptionNoArg, "off" },
+
+  /* xscreensaver-demo uses this one */
   { "-nosplash",          ".splash",           XrmoptionNoArg, "off" },
   { "-nosplash",          ".splash",           XrmoptionNoArg, "off" },
-  { "-idelay",            ".initialDelay",     XrmoptionSepArg, 0 },
-  { "-nice",              ".nice",             XrmoptionSepArg, 0 },
+  { "-no-splash",         ".splash",           XrmoptionNoArg, "off" },
 
 
-  /* Actually these are built in to Xt, but just to be sure... */
-  { "-synchronous",       ".synchronous",      XrmoptionNoArg, "on" },
-  { "-xrm",               NULL,                XrmoptionResArg, NULL }
+  /* useful for debugging */
+  { "-no-capture-stderr",  ".captureStderr",   XrmoptionNoArg, "off" },
 };
 
 };
 
+#ifdef __GNUC__
+ __extension__     /* shut up about "string length is greater than the length
+                      ISO C89 compilers are required to support" when including
+                      the .ad file... */
+#endif
+
 static char *defaults[] = {
 #include "XScreenSaver_ad.h"
  0
 static char *defaults[] = {
 #include "XScreenSaver_ad.h"
  0
@@ -217,36 +228,25 @@ do_help (saver_info *si)
   fflush (stdout);
   fflush (stderr);
   fprintf (stdout, "\
   fflush (stdout);
   fflush (stderr);
   fprintf (stdout, "\
-xscreensaver %s, copyright (c) 1991-1999 by Jamie Zawinski <jwz@jwz.org>\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 before switching.\n\
-    -lock-mode               Require a password before deactivating.\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\
-    -verbose                 Be loud.\n\
-    -no-splash               Don't display a splash-screen at startup.\n\
-    -help                    This message.\n\
-\n\
-See the manual for other options and X resources.\n\
-\n\
-The `xscreensaver' program should be left running in the background.\n\
-Use the `xscreensaver-demo' and `xscreensaver-command' programs to\n\
-manipulate a running xscreensaver.\n\
+xscreensaver %s, copyright (c) 1991-2006 by Jamie Zawinski <jwz@jwz.org>\n\
 \n\
 \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\
-Just getting started?  Try this:\n\
+  All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
+  Rather than editing that file by hand, just run `xscreensaver-demo':\n\
+  that program lets you configure the screen saver graphically,\n\
+  including timeouts, locking, and display modes.\n\
+\n",
+         si->version);
+  fprintf (stdout, "\
+  Just getting started?  Try this:\n\
 \n\
         xscreensaver &\n\
         xscreensaver-demo\n\
 \n\
 \n\
         xscreensaver &\n\
         xscreensaver-demo\n\
 \n\
-For updates, check http://www.jwz.org/xscreensaver/\n\
-\n",
-         si->version);
+  For updates, online manual, and FAQ, please see the web page:\n\
+\n\
+       http://www.jwz.org/xscreensaver/\n\
+\n");
+
   fflush (stdout);
   fflush (stderr);
   exit (1);
   fflush (stdout);
   fflush (stderr);
   exit (1);
@@ -290,19 +290,44 @@ int
 saver_ehandler (Display *dpy, XErrorEvent *error)
 {
   saver_info *si = global_si_kludge;   /* I hate C so much... */
 saver_ehandler (Display *dpy, XErrorEvent *error)
 {
   saver_info *si = global_si_kludge;   /* I hate C so much... */
+  int i;
+  Bool fatal_p;
 
   if (!real_stderr) real_stderr = stderr;
 
   fprintf (real_stderr, "\n"
           "#######################################"
           "#######################################\n\n"
 
   if (!real_stderr) real_stderr = stderr;
 
   fprintf (real_stderr, "\n"
           "#######################################"
           "#######################################\n\n"
-          "%s: X Error!  PLEASE REPORT THIS BUG.\n\n"
-          "#######################################"
-          "#######################################\n\n",
+          "%s: X Error!  PLEASE REPORT THIS BUG.\n",
           blurb());
           blurb());
-  if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
+
+  for (i = 0; i < si->nscreens; i++)
+    {
+      saver_screen_info *ssi = &si->screens[i];
+      fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
+               blurb(), ssi->real_screen_number, ssi->number,
+               (unsigned int) RootWindowOfScreen (si->screens[i].screen),
+               (unsigned int) si->screens[i].real_vroot,
+               (unsigned int) si->screens[i].screensaver_window);
+    }
+
+  fprintf (real_stderr, "\n"
+          "#######################################"
+          "#######################################\n\n");
+
+  fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
+
+  fatal_p = True;  /* The only time I've ever seen a supposedly nonfatal error,
+                      it has been BadImplementation / Xlib sequence lost, which
+                      are in truth pretty damned fatal.
+                    */
+
+  fprintf (real_stderr, "\n");
+
+  if (! fatal_p)
+    fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
+  else
     {
     {
-      fprintf (real_stderr, "\n");
       if (si->prefs.xsync_p)
        {
          saver_exit (si, -1, "because of synchronous X Error");
       if (si->prefs.xsync_p)
        {
          saver_exit (si, -1, "because of synchronous X Error");
@@ -313,15 +338,18 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
                    "#######################################"
                    "#######################################\n\n");
           fprintf (real_stderr,
                    "#######################################"
                    "#######################################\n\n");
           fprintf (real_stderr,
-   "    If at all possible, please re-run xscreensaver with the command line\n"
-   "    arguments `-sync -verbose', and reproduce this bug.  That will cause\n"
-   "    xscreensaver to dump a `core' file to the current directory.  Please\n"
-   "    include the stack trace from that core file in your bug report.\n"
+   "    If at all possible, please re-run xscreensaver with the command\n"
+   "    line arguments `-sync -verbose -no-capture', and reproduce this\n"
+   "    bug.  That will cause xscreensaver to dump a `core' file to the\n"
+   "    current directory.  Please include the stack trace from that core\n"
+   "    file in your bug report.  *DO NOT* mail the core file itself!\n"
+   "    That won't work.\n");
+          fprintf (real_stderr,
    "\n"
    "\n"
-   "    http://www.jwz.org/xscreensaver/bugs.html explains how to create the\n"
-   "    most useful bug reports, and how to examine core files.\n"
+   "    http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
+   "    the most useful bug reports, and how to examine core files.\n"
    "\n"
    "\n"
-   "    The more information you can provide, the better.  But please report\n"
+   "    The more information you can provide, the better.  But please\n"
    "    report this bug, regardless!\n"
    "\n");
           fprintf (real_stderr,
    "    report this bug, regardless!\n"
    "\n");
           fprintf (real_stderr,
@@ -331,8 +359,7 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
          saver_exit (si, -1, 0);
        }
     }
          saver_exit (si, -1, 0);
        }
     }
-  else
-    fprintf (real_stderr, " (nonfatal.)\n");
+
   return 0;
 }
 
   return 0;
 }
 
@@ -366,14 +393,32 @@ startup_ehandler (String name, String type, String class,
   fprintf (stderr, "\n");
 
   describe_uids (si, stderr);
   fprintf (stderr, "\n");
 
   describe_uids (si, stderr);
-  fprintf (stderr, "\n"
-           "%s: Errors at startup are usually authorization problems.\n"
-           "              Did you read the manual?  Specifically, the parts\n"
-           "              that talk about XAUTH, XDM, and root logins?\n"
-           "\n"
-           "              http://www.jwz.org/xscreensaver/man.html\n"
-           "\n",
+
+  if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
+    {
+      fprintf (stderr, "\n"
+          "%s: This is probably because you're logging in as root.  You\n"
+"              shouldn't log in as root: you should log in as a normal user,\n"
+"              and then `su' as needed.  If you insist on logging in as\n"
+"              root, you will have to turn off X's security features before\n"
+"              xscreensaver will work.\n"
+               "\n"
+"              Please read the manual and FAQ for more information:\n",
+               blurb());
+    }
+  else
+    {
+      fprintf (stderr, "\n"
+          "%s: Errors at startup are usually authorization problems.\n"
+"              But you're not logging in as root (good!) so something\n"
+"              else must be wrong.  Did you read the manual and the FAQ?\n",
            blurb());
            blurb());
+    }
+
+  fprintf (stderr, "\n"
+          "              http://www.jwz.org/xscreensaver/faq.html\n"
+          "              http://www.jwz.org/xscreensaver/man.html\n"
+          "\n");
 
   fflush (stderr);
   fflush (stdout);
 
   fflush (stderr);
   fflush (stdout);
@@ -419,22 +464,73 @@ set_version_string (saver_info *si, int *argc, char **argv)
 static void
 privileged_initialization (saver_info *si, int *argc, char **argv)
 {
 static void
 privileged_initialization (saver_info *si, int *argc, char **argv)
 {
+#ifndef NO_LOCKING
+  /* before hack_uid() for proper permissions */
+  lock_priv_init (*argc, argv, si->prefs.verbose_p);
+#endif /* NO_LOCKING */
+
+  hack_uid (si);
+}
+
+
+/* Figure out what locking mechanisms are supported.
+ */
+static void
+lock_initialization (saver_info *si, int *argc, char **argv)
+{
 #ifdef NO_LOCKING
   si->locking_disabled_p = True;
   si->nolock_reason = "not compiled with locking support";
 #else /* !NO_LOCKING */
 #ifdef NO_LOCKING
   si->locking_disabled_p = True;
   si->nolock_reason = "not compiled with locking support";
 #else /* !NO_LOCKING */
-  si->locking_disabled_p = False;
-  /* before hack_uid() for proper permissions */
+
+  /* Finish initializing locking, now that we're out of privileged code. */
   if (! lock_init (*argc, argv, si->prefs.verbose_p))
     {
       si->locking_disabled_p = True;
       si->nolock_reason = "error getting password";
     }
   if (! lock_init (*argc, argv, si->prefs.verbose_p))
     {
       si->locking_disabled_p = True;
       si->nolock_reason = "error getting password";
     }
-#endif /* NO_LOCKING */
 
 
-#ifndef NO_SETUID
-  hack_uid (si);
-#endif /* NO_SETUID */
+  /* If locking is currently enabled, but the environment indicates that
+     we have been launched as GDM's "Background" program, then disable
+     locking just in case.
+   */
+  if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
+    {
+      si->locking_disabled_p = True;
+      si->nolock_reason = "running under GDM";
+    }
+
+  /* If the server is XDarwin (MacOS X) then disable locking.
+     (X grabs only affect X programs, so you can use Command-Tab
+     to bring any other Mac program to the front, e.g., Terminal.)
+   */
+  if (!si->locking_disabled_p)
+    {
+      int op = 0, event = 0, error = 0;
+      Bool macos_p = False;
+
+#ifdef __APPLE__
+      /* Disable locking if *running* on Apple hardware, since we have no
+         reliable way to determine whether the server is running on MacOS.
+         Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
+         but I'm not really sure about that.
+       */
+      macos_p = True;
+#endif
+
+      if (!macos_p)
+        /* This extension exists on the Apple X11 server, but not
+           on earlier versions of the XDarwin server. */
+        macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
+
+      if (macos_p)
+        {
+          si->locking_disabled_p = True;
+          si->nolock_reason = "Cannot lock securely on MacOS X";
+        }
+    }
+
+#endif /* NO_LOCKING */
 }
 
 
 }
 
 
@@ -445,6 +541,24 @@ connect_to_server (saver_info *si, int *argc, char **argv)
 {
   Widget toplevel_shell;
 
 {
   Widget toplevel_shell;
 
+#ifdef HAVE_PUTENV
+  char *d = getenv ("DISPLAY");
+  if (!d || !*d)
+    {
+      char *ndpy = strdup("DISPLAY=:0.0");
+      /* if (si->prefs.verbose_p) */      /* sigh, too early to test this... */
+        fprintf (stderr,
+                 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
+                 blurb(), ndpy+8);
+      if (putenv (ndpy))
+        abort ();
+      /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
+         glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
+         do not.  So we must leak it (and/or the previous setting). Yay.
+       */
+    }
+#endif /* HAVE_PUTENV */
+
   XSetErrorHandler (saver_ehandler);
 
   XtAppSetErrorMsgHandler (si->app, startup_ehandler);
   XSetErrorHandler (saver_ehandler);
 
   XtAppSetErrorMsgHandler (si->app, startup_ehandler);
@@ -466,10 +580,12 @@ connect_to_server (saver_info *si, int *argc, char **argv)
   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 = 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_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
   XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
                                         False);
   XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
   XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
                                         False);
   XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
+  XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
+  XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
   XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
   XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
   XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
   XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
   XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
   XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
@@ -481,6 +597,9 @@ connect_to_server (saver_info *si, int *argc, char **argv)
   XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
   XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
   XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
   XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
   XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
   XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
+  XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
+  XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
+  XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
 
   return toplevel_shell;
 }
 
   return toplevel_shell;
 }
@@ -566,14 +685,15 @@ print_banner (saver_info *si)
      whether to print the banner (and so that the banner gets printed before
      any resource-database-related error messages.)
    */
      whether to print the banner (and so that the banner gets printed before
      any resource-database-related error messages.)
    */
-  p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
+  p->verbose_p = (p->debug_p || 
+                  get_boolean_resource (si->dpy, "verbose", "Boolean"));
 
   /* Ditto, for the locking_disabled_p message. */
 
   /* Ditto, for the locking_disabled_p message. */
-  p->lock_p = get_boolean_resource ("lock", "Boolean");
+  p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
 
   if (p->verbose_p)
     fprintf (stderr,
 
   if (p->verbose_p)
     fprintf (stderr,
-            "%s %s, copyright (c) 1991-1999 "
+            "%s %s, copyright (c) 1991-2006 "
             "by Jamie Zawinski <jwz@jwz.org>.\n",
             progname, si->version);
 
             "by Jamie Zawinski <jwz@jwz.org>.\n",
             progname, si->version);
 
@@ -606,11 +726,17 @@ print_banner (saver_info *si)
       fprintf (stderr, "%s: in process %lu.\n", blurb(),
               (unsigned long) getpid());
     }
       fprintf (stderr, "%s: in process %lu.\n", blurb(),
               (unsigned long) getpid());
     }
+}
+
+static void
+print_lock_failure_banner (saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
 
   /* If locking was not able to be initalized for some reason, explain why.
      (This has to be done after we've read the lock_p resource.)
    */
 
   /* If locking was not able to be initalized for some reason, explain why.
      (This has to be done after we've read the lock_p resource.)
    */
-  if (p->lock_p && si->locking_disabled_p)
+  if (si->locking_disabled_p)
     {
       p->lock_p = False;
       fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
     {
       p->lock_p = False;
       fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
@@ -625,11 +751,12 @@ print_banner (saver_info *si)
                 "\t See the manual for details.\n",
                 blurb());
     }
                 "\t See the manual for details.\n",
                 blurb());
     }
+
 }
 
 
 /* Examine all of the display's screens, and populate the `saver_screen_info'
 }
 
 
 /* Examine all of the display's screens, and populate the `saver_screen_info'
-   structures.
+   structures.  Make sure this is called after hack_environment() sets $PATH.
  */
 static void
 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
  */
 static void
 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
@@ -637,17 +764,130 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
   Bool found_any_writable_cells = False;
   int i;
 
   Bool found_any_writable_cells = False;
   int i;
 
-  si->nscreens = ScreenCount(si->dpy);
-  si->screens = (saver_screen_info *)
-    calloc(sizeof(saver_screen_info), si->nscreens);
+# ifdef HAVE_XINERAMA
+  {
+    int event, error;
+    si->xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) &&
+                      XineramaIsActive (si->dpy));
+  }
+
+  if (si->xinerama_p && ScreenCount (si->dpy) != 1)
+    {
+      si->xinerama_p = False;
+      if (si->prefs.verbose_p)
+       fprintf (stderr,
+                 "%s: Xinerama AND %d screens?  Disabling Xinerama support!\n",
+                 blurb(), ScreenCount(si->dpy));
+    }
+
+  if (si->xinerama_p)
+    {
+      XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &si->nscreens);
+      if (!xsi)
+        si->xinerama_p = False;
+      else
+        {
+          si->screens = (saver_screen_info *)
+            calloc(sizeof(saver_screen_info), si->nscreens);
+          for (i = 0; i < si->nscreens; i++)
+            {
+              si->screens[i].x      = xsi[i].x_org;
+              si->screens[i].y      = xsi[i].y_org;
+              si->screens[i].width  = xsi[i].width;
+              si->screens[i].height = xsi[i].height;
+            }
+          XFree (xsi);
+        }
+      si->default_screen = &si->screens[0];
+      si->default_screen->real_screen_p = True;
+    }
+# endif /* !HAVE_XINERAMA */
 
 
-  si->default_screen = &si->screens[DefaultScreen(si->dpy)];
+  if (!si->xinerama_p)
+    {
+      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->width  = DisplayWidth  (si->dpy, i);
+          ssi->height = DisplayHeight (si->dpy, i);
+          ssi->real_screen_p = True;
+          ssi->real_screen_number = i;
+        }
+    }
+
+
+# ifdef QUAD_MODE
+  /* In "quad mode", we use the Xinerama code to pretend that there are 4
+     screens for every physical screen, and run four times as many hacks...
+   */
+  if (si->prefs.quad_p)
+    {
+      int ns2 = si->nscreens * 4;
+      saver_screen_info *ssi2 = (saver_screen_info *)
+        calloc(sizeof(saver_screen_info), ns2);
+
+      for (i = 0; i < si->nscreens; i++)
+        {
+          saver_screen_info *old = &si->screens[i];
+
+          if (si->prefs.debug_p) old->width = old->width / 2;
+
+          ssi2[i*4  ] = *old;
+          ssi2[i*4+1] = *old;
+          ssi2[i*4+2] = *old;
+          ssi2[i*4+3] = *old;
+
+          ssi2[i*4  ].width  /= 2;
+          ssi2[i*4  ].height /= 2;
+
+          ssi2[i*4+1].x      += ssi2[i*4  ].width;
+          ssi2[i*4+1].width  -= ssi2[i*4  ].width;
+          ssi2[i*4+1].height /= 2;
+
+          ssi2[i*4+2].y      += ssi2[i*4  ].height;
+          ssi2[i*4+2].width  /= 2;
+          ssi2[i*4+2].height -= ssi2[i*4  ].height;
+
+          ssi2[i*4+3].x      += ssi2[i*4+2].width;
+          ssi2[i*4+3].y      += ssi2[i*4+2].height;
+          ssi2[i*4+3].width  -= ssi2[i*4+2].width;
+          ssi2[i*4+3].height -= ssi2[i*4+2].height;
+
+          ssi2[i*4+1].real_screen_p = False;
+          ssi2[i*4+2].real_screen_p = False;
+          ssi2[i*4+3].real_screen_p = False;
+        }
+
+      si->nscreens = ns2;
+      free (si->screens);
+      si->screens = ssi2;
+      si->default_screen = &si->screens[DefaultScreen(si->dpy) * 4];
+      si->xinerama_p = True;
+    }
+# endif /* QUAD_MODE */
+
+  /* finish initializing the screens.
+   */
   for (i = 0; i < si->nscreens; i++)
     {
       saver_screen_info *ssi = &si->screens[i];
       ssi->global = si;
   for (i = 0; i < si->nscreens; i++)
     {
       saver_screen_info *ssi = &si->screens[i];
       ssi->global = si;
-      ssi->screen = ScreenOfDisplay (si->dpy, i);
+
+      ssi->number = i;
+      ssi->screen = ScreenOfDisplay (si->dpy, ssi->real_screen_number);
+      ssi->poll_mouse_last_root_x = -1;
+      ssi->poll_mouse_last_root_y = -1;
+
+      if (!si->xinerama_p)
+        {
+          ssi->width  = WidthOfScreen  (ssi->screen);
+          ssi->height = HeightOfScreen (ssi->screen);
+        }
 
       /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
       ssi->default_visual =
 
       /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
       ssi->default_visual =
@@ -656,6 +896,9 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
       ssi->current_visual = ssi->default_visual;
       ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
 
       ssi->current_visual = ssi->default_visual;
       ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
 
+      /* Execute a subprocess to find the GL visual. */
+      ssi->best_gl_visual = get_best_gl_visual (ssi);
+
       if (ssi == si->default_screen)
        /* Since this is the default screen, use the one already created. */
        ssi->toplevel_shell = toplevel_shell;
       if (ssi == si->default_screen)
        /* Since this is the default screen, use the one already created. */
        ssi->toplevel_shell = toplevel_shell;
@@ -668,7 +911,7 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
                              XtNvisual, ssi->current_visual,
                              XtNdepth,  visual_depth (ssi->screen,
                                                       ssi->current_visual),
                              XtNvisual, ssi->current_visual,
                              XtNdepth,  visual_depth (ssi->screen,
                                                       ssi->current_visual),
-                             0);
+                             NULL);
 
       if (! found_any_writable_cells)
        {
 
       if (! found_any_writable_cells)
        {
@@ -685,7 +928,11 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
        }
     }
 
        }
     }
 
-  si->prefs.fading_possible_p = found_any_writable_cells;
+  si->fading_possible_p = found_any_writable_cells;
+
+#ifdef HAVE_XF86VMODE_GAMMA
+  si->fading_possible_p = True;  /* if we can gamma fade, go for it */
+#endif
 }
 
 
 }
 
 
@@ -756,6 +1003,19 @@ initialize_server_extensions (saver_info *si)
                 blurb());
     }
 
                 blurb());
     }
 
+  /* These are incompatible (or at least, our support for them is...) */
+  if (si->xinerama_p && si->using_mit_saver_extension)
+    {
+      si->using_mit_saver_extension = False;
+      if (p->verbose_p)
+        fprintf (stderr, "%s: Xinerama in use: disabling MIT-SCREEN-SAVER.\n",
+                 blurb());
+    }
+
+#ifdef HAVE_RANDR
+  query_randr_extension (si);
+#endif
+
   if (!system_has_proc_interrupts_p)
     {
       si->using_proc_interrupts = False;
   if (!system_has_proc_interrupts_p)
     {
       si->using_proc_interrupts = False;
@@ -820,8 +1080,12 @@ select_events (saver_info *si)
      for window creation events, so that new subwindows will be noticed.
    */
   for (i = 0; i < si->nscreens; i++)
      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),
-                               False);
+    {
+      saver_screen_info *ssi = &si->screens[i];
+      if (ssi->real_screen_p)
+        start_notice_events_timer (si,
+           RootWindowOfScreen (si->screens[i].screen), False);
+    }
 
   if (p->verbose_p)
     fprintf (stderr, " done.\n");
 
   if (p->verbose_p)
     fprintf (stderr, " done.\n");
@@ -838,11 +1102,21 @@ maybe_reload_init_file (saver_info *si)
        fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
                 blurb(), init_file_name());
 
        fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
                 blurb(), init_file_name());
 
-      load_init_file (p);
+      load_init_file (si->dpy, p);
 
       /* If a server extension is in use, and p->timeout has changed,
         we need to inform the server of the new timeout. */
       disable_builtin_screensaver (si, False);
 
       /* If a server extension is in use, and p->timeout has changed,
         we need to inform the server of the new timeout. */
       disable_builtin_screensaver (si, False);
+
+      /* If the DPMS settings in the init file have changed,
+         change the settings on the server to match. */
+      sync_server_dpms_settings (si->dpy,
+                                 (p->dpms_enabled_p  &&
+                                  p->mode != DONT_BLANK),
+                                 p->dpms_standby / 1000,
+                                 p->dpms_suspend / 1000,
+                                 p->dpms_off / 1000,
+                                 False);
     }
 }
 
     }
 }
 
@@ -864,7 +1138,14 @@ main_loop (saver_info *si)
 
   while (1)
     {
 
   while (1)
     {
+      Bool was_locked = False;
+
+      if (p->verbose_p)
+       fprintf (stderr, "%s: awaiting idleness.\n", blurb());
+
+      check_for_leaks ("unblanked A");
       sleep_until_idle (si, True);
       sleep_until_idle (si, True);
+      check_for_leaks ("unblanked B");
 
       if (p->verbose_p)
        {
 
       if (p->verbose_p)
        {
@@ -872,13 +1153,48 @@ main_loop (saver_info *si)
            fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
                     si->selection_mode, timestring());
          else
            fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
                     si->selection_mode, timestring());
          else
-           if (p->verbose_p)
-             fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
-                      timestring());
+            fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
+                     timestring());
        }
 
       maybe_reload_init_file (si);
 
        }
 
       maybe_reload_init_file (si);
 
+      if (p->mode == DONT_BLANK)
+        {
+          if (p->verbose_p)
+            fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
+                     blurb(), timestring());
+
+          /* Go around the loop and wait for the next bout of idleness,
+             or for the init file to change, or for a remote command to
+             come in, or something.
+
+             But, if locked_p is true, go ahead.  This can only happen
+             if we're in "disabled" mode but a "lock" clientmessage came
+             in: in that case, we should go ahead and blank/lock the screen.
+           */
+          if (!si->locked_p)
+            continue;
+        }
+
+      /* Since we're about to blank the screen, kill the de-race timer,
+         if any.  It might still be running if we have unblanked and then
+         re-blanked in a short period (e.g., when using the "next" button
+         in xscreensaver-demo.)
+       */
+      if (si->de_race_id)
+        {
+          if (p->verbose_p)
+            fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
+                     blurb(), si->de_race_ticks);
+          XtRemoveTimeOut (si->de_race_id);
+          si->de_race_id = 0;
+        }
+
+
+      /* Now, try to blank.
+       */
+
       if (! blank_screen (si))
         {
           /* We were unable to grab either the keyboard or mouse.
       if (! blank_screen (si))
         {
           /* We were unable to grab either the keyboard or mouse.
@@ -889,56 +1205,106 @@ main_loop (saver_info *si)
              see any events, and the display would be wedged.
 
              So, just go around the loop again and wait for the
              see any events, and the display would be wedged.
 
              So, just go around the loop again and wait for the
-             next bout of idleness.
+             next bout of idleness.  (If the user remains idle, we
+             will next try to blank the screen again in no more than
+             60 seconds.)
           */
           */
-
-          fprintf (stderr,
+          Time retry = 60 * 1000;
+          if (p->timeout < retry)
+            retry = p->timeout;
+
+          if (p->debug_p)
+            {
+              fprintf (stderr,
+                  "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
+                       blurb());
+            }
+          else
+            {
+              fprintf (stderr,
                   "%s: unable to grab keyboard or mouse!  Blanking aborted.\n",
                   "%s: unable to grab keyboard or mouse!  Blanking aborted.\n",
-                   blurb(), timestring ());
-          continue;
+                       blurb());
+
+              schedule_wakeup_event (si, retry, p->debug_p);
+              continue;
+            }
         }
 
       kill_screenhack (si);
         }
 
       kill_screenhack (si);
-      spawn_screenhack (si, True);
+
+      if (!si->throttled_p)
+        spawn_screenhack (si, True);
+      else if (p->verbose_p)
+        fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
 
       /* Don't start the cycle timer in demo mode. */
       if (!si->demoing_p && p->cycle)
 
       /* Don't start the cycle timer in demo mode. */
       if (!si->demoing_p && p->cycle)
-       si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
+       si->cycle_id = XtAppAddTimeOut (si->app,
+                                        (si->selection_mode
+                                         /* see comment in cycle_timer() */
+                                         ? 1000 * 60 * 60
+                                         : p->cycle),
+                                        cycle_timer,
                                        (XtPointer) si);
 
 
 #ifndef NO_LOCKING
                                        (XtPointer) si);
 
 
 #ifndef NO_LOCKING
-      if (!si->demoing_p &&            /* if not going into demo mode */
-         p->lock_p &&                  /* and locking is enabled */
-         !si->locking_disabled_p &&    /* and locking is possible */
-         p->lock_timeout == 0)         /* and locking is not timer-deferred */
-       si->locked_p = True;            /* then lock right now. */
-
-      /* locked_p might be true already because of the above, or because of
-        the LOCK ClientMessage.  But if not, and if we're supposed to lock
-        after some time, set up a timer to do so.
+      /* Maybe start locking the screen.
        */
        */
-      if (p->lock_p &&
-         !si->locked_p &&
-         p->lock_timeout > 0)
-       si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
-                                      activate_lock_timer,
-                                      (XtPointer) si);
+      {
+        Time lock_timeout = p->lock_timeout;
+
+        if (si->emergency_lock_p && p->lock_p && lock_timeout)
+          {
+            int secs = p->lock_timeout / 1000;
+            if (p->verbose_p)
+              fprintf (stderr,
+                     "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
+                       blurb(),
+                       (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
+            lock_timeout = 0;
+          }
+
+        si->emergency_lock_p = False;
+
+        if (!si->demoing_p &&           /* if not going into demo mode */
+            p->lock_p &&                /* and locking is enabled */
+            !si->locking_disabled_p &&  /* and locking is possible */
+            lock_timeout == 0)          /* and locking is not timer-deferred */
+          set_locked_p (si, True);      /* then lock right now. */
+
+        /* locked_p might be true already because of the above, or because of
+           the LOCK ClientMessage.  But if not, and if we're supposed to lock
+           after some time, set up a timer to do so.
+        */
+        if (p->lock_p &&
+            !si->locked_p &&
+            lock_timeout > 0)
+          si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
+                                         activate_lock_timer,
+                                         (XtPointer) si);
+      }
 #endif /* !NO_LOCKING */
 
 
       ok_to_unblank = True;
       do {
 
 #endif /* !NO_LOCKING */
 
 
       ok_to_unblank = True;
       do {
 
+        check_for_leaks ("blanked A");
        sleep_until_idle (si, False);           /* until not idle */
        sleep_until_idle (si, False);           /* until not idle */
+        check_for_leaks ("blanked B");
+
        maybe_reload_init_file (si);
 
 #ifndef NO_LOCKING
        maybe_reload_init_file (si);
 
 #ifndef NO_LOCKING
+        /* Maybe unlock the screen.
+         */
        if (si->locked_p)
          {
            saver_screen_info *ssi = si->default_screen;
            if (si->locking_disabled_p) abort ();
 
        if (si->locked_p)
          {
            saver_screen_info *ssi = si->default_screen;
            if (si->locking_disabled_p) abort ();
 
+            was_locked = True;
            si->dbox_up_p = True;
            suspend_screenhack (si, True);
            XUndefineCursor (si->dpy, ssi->screensaver_window);
            si->dbox_up_p = True;
            suspend_screenhack (si, True);
            XUndefineCursor (si->dpy, ssi->screensaver_window);
@@ -948,6 +1314,20 @@ main_loop (saver_info *si)
            si->dbox_up_p = False;
            XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
            suspend_screenhack (si, False);     /* resume */
            si->dbox_up_p = False;
            XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
            suspend_screenhack (si, False);     /* resume */
+
+            if (!ok_to_unblank &&
+                !screenhack_running_p (si))
+              {
+                /* If the lock dialog has been dismissed and we're not about to
+                   unlock the screen, and there is currently no hack running,
+                   then launch one.  (There might be no hack running if DPMS
+                   had kicked in.  But DPMS is off now, so bring back the hack)
+                 */
+                if (si->cycle_id)
+                  XtRemoveTimeOut (si->cycle_id);
+                si->cycle_id = 0;
+                cycle_timer ((XtPointer) si, 0);
+              }
          }
 #endif /* !NO_LOCKING */
 
          }
 #endif /* !NO_LOCKING */
 
@@ -962,10 +1342,21 @@ main_loop (saver_info *si)
       kill_screenhack (si);
       unblank_screen (si);
 
       kill_screenhack (si);
       unblank_screen (si);
 
-      si->locked_p = False;
+      set_locked_p (si, False);
+      si->emergency_lock_p = False;
       si->demoing_p = 0;
       si->selection_mode = 0;
 
       si->demoing_p = 0;
       si->selection_mode = 0;
 
+      /* If we're throttled, and the user has explicitly unlocked the screen,
+         then unthrottle.  If we weren't locked, then don't unthrottle
+         automatically, because someone might have just bumped the desk... */
+      if (was_locked)
+        {
+          if (si->throttled_p && p->verbose_p)
+            fprintf (stderr, "%s: unthrottled.\n", blurb());
+          si->throttled_p = False;
+        }
+
       if (si->cycle_id)
        {
          XtRemoveTimeOut (si->cycle_id);
       if (si->cycle_id)
        {
          XtRemoveTimeOut (si->cycle_id);
@@ -978,12 +1369,15 @@ main_loop (saver_info *si)
          si->lock_id = 0;
        }
 
          si->lock_id = 0;
        }
 
-      if (p->verbose_p)
-       fprintf (stderr, "%s: awaiting idleness.\n", blurb());
+      /* Since we're unblanked now, break race conditions and make
+         sure we stay that way (see comment in timers.c.) */
+      if (! si->de_race_id)
+        de_race_timer ((XtPointer) si, 0);
     }
 }
 
 static void analyze_display (saver_info *si);
     }
 }
 
 static void analyze_display (saver_info *si);
+static void fix_fds (void);
 
 int
 main (int argc, char **argv)
 
 int
 main (int argc, char **argv)
@@ -992,40 +1386,85 @@ main (int argc, char **argv)
   saver_info the_si;
   saver_info *si = &the_si;
   saver_preferences *p = &si->prefs;
   saver_info the_si;
   saver_info *si = &the_si;
   saver_preferences *p = &si->prefs;
+  struct passwd *spasswd;
   int i;
 
   memset(si, 0, sizeof(*si));
   global_si_kludge = si;       /* I hate C so much... */
 
   int i;
 
   memset(si, 0, sizeof(*si));
   global_si_kludge = si;       /* I hate C so much... */
 
-  srandom ((int) time ((time_t *) 0));
+  fix_fds();
+
+# undef ya_rand_init
+  ya_rand_init (0);
 
   save_argv (argc, argv);
   set_version_string (si, &argc, argv);
   privileged_initialization (si, &argc, argv);
   hack_environment (si);
 
 
   save_argv (argc, argv);
   set_version_string (si, &argc, argv);
   privileged_initialization (si, &argc, argv);
   hack_environment (si);
 
+  spasswd = getpwuid(getuid());
+  if (!spasswd)
+    {
+      fprintf(stderr, "Could not figure out who the current user is!\n");
+      fprintf(stderr, "spasswd is %x\n", (unsigned int) spasswd);
+      return 1;
+    }
+
+  si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
+
+# ifndef NO_LOCKING
+  si->unlock_cb = gui_auth_conv;
+  si->auth_finished_cb = auth_finished_cb;
+# endif /* !NO_LOCKING */
+
   shell = connect_to_server (si, &argc, argv);
   process_command_line (si, &argc, argv);
   print_banner (si);
 
   shell = connect_to_server (si, &argc, argv);
   process_command_line (si, &argc, argv);
   print_banner (si);
 
-  initialize_per_screen_info (si, shell);  /* also sets p->fading_possible_p */
+  load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
+  blurb_timestamp_p = p->timestamp_p;  /* kludge */
+  initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
+
+  /* We can only issue this warning now. */
+  if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
+    fprintf (stderr,
+             "%s: there are no PseudoColor or GrayScale visuals.\n"
+             "%s: ignoring the request for fading/unfading.\n",
+             blurb(), blurb());
 
   for (i = 0; i < si->nscreens; i++)
 
   for (i = 0; i < si->nscreens; i++)
-    if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
-      exit (1);
+    {
+      saver_screen_info *ssi = &si->screens[i];
+      if (ssi->real_screen_p)
+        if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
+          exit (1);
+    }
 
 
-  load_init_file (p);
+  lock_initialization (si, &argc, argv);
+  print_lock_failure_banner (si);
 
   if (p->xsync_p) XSynchronize (si->dpy, True);
 
   if (p->xsync_p) XSynchronize (si->dpy, True);
-  blurb_timestamp_p = p->timestamp_p;  /* kludge */
 
   if (p->verbose_p) analyze_display (si);
   initialize_server_extensions (si);
 
   if (p->verbose_p) analyze_display (si);
   initialize_server_extensions (si);
+
+  si->blank_time = time ((time_t) 0); /* must be before ..._window */
   initialize_screensaver_window (si);
   initialize_screensaver_window (si);
+
   select_events (si);
   init_sigchld ();
   select_events (si);
   init_sigchld ();
+
   disable_builtin_screensaver (si, True);
   disable_builtin_screensaver (si, True);
+  sync_server_dpms_settings (si->dpy,
+                             (p->dpms_enabled_p  &&
+                              p->mode != DONT_BLANK),
+                             p->dpms_standby / 1000,
+                             p->dpms_suspend / 1000,
+                             p->dpms_off / 1000,
+                             False);
+
   initialize_stderr (si);
   initialize_stderr (si);
+  handle_signals (si);
 
   make_splash_dialog (si);
 
 
   make_splash_dialog (si);
 
@@ -1033,10 +1472,81 @@ main (int argc, char **argv)
   return 0;
 }
 
   return 0;
 }
 
+static void
+fix_fds (void)
+{
+  /* Bad Things Happen if stdin, stdout, and stderr have been closed
+     (as by the `sh incantation "xscreensaver >&- 2>&-").  When you do
+     that, the X connection gets allocated to one of these fds, and
+     then some random library writes to stderr, and random bits get
+     stuffed down the X pipe, causing "Xlib: sequence lost" errors.
+     So, we cause the first three file descriptors to be open to
+     /dev/null if they aren't open to something else already.  This
+     must be done before any other files are opened (or the closing
+     of that other file will again free up one of the "magic" first
+     three FDs.)
+
+     We do this by opening /dev/null three times, and then closing
+     those fds, *unless* any of them got allocated as #0, #1, or #2,
+     in which case we leave them open.  Gag.
+
+     Really, this crap is technically required of *every* X program,
+     if you want it to be robust in the face of "2>&-".
+   */
+  int fd0 = open ("/dev/null", O_RDWR);
+  int fd1 = open ("/dev/null", O_RDWR);
+  int fd2 = open ("/dev/null", O_RDWR);
+  if (fd0 > 2) close (fd0);
+  if (fd1 > 2) close (fd1);
+  if (fd2 > 2) close (fd2);
+}
+
+
 \f
 /* Processing ClientMessage events.
  */
 
 \f
 /* Processing ClientMessage events.
  */
 
+
+static Bool error_handler_hit_p = False;
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+  error_handler_hit_p = True;
+  return 0;
+}
+
+/* Sometimes some systems send us ClientMessage events with bogus atoms in
+   them.  We only look up the atom names for printing warning messages,
+   so don't bomb out when it happens...
+ */
+static char *
+XGetAtomName_safe (Display *dpy, Atom atom)
+{
+  char *result;
+  XErrorHandler old_handler;
+  if (!atom) return 0;
+
+  XSync (dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+  result = XGetAtomName (dpy, atom);
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+  if (error_handler_hit_p) result = 0;
+
+  if (result)
+    return result;
+  else
+    {
+      char buf[100];
+      sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
+      return strdup (buf);
+    }
+}
+
+
 static void
 clientmessage_response (saver_info *si, Window w, Bool error,
                        const char *stderr_msg,
 static void
 clientmessage_response (saver_info *si, Window w, Bool error,
                        const char *stderr_msg,
@@ -1045,6 +1555,8 @@ clientmessage_response (saver_info *si, Window w, Bool error,
   char *proto;
   int L;
   saver_preferences *p = &si->prefs;
   char *proto;
   int L;
   saver_preferences *p = &si->prefs;
+  XErrorHandler old_handler;
+
   if (error || p->verbose_p)
     fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
 
   if (error || p->verbose_p)
     fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
 
@@ -1054,34 +1566,106 @@ clientmessage_response (saver_info *si, Window w, Bool error,
   strcpy (proto+1, protocol_msg);
   L++;
 
   strcpy (proto+1, protocol_msg);
   L++;
 
+  /* Ignore all X errors while sending a response to a ClientMessage.
+     Pretty much the only way we could get an error here is if the
+     window we're trying to send the reply on has been deleted, in
+     which case, the sender of the ClientMessage won't see our response
+     anyway.
+   */
+  XSync (si->dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
   XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
                   PropModeReplace, (unsigned char *) proto, L);
   XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
                   PropModeReplace, (unsigned char *) proto, L);
+
   XSync (si->dpy, False);
   XSync (si->dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (si->dpy, False);
+
   free (proto);
 }
 
   free (proto);
 }
 
+
+static void
+bogus_clientmessage_warning (saver_info *si, XEvent *event)
+{
+  saver_preferences *p = &si->prefs;
+  char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
+  Window w = event->xclient.window;
+  char wdesc[255];
+  int screen = 0;
+  Bool root_p = False;
+
+  *wdesc = 0;
+  for (screen = 0; screen < si->nscreens; screen++)
+    if (w == si->screens[screen].screensaver_window)
+      {
+        strcpy (wdesc, "xscreensaver");
+        break;
+      }
+    else if (w == RootWindow (si->dpy, screen))
+      {
+        strcpy (wdesc, "root");
+        root_p = True;
+        break;
+      }
+
+  /* If this ClientMessage was sent to the real root window instead of to the
+     xscreensaver window, then it might be intended for someone else who is
+     listening on the root window (e.g., the window manager).  So only print
+     the warning if: we are in debug mode; or if the bogus message was
+     actually sent to one of the xscreensaver-created windows.
+   */
+  if (root_p && !p->debug_p)
+    return;
+
+  if (!*wdesc)
+    {
+      XErrorHandler old_handler;
+      XClassHint hint;
+      XWindowAttributes xgwa;
+      memset (&hint, 0, sizeof(hint));
+      memset (&xgwa, 0, sizeof(xgwa));
+
+      XSync (si->dpy, False);
+      old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+      XGetClassHint (si->dpy, w, &hint);
+      XGetWindowAttributes (si->dpy, w, &xgwa);
+      XSync (si->dpy, False);
+      XSetErrorHandler (old_handler);
+      XSync (si->dpy, False);
+
+      screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
+
+      sprintf (wdesc, "%.20s / %.20s",
+               (hint.res_name  ? hint.res_name  : "(null)"),
+               (hint.res_class ? hint.res_class : "(null)"));
+      if (hint.res_name)  XFree (hint.res_name);
+      if (hint.res_class) XFree (hint.res_class);
+    }
+
+  fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
+           blurb(), screen, (str ? str : "(null)"));
+  fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
+           blurb(), screen, (unsigned long) w, wdesc);
+  if (str) XFree (str);
+}
+
 Bool
 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
 {
 Bool
 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
 {
+  saver_preferences *p = &si->prefs;
   Atom type = 0;
   Window window = event->xclient.window;
 
   /* Preferences might affect our handling of client messages. */
   maybe_reload_init_file (si);
 
   Atom type = 0;
   Window window = event->xclient.window;
 
   /* Preferences might affect our handling of client messages. */
   maybe_reload_init_file (si);
 
-  if (event->xclient.message_type != XA_SCREENSAVER)
+  if (event->xclient.message_type != XA_SCREENSAVER ||
+      event->xclient.format != 32)
     {
     {
-      char *str;
-      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: ClientMessage of format %d received, not 32\n",
-              blurb(), event->xclient.format);
+      bogus_clientmessage_warning (si, event);
       return False;
     }
 
       return False;
     }
 
@@ -1090,11 +1674,24 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
     {
       if (until_idle_p)
        {
     {
       if (until_idle_p)
        {
+          if (p->mode == DONT_BLANK)
+            {
+              clientmessage_response(si, window, True,
+                         "ACTIVATE ClientMessage received in DONT_BLANK mode.",
+                                     "screen blanking is currently disabled.");
+              return False;
+            }
+
          clientmessage_response(si, window, False,
                                 "ACTIVATE ClientMessage received.",
                                 "activating.");
          si->selection_mode = 0;
          si->demoing_p = False;
          clientmessage_response(si, window, False,
                                 "ACTIVATE ClientMessage received.",
                                 "activating.");
          si->selection_mode = 0;
          si->demoing_p = False;
+
+          if (si->throttled_p && p->verbose_p)
+            fprintf (stderr, "%s: unthrottled.\n", blurb());
+         si->throttled_p = False;
+
          if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
            {
              XForceScreenSaver (si->dpy, ScreenSaverActive);
          if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
            {
              XForceScreenSaver (si->dpy, ScreenSaverActive);
@@ -1113,6 +1710,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
     {
       if (! until_idle_p)
        {
     {
       if (! until_idle_p)
        {
+          if (si->throttled_p && p->verbose_p)
+            fprintf (stderr, "%s: unthrottled.\n", blurb());
+         si->throttled_p = False;
+
          clientmessage_response(si, window, False,
                                 "DEACTIVATE ClientMessage received.",
                                 "deactivating.");
          clientmessage_response(si, window, False,
                                 "DEACTIVATE ClientMessage received.",
                                 "deactivating.");
@@ -1126,9 +1727,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
              return True;
            }
        }
              return True;
            }
        }
-      clientmessage_response(si, window, True,
-                          "ClientMessage DEACTIVATE received while inactive.",
-                            "not active.");
+      clientmessage_response(si, window, False,
+     "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
+                            "not active: idle timer reset.");
+      reset_timers (si);
     }
   else if (type == XA_CYCLE)
     {
     }
   else if (type == XA_CYCLE)
     {
@@ -1139,6 +1741,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
                                 "cycling.");
          si->selection_mode = 0;       /* 0 means randomize when its time. */
          si->demoing_p = False;
                                 "cycling.");
          si->selection_mode = 0;       /* 0 means randomize when its time. */
          si->demoing_p = False;
+
+          if (si->throttled_p && p->verbose_p)
+            fprintf (stderr, "%s: unthrottled.\n", blurb());
+         si->throttled_p = False;
+
          if (si->cycle_id)
            XtRemoveTimeOut (si->cycle_id);
          si->cycle_id = 0;
          if (si->cycle_id)
            XtRemoveTimeOut (si->cycle_id);
          si->cycle_id = 0;
@@ -1159,6 +1766,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
       si->selection_mode = (type == XA_NEXT ? -1 : -2);
       si->demoing_p = False;
 
       si->selection_mode = (type == XA_NEXT ? -1 : -2);
       si->demoing_p = False;
 
+      if (si->throttled_p && p->verbose_p)
+        fprintf (stderr, "%s: unthrottled.\n", blurb());
+      si->throttled_p = False;
+
       if (! until_idle_p)
        {
          if (si->cycle_id)
       if (! until_idle_p)
        {
          if (si->cycle_id)
@@ -1175,6 +1786,14 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
       char buf2 [255];
       long which = event->xclient.data.l[1];
 
       char buf2 [255];
       long which = event->xclient.data.l[1];
 
+      if (p->mode == DONT_BLANK)
+        {
+          clientmessage_response(si, window, True,
+                           "SELECT ClientMessage received in DONT_BLANK mode.",
+                                 "screen blanking is currently disabled.");
+          return False;
+        }
+
       sprintf (buf, "SELECT %ld ClientMessage received.", which);
       sprintf (buf2, "activating (%ld).", which);
       clientmessage_response (si, window, False, buf, buf2);
       sprintf (buf, "SELECT %ld ClientMessage received.", which);
       sprintf (buf2, "activating (%ld).", which);
       clientmessage_response (si, window, False, buf, buf2);
@@ -1183,6 +1802,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
       si->selection_mode = which;
       si->demoing_p = False;
 
       si->selection_mode = which;
       si->demoing_p = False;
 
+      if (si->throttled_p && p->verbose_p)
+        fprintf (stderr, "%s: unthrottled.\n", blurb());
+      si->throttled_p = False;
+
       if (! until_idle_p)
        {
          if (si->cycle_id)
       if (! until_idle_p)
        {
          if (si->cycle_id)
@@ -1231,13 +1854,8 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
              XSync (si->dpy, False);
            }
 
              XSync (si->dpy, False);
            }
 
-         /* 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. */
+         restart_process (si);  /* does not return */
+          abort();
        }
       else
        clientmessage_response (si, window, True,
        }
       else
        clientmessage_response (si, window, True,
@@ -1247,7 +1865,7 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
   else if (type == XA_DEMO)
     {
       long arg = event->xclient.data.l[1];
   else if (type == XA_DEMO)
     {
       long arg = event->xclient.data.l[1];
-      Bool demo_one_hack_p = (arg == 300);
+      Bool demo_one_hack_p = (arg == 5000);
 
       if (demo_one_hack_p)
        {
 
       if (demo_one_hack_p)
        {
@@ -1264,6 +1882,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
              si->selection_mode = which;
              si->demoing_p = True;
 
              si->selection_mode = which;
              si->demoing_p = True;
 
+              if (si->throttled_p && p->verbose_p)
+                fprintf (stderr, "%s: unthrottled.\n", blurb());
+              si->throttled_p = False;
+
              return True;
            }
 
              return True;
            }
 
@@ -1305,11 +1927,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
          char *response = (until_idle_p
                            ? "activating and locking."
                            : "locking.");
          char *response = (until_idle_p
                            ? "activating and locking."
                            : "locking.");
-         si->locked_p = True;
-         si->selection_mode = 0;
-         si->demoing_p = False;
          sprintf (buf, "LOCK ClientMessage received; %s", response);
          clientmessage_response (si, window, False, buf, response);
          sprintf (buf, "LOCK ClientMessage received; %s", response);
          clientmessage_response (si, window, False, buf, response);
+         set_locked_p (si, True);
+         si->selection_mode = 0;
+         si->demoing_p = False;
 
          if (si->lock_id)      /* we're doing it now, so lose the timeout */
            {
 
          if (si->lock_id)      /* we're doing it now, so lose the timeout */
            {
@@ -1333,11 +1955,69 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
        }
 #endif /* !NO_LOCKING */
     }
        }
 #endif /* !NO_LOCKING */
     }
+  else if (type == XA_THROTTLE)
+    {
+      /* The THROTTLE command is deprecated -- it predates the XDPMS
+         extension.  Instead of using -throttle, users should instead
+         just power off the monitor (e.g., "xset dpms force off".)
+         In a few minutes, xscreensaver will notice that the monitor
+         is off, and cease running hacks.
+       */
+      if (si->throttled_p)
+       clientmessage_response (si, window, True,
+                                "THROTTLE ClientMessage received, but "
+                                "already throttled.",
+                                "already throttled.");
+      else
+       {
+         char buf [255];
+         char *response = "throttled.";
+         si->throttled_p = True;
+         si->selection_mode = 0;
+         si->demoing_p = False;
+         sprintf (buf, "THROTTLE ClientMessage received; %s", response);
+         clientmessage_response (si, window, False, buf, response);
+
+          if (! until_idle_p)
+            {
+              if (si->cycle_id)
+                XtRemoveTimeOut (si->cycle_id);
+              si->cycle_id = 0;
+              cycle_timer ((XtPointer) si, 0);
+            }
+       }
+    }
+  else if (type == XA_UNTHROTTLE)
+    {
+      if (! si->throttled_p)
+       clientmessage_response (si, window, True,
+                                "UNTHROTTLE ClientMessage received, but "
+                                "not throttled.",
+                                "not throttled.");
+      else
+       {
+         char buf [255];
+         char *response = "unthrottled.";
+         si->throttled_p = False;
+         si->selection_mode = 0;
+         si->demoing_p = False;
+         sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
+         clientmessage_response (si, window, False, buf, response);
+
+          if (! until_idle_p)
+            {
+              if (si->cycle_id)
+                XtRemoveTimeOut (si->cycle_id);
+              si->cycle_id = 0;
+              cycle_timer ((XtPointer) si, 0);
+            }
+       }
+    }
   else
     {
       char buf [1024];
       char *str;
   else
     {
       char buf [1024];
       char *str;
-      str = (type ? XGetAtomName(si->dpy, type) : 0);
+      str = XGetAtomName_safe (si->dpy, type);
 
       if (str)
        {
 
       if (str)
        {
@@ -1367,39 +2047,133 @@ static void
 analyze_display (saver_info *si)
 {
   int i, j;
 analyze_display (saver_info *si)
 {
   int i, j;
-  static const char *exts[][2] = {
-    { "SCREEN_SAVER",     "SGI Screen-Saver" },
-    { "SCREEN-SAVER",     "SGI Screen-Saver" },
-    { "MIT-SCREEN-SAVER",  "MIT Screen-Saver" },
-    { "XIDLE",            "XIdle" },
-    { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
-    { "READDISPLAY",      "SGI Read-Display" },
-    { "MIT-SHM",          "Shared Memory" },
-    { "DOUBLE-BUFFER",    "Double-Buffering" },
-    { "DPMS",             "Power Management" },
-    { "GLX",              "GLX" }
+  static struct {
+    const char *name; const char *desc; Bool useful_p;
+  } exts[] = {
+
+   { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
+#     ifdef HAVE_SGI_SAVER_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
+#     ifdef HAVE_SGI_SAVER_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
+#     ifdef HAVE_MIT_SAVER_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "XIDLE",                                "XIdle",           
+#     ifdef HAVE_XIDLE_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
+#     ifdef HAVE_SGI_VC_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "READDISPLAY",                          "SGI Read-Display",
+#     ifdef HAVE_READ_DISPLAY_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "MIT-SHM",                              "Shared Memory",   
+#     ifdef HAVE_XSHM_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "DOUBLE-BUFFER",                        "Double-Buffering",
+#     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "DPMS",                                 "Power Management",
+#     ifdef HAVE_DPMS_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "GLX",                                  "GLX",             
+#     ifdef HAVE_GL
+        True
+#     else
+        False
+#     endif
+   }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
+#     ifdef HAVE_XF86VMODE
+        True
+#     else
+        False
+#     endif
+   }, { "XINERAMA",                             "Xinerama",
+#     ifdef HAVE_XINERAMA
+        True
+#     else
+        False
+#     endif
+   }, { "RANDR",                                "Resize-and-Rotate",
+#     ifdef HAVE_RANDR
+        True
+#     else
+        False
+#     endif
+   }, { "Apple-DRI",                            "Apple-DRI (XDarwin)",
+        True
+   },
   };
 
   };
 
-  fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
-          DisplayString(si->dpy));
-  fprintf (stderr, "%s: vendor is %s, %d\n", blurb(),
+  fprintf (stderr, "%s: running on display \"%s\" (%d %sscreen%s).\n",
+           blurb(),
+          DisplayString(si->dpy),
+           si->nscreens,
+           (si->xinerama_p ? "Xinerama " : ""),
+           (si->nscreens == 1 ? "" : "s"));
+  fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
           ServerVendor(si->dpy), VendorRelease(si->dpy));
 
   fprintf (stderr, "%s: useful extensions:\n", blurb());
   for (i = 0; i < countof(exts); i++)
     {
       int op = 0, event = 0, error = 0;
           ServerVendor(si->dpy), VendorRelease(si->dpy));
 
   fprintf (stderr, "%s: useful extensions:\n", blurb());
   for (i = 0; i < countof(exts); i++)
     {
       int op = 0, event = 0, error = 0;
-      if (XQueryExtension (si->dpy, exts[i][0], &op, &event, &error))
-       fprintf (stderr, "%s:   %s\n", blurb(), exts[i][1]);
+      char buf [255];
+      int j;
+      if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
+        continue;
+      sprintf (buf, "%s:   ", blurb());
+      j = strlen (buf);
+      strcat (buf, exts[i].desc);
+      if (!exts[i].useful_p)
+        {
+          int k = j + 18;
+          while (strlen (buf) < k) strcat (buf, " ");
+          strcat (buf, "<-- not supported at compile time!");
+        }
+      fprintf (stderr, "%s\n", buf);
     }
 
   for (i = 0; i < si->nscreens; i++)
     {
     }
 
   for (i = 0; i < si->nscreens; i++)
     {
+      saver_screen_info *ssi = &si->screens[i];
       unsigned long colormapped_depths = 0;
       unsigned long non_mapped_depths = 0;
       XVisualInfo vi_in, *vi_out;
       int out_count;
       unsigned long colormapped_depths = 0;
       unsigned long non_mapped_depths = 0;
       XVisualInfo vi_in, *vi_out;
       int out_count;
-      vi_in.screen = i;
+
+      if (!ssi->real_screen_p) continue;
+
+      vi_in.screen = ssi->real_screen_number;
       vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
       if (!vi_out) continue;
       for (j = 0; j < out_count; j++)
       vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
       if (!vi_out) continue;
       for (j = 0; j < out_count; j++)
@@ -1411,21 +2185,37 @@ analyze_display (saver_info *si)
 
       if (colormapped_depths)
        {
 
       if (colormapped_depths)
        {
-         fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
+         fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
+                   ssi->real_screen_number);
          for (j = 0; j < 32; j++)
            if (colormapped_depths & (1 << j))
              fprintf (stderr, " %d", j);
          for (j = 0; j < 32; j++)
            if (colormapped_depths & (1 << j))
              fprintf (stderr, " %d", j);
-         fprintf (stderr, "\n");
+         fprintf (stderr, ".\n");
        }
       if (non_mapped_depths)
        {
        }
       if (non_mapped_depths)
        {
-         fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i);
+         fprintf (stderr, "%s: screen %d non-colormapped depths:",
+                   blurb(), ssi->real_screen_number);
          for (j = 0; j < 32; j++)
            if (non_mapped_depths & (1 << j))
              fprintf (stderr, " %d", j);
          for (j = 0; j < 32; j++)
            if (non_mapped_depths & (1 << j))
              fprintf (stderr, " %d", j);
-         fprintf (stderr, "\n");
+         fprintf (stderr, ".\n");
        }
     }
        }
     }
+
+  if (si->xinerama_p)
+    {
+      fprintf (stderr, "%s: Xinerama layout:\n", blurb());
+      for (i = 0; i < si->nscreens; i++)
+        {
+          saver_screen_info *ssi = &si->screens[i];
+          fprintf (stderr, "%s:   %c %d/%d: %dx%d+%d+%d\n",
+                   blurb(),
+                   (ssi->real_screen_p ? '+' : ' '),
+                   ssi->number, ssi->real_screen_number,
+                   ssi->width, ssi->height, ssi->x, ssi->y);
+        }
+    }
 }
 
 Bool
 }
 
 Bool
@@ -1474,3 +2264,20 @@ display_is_on_console_p (saver_info *si)
     }
   return !not_on_console;
 }
     }
   return !not_on_console;
 }
+
+
+/* Do a little bit of heap introspection...
+ */
+void
+check_for_leaks (const char *where)
+{
+#if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
+  static unsigned long last_brk = 0;
+  int b = (unsigned long) sbrk(0);
+  if (last_brk && last_brk < b)
+    fprintf (stderr, "%s: %s: brk grew by %luK.\n",
+             blurb(), where,
+             (((b - last_brk) + 1023) / 1024));
+  last_brk = b;
+#endif /* HAVE_SBRK */
+}