/* See LICENSE file for copyright and license details. */ #include "cg-base.h" #include #include #include #include #include #include #include #include #include /* Note, that EDID:s are at least 256 hexadecimals long, * sometimes 512 hexadecimals long, and a filename can * often 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 encoded 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); }