http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.34.tar.gz
[xscreensaver] / driver / windows.c
index a9731b02e0b27a474156e02baf73ecb0791933b0..b0198e14a528c2512b0de882d86764e0153281f1 100644 (file)
@@ -1,4 +1,5 @@
-/* xscreensaver, Copyright (c) 1991-1995 Jamie Zawinski <jwz@mcom.com>
+/* windows.c --- turning the screen black; dealing with visuals, virtual roots.
+ * 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
  * implied warranty.
  */
 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef VMS
+# include <unixlib.h>          /* for getpid() */
+# include "vms-gtod.h"         /* for gettimeofday() */
+#endif /* VMS */
+
+#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/Xlib.h>
-#include <X11/Xutil.h>
+#include <X11/Xutil.h>         /* for XSetClassHint() */
 #include <X11/Xatom.h>
-#include <X11/Xos.h>
-#include <X11/Xmu/SysUtil.h>
-
+#include <X11/Xos.h>           /* for time() */
 #include <signal.h>            /* for the signal names */
 
-#include "xscreensaver.h"
+#ifdef HAVE_MIT_SAVER_EXTENSION
+# include <X11/extensions/scrnsaver.h>
+#endif /* HAVE_MIT_SAVER_EXTENSION */
 
-#ifdef HAVE_SAVER_EXTENSION
-#include <X11/extensions/scrnsaver.h>
-extern Bool use_saver_extension;
-#endif /* HAVE_SAVER_EXTENSION */
+#ifdef HAVE_XF86VMODE
+# include <X11/extensions/xf86vmode.h>
+#endif /* HAVE_XF86VMODE */
 
-#if __STDC__
-extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
-#endif /* __STDC__ */
 
-extern Time timeout;
+/* This file doesn't need the Xt headers, so stub these types out... */
+#undef XtPointer
+#define XtAppContext void*
+#define XrmDatabase  void*
+#define XtIntervalId void*
+#define XtPointer    void*
+#define Widget       void*
 
-extern Bool lock_p, demo_mode_p;
+#include "xscreensaver.h"
+#include "visual.h"
+#include "fade.h"
 
-Atom XA_VROOT, XA_XSETROOT_ID;
-Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
 
-#if __STDC__
-extern void describe_visual (FILE *, Display *, Visual *);
-extern void reset_stderr (void);
-#endif
+extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
-Window screensaver_window = 0;
-Cursor cursor;
-Colormap cmap, cmap2;
-Bool install_cmap_p;
-Bool fade_p, unfade_p;
-int fade_seconds, fade_ticks;
+Atom XA_VROOT, XA_XSETROOT_ID;
+Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
+Atom XA_SCREENSAVER_STATUS;
 
-static unsigned long black_pixel;
-static Window real_vroot, real_vroot_value;
 
-#ifdef HAVE_SAVER_EXTENSION
-Window server_saver_window = 0;
-#endif /* HAVE_SAVER_EXTENSION */
+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);
 
 #define ALL_POINTER_EVENTS \
        (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
@@ -61,59 +77,190 @@ Window server_saver_window = 0;
         Button1MotionMask | Button2MotionMask | Button3MotionMask | \
         Button4MotionMask | Button5MotionMask | ButtonMotionMask)
 
-/* I don't really understand Sync vs Async, but these seem to work... */
-#define grab_kbd(win) \
-  XGrabKeyboard (dpy, (win), True, GrabModeSync, GrabModeAsync, CurrentTime)
-#define grab_mouse(win) \
-  XGrabPointer (dpy, (win), True, ALL_POINTER_EVENTS, \
-               GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime)
 
-void
-grab_keyboard_and_mouse P((void))
+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;
+      }
+    }
+}
 
-  if (demo_mode_p) return;
+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 (screensaver_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 (screensaver_window);
-      if (status != GrabSuccess)
-       fprintf (stderr, "%s: %scouldn't grab keyboard!  (%d)\n",
-                progname, (verbose_p ? "## " : ""), status);
-    }
-  status = grab_mouse (screensaver_window);
-  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 (screensaver_window);
-      if (status != GrabSuccess)
-       fprintf (stderr, "%s: %scouldn't grab pointer!  (%d)\n",
-                progname, (verbose_p ? "## " : ""), 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 P((void))
+static void
+ungrab_keyboard_and_mouse (saver_info *si)
 {
-  XUngrabPointer (dpy, CurrentTime);
-  XUngrabKeyboard (dpy, CurrentTime);
+  ungrab_mouse (si);
+  ungrab_kbd (si);
 }
 
 
-void
-ensure_no_screensaver_running ()
+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;
   unsigned int nkids;
-  int (*old_handler) ();
-
-  old_handler = XSetErrorHandler (BadWindow_ehandler);
+  XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
 
   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
     abort ();
@@ -143,56 +290,19 @@ ensure_no_screensaver_running ()
            id = "???";
 
          fprintf (stderr,
-      "%s: %salready running on display %s (window 0x%x)\n from process %s.\n",
-                  progname, (verbose_p ? "## " : ""), DisplayString (dpy),
-                  (int) kids [i], id);
-         exit (1);
+      "%s: already running on display %s (window 0x%x)\n from process %s.\n",
+                  blurb(), DisplayString (dpy), (int) kids [i], id);
+         status = True;
        }
     }
 
   if (kids) XFree ((char *) kids);
   XSync (dpy, False);
   XSetErrorHandler (old_handler);
+  return status;
 }
 
 
-void
-disable_builtin_screensaver ()
-{
-  int server_timeout, server_interval, prefer_blank, allow_exp;
-  /* Turn off the server builtin saver if it is now running. */
-  XForceScreenSaver (dpy, ScreenSaverReset);
-  XGetScreenSaver (dpy, &server_timeout, &server_interval,
-                  &prefer_blank, &allow_exp);
-
-#ifdef HAVE_SAVER_EXTENSION
-  if (use_saver_extension)
-    {
-      /* Override the values specified with "xset" with our own parameters. */
-      prefer_blank = False;
-      allow_exp = True;
-      server_interval = 0;
-      server_timeout = (timeout / 1000);
-      if (verbose_p)
-       fprintf (stderr,
-                "%s: configuring server for saver timeout of %d seconds.\n",
-                progname, server_timeout);
-      XSetScreenSaver (dpy, server_timeout, server_interval,
-                      prefer_blank, allow_exp);
-    }
-  else
-#endif /* HAVE_SAVER_EXTENSION */
-  if (server_timeout != 0)
-    {
-      server_timeout = 0;
-      XSetScreenSaver (dpy, server_timeout, server_interval,
-                      prefer_blank, allow_exp);
-      printf ("%s%sisabling server builtin screensaver.\n\
-       You can re-enable it with \"xset s on\".\n",
-             (verbose_p ? "" : progname), (verbose_p ? "\n\tD" : ": d"));
-    }
-}
-
 \f
 /* Virtual-root hackery */
 
@@ -201,48 +311,41 @@ ERROR!  You must not include vroot.h in this file.
 #endif
 
 static void
-#if __STDC__
-store_vroot_property (Window win, Window value)
-#else
-store_vroot_property (win, value)
-     Window win, value;
-#endif
+store_vroot_property (Display *dpy, Window win, Window value)
 {
 #if 0
-  printf ("%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname, 
-         win,
-         (win == screensaver_window ? "ScreenSaver" :
-          (win == real_vroot ? "VRoot" :
-           (win == real_vroot_value ? "Vroot_value" : "???"))),
-         value,
-         (value == screensaver_window ? "ScreenSaver" :
-          (value == real_vroot ? "VRoot" :
-           (value == real_vroot_value ? "Vroot_value" : "???"))));
+  if (p->verbose_p)
+    fprintf (stderr,
+            "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb(), 
+            win,
+            (win == screensaver_window ? "ScreenSaver" :
+             (win == real_vroot ? "VRoot" :
+              (win == real_vroot_value ? "Vroot_value" : "???"))),
+            value,
+            (value == screensaver_window ? "ScreenSaver" :
+             (value == real_vroot ? "VRoot" :
+              (value == real_vroot_value ? "Vroot_value" : "???"))));
 #endif
   XChangeProperty (dpy, win, XA_VROOT, XA_WINDOW, 32, PropModeReplace,
                   (unsigned char *) &value, 1);
 }
 
 static void
-#if __STDC__
-remove_vroot_property (Window win)
-#else
-remove_vroot_property (win)
-     Window win;
-#endif
+remove_vroot_property (Display *dpy, Window win)
 {
 #if 0
-  printf ("%s: removing XA_VROOT from 0x%x (%s)\n", progname, win, 
-         (win == screensaver_window ? "ScreenSaver" :
-          (win == real_vroot ? "VRoot" :
-           (win == real_vroot_value ? "Vroot_value" : "???"))));
+  if (p->verbose_p)
+    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" : "???"))));
 #endif
   XDeleteProperty (dpy, win, XA_VROOT);
 }
 
 
 static void
-kill_xsetroot_data P((void))
+kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
 {
   Atom type;
   int format;
@@ -261,7 +364,7 @@ kill_xsetroot_data P((void))
      cause the RetainPermanent resources of the client which created it
      (and which no longer exists) to be freed.
    */
-  if (XGetWindowProperty (dpy, screensaver_window, XA_XSETROOT_ID, 0, 1,
+  if (XGetWindowProperty (dpy, window, XA_XSETROOT_ID, 0, 1,
                          True, AnyPropertyType, &type, &format, &nitems, 
                          &bytesafter, (unsigned char **) &dataP)
       == Success
@@ -271,32 +374,44 @@ kill_xsetroot_data P((void))
          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: %sdeleted unrecognised _XSETROOT_ID property: \n\
+       fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
        %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
-                progname, (verbose_p ? "## " : ""),
-                (unsigned long) dataP, (dataP ? *dataP : 0), type,
+                blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
                 format, nitems, bytesafter);
     }
 }
 
 
-static void handle_signals P((Bool on_p));
+static void handle_signals (saver_info *si, Bool on_p);
 
 static void
-save_real_vroot P((void))
+save_real_vroot (saver_screen_info *ssi)
 {
+  saver_info *si = ssi->global;
+  Display *dpy = si->dpy;
+  Screen *screen = ssi->screen;
   int i;
   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);
 
-  real_vroot = 0;
-  real_vroot_value = 0;
+  ssi->real_vroot = 0;
+  ssi->real_vroot_value = 0;
   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
     abort ();
   if (root != root2)
@@ -317,55 +432,76 @@ save_real_vroot P((void))
        continue;
       if (! vrootP)
        continue;
-      if (real_vroot)
+      if (ssi->real_vroot)
        {
-         if (*vrootP == screensaver_window) abort ();
+         if (*vrootP == ssi->screensaver_window) abort ();
          fprintf (stderr,
-           "%s: %smore than one virtual root window found (0x%x and 0x%x).\n",
-                  progname, (verbose_p ? "## " : ""),
-                  (int) real_vroot, (int) kids [i]);
+           "%s: more than one virtual root window found (0x%x and 0x%x).\n",
+                  blurb(), (int) ssi->real_vroot, (int) kids [i]);
          exit (1);
        }
-      real_vroot = kids [i];
-      real_vroot_value = *vrootP;
+      ssi->real_vroot = kids [i];
+      ssi->real_vroot_value = *vrootP;
     }
 
-  if (real_vroot)
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+
+  if (ssi->real_vroot)
     {
-      handle_signals (True);
-      remove_vroot_property (real_vroot);
+      handle_signals (si, True);
+      remove_vroot_property (si->dpy, ssi->real_vroot);
       XSync (dpy, False);
     }
 
   XFree ((char *) kids);
 }
 
+
 static Bool
-restore_real_vroot_1 P((void))
+restore_real_vroot_2 (saver_screen_info *ssi)
 {
-  if (verbose_p && real_vroot)
-    printf ("%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
-           progname, (unsigned long) real_vroot);
-  remove_vroot_property (screensaver_window);
-  if (real_vroot)
+  saver_info *si = ssi->global;
+  saver_preferences *p = &si->prefs;
+  if (p->verbose_p && 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)
     {
-      store_vroot_property (real_vroot, real_vroot_value);
-      real_vroot = 0;
-      real_vroot_value = 0;
+      store_vroot_property (si->dpy, ssi->real_vroot, ssi->real_vroot_value);
+      ssi->real_vroot = 0;
+      ssi->real_vroot_value = 0;
       /* make sure the property change gets there before this process
         terminates!  We might be doing this because we have intercepted
         SIGTERM or something. */
-      XSync (dpy, False);
+      XSync (si->dpy, False);
       return True;
     }
   return False;
 }
 
+static Bool
+restore_real_vroot_1 (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))
+       did_any = True;
+    }
+  return did_any;
+}
+
 void
-restore_real_vroot ()
+restore_real_vroot (saver_info *si)
 {
-  if (restore_real_vroot_1 ())
-    handle_signals (False);
+  if (restore_real_vroot_1 (si))
+    handle_signals (si, False);
 }
 
 \f
@@ -373,96 +509,221 @@ restore_real_vroot ()
    inconsistent state
  */
 
-static const char *sig_names [255] = { 0 };
+const char *
+signal_name(int signal)
+{
+  switch (signal) {
+  case SIGHUP:   return "SIGHUP";
+  case SIGINT:   return "SIGINT";
+  case SIGQUIT:          return "SIGQUIT";
+  case SIGILL:   return "SIGILL";
+  case SIGTRAP:          return "SIGTRAP";
+#ifdef SIGABRT
+  case SIGABRT:          return "SIGABRT";
+#endif
+  case SIGFPE:   return "SIGFPE";
+  case SIGKILL:          return "SIGKILL";
+  case SIGBUS:   return "SIGBUS";
+  case SIGSEGV:          return "SIGSEGV";
+  case SIGPIPE:          return "SIGPIPE";
+  case SIGALRM:          return "SIGALRM";
+  case SIGTERM:          return "SIGTERM";
+#ifdef SIGSTOP
+  case SIGSTOP:          return "SIGSTOP";
+#endif
+#ifdef SIGCONT
+  case SIGCONT:          return "SIGCONT";
+#endif
+#ifdef SIGUSR1
+  case SIGUSR1:          return "SIGUSR1";
+#endif
+#ifdef SIGUSR2
+  case SIGUSR2:          return "SIGUSR2";
+#endif
+#ifdef SIGEMT
+  case SIGEMT:   return "SIGEMT";
+#endif
+#ifdef SIGSYS
+  case SIGSYS:   return "SIGSYS";
+#endif
+#ifdef SIGCHLD
+  case SIGCHLD:          return "SIGCHLD";
+#endif
+#ifdef SIGPWR
+  case SIGPWR:   return "SIGPWR";
+#endif
+#ifdef SIGWINCH
+  case SIGWINCH:  return "SIGWINCH";
+#endif
+#ifdef SIGURG
+  case SIGURG:   return "SIGURG";
+#endif
+#ifdef SIGIO
+  case SIGIO:    return "SIGIO";
+#endif
+#ifdef SIGVTALRM
+  case SIGVTALRM: return "SIGVTALRM";
+#endif
+#ifdef SIGXCPU
+  case SIGXCPU:          return "SIGXCPU";
+#endif
+#ifdef SIGXFSZ
+  case SIGXFSZ:          return "SIGXFSZ";
+#endif
+#ifdef SIGDANGER
+  case SIGDANGER: return "SIGDANGER";
+#endif
+  default:
+    {
+      static char buf[50];
+      sprintf(buf, "signal %d\n", signal);
+      return buf;
+    }
+  }
+}
 
-static void
-restore_real_vroot_handler (sig)
-     int sig;
+
+
+static RETSIGTYPE
+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 ())
-    fprintf (stderr, "\n%s: %s%s (%d) intercepted, vroot restored.\n",
-            progname, (verbose_p ? "## " : ""),
-            ((sig < sizeof(sig_names) && sig >= 0 && sig_names [sig])
-             ? sig_names [sig] : "unknown signal"),
-            sig);
+  if (restore_real_vroot_1 (si))
+    fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
+            blurb(), signal_name(sig));
   kill (getpid (), sig);
 }
 
-
 static void
-#if __STDC__
-catch_signal (int sig, char *signame, Bool on_p)
-#else
-catch_signal (sig, signame, on_p)
-     int sig;
-     char *signame;
-     Bool on_p;
-#endif
+catch_signal (saver_info *si, int sig, Bool on_p)
 {
   if (! on_p)
     signal (sig, SIG_DFL);
   else
     {
-      sig_names [sig] = signame;
-      if (((int) signal (sig, restore_real_vroot_handler)) == -1)
+      if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
        {
          char buf [255];
-         sprintf (buf, "%s: %scouldn't catch %s (%d)", progname,
-                  (verbose_p ? "## " : ""), signame, sig);
+         sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
          perror (buf);
-         restore_real_vroot ();
-         exit (1);
+         saver_exit (si, 1, 0);
        }
     }
 }
 
 static void
-handle_signals (on_p)
-     Bool on_p;
+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 (SIGHUP,  "SIGHUP",  on_p);
-  catch_signal (SIGINT,  "SIGINT",  on_p);
-  catch_signal (SIGQUIT, "SIGQUIT", on_p);
-  catch_signal (SIGILL,  "SIGILL",  on_p);
-  catch_signal (SIGTRAP, "SIGTRAP", on_p);
-  catch_signal (SIGIOT,  "SIGIOT",  on_p);
-  catch_signal (SIGABRT, "SIGABRT", on_p);
+  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);
+#ifdef SIGIOT
+  catch_signal (si, SIGIOT,  on_p);
+#endif
+  catch_signal (si, SIGABRT, on_p);
 #ifdef SIGEMT
-  catch_signal (SIGEMT,  "SIGEMT",  on_p);
+  catch_signal (si, SIGEMT,  on_p);
 #endif
-  catch_signal (SIGFPE,  "SIGFPE",  on_p);
-  catch_signal (SIGBUS,  "SIGBUS",  on_p);
-  catch_signal (SIGSEGV, "SIGSEGV", on_p);
+  catch_signal (si, SIGFPE,  on_p);
+  catch_signal (si, SIGBUS,  on_p);
+  catch_signal (si, SIGSEGV, on_p);
 #ifdef SIGSYS
-  catch_signal (SIGSYS,  "SIGSYS",  on_p);
+  catch_signal (si, SIGSYS,  on_p);
 #endif
-  catch_signal (SIGTERM, "SIGTERM", on_p);
+  catch_signal (si, SIGTERM, on_p);
 #ifdef SIGXCPU
-  catch_signal (SIGXCPU, "SIGXCPU", on_p);
+  catch_signal (si, SIGXCPU, on_p);
 #endif
 #ifdef SIGXFSZ
-  catch_signal (SIGXFSZ, "SIGXFSZ", on_p);
+  catch_signal (si, SIGXFSZ, on_p);
 #endif
 #ifdef SIGDANGER
-  catch_signal (SIGDANGER, "SIGDANGER", on_p);
+  catch_signal (si, SIGDANGER, on_p);
 #endif
 }
 
+void
+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)
+    exit(status);
+
+  exiting = True;
+  
+  vrs = restore_real_vroot_1 (si);
+  emergency_kill_subproc (si);
+  shutdown_stderr (si);
+
+  if (p->verbose_p && vrs)
+    fprintf (real_stderr, "%s: old vroot restored.\n", blurb());
+
+  fflush(real_stdout);
+
+#ifdef VMS     /* on VMS, 1 is the "normal" exit code instead of 0. */
+  if (status == 0) status = 1;
+  else if (status == 1) status = -1;
+#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);
+}
+
 \f
 /* Managing the actual screensaver window */
 
 Bool
-window_exists_p (dpy, window)
-     Display *dpy;
-     Window window;
+window_exists_p (Display *dpy, Window window)
 {
-  int (*old_handler) ();
+  XErrorHandler old_handler;
   XWindowAttributes xgwa;
   xgwa.screen = 0;
   old_handler = XSetErrorHandler (BadWindow_ehandler);
@@ -472,100 +733,411 @@ window_exists_p (dpy, 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
-initialize_screensaver_window P((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;   /* 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
      meantime.  We could just destroy and recreate the window, but that has
      its own set of problems...
    */
   XColor black;
-  XClassHint class_hints;
   XSetWindowAttributes attrs;
   unsigned long attrmask;
-  int width = WidthOfScreen (screen);
-  int height = HeightOfScreen (screen);
-  char id [2048];
+  int x, y, width, height;
+  static Bool printed_visual_info = False;  /* only print the message once. */
+  Window horked_window = 0;
 
-  reset_stderr ();
+  get_screen_viewport (ssi, &x, &y, &width, &height,
+                       (p->verbose_p && !si->screen_blanked_p));
 
   black.red = black.green = black.blue = 0;
 
-  if (cmap == DefaultColormapOfScreen (screen))
-    cmap = 0;
+  if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
+    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 || visual != DefaultVisualOfScreen (screen))
+  if (install_cmap_p)
     {
-      if (! cmap)
+      if (! ssi->cmap)
        {
-         cmap = XCreateColormap (dpy, RootWindowOfScreen (screen),
-                                 visual, AllocNone);
-         if (! XAllocColor (dpy, cmap, &black)) abort ();
-         black_pixel = black.pixel;
+         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;
        }
     }
   else
     {
-      if (cmap)
+      Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
+      if (ssi->cmap)
        {
-         XFreeColors (dpy, cmap, &black_pixel, 1, 0);
-         XFreeColormap (dpy, cmap);
+         XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
+         if (ssi->cmap != ssi->demo_cmap &&
+             ssi->cmap != def_cmap)
+           XFreeColormap (si->dpy, ssi->cmap);
        }
-      cmap = DefaultColormapOfScreen (screen);
-      black_pixel = BlackPixelOfScreen (screen);
-    }
-
-  if (cmap2)
-    {
-      XFreeColormap (dpy, cmap2);
-      cmap2 = 0;
-    }
-
-  if (fade_p)
-    {
-      cmap2 = copy_colormap (dpy, cmap, 0);
-      if (! cmap2)
-       fade_p = unfade_p = 0;
+      ssi->cmap = def_cmap;
+      ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
     }
 
   attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
              CWBackPixel | CWBackingPixel | CWBorderPixel);
   attrs.override_redirect = True;
+
+  /* When use_mit_saver_extension or use_sgi_saver_extension is true, we won't
+     actually be reading these events during normal operation; but we still
+     need to see Button events for demo-mode to work properly.
+   */
   attrs.event_mask = (KeyPressMask | KeyReleaseMask |
                      ButtonPressMask | ButtonReleaseMask |
                      PointerMotionMask);
+
   attrs.backing_store = NotUseful;
-  attrs.colormap = cmap;
-  attrs.background_pixel = black_pixel;
-  attrs.backing_pixel = black_pixel;
-  attrs.border_pixel = black_pixel;
+  attrs.colormap = ssi->cmap;
+  attrs.background_pixel = ssi->black_pixel;
+  attrs.backing_pixel = ssi->black_pixel;
+  attrs.border_pixel = ssi->black_pixel;
 
-#if 0
-  if (demo_mode_p || lock_p) width = width / 2;  /* #### */
-#endif
+  if (p->debug_p) width = width / 2;
 
-  if (screensaver_window || !verbose_p)
+  if (!p->verbose_p || printed_visual_info)
     ;
-  else if (visual == DefaultVisualOfScreen (screen))
+  else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
     {
-      fprintf (stderr, "%s: using default visual ", progname);
-      describe_visual (stderr, dpy, 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, dpy, visual);
-      fprintf (stderr, "%s: default visual: ", progname);
-      describe_visual (stderr, dpy, DefaultVisualOfScreen (screen));
+      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),
+                      ssi->install_cmap_p);
     }
+  printed_visual_info = True;
 
-#ifdef HAVE_SAVER_EXTENSION
-  if (use_saver_extension)
+#ifdef HAVE_MIT_SAVER_EXTENSION
+  if (si->using_mit_saver_extension)
     {
       XScreenSaverInfo *info;
-      Window root = RootWindowOfScreen (screen);
+      Window root = RootWindowOfScreen (ssi->screen);
 
+#if 0
       /* This call sets the server screensaver timeouts to what we think
         they should be (based on the resources and args xscreensaver was
         started with.)  It's important that we do this to sync back up
@@ -576,11 +1148,11 @@ initialize_screensaver_window P((void))
         but a side effect of this would be that the server would map its
         saver window (which we then hide again right away) meaning that
         the bits currently on the screen get blown away.  Ugly. */
-#if 0
+
       /* #### Ok, that doesn't work - when we tell the server that the
         screensaver is "off" it sends us a Deactivate event, which is
         sensible... but causes the saver to never come on.  Hmm. */
-      disable_builtin_screensaver ();
+      disable_builtin_screensaver (si, True);
 #endif /* 0 */
 
 #if 0
@@ -595,181 +1167,528 @@ initialize_screensaver_window P((void))
         window, we'd have to reimplement the ACTIVATE ClientMessage to
         tell the *server* to tell *us* to turn on, to cause the window
         to get created at the right time.  Gag.  */
-      XScreenSaverSetAttributes (dpy, root,
+      XScreenSaverSetAttributes (si->dpy, root,
                                 0, 0, width, height, 0,
-                                visual_depth, InputOutput, visual,
+                                current_depth, InputOutput, visual,
                                 attrmask, &attrs);
-      XSync (dpy, False);
+      XSync (si->dpy, False);
 #endif /* 0 */
 
       info = XScreenSaverAllocInfo ();
-      XScreenSaverQueryInfo (dpy, root, info);
-      server_saver_window = info->window;
-      if (! server_saver_window) abort ();
+      XScreenSaverQueryInfo (si->dpy, root, info);
+      ssi->server_mit_saver_window = info->window;
+      if (! ssi->server_mit_saver_window) abort ();
       XFree (info);
     }
-#endif /* HAVE_SAVER_EXTENSION */
+#endif /* HAVE_MIT_SAVER_EXTENSION */
 
-  if (screensaver_window)
+  if (ssi->screensaver_window)
     {
       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 (dpy, screensaver_window, changesmask, &changes);
-      XChangeWindowAttributes (dpy, 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)
     {
-      screensaver_window =
-       XCreateWindow (dpy, RootWindowOfScreen (screen), 0, 0, width, height,
-                      0, visual_depth, InputOutput, visual, attrmask,
-                      &attrs);
-    }
-
-#ifdef HAVE_SAVER_EXTENSION
-  if (!use_saver_extension ||
-      window_exists_p (dpy, 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...
-     */
-#endif /* HAVE_SAVER_EXTENSION */
-    {
-      class_hints.res_name = progname;
-      class_hints.res_class = progclass;
-      XSetClassHint (dpy, screensaver_window, &class_hints);
-      XStoreName (dpy, screensaver_window, "screensaver");
-      XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_VERSION,
-                      XA_STRING, 8, PropModeReplace,
-                      (unsigned char *) screensaver_version,
-                      strlen (screensaver_version));
-
-      sprintf (id, "%d on host ", getpid ());
-      if (! XmuGetHostname (id + strlen (id), sizeof (id) - strlen (id) - 1))
-       strcat (id, "???");
-      XChangeProperty (dpy, screensaver_window, XA_SCREENSAVER_ID, XA_STRING,
-                      8, PropModeReplace, (unsigned char *) id, strlen (id));
-
-      if (!cursor)
-       {
-         Pixmap bit;
-         bit = XCreatePixmapFromBitmapData (dpy, screensaver_window, "\000",
-                                            1, 1,
-                                            BlackPixelOfScreen (screen),
-                                            BlackPixelOfScreen (screen), 1);
-         cursor = XCreatePixmapCursor (dpy, bit, bit, &black, &black, 0, 0);
-         XFreePixmap (dpy, bit);
-       }
+      ssi->screensaver_window =
+       XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
+                       x, y, width, height,
+                       0, ssi->current_depth, InputOutput,
+                      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);
+          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);
+    }
 
-      XSetWindowBackground (dpy, screensaver_window, black_pixel);
-      if (! demo_mode_p)
-       XDefineCursor (dpy, screensaver_window, cursor);
-      else
-       XUndefineCursor (dpy, screensaver_window);
+  store_saver_id (ssi);       /* store window name and IDs */
+
+  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);
+
+  if (si->demoing_p)
+    XUndefineCursor (si->dpy, ssi->screensaver_window);
+  else
+    XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
+}
+
+void
+initialize_screensaver_window (saver_info *si)
+{
+  int i;
+  for (i = 0; i < si->nscreens; i++)
+    initialize_screensaver_window_1 (&si->screens[i]);
 }
 
 
 void 
-raise_window (inhibit_fade, between_hacks_p)
-     Bool inhibit_fade, between_hacks_p;
-{
-  initialize_screensaver_window ();
-
-  if (fade_p && !inhibit_fade && !demo_mode_p)
-    {
-      int grabbed;
-      Colormap current_map = (between_hacks_p
-                             ? cmap
-                             : DefaultColormapOfScreen (screen));
-      copy_colormap (dpy, current_map, cmap2);
-      XGrabServer (dpy);
-      /* grab and blacken mouse on the root window (saver not mapped yet) */
-      grabbed = grab_mouse (RootWindowOfScreen (screen));
-      /* fade what's on the screen to black */
-      XInstallColormap (dpy, cmap2);
-      fade_colormap (dpy, current_map, cmap2, fade_seconds, fade_ticks, True);
-      XClearWindow (dpy, screensaver_window);
-      XMapRaised (dpy, screensaver_window);
-
-#ifdef HAVE_SAVER_EXTENSION
-      if (server_saver_window && window_exists_p (dpy, server_saver_window))
-       XUnmapWindow (dpy, server_saver_window);
-#endif /* HAVE_SAVER_EXTENSION */
-
-      /* Once the saver window is up, restore the colormap.
-        (The "black" pixels of the two colormaps are compatible.) */
-      XInstallColormap (dpy, cmap);
-      if (grabbed == GrabSuccess)
-       XUngrabPointer (dpy, CurrentTime);
-      XUngrabServer (dpy);
+raise_window (saver_info *si,
+             Bool inhibit_fade, Bool between_hacks_p, Bool dont_clear)
+{
+  saver_preferences *p = &si->prefs;
+  int i;
+
+  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 && si->fading_possible_p && !inhibit_fade)
+    {
+      Window *current_windows = (Window *)
+       calloc(sizeof(Window), si->nscreens);
+      Colormap *current_maps = (Colormap *)
+       calloc(sizeof(Colormap), si->nscreens);
+
+      for (i = 0; i < si->nscreens; i++)
+       {
+         saver_screen_info *ssi = &si->screens[i];
+         current_windows[i] = ssi->screensaver_window;
+         current_maps[i] = (between_hacks_p
+                            ? ssi->cmap
+                            : DefaultColormapOfScreen (ssi->screen));
+         /* Ensure that the default background of the window is really black,
+            not a pixmap or something.  (This does not clear the window.) */
+         XSetWindowBackground (si->dpy, ssi->screensaver_window,
+                               ssi->black_pixel);
+       }
+
+      if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
+
+      XGrabServer (si->dpy);                   /* ############ DANGER! */
+
+      /* 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/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, "%s: fading done.\n", blurb());
+
+#ifdef HAVE_MIT_SAVER_EXTENSION
+      for (i = 0; i < si->nscreens; i++)
+       {
+         saver_screen_info *ssi = &si->screens[i];
+         if (ssi->server_mit_saver_window &&
+             window_exists_p (si->dpy, ssi->server_mit_saver_window))
+           XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
+       }
+#endif /* HAVE_MIT_SAVER_EXTENSION */
+
+      XUngrabServer (si->dpy);
+      XSync (si->dpy, False);                  /* ###### (danger over) */
     }
   else
     {
-      XClearWindow (dpy, screensaver_window);
-      XMapRaised (dpy, screensaver_window);
-#ifdef HAVE_SAVER_EXTENSION
-      if (server_saver_window && window_exists_p (dpy, server_saver_window))
-       XUnmapWindow (dpy, server_saver_window);
-#endif /* HAVE_SAVER_EXTENSION */
+      for (i = 0; i < si->nscreens; i++)
+       {
+         saver_screen_info *ssi = &si->screens[i];
+         if (!dont_clear)
+           XClearWindow (si->dpy, ssi->screensaver_window);
+         if (!dont_clear || ssi->stderr_overlay_window)
+           clear_stderr (ssi);
+         XMapRaised (si->dpy, ssi->screensaver_window);
+#ifdef HAVE_MIT_SAVER_EXTENSION
+         if (ssi->server_mit_saver_window &&
+             window_exists_p (si->dpy, ssi->server_mit_saver_window))
+           XUnmapWindow (si->dpy, ssi->server_mit_saver_window);
+#endif /* HAVE_MIT_SAVER_EXTENSION */
+       }
     }
 
-  if (install_cmap_p)
-    XInstallColormap (dpy, cmap);
+  for (i = 0; i < si->nscreens; i++)
+    {
+      saver_screen_info *ssi = &si->screens[i];
+      if (ssi->cmap)
+       XInstallColormap (si->dpy, ssi->cmap);
+    }
 }
 
-void
-blank_screen ()
-{
-  save_real_vroot ();
-  store_vroot_property (screensaver_window, screensaver_window);
-  raise_window (False, False);
-  grab_keyboard_and_mouse ();
-#ifdef __hpux
-  if (lock_p)
-    XHPDisableReset (dpy);     /* turn off C-Sh-Reset */
-#endif
+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];
+
+      save_real_vroot (ssi);
+      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 */
+    }
+
+  raise_window (si, False, False, False);
+
+  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 ()
-{
-  if (unfade_p && !demo_mode_p)
-    {
-      int grabbed;
-      Colormap default_map = DefaultColormapOfScreen (screen);
-      blacken_colormap (dpy, cmap2);
-      XGrabServer (dpy);
-      /* grab and blacken mouse on the root window. */
-      grabbed = grab_mouse (RootWindowOfScreen (screen));
-      XInstallColormap (dpy, cmap2);
-      XUnmapWindow (dpy, screensaver_window);
-      fade_colormap (dpy, default_map, cmap2, fade_seconds, fade_ticks, False);
-      XInstallColormap (dpy, default_map);
-      if (grabbed == GrabSuccess)
-       XUngrabPointer (dpy, CurrentTime);
-      XUngrabServer (dpy);
+unblank_screen (saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
+  Bool unfade_p = (si->fading_possible_p && p->unfade_p);
+  int i;
+
+  monitor_power_on (si);
+  reset_watchdog_timer (si, False);
+
+  if (si->demoing_p)
+    unfade_p = False;
+
+  if (unfade_p)
+    {
+      Window *current_windows = (Window *)
+       calloc(sizeof(Window), si->nscreens);
+
+      for (i = 0; i < si->nscreens; i++)
+       {
+         saver_screen_info *ssi = &si->screens[i];
+         current_windows[i] = ssi->screensaver_window;
+         /* Ensure that the default background of the window is really black,
+            not a pixmap or something.  (This does not clear the window.) */
+         XSetWindowBackground (si->dpy, ssi->screensaver_window,
+                               ssi->black_pixel);
+       }
+
+      if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
+
+
+      XSync (si->dpy, False);
+      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];
+         clear_stderr (ssi);
+       }
+
+      XUngrabServer (si->dpy);
+      XSync (si->dpy, False);                  /* ###### (danger over) */
+
+
+      fade_screens (si->dpy, 0, current_windows,
+                   p->fade_seconds/1000, p->fade_ticks,
+                   False, False);
+
+      free(current_windows);
+      current_windows = 0;
+
+      if (p->verbose_p) fprintf (stderr, "%s: unfading done.\n", blurb());
     }
   else
     {
-      if (install_cmap_p)
+      for (i = 0; i < si->nscreens; i++)
        {
-         XClearWindow (dpy, screensaver_window); /* avoid technicolor */
-         XInstallColormap (dpy, DefaultColormapOfScreen (screen));
+         saver_screen_info *ssi = &si->screens[i];
+         if (ssi->cmap)
+           {
+             Colormap c = DefaultColormapOfScreen (ssi->screen);
+             /* avoid technicolor */
+             XClearWindow (si->dpy, ssi->screensaver_window);
+             if (c) XInstallColormap (si->dpy, c);
+           }
+         XUnmapWindow (si->dpy, ssi->screensaver_window);
        }
-      XUnmapWindow (dpy, screensaver_window);
-    }
-  kill_xsetroot_data ();
-  ungrab_keyboard_and_mouse ();
-  restore_real_vroot ();
-#ifdef __hpux
-  if (lock_p)
-    XHPEnableReset (dpy);      /* turn C-Sh-Reset back on */
+    }
+
+
+  /* If the focus window does has a non-default colormap, then install
+     that colormap as well.  (On SGIs, this will cause both the root map
+     and the focus map to be installed simultaniously.  It'd be nice to
+     pick up the other colormaps that had been installed, too; perhaps
+     XListInstalledColormaps could be used for that?)
+   */
+  {
+    Window focus = 0;
+    int revert_to;
+    XGetInputFocus (si->dpy, &focus, &revert_to);
+    if (focus && focus != PointerRoot && focus != None)
+      {
+       XWindowAttributes xgwa;
+       xgwa.colormap = 0;
+       XGetWindowAttributes (si->dpy, focus, &xgwa);
+       if (xgwa.colormap &&
+           xgwa.colormap != DefaultColormapOfScreen (xgwa.screen))
+         XInstallColormap (si->dpy, xgwa.colormap);
+      }
+  }
+
+
+  for (i = 0; i < si->nscreens; i++)
+    {
+      saver_screen_info *ssi = &si->screens[i];
+      kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
+    }
+
+  store_saver_status (si);  /* store unblank time */
+  ungrab_keyboard_and_mouse (si);
+  restore_real_vroot (si);
+
+  /* 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.)
+  */
+  for (i = 0; i < si->nscreens; i++)
+    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
+maybe_transfer_grabs (saver_screen_info *ssi,
+                      Window old_w, Window new_w)
+{
+  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));
+      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)
+{
+  saver_info *si = ssi->global;
+  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 = 0;
+  Bool got_it;
+
+  if (visual_name && *visual_name)
+    {
+      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") ||
+               !strcmp(visual_name, "Default-n") ||
+               !strcmp(visual_name, "Default-N"))
+       {
+         visual_name = "default";
+         install_cmap_p = 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
+    {
+      new_v = ssi->default_visual;
+    }
+
+  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;
+
+  if (new_v &&
+      ((ssi->current_visual != new_v) ||
+       (install_cmap_p != was_installed_p)))
+    {
+      Colormap old_c = ssi->cmap;
+      Window old_w = ssi->screensaver_window;
+
+      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:                from ", blurb());
+         describe_visual (stderr, ssi->screen, ssi->current_visual,
+                          was_installed_p);
 #endif
+       }
+
+      reset_stderr (ssi);
+      ssi->current_visual = new_v;
+      ssi->current_depth = visual_depth(ssi->screen, new_v);
+      ssi->cmap = 0;
+      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);
+
+      /* 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)
+       XFreeColormap (si->dpy, old_c);
+    }
+
+  return got_it;
 }