-/* xscreensaver, Copyright (c) 1992-1997 Jamie Zawinski <jwz@netscape.com>
+/* xscreensaver, Copyright (c) 1992-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 "usleep.h"
#include "fade.h"
-
Colormap
copy_colormap (Screen *screen, Visual *visual,
Colormap cmap, Colormap into_cmap)
static void fade_screens_1 (Display *dpy, Colormap *cmaps,
Window *black_windows, 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,
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,
+ Bool out_p, Bool clear_windows);
+#endif /* HAVE_XF86VMODE_GAMMA */
void
int seconds, int ticks,
Bool out_p, Bool clear_windows)
{
+ int oseconds = seconds;
+ Bool was_in_p = !out_p;
+
+ /* When we're asked to fade in, first fade out, then fade in.
+ That way all the transitions are smooth -- from what's on the
+ screen, to black, to the desktop.
+ */
+ if (was_in_p)
+ {
+ clear_windows = True;
+ out_p = True;
+ seconds /= 3;
+ if (seconds == 0)
+ seconds = 1;
+ }
+
+ AGAIN:
+
+/* #### printf("\n\nfade_screens %d %d %d\n", seconds, ticks, out_p); */
+
#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, seconds, ticks, out_p,
clear_windows))
+ ;
+ else
#endif /* HAVE_SGI_VC_EXTENSION */
+
+#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,
+ clear_windows))
+ ;
+ else
+#endif /* HAVE_XF86VMODE_GAMMA */
+
/* 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,
out_p, clear_windows);
+
+ /* If we were supposed to be fading in, do so now (we just faded out,
+ so now fade back in.)
+ */
+ if (was_in_p)
+ {
+ was_in_p = False;
+ out_p = False;
+ seconds = oseconds * 2 / 3;
+ if (seconds == 0)
+ seconds = 1;
+ goto AGAIN;
+ }
}
that all screens really go black.
I'm told that this trick also works with XInside's AcceleratedX when using
- the Matrox Millenium card (which also allows multiple PseudoColor and
+ the Matrox Millennium card (which also allows multiple PseudoColor and
TrueColor visuals to co-exist and display properly at the same time.)
This trick works ok on the 24-bit Indy video hardware, but doesn't work at
releasing the colormaps.
*/
if (out_p && black_windows)
- for (i = 0; i < nscreens; i++)
- {
- if (clear_windows)
- XClearWindow (dpy, black_windows[i]);
- XMapRaised (dpy, black_windows[i]);
- }
-
+ {
+ for (i = 0; i < nscreens; i++)
+ {
+ if (clear_windows)
+ XClearWindow (dpy, black_windows[i]);
+ XMapRaised (dpy, black_windows[i]);
+ }
+ XSync(dpy, False);
+ }
/* Now put the target maps back.
If we're fading out, use the given cmap (or the default cmap, if none.)
}
+\f
+/* SGI Gamma fading */
+
#ifdef HAVE_SGI_VC_EXTENSION
# include <X11/extensions/XSGIvc.h>
-struct screen_gamma_info {
+struct screen_sgi_gamma_info {
int gamma_map; /* ??? always using 0 */
int nred, ngreen, nblue;
unsigned short *red1, *green1, *blue1;
};
-static void whack_gamma(Display *dpy, int screen,
- struct screen_gamma_info *info, float ratio);
+static void sgi_whack_gamma(Display *dpy, int screen,
+ struct screen_sgi_gamma_info *info, float ratio);
static int
sgi_gamma_fade (Display *dpy,
#endif
int i, screen;
int status = -1;
- struct screen_gamma_info *info = (struct screen_gamma_info *)
+ struct screen_sgi_gamma_info *info = (struct screen_sgi_gamma_info *)
calloc(nscreens, sizeof(*info));
/* Get the current gamma maps for all screens.
if (!out_p)
for (screen = 0; screen < nscreens; screen++)
{
- whack_gamma(dpy, screen, &info[screen], 0.0);
+ sgi_whack_gamma(dpy, screen, &info[screen], 0.0);
if (black_windows && black_windows[screen])
{
XUnmapWindow (dpy, black_windows[screen]);
XClearWindow (dpy, black_windows[screen]);
+ XSync(dpy, False);
}
}
{
for (screen = 0; screen < nscreens; screen++)
{
- whack_gamma(dpy, screen, &info[screen],
- (((float)i) / ((float)steps)));
+ sgi_whack_gamma(dpy, screen, &info[screen],
+ (((float)i) / ((float)steps)));
/* If there is user activity, bug out. (Bug out on keypresses or
mouse presses, but not motion, and not release events. Bugging
for (screen = 0; screen < nscreens; screen++)
{
if (clear_windows)
- XClearWindow (dpy, black_windows[i]);
- XMapRaised (dpy, black_windows[i]);
+ XClearWindow (dpy, black_windows[screen]);
+ XMapRaised (dpy, black_windows[screen]);
}
XSync(dpy, False);
}
+ /* I can't explain this; without this delay, we get a flicker.
+ I suppose there's some lossage with stale bits being in the
+ hardware frame buffer or something, and this delay gives it
+ time to flush out. This sucks! */
+ usleep(100000); /* 1/10th second */
+
for (screen = 0; screen < nscreens; screen++)
- whack_gamma(dpy, screen, &info[screen], 1.0);
+ sgi_whack_gamma(dpy, screen, &info[screen], 1.0);
XSync(dpy, False);
status = 0;
if (info[screen].blue2) free (info[screen].blue2);
}
free(info);
+
return status;
}
static void
-whack_gamma(Display *dpy, int screen, struct screen_gamma_info *info,
- float ratio)
+sgi_whack_gamma(Display *dpy, int screen, struct screen_sgi_gamma_info *info,
+ float ratio)
{
int k;
+
if (ratio < 0) ratio = 0;
if (ratio > 1) ratio = 1;
for (k = 0; k < info->gamma_size; k++)
info->green2[k] = info->green1[k] * ratio;
info->blue2[k] = info->blue1[k] * ratio;
}
+
XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nred,
XSGIVC_MComponentRed, info->red2);
XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->ngreen,
#endif /* HAVE_SGI_VC_EXTENSION */
-
\f
-#if 0
-#include "screenhack.h"
+/* XFree86 4.x+ Gamma fading */
-char *progclass = "foo";
-char *defaults [] = {
- 0
-};
+#ifdef HAVE_XF86VMODE_GAMMA
-XrmOptionDescRec options [] = {0};
-int options_size = 0;
+#include <X11/extensions/xf86vmode.h>
-void
-screenhack (dpy, w)
- Display *dpy;
- Window w;
+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,
+ Bool out_p, Bool clear_windows)
{
- int seconds = 3;
- int ticks = 20;
- int delay = 1;
+ int steps = seconds * ticks;
+ long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
+ XEvent dummy_event;
+ int nscreens = ScreenCount(dpy);
+ struct timeval then, now;
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+#endif
+ int i, screen;
+ int status = -1;
+ xf86_gamma_info *info = 0;
- while (1)
+ 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);
+
+ /* If this server doesn't have the gamma extension, bug out. */
+ if (ext_ok == 0)
+ goto FAIL;
+
+# 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++)
{
- XSync (dpy, False);
+ if (ext_ok == 1) /* only have gamma parameter, not ramps. */
+ {
+ if (!XF86VidModeGetGamma(dpy, screen, &info[screen].vmg))
+ goto FAIL;
+ }
+ 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;
+ }
+ else
+ abort();
+ }
+
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&then, &tzp);
+#else
+ gettimeofday(&then);
+#endif
- fprintf(stderr,"out..."); fflush(stderr);
- fade_screens (dpy, 0, seconds, ticks, True);
- fprintf(stderr, "done.\n"); fflush(stderr);
+ /* If we're fading in (from black), then first crank the gamma all the
+ way down to 0, then take the windows off the screen.
+ */
+ if (!out_p)
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ xf86_whack_gamma(dpy, screen, &info[screen], 0.0);
+ if (black_windows && black_windows[screen])
+ {
+ XUnmapWindow (dpy, black_windows[screen]);
+ XClearWindow (dpy, black_windows[screen]);
+ XSync(dpy, False);
+ }
+ }
- if (delay) sleep (delay);
- fprintf(stderr,"in..."); fflush(stderr);
- fade_screens (dpy, 0, seconds, ticks, False);
- fprintf(stderr, "done.\n"); fflush(stderr);
+ /* Iterate by steps of the animation... */
+ for (i = (out_p ? steps : 0);
+ (out_p ? i > 0 : i < steps);
+ (out_p ? i-- : i++))
+ {
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ xf86_whack_gamma(dpy, screen, &info[screen],
+ (((float)i) / ((float)steps)));
+
+ /* If there is user activity, bug out. (Bug out on keypresses or
+ mouse presses, but not motion, and not release events. Bugging
+ out on motion made the unfade hack be totally useless, I think.)
- if (delay) sleep (delay);
+ We put the event back so that the calling code can notice it too.
+ It would be better to not remove it at all, but that's harder
+ because Xlib has such a non-design for this kind of crap, and
+ in this application it doesn't matter if the events end up out
+ of order, so in the grand unix tradition we say "fuck it" and
+ do something that mostly works for the time being.
+ */
+ if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
+ &dummy_event))
+ {
+ XPutBackEvent (dpy, &dummy_event);
+ goto DONE;
+ }
+
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday(&now, &tzp);
+#else
+ gettimeofday(&now);
+#endif
+
+ /* 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);
+ }
+ }
}
+
+
+ DONE:
+
+ if (out_p && black_windows)
+ {
+ for (screen = 0; screen < nscreens; screen++)
+ {
+ if (clear_windows)
+ XClearWindow (dpy, black_windows[screen]);
+ XMapRaised (dpy, black_windows[screen]);
+ }
+ XSync(dpy, False);
+ }
+
+ /* I can't explain this; without this delay, we get a flicker.
+ I suppose there's some lossage with stale bits being in the
+ hardware frame buffer or something, and this delay gives it
+ time to flush out. This sucks! */
+ usleep(100000); /* 1/10th second */
+
+ for (screen = 0; screen < nscreens; screen++)
+ xf86_whack_gamma(dpy, screen, &info[screen], 1.0);
+ XSync(dpy, False);
+
+ status = 0;
+
+ FAIL:
+ 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;
}
-#endif
+
+/* 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;
+
+ if (!XQueryExtension (dpy, XF86_VIDMODE_NAME, &op, &event, &error))
+ return 0; /* display doesn't have the extension. */
+
+ if (!XF86VidModeQueryVersion (dpy, &major, &minor))
+ return 0; /* 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_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 2;
+}
+
+
+/* XFree doesn't let you set gamma to a value smaller than this.
+ Apparently they didn't anticipate the trick I'm doing here...
+ */
+#define XF86_MIN_GAMMA 0.1
+
+
+static Bool
+xf86_whack_gamma(Display *dpy, int screen, xf86_gamma_info *info,
+ float ratio)
+{
+ Bool status;
+
+ if (ratio < 0) ratio = 0;
+ if (ratio > 1) ratio = 1;
+
+ 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;
+# endif
+
+ 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 */
+ }
+
+ XSync(dpy, False);
+ return status;
+}
+
+#endif /* HAVE_XF86VMODE_GAMMA */