http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.34.tar.gz
[xscreensaver] / driver / windows.c
index 7a938c5a53a7f394bc4c605c0aed99c0238041c6..b0198e14a528c2512b0de882d86764e0153281f1 100644 (file)
@@ -1,5 +1,5 @@
 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
- * xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@netscape.com>
+ * xscreensaver, Copyright (c) 1991-2001 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
 #ifdef VMS
 # include <unixlib.h>          /* for getpid() */
 # include "vms-gtod.h"         /* for gettimeofday() */
-# if !defined(HAVE_UNAME) && (__VMS_VER >= 70000000)
-#  define HAVE_UNAME 1
-# endif /* !HAVE_UNAME */
 #endif /* VMS */
 
-# ifdef HAVE_UNAME
-#  include <sys/utsname.h>     /* for uname() */
-# endif /* HAVE_UNAME */
+#ifndef VMS
+# include <pwd.h>              /* for getpwuid() */
+#else /* VMS */
+# include "vms-pwd.h"
+#endif /* VMS */
+
+#ifdef HAVE_UNAME
+# include <sys/utsname.h>      /* for uname() */
+#endif /* HAVE_UNAME */
 
 #include <stdio.h>
 #include <X11/Xproto.h>                /* for CARD32 */
 # include <X11/extensions/scrnsaver.h>
 #endif /* HAVE_MIT_SAVER_EXTENSION */
 
-
-#ifdef HAVE_XHPDISABLERESET
-# include <X11/XHPlib.h>
-
- /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
-    or BadAccess errors occur.  (Ok for this to be global, since it
-    affects the whole machine, not just the current screen.) */
-  Bool hp_locked_p = False;
-
-#endif /* HAVE_XHPDISABLERESET */
+#ifdef HAVE_XF86VMODE
+# include <X11/extensions/xf86vmode.h>
+#endif /* HAVE_XF86VMODE */
 
 
 /* This file doesn't need the Xt headers, so stub these types out... */
 #include "visual.h"
 #include "fade.h"
 
+
 extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
 Atom XA_VROOT, XA_XSETROOT_ID;
-Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
-Atom XA_SCREENSAVER_TIME;
+Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
+Atom XA_SCREENSAVER_STATUS;
 
 
 extern saver_info *global_si_kludge;   /* I hate C so much... */
 
-
-static void store_activate_time (saver_info *si, Bool use_last_p);
+static void maybe_transfer_grabs (saver_screen_info *ssi,
+                                  Window old_w, Window new_w);
 
 #define ALL_POINTER_EVENTS \
        (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
@@ -80,50 +77,185 @@ static void store_activate_time (saver_info *si, Bool use_last_p);
         Button1MotionMask | Button2MotionMask | Button3MotionMask | \
         Button4MotionMask | Button5MotionMask | ButtonMotionMask)
 
-/* I don't really understand Sync vs Async, but these seem to work... */
-#define grab_kbd(dpy,win) \
-  XGrabKeyboard ((dpy), (win), True, GrabModeSync, GrabModeAsync, CurrentTime)
-#define grab_mouse(dpy,win,cursor) \
-  XGrabPointer ((dpy), (win), True, ALL_POINTER_EVENTS, \
-               GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime)
 
-void
-grab_keyboard_and_mouse (Display *dpy, Window window, Cursor cursor)
+static const char *
+grab_string(int status)
 {
-  Status status;
-  XSync (dpy, False);
+  switch (status)
+    {
+    case GrabSuccess:     return "GrabSuccess";
+    case AlreadyGrabbed:  return "AlreadyGrabbed";
+    case GrabInvalidTime: return "GrabInvalidTime";
+    case GrabNotViewable: return "GrabNotViewable";
+    case GrabFrozen:      return "GrabFrozen";
+    default:
+      {
+       static char foo[255];
+       sprintf(foo, "unknown status: %d", status);
+       return foo;
+      }
+    }
+}
+
+static int
+grab_kbd(saver_info *si, Window w)
+{
+  saver_preferences *p = &si->prefs;
+  int status = XGrabKeyboard (si->dpy, w, True,
+                             /* I don't really understand Sync vs Async,
+                                but these seem to work... */
+                             GrabModeSync, GrabModeAsync,
+                             CurrentTime);
+  if (status == GrabSuccess)
+    si->keyboard_grab_window = w;
+
+  if (p->verbose_p)
+    fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
+           blurb(), (unsigned long) w, grab_string(status));
+  return status;
+}
+
+
+static int
+grab_mouse (saver_info *si, Window w, Cursor cursor)
+{
+  saver_preferences *p = &si->prefs;
+  int status = XGrabPointer (si->dpy, w, True, ALL_POINTER_EVENTS,
+                            GrabModeAsync, GrabModeAsync, w,
+                            cursor, CurrentTime);
+  if (status == GrabSuccess)
+    si->mouse_grab_window = w;
+
+  if (p->verbose_p)
+    fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
+           blurb(), (unsigned long) w, grab_string(status));
+  return status;
+}
 
-  status = grab_kbd (dpy, window);
-  if (status != GrabSuccess)
-    {  /* try again in a second */
+
+static void
+ungrab_kbd(saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
+  XUngrabKeyboard(si->dpy, CurrentTime);
+  if (p->verbose_p)
+    fprintf(stderr, "%s: ungrabbing keyboard (was 0x%x).\n", blurb(),
+           (unsigned long) si->keyboard_grab_window);
+  si->keyboard_grab_window = 0;
+}
+
+
+static void
+ungrab_mouse(saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
+  XUngrabPointer(si->dpy, CurrentTime);
+  if (p->verbose_p)
+    fprintf(stderr, "%s: ungrabbing mouse (was 0x%x).\n", blurb(),
+           (unsigned long) si->mouse_grab_window);
+  si->mouse_grab_window = 0;
+}
+
+
+static Bool
+grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
+{
+  Status mstatus, kstatus;
+  int i;
+  int retries = 4;
+
+  for (i = 0; i < retries; i++)
+    {
+      XSync (si->dpy, False);
+      kstatus = grab_kbd (si, window);
+      if (kstatus == GrabSuccess)
+        break;
+
+      /* else, wait a second and try to grab again. */
       sleep (1);
-      status = grab_kbd (dpy, window);
-      if (status != GrabSuccess)
-       fprintf (stderr, "%s: couldn't grab keyboard!  (%d)\n",
-                progname, status);
     }
-  status = grab_mouse (dpy, window, cursor);
-  if (status != GrabSuccess)
-    {  /* try again in a second */
+
+  if (kstatus != GrabSuccess)
+    fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
+             blurb(), grab_string(kstatus));
+
+  for (i = 0; i < retries; i++)
+    {
+      XSync (si->dpy, False);
+      mstatus = grab_mouse (si, window, cursor);
+      if (mstatus == GrabSuccess)
+        break;
+
+      /* else, wait a second and try to grab again. */
       sleep (1);
-      status = grab_mouse (dpy, window, cursor);
-      if (status != GrabSuccess)
-       fprintf (stderr, "%s: couldn't grab pointer!  (%d)\n",
-                progname, status);
     }
+
+  if (mstatus != GrabSuccess)
+    fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
+             blurb(), grab_string(mstatus));
+
+  return (kstatus == GrabSuccess ||
+         mstatus == GrabSuccess);
 }
 
-void
-ungrab_keyboard_and_mouse (Display *dpy)
+static void
+ungrab_keyboard_and_mouse (saver_info *si)
 {
-  XUngrabPointer (dpy, CurrentTime);
-  XUngrabKeyboard (dpy, CurrentTime);
+  ungrab_mouse (si);
+  ungrab_kbd (si);
 }
 
 
-void
+int
+move_mouse_grab (saver_info *si, Window to, Cursor cursor)
+{
+  Window old = si->mouse_grab_window;
+
+  if (old == 0)
+    return grab_mouse (si, to, cursor);
+  else
+    {
+      saver_preferences *p = &si->prefs;
+      int status;
+
+      XSync (si->dpy, False);
+      XGrabServer (si->dpy);                   /* ############ DANGER! */
+      XSync (si->dpy, False);
+
+      if (p->verbose_p)
+        fprintf(stderr, "%s: grabbing server...\n", blurb());
+
+      ungrab_mouse (si);
+      status = grab_mouse (si, to, cursor);
+
+      if (status != GrabSuccess)   /* Augh! */
+        {
+          sleep (1);               /* Note dramatic evil of sleeping
+                                      with server grabbed. */
+          XSync (si->dpy, False);
+          status = grab_mouse (si, to, cursor);
+        }
+
+      if (status != GrabSuccess)   /* Augh!  Try to get the old one back... */
+        grab_mouse (si, to, cursor);
+
+      XUngrabServer (si->dpy);
+      XSync (si->dpy, False);                  /* ###### (danger over) */
+
+      if (p->verbose_p)
+        fprintf(stderr, "%s: ungrabbing server.\n", blurb());
+
+      return status;
+    }
+}
+
+
+/* Prints an error message to stderr and returns True if there is another
+   xscreensaver running already.  Silently returns False otherwise. */
+Bool
 ensure_no_screensaver_running (Display *dpy, Screen *screen)
 {
+  Bool status = 0;
   int i;
   Window root = RootWindowOfScreen (screen);
   Window root2, parent, *kids;
@@ -159,14 +291,15 @@ ensure_no_screensaver_running (Display *dpy, Screen *screen)
 
          fprintf (stderr,
       "%s: already running on display %s (window 0x%x)\n from process %s.\n",
-                  progname, DisplayString (dpy), (int) kids [i], id);
-         exit (1);
+                  blurb(), DisplayString (dpy), (int) kids [i], id);
+         status = True;
        }
     }
 
   if (kids) XFree ((char *) kids);
   XSync (dpy, False);
   XSetErrorHandler (old_handler);
+  return status;
 }
 
 
@@ -183,7 +316,7 @@ store_vroot_property (Display *dpy, Window win, Window value)
 #if 0
   if (p->verbose_p)
     fprintf (stderr,
-            "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname
+            "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb()
             win,
             (win == screensaver_window ? "ScreenSaver" :
              (win == real_vroot ? "VRoot" :
@@ -202,7 +335,7 @@ remove_vroot_property (Display *dpy, Window win)
 {
 #if 0
   if (p->verbose_p)
-    fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", progname, win, 
+    fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win, 
             (win == screensaver_window ? "ScreenSaver" :
              (win == real_vroot ? "VRoot" :
               (win == real_vroot_value ? "Vroot_value" : "???"))));
@@ -241,14 +374,14 @@ kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
          nitems == 1 && bytesafter == 0)
        {
          if (verbose_p)
-           printf ("%s: destroying xsetroot data (0x%lX).\n",
-                   progname, *dataP);
+           fprintf (stderr, "%s: destroying xsetroot data (0x%lX).\n",
+                    blurb(), *dataP);
          XKillClient (dpy, *dataP);
        }
       else
        fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
        %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
-                progname, (unsigned long) dataP, (dataP ? *dataP : 0), type,
+                blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
                 format, nitems, bytesafter);
     }
 }
@@ -266,6 +399,16 @@ save_real_vroot (saver_screen_info *ssi)
   Window root = RootWindowOfScreen (screen);
   Window root2, parent, *kids;
   unsigned int nkids;
+  XErrorHandler old_handler;
+
+  /* It's possible that a window might be deleted between our call to
+     XQueryTree() and our call to XGetWindowProperty().  Don't die if
+     that happens (but just ignore that window, it's not the one we're
+     interested in anyway.)
+   */
+  XSync (dpy, False);
+  old_handler = XSetErrorHandler (BadWindow_ehandler);
+  XSync (dpy, False);
 
   ssi->real_vroot = 0;
   ssi->real_vroot_value = 0;
@@ -294,13 +437,17 @@ save_real_vroot (saver_screen_info *ssi)
          if (*vrootP == ssi->screensaver_window) abort ();
          fprintf (stderr,
            "%s: more than one virtual root window found (0x%x and 0x%x).\n",
-                  progname, (int) ssi->real_vroot, (int) kids [i]);
+                  blurb(), (int) ssi->real_vroot, (int) kids [i]);
          exit (1);
        }
       ssi->real_vroot = kids [i];
       ssi->real_vroot_value = *vrootP;
     }
 
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+
   if (ssi->real_vroot)
     {
       handle_signals (si, True);
@@ -318,8 +465,9 @@ restore_real_vroot_2 (saver_screen_info *ssi)
   saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
   if (p->verbose_p && ssi->real_vroot)
-    printf ("%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
-           progname, (unsigned long) ssi->real_vroot);
+    fprintf (stderr,
+            "%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
+            blurb(), (unsigned long) ssi->real_vroot);
   remove_vroot_property (si->dpy, ssi->screensaver_window);
   if (ssi->real_vroot)
     {
@@ -444,7 +592,7 @@ restore_real_vroot_handler (int sig)
   signal (sig, SIG_DFL);
   if (restore_real_vroot_1 (si))
     fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
-            progname, signal_name(sig));
+            blurb(), signal_name(sig));
   kill (getpid (), sig);
 }
 
@@ -458,9 +606,9 @@ catch_signal (saver_info *si, int sig, Bool on_p)
       if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
        {
          char buf [255];
-         sprintf (buf, "%s: couldn't catch %s", progname, signal_name(sig));
+         sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
          perror (buf);
-         saver_exit (si, 1);
+         saver_exit (si, 1, 0);
        }
     }
 }
@@ -469,8 +617,8 @@ static void
 handle_signals (saver_info *si, Bool on_p)
 {
 #if 0
-  if (on_p) printf ("handling signals\n");
-  else printf ("unhandling signals\n");
+  if (on_p) fprintf (stderr, "handling signals\n");
+  else fprintf (stderr, "unhandling signals\n");
 #endif
 
   catch_signal (si, SIGHUP,  on_p);
@@ -478,7 +626,9 @@ handle_signals (saver_info *si, Bool on_p)
   catch_signal (si, SIGQUIT, on_p);
   catch_signal (si, SIGILL,  on_p);
   catch_signal (si, SIGTRAP, on_p);
+#ifdef SIGIOT
   catch_signal (si, SIGIOT,  on_p);
+#endif
   catch_signal (si, SIGABRT, on_p);
 #ifdef SIGEMT
   catch_signal (si, SIGEMT,  on_p);
@@ -502,10 +652,11 @@ handle_signals (saver_info *si, Bool on_p)
 }
 
 void
-saver_exit (saver_info *si, int status)
+saver_exit (saver_info *si, int status, const char *dump_core_reason)
 {
   saver_preferences *p = &si->prefs;
   static Bool exiting = False;
+  Bool bugp;
   Bool vrs;
 
   if (exiting)
@@ -515,11 +666,10 @@ saver_exit (saver_info *si, int status)
   
   vrs = restore_real_vroot_1 (si);
   emergency_kill_subproc (si);
+  shutdown_stderr (si);
 
-  if (vrs && (p->verbose_p || status != 0))
-    fprintf (real_stderr, "%s: vroot restored, exiting.\n", progname);
-  else if (p->verbose_p)
-    fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", progname);
+  if (p->verbose_p && vrs)
+    fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
 
   fflush(real_stdout);
 
@@ -528,11 +678,41 @@ saver_exit (saver_info *si, int status)
   else if (status == 1) status = -1;
 #endif
 
-#ifdef DEBUG
-  if (si->prefs.debug_p)
-    /* Do this to drop a core file, so that we can get a stack trace. */
-    abort();
-#endif
+  bugp = !!dump_core_reason;
+
+  if (si->prefs.debug_p && !dump_core_reason)
+    dump_core_reason = "because of -debug";
+
+  if (dump_core_reason)
+    {
+      /* Note that the Linux man page for setuid() says If uid is
+        different from the old effective uid, the process will be
+        forbidden from leaving core dumps.
+      */
+      char cwd[4096]; /* should really be PATH_MAX, but who cares. */
+      cwd[0] = 0;
+      fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
+             dump_core_reason);
+
+      if (bugp)
+       fprintf(real_stderr,
+               "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
+               "\t\tfor bug reporting information.\n\n",
+               blurb());
+
+# if defined(HAVE_GETCWD)
+      if (!getcwd (cwd, sizeof(cwd)))
+# elif defined(HAVE_GETWD)
+      if (!getwd (cwd))
+# endif
+        strcpy(cwd, "unknown.");
+
+      fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
+      describe_uids (si, real_stderr);
+
+      /* Do this to drop a core file, so that we can get a stack trace. */
+      abort();
+    }
 
   exit (status);
 }
@@ -553,12 +733,314 @@ window_exists_p (Display *dpy, Window window)
   return (xgwa.screen != 0);
 }
 
+static void
+store_saver_id (saver_screen_info *ssi)
+{
+  XClassHint class_hints;
+  saver_info *si = ssi->global;
+  unsigned long pid = (unsigned long) getpid ();
+  char buf[20];
+  struct passwd *p = getpwuid (getuid ());
+  const char *name, *host;
+  char *id;
+
+  /* First store the name and class on the window.
+   */
+  class_hints.res_name = progname;
+  class_hints.res_class = progclass;
+  XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
+  XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
+
+  /* Then store the xscreensaver version number.
+   */
+  XChangeProperty (si->dpy, ssi->screensaver_window,
+                  XA_SCREENSAVER_VERSION,
+                  XA_STRING, 8, PropModeReplace,
+                  (unsigned char *) si->version,
+                  strlen (si->version));
+
+  /* Now store the XSCREENSAVER_ID property, that says what user and host
+     xscreensaver is running as.
+   */
+
+  if (p && p->pw_name && *p->pw_name)
+    name = p->pw_name;
+  else if (p)
+    {
+      sprintf (buf, "%lu", (unsigned long) p->pw_uid);
+      name = buf;
+    }
+  else
+    name = "???";
+
+# if defined(HAVE_UNAME)
+  {
+    struct utsname uts;
+    if (uname (&uts) < 0)
+      host = "???";
+    else
+      host = uts.nodename;
+  }
+# elif defined(VMS)
+  host = getenv("SYS$NODE");
+# else  /* !HAVE_UNAME && !VMS */
+  host = "???";
+# endif /* !HAVE_UNAME && !VMS */
+
+  id = (char *) malloc (strlen(name) + strlen(host) + 50);
+  sprintf (id, "%lu (%s@%s)", pid, name, host);
+
+  XChangeProperty (si->dpy, ssi->screensaver_window,
+                  XA_SCREENSAVER_ID, XA_STRING,
+                  8, PropModeReplace,
+                  (unsigned char *) id, strlen (id));
+  free (id);
+}
+
+
+void
+store_saver_status (saver_info *si)
+{
+  CARD32 *status;
+  int size = si->nscreens + 2;
+  int i;
+
+  status = (CARD32 *) calloc (size, sizeof(CARD32));
+
+  status[0] = (CARD32) (si->screen_blanked_p
+                        ? (si->locked_p ? XA_LOCK : XA_BLANK)
+                        : 0);
+  status[1] = (CARD32) si->blank_time;
+
+  for (i = 0; i < si->nscreens; i++)
+    {
+      saver_screen_info *ssi = &si->screens[i];
+      status [2 + i] = ssi->current_hack + 1;
+    }
+
+  XChangeProperty (si->dpy,
+                   RootWindow (si->dpy, 0),  /* always screen #0 */
+                   XA_SCREENSAVER_STATUS,
+                   XA_INTEGER, 32, PropModeReplace,
+                   (unsigned char *) status, size);
+}
+
+
+
+/* Returns the area of the screen which the xscreensaver window should cover.
+   Normally this is the whole screen, but if the X server's root window is
+   actually larger than the monitor's displayable area, then we want to
+   operate in the currently-visible portion of the desktop instead.
+ */
+void
+get_screen_viewport (saver_screen_info *ssi,
+                     int *x_ret, int *y_ret,
+                     int *w_ret, int *h_ret,
+                     Bool verbose_p)
+{
+  int w = WidthOfScreen (ssi->screen);
+  int h = HeightOfScreen (ssi->screen);
+
+#ifdef HAVE_XF86VMODE
+  saver_info *si = ssi->global;
+  int screen_no = screen_number (ssi->screen);
+  int op, event, error;
+  int dot;
+  XF86VidModeModeLine ml;
+  int x, y;
+
+  /* Check for Xinerama first, because the VidModeExtension is broken
+     when Xinerama is present.  Wheee!
+   */
+
+  if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
+      XF86VidModeQueryExtension (si->dpy, &event, &error) &&
+      XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
+      XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
+    {
+      char msg[512];
+      *x_ret = x;
+      *y_ret = y;
+      *w_ret = ml.hdisplay;
+      *h_ret = ml.vdisplay;
+
+      if (*x_ret == 0 && *y_ret == 0 && *w_ret == w && *h_ret == h)
+        /* There is no viewport -- the screen does not scroll. */
+        return;
+
+
+      /* Apparently some versions of XFree86 return nonsense here!
+         I've had reports of 1024x768 viewports at -1936862040, -1953705044.
+         So, sanity-check the values and give up if they are out of range.
+       */
+      if (*x_ret <  0 || *x_ret >= w ||
+          *y_ret <  0 || *y_ret >= h ||
+          *w_ret <= 0 || *w_ret >  w ||
+          *h_ret <= 0 || *h_ret >  h)
+        {
+          static int warned_once = 0;
+          if (!warned_once)
+            {
+              fprintf (stderr, "\n"
+                  "%s: X SERVER BUG: %dx%d viewport at %d,%d is impossible.\n"
+                  "%s: The XVidMode server extension is returning nonsense.\n"
+                  "%s: Please report this bug to your X server vendor.\n\n",
+                       blurb(), *w_ret, *h_ret, *x_ret, *y_ret,
+                       blurb(), blurb());
+              warned_once = 1;
+            }
+          *x_ret = 0;
+          *y_ret = 0;
+          *w_ret = w;
+          *h_ret = h;
+          return;
+        }
+          
+
+      sprintf (msg, "%s: vp is %dx%d+%d+%d",
+               blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
+
+
+      /* Apparently, though the server stores the X position in increments of
+         1 pixel, it will only make changes to the *display* in some other
+         increment.  With XF86_SVGA on a Thinkpad, the display only updates
+         in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
+         pixels in 16-bit mode.  I don't know what it does in 24- and 32-bit
+         mode, because I don't have enough video memory to find out.
+
+         I consider it a bug that XF86VidModeGetViewPort() is telling me the
+         server's *target* scroll position rather than the server's *actual*
+         scroll position.  David Dawes agrees, and says they may fix this in
+         XFree86 4.0, but it's notrivial.
+
+         He also confirms that this behavior is server-dependent, so the
+         actual scroll position cannot be reliably determined by the client.
+         So... that means the only solution is to provide a ``sandbox''
+         around the blackout window -- we make the window be up to N pixels
+         larger than the viewport on both the left and right sides.  That
+         means some part of the outer edges of each hack might not be
+         visible, but screw it.
+
+         I'm going to guess that 16 pixels is enough, and that the Y dimension
+         doesn't have this problem.
+
+         The drawback of doing this, of course, is that some of the screenhacks
+         will still look pretty stupid -- for example, "slidescreen" will cut
+         off the left and right edges of the grid, etc.
+      */
+# define FUDGE 16
+      if (x > 0 && x < w - ml.hdisplay)  /* not at left edge or right edge */
+        {
+          /* Round X position down to next lower multiple of FUDGE.
+             Increase width by 2*FUDGE in case some server rounds up.
+           */
+          *x_ret = ((x - 1) / FUDGE) * FUDGE;
+          *w_ret += (FUDGE * 2);
+        }
+# undef FUDGE
+
+      if (*x_ret != x ||
+          *y_ret != y ||
+          *w_ret != ml.hdisplay ||
+          *h_ret != ml.vdisplay)
+        sprintf (msg + strlen(msg), "; fudged to %dx%d+%d+%d",
+                 *w_ret, *h_ret, *x_ret, *y_ret);
+
+      if (verbose_p)
+        fprintf (stderr, "%s.\n", msg);
+
+      return;
+    }
+
+#endif /* HAVE_XF86VMODE */
+
+  *x_ret = 0;
+  *y_ret = 0;
+  *w_ret = w;
+  *h_ret = h;
+}
+
+
+static Bool error_handler_hit_p = False;
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+  error_handler_hit_p = True;
+  return 0;
+}
+
+
+/* Returns True if successful, False if an X error occurred.
+   We need this because other programs might have done things to
+   our window that will cause XChangeWindowAttributes() to fail:
+   if that happens, we give up, destroy the window, and re-create
+   it.
+ */
+static Bool
+safe_XChangeWindowAttributes (Display *dpy, Window window,
+                              unsigned long mask,
+                              XSetWindowAttributes *attrs)
+{
+  XErrorHandler old_handler;
+  XSync (dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+  XChangeWindowAttributes (dpy, window, mask, attrs);
+
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+
+  return (!error_handler_hit_p);
+}
+
+
+/* This might not be necessary, but just in case. */
+static Bool
+safe_XConfigureWindow (Display *dpy, Window window,
+                       unsigned long mask, XWindowChanges *changes)
+{
+  XErrorHandler old_handler;
+  XSync (dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+  XConfigureWindow (dpy, window, mask, changes);
+
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+
+  return (!error_handler_hit_p);
+}
+
+/* This might not be necessary, but just in case. */
+static Bool
+safe_XDestroyWindow (Display *dpy, Window window)
+{
+  XErrorHandler old_handler;
+  XSync (dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+  XDestroyWindow (dpy, window);
+
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+
+  return (!error_handler_hit_p);
+}
+
+
 static void
 initialize_screensaver_window_1 (saver_screen_info *ssi)
 {
   saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
-  Bool install_cmap_p = ssi->install_cmap_p;
+  Bool install_cmap_p = ssi->install_cmap_p;   /* not p->install_cmap_p */
 
   /* This resets the screensaver window as fully as possible, since there's
      no way of knowing what some random client may have done to us in the
@@ -566,13 +1048,14 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
      its own set of problems...
    */
   XColor black;
-  XClassHint class_hints;
   XSetWindowAttributes attrs;
   unsigned long attrmask;
-  int width = WidthOfScreen (ssi->screen);
-  int height = HeightOfScreen (ssi->screen);
-  char id [2048];
+  int x, y, width, height;
   static Bool printed_visual_info = False;  /* only print the message once. */
+  Window horked_window = 0;
+
+  get_screen_viewport (ssi, &x, &y, &width, &height,
+                       (p->verbose_p && !si->screen_blanked_p));
 
   black.red = black.green = black.blue = 0;
 
@@ -580,13 +1063,15 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
     ssi->cmap = 0;
 
   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
+    /* It's not the default visual, so we have no choice but to install. */
     install_cmap_p = True;
 
   if (install_cmap_p)
     {
       if (! ssi->cmap)
        {
-         ssi->cmap = XCreateColormap (si->dpy, RootWindowOfScreen (ssi->screen),
+         ssi->cmap = XCreateColormap (si->dpy,
+                                      RootWindowOfScreen (ssi->screen),
                                      ssi->current_visual, AllocNone);
          if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
          ssi->black_pixel = black.pixel;
@@ -624,29 +1109,30 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
   attrs.backing_pixel = ssi->black_pixel;
   attrs.border_pixel = ssi->black_pixel;
 
-#ifdef DEBUG
   if (p->debug_p) width = width / 2;
-#endif /* DEBUG */
 
   if (!p->verbose_p || printed_visual_info)
     ;
   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
     {
-      fprintf (stderr, "%s: using default visual ", progname);
-      describe_visual (stderr, ssi->screen, ssi->current_visual);
+      fprintf (stderr, "%s: using default visual ", blurb());
+      describe_visual (stderr, ssi->screen, ssi->current_visual,
+                      install_cmap_p);
     }
   else
     {
-      fprintf (stderr, "%s: using visual:   ", progname);
-      describe_visual (stderr, ssi->screen, ssi->current_visual);
-      fprintf (stderr, "%s: default visual: ", progname);
+      fprintf (stderr, "%s: using visual:   ", blurb());
+      describe_visual (stderr, ssi->screen, ssi->current_visual,
+                      install_cmap_p);
+      fprintf (stderr, "%s: default visual: ", blurb());
       describe_visual (stderr, ssi->screen,
-                      DefaultVisualOfScreen (ssi->screen));
+                      DefaultVisualOfScreen (ssi->screen),
+                      ssi->install_cmap_p);
     }
   printed_visual_info = True;
 
 #ifdef HAVE_MIT_SAVER_EXTENSION
-  if (p->use_mit_saver_extension)
+  if (si->using_mit_saver_extension)
     {
       XScreenSaverInfo *info;
       Window root = RootWindowOfScreen (ssi->screen);
@@ -700,92 +1186,68 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
     {
       XWindowChanges changes;
       unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
-      changes.x = 0;
-      changes.y = 0;
+      changes.x = x;
+      changes.y = y;
       changes.width = width;
       changes.height = height;
       changes.border_width = 0;
 
-      XConfigureWindow (si->dpy, ssi->screensaver_window,
-                       changesmask, &changes);
-      XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
-                              attrmask, &attrs);
+      if (! (safe_XConfigureWindow (si->dpy, ssi->screensaver_window,
+                                  changesmask, &changes) &&
+             safe_XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
+                                           attrmask, &attrs)))
+        {
+          horked_window = ssi->screensaver_window;
+          ssi->screensaver_window = 0;
+        }
     }
-  else
+
+  if (!ssi->screensaver_window)
     {
       ssi->screensaver_window =
-       XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen), 0, 0,
-                      width, height, 0, ssi->current_depth, InputOutput,
+       XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
+                       x, y, width, height,
+                       0, ssi->current_depth, InputOutput,
                       ssi->current_visual, attrmask, &attrs);
+
       reset_stderr (ssi);
-      store_activate_time(si, True);
+
+      if (horked_window)
+        {
+          fprintf (stderr,
+            "%s: someone horked our saver window (0x%lx)!  Recreating it...\n",
+                   blurb(), (unsigned long) horked_window);
+          maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window);
+          safe_XDestroyWindow (si->dpy, horked_window);
+          horked_window = 0;
+        }
+
       if (p->verbose_p)
        fprintf (stderr, "%s: saver window is 0x%lx.\n",
-                progname, (unsigned long) ssi->screensaver_window);
+                blurb(), (unsigned long) ssi->screensaver_window);
     }
 
-#ifdef HAVE_MIT_SAVER_EXTENSION
-  if (!p->use_mit_saver_extension ||
-      window_exists_p (si->dpy, ssi->screensaver_window))
-    /* When using the MIT-SCREEN-SAVER extension, the window pointed to
-       by screensaver_window only exists while the saver is active.
-       So we must be careful to only try and manipulate it while it
-       exists...
-       (#### The above comment would be true if the MIT extension actually
-       worked, but it's not true today -- see `server_mit_saver_window'.)
-     */
-#endif /* HAVE_MIT_SAVER_EXTENSION */
-    {
-      class_hints.res_name = progname;
-      class_hints.res_class = progclass;
-      XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
-      XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
-      XChangeProperty (si->dpy, ssi->screensaver_window,
-                      XA_SCREENSAVER_VERSION,
-                      XA_STRING, 8, PropModeReplace,
-                      (unsigned char *) si->version,
-                      strlen (si->version));
-
-      sprintf (id, "%lu on host ", (unsigned long) getpid ());
+  store_saver_id (ssi);       /* store window name and IDs */
 
-# if defined(HAVE_UNAME)
-      {
-       struct utsname uts;
-       if (uname (&uts) < 0)
-         strcat (id, "???");
-       else
-         strcat (id, uts.nodename);
-      }
-# elif defined(VMS)
-      strcat (id, getenv("SYS$NODE"));
-# else  /* !HAVE_UNAME && !VMS */
-      strcat (id, "???");
-# endif /* !HAVE_UNAME && !VMS */
-
-      XChangeProperty (si->dpy, ssi->screensaver_window,
-                      XA_SCREENSAVER_ID, XA_STRING,
-                      8, PropModeReplace, (unsigned char *) id, strlen (id));
+  if (!ssi->cursor)
+    {
+      Pixmap bit;
+      bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
+                                        "\000", 1, 1,
+                                        BlackPixelOfScreen (ssi->screen),
+                                        BlackPixelOfScreen (ssi->screen),
+                                        1);
+      ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
+                                        0, 0);
+      XFreePixmap (si->dpy, bit);
+    }
 
-      if (!ssi->cursor)
-       {
-         Pixmap bit;
-         bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
-                                            "\000", 1, 1,
-                                            BlackPixelOfScreen (ssi->screen),
-                                            BlackPixelOfScreen (ssi->screen),
-                                            1);
-         ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
-                                            0, 0);
-         XFreePixmap (si->dpy, bit);
-       }
+  XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
 
-      XSetWindowBackground (si->dpy, ssi->screensaver_window,
-                           ssi->black_pixel);
-      if (si->demo_mode_p)
-       XUndefineCursor (si->dpy, ssi->screensaver_window);
-      else
-       XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
-    }
+  if (si->demoing_p)
+    XUndefineCursor (si->dpy, ssi->screensaver_window);
+  else
+    XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
 }
 
 void
@@ -804,12 +1266,19 @@ raise_window (saver_info *si,
   saver_preferences *p = &si->prefs;
   int i;
 
-  initialize_screensaver_window (si);
+  if (si->demoing_p)
+    inhibit_fade = True;
+
+  if (si->emergency_lock_p)
+    inhibit_fade = True;
+
+  if (!dont_clear)
+    initialize_screensaver_window (si);
+
   reset_watchdog_timer (si, True);
 
-  if (p->fade_p && !inhibit_fade && !si->demo_mode_p)
+  if (p->fade_p && si->fading_possible_p && !inhibit_fade)
     {
-      int grabbed = -1;
       Window *current_windows = (Window *)
        calloc(sizeof(Window), si->nscreens);
       Colormap *current_maps = (Colormap *)
@@ -828,34 +1297,33 @@ raise_window (saver_info *si,
                                ssi->black_pixel);
        }
 
-      if (p->verbose_p) fprintf (stderr, "%s: fading... ", progname);
+      if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
 
-      XGrabServer (si->dpy);
+      XGrabServer (si->dpy);                   /* ############ DANGER! */
 
-      for (i = 0; i < si->nscreens; i++)
-       {
-         saver_screen_info *ssi = &si->screens[i];
-
-         /* grab and blacken mouse on the root window (saver not mapped yet)
-          */
-         if (grabbed != GrabSuccess)
-           grabbed = grab_mouse (si->dpy, ssi->screensaver_window,
-                                 (si->demo_mode_p ? 0 : ssi->cursor));
-
-         if (!dont_clear || ssi->stderr_overlay_window)
-           /* Do this before the fade, since the stderr cmap won't fade
-              even if we uninstall it (beats me...) */
-           clear_stderr (ssi);
-       }
+      /* Clear the stderr layer on each screen.
+       */
+      if (!dont_clear)
+       for (i = 0; i < si->nscreens; i++)
+         {
+           saver_screen_info *ssi = &si->screens[i];
+           if (ssi->stderr_overlay_window)
+             /* Do this before the fade, since the stderr cmap won't fade
+                even if we uninstall it (beats me...) */
+             clear_stderr (ssi);
+         }
 
+      /* Note!  The server is grabbed, and this will take several seconds
+        to complete! */
       fade_screens (si->dpy, current_maps, current_windows,
-                   p->fade_seconds, p->fade_ticks, True, !dont_clear);
+                   p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
+
       free(current_maps);
       free(current_windows);
       current_maps = 0;
       current_windows = 0;
 
-      if (p->verbose_p) fprintf (stderr, "fading done.\n");
+      if (p->verbose_p) fprintf (stderr, "%s: fading done.\n", blurb());
 
 #ifdef HAVE_MIT_SAVER_EXTENSION
       for (i = 0; i < si->nscreens; i++)
@@ -867,9 +1335,8 @@ raise_window (saver_info *si,
        }
 #endif /* HAVE_MIT_SAVER_EXTENSION */
 
-      if (grabbed == GrabSuccess)
-       XUngrabPointer (si->dpy, CurrentTime);
       XUngrabServer (si->dpy);
+      XSync (si->dpy, False);                  /* ###### (danger over) */
     }
   else
     {
@@ -897,10 +1364,34 @@ raise_window (saver_info *si,
     }
 }
 
-void
+Bool
 blank_screen (saver_info *si)
 {
   int i;
+  Bool ok;
+
+  /* Note: we do our grabs on the root window, not on the screensaver window.
+     If we grabbed on the saver window, then the demo mode and lock dialog
+     boxes wouldn't get any events.
+   */
+  ok = grab_keyboard_and_mouse (si,
+                                /*si->screens[0].screensaver_window,*/
+                                RootWindowOfScreen(si->screens[0].screen),
+                                (si->demoing_p
+                                 ? 0
+                                 : si->screens[0].cursor));
+
+
+  if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
+    /* If we're using a server extension, then failure to get a grab is
+       not a big deal -- even without the grab, we will still be able
+       to un-blank when there is user activity, since the server will
+       tell us. */
+    ok = True;
+
+  if (!ok)
+    return False;
+
   for (i = 0; i < si->nscreens; i++)
     {
       saver_screen_info *ssi = &si->screens[i];
@@ -909,35 +1400,46 @@ blank_screen (saver_info *si)
       store_vroot_property (si->dpy,
                            ssi->screensaver_window,
                            ssi->screensaver_window);
+
+#ifdef HAVE_XF86VMODE
+      {
+        int ev, er;
+        if (!XF86VidModeQueryExtension (si->dpy, &ev, &er) ||
+            !XF86VidModeGetViewPort (si->dpy, i,
+                                     &ssi->blank_vp_x,
+                                     &ssi->blank_vp_y))
+          ssi->blank_vp_x = ssi->blank_vp_y = -1;
+      }
+#endif /* HAVE_XF86VMODE */
     }
-  store_activate_time (si, si->screen_blanked_p);
+
   raise_window (si, False, False, False);
-  /* #### */
-  grab_keyboard_and_mouse (si->dpy, si->screens[0].screensaver_window,
-                          (si->demo_mode_p ? 0 : si->screens[0].cursor));
-#ifdef HAVE_XHPDISABLERESET
-  if (si->locked_p && !hp_locked_p)
-    {
-      XHPDisableReset (si->dpy);       /* turn off C-Sh-Reset */
-      hp_locked_p = True;
-    }
-#endif
 
   si->screen_blanked_p = True;
+  si->blank_time = time ((time_t) 0);
+  si->last_wall_clock_time = 0;
+
+  store_saver_status (si);  /* store blank time */
+
+  return True;
 }
 
+
 void
 unblank_screen (saver_info *si)
 {
   saver_preferences *p = &si->prefs;
+  Bool unfade_p = (si->fading_possible_p && p->unfade_p);
   int i;
 
-  store_activate_time (si, True);
+  monitor_power_on (si);
   reset_watchdog_timer (si, False);
 
-  if (p->unfade_p && !si->demo_mode_p)
+  if (si->demoing_p)
+    unfade_p = False;
+
+  if (unfade_p)
     {
-      int grabbed = -1;
       Window *current_windows = (Window *)
        calloc(sizeof(Window), si->nscreens);
 
@@ -951,32 +1453,33 @@ unblank_screen (saver_info *si)
                                ssi->black_pixel);
        }
 
-      if (p->verbose_p) fprintf (stderr, "%s: unfading... ", progname);
+      if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
+
 
       XSync (si->dpy, False);
-      XGrabServer (si->dpy);
+      XGrabServer (si->dpy);                   /* ############ DANGER! */
       XSync (si->dpy, False);
+
+      /* Clear the stderr layer on each screen.
+       */
       for (i = 0; i < si->nscreens; i++)
        {
          saver_screen_info *ssi = &si->screens[i];
-         if (grabbed != GrabSuccess)
-           grabbed = grab_mouse (si->dpy, RootWindowOfScreen (ssi->screen),
-                                 0);
          clear_stderr (ssi);
        }
+
       XUngrabServer (si->dpy);
-      XSync (si->dpy, False);
+      XSync (si->dpy, False);                  /* ###### (danger over) */
+
 
       fade_screens (si->dpy, 0, current_windows,
-                   p->fade_seconds, p->fade_ticks,
+                   p->fade_seconds/1000, p->fade_ticks,
                    False, False);
 
       free(current_windows);
       current_windows = 0;
 
-      if (p->verbose_p) fprintf (stderr, "unfading done.\n");
-      if (grabbed == GrabSuccess)
-       XUngrabPointer (si->dpy, CurrentTime);
+      if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
     }
   else
     {
@@ -1023,19 +1526,10 @@ unblank_screen (saver_info *si)
       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
     }
 
-  store_activate_time(si, False);  /* store unblank time */
-
-  ungrab_keyboard_and_mouse (si->dpy);
+  store_saver_status (si);  /* store unblank time */
+  ungrab_keyboard_and_mouse (si);
   restore_real_vroot (si);
 
-#ifdef HAVE_XHPDISABLERESET
-  if (hp_locked_p)
-    {
-      XHPEnableReset (si->dpy);        /* turn C-Sh-Reset back on */
-      hp_locked_p = False;
-    }
-#endif
-
   /* Unmap the windows a second time, dammit -- just to avoid a race
      with the screen-grabbing hacks.  (I'm not sure if this is really
      necessary; I'm stabbing in the dark now.)
@@ -1044,29 +1538,54 @@ unblank_screen (saver_info *si)
     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
 
   si->screen_blanked_p = False;
+  si->blank_time = time ((time_t) 0);
+  si->last_wall_clock_time = 0;
+
+  store_saver_status (si);  /* store unblank time */
 }
 
 
+/* Transfer any grabs from the old window to the new.
+   Actually I think none of this is necessary, since we always
+   hold our grabs on the root window, but I wrote this before
+   re-discovering that...
+ */
 static void
-store_activate_time (saver_info *si, Bool use_last_p)
+maybe_transfer_grabs (saver_screen_info *ssi,
+                      Window old_w, Window new_w)
 {
-  static time_t last_time = 0;
-  time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
-  CARD32 now32 = (CARD32) now;
-  int i;
-  last_time = now;
+  saver_info *si = ssi->global;
 
-  for (i = 0; i < si->nscreens; i++)
+  /* If the old window held our mouse grab, transfer the grab to the new
+     window.  (Grab the server while so doing, to avoid a race condition.)
+   */
+  if (old_w == si->mouse_grab_window)
     {
-      saver_screen_info *ssi = &si->screens[i];
-      if (!ssi->screensaver_window) continue;
-      XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
-                      XA_INTEGER, 32, PropModeReplace,
-                      (unsigned char *) &now32, 1);
+      XGrabServer (si->dpy);           /* ############ DANGER! */
+      ungrab_mouse (si);
+      grab_mouse (si, ssi->screensaver_window,
+                  (si->demoing_p
+                   ? 0
+                   : ssi->cursor));
+      XUngrabServer (si->dpy);
+      XSync (si->dpy, False);          /* ###### (danger over) */
+    }
+
+  /* If the old window held our keyboard grab, transfer the grab to the new
+     window.  (Grab the server while so doing, to avoid a race condition.)
+   */
+  if (old_w == si->keyboard_grab_window)
+    {
+      XGrabServer (si->dpy);           /* ############ DANGER! */
+      ungrab_kbd(si);
+      grab_kbd(si, ssi->screensaver_window);
+      XUngrabServer (si->dpy);
+      XSync (si->dpy, False);          /* ###### (danger over) */
     }
 }
 
 
+
 Bool
 select_visual (saver_screen_info *ssi, const char *visual_name)
 {
@@ -1074,22 +1593,37 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
   saver_preferences *p = &si->prefs;
   Bool install_cmap_p = p->install_cmap_p;
   Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
-  Visual *new_v;
+  Visual *new_v = 0;
   Bool got_it;
 
   if (visual_name && *visual_name)
     {
-      if (!strcmp(visual_name, "default-i"))
+      if (!strcmp(visual_name, "default-i") ||
+          !strcmp(visual_name, "Default-i") ||
+          !strcmp(visual_name, "Default-I")
+          )
        {
          visual_name = "default";
          install_cmap_p = True;
        }
-      else if (!strcmp(visual_name, "default-n"))
+      else if (!strcmp(visual_name, "default-n") ||
+               !strcmp(visual_name, "Default-n") ||
+               !strcmp(visual_name, "Default-N"))
        {
          visual_name = "default";
          install_cmap_p = False;
        }
-      new_v = get_visual (ssi->screen, visual_name, True, False);
+      else if (!strcmp(visual_name, "gl") ||
+               !strcmp(visual_name, "Gl") ||
+               !strcmp(visual_name, "GL"))
+        {
+          new_v = ssi->best_gl_visual;
+          if (!new_v && p->verbose_p)
+            fprintf (stderr, "%s: no GL visuals.\n", progname);
+        }
+
+      if (!new_v)
+        new_v = get_visual (ssi->screen, visual_name, True, False);
     }
   else
     {
@@ -1099,6 +1633,7 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
   got_it = !!new_v;
 
   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
+    /* It's not the default visual, so we have no choice but to install. */
     install_cmap_p = True;
 
   ssi->install_cmap_p = install_cmap_p;
@@ -1112,16 +1647,12 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
 
       if (p->verbose_p)
        {
+         fprintf (stderr, "%s: switching to visual ", blurb());
+         describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
 #if 0
-         fprintf (stderr, "%s: switching visuals\tfrom: ", progname);
-         describe_visual (stderr, ssi->screen, ssi->current_visual);
-         fprintf (stderr, "\t\t\t\tto:   ");
-         describe_visual (stderr, ssi->screen, new_v);
-         fprintf (stderr, "\t\t\t\t install cmap:   %s\n",
-                  (install_cmap_p ? "yes" : "no"));
-#else
-         fprintf (stderr, "%s: switching to visual ", progname);
-         describe_visual (stderr, ssi->screen, new_v);
+         fprintf (stderr, "%s:                from ", blurb());
+         describe_visual (stderr, ssi->screen, ssi->current_visual,
+                          was_installed_p);
 #endif
        }
 
@@ -1132,12 +1663,27 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
       ssi->screensaver_window = 0;
 
       initialize_screensaver_window_1 (ssi);
+
+      /* stderr_overlay_window is a child of screensaver_window, so we need
+        to destroy that as well (actually, we just need to invalidate and
+        drop our pointers to it, but this will destroy it, which is ok so
+        long as it happens before old_w itself is destroyed.) */
+      reset_stderr (ssi);
+
       raise_window (si, True, True, False);
       store_vroot_property (si->dpy,
                            ssi->screensaver_window, ssi->screensaver_window);
-      store_activate_time (si, True);
 
+      /* Transfer any grabs from the old window to the new. */
+      maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window);
+
+      /* Now we can destroy the old window without horking our grabs. */
       XDestroyWindow (si->dpy, old_w);
+
+      if (p->verbose_p)
+       fprintf (stderr, "%s: destroyed old saver window 0x%lx.\n",
+                blurb(), (unsigned long) old_w);
+
       if (old_c &&
          old_c != DefaultColormapOfScreen (ssi->screen) &&
          old_c != ssi->demo_cmap)