aboutsummaryrefslogtreecommitdiffstats
path: root/redshift/backend-direct.c
diff options
context:
space:
mode:
Diffstat (limited to 'redshift/backend-direct.c')
-rw-r--r--redshift/backend-direct.c941
1 files changed, 941 insertions, 0 deletions
diff --git a/redshift/backend-direct.c b/redshift/backend-direct.c
new file mode 100644
index 0000000..9f62c4d
--- /dev/null
+++ b/redshift/backend-direct.c
@@ -0,0 +1,941 @@
+/*-
+ * redshift-ng - Automatically adjust display colour temperature according the Sun
+ *
+ * Copyright (c) 2009-2018 Jon Lund Steffensen <jonlst@gmail.com>
+ * Copyright (c) 2014-2016, 2025 Mattias Andrée <m@maandree.se>
+ *
+ * redshift-ng 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-ng 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-ng. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "common.h"
+
+
+/**
+ * Union of libgamma gamma ramp structures
+ */
+union gamma_ramps {
+ /**
+ * Gamma ramp sizes
+ */
+ struct {
+ size_t red; /**< Size of the gamma ramp for the red channel */
+ size_t green; /**< Size of the gamma ramp for the green channel */
+ size_t blue; /**< Size of the gamma ramp for the blue channel */
+ } size;
+
+ /* Ramp structures */
+#define X(SUFFIX, RAMPS, TYPE, MAX, DEPTH)\
+ struct libgamma_gamma_##RAMPS RAMPS
+ LIST_RAMPS_STOP_VALUE_TYPES(X, ;);
+#undef X
+};
+
+
+/**
+ * State for partition (e.g. X screen or graphics card)
+ */
+struct partition_state {
+ /**
+ * libgamma state
+ */
+ struct libgamma_partition_state state;
+
+ /**
+ * Index of CRTC when indexing spans multiple X screens
+ */
+ size_t crtc_num_offset;
+};
+
+
+/**
+ * CRTC state
+ */
+struct crtc_state {
+ /**
+ * libgamma state
+ */
+ struct libgamma_crtc_state state;
+
+ /**
+ * Gamma ramp depth, 0 if uninitialised,
+ * -1 if single-precision float,
+ * -2 if double-precision float,
+ * otherwise the number of bits (of unsigned integer)
+ */
+ signed gamma_depth;
+
+ /**
+ * Gamma ramps used by the gamma adjustment function
+ */
+ union gamma_ramps gamma_ramps;
+
+ /**
+ * Original state of gamma ramps
+ */
+ union gamma_ramps saved_gamma_ramps;
+};
+
+
+/**
+ * CRTC number, either overall number or partion and number within partition
+ */
+struct crtc_number {
+ /**
+ * The partition of the EDID, -1 if using overall ordinal
+ */
+ ssize_t partition;
+
+ /**
+ * CRTC number within the partition, or overall if no partition is specified
+ */
+ size_t crtc;
+};
+
+
+struct gamma_state {
+ /**
+ * libgamma state for the site (e.g. X display)
+ */
+ struct libgamma_site_state site;
+
+ /**
+ * Whether the adjustment method supports fetching
+ * EDIDs from the outputs
+ */
+ unsigned supports_edid : 1;
+
+ /**
+ * Whether the adjustment method supports multiple
+ * sites rather than just the default site
+ */
+ unsigned multiple_sites : 1;
+
+ /**
+ * Whether the adjustment method supports multiple partitions
+ * per site
+ */
+ unsigned multiple_partitions : 1;
+
+ /**
+ * Whether the adjustment method supports multiple CRTC:s
+ * per partition per site
+ */
+ unsigned multiple_crtcs : 1;
+
+ /**
+ * Whether partitions are graphics cards
+ */
+ unsigned partitions_are_graphics_cards : 1;
+
+ /**
+ * Whether a parition has been selected
+ */
+ unsigned partitions_selected : 1;
+
+ /**
+ * Whether CRTCs have been selected
+ */
+ unsigned crtcs_selected : 1;
+
+ /**
+ * Whether the program has connected to the site
+ */
+ unsigned connected : 1;
+
+ /**
+ * The libgamma constant for the adjustment method
+ */
+ int method;
+
+ /**
+ * Number of selected CRTCs
+ */
+ size_t ncrtcs;
+
+ /**
+ * Number of selected EDIDs
+ */
+ size_t nedids;
+
+ /**
+ * Number of selected parition
+ */
+ size_t npartitions;
+
+ /**
+ * Selected paritions (if `partitions_selected`)
+ */
+ size_t *selected_partitions;
+
+ /**
+ * Selected CRTC numbers
+ *
+ * Deallocated by when no longer needed
+ */
+ struct crtc_number *selected_crtcs;
+
+ /**
+ * Selected EDIDs
+ *
+ * Deallocated by when no longer needed
+ */
+ char **selected_edids;
+
+ /**
+ * Name of site to connect to, `NULL` if not selected
+ * or once connected to the site
+ */
+ char *site_name;
+
+ /**
+ * State for selected paritions
+ */
+ struct partition_state *partitions;
+
+ /**
+ * State for selected CRTCs
+ */
+ struct crtc_state *crtcs;
+};
+
+
+int
+direct_create(struct gamma_state **state_out, int method, const char *method_name)
+{
+ struct libgamma_method_capabilities caps;
+ struct gamma_state *state;
+ int err;
+
+ *state_out = NULL;
+
+ if (!libgamma_is_method_available(method)) {
+ weprintf(_("Adjustment method `%s' is not available"), method_name);
+ return -1;
+ }
+
+ err = libgamma_method_capabilities(&caps, sizeof(caps), method);
+ if (err) {
+ weprintf("libgamma_method_capabilities %s: %s", libgamma_const_of_method(method), libgamma_strerror(err));
+ return -1;
+ }
+
+ state = *state_out = ecalloc(1, sizeof(**state_out));
+ state->selected_crtcs = NULL;
+ state->partitions = NULL;
+ state->site_name = NULL;
+ state->method = method;
+ state->supports_edid = (caps.crtc_information & LIBGAMMA_CRTC_INFO_EDID) ? 1 : 0;
+ state->multiple_sites = caps.multiple_sites;
+ state->multiple_partitions = caps.multiple_partitions;
+ state->multiple_crtcs = caps.multiple_crtcs;
+ state->partitions_are_graphics_cards = caps.partitions_are_graphics_cards;
+
+ if (state->multiple_sites)
+ return 0;
+
+ err = libgamma_site_initialise(&state->site, method, NULL);
+ if (err) {
+ weprintf("libgamma_site_initialise %s NULL: %s", libgamma_const_of_method(method), libgamma_strerror(err));
+ free(state);
+ *state_out = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void
+direct_print_help(int method)
+{
+ struct libgamma_method_capabilities caps;
+
+ if (libgamma_method_capabilities(&caps, sizeof(caps), method)) {
+ printf(_("Adjustment method not available\n"));
+ printf("\n");
+ return;
+ }
+
+ if (caps.multiple_sites)
+ printf(" display=%s %s\n", _("NAME "), _("Display server instance to apply adjustments to"));
+
+ if (caps.multiple_partitions && caps.partitions_are_graphics_cards) {
+ /* TRANSLATORS: "N" represents "ordinal"; right-pad with spaces to preserve display width */
+ printf(" card=%s %s\n", _("N "), _("Graphics card to apply adjustments to"));
+ } else if (caps.multiple_partitions) {
+ /* TRANSLATORS: "N" represents "ordinal"; right-pad with spaces to preserve display width */
+ printf(" screen=%s %s\n", _("N "), _("List of comma-separated X screens to apply adjustments to"));
+ }
+
+ if (caps.multiple_crtcs) {
+ /* TRANSLATORS: "N" represents "number"; right-pad with spaces to preserve display width */
+ printf(" crtc=%s %s\n", _("N "), _("List of comma-separated CRTCs to apply adjustments to"));
+ }
+ if (caps.multiple_crtcs && (caps.crtc_information & LIBGAMMA_CRTC_INFO_EDID)) {
+ printf(" edid=%s %s\n", _("EDID "), _("List of comma-separated EDIDS of monitors to apply "
+ "adjustments to, enter `list' to list available monitors"));
+ }
+
+ if (caps.multiple_sites || caps.multiple_partitions || caps.multiple_crtcs)
+ printf("\n");
+}
+
+
+int
+direct_set_option(struct gamma_state *state, const char *key, const char *value)
+{
+ if (state->multiple_sites && !strcasecmp(key, "display")) {
+ return direct_set_site(state, key, value);
+ } else if (state->multiple_partitions && !strcasecmp(key, state->partitions_are_graphics_cards ? "card" : "screen")) {
+ return direct_set_partitions(state, key, value);
+ } else if (state->multiple_crtcs && !strcasecmp(key, "crtc")) {
+ return direct_set_crtcs(state, key, value);
+ } else if (state->multiple_crtcs && state->supports_edid && !strcasecmp(key, "edid")) {
+ return direct_set_edids(state, key, value);
+ } else if (!strcasecmp(key, "preserve")) {
+ weprintf(_("Deprecated method parameter ignored: `%s'."), key);
+ return 0;
+ } else {
+ weprintf(_("Unknown method parameter: `%s'."), key);
+ return -1;
+ }
+}
+
+
+int
+direct_set_site(struct gamma_state *state, const char *key, const char *value)
+{
+ if (state->site_name) {
+ weprintf(_("`%s' option specified multiple times, using last selection."), key);
+ free(state->site_name);
+ }
+ state->site_name = estrdup(value);
+ return 0;
+}
+
+
+int
+direct_set_partitions(struct gamma_state *state, const char *key, const char *value)
+{
+ const char *end, *p;
+ uintmax_t num;
+ size_t count;
+
+ /* Check previously unspecified */
+ if (state->partitions_selected)
+ weprintf(_("`%s' option specified multiple times, using last selection."), key);
+ state->partitions_selected = 1;
+
+ /* Check if all are selected */
+ state->npartitions = 0;
+ free(state->selected_partitions);
+ state->selected_partitions = NULL;
+ if (!*value || !strcasecmp(value, "all"))
+ return 0;
+
+ /* Get number count */
+ for (p = value, count = 1; *p; p++)
+ if (*p == ',')
+ count++;
+ state->selected_partitions = ecalloc(count, sizeof(*state->selected_partitions));
+
+ /* Parse numbers */
+ errno = 0;
+ for (p = value; *p; p = end) {
+ num = strtoumax(p, (void *)&end, 10);
+ state->selected_partitions[state->npartitions++] = (size_t)num;
+ if (num > (uintmax_t)SIZE_MAX || (*end && *end != ',') || !isdigit(*p) || errno) {
+ weprintf(_("Invalid value of `%s' option: `%s'."), key, value);
+ return -1;
+ }
+ end = &end[*end == ','];
+ }
+
+ return 0;
+}
+
+
+int
+direct_set_crtcs(struct gamma_state *state, const char *key, const char *value)
+{
+ const char *end, *p;
+ uintmax_t num;
+ size_t count;
+
+ /* Check previously unspecified */
+ if (state->crtcs_selected)
+ weprintf(_("`%s' option specified multiple times, using last selection."), key);
+ state->crtcs_selected = 1;
+
+ /* Check if all are selected */
+ state->ncrtcs = 0;
+ free(state->selected_crtcs);
+ state->selected_crtcs = NULL;
+ if (!*value || !strcasecmp(value, "all"))
+ return 0;
+
+ /* Get number count */
+ for (p = value, count = 1; *p; p++)
+ if (*p == ',')
+ count++;
+ state->selected_crtcs = ecalloc(count, sizeof(*state->selected_crtcs));
+
+ /* Parse numbers */
+ errno = 0;
+ for (p = value; *p; p = end) {
+ num = strtoumax(p, (void *)&end, 10);
+ state->selected_crtcs[state->ncrtcs].partition = -1;
+ state->selected_crtcs[state->ncrtcs].crtc = (size_t)num;
+ if (num > (uintmax_t)SIZE_MAX || (*end && *end != ',' && *end != '.') || !isdigit(*p) || errno) {
+ invalid:
+ weprintf(_("Invalid value of `%s' option: `%s'."), key, value);
+ return -1;
+ }
+ if (*end == '.') {
+ if (!state->multiple_partitions || num > (uintmax_t)SSIZE_MAX)
+ goto invalid;
+ state->selected_crtcs[state->ncrtcs].partition = (ssize_t)num;
+ p = &end[1];
+ num = strtoumax(p, (void *)&end, 10);
+ state->selected_crtcs[state->ncrtcs].crtc = (size_t)num;
+ if (num > (uintmax_t)SIZE_MAX || (*end && *end != ',') || !isdigit(*p) || errno)
+ goto invalid;
+ }
+ end = &end[*end == ','];
+ state->ncrtcs++;
+ }
+
+ return 0;
+}
+
+
+int
+direct_set_edids(GAMMA_STATE *state, const char *key, const char *value)
+{
+ const char *end, *p;
+ size_t count, len;
+
+ /* Check previously unspecified */
+ if (state->nedids) {
+ weprintf(_("`%s' option specified multiple times, using last selection."), key);
+ while (state->nedids)
+ free(state->selected_edids[--state->nedids]);
+ free(state->selected_edids);
+ state->selected_edids = NULL;
+ state->nedids = 0;
+ }
+
+ /* Get number count */
+ for (p = value, count = 1; *p; p++)
+ if (*p == ',')
+ count++;
+ state->selected_edids = ecalloc(count, sizeof(*state->selected_edids));
+
+ /* Split */
+ for (p = value;; p = end) {
+ end = strchr(p, ',');
+ if (!end) {
+ state->selected_edids[state->nedids++] = estrdup(p);
+ break;
+ }
+ len = (size_t)(end++ - p);
+ state->selected_edids[state->nedids] = emalloc(len + 1U);
+ memcpy(state->selected_edids[state->nedids], p, len);
+ state->selected_edids[state->nedids][len] = '\0';
+ state->nedids++;
+ }
+
+ return 0;
+}
+
+
+int
+direct_start(struct gamma_state *state)
+{
+ struct {
+ size_t partition;
+ size_t crtc;
+ char *edid;
+ } *resolved_edids = NULL;
+ struct libgamma_crtc_information crtc_info;
+ struct libgamma_crtc_state crtc_state;
+ size_t crtc_num_offset = 0;
+ size_t crtcs_removed = 0;
+ size_t nresolved_edids = 0;
+ size_t i, j, k, num, part, count;
+ int err;
+
+ /* Connect to display server */
+ if (state->multiple_sites) {
+ if (state->site_name && !*state->site_name) {
+ free(state->site_name);
+ state->site_name = NULL;
+ }
+ err = libgamma_site_initialise(&state->site, state->method, state->site_name);
+ state->site_name = NULL;
+ if (err) {
+ weprintf("libgamma_site_initialise %s %s: %s",
+ libgamma_const_of_method(state->method),
+ state->site_name ? state->site_name : "NULL",
+ libgamma_strerror(err));
+ return -1;
+ }
+ state->connected = 1;
+ }
+
+ /* Allocate partition states */
+ if (state->npartitions) {
+ state->partitions = ecalloc(state->npartitions, sizeof(*state->partitions));
+ for (i = 0; i < state->npartitions; i++) {
+ state->partitions[i].state.partition = state->selected_partitions[i];
+ state->partitions[i].state.data = NULL;
+ }
+ free(state->selected_partitions);
+ state->selected_partitions = NULL;
+ } else if (!state->site.partitions_available) {
+ if (state->partitions_are_graphics_cards)
+ weprintf(_("No graphics card found."));
+ else
+ weprintf(_("No X screen found."));
+ return -1;
+ } else {
+ state->npartitions = state->site.partitions_available;
+ state->partitions = ecalloc(state->npartitions, sizeof(*state->partitions));
+ for (i = 0; i < state->npartitions; i++) {
+ state->partitions[i].state.partition = i;
+ state->partitions[i].state.data = NULL;
+ }
+ }
+
+ /* Map the partition indices in CRTC numbers from indices of selected
+ * partitions, and allocate partitions specified via CRTC numbers */
+ for (i = 0; i < state->ncrtcs; i++) {
+ if (state->selected_crtcs[i].partition < 0)
+ continue;
+ for (j = 0; j < state->npartitions; j++) {
+ if ((size_t)state->selected_crtcs[i].partition == state->partitions[j].state.partition) {
+ state->selected_crtcs[i].partition = (ssize_t)j;
+ break;
+ }
+ }
+ if (j == state->npartitions) {
+ state->partitions = realloc(state->partitions, (state->npartitions + 1U) * sizeof(*state->partitions));
+ state->partitions[state->npartitions].state.partition = (size_t)state->selected_crtcs[i].partition;
+ state->partitions[state->npartitions].state.data = NULL;
+ state->npartitions++;
+ }
+ }
+
+ /* Initialise partitions */
+ for (i = 0; i < state->npartitions; i++) {
+ num = state->partitions[i].state.partition;
+ err = libgamma_partition_initialise(&state->partitions[i].state, &state->site, num);
+ if (err) {
+ weprintf("libgamma_partition_initialise %zu: %s", num, libgamma_strerror(err));
+ return -1;
+ }
+ state->partitions[i].crtc_num_offset = crtc_num_offset;
+ crtc_num_offset += state->partitions[i].state.crtcs_available;
+ }
+
+ /* Resolve EDIDs */
+ if (state->nedids) {
+ for (i = 0; i < state->npartitions; i++) {
+ count = state->partitions[i].state.crtcs_available;
+ if (!count)
+ continue;
+ resolved_edids = erealloc(resolved_edids, (nresolved_edids + count) + sizeof(*resolved_edids));
+ for (j = 0; j < count; j++) {
+ if (libgamma_crtc_initialise(&crtc_state, &state->partitions[i].state, j))
+ continue;
+ libgamma_get_crtc_information(&crtc_info, sizeof(crtc_info), &crtc_state, LIBGAMMA_CRTC_INFO_EDID);
+ if (crtc_info.edid_error)
+ goto next_crtc;
+ resolved_edids[nresolved_edids].partition = i;
+ resolved_edids[nresolved_edids].crtc = j;
+ resolved_edids[nresolved_edids].edid = libgamma_behex_edid(crtc_info.edid, crtc_info.edid_length);
+ if (!resolved_edids[nresolved_edids].edid)
+ eprintf("libgamma_behex_edid:");
+ nresolved_edids++;
+ next_crtc:
+ libgamma_crtc_information_destroy(&crtc_info);
+ libgamma_crtc_destroy(&crtc_state);
+ }
+ }
+ for (i = 0; i < state->nedids; i++) {
+ if (!strcasecmp(state->selected_edids[i], "list")) {
+ printf(_("Available outputs:\n"));
+ for (i = 0; i < nresolved_edids; i++)
+ printf(" %s\n", resolved_edids[i].edid);
+ direct_free(state);
+ exit(0);
+ }
+ for (j = 0; j < nresolved_edids; j++) {
+ if (!strcasecmp(state->selected_edids[i], resolved_edids[i].edid)) {
+ if (!state->multiple_partitions) {
+ weprintf("Resolved output `%s' to CRTC %zu",
+ resolved_edids[i].edid, resolved_edids[i].crtc);
+ } else if (state->partitions_are_graphics_cards) {
+ weprintf("Resolved output `%s' to CRTC %zu on graphics card %zu",
+ resolved_edids[i].edid, resolved_edids[i].crtc,
+ resolved_edids[i].partition);
+ } else {
+ weprintf("Resolved output `%s' to CRTC %zu on X screen %zu",
+ resolved_edids[i].edid, resolved_edids[i].crtc,
+ resolved_edids[i].partition);
+ }
+ count = state->ncrtcs + 1U;
+ state->selected_crtcs = erealloc(state->selected_crtcs, count * sizeof(*state->crtcs));
+ state->selected_crtcs[state->ncrtcs].partition = (ssize_t)resolved_edids[i].partition;
+ state->selected_crtcs[state->ncrtcs].crtc = resolved_edids[i].crtc;
+ state->ncrtcs++;
+ goto next_edid;
+ }
+ }
+ weprintf(_("Output `%s' found."), state->selected_edids[i]);
+ next_edid:
+ free(state->selected_edids[i]);
+ state->selected_edids[i] = NULL;
+ }
+ while (nresolved_edids)
+ free(resolved_edids[--nresolved_edids].edid);
+ free(resolved_edids);
+ free(state->selected_edids);
+ state->selected_edids = NULL;
+ }
+
+ /* Allocate CRTCs states and map to partition–CRTC pairs */
+ if (state->ncrtcs) {
+ state->crtcs = ecalloc(state->ncrtcs, sizeof(*state->crtcs));
+ for (i = 0; i < state->ncrtcs; i++) {
+ state->crtcs[i].state.data = NULL;
+ state->crtcs[i].state.partition = NULL;
+ if (state->selected_crtcs[i].partition >= 0)
+ state->crtcs[i].state.partition = &state->partitions[state->selected_crtcs[i].partition].state;
+ state->crtcs[i].state.crtc = state->selected_crtcs[i].crtc;
+ }
+ for (i = 0; i < state->ncrtcs; i++) {
+ if (state->crtcs[i].state.partition)
+ continue;
+ for (j = 1; j < state->npartitions; j++)
+ if (state->crtcs[i].state.crtc < state->partitions[j].crtc_num_offset)
+ break;
+ state->crtcs[i].state.partition = &state->partitions[j - 1U].state;
+ state->crtcs[i].state.crtc -= state->partitions[j - 1U].crtc_num_offset;
+ }
+ } else if (!crtc_num_offset) {
+ weprintf(_("No CRTCs found."));
+ return -1;
+ } else {
+ state->ncrtcs = crtc_num_offset;
+ state->crtcs = ecalloc(state->ncrtcs, sizeof(*state->crtcs));
+ for (j = 0, i = 0; j < state->npartitions; j++) {
+ for (k = 0; k < state->partitions[j].state.crtcs_available; k++, i++) {
+ state->crtcs[i].state.data = NULL;
+ state->crtcs[i].state.partition = &state->partitions[j].state;
+ state->crtcs[i].state.crtc = k;
+ }
+ }
+ }
+
+ /* Initialise CRTCs and fetch original gamma adjustments */
+ for (i = 0; i < state->ncrtcs; i++) {
+ /* Get CRTC */
+ part = state->crtcs[i].state.partition->partition;
+ num = state->crtcs[i].state.crtc;
+ err = libgamma_crtc_initialise(&state->crtcs[i].state, state->crtcs[i].state.partition, num);
+ if (err) {
+ weprintf("libgamma_crtc_initialise %zu %zu: %s", part, num, libgamma_strerror(err));
+ return -1;
+ }
+
+ /* Get gamma ramp inforamtion for CRTC */
+ libgamma_get_crtc_information(&crtc_info, sizeof(crtc_info), &state->crtcs[i].state,
+ LIBGAMMA_CRTC_INFO_GAMMA_SIZE |
+ LIBGAMMA_CRTC_INFO_GAMMA_DEPTH |
+ LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT);
+ if (crtc_info.gamma_size_error || crtc_info.gamma_depth_error) {
+ libgamma_crtc_destroy(&state->crtcs[i].state);
+ state->crtcs[i].state.data = NULL;
+ crtcs_removed++;
+ goto no_gamma_support;
+ }
+ if (!crtc_info.gamma_support_error && crtc_info.gamma_support == LIBGAMMA_NO) {
+ no_gamma_support:
+ if (state->multiple_crtcs) {
+ if (!state->multiple_partitions)
+ weprintf(_("Adjustments are not supported on CRTC %zu."), num);
+ else if (state->partitions_are_graphics_cards)
+ weprintf(_("Adjustments are not supported on CRTC %zu on graphics card %zu."), num, part);
+ else
+ weprintf(_("Adjustments are not supported on CRTC %zu on X screen %zu."), num, part);
+ } else {
+ if (!state->multiple_partitions)
+ weprintf(_("Adjustments are not supported."));
+ else if (state->partitions_are_graphics_cards)
+ weprintf(_("Adjustments are not supported on graphics card %zu."), part);
+ else
+ weprintf(_("Adjustments are not supported on X screen %zu."), part);
+ }
+ if (!state->crtcs[i].state.data)
+ goto next;
+ }
+
+ /* Initialise and fetch gamma adjustments */
+ state->crtcs[i].gamma_ramps.size.red = crtc_info.red_gamma_size;
+ state->crtcs[i].gamma_ramps.size.green = crtc_info.green_gamma_size;
+ state->crtcs[i].gamma_ramps.size.blue = crtc_info.blue_gamma_size;
+ state->crtcs[i].saved_gamma_ramps.size.red = crtc_info.red_gamma_size;
+ state->crtcs[i].saved_gamma_ramps.size.green = crtc_info.green_gamma_size;
+ state->crtcs[i].saved_gamma_ramps.size.blue = crtc_info.blue_gamma_size;
+ switch (crtc_info.gamma_depth) {
+#define X(SUFFIX, RAMPS, TYPE, MAX, DEPTH)\
+ case DEPTH:\
+ if (libgamma_gamma_##RAMPS##_initialise(&state->crtcs[i].gamma_ramps.RAMPS) ||\
+ libgamma_gamma_##RAMPS##_initialise(&state->crtcs[i].saved_gamma_ramps.RAMPS))\
+ eprintf("libgamma_gamma_"#RAMPS"_initialise:");\
+ j = 0;\
+ do {\
+ err = libgamma_crtc_get_gamma_##RAMPS(&state->crtcs[i].state,\
+ &state->crtcs[i].saved_gamma_ramps.RAMPS);\
+ } while (err && j++ < 10);\
+ if (!err) {\
+ state->crtcs[i].gamma_depth = DEPTH;\
+ break;\
+ }\
+ if (state->multiple_crtcs) {\
+ if (!state->multiple_partitions)\
+ weprintf(_("Could not get current adjustments for CRTC %zu."), num);\
+ else if (state->partitions_are_graphics_cards)\
+ weprintf(_("Could not get current adjustments for CRTC %zu on graphics card %zu."),\
+ num, part);\
+ else\
+ weprintf(_("Could not get current adjustments for CRTC %zu on X screen %zu."), num, part);\
+ } else {\
+ if (!state->multiple_partitions)\
+ weprintf(_("Could not get current adjustments."));\
+ else if (state->partitions_are_graphics_cards)\
+ weprintf(_("Could not get current adjustments for graphics card %zu."), part);\
+ else\
+ weprintf(_("Could not get current adjustments for X screen %zu."), part);\
+ }\
+ libgamma_gamma_##RAMPS##_destroy(&state->crtcs[i].gamma_ramps.RAMPS);\
+ libgamma_gamma_##RAMPS##_destroy(&state->crtcs[i].saved_gamma_ramps.RAMPS);\
+ libgamma_crtc_destroy(&state->crtcs[i].state);\
+ state->crtcs[i].state.data = NULL;\
+ crtcs_removed++;\
+ goto next
+
+ LIST_RAMPS_STOP_VALUE_TYPES(X, ;);
+#undef X
+
+ default:
+ if (state->multiple_crtcs) {
+ if (!state->multiple_partitions)
+ weprintf(_("Unsupported gamma ramp type on CRTC %zu."), num);
+ else if (state->partitions_are_graphics_cards)
+ weprintf(_("Unsupported gamma ramp type on CRTC %zu on graphics card %zu."), num, part);
+ else
+ weprintf(_("Unsupported gamma ramp type on CRTC %zu on X screen %zu."), num, part);
+ } else {
+ if (!state->multiple_partitions)
+ weprintf(_("Unsupported gamma ramp type."));
+ else if (state->partitions_are_graphics_cards)
+ weprintf(_("Unsupported gamma ramp type on graphics card %zu."), part);
+ else
+ weprintf(_("Unsupported gamma ramp type on X screen %zu."), part);
+ }
+ return -1;
+ }
+
+ next:
+ libgamma_crtc_information_destroy(&crtc_info);
+ }
+
+ /* Unlist removed CRTCs */
+ if (crtcs_removed) {
+ num = state->ncrtcs - crtcs_removed;
+ for (i = j = 0; crtcs_removed; i++, j++)
+ if (!state->crtcs[i].state.data)
+ break;
+ for (; crtcs_removed; i++) {
+ if (state->crtcs[i].state.data)
+ crtcs_removed--;
+ else
+ memmove(&state->crtcs[j++], &state->crtcs[i], sizeof(*state->crtcs));
+ }
+ memmove(&state->crtcs[j], &state->crtcs[i], (state->ncrtcs - i) * sizeof(*state->crtcs));
+ state->ncrtcs = num;
+ }
+ if (!state->ncrtcs) {
+ weprintf(_("No usable CRTCs available."));
+ return -1;
+ }
+
+ free(state->selected_crtcs);
+ state->selected_crtcs = NULL;
+ return 0;
+}
+
+
+int
+direct_apply(struct gamma_state *state, const struct colour_setting *setting, int preserve)
+{
+ size_t i, err_count = 0, crtc, part;
+ const char *errstr;
+ int err;
+
+ for (i = 0; i < state->ncrtcs; i++) {
+ switch (state->crtcs[i].gamma_depth) {
+#define X(SUFFIX, RAMPS, TYPE, MAX, DEPTH)\
+ case DEPTH:\
+ fill_ramps_##SUFFIX(state->crtcs[i].gamma_ramps.RAMPS.red,\
+ state->crtcs[i].gamma_ramps.RAMPS.green,\
+ state->crtcs[i].gamma_ramps.RAMPS.blue,\
+ preserve ? state->crtcs[i].saved_gamma_ramps.RAMPS.red : NULL,\
+ preserve ? state->crtcs[i].saved_gamma_ramps.RAMPS.green : NULL,\
+ preserve ? state->crtcs[i].saved_gamma_ramps.RAMPS.blue : NULL,\
+ state->crtcs[i].gamma_ramps.size.red,\
+ state->crtcs[i].gamma_ramps.size.green,\
+ state->crtcs[i].gamma_ramps.size.blue,\
+ setting);\
+ err = libgamma_crtc_set_gamma_##RAMPS(&state->crtcs[i].state, &state->crtcs[i].gamma_ramps.RAMPS);\
+ break
+
+ LIST_RAMPS_STOP_VALUE_TYPES(X, ;);
+#undef X
+
+ default:
+ err_count++;
+ err = 0;
+ break;
+ }
+
+ if (err) {
+ err_count++;
+ crtc = state->crtcs[i].state.crtc;
+ part = state->crtcs[i].state.partition->partition;
+ errstr = libgamma_strerror(err);
+ if (state->multiple_crtcs) {
+ if (!state->multiple_partitions)
+ weprintf(_("Unable to set adjustments for CRTC %zu: %s."), crtc, errstr);
+ else if (state->partitions_are_graphics_cards)
+ weprintf(_("Unable to set adjustments for CRTC %zu on graphics card %zu: %s."),
+ crtc, part, errstr);
+ else
+ weprintf(_("Unable to set adjustments for CRTC %zu on X screen %zu: %s."),
+ crtc, part, errstr);
+ } else {
+ if (!state->multiple_partitions)
+ weprintf(_("Unable to set adjustments: %s."), errstr);
+ else if (state->partitions_are_graphics_cards)
+ weprintf(_("Unable to set adjustments for graphics card %zu: %s."), part, errstr);
+ else
+ weprintf(_("Unable to set adjustments for X screen %zu: %s."), part, errstr);
+ }
+ }
+ }
+
+ return err_count == state->ncrtcs ? -1 : 0;
+}
+
+
+void
+direct_restore(struct gamma_state *state)
+{
+ size_t i, crtc, part;
+ const char *errstr;
+ int err;
+
+ for (i = 0; i < state->ncrtcs; i++) {
+ switch (state->crtcs[i].gamma_depth) {
+#define X(SUFFIX, RAMPS, TYPE, MAX, DEPTH)\
+ case DEPTH:\
+ err = libgamma_crtc_set_gamma_##RAMPS(&state->crtcs[i].state, &state->crtcs[i].saved_gamma_ramps.RAMPS);\
+ break
+
+ LIST_RAMPS_STOP_VALUE_TYPES(X, ;);
+#undef X
+
+ default:
+ err = 0;
+ break;
+ }
+
+ if (err) {
+ crtc = state->crtcs[i].state.crtc;
+ part = state->crtcs[i].state.partition->partition;
+ errstr = libgamma_strerror(err);
+ if (state->multiple_crtcs) {
+ if (!state->multiple_partitions)
+ weprintf(_("Unable to restore adjustments for CRTC %zu: %s."), crtc, errstr);
+ else if (state->partitions_are_graphics_cards)
+ weprintf(_("Unable to restore adjustments for CRTC %zu on graphics card %zu: %s."),
+ crtc, part, errstr);
+ else
+ weprintf(_("Unable to restore adjustments for CRTC %zu on X screen %zu: %s."),
+ crtc, part, errstr);
+ } else {
+ if (!state->multiple_partitions)
+ weprintf(_("Unable to restore adjustments: %s."), errstr);
+ else if (state->partitions_are_graphics_cards)
+ weprintf(_("Unable to restore adjustments for graphics card %zu: %s."), part, errstr);
+ else
+ weprintf(_("Unable to restore adjustments for X screen %zu: %s."), part, errstr);
+ }
+ }
+ }
+}
+
+
+void
+direct_free(struct gamma_state *state)
+{
+ size_t i;
+ if (state->crtcs) {
+ for (i = 0; i < state->ncrtcs; i++) {
+ switch (state->crtcs[i].gamma_depth) {
+#define X(SUFFIX, RAMPS, TYPE, MAX, DEPTH)\
+ case DEPTH:\
+ libgamma_gamma_##RAMPS##_destroy(&state->crtcs[i].gamma_ramps.RAMPS);\
+ libgamma_gamma_##RAMPS##_destroy(&state->crtcs[i].saved_gamma_ramps.RAMPS);\
+ break
+ LIST_RAMPS_STOP_VALUE_TYPES(X, ;);
+#undef X
+ default:
+ case 0: /* not initialised */
+ break;
+ }
+ if (state->crtcs[i].state.data)
+ libgamma_crtc_destroy(&state->crtcs[i].state);
+ }
+ free(state->crtcs);
+ }
+ if (state->partitions) {
+ for (i = 0; i < state->npartitions; i++)
+ if (state->partitions[i].state.data)
+ libgamma_partition_destroy(&state->partitions[i].state);
+ free(state->partitions);
+ }
+ free(state->selected_partitions);
+ free(state->selected_crtcs);
+ if (state->selected_edids) {
+ for (i = 0; i < state->nedids; i++)
+ free(state->selected_edids[i]);
+ free(state->selected_edids);
+ }
+ if (state->connected)
+ libgamma_site_destroy(&state->site);
+ free(state->site_name);
+ free(state);
+}