$OpenBSD: patch-src_video_x11_SDL_x11video_c,v 1.1 2011/05/13 07:57:24 jasper Exp $

Add support for XRandR and VidMode gamma ramps.
From upstream bz: http://bugzilla.libsdl.org/show_bug.cgi?id=971

Will not be part of SDL 1.3 to gamma API removal.

--- src/video/x11/SDL_x11video.c.orig	Mon Dec 31 05:48:13 2007
+++ src/video/x11/SDL_x11video.c	Tue May  3 15:12:12 2011
@@ -65,6 +65,9 @@ static int X11_ToggleFullScreen(_THIS, int on);
 static void X11_UpdateMouse(_THIS);
 static int X11_SetColors(_THIS, int firstcolor, int ncolors,
 			 SDL_Color *colors);
+static void X11_FreeSavedGammaRamp(_THIS);
+static int X11_RestoreGammaRamp(_THIS);
+static int X11_SaveGammaRamp(_THIS);
 static int X11_SetGammaRamp(_THIS, Uint16 *ramp);
 static void X11_VideoQuit(_THIS);
 
@@ -665,6 +668,7 @@ static int X11_VideoInit(_THIS, SDL_PixelFormat *vform
 	if ( this->hidden->depth == 32 ) {
 		vformat->Amask = (0xFFFFFFFF & ~(vformat->Rmask|vformat->Gmask|vformat->Bmask));
 	}
+	X11_SaveGammaRamp(this);
 	X11_SaveVidModeGamma(this);
 
 	/* Allow environment override of screensaver disable. */
@@ -1429,11 +1433,249 @@ int X11_SetColors(_THIS, int firstcolor, int ncolors, 
 	return nrej == 0;
 }
 
+void X11_FreeSavedGammaRamp(_THIS)
+{
+	int i;
+
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+	if (gamma_ramp_saved_xrr) {
+		for ( i=0; i<gamma_ramp_saved_xrr_size; ++i ) {
+			XRRFreeGamma(gamma_ramp_saved_xrr[i]);
+		}
+		SDL_free(gamma_ramp_saved_xrr);
+		gamma_ramp_saved_xrr = NULL;
+	}
+#endif
+
+#if SDL_VIDEO_DRIVER_X11_VIDMODE
+	if (gamma_ramp_saved_vm) {
+		SDL_free(gamma_ramp_saved_vm);
+		gamma_ramp_saved_vm = NULL;
+	}
+#endif
+}
+
+int X11_SaveGammaRamp(_THIS)
+{
+	int i;
+	Bool succeeded;
+
+	X11_FreeSavedGammaRamp(this);
+
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+	if (use_xrandr) {
+		XRRScreenResources *resrc;
+
+		if (use_xrandr >= 103) {
+			resrc = XRRGetScreenResourcesCurrent(SDL_Display, SDL_Root);
+		} else {
+			resrc = XRRGetScreenResources(SDL_Display, SDL_Root);
+		}
+
+		if (resrc != NULL) {
+			gamma_ramp_saved_xrr_size = resrc->ncrtc;
+			gamma_ramp_saved_xrr = SDL_malloc(
+					sizeof(gamma_ramp_saved_xrr[0]) *
+					gamma_ramp_saved_xrr_size);
+		}
+
+		for ( i=0; resrc != NULL && i<resrc->ncrtc; ++i ) {
+			gamma_ramp_saved_xrr[i] = XRRGetCrtcGamma(SDL_Display,
+					resrc->crtcs[i]);
+		}
+
+		if (resrc != NULL) {
+			XRRFreeScreenResources(resrc);
+			return(0);
+		}
+	}
+#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
+
+#if SDL_VIDEO_DRIVER_X11_VIDMODE
+	if (use_vidmode >= 200 && gamma_ramp_size == 0) {
+		succeeded = SDL_NAME(XF86VidModeGetGammaRampSize)(
+				SDL_Display, SDL_Screen,
+				&gamma_ramp_size);
+
+		if (!succeeded) {
+			gamma_ramp_size = 0;
+		}
+	}
+
+	if (use_vidmode >= 200 && gamma_ramp_size > 0) {
+		gamma_ramp_saved_vm = SDL_malloc(
+				sizeof(gamma_ramp_saved_vm[0]) *
+				3 * gamma_ramp_size);
+
+		succeeded = SDL_NAME(XF86VidModeGetGammaRamp)(
+				SDL_Display, SDL_Screen,
+				gamma_ramp_size,
+				gamma_ramp_saved_vm + 0*gamma_ramp_size,
+				gamma_ramp_saved_vm + 1*gamma_ramp_size,
+				gamma_ramp_saved_vm + 2*gamma_ramp_size);
+
+		if (!succeeded) {
+			SDL_free(gamma_ramp_saved_vm);
+			gamma_ramp_saved_vm = NULL;
+		} else {
+			return(0);
+		}
+	}
+#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
+
+	return(-1);
+}
+
+int X11_RestoreGammaRamp(_THIS)
+{
+	int i;
+	Bool succeeded;
+
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+	if (use_xrandr && gamma_ramp_saved_xrr != NULL) {
+		XRRScreenResources *resrc;
+
+		if (use_xrandr >= 103) {
+			resrc = XRRGetScreenResourcesCurrent(SDL_Display, SDL_Root);
+		} else {
+			resrc = XRRGetScreenResources(SDL_Display, SDL_Root);
+		}
+
+		for ( i=0; resrc != NULL && i<resrc->ncrtc && i<gamma_ramp_saved_xrr_size; ++i ) {
+			XRRSetCrtcGamma(SDL_Display, resrc->crtcs[i],
+					gamma_ramp_saved_xrr[i]);
+		}
+
+		if (resrc != NULL) {
+			XRRFreeScreenResources(resrc);
+			return(0);
+		}
+	}
+#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
+
+#if SDL_VIDEO_DRIVER_X11_VIDMODE
+	if (use_vidmode >= 200 && gamma_ramp_saved_vm != NULL) {
+		succeeded = SDL_NAME(XF86VidModeSetGammaRamp)(
+				SDL_Display, SDL_Screen,
+				gamma_ramp_size,
+				gamma_ramp_saved_vm + 0*gamma_ramp_size,
+				gamma_ramp_saved_vm + 1*gamma_ramp_size,
+				gamma_ramp_saved_vm + 2*gamma_ramp_size);
+
+		if (succeeded)
+			return(0);
+	}
+#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
+
+	return(-1);
+}
+
 int X11_SetGammaRamp(_THIS, Uint16 *ramp)
 {
-	int i, ncolors;
+	int i, j, ncolors;
 	XColor xcmap[256];
+	Bool succeeded;
 
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+	if (use_xrandr) {
+		XRRCrtcGamma *gamma = NULL;
+		int gammasize = 0;
+		XRRScreenResources *resrc;
+
+		if (use_xrandr >= 103) {
+			resrc = XRRGetScreenResourcesCurrent(SDL_Display, SDL_Root);
+		} else {
+			resrc = XRRGetScreenResources(SDL_Display, SDL_Root);
+		}
+
+		/* Implementation Note:
+		 * We try to make few assumptions here, notably:
+		 *  - Ramp size can vary between crtcs
+		 *    (which can happen when using multiple video cards)
+		 *  - Ramp size (by index) can vary between calls
+		 *    (which can happen if the window is moved to a new crtc)
+		 */
+		for ( i=0; resrc != NULL && i<resrc->ncrtc; ++i ) {
+			int crtcgs;
+
+			crtcgs = XRRGetCrtcGammaSize(SDL_Display,
+					resrc->crtcs[i]);
+			if (crtcgs != gammasize) {
+				/* Size in this CRTC differs from last */
+				if (gamma != NULL) {
+					XRRFreeGamma(gamma);
+				}
+
+				gammasize = crtcgs;
+				gamma = XRRAllocGamma(gammasize);
+
+				for ( j=0; j<gammasize; ++j ) {
+					gamma->red[j]   =
+						ramp[0*256+j*256/gammasize];
+					gamma->green[j] =
+						ramp[1*256+j*256/gammasize];
+					gamma->blue[j]  =
+						ramp[2*256+j*256/gammasize];
+				}
+			}
+
+			XRRSetCrtcGamma(SDL_Display, resrc->crtcs[i], gamma);
+		}
+
+		if (gamma != NULL) {
+			XRRFreeGamma(gamma);
+		}
+
+		if (resrc != NULL) {
+			XRRFreeScreenResources(resrc);
+			return(0);
+		}
+	}
+#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
+
+#if SDL_VIDEO_DRIVER_X11_VIDMODE
+	if (use_vidmode >= 200 && gamma_ramp_size == 0) {
+		succeeded = SDL_NAME(XF86VidModeGetGammaRampSize)(
+				SDL_Display, SDL_Screen,
+				&gamma_ramp_size);
+
+		if (!succeeded) {
+			gamma_ramp_size = 0;
+		}
+	}
+
+	if (use_vidmode >= 200 && gamma_ramp_size > 0) {
+		Uint16 *sizedramp;
+
+		if (gamma_ramp_size == 256) {
+			sizedramp = ramp;
+		} else {
+			sizedramp = SDL_stack_alloc(Uint16, 3*gamma_ramp_size);
+			if (sizedramp == NULL) {
+				SDL_OutOfMemory();
+				return(-1);
+			}
+
+			for ( i=0; i<gamma_ramp_size*3; ++i ) {
+				sizedramp[i] = ramp[i * 256 / gamma_ramp_size];
+			}
+		}
+
+		succeeded = SDL_NAME(XF86VidModeSetGammaRamp)(
+				SDL_Display, SDL_Screen,
+				gamma_ramp_size,
+				sizedramp+0*256,
+				sizedramp+1*256,
+				sizedramp+2*256);
+
+		if (gamma_ramp_size != 256) {
+			SDL_stack_free(sizedramp);
+		}
+
+		return(succeeded ? 0 : -1);
+	}
+#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
+
 	/* See if actually setting the gamma is supported */
 	if ( SDL_Visual->class != DirectColor ) {
 	    SDL_SetError("Gamma correction not supported on this visual");
@@ -1504,7 +1746,9 @@ void X11_VideoQuit(_THIS)
 		/* Restore gamma settings if they've changed */
 		if ( SDL_GetAppState() & SDL_APPACTIVE ) {
 			X11_SwapVidModeGamma(this);
+			X11_RestoreGammaRamp(this);
 		}
+		X11_FreeSavedGammaRamp(this);
 
 		/* Restore DPMS and screensaver settings */
 		X11_RestoreScreenSaver(this, SDL_Display, screensaver_timeout, dpms_enabled);
