http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.34.tar.gz
[xscreensaver] / driver / windows.c
index a61788ba3510df0ebd07680a5b0c8afda8fe4180..b0198e14a528c2512b0de882d86764e0153281f1 100644 (file)
@@ -1,5 +1,5 @@
 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
- * xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@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
 # include <X11/extensions/xf86vmode.h>
 #endif /* HAVE_XF86VMODE */
 
-#ifdef HAVE_XHPDISABLERESET
-# include <X11/XHPlib.h>
-
- /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
-    or BadAccess errors occur.  (Ok for this to be global, since it
-    affects the whole machine, not just the current screen.) */
-  Bool hp_locked_p = False;
-
-#endif /* HAVE_XHPDISABLERESET */
-
 
 /* This file doesn't need the Xt headers, so stub these types out... */
 #undef XtPointer
 #include "fade.h"
 
 
-#ifdef HAVE_VT_LOCKSWITCH
-# include <fcntl.h>
-# include <sys/ioctl.h>
-# include <sys/vt.h>
-  static void lock_vt (saver_info *si, Bool lock_p);
-#endif /* HAVE_VT_LOCKSWITCH */
-
-
 extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
 Atom XA_VROOT, XA_XSETROOT_ID;
 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
-Atom XA_SCREENSAVER_TIME;
+Atom XA_SCREENSAVER_STATUS;
 
 
 extern saver_info *global_si_kludge;   /* I hate C so much... */
 
-
-static void store_activate_time (saver_info *si, Bool use_last_p);
+static void maybe_transfer_grabs (saver_screen_info *ssi,
+                                  Window old_w, Window new_w);
 
 #define ALL_POINTER_EVENTS \
        (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
@@ -179,28 +161,39 @@ static Bool
 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
 {
   Status mstatus, kstatus;
-  XSync (si->dpy, False);
+  int i;
+  int retries = 4;
 
-  kstatus = grab_kbd (si, window);
-  if (kstatus != GrabSuccess)
-    {  /* try again in a second */
-      sleep (1);
+  for (i = 0; i < retries; i++)
+    {
+      XSync (si->dpy, False);
       kstatus = grab_kbd (si, window);
-      if (kstatus != GrabSuccess)
-       fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
-                blurb(), grab_string(kstatus));
-    }
+      if (kstatus == GrabSuccess)
+        break;
 
-  mstatus = grab_mouse (si, window, cursor);
-  if (mstatus != GrabSuccess)
-    {  /* try again in a second */
+      /* else, wait a second and try to grab again. */
       sleep (1);
+    }
+
+  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)
-       fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
-                blurb(), grab_string(mstatus));
+      if (mstatus == GrabSuccess)
+        break;
+
+      /* else, wait a second and try to grab again. */
+      sleep (1);
     }
 
+  if (mstatus != GrabSuccess)
+    fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
+             blurb(), grab_string(mstatus));
+
   return (kstatus == GrabSuccess ||
          mstatus == GrabSuccess);
 }
@@ -633,7 +626,9 @@ handle_signals (saver_info *si, Bool on_p)
   catch_signal (si, SIGQUIT, on_p);
   catch_signal (si, SIGILL,  on_p);
   catch_signal (si, SIGTRAP, on_p);
+#ifdef SIGIOT
   catch_signal (si, SIGIOT,  on_p);
+#endif
   catch_signal (si, SIGABRT, on_p);
 #ifdef SIGEMT
   catch_signal (si, SIGEMT,  on_p);
@@ -671,11 +666,10 @@ saver_exit (saver_info *si, int status, const char *dump_core_reason)
   
   vrs = restore_real_vroot_1 (si);
   emergency_kill_subproc (si);
+  shutdown_stderr (si);
 
-  if (vrs && (p->verbose_p || status != 0))
-    fprintf (real_stderr, "%s: vroot restored, exiting.\n", 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);
 
@@ -749,7 +743,7 @@ store_saver_id (saver_screen_info *ssi)
   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;
@@ -804,6 +798,35 @@ store_saver_id (saver_screen_info *ssi)
 }
 
 
+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
@@ -821,12 +844,17 @@ get_screen_viewport (saver_screen_info *ssi,
 #ifdef HAVE_XF86VMODE
   saver_info *si = ssi->global;
   int screen_no = screen_number (ssi->screen);
-  int event, error;
+  int op, event, error;
   int dot;
   XF86VidModeModeLine ml;
   int x, y;
 
-  if (XF86VidModeQueryExtension (si->dpy, &event, &error) &&
+  /* 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))
     {
@@ -840,6 +868,35 @@ get_screen_viewport (saver_screen_info *ssi,
         /* 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);
 
@@ -904,6 +961,80 @@ 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 void
 initialize_screensaver_window_1 (saver_screen_info *ssi)
 {
@@ -921,8 +1052,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 (si->default_screen, &x, &y, &width, &height,
+  get_screen_viewport (ssi, &x, &y, &width, &height,
                        (p->verbose_p && !si->screen_blanked_p));
 
   black.red = black.green = black.blue = 0;
@@ -1060,12 +1192,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),
@@ -1074,14 +1211,23 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
                       ssi->current_visual, attrmask, &attrs);
 
       reset_stderr (ssi);
-      store_activate_time(si, True);
+
+      if (horked_window)
+        {
+          fprintf (stderr,
+            "%s: someone horked our saver window (0x%lx)!  Recreating it...\n",
+                   blurb(), (unsigned long) horked_window);
+          maybe_transfer_grabs (ssi, horked_window, ssi->screensaver_window);
+          safe_XDestroyWindow (si->dpy, horked_window);
+          horked_window = 0;
+        }
+
       if (p->verbose_p)
        fprintf (stderr, "%s: saver window is 0x%lx.\n",
                 blurb(), (unsigned long) ssi->screensaver_window);
     }
 
-
-  store_saver_id (ssi);
+  store_saver_id (ssi);       /* store window name and IDs */
 
   if (!ssi->cursor)
     {
@@ -1266,28 +1412,19 @@ blank_screen (saver_info *si)
       }
 #endif /* HAVE_XF86VMODE */
     }
-  store_activate_time (si, si->screen_blanked_p);
-  raise_window (si, False, False, False);
 
-#ifdef HAVE_XHPDISABLERESET
-  if (si->locked_p && !hp_locked_p)
-    {
-      XHPDisableReset (si->dpy);       /* turn off C-Sh-Reset */
-      hp_locked_p = True;
-    }
-#endif
-
-#ifdef HAVE_VT_LOCKSWITCH
-  if (si->locked_p)
-      lock_vt (si, True);              /* turn off C-Alt-Fn */
-#endif
+  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 (saver_info *si)
 {
@@ -1296,8 +1433,6 @@ unblank_screen (saver_info *si)
   int i;
 
   monitor_power_on (si);
-
-  store_activate_time (si, True);
   reset_watchdog_timer (si, False);
 
   if (si->demoing_p)
@@ -1391,23 +1526,10 @@ unblank_screen (saver_info *si)
       kill_xsetroot_data (si->dpy, ssi->screensaver_window, p->verbose_p);
     }
 
-  store_activate_time(si, False);  /* store unblank time */
-
+  store_saver_status (si);  /* store unblank time */
   ungrab_keyboard_and_mouse (si);
   restore_real_vroot (si);
 
-#ifdef HAVE_XHPDISABLERESET
-  if (hp_locked_p)
-    {
-      XHPEnableReset (si->dpy);        /* turn C-Sh-Reset back on */
-      hp_locked_p = False;
-    }
-#endif
-
-#ifdef HAVE_VT_LOCKSWITCH
-  lock_vt (si, False);         /* turn C-Alt-Fn back on */
-#endif
-
   /* Unmap the windows a second time, dammit -- just to avoid a race
      with the screen-grabbing hacks.  (I'm not sure if this is really
      necessary; I'm stabbing in the dark now.)
@@ -1416,30 +1538,54 @@ unblank_screen (saver_info *si)
     XUnmapWindow (si->dpy, si->screens[i].screensaver_window);
 
   si->screen_blanked_p = False;
+  si->blank_time = time ((time_t) 0);
   si->last_wall_clock_time = 0;
+
+  store_saver_status (si);  /* store unblank time */
 }
 
 
+/* Transfer any grabs from the old window to the new.
+   Actually I think none of this is necessary, since we always
+   hold our grabs on the root window, but I wrote this before
+   re-discovering that...
+ */
 static void
-store_activate_time (saver_info *si, Bool use_last_p)
+maybe_transfer_grabs (saver_screen_info *ssi,
+                      Window old_w, Window new_w)
 {
-  static time_t last_time = 0;
-  time_t now = ((use_last_p && last_time) ? last_time : time ((time_t) 0));
-  CARD32 now32 = (CARD32) now;
-  int i;
-  last_time = now;
+  saver_info *si = ssi->global;
 
-  for (i = 0; i < si->nscreens; i++)
+  /* If the old window held our mouse grab, transfer the grab to the new
+     window.  (Grab the server while so doing, to avoid a race condition.)
+   */
+  if (old_w == si->mouse_grab_window)
     {
-      saver_screen_info *ssi = &si->screens[i];
-      if (!ssi->screensaver_window) continue;
-      XChangeProperty (si->dpy, ssi->screensaver_window, XA_SCREENSAVER_TIME,
-                      XA_INTEGER, 32, PropModeReplace,
-                      (unsigned char *) &now32, 1);
+      XGrabServer (si->dpy);           /* ############ DANGER! */
+      ungrab_mouse (si);
+      grab_mouse (si, ssi->screensaver_window,
+                  (si->demoing_p
+                   ? 0
+                   : ssi->cursor));
+      XUngrabServer (si->dpy);
+      XSync (si->dpy, False);          /* ###### (danger over) */
+    }
+
+  /* If the old window held our keyboard grab, transfer the grab to the new
+     window.  (Grab the server while so doing, to avoid a race condition.)
+   */
+  if (old_w == si->keyboard_grab_window)
+    {
+      XGrabServer (si->dpy);           /* ############ DANGER! */
+      ungrab_kbd(si);
+      grab_kbd(si, ssi->screensaver_window);
+      XUngrabServer (si->dpy);
+      XSync (si->dpy, False);          /* ###### (danger over) */
     }
 }
 
 
+
 Bool
 select_visual (saver_screen_info *ssi, const char *visual_name)
 {
@@ -1452,25 +1598,29 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
 
   if (visual_name && *visual_name)
     {
-      if (!strcmp(visual_name, "default-i"))
+      if (!strcmp(visual_name, "default-i") ||
+          !strcmp(visual_name, "Default-i") ||
+          !strcmp(visual_name, "Default-I")
+          )
        {
          visual_name = "default";
          install_cmap_p = True;
        }
-      else if (!strcmp(visual_name, "default-n"))
+      else if (!strcmp(visual_name, "default-n") ||
+               !strcmp(visual_name, "Default-n") ||
+               !strcmp(visual_name, "Default-N"))
        {
          visual_name = "default";
          install_cmap_p = False;
        }
-#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);
@@ -1523,48 +1673,11 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
       raise_window (si, True, True, False);
       store_vroot_property (si->dpy,
                            ssi->screensaver_window, ssi->screensaver_window);
-      store_activate_time (si, True);
-
 
+      /* Transfer any grabs from the old window to the new. */
+      maybe_transfer_grabs (ssi, old_w, ssi->screensaver_window);
 
-      /* 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)
@@ -1579,56 +1692,3 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
 
   return got_it;
 }
-
-\f
-/* VT locking */
-
-#ifdef HAVE_VT_LOCKSWITCH
-static void
-lock_vt (saver_info *si, Bool lock_p)
-{
-  saver_preferences *p = &si->prefs;
-  static Bool locked_p = False;
-  const char *dev_console = "/dev/console";
-  int fd;
-
-  if (lock_p == locked_p)
-    return;
-
-  if (lock_p && !p->lock_vt_p)
-    return;
-
-  fd = open (dev_console, O_RDWR);
-  if (fd < 0)
-    {
-      char buf [255];
-      sprintf (buf, "%s: couldn't %s VTs: %s", blurb(),
-              (lock_p ? "lock" : "unlock"),
-              dev_console);
-#if 0 /* #### doesn't work yet, so don't bother complaining */
-      perror (buf);
-#endif
-      return;
-    }
-
-  if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0)
-    {
-      locked_p = lock_p;
-
-      if (p->verbose_p)
-       fprintf (stderr, "%s: %s VTs\n", blurb(),
-                (lock_p ? "locked" : "unlocked"));
-    }
-  else
-    {
-      char buf [255];
-      sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(),
-              (lock_p ? "lock" : "unlock"));
-#if 0 /* #### doesn't work yet, so don't bother complaining */
-      perror (buf);
-#endif
-    }
-
-  close (fd);
-}
-#endif /* HAVE_VT_LOCKSWITCH */