http://svn.poeml.de/viewvc/ppc/src-unpacked/xscreensaver/xscreensaver-4.12.tar.bz2...
[xscreensaver] / driver / xscreensaver.c
index 63a63c83da2360cdc70ff06e59bb905210abeb57..a70de0e4043a2eba2e497f7b36210824cd85c530 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2001 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2003 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
 #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 <time.h>
+#include <sys/time.h>
 #include <netdb.h>     /* for gethostbyname() */
 #ifdef HAVE_XMU
 # ifndef VMS
@@ -169,6 +174,23 @@ Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
 
 \f
 static XrmOptionDescRec options [] = {
+
+  { "-verbose",                   ".verbose",          XrmoptionNoArg, "on" },
+  { "-silent",            ".verbose",          XrmoptionNoArg, "off" },
+
+  /* xscreensaver-demo uses this one */
+  { "-nosplash",          ".splash",           XrmoptionNoArg, "off" },
+  { "-no-splash",         ".splash",           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" },
@@ -180,11 +202,7 @@ static XrmOptionDescRec options [] = {
   { "-visual",            ".visualID",         XrmoptionSepArg, 0 },
   { "-install",                   ".installColormap",  XrmoptionNoArg, "on" },
   { "-no-install",        ".installColormap",  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" },
@@ -193,17 +211,17 @@ static XrmOptionDescRec options [] = {
   { "-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" },
-  { "-nosplash",          ".splash",           XrmoptionNoArg, "off" },
   { "-idelay",            ".initialDelay",     XrmoptionSepArg, 0 },
   { "-nice",              ".nice",             XrmoptionSepArg, 0 },
-
-  /* Actually these are built in to Xt, but just to be sure... */
-  { "-synchronous",       ".synchronous",      XrmoptionNoArg, "on" },
-  { "-xrm",               NULL,                XrmoptionResArg, NULL }
+#endif /* 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
@@ -219,36 +237,25 @@ do_help (saver_info *si)
   fflush (stdout);
   fflush (stderr);
   fprintf (stdout, "\
-xscreensaver %s, copyright (c) 1991-2001 by Jamie Zawinski <jwz@jwz.org>\n\
-The standard Xt command-line options are accepted; other options include:\n\
+xscreensaver %s, copyright (c) 1991-2003 by Jamie Zawinski <jwz@jwz.org>\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\
-\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\
-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);
@@ -293,6 +300,7 @@ 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;
 
@@ -305,17 +313,27 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
   for (i = 0; i < si->nscreens; i++)
     fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
              blurb(), i,
-             RootWindowOfScreen (si->screens[i].screen),
-             si->screens[i].real_vroot,
-             si->screens[i].screensaver_window);
+             (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");
 
-  if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
+  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");
@@ -331,7 +349,8 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
    "    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"
+   "    That won't work.\n");
+          fprintf (real_stderr,
    "\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"
@@ -346,8 +365,7 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
          saver_exit (si, -1, 0);
        }
     }
-  else
-    fprintf (real_stderr, " (nonfatal.)\n");
+
   return 0;
 }
 
@@ -477,9 +495,48 @@ lock_initialization (saver_info *si, int *argc, char **argv)
       si->locking_disabled_p = True;
       si->nolock_reason = "error getting password";
     }
-#endif /* NO_LOCKING */
 
-  hack_uid (si);
+  /* 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 */
 }
 
 
@@ -494,13 +551,17 @@ connect_to_server (saver_info *si, int *argc, char **argv)
   char *d = getenv ("DISPLAY");
   if (!d || !*d)
     {
-      char ndpy[] = "DISPLAY=:0.0";
+      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 */
 
@@ -529,6 +590,8 @@ connect_to_server (saver_info *si, int *argc, char **argv)
   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);
@@ -635,7 +698,7 @@ print_banner (saver_info *si)
 
   if (p->verbose_p)
     fprintf (stderr,
-            "%s %s, copyright (c) 1991-2001 "
+            "%s %s, copyright (c) 1991-2003 "
             "by Jamie Zawinski <jwz@jwz.org>.\n",
             progname, si->version);
 
@@ -668,11 +731,17 @@ print_banner (saver_info *si)
       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 (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(),
@@ -687,6 +756,7 @@ print_banner (saver_info *si)
                 "\t See the manual for details.\n",
                 blurb());
     }
+
 }
 
 
@@ -710,6 +780,7 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
       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 =
@@ -733,7 +804,7 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
                              XtNvisual, ssi->current_visual,
                              XtNdepth,  visual_depth (ssi->screen,
                                                       ssi->current_visual),
-                             0);
+                             NULL);
 
       if (! found_any_writable_cells)
        {
@@ -915,7 +986,9 @@ maybe_reload_init_file (saver_info *si)
 
       /* 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,
+      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,
@@ -942,7 +1015,13 @@ main_loop (saver_info *si)
   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);
+      check_for_leaks ("unblanked B");
 
       if (p->verbose_p)
        {
@@ -950,13 +1029,25 @@ main_loop (saver_info *si)
            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);
 
+      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.
+           */
+          continue;
+        }
+
       if (! blank_screen (si))
         {
           /* We were unable to grab either the keyboard or mouse.
@@ -995,6 +1086,8 @@ main_loop (saver_info *si)
 
 
 #ifndef NO_LOCKING
+      /* Maybe start locking the screen.
+       */
       {
         Time lock_timeout = p->lock_timeout;
 
@@ -1034,10 +1127,15 @@ main_loop (saver_info *si)
       ok_to_unblank = True;
       do {
 
+        check_for_leaks ("blanked A");
        sleep_until_idle (si, False);           /* until not idle */
+        check_for_leaks ("blanked B");
+
        maybe_reload_init_file (si);
 
 #ifndef NO_LOCKING
+        /* Maybe unlock the screen.
+         */
        if (si->locked_p)
          {
            saver_screen_info *ssi = si->default_screen;
@@ -1108,12 +1206,57 @@ main_loop (saver_info *si)
          si->lock_id = 0;
        }
 
-      if (p->verbose_p)
-       fprintf (stderr, "%s: awaiting idleness.\n", blurb());
+      /* 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);
+      }
     }
 }
 
 static void analyze_display (saver_info *si);
+static void fix_fds (void);
 
 int
 main (int argc, char **argv)
@@ -1127,6 +1270,8 @@ main (int argc, char **argv)
   memset(si, 0, sizeof(*si));
   global_si_kludge = si;       /* I hate C so much... */
 
+  fix_fds();
+
 # undef ya_rand_init
   ya_rand_init (0);
 
@@ -1140,6 +1285,7 @@ main (int argc, char **argv)
   print_banner (si);
 
   load_init_file (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 warnings now. */
@@ -1154,9 +1300,9 @@ main (int argc, char **argv)
       exit (1);
 
   lock_initialization (si, &argc, argv);
+  print_lock_failure_banner (si);
 
   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);
@@ -1168,13 +1314,16 @@ main (int argc, char **argv)
   init_sigchld ();
 
   disable_builtin_screensaver (si, True);
-  sync_server_dpms_settings (si->dpy, p->dpms_enabled_p,
+  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);
+  handle_signals (si);
 
   make_splash_dialog (si);
 
@@ -1182,6 +1331,36 @@ main (int argc, char **argv)
   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.
  */
@@ -1221,7 +1400,7 @@ XGetAtomName_safe (Display *dpy, Atom atom)
   else
     {
       char buf[100];
-      sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned long) atom);
+      sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
       return strdup (buf);
     }
 }
@@ -1266,6 +1445,60 @@ clientmessage_response (saver_info *si, Window w, Bool error,
   free (proto);
 }
 
+
+static void
+bogus_clientmessage_warning (saver_info *si, XEvent *event)
+{
+  char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
+  Window w = event->xclient.window;
+  char wdesc[255];
+  int screen = 0;
+
+  *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");
+        break;
+      }
+
+  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)
 {
@@ -1276,19 +1509,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
   /* 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_safe (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;
     }
 
@@ -1342,9 +1566,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
              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)
     {
@@ -1460,17 +1685,8 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
              XSync (si->dpy, False);
            }
 
-          fflush (stdout);
-          fflush (stderr);
-          if (real_stdout) fflush (real_stdout);
-          if (real_stderr) fflush (real_stderr);
-         /* 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,
@@ -1660,13 +1876,13 @@ analyze_display (saver_info *si)
     const char *name; const char *desc; Bool useful_p;
   } exts[] = {
 
-   { "SCREEN_SAVER",                            "SGI Screen-Saver",
+   { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
 #     ifdef HAVE_SGI_SAVER_EXTENSION
         True
 #     else
         False
 #     endif
-   }, { "SCREEN-SAVER",                         "SGI Screen-Saver",
+   }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
 #     ifdef HAVE_SGI_SAVER_EXTENSION
         True
 #     else
@@ -1728,23 +1944,36 @@ analyze_display (saver_info *si)
 #     endif
    }, { "XINERAMA",                             "Xinerama",
         True
+   }, { "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 screen%s).\n",
+           blurb(),
+          DisplayString(si->dpy),
+           si->nscreens, (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;
-      if (XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
-       fprintf (stderr, "%s:  %s%s\n", blurb(),
-                 exts[i].desc,
-                 (exts[i].useful_p ? "" :
-                  "       \t<== unsupported at compile-time!"));
+      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++)
@@ -1769,15 +1998,16 @@ analyze_display (saver_info *si)
          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)
        {
-         fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i);
+         fprintf (stderr, "%s: screen %d non-colormapped depths:",
+                   blurb(), i);
          for (j = 0; j < 32; j++)
            if (non_mapped_depths & (1 << j))
              fprintf (stderr, " %d", j);
-         fprintf (stderr, "\n");
+         fprintf (stderr, ".\n");
        }
     }
 }
@@ -1828,3 +2058,20 @@ display_is_on_console_p (saver_info *si)
     }
   return !not_on_console;
 }
+
+
+/* Do a little bit of heap introspection...
+ */
+void
+check_for_leaks (const char *where)
+{
+#ifdef HAVE_SBRK
+  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 */
+}