http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / driver / xscreensaver.c
index 57cc234d760cd13ca05e6c102fb909a4c3a0b7cb..2fdc4ac6d20b5a04ae6ff66f14ec8cedf854274a 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2002 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2011 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.
+ *
+ *   (See comments in screens.c for more details about Xinerama/RANDR stuff.)
+ *
+ *   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,7 +67,7 @@
  *   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,
  *   and a few other things.  The included "xscreensaver-command" program
  *   sends these messsages.
  *   screensaver on or off now, regardless of the idleness of the user,
  *   and a few other things.  The included "xscreensaver-command" program
  *   sends these messsages.
  *   "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
 #include <stdio.h>
 #include <ctype.h>
 #include <X11/Xlib.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <X11/Xlib.h>
+
+#ifdef ENABLE_NLS
+# include <locale.h>
+# include <libintl.h>
+#endif /* ENABLE_NLS */
+
+#include <X11/Xlibint.h>
+
 #include <X11/Xatom.h>
 #include <X11/Intrinsic.h>
 #include <X11/StringDefs.h>
 #include <X11/Xatom.h>
 #include <X11/Intrinsic.h>
 #include <X11/StringDefs.h>
 #include <time.h>
 #include <sys/time.h>
 #include <netdb.h>     /* for gethostbyname() */
 #include <time.h>
 #include <sys/time.h>
 #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 "xmu.h"
 #endif /* !HAVE_XMU */
 
 # include "xmu.h"
 #endif /* !HAVE_XMU */
 
+#ifdef HAVE_MIT_SAVER_EXTENSION
+#include <X11/extensions/scrnsaver.h>
+#endif /* HAVE_MIT_SAVER_EXTENSION */
+
 #ifdef HAVE_XIDLE_EXTENSION
 # include <X11/extensions/xidle.h>
 #endif /* HAVE_XIDLE_EXTENSION */
 
 #ifdef HAVE_XIDLE_EXTENSION
 # include <X11/extensions/xidle.h>
 #endif /* HAVE_XIDLE_EXTENSION */
 
+#ifdef HAVE_SGI_VC_EXTENSION
+# include <X11/extensions/XSGIvc.h>
+#endif /* HAVE_SGI_VC_EXTENSION */
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+# include <X11/extensions/readdisplay.h>
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
+
+#ifdef HAVE_XSHM_EXTENSION
+# include <X11/extensions/XShm.h>
+#endif /* HAVE_XSHM_EXTENSION */
+
+#ifdef HAVE_DPMS_EXTENSION
+# include <X11/extensions/dpms.h>
+#endif /* HAVE_DPMS_EXTENSION */
+
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+# include <X11/extensions/Xdbe.h>
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+#ifdef HAVE_XF86VMODE
+# include <X11/extensions/xf86vmode.h>
+#endif /* HAVE_XF86VMODE */
+
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+# include <X11/extensions/xf86misc.h>
+#endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
+
+#ifdef HAVE_XINERAMA
+# include <X11/extensions/Xinerama.h>
+#endif /* HAVE_XINERAMA */
+
+#ifdef HAVE_RANDR
+# include <X11/extensions/Xrandr.h>
+#endif /* HAVE_RANDR */
+
+
 #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... */
 
@@ -181,38 +250,15 @@ static XrmOptionDescRec options [] = {
 
   /* useful for debugging */
   { "-no-capture-stderr",  ".captureStderr",   XrmoptionNoArg, "off" },
 
   /* useful for debugging */
   { "-no-capture-stderr",  ".captureStderr",   XrmoptionNoArg, "off" },
-
-  /* There's really no reason to have these command-line args; they just
-     lead to confusion when the .xscreensaver file has conflicting values.
-   */
-#if 0
-  { "-splash",            ".splash",           XrmoptionNoArg, "on" },
-  { "-capture-stderr",    ".captureStderr",    XrmoptionNoArg, "on" },
-  { "-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" },
-  { "-timestamp",         ".timestamp",        XrmoptionNoArg, "on" },
-  { "-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" },
-  { "-idelay",            ".initialDelay",     XrmoptionSepArg, 0 },
-  { "-nice",              ".nice",             XrmoptionSepArg, 0 },
-#endif /* 0 */
+  { "-log",               ".logFile",          XrmoptionSepArg, 0 },
 };
 
 };
 
+#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
@@ -228,13 +274,15 @@ do_help (saver_info *si)
   fflush (stdout);
   fflush (stderr);
   fprintf (stdout, "\
   fflush (stdout);
   fflush (stderr);
   fprintf (stdout, "\
-xscreensaver %s, copyright (c) 1991-2002 by Jamie Zawinski <jwz@jwz.org>\n\
+xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
 \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\
   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\
+\n",
+         si->version);
+  fprintf (stdout, "\
   Just getting started?  Try this:\n\
 \n\
         xscreensaver &\n\
   Just getting started?  Try this:\n\
 \n\
         xscreensaver &\n\
@@ -243,8 +291,8 @@ xscreensaver %s, copyright (c) 1991-2002 by Jamie Zawinski <jwz@jwz.org>\n\
   For updates, online manual, and FAQ, please see the web page:\n\
 \n\
        http://www.jwz.org/xscreensaver/\n\
   For updates, online manual, and FAQ, please see the web page:\n\
 \n\
        http://www.jwz.org/xscreensaver/\n\
-\n",
-         si->version);
+\n");
+
   fflush (stdout);
   fflush (stderr);
   exit (1);
   fflush (stdout);
   fflush (stderr);
   exit (1);
@@ -261,7 +309,7 @@ timestring (void)
   return str;
 }
 
   return str;
 }
 
-static Bool blurb_timestamp_p = False;   /* kludge */
+static Bool blurb_timestamp_p = True;   /* kludge */
 
 const char *
 blurb (void)
 
 const char *
 blurb (void)
@@ -300,11 +348,14 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
           blurb());
 
   for (i = 0; i < si->nscreens; i++)
           blurb());
 
   for (i = 0; i < si->nscreens; i++)
-    fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
-             blurb(), i,
-             (unsigned int) RootWindowOfScreen (si->screens[i].screen),
-             (unsigned int) si->screens[i].real_vroot,
-             (unsigned int) si->screens[i].screensaver_window);
+    {
+      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"
           "#######################################"
 
   fprintf (real_stderr, "\n"
           "#######################################"
@@ -329,26 +380,30 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
        }
       else
        {
        }
       else
        {
+#ifdef __GNUC__
+  __extension__   /* don't warn about "string length is greater than the
+                     length ISO C89 compilers are required to support". */
+#endif
           fprintf (real_stderr,
           fprintf (real_stderr,
-                   "#######################################"
-                   "#######################################\n\n");
-          fprintf (real_stderr,
+   "#######################################################################\n"
+   "\n"
    "    If at all possible, please re-run xscreensaver with the command\n"
    "    If at all possible, please re-run xscreensaver with the command\n"
-   "    line arguments `-sync -verbose -no-capture', and reproduce this\n"
+   "    line arguments `-sync -verbose -log log.txt', 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"
    "    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"
+   "    file in your bug report.  *DO NOT* mail the core file itself!  That\n"
+   "    won't work.  A \"log.txt\" file will also be written.  Please *do*\n"
+   "    include the complete \"log.txt\" file with your bug report.\n"
    "\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"
    "    The more information you can provide, the better.  But please\n"
    "    report this bug, regardless!\n"
    "\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"
    "    The more information you can provide, the better.  But please\n"
    "    report this bug, regardless!\n"
+   "\n"
+   "#######################################################################\n"
+   "\n"
    "\n");
    "\n");
-          fprintf (real_stderr,
-                   "#######################################"
-                   "#######################################\n\n");
 
          saver_exit (si, -1, 0);
        }
 
          saver_exit (si, -1, 0);
        }
@@ -493,6 +548,40 @@ lock_initialization (saver_info *si, int *argc, char **argv)
       si->locking_disabled_p = True;
       si->nolock_reason = "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";
+        }
+    }
+
+  if (si->prefs.debug_p)    /* But allow locking anyway in debug mode. */
+    si->locking_disabled_p = False;
+
 #endif /* NO_LOCKING */
 }
 
 #endif /* NO_LOCKING */
 }
 
@@ -621,13 +710,6 @@ process_command_line (saver_info *si, int *argc, char **argv)
     with `xscreensaver-demo' or `xscreensaver-command'.\n\
 .   See the man pages for details, or check the web page:\n\
     http://www.jwz.org/xscreensaver/\n\n");
     with `xscreensaver-demo' or `xscreensaver-command'.\n\
 .   See the man pages for details, or check the web page:\n\
     http://www.jwz.org/xscreensaver/\n\n");
-
-             /* 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);
            }
 
          exit (1);
@@ -648,14 +730,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-2002 "
+            "%s %s, copyright (c) 1991-2008 "
             "by Jamie Zawinski <jwz@jwz.org>.\n",
             progname, si->version);
 
             "by Jamie Zawinski <jwz@jwz.org>.\n",
             progname, si->version);
 
@@ -688,11 +771,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(),
@@ -707,6 +796,26 @@ print_banner (saver_info *si)
                 "\t See the manual for details.\n",
                 blurb());
     }
                 "\t See the manual for details.\n",
                 blurb());
     }
+
+}
+
+
+/* called from screens.c so that all the Xt crud is here. */
+void
+initialize_screen_root_widget (saver_screen_info *ssi)
+{
+  saver_info *si = ssi->global;
+  if (ssi->toplevel_shell)
+    XtDestroyWidget (ssi->toplevel_shell);
+  ssi->toplevel_shell =
+    XtVaAppCreateShell (progname, progclass, 
+                        applicationShellWidgetClass,
+                        si->dpy,
+                        XtNscreen, ssi->screen,
+                        XtNvisual, ssi->current_visual,
+                        XtNdepth,  visual_depth (ssi->screen,
+                                                 ssi->current_visual),
+                        NULL);
 }
 
 
 }
 
 
@@ -716,63 +825,28 @@ print_banner (saver_info *si)
 static void
 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
 {
 static void
 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
 {
-  Bool found_any_writable_cells = False;
   int i;
 
   int i;
 
-  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)];
+  update_screen_layout (si);
 
 
+  /* Check to see whether fading is ever possible -- if any of the
+     screens on the display has a PseudoColor visual, then fading can
+     work (on at least some screens.)  If no screen has a PseudoColor
+     visual, then don't bother ever trying to fade, because it will
+     just cause a delay without causing any visible effect.
+   */
   for (i = 0; i < si->nscreens; i++)
     {
       saver_screen_info *ssi = &si->screens[i];
   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;
-
-      /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
-      ssi->default_visual =
-       get_visual_resource (ssi->screen, "visualID", "VisualID", False);
-
-      ssi->current_visual = ssi->default_visual;
-      ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
-
-      /* 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;
-      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);
-
-      if (! found_any_writable_cells)
-       {
-         /* Check to see whether fading is ever possible -- if any of the
-            screens on the display has a PseudoColor visual, then fading can
-            work (on at least some screens.)  If no screen has a PseudoColor
-            visual, then don't bother ever trying to fade, because it will
-            just cause a delay without causing any visible effect.
-         */
-         if (has_writable_cells (ssi->screen, ssi->current_visual) ||
-             get_visual (ssi->screen, "PseudoColor", True, False) ||
-             get_visual (ssi->screen, "GrayScale", True, False))
-           found_any_writable_cells = True;
-       }
+      if (has_writable_cells (ssi->screen, ssi->current_visual) ||
+          get_visual (ssi->screen, "PseudoColor", True, False) ||
+          get_visual (ssi->screen, "GrayScale", True, False))
+        {
+          si->fading_possible_p = True;
+          break;
+        }
     }
 
     }
 
-  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
 #ifdef HAVE_XF86VMODE_GAMMA
   si->fading_possible_p = True;  /* if we can gamma fade, go for it */
 #endif
@@ -791,26 +865,41 @@ initialize_server_extensions (saver_info *si)
   Bool server_has_sgi_saver_extension_p = False;
   Bool server_has_mit_saver_extension_p = False;
   Bool system_has_proc_interrupts_p = False;
   Bool server_has_sgi_saver_extension_p = False;
   Bool server_has_mit_saver_extension_p = False;
   Bool system_has_proc_interrupts_p = False;
+  Bool server_has_xinput_extension_p = False;
   const char *piwhy = 0;
 
   si->using_xidle_extension = p->use_xidle_extension;
   si->using_sgi_saver_extension = p->use_sgi_saver_extension;
   si->using_mit_saver_extension = p->use_mit_saver_extension;
   si->using_proc_interrupts = p->use_proc_interrupts;
   const char *piwhy = 0;
 
   si->using_xidle_extension = p->use_xidle_extension;
   si->using_sgi_saver_extension = p->use_sgi_saver_extension;
   si->using_mit_saver_extension = p->use_mit_saver_extension;
   si->using_proc_interrupts = p->use_proc_interrupts;
+  si->using_xinput_extension = p->use_xinput_extension;
 
 #ifdef HAVE_XIDLE_EXTENSION
 
 #ifdef HAVE_XIDLE_EXTENSION
-  server_has_xidle_extension_p = query_xidle_extension (si);
+  {
+    int ev, er;
+    server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
+  }
 #endif
 #ifdef HAVE_SGI_SAVER_EXTENSION
 #endif
 #ifdef HAVE_SGI_SAVER_EXTENSION
-  server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
+  server_has_sgi_saver_extension_p =
+    XScreenSaverQueryExtension (si->dpy,
+                                &si->sgi_saver_ext_event_number,
+                                &si->sgi_saver_ext_error_number);
 #endif
 #ifdef HAVE_MIT_SAVER_EXTENSION
 #endif
 #ifdef HAVE_MIT_SAVER_EXTENSION
-  server_has_mit_saver_extension_p = query_mit_saver_extension (si);
+  server_has_mit_saver_extension_p =
+    XScreenSaverQueryExtension (si->dpy,
+                                &si->mit_saver_ext_event_number,
+                                &si->mit_saver_ext_error_number);
 #endif
 #ifdef HAVE_PROC_INTERRUPTS
   system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
 #endif
 
 #endif
 #ifdef HAVE_PROC_INTERRUPTS
   system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
 #endif
 
+#ifdef HAVE_XINPUT
+  server_has_xinput_extension_p = query_xinput_extension (si);
+#endif
+
   if (!server_has_xidle_extension_p)
     si->using_xidle_extension = False;
   else if (p->verbose_p)
   if (!server_has_xidle_extension_p)
     si->using_xidle_extension = False;
   else if (p->verbose_p)
@@ -846,6 +935,49 @@ initialize_server_extensions (saver_info *si)
                 blurb());
     }
 
                 blurb());
     }
 
+#ifdef HAVE_RANDR
+  if (XRRQueryExtension (si->dpy,
+                         &si->randr_event_number, &si->randr_error_number))
+    {
+      int nscreens = ScreenCount (si->dpy);  /* number of *real* screens */
+      int i;
+
+      si->using_randr_extension = TRUE;
+
+      if (p->verbose_p)
+       fprintf (stderr, "%s: selecting RANDR events\n", blurb());
+      for (i = 0; i < nscreens; i++)
+#  ifdef RRScreenChangeNotifyMask                 /* randr.h 1.5, 2002/09/29 */
+        XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
+                        RRScreenChangeNotifyMask);
+#  else  /* !RRScreenChangeNotifyMask */          /* Xrandr.h 1.4, 2001/06/07 */
+        XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
+#  endif /* !RRScreenChangeNotifyMask */
+    }
+# endif /* HAVE_RANDR */
+
+#ifdef HAVE_XINPUT
+  if (!server_has_xinput_extension_p)
+    si->using_xinput_extension = False;
+  else
+    {
+      if (si->using_xinput_extension)
+        init_xinput_extension(si);
+
+      if (p->verbose_p)
+        {
+          if (si->using_xinput_extension)
+            fprintf (stderr,
+                     "%s: selecting events from %d XInputExtension devices.\n",
+                     blurb(), si->num_xinput_devices);
+          else
+            fprintf (stderr,
+                     "%s: not using XInputExtension.\n",
+                     blurb());
+        }
+    }
+#endif
+
   if (!system_has_proc_interrupts_p)
     {
       si->using_proc_interrupts = False;
   if (!system_has_proc_interrupts_p)
     {
       si->using_proc_interrupts = False;
@@ -867,6 +999,26 @@ initialize_server_extensions (saver_info *si)
 }
 
 
 }
 
 
+#ifdef DEBUG_MULTISCREEN
+static void
+debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
+{
+  saver_info *si = (saver_info *) closure;
+  saver_preferences *p = &si->prefs;
+  if (update_screen_layout (si))
+    {
+      if (p->verbose_p)
+        {
+          fprintf (stderr, "%s: new layout:\n", blurb());
+          describe_monitor_layout (si);
+        }
+      resize_screensaver_window (si);
+    }
+  XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
+}
+#endif /* DEBUG_MULTISCREEN */
+
+
 /* For the case where we aren't using an server extensions, select user events
    on all the existing windows, and launch timers to select events on
    newly-created windows as well.
 /* For the case where we aren't using an server extensions, select user events
    on all the existing windows, and launch timers to select events on
    newly-created windows as well.
@@ -910,11 +1062,19 @@ 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");
+
+# ifdef DEBUG_MULTISCREEN
+  if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
+# endif
 }
 
 
 }
 
 
@@ -928,7 +1088,7 @@ 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. */
 
       /* If a server extension is in use, and p->timeout has changed,
         we need to inform the server of the new timeout. */
@@ -961,6 +1121,7 @@ main_loop (saver_info *si)
 {
   saver_preferences *p = &si->prefs;
   Bool ok_to_unblank;
 {
   saver_preferences *p = &si->prefs;
   Bool ok_to_unblank;
+  int i;
 
   while (1)
     {
 
   while (1)
     {
@@ -994,10 +1155,33 @@ main_loop (saver_info *si)
           /* 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.
           /* 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.
            */
            */
-          continue;
+          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.
@@ -1008,21 +1192,45 @@ 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.)
           */
           */
+          Time retry = 60 * 1000;
+          if (p->timeout < retry)
+            retry = p->timeout;
 
 
-          fprintf (stderr,
+          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());
-          continue;
+                       blurb());
+
+              schedule_wakeup_event (si, retry, p->debug_p);
+              continue;
+            }
         }
 
         }
 
-      kill_screenhack (si);
+      for (i = 0; i < si->nscreens; i++)
+        kill_screenhack (&si->screens[i]);
 
 
-      if (!si->throttled_p)
-        spawn_screenhack (si, True);
-      else if (p->verbose_p)
+      raise_window (si, True, True, False);
+      if (si->throttled_p)
         fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
         fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
+      else
+        for (i = 0; i < si->nscreens; i++)
+          spawn_screenhack (&si->screens[i]);
+
+      /* If we are blanking only, we might as well power down the monitor
+         right now, regardless of what the DPMS settings are. */
+      if (p->mode == BLANK_ONLY)
+        monitor_power_on (si, False);
 
       /* 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)
@@ -1093,14 +1301,16 @@ main_loop (saver_info *si)
 
             was_locked = True;
            si->dbox_up_p = True;
 
             was_locked = True;
            si->dbox_up_p = True;
-           suspend_screenhack (si, True);
+            for (i = 0; i < si->nscreens; i++)
+              suspend_screenhack (&si->screens[i], True);        /* suspend */
            XUndefineCursor (si->dpy, ssi->screensaver_window);
 
            ok_to_unblank = unlock_p (si);
 
            si->dbox_up_p = False;
            XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
            XUndefineCursor (si->dpy, ssi->screensaver_window);
 
            ok_to_unblank = unlock_p (si);
 
            si->dbox_up_p = False;
            XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
-           suspend_screenhack (si, False);     /* resume */
+            for (i = 0; i < si->nscreens; i++)
+              suspend_screenhack (&si->screens[i], False);        /* resume */
 
             if (!ok_to_unblank &&
                 !screenhack_running_p (si))
 
             if (!ok_to_unblank &&
                 !screenhack_running_p (si))
@@ -1126,7 +1336,8 @@ main_loop (saver_info *si)
                 blurb(), timestring ());
 
       /* Kill before unblanking, to stop drawing as soon as possible. */
                 blurb(), timestring ());
 
       /* Kill before unblanking, to stop drawing as soon as possible. */
-      kill_screenhack (si);
+      for (i = 0; i < si->nscreens; i++)
+        kill_screenhack (&si->screens[i]);
       unblank_screen (si);
 
       set_locked_p (si, False);
       unblank_screen (si);
 
       set_locked_p (si, False);
@@ -1156,52 +1367,10 @@ main_loop (saver_info *si)
          si->lock_id = 0;
        }
 
          si->lock_id = 0;
        }
 
-      /* It's possible that a race condition could have led to the saver
-         window being unexpectedly still mapped.  This can happen like so:
-
-          - screen is blanked
-          - hack is launched
-          - that hack tries to grab a screen image( it does this by
-            first unmapping the saver window, then remapping it.)
-          - hack unmaps window
-          - hack waits
-          - user becomes active
-          - hack re-maps window (*)
-          - driver kills subprocess
-          - driver unmaps window (**)
-
-         The race is that (*) might have been sent to the server before
-         the client process was killed, but, due to scheduling randomness,
-         might not have been received by the server until after (**).
-         In other words, (*) and (**) might happen out of order, meaning
-         the driver will unmap the window, and then after that, the
-         recently-dead client will re-map it.  This leaves the user
-         locked out (it looks like a desktop, but it's not!)
-
-         To avoid this: after un-blanking the screen, sleep for a second,
-         and then really make sure the window is unmapped.
-       */
-      {
-        int i;
-        XSync (si->dpy, False);
-        sleep (1);
-        for (i = 0; i < si->nscreens; i++)
-          {
-            saver_screen_info *ssi = &si->screens[i];
-            Window w = ssi->screensaver_window;
-            XWindowAttributes xgwa;
-            XGetWindowAttributes (si->dpy, w, &xgwa);
-            if (xgwa.map_state != IsUnmapped)
-              {
-                if (p->verbose_p)
-                  fprintf (stderr,
-                           "%s: %d: client race! emergency unmap 0x%lx.\n",
-                           blurb(), i, (unsigned long) w);
-                XUnmapWindow (si->dpy, w);
-              }
-          }
-        XSync (si->dpy, False);
-      }
+      /* 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);
     }
 }
 
     }
 }
 
@@ -1215,8 +1384,26 @@ 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;
 
   int i;
 
+  /* It turns out that if we do NLS stuff here, people running in Japanese
+     locales get font craziness on the password dialog, presumably because
+     it is displaying Japanese characters in a non-Japanese font.  I don't
+     understand how to automatically make all this crap work properly by
+     default, so until someone sends me a better patch, just leave it off
+     and run the daemon in English.  -- jwz, 29-Sep-2010
+   */
+#undef ENABLE_NLS
+
+#ifdef ENABLE_NLS
+  if (!setlocale (LC_ALL, ""))
+    fprintf (stderr, "locale not supported by C library\n");
+
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+  textdomain (GETTEXT_PACKAGE);
+#endif /* ENABLE_NLS */
+
   memset(si, 0, sizeof(*si));
   global_si_kludge = si;       /* I hate C so much... */
 
   memset(si, 0, sizeof(*si));
   global_si_kludge = si;       /* I hate C so much... */
 
@@ -1230,15 +1417,30 @@ main (int argc, char **argv)
   privileged_initialization (si, &argc, argv);
   hack_environment (si);
 
   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");
+      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);
   shell = connect_to_server (si, &argc, argv);
   process_command_line (si, &argc, argv);
+  stderr_log_file (si);
   print_banner (si);
 
   print_banner (si);
 
-  load_init_file (p);  /* must be before initialize_per_screen_info() */
+  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 */
 
   blurb_timestamp_p = p->timestamp_p;  /* kludge */
   initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
 
-  /* We can only issue this warnings now. */
+  /* 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"
   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"
@@ -1246,10 +1448,15 @@ main (int argc, char **argv)
              blurb(), blurb());
 
   for (i = 0; i < si->nscreens; i++)
              blurb(), blurb());
 
   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);
+    }
 
   lock_initialization (si, &argc, argv);
 
   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);
 
@@ -1398,10 +1605,23 @@ clientmessage_response (saver_info *si, Window w, Bool error,
 static void
 bogus_clientmessage_warning (saver_info *si, XEvent *event)
 {
 static void
 bogus_clientmessage_warning (saver_info *si, XEvent *event)
 {
+#if 0  /* Oh, fuck it.  GNOME likes to spew random ClientMessages at us
+          all the time.  This is presumably indicative of an error in
+          the sender of that ClientMessage: if we're getting it and 
+          ignoring it, then it's not reaching the intended recipient.
+          But people complain to me about this all the time ("waaah!
+          xscreensaver is printing to it's stderr and that gets my
+          panties all in a bunch!")  And I'm sick of hearing about it.
+          So we'll just ignore these messages and let GNOME go right
+          ahead and continue to stumble along in its malfunction.
+        */
+
+  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;
   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++)
 
   *wdesc = 0;
   for (screen = 0; screen < si->nscreens; screen++)
@@ -1413,9 +1633,19 @@ bogus_clientmessage_warning (saver_info *si, XEvent *event)
     else if (w == RootWindow (si->dpy, screen))
       {
         strcpy (wdesc, "root");
     else if (w == RootWindow (si->dpy, screen))
       {
         strcpy (wdesc, "root");
+        root_p = True;
         break;
       }
 
         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;
   if (!*wdesc)
     {
       XErrorHandler old_handler;
@@ -1446,8 +1676,11 @@ bogus_clientmessage_warning (saver_info *si, XEvent *event)
   fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
            blurb(), screen, (unsigned long) w, wdesc);
   if (str) XFree (str);
   fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
            blurb(), screen, (unsigned long) w, wdesc);
   if (str) XFree (str);
+
+#endif /* 0 */
 }
 
 }
 
+
 Bool
 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
 {
 Bool
 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
 {
@@ -1470,6 +1703,14 @@ 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.");
          clientmessage_response(si, window, False,
                                 "ACTIVATE ClientMessage received.",
                                 "activating.");
@@ -1574,6 +1815,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);
@@ -1606,8 +1855,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
                                  "exiting.");
          if (! until_idle_p)
            {
                                  "exiting.");
          if (! until_idle_p)
            {
+              int i;
+              for (i = 0; i < si->nscreens; i++)
+                kill_screenhack (&si->screens[i]);
              unblank_screen (si);
              unblank_screen (si);
-             kill_screenhack (si);
              XSync (si->dpy, False);
            }
          saver_exit (si, 0, 0);
              XSync (si->dpy, False);
            }
          saver_exit (si, 0, 0);
@@ -1629,8 +1880,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
                                  "restarting.");
          if (! until_idle_p)
            {
                                  "restarting.");
          if (! until_idle_p)
            {
+              int i;
+              for (i = 0; i < si->nscreens; i++)
+                kill_screenhack (&si->screens[i]);
              unblank_screen (si);
              unblank_screen (si);
-             kill_screenhack (si);
              XSync (si->dpy, False);
            }
 
              XSync (si->dpy, False);
            }
 
@@ -1645,7 +1898,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)
        {
@@ -1737,6 +1990,12 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
     }
   else if (type == XA_THROTTLE)
     {
     }
   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 "
       if (si->throttled_p)
        clientmessage_response (si, window, True,
                                 "THROTTLE ClientMessage received, but "
@@ -1822,84 +2081,122 @@ analyze_display (saver_info *si)
 {
   int i, j;
   static struct {
 {
   int i, j;
   static struct {
-    const char *name; const char *desc; Bool useful_p;
+    const char *name; const char *desc; 
+    Bool useful_p;
+    Status (*version_fn) (Display *, int *majP, int *minP);
   } exts[] = {
 
    { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
 #     ifdef HAVE_SGI_SAVER_EXTENSION
   } exts[] = {
 
    { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
 #     ifdef HAVE_SGI_SAVER_EXTENSION
-        True
+        True,  0
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
 #     ifdef HAVE_SGI_SAVER_EXTENSION
 #     endif
    }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
 #     ifdef HAVE_SGI_SAVER_EXTENSION
-        True
+        True,  0
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
 #     ifdef HAVE_MIT_SAVER_EXTENSION
 #     endif
    }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
 #     ifdef HAVE_MIT_SAVER_EXTENSION
-        True
+        True,  XScreenSaverQueryVersion
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "XIDLE",                                "XIdle",           
 #     ifdef HAVE_XIDLE_EXTENSION
 #     endif
    }, { "XIDLE",                                "XIdle",           
 #     ifdef HAVE_XIDLE_EXTENSION
-        True
+        True,  0
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
 #     ifdef HAVE_SGI_VC_EXTENSION
 #     endif
    }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
 #     ifdef HAVE_SGI_VC_EXTENSION
-        True
+        True,  XSGIvcQueryVersion
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "READDISPLAY",                          "SGI Read-Display",
 #     ifdef HAVE_READ_DISPLAY_EXTENSION
 #     endif
    }, { "READDISPLAY",                          "SGI Read-Display",
 #     ifdef HAVE_READ_DISPLAY_EXTENSION
-        True
+        True,  XReadDisplayQueryVersion
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "MIT-SHM",                              "Shared Memory",   
 #     ifdef HAVE_XSHM_EXTENSION
 #     endif
    }, { "MIT-SHM",                              "Shared Memory",   
 #     ifdef HAVE_XSHM_EXTENSION
-        True
+        True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "DOUBLE-BUFFER",                        "Double-Buffering",
 #     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
 #     endif
    }, { "DOUBLE-BUFFER",                        "Double-Buffering",
 #     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
-        True
+        True, XdbeQueryExtension
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "DPMS",                                 "Power Management",
 #     ifdef HAVE_DPMS_EXTENSION
 #     endif
    }, { "DPMS",                                 "Power Management",
 #     ifdef HAVE_DPMS_EXTENSION
-        True
+        True,  DPMSGetVersion
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "GLX",                                  "GLX",             
 #     ifdef HAVE_GL
 #     endif
    }, { "GLX",                                  "GLX",             
 #     ifdef HAVE_GL
-        True
+        True,  0
 #     else
 #     else
-        False
+        False, 0
 #     endif
    }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
 #     ifdef HAVE_XF86VMODE
 #     endif
    }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
 #     ifdef HAVE_XF86VMODE
-        True
+        True,  XF86VidModeQueryVersion
 #     else
 #     else
-        False
+        False, 0
+#     endif
+   }, { "XC-VidModeExtension",                  "XC Video-Mode", 
+#     ifdef HAVE_XF86VMODE
+        True,  XF86VidModeQueryVersion
+#     else
+        False, 0
+#     endif
+   }, { "XFree86-MISC",                         "XF86 Misc", 
+#     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+        True,  XF86MiscQueryVersion
+#     else
+        False, 0
+#     endif
+   }, { "XC-MISC",                              "XC Misc", 
+#     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+        True,  XF86MiscQueryVersion
+#     else
+        False, 0
 #     endif
    }, { "XINERAMA",                             "Xinerama",
 #     endif
    }, { "XINERAMA",                             "Xinerama",
-        True
+#     ifdef HAVE_XINERAMA
+        True,  XineramaQueryVersion
+#     else
+        False, 0
+#     endif
+   }, { "RANDR",                                "Resize-and-Rotate",
+#     ifdef HAVE_RANDR
+        True,  XRRQueryVersion
+#     else
+        False, 0
+#     endif
+   }, { "DRI",                                 "DRI",
+        True,  0
+   }, { "NV-CONTROL",                           "NVidia",
+        True,  0
+   }, { "NV-GLX",                               "NVidia GLX",
+        True,  0
+   }, { "Apple-DRI",                            "Apple-DRI (XDarwin)",
+        True,  0
+   }, { "XInputExtension",                      "XInput",
+        True,  0
    },
   };
 
    },
   };
 
-  fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n",
-           blurb(),
-          DisplayString(si->dpy),
-           si->nscreens, (si->nscreens == 1 ? "" : "s"));
+  fprintf (stderr, "%s: running on display \"%s\"\n", blurb(), 
+           DisplayString(si->dpy));
   fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
           ServerVendor(si->dpy), VendorRelease(si->dpy));
 
   fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
           ServerVendor(si->dpy), VendorRelease(si->dpy));
 
@@ -1908,28 +2205,47 @@ analyze_display (saver_info *si)
     {
       int op = 0, event = 0, error = 0;
       char buf [255];
     {
       int op = 0, event = 0, error = 0;
       char buf [255];
+      int maj = 0, min = 0;
+      int dummy1, dummy2, dummy3;
       int j;
       int j;
+
+      /* Most of the extension version functions take 3 args,
+         writing results into args 2 and 3, but some take more.
+         We only ever care about the first two results, but we
+         pass in three extra pointers just in case.
+       */
+      Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
+        (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
+
       if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
         continue;
       sprintf (buf, "%s:   ", blurb());
       j = strlen (buf);
       strcat (buf, exts[i].desc);
       if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
         continue;
       sprintf (buf, "%s:   ", blurb());
       j = strlen (buf);
       strcat (buf, exts[i].desc);
+
+      if (!version_fn_2)
+        ;
+      else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
+        sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
+      else
+        strcat (buf, " (unavailable)");
+
       if (!exts[i].useful_p)
       if (!exts[i].useful_p)
-        {
-          int k = j + 18;
-          while (strlen (buf) < k) strcat (buf, " ");
-          strcat (buf, "<-- not supported at compile time!");
-        }
+        strcat (buf, " (disabled at compile time)");
       fprintf (stderr, "%s\n", buf);
     }
 
   for (i = 0; i < si->nscreens; i++)
     {
       fprintf (stderr, "%s\n", buf);
     }
 
   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++)
@@ -1941,7 +2257,8 @@ 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);
@@ -1950,15 +2267,18 @@ analyze_display (saver_info *si)
       if (non_mapped_depths)
        {
          fprintf (stderr, "%s: screen %d non-colormapped depths:",
       if (non_mapped_depths)
        {
          fprintf (stderr, "%s: screen %d non-colormapped depths:",
-                   blurb(), i);
+                   blurb(), ssi->real_screen_number);
          for (j = 0; j < 32; j++)
            if (non_mapped_depths & (1 << j))
              fprintf (stderr, " %d", j);
          fprintf (stderr, ".\n");
        }
     }
          for (j = 0; j < 32; j++)
            if (non_mapped_depths & (1 << j))
              fprintf (stderr, " %d", j);
          fprintf (stderr, ".\n");
        }
     }
+
+  describe_monitor_layout (si);
 }
 
 }
 
+
 Bool
 display_is_on_console_p (saver_info *si)
 {
 Bool
 display_is_on_console_p (saver_info *si)
 {
@@ -1978,6 +2298,8 @@ display_is_on_console_p (saver_info *si)
        not_on_console = False;
       else if (gethostname (localname, sizeof (localname)))
        not_on_console = True;  /* can't find hostname? */
        not_on_console = False;
       else if (gethostname (localname, sizeof (localname)))
        not_on_console = True;  /* can't find hostname? */
+      else if (!strncmp (dpyname, "/tmp/launch-", 12))  /* MacOS X launchd */
+       not_on_console = False;
       else
        {
          /* We have to call gethostbyname() on the result of gethostname()
       else
        {
          /* We have to call gethostbyname() on the result of gethostname()
@@ -2012,7 +2334,7 @@ display_is_on_console_p (saver_info *si)
 void
 check_for_leaks (const char *where)
 {
 void
 check_for_leaks (const char *where)
 {
-#ifdef HAVE_SBRK
+#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)
   static unsigned long last_brk = 0;
   int b = (unsigned long) sbrk(0);
   if (last_brk && last_brk < b)