http://ftp.x.org/contrib/applications/xscreensaver-2.24.tar.gz
[xscreensaver] / driver / windows.c
index 7a938c5a53a7f394bc4c605c0aed99c0238041c6..a460e6da4937377d9727e951b449c6d4439d60b1 100644 (file)
@@ -65,7 +65,7 @@
 extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
 Atom XA_VROOT, XA_XSETROOT_ID;
-Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
+Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
 Atom XA_SCREENSAVER_TIME;
 
 
@@ -80,50 +80,135 @@ static void store_activate_time (saver_info *si, Bool use_last_p);
         Button1MotionMask | Button2MotionMask | Button3MotionMask | \
         Button4MotionMask | Button5MotionMask | ButtonMotionMask)
 
-/* I don't really understand Sync vs Async, but these seem to work... */
-#define grab_kbd(dpy,win) \
-  XGrabKeyboard ((dpy), (win), True, GrabModeSync, GrabModeAsync, CurrentTime)
-#define grab_mouse(dpy,win,cursor) \
-  XGrabPointer ((dpy), (win), True, ALL_POINTER_EVENTS, \
-               GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime)
+
+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->debug_p)
+    fprintf(stderr, "%s: XGrabKeyboard(... 0x%x ...) ==> %s\n",
+           blurb(), (unsigned long) w,
+           (status == GrabSuccess ? "GrabSuccess" :
+            status == AlreadyGrabbed ? "AlreadyGrabbed" :
+            status == GrabInvalidTime ? "GrabInvalidTime" :
+            status == GrabNotViewable ? "GrabNotViewable" :
+            status == GrabFrozen ? "GrabFrozen" :
+            "???"));
+
+  return status;
+}
+
+static const char *
+grab_string(int status)
+{
+  switch (status)
+    {
+    case GrabSuccess:     return "GrabSuccess";     break;
+    case AlreadyGrabbed:  return "AlreadyGrabbed";  break;
+    case GrabInvalidTime: return "GrabInvalidTime"; break;
+    case GrabNotViewable: return "GrabNotViewable"; break;
+    case GrabFrozen:      return "GrabFrozen";      break;
+    default:
+      {
+       static char foo[255];
+       sprintf(foo, "unknown status: %d", status);
+       return foo;
+      }
+    }
+}
+
+
+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, None,
+                            cursor, CurrentTime);
+  if (status == GrabSuccess)
+    si->mouse_grab_window = w;
+
+  if (p->debug_p)
+    fprintf(stderr, "%s: XGrabPointer(... 0x%x, 0x%x ...) ==> %s\n",
+           blurb(), (unsigned long) w, (unsigned long) cursor,
+           grab_string(status));
+  return status;
+}
+
+
+static void
+ungrab_kbd(saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
+  XUngrabKeyboard(si->dpy, CurrentTime);
+  if (p->debug_p)
+    fprintf(stderr, "%s: XUngrabKeyboard (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->debug_p)
+    fprintf(stderr, "%s: XUngrabPointer (was 0x%x)\n", blurb(),
+           (unsigned long) si->mouse_grab_window);
+  si->mouse_grab_window = 0;
+}
+
 
 void
-grab_keyboard_and_mouse (Display *dpy, Window window, Cursor cursor)
+grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
 {
   Status status;
-  XSync (dpy, False);
+  XSync (si->dpy, False);
 
-  status = grab_kbd (dpy, window);
+  status = grab_kbd (si, window);
   if (status != GrabSuccess)
     {  /* try again in a second */
       sleep (1);
-      status = grab_kbd (dpy, window);
+      status = grab_kbd (si, window);
       if (status != GrabSuccess)
-       fprintf (stderr, "%s: couldn't grab keyboard!  (%d)\n",
-                progname, status);
+       fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
+                blurb(), grab_string(status));
     }
-  status = grab_mouse (dpy, window, cursor);
+
+  status = grab_mouse (si, window, cursor);
   if (status != GrabSuccess)
     {  /* try again in a second */
       sleep (1);
-      status = grab_mouse (dpy, window, cursor);
+      status = grab_mouse (si, window, cursor);
       if (status != GrabSuccess)
-       fprintf (stderr, "%s: couldn't grab pointer!  (%d)\n",
-                progname, status);
+       fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
+                blurb(), grab_string(status));
     }
 }
 
 void
-ungrab_keyboard_and_mouse (Display *dpy)
+ungrab_keyboard_and_mouse (saver_info *si)
 {
-  XUngrabPointer (dpy, CurrentTime);
-  XUngrabKeyboard (dpy, CurrentTime);
+  ungrab_mouse (si);
+  ungrab_kbd (si);
 }
 
 
-void
+/* Prints an error message to stderr and returns True if there is another
+   xscreensaver running already.  Silently returns False otherwise. */
+Bool
 ensure_no_screensaver_running (Display *dpy, Screen *screen)
 {
+  Bool status = 0;
   int i;
   Window root = RootWindowOfScreen (screen);
   Window root2, parent, *kids;
@@ -159,14 +244,15 @@ ensure_no_screensaver_running (Display *dpy, Screen *screen)
 
          fprintf (stderr,
       "%s: already running on display %s (window 0x%x)\n from process %s.\n",
-                  progname, DisplayString (dpy), (int) kids [i], id);
-         exit (1);
+                  blurb(), DisplayString (dpy), (int) kids [i], id);
+         status = True;
        }
     }
 
   if (kids) XFree ((char *) kids);
   XSync (dpy, False);
   XSetErrorHandler (old_handler);
+  return status;
 }
 
 
@@ -183,7 +269,7 @@ store_vroot_property (Display *dpy, Window win, Window value)
 #if 0
   if (p->verbose_p)
     fprintf (stderr,
-            "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", progname
+            "%s: storing XA_VROOT = 0x%x (%s) = 0x%x (%s)\n", blurb()
             win,
             (win == screensaver_window ? "ScreenSaver" :
              (win == real_vroot ? "VRoot" :
@@ -202,7 +288,7 @@ remove_vroot_property (Display *dpy, Window win)
 {
 #if 0
   if (p->verbose_p)
-    fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", progname, win, 
+    fprintf (stderr, "%s: removing XA_VROOT from 0x%x (%s)\n", blurb(), win, 
             (win == screensaver_window ? "ScreenSaver" :
              (win == real_vroot ? "VRoot" :
               (win == real_vroot_value ? "Vroot_value" : "???"))));
@@ -242,13 +328,13 @@ kill_xsetroot_data (Display *dpy, Window window, Bool verbose_p)
        {
          if (verbose_p)
            printf ("%s: destroying xsetroot data (0x%lX).\n",
-                   progname, *dataP);
+                   blurb(), *dataP);
          XKillClient (dpy, *dataP);
        }
       else
        fprintf (stderr, "%s: deleted unrecognised _XSETROOT_ID property: \n\
        %lu, %lu; type: %lu, format: %d, nitems: %lu, bytesafter %ld\n",
-                progname, (unsigned long) dataP, (dataP ? *dataP : 0), type,
+                blurb(), (unsigned long) dataP, (dataP ? *dataP : 0), type,
                 format, nitems, bytesafter);
     }
 }
@@ -294,7 +380,7 @@ save_real_vroot (saver_screen_info *ssi)
          if (*vrootP == ssi->screensaver_window) abort ();
          fprintf (stderr,
            "%s: more than one virtual root window found (0x%x and 0x%x).\n",
-                  progname, (int) ssi->real_vroot, (int) kids [i]);
+                  blurb(), (int) ssi->real_vroot, (int) kids [i]);
          exit (1);
        }
       ssi->real_vroot = kids [i];
@@ -319,7 +405,7 @@ restore_real_vroot_2 (saver_screen_info *ssi)
   saver_preferences *p = &si->prefs;
   if (p->verbose_p && ssi->real_vroot)
     printf ("%s: restoring __SWM_VROOT property on the real vroot (0x%lx).\n",
-           progname, (unsigned long) ssi->real_vroot);
+           blurb(), (unsigned long) ssi->real_vroot);
   remove_vroot_property (si->dpy, ssi->screensaver_window);
   if (ssi->real_vroot)
     {
@@ -444,7 +530,7 @@ restore_real_vroot_handler (int sig)
   signal (sig, SIG_DFL);
   if (restore_real_vroot_1 (si))
     fprintf (real_stderr, "\n%s: %s intercepted, vroot restored.\n",
-            progname, signal_name(sig));
+            blurb(), signal_name(sig));
   kill (getpid (), sig);
 }
 
@@ -458,7 +544,7 @@ catch_signal (saver_info *si, int sig, Bool on_p)
       if (((long) signal (sig, restore_real_vroot_handler)) == -1L)
        {
          char buf [255];
-         sprintf (buf, "%s: couldn't catch %s", progname, signal_name(sig));
+         sprintf (buf, "%s: couldn't catch %s", blurb(), signal_name(sig));
          perror (buf);
          saver_exit (si, 1);
        }
@@ -517,9 +603,9 @@ saver_exit (saver_info *si, int status)
   emergency_kill_subproc (si);
 
   if (vrs && (p->verbose_p || status != 0))
-    fprintf (real_stderr, "%s: vroot restored, exiting.\n", progname);
+    fprintf (real_stderr, "%s: vroot restored, exiting.\n", blurb());
   else if (p->verbose_p)
-    fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", progname);
+    fprintf (real_stderr, "%s: no vroot to restore; exiting.\n", blurb());
 
   fflush(real_stdout);
 
@@ -528,11 +614,12 @@ saver_exit (saver_info *si, int status)
   else if (status == 1) status = -1;
 #endif
 
-#ifdef DEBUG
   if (si->prefs.debug_p)
-    /* Do this to drop a core file, so that we can get a stack trace. */
-    abort();
-#endif
+    {
+      fprintf(real_stderr, "%s: dumping core (because of -debug)\n", blurb());
+      /* Do this to drop a core file, so that we can get a stack trace. */
+      abort();
+    }
 
   exit (status);
 }
@@ -558,7 +645,7 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
 {
   saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
-  Bool install_cmap_p = ssi->install_cmap_p;
+  Bool install_cmap_p = (ssi->install_cmap_p || 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
@@ -624,24 +711,25 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
   attrs.backing_pixel = ssi->black_pixel;
   attrs.border_pixel = ssi->black_pixel;
 
-#ifdef DEBUG
   if (p->debug_p) width = width / 2;
-#endif /* DEBUG */
 
   if (!p->verbose_p || printed_visual_info)
     ;
   else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
     {
-      fprintf (stderr, "%s: using default visual ", progname);
-      describe_visual (stderr, ssi->screen, ssi->current_visual);
+      fprintf (stderr, "%s: using default visual ", blurb());
+      describe_visual (stderr, ssi->screen, ssi->current_visual,
+                      install_cmap_p);
     }
   else
     {
-      fprintf (stderr, "%s: using visual:   ", progname);
-      describe_visual (stderr, ssi->screen, ssi->current_visual);
-      fprintf (stderr, "%s: default visual: ", progname);
+      fprintf (stderr, "%s: using visual:   ", blurb());
+      describe_visual (stderr, ssi->screen, ssi->current_visual,
+                      install_cmap_p);
+      fprintf (stderr, "%s: default visual: ", blurb());
       describe_visual (stderr, ssi->screen,
-                      DefaultVisualOfScreen (ssi->screen));
+                      DefaultVisualOfScreen (ssi->screen),
+                      ssi->install_cmap_p);
     }
   printed_visual_info = True;
 
@@ -721,7 +809,7 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
       store_activate_time(si, True);
       if (p->verbose_p)
        fprintf (stderr, "%s: saver window is 0x%lx.\n",
-                progname, (unsigned long) ssi->screensaver_window);
+                blurb(), (unsigned long) ssi->screensaver_window);
     }
 
 #ifdef HAVE_MIT_SAVER_EXTENSION
@@ -828,10 +916,19 @@ raise_window (saver_info *si,
                                ssi->black_pixel);
        }
 
-      if (p->verbose_p) fprintf (stderr, "%s: fading... ", progname);
+      if (p->verbose_p) fprintf (stderr, "%s: fading... ", blurb());
 
-      XGrabServer (si->dpy);
+      XGrabServer (si->dpy);                   /* ############ DANGER! */
 
+      /* Clear the stderr layer on each screen.
+        Grab the mouse on the first screen on which the mouse is grabbable
+        (if there are multiple heads, I think you might only be able to
+        grab the mouse on the screen that currently has the mouse?  Anyway,
+        we only grab the mouse once, and don't try again after the grab
+        has succeeded.)  We grab the mouse on the root window of the screen,
+        not on the screensaver window, since the screensaver window is not
+        yet mapped.
+       */
       for (i = 0; i < si->nscreens; i++)
        {
          saver_screen_info *ssi = &si->screens[i];
@@ -839,8 +936,11 @@ raise_window (saver_info *si,
          /* grab and blacken mouse on the root window (saver not mapped yet)
           */
          if (grabbed != GrabSuccess)
-           grabbed = grab_mouse (si->dpy, ssi->screensaver_window,
-                                 (si->demo_mode_p ? 0 : ssi->cursor));
+           {
+             Window root = RootWindowOfScreen(ssi->screen);
+             grabbed = grab_mouse (si, root,
+                                   (si->demo_mode_p ? 0 : ssi->cursor));
+           }
 
          if (!dont_clear || ssi->stderr_overlay_window)
            /* Do this before the fade, since the stderr cmap won't fade
@@ -848,8 +948,11 @@ raise_window (saver_info *si,
            clear_stderr (ssi);
        }
 
+      /* Note!  The server is grabbed, and this will take several seconds
+        to complete! */
       fade_screens (si->dpy, current_maps, current_windows,
                    p->fade_seconds, p->fade_ticks, True, !dont_clear);
+
       free(current_maps);
       free(current_windows);
       current_maps = 0;
@@ -867,9 +970,12 @@ raise_window (saver_info *si,
        }
 #endif /* HAVE_MIT_SAVER_EXTENSION */
 
+      /* If we had successfully grabbed the mouse, let it go now. */
       if (grabbed == GrabSuccess)
-       XUngrabPointer (si->dpy, CurrentTime);
+       ungrab_mouse (si);
+
       XUngrabServer (si->dpy);
+      XSync (si->dpy, False);                  /* ###### (danger over) */
     }
   else
     {
@@ -912,9 +1018,16 @@ blank_screen (saver_info *si)
     }
   store_activate_time (si, si->screen_blanked_p);
   raise_window (si, False, False, False);
-  /* #### */
-  grab_keyboard_and_mouse (si->dpy, si->screens[0].screensaver_window,
+
+  /* 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.
+   */
+  grab_keyboard_and_mouse (si,
+                          /*si->screens[0].screensaver_window,*/
+                          RootWindowOfScreen(si->screens[0].screen),
                           (si->demo_mode_p ? 0 : si->screens[0].cursor));
+
 #ifdef HAVE_XHPDISABLERESET
   if (si->locked_p && !hp_locked_p)
     {
@@ -932,6 +1045,8 @@ unblank_screen (saver_info *si)
   saver_preferences *p = &si->prefs;
   int i;
 
+  monitor_power_on (si);
+
   store_activate_time (si, True);
   reset_watchdog_timer (si, False);
 
@@ -951,21 +1066,32 @@ unblank_screen (saver_info *si)
                                ssi->black_pixel);
        }
 
-      if (p->verbose_p) fprintf (stderr, "%s: unfading... ", progname);
+      if (p->verbose_p) fprintf (stderr, "%s: unfading... ", blurb());
+
 
       XSync (si->dpy, False);
-      XGrabServer (si->dpy);
+      XGrabServer (si->dpy);                   /* ############ DANGER! */
       XSync (si->dpy, False);
+
+      /* Clear the stderr layer on each screen.
+        Grab the mouse on the first screen on which the mouse is grabbable
+        (if there are multiple heads, I think you might only be able to
+        grab the mouse on the screen that currently has the mouse?  Anyway,
+        we only grab the mouse once, and don't try again after the grab
+        has succeeded.)
+       */
       for (i = 0; i < si->nscreens; i++)
        {
          saver_screen_info *ssi = &si->screens[i];
          if (grabbed != GrabSuccess)
-           grabbed = grab_mouse (si->dpy, RootWindowOfScreen (ssi->screen),
+           grabbed = grab_mouse (si, RootWindowOfScreen (ssi->screen),
                                  0);
          clear_stderr (ssi);
        }
+
       XUngrabServer (si->dpy);
-      XSync (si->dpy, False);
+      XSync (si->dpy, False);                  /* ###### (danger over) */
+
 
       fade_screens (si->dpy, 0, current_windows,
                    p->fade_seconds, p->fade_ticks,
@@ -975,8 +1101,10 @@ unblank_screen (saver_info *si)
       current_windows = 0;
 
       if (p->verbose_p) fprintf (stderr, "unfading done.\n");
+
+      /* If we had successfully grabbed the mouse, let it go now. */
       if (grabbed == GrabSuccess)
-       XUngrabPointer (si->dpy, CurrentTime);
+       ungrab_mouse (si);
     }
   else
     {
@@ -1025,7 +1153,7 @@ unblank_screen (saver_info *si)
 
   store_activate_time(si, False);  /* store unblank time */
 
-  ungrab_keyboard_and_mouse (si->dpy);
+  ungrab_keyboard_and_mouse (si);
   restore_real_vroot (si);
 
 #ifdef HAVE_XHPDISABLERESET
@@ -1112,16 +1240,12 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
 
       if (p->verbose_p)
        {
+         fprintf (stderr, "%s: switching to visual ", blurb());
+         describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
 #if 0
-         fprintf (stderr, "%s: switching visuals\tfrom: ", progname);
-         describe_visual (stderr, ssi->screen, ssi->current_visual);
-         fprintf (stderr, "\t\t\t\tto:   ");
-         describe_visual (stderr, ssi->screen, new_v);
-         fprintf (stderr, "\t\t\t\t install cmap:   %s\n",
-                  (install_cmap_p ? "yes" : "no"));
-#else
-         fprintf (stderr, "%s: switching to visual ", progname);
-         describe_visual (stderr, ssi->screen, new_v);
+         fprintf (stderr, "%s:                from ", blurb());
+         describe_visual (stderr, ssi->screen, ssi->current_visual,
+                          was_installed_p);
 #endif
        }
 
@@ -1137,7 +1261,50 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
                            ssi->screensaver_window, ssi->screensaver_window);
       store_activate_time (si, True);
 
+
+
+      /* 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->demo_mode_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. */
+
       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)