http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / utils / fade.c
index 00eb9d1ad0610aec7c0fc5a94b72602f6fe0ea5e..d3f7ca3aa051ff1e808f7768b27957cfc66ab5df 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1992-2001 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1992-2008 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
@@ -73,24 +73,28 @@ blacken_colormap (Screen *screen, Colormap cmap)
 
 
 static void fade_screens_1 (Display *dpy, Colormap *cmaps,
-                           Window *black_windows, int seconds, int ticks,
+                           Window *black_windows, int nwindows,
+                            int seconds, int ticks,
                            Bool out_p, Bool clear_windows);
 
 #ifdef HAVE_SGI_VC_EXTENSION
 static int sgi_gamma_fade (Display *dpy,
-                          Window *black_windows, int seconds, int ticks,
+                          Window *black_windows, int nwindows,
+                           int seconds, int ticks,
                           Bool out_p, Bool clear_windows);
 #endif /* HAVE_SGI_VC_EXTENSION */
 
 #ifdef HAVE_XF86VMODE_GAMMA
 static int xf86_gamma_fade (Display *dpy,
-                            Window *black_windows, int seconds, int ticks,
+                            Window *black_windows, int nwindows,
+                            int seconds, int ticks,
                             Bool out_p, Bool clear_windows);
 #endif /* HAVE_XF86VMODE_GAMMA */
 
 
 void
-fade_screens (Display *dpy, Colormap *cmaps, Window *black_windows,
+fade_screens (Display *dpy, Colormap *cmaps,
+              Window *black_windows, int nwindows,
              int seconds, int ticks,
              Bool out_p, Bool clear_windows)
 {
@@ -116,7 +120,8 @@ fade_screens (Display *dpy, Colormap *cmaps, Window *black_windows,
 
 #ifdef HAVE_SGI_VC_EXTENSION
   /* First try to do it by fading the gamma in an SGI-specific way... */
-  if (0 == sgi_gamma_fade(dpy, black_windows, seconds, ticks, out_p,
+  if (0 == sgi_gamma_fade(dpy, black_windows, nwindows,
+                          seconds, ticks, out_p,
                          clear_windows))
     ;
   else
@@ -124,7 +129,8 @@ fade_screens (Display *dpy, Colormap *cmaps, Window *black_windows,
 
 #ifdef HAVE_XF86VMODE_GAMMA
   /* Then try to do it by fading the gamma in an XFree86-specific way... */
-  if (0 == xf86_gamma_fade(dpy, black_windows, seconds, ticks, out_p,
+  if (0 == xf86_gamma_fade(dpy, black_windows, nwindows,
+                           seconds, ticks, out_p,
                            clear_windows))
     ;
   else
@@ -132,7 +138,8 @@ fade_screens (Display *dpy, Colormap *cmaps, Window *black_windows,
 
     /* Else, do it the old-fashioned way, which (somewhat) loses if
        there are TrueColor windows visible. */
-    fade_screens_1 (dpy, cmaps, black_windows, seconds, ticks,
+    fade_screens_1 (dpy, cmaps, black_windows, nwindows,
+                    seconds, ticks,
                    out_p, clear_windows);
 
   /* If we were supposed to be fading in, do so now (we just faded out,
@@ -150,9 +157,31 @@ fade_screens (Display *dpy, Colormap *cmaps, Window *black_windows,
 }
 
 
+static void
+sleep_from (struct timeval *now, struct timeval *then, long usecs_per_step)
+{
+  /* If several seconds have passed, the machine must have been asleep
+     or thrashing or something.  Don't sleep in that case, to avoid
+     overflowing and sleeping for an unconscionably long time.  This
+     function should only be sleeping for very short periods.
+   */
+  if (now->tv_sec - then->tv_sec < 5)
+    {
+      long diff = (((now->tv_sec - then->tv_sec) * 1000000) +
+                   now->tv_usec - then->tv_usec);
+      if (usecs_per_step > diff)
+        usleep (usecs_per_step - diff);
+    }
+
+  then->tv_sec  = now->tv_sec;
+  then->tv_usec = now->tv_usec;
+}
+
+
+
 /* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video
    hardware, which is capable of installing multiple (4) colormaps
-   simultaniously.  We have to install multiple copies of the same set of
+   simultaneously.  We have to install multiple copies of the same set of
    colors in order to fill up all the available slots in the hardware color
    lookup table, so we install an extra N colormaps per screen to make sure
    that all screens really go black.
@@ -168,7 +197,8 @@ fade_screens (Display *dpy, Colormap *cmaps, Window *black_windows,
    what is implemented in sgi_gamma_fade(), so we use that if we can.
  */
 static void
-fade_screens_1 (Display *dpy, Colormap *cmaps, Window *black_windows,
+fade_screens_1 (Display *dpy, Colormap *cmaps,
+                Window *black_windows, int nwindows,
                int seconds, int ticks,
                Bool out_p, Bool clear_windows)
 {
@@ -286,7 +316,7 @@ fade_screens_1 (Display *dpy, Colormap *cmaps, Window *black_windows,
          installed = True;
 
          if (black_windows && !out_p)
-           for (j = 0; j < nscreens; j++)
+           for (j = 0; j < nwindows; j++)
              if (black_windows[j])
                {
                  XUnmapWindow (dpy, black_windows[j]);
@@ -321,14 +351,7 @@ fade_screens_1 (Display *dpy, Colormap *cmaps, Window *black_windows,
 
       /* If we haven't already used up our alotted time, sleep to avoid
         changing the colormap too fast. */
-      {
-       long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
-                    now.tv_usec - then.tv_usec);
-       then.tv_sec = now.tv_sec;
-       then.tv_usec = now.tv_usec;
-       if (usecs_per_step > diff)
-         usleep (usecs_per_step - diff);
-      }
+      sleep_from (&now, &then, usecs_per_step);
     }
 
  DONE:
@@ -341,7 +364,7 @@ fade_screens_1 (Display *dpy, Colormap *cmaps, Window *black_windows,
    */
   if (out_p && black_windows)
     {
-      for (i = 0; i < nscreens; i++)
+      for (i = 0; i < nwindows; i++)
        {
          if (clear_windows)
            XClearWindow (dpy, black_windows[i]);
@@ -400,7 +423,8 @@ static void sgi_whack_gamma(Display *dpy, int screen,
 
 static int
 sgi_gamma_fade (Display *dpy,
-               Window *black_windows, int seconds, int ticks,
+               Window *black_windows, int nwindows,
+                int seconds, int ticks,
                Bool out_p, Bool clear_windows)
 {
   int steps = seconds * ticks;
@@ -472,17 +496,18 @@ sgi_gamma_fade (Display *dpy,
      way down to 0, then take the windows off the screen.
    */
   if (!out_p)
-    for (screen = 0; screen < nscreens; screen++)
-      {
+    {
+      for (screen = 0; screen < nscreens; screen++)
        sgi_whack_gamma(dpy, screen, &info[screen], 0.0);
+      
+      for (screen = 0; screen < nwindows; screen++)
        if (black_windows && black_windows[screen])
          {
            XUnmapWindow (dpy, black_windows[screen]);
            XClearWindow (dpy, black_windows[screen]);
            XSync(dpy, False);
          }
-      }
-
+    }
 
   /* Iterate by steps of the animation... */
   for (i = (out_p ? steps : 0);
@@ -520,14 +545,7 @@ sgi_gamma_fade (Display *dpy,
 
          /* If we haven't already used up our alotted time, sleep to avoid
             changing the colormap too fast. */
-         {
-           long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
-                        now.tv_usec - then.tv_usec);
-           then.tv_sec = now.tv_sec;
-           then.tv_usec = now.tv_usec;
-           if (usecs_per_step > diff)
-             usleep (usecs_per_step - diff);
-         }
+          sleep_from (&now, &then, usecs_per_step);
        }
     }
   
@@ -536,7 +554,7 @@ sgi_gamma_fade (Display *dpy,
 
   if (out_p && black_windows)
     {
-      for (screen = 0; screen < nscreens; screen++)
+      for (screen = 0; screen < nwindows; screen++)
        {
          if (clear_windows)
            XClearWindow (dpy, black_windows[screen]);
@@ -618,7 +636,8 @@ static Bool xf86_whack_gamma (Display *dpy, int screen,
 
 static int
 xf86_gamma_fade (Display *dpy,
-                 Window *black_windows, int seconds, int ticks,
+                 Window *black_windows, int nwindows,
+                 int seconds, int ticks,
                  Bool out_p, Bool clear_windows)
 {
   int steps = seconds * ticks;
@@ -698,17 +717,17 @@ xf86_gamma_fade (Display *dpy,
      way down to 0, then take the windows off the screen.
    */
   if (!out_p)
-    for (screen = 0; screen < nscreens; screen++)
-      {
+    {
+      for (screen = 0; screen < nscreens; screen++)
        xf86_whack_gamma(dpy, screen, &info[screen], 0.0);
+      for (screen = 0; screen < nwindows; screen++)
        if (black_windows && black_windows[screen])
          {
            XUnmapWindow (dpy, black_windows[screen]);
            XClearWindow (dpy, black_windows[screen]);
            XSync(dpy, False);
          }
-      }
-
+    }
 
   /* Iterate by steps of the animation... */
   for (i = (out_p ? steps : 0);
@@ -746,14 +765,7 @@ xf86_gamma_fade (Display *dpy,
 
          /* If we haven't already used up our alotted time, sleep to avoid
             changing the colormap too fast. */
-         {
-           long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
-                        now.tv_usec - then.tv_usec);
-           then.tv_sec = now.tv_sec;
-           then.tv_usec = now.tv_usec;
-           if (usecs_per_step > diff)
-             usleep (usecs_per_step - diff);
-         }
+          sleep_from (&now, &then, usecs_per_step);
        }
     }
   
@@ -762,7 +774,7 @@ xf86_gamma_fade (Display *dpy,
 
   if (out_p && black_windows)
     {
-      for (screen = 0; screen < nscreens; screen++)
+      for (screen = 0; screen < nwindows; screen++)
        {
          if (clear_windows)
            XClearWindow (dpy, black_windows[screen]);
@@ -799,27 +811,66 @@ xf86_gamma_fade (Display *dpy,
 }
 
 
+/* This bullshit is needed because the VidMode extension doesn't work
+   on remote displays -- but if the remote display has the extension
+   at all, XF86VidModeQueryExtension returns true, and then
+   XF86VidModeQueryVersion dies with an X error.  Thank you XFree,
+   may I have another.
+ */
+
+static Bool error_handler_hit_p = False;
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+  error_handler_hit_p = True;
+  return 0;
+}
+
+static Bool
+safe_XF86VidModeQueryVersion (Display *dpy, int *majP, int *minP)
+{
+  Bool result;
+  XErrorHandler old_handler;
+  XSync (dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+  result = XF86VidModeQueryVersion (dpy, majP, minP);
+
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+
+  return (error_handler_hit_p
+          ? False
+          : result);
+}
+
+
+
 /* VidModeExtension version 2.0 or better is needed to do gamma.
    2.0 added gamma values; 2.1 added gamma ramps.
  */
-# define XF86_VIDMODE_NAME "XFree86-VidModeExtension"
 # define XF86_VIDMODE_GAMMA_MIN_MAJOR 2
 # define XF86_VIDMODE_GAMMA_MIN_MINOR 0
 # define XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR 2
 # define XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR 1
 
+
+
 /* Returns 0 if gamma fading not available; 1 if only gamma value setting
    is available; 2 if gamma ramps are available.
  */
 static int
 xf86_check_gamma_extension (Display *dpy)
 {
-  int op, event, error, major, minor;
+  int event, error, major, minor;
 
-  if (!XQueryExtension (dpy, XF86_VIDMODE_NAME, &op, &event, &error))
+  if (!XF86VidModeQueryExtension (dpy, &event, &error))
     return 0;  /* display doesn't have the extension. */
 
-  if (!XF86VidModeQueryVersion (dpy, &major, &minor))
+  if (!safe_XF86VidModeQueryVersion (dpy, &major, &minor))
     return 0;  /* unable to get version number? */
 
   if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR ||