/* See LICENSE file for copyright and license details. */
#include "cg-base.h"
#include <libclut.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Note, that EDID:s are 256 hexadecimals long, and
* a filename can only be 255 characters long. */
/**
* Magic number for dual-byte precision lookup table based profiles
*/
#define MLUT_TAG 0x6D4C5554L
/**
* Magic number for gamma–brightness–contrast based profiles
* and for variable precision lookup table profiles
*/
#define VCGT_TAG 0x76636774L
/**
* The filename of the configuration file
*/
#define ICCTAB "icctab"
/**
* The default filter priority for the program
*/
const int64_t default_priority = 0;
/**
* The default class for the program
*/
char default_class[] = PKGNAME "::cg-icc::standard";
/**
* Class suffixes
*/
const char *const *class_suffixes = (const char *const[]){NULL};
/**
* -d: keep process alive and remove filter on death
*/
static int dflag = 0;
/**
* -x: remove filter rather than adding it
*/
static int xflag = 0;
/**
* The panhame of the selected ICC profile
*/
static const char *icc_pathname = NULL;
/**
* Gamma ramps loaded from `icc_pathname`
*/
static libcoopgamma_ramps_t uniramps;
/**
* The datatype of the stops in the ramps of `uniramps`
*/
static libcoopgamma_depth_t unidepth = 0;
/**
* Parsed ICC profiles for each CRTC
*/
static libcoopgamma_ramps_t *rampses = NULL;
/**
* The datatype of the stops in the ramps of
* corresponding element in `rampses`
*/
static libcoopgamma_depth_t *depths = NULL;
/**
* File descriptor for configuration directory
*/
static int confdirfd = -1;
/**
* List of CRTC:s
*/
static char **crtc_icc_keys = NULL;
/**
* List of ICC profile pathnames for corresponding
* CRTC in `crtc_icc_keys`
*/
static char **crtc_icc_values = NULL;
/**
* Print usage information and exit
*/
void
usage(void)
{
fprintf(stderr,
"usage: %s [-M method] [-S site] [-c crtc]... [-R rule] "
"(-x | [-p priority] [-d] [file])\n",
argv0);
exit(1);
}
/**
* Perform cleanup so valgrind output is clean
*
* @param ret The value to return
* @return `ret` is returned as is
*/
static int
cleanup(int ret)
{
int saved_errno = errno;
size_t i;
libcoopgamma_ramps_destroy(&uniramps);
if (confdirfd >= 0)
close(confdirfd);
if (rampses)
for (i = 0; i < crtcs_n; i++)
libcoopgamma_ramps_destroy(rampses + i);
free(rampses);
free(depths);
if (crtc_icc_keys)
for (i = 0; crtc_icc_keys[i]; i++)
free(crtc_icc_keys[i]);
free(crtc_icc_keys);
if (crtc_icc_values)
for (i = 0; crtc_icc_values[i]; i++)
free(crtc_icc_values[i]);
free(crtc_icc_values);
errno = saved_errno;
return ret;
}
/**
* Handle a command line option
*
* @param opt The option, it is a NUL-terminate two-character
* string starting with either '-' or '+', if the
* argument is not recognised, call `usage`. This
* string will not be "-M", "-S", "-c", "-p", or "-R".
* @param arg The argument associated with `opt`,
* `NULL` there is no next argument, if this
* parameter is `NULL` but needed, call `usage`
* @return 0 if `arg` was not used,
* 1 if `arg` was used,
* -1 on error
*/
int
handle_opt(char *opt, char *arg)
{
if (opt[0] == '-') {
switch (opt[1]) {
case 'd':
if (dflag || xflag)
usage();
dflag = 1;
break;
case 'x':
if (xflag || dflag)
usage();
xflag = 1;
break;
default:
usage();
}
}
return 0;
(void) arg;
}
/**
* Populate `crtc_icc_keys` and `crtc_icc_value`
*
* @path fd File descriptor for the ICC profile table
* @path dirname The dirname of the ICC profile table
* @return Zero on success, -1 on error
*/
static int
load_icc_table(int fd, const char *dirname)
{
FILE *fp;
ssize_t len;
size_t lineno = 1, size = 0;
char *p, *q, *line = NULL;
int saved_errno;
size_t ptr = 0, siz = 0;
void *new;
size_t dirname_len = strlen(dirname);
fp = fdopen(fd, "rb");
if (!fp)
return -1;
for (; (len = getline(&line, &size, fp)) >= 0; lineno++) {
if (len && line[len - 1] == '\n')
line[--len] = '\0';
p = line + strspn(line, " \t");
if (!*p || *p == '#')
continue;
q = p + strspn(p, "0123456789abcdefABCDEF");
if (*q != ' ' && *q != '\t') {
fprintf(stderr, "%s: warning: line %zu is malformated in %s/%s\n",
argv0, lineno, dirname, ICCTAB);
continue;
}
*q = '\0';
if ((size_t)(q - p) != 256) {
fprintf(stderr, "%s: warning: EDID on line %zu in %s/%s looks to be of wrong length: %s\n",
argv0, lineno, dirname, ICCTAB, p);
}
q++;
q += strspn(p, " \t");
if (!*q) {
fprintf(stderr, "%s: warning: line %zu is malformated in %s/%s\n",
argv0, lineno, dirname, ICCTAB);
continue;
}
if (strchr(" \t", strchr(q, '\0')[-1])) {
fprintf(stderr, "%s: warning: filename on line %zu in %s/%s ends with white space: %s\n",
argv0, lineno, dirname, ICCTAB, q);
}
if (ptr == siz) {
new = realloc(crtc_icc_keys, (siz + 5) * sizeof(*crtc_icc_keys));
if (!new)
goto fail;
crtc_icc_keys = new;
new = realloc(crtc_icc_values, (siz + 5) * sizeof(*crtc_icc_values));
if (!new)
goto fail;
crtc_icc_values = new;
siz += 4;
}
crtc_icc_values[ptr] = malloc((*q == '/' ? 1 : dirname_len + sizeof("/")) + strlen(q));
if (!crtc_icc_values[ptr])
goto fail;
if (*q == '/')
strcpy(crtc_icc_values[ptr], q);
else
stpcpy(stpcpy(stpcpy(crtc_icc_values[ptr], dirname), "/"), q);
crtc_icc_keys[ptr] = malloc(strlen(p) + 1);
if (!crtc_icc_keys[ptr]) {
ptr++;
goto fail;
}
strcpy(crtc_icc_keys[ptr], p);
ptr++;
}
if (ferror(fp))
goto fail;
if (crtc_icc_keys)
crtc_icc_keys[ptr] = NULL;
if (crtc_icc_values)
crtc_icc_values[ptr] = NULL;
fclose(fp);
free(line);
return 0;
fail:
saved_errno = errno;
if (crtc_icc_keys)
crtc_icc_keys[ptr] = NULL;
if (crtc_icc_values)
crtc_icc_values[ptr] = NULL;
fclose(fp);
free(line);
errno = saved_errno;
return -1;
}
/**
* This function is called after the last
* call to `handle_opt`
*
* @param argc The number of unparsed arguments
* @param argv `NULL` terminated list of unparsed arguments
* @param prio The argument associated with the "-p" option
* @return Zero on success, -1 on error
*/
int
handle_args(int argc, char *argv[], char *prio)
{
struct passwd *pw;
char *path = NULL;
int saved_errno;
int fd = -1, q = xflag + dflag;
if ((q > 1) || (xflag && (argc > 0 || prio)) || argc > 1)
usage();
icc_pathname = *argv;
memset(&uniramps, 0, sizeof(uniramps));
if (!xflag && !icc_pathname) {
pw = getpwuid(getuid());
if (!pw || !pw->pw_dir)
goto fail;
path = malloc(strlen(pw->pw_dir) + sizeof("/.config"));
if (!path)
goto fail;
sprintf(path, "%s/.config", pw->pw_dir);
if (access(path, F_OK) < 0)
sprintf(path, "/etc");
confdirfd = open(path, O_DIRECTORY);
if (confdirfd < 0)
goto fail;
fd = openat(confdirfd, ICCTAB, O_RDONLY);
if (fd < 0)
goto fail;
if (load_icc_table(fd, path) < 0)
goto fail;
free(path);
path = NULL;
close(fd), fd = -1;
}
return 0;
fail:
saved_errno = errno;
free(path);
path = NULL;
if (fd >= 0)
close(fd);
errno = saved_errno;
return cleanup(-1);
}
/**
* Read an unsigned 64-bit integer
*
* @param content The beginning of the encoded integer
* @return The integer, decoded
*/
static uint64_t
icc_uint64(const char *restrict content)
{
uint64_t rc;
rc = (uint64_t)(unsigned char)(content[0]), rc = (uint64_t)(rc << 8);
rc = (uint64_t)(rc | (uint64_t)(unsigned char)(content[1])), rc = (uint64_t)(rc << 8);
rc = (uint64_t)(rc | (uint64_t)(unsigned char)(content[2])), rc = (uint64_t)(rc << 8);
rc = (uint64_t)(rc | (uint64_t)(unsigned char)(content[3])), rc = (uint64_t)(rc << 8);
rc = (uint64_t)(rc | (uint64_t)(unsigned char)(content[4])), rc = (uint64_t)(rc << 8);
rc = (uint64_t)(rc | (uint64_t)(unsigned char)(content[5])), rc = (uint64_t)(rc << 8);
rc = (uint64_t)(rc | (uint64_t)(unsigned char)(content[6])), rc = (uint64_t)(rc << 8);
rc = (uint64_t)(rc | (uint64_t)(unsigned char)(content[7]));
return rc;
}
/**
* Read an unsigned 32-bit integer
*
* @param content The beginning of the encoded integer
* @return The integer, decoded
*/
static uint32_t
icc_uint32(const char *restrict content)
{
uint32_t rc;
rc = (uint32_t)(unsigned char)(content[0]), rc = (uint32_t)(rc << 8);
rc = (uint32_t)(rc | (uint32_t)(unsigned char)(content[1])), rc = (uint32_t)(rc << 8);
rc = (uint32_t)(rc | (uint32_t)(unsigned char)(content[2])), rc = (uint32_t)(rc << 8);
rc = (uint32_t)(rc | (uint32_t)(unsigned char)(content[3]));
return rc;
}
/**
* Read an unsigned 16-bit integer
*
* @param content The beginning of the encoded integer
* @return The integer, decoded
*/
static uint16_t
icc_uint16(const char *restrict content)
{
uint16_t rc;
rc = (uint16_t)(unsigned char)(content[0]), rc = (uint16_t)(rc << 8);
rc = (uint16_t)(rc | (uint16_t)(unsigned char)(content[1]));
return rc;
}
/**
* Read an unsigned 8-bit integer
*
* @param content The beginning of the encoded integer
* @return The integer, decoded
*/
static uint8_t
icc_uint8(const char *restrict content)
{
return (uint8_t)(content[0]);
}
/**
* Read a floating-point value
*
* @param content The beginning of the encoded value
* @param width The number of bytes with which the value is encoded
* @return The value, decoded
*/
static double icc_double(const char *restrict content, size_t width)
{
double ret = 0;
size_t i;
for (i = 0; i < width; i++) {
ret /= 256;
ret += (double)(unsigned char)(content[width - 1 - i]);
}
ret /= 255;
return ret;
}
/**
* Parse an ICC profile
*
* @param content The content of the ICC profile file
* @param n The byte-size of `content`
* @param ramps Output parameter for the filter stored in the ICC profile,
* `.red_size`, `.green_size`, `.blue_size` should already be
* set (these values can however be modified.)
* @param depth Output parameter for ramps stop value type
* @return Zero on success, -1 on error, -2 if no usable data is
* available in the profile.
*/
static int parse_icc(const char *restrict content, size_t n, libcoopgamma_ramps_t *ramps, libcoopgamma_depth_t *depth)
{
uint32_t tag_name, tag_offset, tag_size, gamma_type;
size_t n_channels, n_entries, entry_size;
double r_gamma, r_min, r_max, g_gamma, g_min, g_max, b_gamma, b_min, b_max;
uint32_t i_tag, n_tags;
size_t i, ptr = 0, xptr;
/* Skip header */
if (n - ptr < 128)
return -2;
ptr += 128;
/* Get the number of tags */
if (n - ptr < 4)
return -2;
n_tags = icc_uint32(content + ptr), ptr += 4;
for (i_tag = 0, xptr = ptr; i_tag < n_tags; i_tag++, ptr = xptr) {
/* Get profile encoding type, offset to the profile and the encoding size of its data */
if (n - ptr < 12)
return -2;
tag_name = icc_uint32(content + ptr), ptr += 4;
tag_offset = icc_uint32(content + ptr), ptr += 4;
tag_size = icc_uint32(content + ptr), ptr += 4;
xptr = ptr;
/* Jump to the profile data */
if (tag_offset > INT32_MAX - tag_size)
return -2;
if (tag_offset + tag_size > n)
return -2;
ptr = tag_offset;
if (tag_name == MLUT_TAG) {
/* The profile is encododed as an dual-byte precision lookup table */
/* Initialise ramps */
*depth = LIBCOOPGAMMA_UINT16;
ramps->u16.red_size = 256;
ramps->u16.green_size = 256;
ramps->u16.blue_size = 256;
if (libcoopgamma_ramps_initialise(&ramps->u16) < 0)
return -1;
/* Get the lookup table */
if (n - ptr < 3 * 256 * 2)
continue;
for (i = 0; i < 256; i++, ptr += 2)
ramps->u16.red[i] = icc_uint16(content + ptr);
for (i = 0; i < 256; i++, ptr += 2)
ramps->u16.green[i] = icc_uint16(content + ptr);
for (i = 0; i < 256; i++, ptr += 2)
ramps->u16.blue[i] = icc_uint16(content + ptr);
return 0;
} else if (tag_name == VCGT_TAG) {
/* The profile is encoded as with gamma, brightness and contrast values
* or as a variable precision lookup table profile */
/* VCGT profiles starts where their magic number */
if (n - ptr < 4)
continue;
tag_name = icc_uint32(content + ptr), ptr += 4;
if (tag_name != VCGT_TAG)
continue;
/* Skip four bytes */
if (n - ptr < 4)
continue;
ptr += 4;
/* Get the actual encoding type */
if (n - ptr < 4)
continue;
gamma_type = icc_uint32(content + ptr), ptr += 4;
if (!gamma_type) {
/* The profile is encoded as a variable precision lookup table */
/* Get metadata */
if (n - ptr < 3 * 4)
continue;
n_channels = (size_t)icc_uint16(content + ptr), ptr += 2;
n_entries = (size_t)icc_uint16(content + ptr), ptr += 2;
entry_size = (size_t)icc_uint16(content + ptr), ptr += 2;
if (tag_size == 1584)
n_channels = 3, n_entries = 256, entry_size = 2;
if (n_channels != 3)
/* Assuming sRGB, can only be an correct assumption if there are exactly three channels */
continue;
/* Check data availability */
if (n_channels > SIZE_MAX / n_entries)
continue;
if (entry_size > SIZE_MAX / (n_entries * n_channels))
continue;
if (n - ptr < n_channels * n_entries * entry_size)
continue;
/* Initialise ramps */
ramps->u8.red_size = n_entries;
ramps->u8.green_size = n_entries;
ramps->u8.blue_size = n_entries;
switch (entry_size) {
case 1:
*depth = LIBCOOPGAMMA_UINT8;
if (libcoopgamma_ramps_initialise(&ramps->u8) < 0)
return -1;
break;
case 2:
*depth = LIBCOOPGAMMA_UINT16;
if (libcoopgamma_ramps_initialise(&ramps->u16) < 0)
return -1;
break;
case 4:
*depth = LIBCOOPGAMMA_UINT32;
if (libcoopgamma_ramps_initialise(&ramps->u32) < 0)
return -1;
break;
case 8:
*depth = LIBCOOPGAMMA_UINT64;
if (libcoopgamma_ramps_initialise(&ramps->u64) < 0)
return -1;
break;
default:
*depth = LIBCOOPGAMMA_DOUBLE;
if (libcoopgamma_ramps_initialise(&ramps->d) < 0)
return -1;
break;
}
/* Get the lookup table */
switch (*depth) {
case LIBCOOPGAMMA_UINT8:
for (i = 0; i < ramps->u8.red_size; i++, ptr += 1)
ramps->u8.red[i] = icc_uint8(content + ptr);
for (i = 0; i < ramps->u8.green_size; i++, ptr += 1)
ramps->u8.green[i] = icc_uint8(content + ptr);
for (i = 0; i < ramps->u8.blue_size; i++, ptr += 1)
ramps->u8.blue[i] = icc_uint8(content + ptr);
break;
case LIBCOOPGAMMA_UINT16:
for (i = 0; i < ramps->u16.red_size; i++, ptr += 2)
ramps->u16.red[i] = icc_uint16(content + ptr);
for (i = 0; i < ramps->u16.green_size; i++, ptr += 2)
ramps->u16.green[i] = icc_uint16(content + ptr);
for (i = 0; i < ramps->u16.blue_size; i++, ptr += 2)
ramps->u16.blue[i] = icc_uint16(content + ptr);
break;
case LIBCOOPGAMMA_UINT32:
for (i = 0; i < ramps->u32.red_size; i++, ptr += 4)
ramps->u32.red[i] = icc_uint32(content + ptr);
for (i = 0; i < ramps->u32.green_size; i++, ptr += 4)
ramps->u32.green[i] = icc_uint32(content + ptr);
for (i = 0; i < ramps->u32.blue_size; i++, ptr += 4)
ramps->u32.blue[i] = icc_uint32(content + ptr);
break;
case LIBCOOPGAMMA_UINT64:
for (i = 0; i < ramps->u64.red_size; i++, ptr += 8)
ramps->u64.red[i] = icc_uint64(content + ptr);
for (i = 0; i < ramps->u64.green_size; i++, ptr += 8)
ramps->u64.green[i] = icc_uint64(content + ptr);
for (i = 0; i < ramps->u64.blue_size; i++, ptr += 8)
ramps->u64.blue[i] = icc_uint64(content + ptr);
break;
case LIBCOOPGAMMA_FLOAT:
case LIBCOOPGAMMA_DOUBLE:
default:
for (i = 0; i < ramps->d.red_size; i++, ptr += entry_size)
ramps->d.red[i] = icc_double(content + ptr, entry_size);
for (i = 0; i < ramps->d.green_size; i++, ptr += entry_size)
ramps->d.green[i] = icc_double(content + ptr, entry_size);
for (i = 0; i < ramps->d.blue_size; i++, ptr += entry_size)
ramps->d.blue[i] = icc_double(content + ptr, entry_size);
break;
}
return 0;
} else if (gamma_type == 1) {
/* The profile is encoded with gamma, brightness and contrast values */
/* Get the gamma, brightness and contrast */
if (n - ptr < 9 * 4)
continue;
r_gamma = icc_uint32(content + ptr), r_gamma /= 65536L, ptr += 4;
r_min = icc_uint32(content + ptr), r_min /= 65536L, ptr += 4;
r_max = icc_uint32(content + ptr), r_max /= 65536L, ptr += 4;
g_gamma = icc_uint32(content + ptr), g_gamma /= 65536L, ptr += 4;
g_min = icc_uint32(content + ptr), g_min /= 65536L, ptr += 4;
g_max = icc_uint32(content + ptr), g_max /= 65536L, ptr += 4;
b_gamma = icc_uint32(content + ptr), b_gamma /= 65536L, ptr += 4;
b_min = icc_uint32(content + ptr), b_min /= 65536L, ptr += 4;
b_max = icc_uint32(content + ptr), b_max /= 65536L, ptr += 4;
/* Initialise ramps */
*depth = LIBCOOPGAMMA_DOUBLE;
if (libcoopgamma_ramps_initialise(&ramps->d) < 0)
return -1;
/* Set ramps */
libclut_start_over(&ramps->d, (double)1, double, 1, 1, 1);
libclut_gamma(&ramps->d, (double)1, double, r_gamma, g_gamma, b_gamma);
libclut_rgb_limits(&ramps->d, (double)1, double, r_min, r_max, g_min, g_max, b_min, b_max);
return 0;
}
}
}
return -2;
}
/**
* Load an ICC profile
*
* @param file The ICC-profile file
* @param ramps Output parameter for the filter stored in the ICC profile,
* `.red_size`, `.green_size`, `.blue_size` should already be
* set (these values can however be modified.)
* @param depth Output parameter for ramps stop value type
* @return Zero on success, -1 on error, -2 if no usable data is
* available in the profile.
*/
static int
load_icc(const char *file, libcoopgamma_ramps_t *ramps, libcoopgamma_depth_t *depth)
{
char *content = NULL;
size_t ptr = 0, size = 0;
ssize_t got;
int fd = -1, r = -1, saved_errno;
size_t new_size;
void *new;
fd = open(file, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT) {
fprintf(stderr, "%s: %s: %s\n", argv0, strerror(ENOENT), file);
errno = 0;
}
goto fail;
}
for (;;) {
if (ptr == size) {
new_size = size ? (size << 1) : 4098;
new = realloc(content, new_size);
if (!new)
goto fail;
content = new;
size = new_size;
}
got = read(fd, content + ptr, size - ptr);
if (got < 0) {
if (errno == EINTR)
continue;
goto fail;
}
if (!got)
break;
ptr += (size_t)got;
}
close(fd), fd = -1;
r = parse_icc(content, ptr, ramps, depth);
fail:
saved_errno = errno;
if (fd >= 0)
close(fd);
free(content);
errno = saved_errno;
return r;
}
/**
* Get the pathname of the ICC profile for a CRTC
*
* @param crtc The CRTC name
* @return The ICC profile file
*/
static const char *
get_icc(const char *crtc)
{
size_t i;
if (crtc_icc_keys)
for (i = 0; crtc_icc_keys[i]; i++)
if (!strcasecmp(crtc, crtc_icc_keys[i]))
return crtc_icc_values[i];
return NULL;
}
/**
* Fill a filter
*
* @param filter The filter to fill
* @param ramps The prototype filter
* @param depth The prototype filter's stop datatype
*/
static void
fill_filter(libcoopgamma_filter_t *filter, const libcoopgamma_ramps_t *ramps, libcoopgamma_depth_t depth)
{
switch (filter->depth) {
#define X(CONST, MEMBER, MAX, TYPE)\
case CONST:\
switch (depth) {\
case LIBCOOPGAMMA_UINT8:\
libclut_translate(&filter->ramps.MEMBER, MAX, TYPE, &ramps->u8, UINT8_MAX, uint8_t);\
break;\
case LIBCOOPGAMMA_UINT16:\
libclut_translate(&filter->ramps.MEMBER, MAX, TYPE, &ramps->u16, UINT16_MAX, uint16_t);\
break;\
case LIBCOOPGAMMA_UINT32:\
libclut_translate(&filter->ramps.MEMBER, MAX, TYPE, &ramps->u32, UINT32_MAX, uint32_t);\
break;\
case LIBCOOPGAMMA_UINT64:\
libclut_translate(&filter->ramps.MEMBER, MAX, TYPE, &ramps->u64, UINT64_MAX, uint64_t);\
break;\
case LIBCOOPGAMMA_FLOAT:\
libclut_translate(&filter->ramps.MEMBER, MAX, TYPE, &ramps->f, (float)1, float);\
break;\
case LIBCOOPGAMMA_DOUBLE:\
libclut_translate(&filter->ramps.MEMBER, MAX, TYPE, &ramps->d, (double)1, double);\
break;\
}\
break;
LIST_DEPTHS
#undef X
default:
abort();
}
}
/**
* The main function for the program-specific code
*
* @return 0: Success
* -1: Error, `errno` set
* -2: Error, `cg.error` set
* -3: Error, message already printed
*/
int
start(void)
{
int r;
size_t i, j;
const char *path;
if (xflag)
for (i = 0; i < crtcs_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE;
else if (dflag)
for (i = 0; i < crtcs_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH;
else
for (i = 0; i < crtcs_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL;
if (!xflag && !icc_pathname)
if ((r = make_slaves()) < 0)
return cleanup(r);
if (icc_pathname) {
uniramps.u8.red_size = uniramps.u8.green_size = uniramps.u8.blue_size = 1;
for (i = 0; i < crtcs_n; i++) {
if (uniramps.u8.red_size < crtc_updates[i].filter.ramps.u8.red_size)
uniramps. u8.red_size = crtc_updates[i].filter.ramps.u8.red_size;
if (uniramps.u8.green_size < crtc_updates[i].filter.ramps.u8.green_size)
uniramps. u8.green_size = crtc_updates[i].filter.ramps.u8.green_size;
if (uniramps.u8.blue_size < crtc_updates[i].filter.ramps.u8.blue_size)
uniramps. u8.blue_size = crtc_updates[i].filter.ramps.u8.blue_size;
}
switch (load_icc(icc_pathname, &uniramps, &unidepth)) {
case 0:
break;
case -1:
return cleanup(-1);
case -2:
fprintf(stderr, "%s: unusable ICC profile: %s\n", argv0, icc_pathname);
return cleanup(-3);
}
} else {
rampses = calloc(crtcs_n, sizeof(*rampses));
if (!rampses)
return cleanup(-1);
depths = malloc(crtcs_n * sizeof(*depths));
if (!depths)
return cleanup(-1);
for (i = 0; i < crtcs_n; i++) {
rampses[i].u8.red_size = crtc_updates[i].filter.ramps.u8.red_size;
rampses[i].u8.green_size = crtc_updates[i].filter.ramps.u8.green_size;
rampses[i].u8.blue_size = crtc_updates[i].filter.ramps.u8.blue_size;
path = get_icc(crtc_updates[i].filter.crtc);
if (!path) {
/* TODO remove CRTC */
} else {
switch (load_icc(path, rampses + i, depths + i)) {
case 0:
break;
case -1:
return cleanup(-1);
case -2:
fprintf(stderr, "%s: unusable ICC profile: %s\n", argv0, path);
return cleanup(-3);
}
}
}
}
for (i = 0, r = 1; i < crtcs_n; i++) {
if (!crtc_updates[i].master || !crtc_info[i].supported)
continue;
if (!xflag) {
if (icc_pathname)
fill_filter(&crtc_updates[i].filter, &uniramps, unidepth);
else
fill_filter(&crtc_updates[i].filter, rampses + i, depths[i]);
}
r = update_filter(i, 0);
if (r == -2 || (r == -1 && errno != EAGAIN))
return cleanup(r);
if (crtc_updates[i].slaves) {
for (j = 0; crtc_updates[i].slaves[j]; j++) {
r = update_filter(crtc_updates[i].slaves[j], 0);
if (r == -2 || (r == -1 && errno != EAGAIN))
return cleanup(r);
}
}
}
while (r != 1)
if ((r = synchronise(-1)) < 0)
return cleanup(r);
if (!dflag)
return cleanup(0);
if (libcoopgamma_set_nonblocking(&cg, 0) < 0)
return cleanup(-1);
for (;;) {
if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) {
switch (errno) {
case 0:
break;
case ENOTRECOVERABLE:
goto enotrecoverable;
default:
return cleanup(-1);
}
}
}
enotrecoverable:
pause();
return cleanup(-1);
}