aboutsummaryrefslogtreecommitdiffstats
path: root/src/gamma-randr.c
diff options
context:
space:
mode:
authorJon Lund Steffensen <jonlst@gmail.com>2010-05-24 23:37:36 +0200
committerJon Lund Steffensen <jonlst@gmail.com>2010-05-24 23:37:36 +0200
commit65dd76a221e23a7200b33645326e90c0c02be7ed (patch)
treefd3ea9ba55dcc874b49f8fb845035a78aa44d2a7 /src/gamma-randr.c
parentPass arguments as string to adjustment methods. (diff)
downloadredshift-ng-65dd76a221e23a7200b33645326e90c0c02be7ed.tar.gz
redshift-ng-65dd76a221e23a7200b33645326e90c0c02be7ed.tar.bz2
redshift-ng-65dd76a221e23a7200b33645326e90c0c02be7ed.tar.xz
Use the prefix 'gamma' for gamma adjustment source files.
Diffstat (limited to 'src/gamma-randr.c')
-rw-r--r--src/gamma-randr.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/gamma-randr.c b/src/gamma-randr.c
new file mode 100644
index 0000000..ec70746
--- /dev/null
+++ b/src/gamma-randr.c
@@ -0,0 +1,367 @@
+/* gamma-randr.c -- X RANDR gamma adjustment source
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright (c) 2010 Jon Lund Steffensen <jonlst@gmail.com>
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(s) gettext(s)
+#else
+# define _(s) s
+#endif
+
+#include <xcb/xcb.h>
+#include <xcb/randr.h>
+
+#include "gamma-randr.h"
+#include "colorramp.h"
+
+
+#define RANDR_VERSION_MAJOR 1
+#define RANDR_VERSION_MINOR 3
+
+
+int
+randr_init(randr_state_t *state, char *args)
+{
+ int screen_num = -1;
+ int crtc_num = -1;
+
+ /* Parse arguments. */
+ while (args != NULL) {
+ char *next_arg = strchr(args, ':');
+ if (next_arg != NULL) *(next_arg++) = '\0';
+
+ char *value = strchr(args, '=');
+ if (value != NULL) *(value++) = '\0';
+
+ if (strcasecmp(args, "screen") == 0) {
+ if (value == NULL) {
+ fprintf(stderr, _("Missing value for"
+ " parameter: `%s'.\n"),
+ args);
+ return -1;
+ }
+ screen_num = atoi(value);
+ } else if (strcasecmp(args, "crtc") == 0) {
+ if (value == NULL) {
+ fprintf(stderr, _("Missing value for"
+ " parameter: `%s'.\n"),
+ args);
+ return -1;
+ }
+ crtc_num = atoi(value);
+ } else {
+ fprintf(stderr, _("Unknown method parameter: `%s'.\n"),
+ args);
+ return -1;
+ }
+
+ args = next_arg;
+ }
+
+ xcb_generic_error_t *error;
+
+ /* Open X server connection */
+ int preferred_screen;
+ state->conn = xcb_connect(NULL, &preferred_screen);
+
+ if (screen_num < 0) screen_num = preferred_screen;
+
+ /* Query RandR version */
+ xcb_randr_query_version_cookie_t ver_cookie =
+ xcb_randr_query_version(state->conn, RANDR_VERSION_MAJOR,
+ RANDR_VERSION_MINOR);
+ xcb_randr_query_version_reply_t *ver_reply =
+ xcb_randr_query_version_reply(state->conn, ver_cookie, &error);
+
+ if (error) {
+ fprintf(stderr, _("`%s' returned error %d\n"),
+ "RANDR Query Version", error->error_code);
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ if (ver_reply->major_version < RANDR_VERSION_MAJOR ||
+ ver_reply->minor_version < RANDR_VERSION_MINOR) {
+ fprintf(stderr, _("Unsupported RANDR version (%u.%u)\n"),
+ ver_reply->major_version, ver_reply->minor_version);
+ free(ver_reply);
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ free(ver_reply);
+
+ /* Get screen */
+ const xcb_setup_t *setup = xcb_get_setup(state->conn);
+ xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
+ state->screen = NULL;
+
+ for (int i = 0; iter.rem > 0; i++) {
+ if (i == screen_num) {
+ state->screen = iter.data;
+ break;
+ }
+ xcb_screen_next(&iter);
+ }
+
+ if (state->screen == NULL) {
+ fprintf(stderr, _("Screen %i could not be found.\n"),
+ screen_num);
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ /* Get list of CRTCs for the screen */
+ xcb_randr_get_screen_resources_current_cookie_t res_cookie =
+ xcb_randr_get_screen_resources_current(state->conn,
+ state->screen->root);
+ xcb_randr_get_screen_resources_current_reply_t *res_reply =
+ xcb_randr_get_screen_resources_current_reply(state->conn,
+ res_cookie,
+ &error);
+
+ if (error) {
+ fprintf(stderr, _("`%s' returned error %d\n"),
+ "RANDR Get Screen Resources Current",
+ error->error_code);
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ state->crtc_num = crtc_num;
+ state->crtc_count = res_reply->num_crtcs;
+ state->crtcs = malloc(state->crtc_count * sizeof(randr_crtc_state_t));
+ if (state->crtcs == NULL) {
+ perror("malloc");
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ xcb_randr_crtc_t *crtcs =
+ xcb_randr_get_screen_resources_current_crtcs(res_reply);
+
+ /* Save CRTC identifier in state */
+ for (int i = 0; i < state->crtc_count; i++) {
+ state->crtcs[i].crtc = crtcs[i];
+ }
+
+ free(res_reply);
+
+ /* Save size and gamma ramps of all CRTCs.
+ Current gamma ramps are saved so we can restore them
+ at program exit. */
+ for (int i = 0; i < state->crtc_count; i++) {
+ xcb_randr_crtc_t crtc = state->crtcs[i].crtc;
+
+ /* Request size of gamma ramps */
+ xcb_randr_get_crtc_gamma_size_cookie_t gamma_size_cookie =
+ xcb_randr_get_crtc_gamma_size(state->conn, crtc);
+ xcb_randr_get_crtc_gamma_size_reply_t *gamma_size_reply =
+ xcb_randr_get_crtc_gamma_size_reply(state->conn,
+ gamma_size_cookie,
+ &error);
+
+ if (error) {
+ fprintf(stderr, _("`%s' returned error %d\n"),
+ "RANDR Get CRTC Gamma Size",
+ error->error_code);
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ unsigned int ramp_size = gamma_size_reply->size;
+ state->crtcs[i].ramp_size = ramp_size;
+
+ free(gamma_size_reply);
+
+ if (ramp_size == 0) {
+ fprintf(stderr, _("Gamma ramp size too small: %i\n"),
+ ramp_size);
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ /* Request current gamma ramps */
+ xcb_randr_get_crtc_gamma_cookie_t gamma_get_cookie =
+ xcb_randr_get_crtc_gamma(state->conn, crtc);
+ xcb_randr_get_crtc_gamma_reply_t *gamma_get_reply =
+ xcb_randr_get_crtc_gamma_reply(state->conn,
+ gamma_get_cookie,
+ &error);
+
+ if (error) {
+ fprintf(stderr, _("`%s' returned error %d\n"),
+ "RANDR Get CRTC Gamma", error->error_code);
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ uint16_t *gamma_r =
+ xcb_randr_get_crtc_gamma_red(gamma_get_reply);
+ uint16_t *gamma_g =
+ xcb_randr_get_crtc_gamma_green(gamma_get_reply);
+ uint16_t *gamma_b =
+ xcb_randr_get_crtc_gamma_blue(gamma_get_reply);
+
+ /* Allocate space for saved gamma ramps */
+ state->crtcs[i].saved_ramps =
+ malloc(3*ramp_size*sizeof(uint16_t));
+ if (state->crtcs[i].saved_ramps == NULL) {
+ perror("malloc");
+ free(gamma_get_reply);
+ xcb_disconnect(state->conn);
+ return -1;
+ }
+
+ /* Copy gamma ramps into CRTC state */
+ memcpy(&state->crtcs[i].saved_ramps[0*ramp_size], gamma_r,
+ ramp_size*sizeof(uint16_t));
+ memcpy(&state->crtcs[i].saved_ramps[1*ramp_size], gamma_g,
+ ramp_size*sizeof(uint16_t));
+ memcpy(&state->crtcs[i].saved_ramps[2*ramp_size], gamma_b,
+ ramp_size*sizeof(uint16_t));
+
+ free(gamma_get_reply);
+ }
+
+ return 0;
+}
+
+void
+randr_restore(randr_state_t *state)
+{
+ xcb_generic_error_t *error;
+
+ /* Restore CRTC gamma ramps */
+ for (int i = 0; i < state->crtc_count; i++) {
+ xcb_randr_crtc_t crtc = state->crtcs[i].crtc;
+
+ unsigned int ramp_size = state->crtcs[i].ramp_size;
+ uint16_t *gamma_r = &state->crtcs[i].saved_ramps[0*ramp_size];
+ uint16_t *gamma_g = &state->crtcs[i].saved_ramps[1*ramp_size];
+ uint16_t *gamma_b = &state->crtcs[i].saved_ramps[2*ramp_size];
+
+ /* Set gamma ramps */
+ xcb_void_cookie_t gamma_set_cookie =
+ xcb_randr_set_crtc_gamma_checked(state->conn, crtc,
+ ramp_size, gamma_r,
+ gamma_g, gamma_b);
+ error = xcb_request_check(state->conn, gamma_set_cookie);
+
+ if (error) {
+ fprintf(stderr, _("`%s' returned error %d\n"),
+ "RANDR Set CRTC Gamma", error->error_code);
+ fprintf(stderr, _("Unable to restore CRTC %i\n"), i);
+ }
+ }
+}
+
+void
+randr_free(randr_state_t *state)
+{
+ /* Free CRTC state */
+ for (int i = 0; i < state->crtc_count; i++) {
+ free(state->crtcs[i].saved_ramps);
+ }
+ free(state->crtcs);
+
+ /* Close connection */
+ xcb_disconnect(state->conn);
+}
+
+static int
+randr_set_temperature_for_crtc(randr_state_t *state, int crtc_num, int temp,
+ float gamma[3])
+{
+ xcb_generic_error_t *error;
+
+ if (crtc_num >= state->crtc_count || crtc_num < 0) {
+ fprintf(stderr, _("CRTC %d does not exist. "),
+ state->crtc_num);
+ if (state->crtc_count > 1) {
+ fprintf(stderr, _("Valid CRTCs are [0-%d].\n"),
+ state->crtc_count-1);
+ } else {
+ fprintf(stderr, _("Only CRTC 0 exists.\n"));
+ }
+
+ return -1;
+ }
+
+ xcb_randr_crtc_t crtc = state->crtcs[crtc_num].crtc;
+ unsigned int ramp_size = state->crtcs[crtc_num].ramp_size;
+
+ /* Create new gamma ramps */
+ uint16_t *gamma_ramps = malloc(3*ramp_size*sizeof(uint16_t));
+ if (gamma_ramps == NULL) {
+ perror("malloc");
+ return -1;
+ }
+
+ uint16_t *gamma_r = &gamma_ramps[0*ramp_size];
+ uint16_t *gamma_g = &gamma_ramps[1*ramp_size];
+ uint16_t *gamma_b = &gamma_ramps[2*ramp_size];
+
+ colorramp_fill(gamma_r, gamma_g, gamma_b, ramp_size,
+ temp, gamma);
+
+ /* Set new gamma ramps */
+ xcb_void_cookie_t gamma_set_cookie =
+ xcb_randr_set_crtc_gamma_checked(state->conn, crtc,
+ ramp_size, gamma_r,
+ gamma_g, gamma_b);
+ error = xcb_request_check(state->conn, gamma_set_cookie);
+
+ if (error) {
+ fprintf(stderr, _("`%s' returned error %d\n"),
+ "RANDR Set CRTC Gamma", error->error_code);
+ free(gamma_ramps);
+ return -1;
+ }
+
+ free(gamma_ramps);
+
+ return 0;
+}
+
+int
+randr_set_temperature(randr_state_t *state, int temp, float gamma[3])
+{
+ int r;
+
+ /* If no CRTC number has been specified,
+ set temperature on all CRTCs. */
+ if (state->crtc_num < 0) {
+ for (int i = 0; i < state->crtc_count; i++) {
+ r = randr_set_temperature_for_crtc(state, i,
+ temp, gamma);
+ if (r < 0) return -1;
+ }
+ } else {
+ return randr_set_temperature_for_crtc(state, state->crtc_num,
+ temp, gamma);
+ }
+
+ return 0;
+}