ftp://ftp.swin.edu.au/slackware/slackware-9.1/source/xap/xscreensaver/xscreensaver...
[xscreensaver] / utils / fade.c
index 1b975f681ca403d93354b983e6dce635d3a1dff8..18e940680a1fb8d607118d66ac68e57058a12f23 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1992-2001 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1992-2003 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,
@@ -168,7 +175,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 +294,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]);
@@ -341,7 +349,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 +408,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 +481,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);
@@ -536,7 +546,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]);
@@ -606,13 +616,20 @@ sgi_whack_gamma(Display *dpy, int screen, struct screen_sgi_gamma_info *info,
 
 #include <X11/extensions/xf86vmode.h>
 
-static Bool xf86_whack_gamma(Display *dpy, int screen,
-                             XF86VidModeGamma *info, float ratio);
-static Bool xf86_check_gamma_extension (Display *dpy);
+typedef struct {
+  XF86VidModeGamma vmg;
+  int size;
+  unsigned short *r, *g, *b;
+} xf86_gamma_info;
+
+static int xf86_check_gamma_extension (Display *dpy);
+static Bool xf86_whack_gamma (Display *dpy, int screen,
+                              xf86_gamma_info *ginfo, float ratio);
 
 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;
@@ -625,27 +642,61 @@ xf86_gamma_fade (Display *dpy,
 #endif
   int i, screen;
   int status = -1;
-  XF86VidModeGamma *info = 0;
+  xf86_gamma_info *info = 0;
 
   static int ext_ok = -1;
 
   /* Only probe the extension once: the answer isn't going to change. */
   if (ext_ok == -1)
-    ext_ok = (xf86_check_gamma_extension (dpy) ? 1 : 0);
+    ext_ok = xf86_check_gamma_extension (dpy);
 
   /* If this server doesn't have the gamma extension, bug out. */
   if (ext_ok == 0)
     goto FAIL;
 
-  info = (XF86VidModeGamma *) calloc(nscreens, sizeof(*info));
+# ifndef HAVE_XF86VMODE_GAMMA_RAMP
+  if (ext_ok == 2) ext_ok = 1;  /* server is newer than client! */
+# endif
+
+  info = (xf86_gamma_info *) calloc(nscreens, sizeof(*info));
 
   /* Get the current gamma maps for all screens.
      Bug out and return -1 if we can't get them for some screen.
    */
   for (screen = 0; screen < nscreens; screen++)
     {
-      if (!XF86VidModeGetGamma(dpy, screen, &info[screen]))
-       goto FAIL;
+      if (ext_ok == 1)  /* only have gamma parameter, not ramps. */
+        {
+          if (!XF86VidModeGetGamma(dpy, screen, &info[screen].vmg))
+            goto FAIL;
+        }
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+      else if (ext_ok == 2)  /* have ramps */
+        {
+          if (!XF86VidModeGetGammaRampSize(dpy, screen, &info[screen].size))
+            goto FAIL;
+          if (info[screen].size <= 0)
+            goto FAIL;
+
+          info[screen].r = (unsigned short *)
+            calloc(info[screen].size, sizeof(unsigned short));
+          info[screen].g = (unsigned short *)
+            calloc(info[screen].size, sizeof(unsigned short));
+          info[screen].b = (unsigned short *)
+            calloc(info[screen].size, sizeof(unsigned short));
+
+          if (!(info[screen].r && info[screen].g && info[screen].b))
+            goto FAIL;
+
+          if (!XF86VidModeGetGammaRamp(dpy, screen, info[screen].size,
+                                       info[screen].r,
+                                       info[screen].g,
+                                       info[screen].b))
+            goto FAIL;
+        }
+# endif /* HAVE_XF86VMODE_GAMMA_RAMP */
+      else
+        abort();
     }
 
 #ifdef GETTIMEOFDAY_TWO_ARGS
@@ -658,17 +709,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);
@@ -722,7 +773,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]);
@@ -744,35 +795,95 @@ xf86_gamma_fade (Display *dpy,
   status = 0;
 
  FAIL:
-  if (info) free(info);
+  if (info)
+    {
+      for (screen = 0; screen < nscreens; screen++)
+        {
+          if (info[screen].r) free(info[screen].r);
+          if (info[screen].g) free(info[screen].g);
+          if (info[screen].b) free(info[screen].b);
+        }
+      free(info);
+    }
 
   return status;
 }
 
 
-/* VidModeExtension version 2.0 or better is needed to do gamma. */
-# define XF86_VIDMODE_NAME "XFree86-VidModeExtension"
-# define XF86_VIDMODE_MIN_MAJOR 2
-# define XF86_VIDMODE_MIN_MINOR 0
+/* 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_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 (!XF86VidModeQueryExtension (dpy, &event, &error))
+    return 0;  /* display doesn't have the extension. */
 
-  if (!XQueryExtension (dpy, XF86_VIDMODE_NAME, &op, &event, &error))
-    return False;  /* display doesn't have the extension. */
+  if (!safe_XF86VidModeQueryVersion (dpy, &major, &minor))
+    return 0;  /* unable to get version number? */
 
-  if (!XF86VidModeQueryVersion (dpy, &major, &minor))
-    return False;  /* unable to get version number? */
+  if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR || 
+      (major == XF86_VIDMODE_GAMMA_MIN_MAJOR &&
+       minor < XF86_VIDMODE_GAMMA_MIN_MINOR))
+    return 0;  /* extension is too old for gamma. */
 
-  if (major < XF86_VIDMODE_MIN_MAJOR || 
-      (major == XF86_VIDMODE_MIN_MAJOR &&
-       minor < XF86_VIDMODE_MIN_MINOR))
-    return False;  /* extension is too old. */
+  if (major < XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR || 
+      (major == XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR &&
+       minor < XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR))
+    return 1;  /* extension is too old for gamma ramps. */
 
   /* Copacetic */
-  return True;
+  return 2;
 }
 
 
@@ -783,28 +894,58 @@ xf86_check_gamma_extension (Display *dpy)
 
 
 static Bool
-xf86_whack_gamma(Display *dpy, int screen, XF86VidModeGamma *info,
+xf86_whack_gamma(Display *dpy, int screen, xf86_gamma_info *info,
                  float ratio)
 {
   Bool status;
-  XF86VidModeGamma g2;
 
   if (ratio < 0) ratio = 0;
   if (ratio > 1) ratio = 1;
 
-  g2.red   = info->red   * ratio;
-  g2.green = info->green * ratio;
-  g2.blue  = info->blue  * ratio;
+  if (info->size == 0)    /* we only have a gamma number, not a ramp. */
+    {
+      XF86VidModeGamma g2;
+
+      g2.red   = info->vmg.red   * ratio;
+      g2.green = info->vmg.green * ratio;
+      g2.blue  = info->vmg.blue  * ratio;
 
 # ifdef XF86_MIN_GAMMA
-  if (g2.red   < XF86_MIN_GAMMA) g2.red   = XF86_MIN_GAMMA;
-  if (g2.green < XF86_MIN_GAMMA) g2.green = XF86_MIN_GAMMA;
-  if (g2.blue  < XF86_MIN_GAMMA) g2.blue  = XF86_MIN_GAMMA;
+      if (g2.red   < XF86_MIN_GAMMA) g2.red   = XF86_MIN_GAMMA;
+      if (g2.green < XF86_MIN_GAMMA) g2.green = XF86_MIN_GAMMA;
+      if (g2.blue  < XF86_MIN_GAMMA) g2.blue  = XF86_MIN_GAMMA;
 # endif
 
-/* #### printf("  G %4.2f %4.2f\n", ratio, g2.red); */
+      status = XF86VidModeSetGamma (dpy, screen, &g2);
+    }
+  else
+    {
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+
+      unsigned short *r, *g, *b;
+      int i;
+      r = (unsigned short *) malloc(info->size * sizeof(unsigned short));
+      g = (unsigned short *) malloc(info->size * sizeof(unsigned short));
+      b = (unsigned short *) malloc(info->size * sizeof(unsigned short));
+
+      for (i = 0; i < info->size; i++)
+        {
+          r[i] = info->r[i] * ratio;
+          g[i] = info->g[i] * ratio;
+          b[i] = info->b[i] * ratio;
+        }
+
+      status = XF86VidModeSetGammaRamp(dpy, screen, info->size, r, g, b);
+
+      free (r);
+      free (g);
+      free (b);
+
+# else  /* !HAVE_XF86VMODE_GAMMA_RAMP */
+      abort();
+# endif /* !HAVE_XF86VMODE_GAMMA_RAMP */
+    }
 
-  status = XF86VidModeSetGamma (dpy, screen, &g2);
   XSync(dpy, False);
   return status;
 }