From df86bb7133a0da3c638fcf0cdb84910bc84e2cb0 Mon Sep 17 00:00:00 2001
From: Eric Piel <eric.piel@tremplin-utc.net>
Date: Sun, 10 Jan 2010 00:08:53 +0100
Subject: [PATCH] xrandr: get gamma and brightness
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Even in verbose query mode, gamma and brigthness were not displayed.
That's because they are not stored in the server the same way they are
specified on the command line: they are stored as 256 * 3 u16 while
the command line is 3 + 1 floats.  Still, this is useful info for the
users, and they don't care about how it's stored in the server.

So we do a regression over the values stored to recover info in the same
way as on the command line: gamma and brightness.

Also, make sure that when we set the gamma, the maximum value is 65535,
needed to handle correctly the clamped values.

Signed-off-by: Éric Piel <eric.piel@tremplin-utc.net>
---
 xrandr.c   |  107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 xrandr.man |   10 ++++--
 2 files changed, 105 insertions(+), 12 deletions(-)

diff --git a/xrandr.c b/xrandr.c
index 65e0623..019021b 100644
--- a/xrandr.c
+++ b/xrandr.c
@@ -943,6 +943,17 @@ output_is_primary(output_t *output)
     return False;
 }
 
+/* Returns the index of the last value in an array < 65535 */
+static int
+find_last_non_clamped(CARD16 array[], int size) {
+    int i;
+    for (i = size - 1; i > 0; i--) {
+        if (array[i] < 65535)
+	    return i;
+    }
+    return 0;
+}
+
 static void
 set_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info)
 {
@@ -1055,6 +1066,81 @@ set_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info)
 	       rotation_name (output->rotation),
 	       reflection_name (output->rotation));
 
+    /* set gamma */
+    if (!(output->changes & changes_gamma))
+    {
+	if (output->crtc_info) {
+	    crtc_t *crtc = output->crtc_info;
+	    XRRCrtcGamma *gamma;
+	    double i1, v1, i2, v2;
+	    int size, middle, last_best, last_red, last_green, last_blue;
+	    CARD16 *best_array;
+
+	    size = XRRGetCrtcGammaSize(dpy, crtc->crtc.xid);
+	    if (!size)
+		fatal("output %s cannot get gamma size\n", output->output.string);
+
+	    gamma = XRRGetCrtcGamma(dpy, crtc->crtc.xid);
+	    if (!gamma)
+		fatal("output %s cannot get gamma\n", output->output.string);
+
+	    /*
+	     * Here is a bit tricky because gamma is a whole curve for each
+	     * color.  So, typically, we need to represent 3 * 256 values as 3 + 1
+	     * values.  Therefore, we approximate the gamma curve (v) by supposing
+	     * it always follows the way we set it: a power function (i^g)
+	     * multiplied by a brightness (b).
+	     * v = i^g * b
+	     * so g = (ln(v) - ln(b))/ln(i)
+	     * and b can be found using two points (v1,i1) and (v2, i2):
+	     * b = e^((ln(v2)*ln(i1) - ln(v1)*ln(i2))/ln(i1/i2))
+	     * For the best resolution, we select i2 at the highest place not
+	     * clamped and i1 at i2/2. Note that if i2 = 1 (as in most normal
+	     * cases), then b = v2.
+	     */
+	    last_red = find_last_non_clamped(gamma->red, size);
+	    last_green = find_last_non_clamped(gamma->green, size);
+	    last_blue = find_last_non_clamped(gamma->blue, size);
+	    best_array = gamma->red;
+	    last_best = last_red;
+	    if (last_green > last_best) {
+		last_best = last_green;
+		best_array = gamma->green;
+	    }
+	    if (last_blue > last_best) {
+		last_best = last_blue;
+		best_array = gamma->blue;
+	    }
+	    if (last_best == 0)
+		last_best = 1;
+
+	    middle = last_best / 2;
+	    i1 = (double)(middle + 1) / size;
+	    v1 = (double)(best_array[middle]) / 65535;
+	    i2 = (double)(last_best + 1) / size;
+	    v2 = (double)(best_array[last_best]) / 65535;
+	    if (v2 < 0.0001) { /* The screen is black */
+		output->brightness = 0;
+		output->gamma.red = 1;
+		output->gamma.green = 1;
+		output->gamma.blue = 1;
+	    } else {
+		if ((last_best + 1) == size)
+		    output->brightness = v2;
+		else
+		    output->brightness = exp((log(v2)*log(i1) - log(v1)*log(i2))/log(i1/i2));
+		output->gamma.red = log((double)(gamma->red[last_red / 2]) / output->brightness
+					/ 65535) / log((double)((last_red / 2) + 1) / size);
+		output->gamma.green = log((double)(gamma->green[last_green / 2]) / output->brightness
+					  / 65535) / log((double)((last_green / 2) + 1) / size);
+		output->gamma.blue = log((double)(gamma->blue[last_blue / 2]) / output->brightness
+					 / 65535) / log((double)((last_blue / 2) + 1) / size);
+	    }
+
+	    XRRFreeGamma(gamma);
+	}
+    }
+
     /* set transformation */
     if (!(output->changes & changes_transform))
     {
@@ -1225,25 +1311,25 @@ set_gamma(void)
 
 	for (i = 0; i < size; i++) {
 	    if (output->gamma.red == 1.0 && output->brightness == 1.0)
-		gamma->red[i] = i << 8;
+		gamma->red[i] = (i << 8) + i;
 	    else
 		gamma->red[i] = dmin(pow((double)i/(double)(size - 1),
-			    (double)output->gamma.red) * (double)(size - 1)
-			    * (double)output->brightness * 256, 65535.0);
+					 output->gamma.red) * output->brightness,
+				     1.0) * 65535.0;
 
 	    if (output->gamma.green == 1.0 && output->brightness == 1.0)
-		gamma->green[i] = i << 8;
+		gamma->green[i] = (i << 8) + i;
 	    else
 		gamma->green[i] = dmin(pow((double)i/(double)(size - 1),
-			    (double)output->gamma.green) * (double)(size - 1)
-			    * (double)output->brightness * 256, 65535);
+					   output->gamma.green) * output->brightness,
+				       1.0) * 65535.0;
 
 	    if (output->gamma.blue == 1.0 && output->brightness == 1.0)
-		gamma->blue[i] = i << 8;
+		gamma->blue[i] = (i << 8) + i;
 	    else
 		gamma->blue[i] = dmin(pow((double)i/(double)(size - 1),
-			    (double)output->gamma.blue) * (double)(size - 1)
-			    * (double)output->brightness * 256, 65535);
+					  output->gamma.blue) * output->brightness,
+				      1.0) * 65535.0;
 	}
 
 	XRRSetCrtcGamma(dpy, crtc->crtc.xid, gamma);
@@ -2919,6 +3005,9 @@ main (int argc, char **argv)
 		printf ("\tIdentifier: 0x%x\n", (int)output->output.xid);
 		printf ("\tTimestamp:  %d\n", (int)output_info->timestamp);
 		printf ("\tSubpixel:   %s\n", order[output_info->subpixel_order]);
+		printf ("\tGamma:      %#.2g:%#.2g:%#.2g\n",
+			output->gamma.red, output->gamma.green, output->gamma.blue);
+		printf ("\tBrightness: %#.2g\n", output->brightness);
 		printf ("\tClones:    ");
 		for (j = 0; j < output_info->nclone; j++)
 		{
diff --git a/xrandr.man b/xrandr.man
index 335b7c0..aa1d9c5 100644
--- a/xrandr.man
+++ b/xrandr.man
@@ -105,7 +105,9 @@ Print out a summary of the usage and exit.
 Print out the RandR version reported by the X server and exit.
 .IP \-\-verbose
 Causes xrandr to be more verbose. When used with \-q (or without other
-options), xrandr will display more information about the server state. When
+options), xrandr will display more information about the server state. Please
+note that the gamma and brightness informations are only approximations of the
+complete color profile stored in the server. When
 used along with options that reconfigure the system, progress will be
 reported while executing the configuration changes.
 .IP "\-q, \-\-query"
@@ -287,11 +289,13 @@ for some reason, this option can override the normal selection.
 .IP "\-\-gamma \fIred\fP:\fIgreen\fP:\fIblue\fP"
 Set the specified floating point values as gamma correction on the crtc 
 currently attached to this output. Note that you cannot get two different values
-for cloned outputs and that switching an output to another crtc doesn't change
+for cloned outputs (i.e.: which share the same crtc) and that switching an output to another crtc doesn't change
 the crtc gamma corrections at all.
 .IP "\-\-brightness \fIbrightness\fP"
 Multiply the gamma values on the crtc currently attached to the output to
 specified floating value. Useful for overly bright or overly dim outputs.
+However, this is a software only modification, if your hardware has support to
+actually change the brightness, you will probably prefer to use \fBxbacklight\fR.
 .PP
 .SH "RandR version 1.1 options"
 These options are available for X servers supporting RandR version 1.1 or
@@ -342,7 +346,7 @@ when the projector is slightly above the screen:
 xrandr --fb 1024x768 --output VGA --transform 1.24,0.16,-124,0,1.24,0,0,0.000316,1
 .RE
 .SH "SEE ALSO"
-Xrandr(3), cvt(1), xkeystone(1)
+Xrandr(3), cvt(1), xkeystone(1), xbacklight(1)
 .SH AUTHORS
 Keith Packard,
 Open Source Technology Center, Intel Corporation.
-- 
1.6.6
