http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.03.tar.gz
[xscreensaver] / driver / windows.c
index 21eb6a39aac25a71b15d7aed591f5fdc2d2c78fd..cbc72d76b698bc8322a2b435f3f521a9f644f824 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@jwz.org>
+ * 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
@@ -36,6 +36,8 @@
 #include <X11/Xatom.h>
 #include <X11/Xos.h>           /* for time() */
 #include <signal.h>            /* for the signal names */
+#include <time.h>
+#include <sys/time.h>
 
 #ifdef HAVE_MIT_SAVER_EXTENSION
 # include <X11/extensions/scrnsaver.h>
 # include <X11/extensions/xf86vmode.h>
 #endif /* HAVE_XF86VMODE */
 
+#ifdef HAVE_XINERAMA
+# include <X11/extensions/Xinerama.h>
+#endif /* HAVE_XINERAMA */
+
 
 /* This file doesn't need the Xt headers, so stub these types out... */
 #undef XtPointer
 
 extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
-Atom XA_VROOT, XA_XSETROOT_ID;
+Atom XA_VROOT, XA_XSETROOT_ID, XA_ESETROOT_PMAP_ID, XA_XROOTPMAP_ID;
 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 maybe_transfer_grabs (saver_screen_info *ssi,
+                                  Window old_w, Window new_w, int new_screen);
 
 #define ALL_POINTER_EVENTS \
        (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
@@ -96,7 +104,7 @@ grab_string(int status)
 }
 
 static int
-grab_kbd(saver_info *si, Window w)
+grab_kbd(saver_info *si, Window w, int screen_no)
 {
   saver_preferences *p = &si->prefs;
   int status = XGrabKeyboard (si->dpy, w, True,
@@ -105,28 +113,34 @@ grab_kbd(saver_info *si, Window w)
                              GrabModeSync, GrabModeAsync,
                              CurrentTime);
   if (status == GrabSuccess)
-    si->keyboard_grab_window = w;
+    {
+      si->keyboard_grab_window = w;
+      si->keyboard_grab_screen = screen_no;
+    }
 
   if (p->verbose_p)
-    fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
-           blurb(), (unsigned long) w, grab_string(status));
+    fprintf(stderr, "%s: %d: grabbing keyboard on 0x%x... %s.\n",
+           blurb(), screen_no, (unsigned long) w, grab_string(status));
   return status;
 }
 
 
 static int
-grab_mouse (saver_info *si, Window w, Cursor cursor)
+grab_mouse (saver_info *si, Window w, Cursor cursor, int screen_no)
 {
   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;
+    {
+      si->mouse_grab_window = w;
+      si->mouse_grab_screen = screen_no;
+    }
 
   if (p->verbose_p)
-    fprintf(stderr, "%s: grabbing mouse on 0x%x... %s.\n",
-           blurb(), (unsigned long) w, grab_string(status));
+    fprintf(stderr, "%s: %d: grabbing mouse on 0x%x... %s.\n",
+           blurb(), screen_no, (unsigned long) w, grab_string(status));
   return status;
 }
 
@@ -137,8 +151,9 @@ 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);
+    fprintf(stderr, "%s: %d: ungrabbing keyboard (was 0x%x).\n",
+            blurb(), si->keyboard_grab_screen,
+            (unsigned long) si->keyboard_grab_window);
   si->keyboard_grab_window = 0;
 }
 
@@ -149,38 +164,51 @@ 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);
+    fprintf(stderr, "%s: %d: ungrabbing mouse (was 0x%x).\n",
+            blurb(), si->mouse_grab_screen,
+            (unsigned long) si->mouse_grab_window);
   si->mouse_grab_window = 0;
 }
 
 
 static Bool
-grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
+grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor,
+                         int screen_no)
 {
-  Status mstatus, kstatus;
-  XSync (si->dpy, False);
+  Status mstatus = 0, kstatus = 0;
+  int i;
+  int retries = 4;
 
-  kstatus = grab_kbd (si, window);
-  if (kstatus != GrabSuccess)
-    {  /* try again in a second */
+  for (i = 0; i < retries; i++)
+    {
+      XSync (si->dpy, False);
+      kstatus = grab_kbd (si, window, screen_no);
+      if (kstatus == GrabSuccess)
+        break;
+
+      /* else, wait a second and try to grab again. */
       sleep (1);
-      kstatus = grab_kbd (si, window);
-      if (kstatus != GrabSuccess)
-       fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
-                blurb(), grab_string(kstatus));
     }
 
-  mstatus = grab_mouse (si, window, cursor);
-  if (mstatus != 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, screen_no);
+      if (mstatus == GrabSuccess)
+        break;
+
+      /* else, wait a second and try to grab again. */
       sleep (1);
-      mstatus = grab_mouse (si, window, cursor);
-      if (mstatus != GrabSuccess)
-       fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
-                blurb(), grab_string(mstatus));
     }
 
+  if (mstatus != GrabSuccess)
+    fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
+             blurb(), grab_string(mstatus));
+
   return (kstatus == GrabSuccess ||
          mstatus == GrabSuccess);
 }
@@ -194,12 +222,12 @@ ungrab_keyboard_and_mouse (saver_info *si)
 
 
 int
-move_mouse_grab (saver_info *si, Window to, Cursor cursor)
+move_mouse_grab (saver_info *si, Window to, Cursor cursor, int to_screen_no)
 {
   Window old = si->mouse_grab_window;
 
   if (old == 0)
-    return grab_mouse (si, to, cursor);
+    return grab_mouse (si, to, cursor, to_screen_no);
   else
     {
       saver_preferences *p = &si->prefs;
@@ -213,18 +241,18 @@ move_mouse_grab (saver_info *si, Window to, Cursor cursor)
         fprintf(stderr, "%s: grabbing server...\n", blurb());
 
       ungrab_mouse (si);
-      status = grab_mouse (si, to, cursor);
+      status = grab_mouse (si, to, cursor, to_screen_no);
 
       if (status != GrabSuccess)   /* Augh! */
         {
           sleep (1);               /* Note dramatic evil of sleeping
                                       with server grabbed. */
           XSync (si->dpy, False);
-          status = grab_mouse (si, to, cursor);
+          status = grab_mouse (si, to, cursor, to_screen_no);
         }
 
       if (status != GrabSuccess)   /* Augh!  Try to get the old one back... */
-        grab_mouse (si, to, cursor);
+        grab_mouse (si, old, cursor, to_screen_no);
 
       XUngrabServer (si->dpy);
       XSync (si->dpy, False);                  /* ###### (danger over) */
@@ -331,8 +359,12 @@ remove_vroot_property (Display *dpy, Window win)
 }
 
 
+static Bool safe_XKillClient (Display *dpy, XID id);
+
 static void
-kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
+kill_xsetroot_data_1 (Display *dpy, Window window,
+                      Atom prop, const char *atom_name,
+                      Bool verbose_p)
 {
   Atom type;
   int format;
@@ -350,8 +382,12 @@ kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
      delete of the _XSETROOT_ID property, and if it held a pixmap, then we
      cause the RetainPermanent resources of the client which created it
      (and which no longer exists) to be freed.
+
+     Update: it seems that Gnome and KDE do this same trick, but with the
+     properties "ESETROOT_PMAP_ID" and/or "_XROOTPMAP_ID" instead of
+     "_XSETROOT_ID".  So, we'll kill those too.
    */
-  if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
+  if (XGetWindowProperty (dpy, window, prop, 0, 1,
                          True, AnyPropertyType, &type, &format, &nitems, 
                          &bytesafter, (unsigned char **) &dataP)
       == Success
@@ -361,20 +397,31 @@ kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
          nitems == 1 && bytesafter == 0)
        {
          if (verbose_p)
-           fprintf (stderr, "%s: destroying xsetroot data (0x%lX).\n",
-                    blurb(), *dataP);
-         XKillClient (dpy, *dataP);
+           fprintf (stderr, "%s: destroying %s data (0x%lX).\n",
+                    blurb(), atom_name, *dataP);
+         safe_XKillClient (dpy, *dataP);
        }
       else
-       fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
-       %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
-                blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
+       fprintf (stderr,
+                 "%s: deleted unrecognised %s property: \n"
+                 "\t%lu, %lu; type: %lu, format: %d, "
+                 "nitems: %lu, bytesafter %ld\n",
+                blurb(), atom_name,
+                 (unsigned long) dataP, (dataP ? *dataP : 0), type,
                 format, nitems, bytesafter);
     }
 }
 
 
-static void handle_signals (saver_info *si, Bool on_p);
+static void
+kill_xsetroot_data (Display *dpy, Window w, Bool verbose_p)
+{
+  kill_xsetroot_data_1 (dpy, w, XA_XSETROOT_ID, "_XSETROOT_ID", verbose_p);
+  kill_xsetroot_data_1 (dpy, w, XA_ESETROOT_PMAP_ID, "ESETROOT_PMAP_ID",
+                        verbose_p);
+  kill_xsetroot_data_1 (dpy, w, XA_XROOTPMAP_ID, "_XROOTPMAP_ID", verbose_p);
+}
+
 
 static void
 save_real_vroot (saver_screen_info *ssi)
@@ -437,7 +484,6 @@ save_real_vroot (saver_screen_info *ssi)
 
   if (ssi->real_vroot)
     {
-      handle_signals (si, True);
       remove_vroot_property (si->dpy, ssi->real_vroot);
       XSync (dpy, False);
     }
@@ -447,7 +493,7 @@ save_real_vroot (saver_screen_info *ssi)
 
 
 static Bool
-restore_real_vroot_2 (saver_screen_info *ssi)
+restore_real_vroot_1 (saver_screen_info *ssi)
 {
   saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
@@ -470,27 +516,20 @@ restore_real_vroot_2 (saver_screen_info *ssi)
   return False;
 }
 
-static Bool
-restore_real_vroot_1 (saver_info *si)
+Bool
+restore_real_vroot (saver_info *si)
 {
   int i;
   Bool did_any = False;
   for (i = 0; i < si->nscreens; i++)
     {
       saver_screen_info *ssi = &si->screens[i];
-      if (restore_real_vroot_2 (ssi))
+      if (restore_real_vroot_1 (ssi))
        did_any = True;
     }
   return did_any;
 }
 
-void
-restore_real_vroot (saver_info *si)
-{
-  if (restore_real_vroot_1 (si))
-    handle_signals (si, False);
-}
-
 \f
 /* Signal hackery to ensure that the vroot doesn't get left in an 
    inconsistent state
@@ -577,65 +616,102 @@ restore_real_vroot_handler (int sig)
   saver_info *si = global_si_kludge;   /* I hate C so much... */
 
   signal (sig, SIG_DFL);
-  if (restore_real_vroot_1 (si))
+  if (restore_real_vroot (si))
     fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
             blurb(), signal_name(sig));
   kill (getpid (), sig);
 }
 
 static void
-catch_signal (saver_info *si, int sig, Bool on_p)
+catch_signal (saver_info *si, int sig, RETSIGTYPE (*handler) (int))
 {
-  if (! on_p)
-    signal (sig, SIG_DFL);
-  else
+# ifdef HAVE_SIGACTION
+
+  struct sigaction a;
+  a.sa_handler = handler;
+  sigemptyset (&a.sa_mask);
+  a.sa_flags = 0;
+
+  /* On Linux 2.4.9 (at least) we need to tell the kernel to not mask delivery
+     of this signal from inside its handler, or else when we execvp() the
+     process again, it starts up with SIGHUP blocked, meaning that killing
+     it with -HUP only works *once*.  You'd think that execvp() would reset
+     all the signal masks, but it doesn't.
+   */
+#  if defined(SA_NOMASK)
+  a.sa_flags |= SA_NOMASK;
+#  elif defined(SA_NODEFER)
+  a.sa_flags |= SA_NODEFER;
+#  endif
+
+  if (sigaction (sig, &a, 0) < 0)
+# else  /* !HAVE_SIGACTION */
+  if (((long) signal (sig, handler)) == -1L)
+# endif /* !HAVE_SIGACTION */
     {
-      if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
-       {
-         char buf [255];
-         sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
-         perror (buf);
-         saver_exit (si, 1, 0);
-       }
+      char buf [255];
+      sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
+      perror (buf);
+      saver_exit (si, 1, 0);
     }
 }
 
-static void
-handle_signals (saver_info *si, Bool on_p)
+static RETSIGTYPE saver_sighup_handler (int sig);
+
+void
+handle_signals (saver_info *si)
 {
-#if 0
-  if (on_p) fprintf (stderr, "handling signals\n");
-  else fprintf (stderr, "unhandling signals\n");
+  catch_signal (si, SIGHUP, saver_sighup_handler);
+
+  catch_signal (si, SIGINT,  restore_real_vroot_handler);
+  catch_signal (si, SIGQUIT, restore_real_vroot_handler);
+  catch_signal (si, SIGILL,  restore_real_vroot_handler);
+  catch_signal (si, SIGTRAP, restore_real_vroot_handler);
+#ifdef SIGIOT
+  catch_signal (si, SIGIOT,  restore_real_vroot_handler);
 #endif
-
-  catch_signal (si, SIGHUP,  on_p);
-  catch_signal (si, SIGINT,  on_p);
-  catch_signal (si, SIGQUIT, on_p);
-  catch_signal (si, SIGILL,  on_p);
-  catch_signal (si, SIGTRAP, on_p);
-  catch_signal (si, SIGIOT,  on_p);
-  catch_signal (si, SIGABRT, on_p);
+  catch_signal (si, SIGABRT, restore_real_vroot_handler);
 #ifdef SIGEMT
-  catch_signal (si, SIGEMT,  on_p);
+  catch_signal (si, SIGEMT,  restore_real_vroot_handler);
 #endif
-  catch_signal (si, SIGFPE,  on_p);
-  catch_signal (si, SIGBUS,  on_p);
-  catch_signal (si, SIGSEGV, on_p);
+  catch_signal (si, SIGFPE,  restore_real_vroot_handler);
+  catch_signal (si, SIGBUS,  restore_real_vroot_handler);
+  catch_signal (si, SIGSEGV, restore_real_vroot_handler);
 #ifdef SIGSYS
-  catch_signal (si, SIGSYS,  on_p);
+  catch_signal (si, SIGSYS,  restore_real_vroot_handler);
 #endif
-  catch_signal (si, SIGTERM, on_p);
+  catch_signal (si, SIGTERM, restore_real_vroot_handler);
 #ifdef SIGXCPU
-  catch_signal (si, SIGXCPU, on_p);
+  catch_signal (si, SIGXCPU, restore_real_vroot_handler);
 #endif
 #ifdef SIGXFSZ
-  catch_signal (si, SIGXFSZ, on_p);
+  catch_signal (si, SIGXFSZ, restore_real_vroot_handler);
 #endif
 #ifdef SIGDANGER
-  catch_signal (si, SIGDANGER, on_p);
+  catch_signal (si, SIGDANGER, restore_real_vroot_handler);
 #endif
 }
 
+
+static RETSIGTYPE
+saver_sighup_handler (int sig)
+{
+  saver_info *si = global_si_kludge;   /* I hate C so much... */
+
+  /* Re-establish SIGHUP handler */
+  catch_signal (si, SIGHUP, saver_sighup_handler);
+
+  fprintf (stderr, "%s: %s received: restarting...\n",
+           blurb(), signal_name(sig));
+  unblank_screen (si);
+  kill_screenhack (si);
+  XSync (si->dpy, False);
+  restart_process (si);   /* Does not return */
+  abort ();
+}
+
+
+
 void
 saver_exit (saver_info *si, int status, const char *dump_core_reason)
 {
@@ -649,16 +725,12 @@ saver_exit (saver_info *si, int status, const char *dump_core_reason)
 
   exiting = True;
   
-  vrs = restore_real_vroot_1 (si);
+  vrs = restore_real_vroot (si);
   emergency_kill_subproc (si);
+  shutdown_stderr (si);
 
-  if (p->verbose_p)    /* nobody cares about this */
-    {
-      if (vrs)
-        fprintf (real_stderr, "%s: vroot restored, exiting.\n", blurb());
-      else if (p->verbose_p)
-        fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", blurb());
-    }
+  if (p->verbose_p && vrs)
+    fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
 
   fflush(real_stdout);
 
@@ -686,7 +758,7 @@ saver_exit (saver_info *si, int status, const char *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",
+               "\t\t\tfor bug reporting information.\n\n",
                blurb());
 
 # if defined(HAVE_GETCWD)
@@ -812,6 +884,7 @@ store_saver_status (saver_info *si)
                    XA_SCREENSAVER_STATUS,
                    XA_INTEGER, 32, PropModeReplace,
                    (unsigned char *) status, size);
+  free (status);
 }
 
 
@@ -825,6 +898,7 @@ void
 get_screen_viewport (saver_screen_info *ssi,
                      int *x_ret, int *y_ret,
                      int *w_ret, int *h_ret,
+                     int target_x, int target_y,
                      Bool verbose_p)
 {
   int w = WidthOfScreen (ssi->screen);
@@ -832,20 +906,68 @@ get_screen_viewport (saver_screen_info *ssi,
 
 #ifdef HAVE_XF86VMODE
   saver_info *si = ssi->global;
-  int screen_no = screen_number (ssi->screen);
-  int op, event, error;
+  int event, error;
   int dot;
   XF86VidModeModeLine ml;
   int x, y;
+  Bool xinerama_p;
+  Bool placement_only_p = (target_x != -1 && target_y != -1);
+
+#ifdef HAVE_XINERAMA
+  xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) &&
+                XineramaIsActive (si->dpy));
+#else  /* !HAVE_XINERAMA */
+  /* Even if we don't have the client-side Xinerama lib, check to see if
+     the server supports Xinerama, so that we know to ignore the VidMode
+     extension -- otherwise a server crash could result.  Yay. */
+  xinerama_p = XQueryExtension (si->dpy, "XINERAMA", &error, &event, &error);
+  
+#endif /* !HAVE_XINERAMA */
 
-  /* Check for Xinerama first, because the VidModeExtension is broken
-     when Xinerama is present.  Wheee!
-   */
+#ifdef HAVE_XINERAMA
+  if (xinerama_p && placement_only_p)
+    {
+      int nscreens = 0;
+      XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens);
+      if (xsi)
+        {
+          /* Find the screen that contains the mouse. */
+          int which = -1;
+          int i;
+          for (i = 0; i < nscreens; i++)
+            {
+              if (target_x >= xsi[i].x_org &&
+                  target_y >= xsi[i].y_org &&
+                  target_x < xsi[i].x_org + xsi[i].width &&
+                  target_y < xsi[i].y_org + xsi[i].height)
+                which = i;
+              if (verbose_p)
+                {
+                  fprintf (stderr, "%s: %d: xinerama vp: %dx%d+%d+%d",
+                           blurb(), i,
+                           xsi[which].width, xsi[which].height,
+                           xsi[i].x_org, xsi[i].y_org);
+                  if (which == i)
+                    fprintf (stderr, "; mouse at %d,%d",
+                             target_x, target_y);
+                  fprintf (stderr, ".\n");
+                }
+            }
+          if (which == -1) which = 0;  /* didn't find it?  Use the first. */
+          *x_ret = xsi[which].x_org;
+          *y_ret = xsi[which].y_org;
+          *w_ret = xsi[which].width;
+          *h_ret = xsi[which].height;
+          XFree (xsi);
+          return;
+        }
+    }
+#endif /* HAVE_XINERAMA */
 
-  if (!XQueryExtension (si->dpy, "XINERAMA", &op, &event, &error) &&
+  if (!xinerama_p &&  /* Xinerama + VidMode = broken. */
       XF86VidModeQueryExtension (si->dpy, &event, &error) &&
-      XF86VidModeGetModeLine (si->dpy, screen_no, &dot, &ml) &&
-      XF86VidModeGetViewPort (si->dpy, screen_no, &x, &y))
+      XF86VidModeGetModeLine (si->dpy, ssi->number, &dot, &ml) &&
+      XF86VidModeGetViewPort (si->dpy, ssi->number, &x, &y))
     {
       char msg[512];
       *x_ret = x;
@@ -884,10 +1006,10 @@ get_screen_viewport (saver_screen_info *ssi,
           *h_ret = h;
           return;
         }
-          
 
-      sprintf (msg, "%s: vp is %dx%d+%d+%d",
-               blurb(), *w_ret, *h_ret, *x_ret, *y_ret);
+      sprintf (msg, "%s: %d: vp is %dx%d+%d+%d",
+               blurb(), ssi->number,
+               *w_ret, *h_ret, *x_ret, *y_ret);
 
 
       /* Apparently, though the server stores the X position in increments of
@@ -950,6 +1072,98 @@ get_screen_viewport (saver_screen_info *ssi,
 }
 
 
+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 Bool
+safe_XKillClient (Display *dpy, XID id)
+{
+  XErrorHandler old_handler;
+  XSync (dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+  XKillClient (dpy, id);
+
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+
+  return (!error_handler_hit_p);
+}
+
+
 static void
 initialize_screensaver_window_1 (saver_screen_info *ssi)
 {
@@ -967,8 +1181,9 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
   unsigned long attrmask;
   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,
+  get_screen_viewport (ssi, &x, &y, &width, &height, -1, -1,
                        (p->verbose_p && !si->screen_blanked_p));
 
   black.red = black.green = black.blue = 0;
@@ -1029,7 +1244,7 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
     ;
   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
     {
-      fprintf (stderr, "%s: using default visual ", blurb());
+      fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
       describe_visual (stderr, ssi->screen, ssi->current_visual,
                       install_cmap_p);
     }
@@ -1106,12 +1321,17 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
       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),
@@ -1120,9 +1340,22 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
                       ssi->current_visual, attrmask, &attrs);
 
       reset_stderr (ssi);
+
+      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,
+                                ssi->number);
+          safe_XDestroyWindow (si->dpy, horked_window);
+          horked_window = 0;
+        }
+
       if (p->verbose_p)
-       fprintf (stderr, "%s: saver window is 0x%lx.\n",
-                blurb(), (unsigned long) ssi->screensaver_window);
+       fprintf (stderr, "%s: %d: saver window is 0x%lx.\n",
+                 blurb(), ssi->number,
+                 (unsigned long) ssi->screensaver_window);
     }
 
   store_saver_id (ssi);       /* store window name and IDs */
@@ -1262,22 +1495,63 @@ raise_window (saver_info *si,
     }
 }
 
+
+int
+mouse_screen (saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
+
+  if (si->nscreens == 1)
+    return 0;
+  else
+    {
+      int i;
+      for (i = 0; i < si->nscreens; i++)
+        {
+          saver_screen_info *ssi = &si->screens[i];
+          Window pointer_root, pointer_child;
+          int root_x, root_y, win_x, win_y;
+          unsigned int mask;
+          if (XQueryPointer (si->dpy,
+                             RootWindowOfScreen (ssi->screen),
+                             &pointer_root, &pointer_child,
+                             &root_x, &root_y, &win_x, &win_y, &mask))
+            {
+              if (p->verbose_p)
+                fprintf (stderr, "%s: mouse is on screen %d\n",
+                         blurb(), i, si->nscreens);
+              return i;
+            }
+        }
+
+      /* couldn't figure out where the mouse is?  Oh well. */
+      return 0;
+    }
+}
+
+
 Bool
 blank_screen (saver_info *si)
 {
   int i;
   Bool ok;
+  Window w;
+  int mscreen;
 
   /* 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.
+
+     By "the root window", we mean "the root window that contains the mouse."
+     We use to always grab the mouse on screen 0, but that has the effect of
+     moving the mouse to screen 0 from whichever screen it was on, on
+     multi-head systems.
    */
-  ok = grab_keyboard_and_mouse (si,
-                                /*si->screens[0].screensaver_window,*/
-                                RootWindowOfScreen(si->screens[0].screen),
-                                (si->demoing_p
-                                 ? 0
-                                 : si->screens[0].cursor));
+  mscreen = mouse_screen (si);
+  w = RootWindowOfScreen(si->screens[mscreen].screen);
+  ok = grab_keyboard_and_mouse (si, w,
+                                (si->demoing_p ? 0 : si->screens[0].cursor),
+                                mscreen);
 
 
   if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
@@ -1443,6 +1717,47 @@ unblank_screen (saver_info *si)
 }
 
 
+/* 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
+maybe_transfer_grabs (saver_screen_info *ssi,
+                      Window old_w, Window new_w,
+                      int new_screen_no)
+{
+  saver_info *si = ssi->global;
+
+  /* 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)
+    {
+      XGrabServer (si->dpy);           /* ############ DANGER! */
+      ungrab_mouse (si);
+      grab_mouse (si, ssi->screensaver_window,
+                  (si->demoing_p ? 0 : ssi->cursor),
+                  new_screen_no);
+      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, ssi->number);
+      XUngrabServer (si->dpy);
+      XSync (si->dpy, False);          /* ###### (danger over) */
+    }
+}
+
+
+
 Bool
 select_visual (saver_screen_info *ssi, const char *visual_name)
 {
@@ -1470,16 +1785,14 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
          visual_name = "default";
          install_cmap_p = False;
        }
-#ifdef DAEMON_USE_GL
       else if (!strcmp(visual_name, "gl") ||
                !strcmp(visual_name, "Gl") ||
                !strcmp(visual_name, "GL"))
         {
-          new_v = get_gl_visual (ssi->screen);
+          new_v = ssi->best_gl_visual;
           if (!new_v && p->verbose_p)
             fprintf (stderr, "%s: no GL visuals.\n", progname);
         }
-#endif /* DAEMON_USE_GL */
 
       if (!new_v)
         new_v = get_visual (ssi->screen, visual_name, True, False);
@@ -1506,10 +1819,10 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
 
       if (p->verbose_p)
        {
-         fprintf (stderr, "%s: switching to visual ", blurb());
+         fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
          describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
 #if 0
-         fprintf (stderr, "%s:                from ", blurb());
+         fprintf (stderr, "%s:                  from ", blurb());
          describe_visual (stderr, ssi->screen, ssi->current_visual,
                           was_installed_p);
 #endif
@@ -1533,50 +1846,15 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
       store_vroot_property (si->dpy,
                            ssi->screensaver_window, ssi->screensaver_window);
 
+      /* Transfer any grabs from the old window to the new. */
+      maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window, ssi->number);
 
-      /* Transfer the 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...
-       */
-
-
-      /* If we're destroying the window that holds 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)
-       {
-         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 we're destroying the window that holds 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) */
-       }
-
-      /* Now we can destroy this window without horking our grabs. */
-
+      /* 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);
+       fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx.\n",
+                blurb(), ssi->number, (unsigned long) old_w);
 
       if (old_c &&
          old_c != DefaultColormapOfScreen (ssi->screen) &&