aboutsummaryrefslogtreecommitdiffstats
path: root/cg-base.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2019-10-06 15:42:19 +0200
committerMattias Andrée <maandree@kth.se>2019-10-06 15:42:19 +0200
commit60f0fced886d2d5afd05cc157bee65f41869ce6d (patch)
treec6cf516c040002911d2184b6c96a4ba9a05c54ee /cg-base.c
parenttypo (diff)
downloadradharc-60f0fced886d2d5afd05cc157bee65f41869ce6d.tar.gz
radharc-60f0fced886d2d5afd05cc157bee65f41869ce6d.tar.bz2
radharc-60f0fced886d2d5afd05cc157bee65f41869ce6d.tar.xz
Rewrite everything
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'cg-base.c')
-rw-r--r--cg-base.c927
1 files changed, 927 insertions, 0 deletions
diff --git a/cg-base.c b/cg-base.c
new file mode 100644
index 0000000..9f77937
--- /dev/null
+++ b/cg-base.c
@@ -0,0 +1,927 @@
+/**
+ * cg-tools -- Cooperative gamma-enabled tools
+ * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se)
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "cg-base.h"
+
+#include <libclut.h>
+
+#include <alloca.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+
+/**
+ * The process's name
+ */
+const char* argv0 = NULL;
+
+/**
+ * The libcoopgamma context
+ */
+libcoopgamma_context_t cg;
+
+/**
+ * The names of the selected CRTC:s
+ */
+char** crtcs = NULL;
+
+/**
+ * Gamma ramp updates for each CRTC
+ */
+filter_update_t* crtc_updates = NULL;
+
+/**
+ * CRTC and monitor information about
+ * each selected CRTC and connect monitor
+ */
+libcoopgamma_crtc_info_t* crtc_info = NULL;
+
+/**
+ * The number of selected CRTC:s
+ */
+size_t crtcs_n = 0;
+
+/**
+ * The number of filters
+ */
+size_t filters_n = 0;
+
+
+/**
+ * Contexts for asynchronous ramp updates
+ */
+static libcoopgamma_async_context_t* asyncs = NULL;
+
+/**
+ * The number of pending receives
+ */
+static size_t pending_recvs = 0;
+
+/**
+ * Whether message must be flushed
+ */
+static int flush_pending = 0;
+
+
+
+/**
+ * Data used to sort CRTC:s
+ */
+struct crtc_sort_data
+{
+ /**
+ * The gamma ramp type
+ */
+ libcoopgamma_depth_t depth;
+
+ /**
+ * Should be 0
+ */
+ int __padding;
+
+ /**
+ * The size of the red gamma ramp
+ */
+ size_t red_size;
+
+ /**
+ * The size of the green gamma ramp
+ */
+ size_t green_size;
+
+ /**
+ * The size of the blue gamma ramp
+ */
+ size_t blue_size;
+
+ /**
+ * The index of the CRTC
+ */
+ size_t index;
+};
+
+
+
+/**
+ * Compare two strings
+ *
+ * @param a Return -1 if this string is `NULL` or less than `b`
+ * @param b Return +1 if this string is less than `a`
+ * @return See `a` and `b`, 0 is returned if `a` and `b` are equal
+ */
+static int nulstrcmp(const char *a, const char *b)
+{
+ return (a == NULL) ? -1 : strcmp(a, b);
+}
+
+
+/**
+ * Compare two instances of `crtc_sort_data`
+ *
+ * @param a_ Return -1 if this one is lower
+ * @param b_ Return +1 if this one is higher
+ * @return See `a_` and `b_`, only -1 or +1 can be returned
+ */
+static int crtc_sort_data_cmp(const void* a_, const void* b_)
+{
+ const struct crtc_sort_data* a = a_;
+ const struct crtc_sort_data* b = b_;
+ int cmp = memcmp(a, b, sizeof(*a) - sizeof(a->index));
+ return cmp ? cmp : a->index < b->index ? -1 : +1;
+}
+
+
+/**
+ * Make elements in `crtc_updates` slaves where appropriate
+ *
+ * @return Zero on success, -1 on error
+ */
+int make_slaves(void)
+{
+ struct crtc_sort_data* data;
+ size_t i, j, n = 0, master = 0, master_i;
+
+ data = alloca(filters_n * sizeof(*data));
+ memset(data, 0, filters_n * sizeof(*data));
+ for (i = 0; i < filters_n; i++)
+ {
+ if (!(crtc_info[crtc_updates[i].crtc].supported))
+ continue;
+
+ data[n].depth = crtc_updates[i].filter.depth;
+ data[n].red_size = crtc_updates[i].filter.ramps.u8.red_size;
+ data[n].green_size = crtc_updates[i].filter.ramps.u8.green_size;
+ data[n].blue_size = crtc_updates[i].filter.ramps.u8.blue_size;
+ data[n].index = i;
+ n++;
+ }
+
+ qsort(data, n, sizeof(*data), crtc_sort_data_cmp);
+ if (n == 0)
+ return 0;
+
+ master_i = data[0].index;
+ for (i = 1; i < n; i++)
+ if (memcmp(data + i, data + master, sizeof(*data) - sizeof(data->index)))
+ {
+ if (master + 1 < i)
+ {
+ crtc_updates[master_i].slaves = calloc(i - master, sizeof(size_t));
+ if (crtc_updates[master_i].slaves == NULL)
+ return -1;
+ for (j = 1; master + j < i; j++)
+ crtc_updates[master_i].slaves[j - 1] = data[master + j].index;
+ }
+ master = i;
+ master_i = data[master].index;
+ }
+ else
+ {
+ libcoopgamma_ramps_destroy(&(crtc_updates[data[i].index].filter.ramps.u8));
+ crtc_updates[data[i].index].master = 0;
+ crtc_updates[data[i].index].filter.ramps.u8 = crtc_updates[master_i].filter.ramps.u8;
+ }
+
+ if (master + 1 < i)
+ {
+ crtc_updates[master_i].slaves = calloc(i - master, sizeof(size_t));
+ if (crtc_updates[master_i].slaves == NULL)
+ return -1;
+ for (j = 1; master + j < i; j++)
+ crtc_updates[master_i].slaves[j - 1] = data[master + j].index;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Update a filter and synchronise calls
+ *
+ * @param index The index of the CRTC
+ * @param timeout The number of milliseconds a call to `poll` may block,
+ * -1 if it may block forever
+ * @return 1: Success, no pending synchronisations
+ * 0: Success, with still pending synchronisations
+ * -1: Error, `errno` set
+ * -2: Error, `cg.error` set
+ *
+ * @throws EINTR Call to `poll` was interrupted by a signal
+ * @throws EAGAIN Call to `poll` timed out
+ */
+int update_filter(size_t index, int timeout)
+{
+ filter_update_t* filter = crtc_updates + index;
+
+ if (!(filter->synced) || filter->failed)
+ abort();
+
+ pending_recvs += 1;
+
+ if (libcoopgamma_set_gamma_send(&(filter->filter), &cg, asyncs + index) < 0)
+ switch (errno)
+ {
+ case EINTR:
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ flush_pending = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ filter->synced = 0;
+ return synchronise(timeout);
+}
+
+
+/**
+ * Synchronised calls
+ *
+ * @param timeout The number of milliseconds a call to `poll` may block,
+ * -1 if it may block forever
+ * @return 1: Success, no pending synchronisations
+ * 0: Success, with still pending synchronisations
+ * -1: Error, `errno` set
+ * -2: Error, `cg.error` set
+ *
+ * @throws EINTR Call to `poll` was interrupted by a signal
+ * @throws EAGAIN Call to `poll` timed out
+ */
+int synchronise(int timeout)
+{
+ struct pollfd pollfd;
+ size_t selected;
+
+ pollfd.fd = cg.fd;
+ pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
+ if (flush_pending > 0)
+ pollfd.events |= POLLOUT;
+
+ pollfd.revents = 0;
+ if (poll(&pollfd, (nfds_t)1, timeout) < 0)
+ return -1;
+
+ if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL))
+ {
+ if (libcoopgamma_flush(&cg) < 0)
+ goto sync;
+ flush_pending = 0;
+ }
+
+ if ((timeout < 0) && (pending_recvs > 0))
+ if (!(pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)))
+ {
+ pollfd.revents = 0;
+ if (poll(&pollfd, (nfds_t)1, -1) < 0)
+ return -1;
+ }
+
+ sync:
+ if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI | POLLERR | POLLHUP | POLLNVAL))
+ for (;;)
+ {
+ if (libcoopgamma_synchronise(&cg, asyncs, filters_n, &selected) < 0)
+ {
+ if (errno == 0)
+ continue;
+ else
+ goto fail;
+ }
+ if (crtc_updates[selected].synced)
+ continue;
+ crtc_updates[selected].synced = 1;
+ pending_recvs -= 1;
+ if (libcoopgamma_set_gamma_recv(&cg, asyncs + selected) < 0)
+ {
+ if (cg.error.server_side)
+ {
+ crtc_updates[selected].error = cg.error;
+ crtc_updates[selected].failed = 1;
+ memset(&(cg.error), 0, sizeof(cg.error));
+ }
+ else
+ goto cg_fail;
+ }
+ }
+
+ return pending_recvs == 0;
+ cg_fail:
+ return -2;
+ fail:
+ switch (errno)
+ {
+ case EINTR:
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ return pending_recvs == 0;
+ default:
+ return -1;
+ }
+}
+
+
+/**
+ * Initialise the process, specifically
+ * reset the signal mask and signal handlers
+ *
+ * @return Zero on success, -1 on error
+ */
+static int initialise_proc(void)
+{
+ sigset_t sigmask;
+ int sig;
+
+ for (sig = 1; sig < _NSIG; sig++)
+ if (signal(sig, SIG_DFL) == SIG_ERR)
+ if (sig == SIGCHLD)
+ return -1;
+
+ if (sigemptyset(&sigmask) < 0)
+ return -1;
+ if (sigprocmask(SIG_SETMASK, &sigmask, NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * Print, to stdout, a list of all
+ * recognised adjustment methods
+ *
+ * @return Zero on success, -1 on error
+ */
+static int list_methods(void)
+{
+ char** list;
+ size_t i;
+
+ list = libcoopgamma_get_methods();
+ if (list == NULL)
+ return -1;
+ for (i = 0; list[i]; i++)
+ printf("%s\n", list[i]);
+ free(list);
+ if (fflush(stdout) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * Print, to stdout, a list of all CRTC:s
+ *
+ * A connection to the coopgamma server
+ * must have been made
+ *
+ * @return Zero on success, -1 on error, -2
+ * on libcoopgamma error
+ */
+static int list_crtcs(void)
+{
+ char** list;
+ size_t i;
+
+ list = libcoopgamma_get_crtcs_sync(&cg);
+ if (list == NULL)
+ return -2;
+ for (i = 0; list[i]; i++)
+ printf("%s\n", list[i]);
+ free(list);
+ if (fflush(stdout) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * Fill the list of CRTC information
+ *
+ * @return Zero on success, -1 on error, -2
+ * on libcoopgamma error
+ */
+static int get_crtc_info(void)
+{
+ size_t i, unsynced = 0, selected;
+ char* synced;
+ int need_flush = 0;
+ struct pollfd pollfd;
+
+ synced = alloca(crtcs_n * sizeof(*synced));
+ memset(synced, 0, crtcs_n * sizeof(*synced));
+
+ i = 0;
+ pollfd.fd = cg.fd;
+ pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
+
+ while ((unsynced > 0) || (i < crtcs_n))
+ {
+ wait:
+ if (i < crtcs_n)
+ pollfd.events |= POLLOUT;
+ else
+ pollfd.events &= ~POLLOUT;
+
+ pollfd.revents = 0;
+ if (poll(&pollfd, (nfds_t)1, -1) < 0)
+ goto fail;
+
+ if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL))
+ {
+ if (need_flush && (libcoopgamma_flush(&cg) < 0))
+ goto send_fail;
+ need_flush = 0;
+ for (; i < crtcs_n; i++)
+ if (unsynced++, libcoopgamma_get_gamma_info_send(crtcs[i], &cg, asyncs + i) < 0)
+ goto send_fail;
+ goto send_done;
+ send_fail:
+ switch (errno)
+ {
+ case EINTR:
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ i++;
+ need_flush = 1;
+ break;
+ default:
+ goto fail;
+ }
+ }
+ send_done:
+
+ if ((unsynced == 0) && (i == crtcs_n))
+ break;
+
+ if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI))
+ while (unsynced > 0)
+ switch (libcoopgamma_synchronise(&cg, asyncs, i, &selected))
+ {
+ case 0:
+ if (synced[selected])
+ {
+ libcoopgamma_skip_message(&cg);
+ break;
+ }
+ synced[selected] = 1;
+ unsynced -= 1;
+ if (libcoopgamma_get_gamma_info_recv(crtc_info + selected, &cg, asyncs + selected) < 0)
+ goto cg_fail;
+ break;
+ case -1:
+ switch (errno)
+ {
+ case 0:
+ break;
+ case EINTR:
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ goto wait;
+ default:
+ goto fail;
+ }
+ break;
+ }
+ }
+
+ return 0;
+ fail:
+ return -1;
+ cg_fail:
+ return -2;
+}
+
+
+/**
+ * -M METHOD
+ * Select adjustment method. If METHOD is "?",
+ * available methods will be printed to stdout.
+ *
+ * -S SITE
+ * Select site (display server instance).
+ *
+ * -c CRTC
+ * Select CRT controller. If CRTC is "?", CRTC:s
+ * will be printed to stdout.
+ *
+ * This option can be used multiple times. If it
+ * is not used at all, all CRTC:s will be selected.
+ *
+ * -p PRIORITY
+ * Select the priority for the filter, this should
+ * be a signed two's-complement integer. If
+ * PRIORITY is "?", the default priority for the
+ * program is printed to stdout.
+ *
+ * -R RULE
+ * The rule of the filter, that is, the last part
+ * of the class which is its identifier. If RULE
+ * is "?" the default rule is printed to stdout,
+ * if RULE is "??" the default class is printed
+ * to stdout.
+ *
+ * @param argc The number of command line arguments
+ * @param argv The command line arguments
+ * @return 0 on success, 1 on error
+ */
+int main(int argc, char* argv[])
+{
+ int stage = 0;
+ int dealloc_crtcs = 0;
+ int rc = 0;
+ char* method = NULL;
+ char* site = NULL;
+ size_t crtc_i = 0;
+ int64_t priority = default_priority;
+ char* prio = NULL;
+ char* rule = NULL;
+ char* class = default_class;
+ char** classes = NULL;
+ size_t classes_n = 0;
+ int explicit_crtcs = 0;
+ int have_crtc_q = 0;
+ size_t i, filter_i;
+
+ argv0 = *argv++, argc--;
+
+ if (initialise_proc() < 0)
+ goto fail;
+
+ crtcs = alloca(argc * sizeof(*crtcs));
+
+ for (; *argv; argv++, argc--)
+ {
+ char* args = *argv;
+ char opt[3];
+ if (!strcmp(args, "--"))
+ {
+ argv++, argc--;
+ break;
+ }
+ opt[0] = *args++;
+ opt[2] = '\0';
+ if ((*opt != '-') && (*opt != '+'))
+ break;
+ while (*args)
+ {
+ char* arg;
+ int at_end;
+ opt[1] = *args++;
+ arg = args;
+ if ((at_end = !*arg))
+ arg = argv[1];
+ if (!strcmp(opt, "-M"))
+ {
+ if ((method != NULL) || ((method = arg) == NULL))
+ usage();
+ }
+ else if (!strcmp(opt, "-S"))
+ {
+ if ((site != NULL) || ((site = arg) == NULL))
+ usage();
+ }
+ else if (!strcmp(opt, "-c"))
+ {
+ if (arg == NULL)
+ usage();
+ crtcs[crtc_i++] = arg;
+ explicit_crtcs = 1;
+ if (!have_crtc_q && !strcmp(arg, "?"))
+ have_crtc_q = 1;
+ }
+ else if (!strcmp(opt, "-p"))
+ {
+ if ((prio != NULL) || ((prio = arg) == NULL))
+ usage();
+ }
+ else if (!strcmp(opt, "-R"))
+ {
+ if ((rule != NULL) || ((rule = arg) == NULL))
+ usage();
+ }
+ else
+ switch (handle_opt(opt, arg))
+ {
+ case 0:
+ goto next_opt;
+ case 1:
+ break;
+ default:
+ goto fail;
+ }
+ argv += at_end;
+ argc -= at_end;
+ break;
+ next_opt:;
+ }
+ }
+
+ crtcs_n = crtc_i;
+ crtcs[crtc_i] = NULL;
+ if (!have_crtc_q && nulstrcmp(method, "?") &&
+ nulstrcmp(rule, "?") && nulstrcmp(rule, "??") &&
+ ((default_priority == NO_DEFAULT_PRIORITY) || nulstrcmp(prio, "?")))
+ if (handle_args(argc, argv, prio) < 0)
+ goto fail;
+
+ if (default_priority != NO_DEFAULT_PRIORITY)
+ {
+ if (!nulstrcmp(prio, "?"))
+ {
+ printf("%" PRIi64 "\n", priority);
+ return 0;
+ }
+ else if (prio != NULL)
+ {
+ char *end;
+ errno = 0;
+ priority = (int64_t)strtoll(prio, &end, 10);
+ if (errno || *end || !*prio)
+ usage();
+ }
+ }
+
+ if (!nulstrcmp(rule, "??"))
+ {
+ size_t i;
+ if (*class_suffixes == NULL)
+ printf("%s\n", class);
+ else
+ for (i = 0; class_suffixes[i] != NULL; i++)
+ printf("%s%s\n", class, class_suffixes[i]);
+ return 0;
+ }
+ else if (!nulstrcmp(rule, "?"))
+ {
+ printf("%s\n", strstr(strstr(class, "::") + 2, "::") + 2);
+ return 0;
+ }
+ else if (rule != NULL)
+ {
+ char* p = strstr(strstr(class, "::") + 2, "::") + 2;
+ size_t n = (size_t)(p - class);
+ class = alloca(strlen(rule) + n + (size_t)1);
+ memcpy(class, default_class, n);
+ strcpy(class + n, rule);
+ if (strchr(class, '\n'))
+ {
+ fprintf(stderr, "%s: LF character is not allowed in the filter's class\n", argv0);
+ goto custom_fail;
+ }
+ }
+
+ if (!nulstrcmp(method, "?"))
+ {
+ if (list_methods() < 0)
+ goto fail;
+ return 0;
+ }
+
+ if (libcoopgamma_context_initialise(&cg) < 0)
+ goto fail;
+ stage++;
+ if (libcoopgamma_connect(method, site, &cg) < 0)
+ {
+ fprintf(stderr, "%s: server failed to initialise\n", argv0);
+ goto custom_fail;
+ }
+ stage++;
+
+ if (have_crtc_q)
+ switch (list_crtcs())
+ {
+ case 0:
+ goto done;
+ case -1:
+ goto fail;
+ default:
+ goto cg_fail;
+ }
+
+ if (crtcs_n == 0)
+ {
+ crtcs = libcoopgamma_get_crtcs_sync(&cg);
+ if (crtcs == NULL)
+ goto cg_fail;
+ dealloc_crtcs = 1;
+ for (; crtcs[crtcs_n] != NULL; crtcs_n++);
+ }
+
+ if (crtcs_n == 0)
+ {
+ fprintf(stderr, "%s: no CRTC:s are available\n", argv0);
+ goto custom_fail;
+ }
+
+ if (*class_suffixes == NULL)
+ {
+ classes = &class;
+ classes_n = 1;
+ }
+ else
+ {
+ size_t len = strlen(class);
+ while (class_suffixes[classes_n])
+ classes_n++;
+ classes = alloca(classes_n * sizeof(*classes));
+ for (i = 0; i < classes_n; i++)
+ {
+ classes[i] = alloca(len + strlen(class_suffixes[i]) + sizeof(":"));
+ stpcpy(stpcpy(stpcpy(classes[i], class), ":"), class_suffixes[i]);
+ }
+ }
+ filters_n = classes_n * crtcs_n;
+
+ crtc_info = alloca(crtcs_n * sizeof(*crtc_info));
+ memset(crtc_info, 0, crtcs_n * sizeof(*crtc_info));
+ for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++)
+ if (libcoopgamma_crtc_info_initialise(crtc_info + crtc_i) < 0)
+ goto cg_fail;
+
+ if (libcoopgamma_set_nonblocking(&cg, 1) < 0)
+ goto fail;
+
+ asyncs = alloca(filters_n * sizeof(*asyncs));
+ memset(asyncs, 0, filters_n * sizeof(*asyncs));
+ for (filter_i = 0; filter_i < filters_n; filter_i++)
+ if (libcoopgamma_async_context_initialise(asyncs + filter_i) < 0)
+ goto fail;
+
+ switch (get_crtc_info())
+ {
+ case 0:
+ break;
+ case -1:
+ goto fail;
+ case -2:
+ goto cg_fail;
+ }
+
+ for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++)
+ {
+ if (explicit_crtcs && !(crtc_info[crtc_i].supported))
+ fprintf(stderr, "%s: warning: gamma adjustments not supported on CRTC: %s\n",
+ argv0, crtcs[crtc_i]);
+ if (crtc_info[crtc_i].cooperative == 0)
+ fprintf(stderr, "%s: warning: cooperative gamma server not running for CRTC: %s\n",
+ argv0, crtcs[crtc_i]);
+ }
+
+ crtc_updates = alloca(filters_n * sizeof(*crtc_updates));
+ memset(crtc_updates, 0, filters_n * sizeof(*crtc_updates));
+ for (filter_i = i = 0; i < classes_n; i++)
+ for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++, filter_i++)
+ {
+ if (libcoopgamma_filter_initialise(&(crtc_updates[filter_i].filter)) < 0)
+ goto fail;
+ if (libcoopgamma_error_initialise(&(crtc_updates[filter_i].error)) < 0)
+ goto fail;
+ crtc_updates[filter_i].crtc = crtc_i;
+ crtc_updates[filter_i].synced = 1;
+ crtc_updates[filter_i].failed = 0;
+ crtc_updates[filter_i].master = 1;
+ crtc_updates[filter_i].slaves = NULL;
+ crtc_updates[filter_i].filter.crtc = crtcs[crtc_i];
+ crtc_updates[filter_i].filter.class = classes[i];
+ crtc_updates[filter_i].filter.priority = priority;
+ crtc_updates[filter_i].filter.depth = crtc_info[crtc_i].depth;
+ crtc_updates[filter_i].filter.ramps.u8.red_size = crtc_info[crtc_i].red_size;
+ crtc_updates[filter_i].filter.ramps.u8.green_size = crtc_info[crtc_i].green_size;
+ crtc_updates[filter_i].filter.ramps.u8.blue_size = crtc_info[crtc_i].blue_size;
+ switch (crtc_updates[filter_i].filter.depth)
+ {
+#define X(CONST, MEMBER, MAX, TYPE)\
+ case CONST:\
+ libcoopgamma_ramps_initialise(&(crtc_updates[filter_i].filter.ramps.MEMBER));\
+ libclut_start_over(&(crtc_updates[filter_i].filter.ramps.MEMBER), MAX, TYPE, 1, 1, 1);\
+ break;
+LIST_DEPTHS
+#undef X
+ default:
+ fprintf(stderr, "%s: internal error: gamma ramp type is unrecognised: %i\n",
+ argv0, crtc_updates[filter_i].filter.depth);
+ goto custom_fail;
+ }
+ }
+
+ switch (start())
+ {
+ case 0:
+ break;
+ case -1:
+ goto fail;
+ case -2:
+ goto cg_fail;
+ case -3:
+ goto custom_fail;
+ }
+
+ for (filter_i = 0; filter_i < filters_n; filter_i++)
+ if (crtc_updates[filter_i].failed)
+ {
+ const char* side = cg.error.server_side ? "server" : "client";
+ const char* crtc = crtc_updates[filter_i].filter.crtc;
+ if (cg.error.custom)
+ {
+ if ((cg.error.number != 0) && (cg.error.description != NULL))
+ fprintf(stderr, "%s: %s-side error number %" PRIu64 " for CRTC %s: %s\n",
+ argv0, side, cg.error.number, crtc, cg.error.description);
+ else if (cg.error.number != 0)
+ fprintf(stderr, "%s: %s-side error number %" PRIu64 " for CRTC %s\n",
+ argv0, side, cg.error.number, crtc);
+ else if (cg.error.description != NULL)
+ fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, cg.error.description);
+ }
+ else if (cg.error.description != NULL)
+ fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, cg.error.description);
+ else
+ fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, strerror(cg.error.number));
+ }
+
+ done:
+ if (dealloc_crtcs)
+ free(crtcs);
+ if (crtc_info != NULL)
+ for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++)
+ libcoopgamma_crtc_info_destroy(crtc_info + crtc_i);
+ if (asyncs != NULL)
+ for (filter_i = 0; filter_i < filters_n; filter_i++)
+ libcoopgamma_async_context_destroy(asyncs + filter_i);
+ if (stage >= 1)
+ libcoopgamma_context_destroy(&cg, stage >= 2);
+ if (crtc_updates != NULL)
+ for (filter_i = 0; filter_i < filters_n; filter_i++)
+ {
+ if (crtc_updates[filter_i].master == 0)
+ memset(&(crtc_updates[filter_i].filter.ramps.u8), 0, sizeof(crtc_updates[filter_i].filter.ramps.u8));
+ crtc_updates[filter_i].filter.crtc = NULL;
+ crtc_updates[filter_i].filter.class = NULL;
+ libcoopgamma_filter_destroy(&(crtc_updates[filter_i].filter));
+ libcoopgamma_error_destroy(&(crtc_updates[filter_i].error));
+ free(crtc_updates[filter_i].slaves);
+ }
+ return rc;
+
+ custom_fail:
+ rc = 1;
+ goto done;
+
+ fail:
+ rc = 1;
+ if (errno)
+ perror(argv0);
+ goto done;
+
+ cg_fail:
+ rc = 1;
+ {
+ const char* side = cg.error.server_side ? "server" : "client";
+ if (cg.error.custom)
+ {
+ if ((cg.error.number != 0) && (cg.error.description != NULL))
+ fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n",
+ argv0, side, cg.error.number, cg.error.description);
+ else if (cg.error.number != 0)
+ fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number);
+ else if (cg.error.description != NULL)
+ fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description);
+ }
+ else if (cg.error.description != NULL)
+ fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description);
+ else
+ fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror(cg.error.number));
+ }
+ goto done;
+}
+