diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cg-icc.c | 387 |
1 files changed, 380 insertions, 7 deletions
diff --git a/src/cg-icc.c b/src/cg-icc.c index 6d18dd4..60fc696 100644 --- a/src/cg-icc.c +++ b/src/cg-icc.c @@ -19,8 +19,19 @@ #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. */ @@ -50,9 +61,56 @@ char* const default_class = PKGNAME "::cg-icc::standard"; /** + * -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_filepath = NULL; +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; @@ -70,6 +128,37 @@ void usage(void) /** + * 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 != NULL) + for (i = 0; i < crtcs_n; i++) + libcoopgamma_ramps_destroy(rampses + i); + free(rampses); + free(depths); + if (crtc_icc_keys != NULL) + for (i = 0; crtc_icc_keys[i] != NULL; i++) + free(crtc_icc_keys[i]); + free(crtc_icc_keys); + if (crtc_icc_values != NULL) + for (i = 0; crtc_icc_values[i] != NULL; 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 @@ -121,8 +210,10 @@ int handle_opt(char* opt, char* arg) int handle_args(int argc, char* argv[], char* method, char* site, char** crtcs, char* prio, char* rule) { + struct passwd* pw; + char* path = NULL; int free_fflag = 0, saved_errno; - int q = xflag + dflag; + int fd = -1, q = xflag + dflag; q += (method != NULL) && !strcmp(method, "?"); q += (prio != NULL) && !strcmp(prio, "?"); q += (rule != NULL) && (!strcmp(rule, "?") || !strcmp(rule, "??")); @@ -130,12 +221,43 @@ int handle_args(int argc, char* argv[], char* method, char* site, q += !strcmp(*crtcs, "?"); if ((q > 1) || (xflag && ((argc > 0) || (prio != NULL))) || (argc > 1)) usage(); - icc_filepath = *argv; + icc_pathname = *argv; + memset(&uniramps, 0, sizeof(uniramps)); + if (icc_pathname != NULL) + { + pw = getpwuid(getuid()); + if ((pw == NULL) || (pw->pw_dir == NULL)) + goto fail; + + path = malloc(strlen(pw->pw_dir) + sizeof("/.config")); + if (path == NULL) + 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; + + free(path), path = NULL; + + fd = openat(confdirfd, "icc", O_RDONLY); + if (fd < 0) + goto fail; + + /* TODO read */ + + close(fd), fd = -1; + } return 0; fail: saved_errno = errno; - if (free_fflag) - free(fflag), fflag = NULL; + free(path), path = NULL; + if (fd >= 0) + close(fd); errno = saved_errno; return cleanup(-1); } @@ -202,7 +324,7 @@ static uint16_t icc_uint16(const char* restrict content) */ static uint16_t icc_uint8(const char* restrict content) { - return (uint8_t)(content[0]) + return (uint8_t)(content[0]); } @@ -223,7 +345,7 @@ static double icc_double(const char* restrict content, size_t width) ret += (double)(unsigned char)(content[width - 1 - i]); } ret /= 255; - return ret + return ret; } @@ -461,3 +583,254 @@ static int parse_icc(const char* restrict content, size_t n, libcoopgamma_ramps_ 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; + + fd = open(file, O_RDONLY); + if (fd < 0) + goto fail; + + for (;;) + { + if (ptr == size) + { + size_t new_size = size ? (size << 1) : 4098; + void* new = realloc(content, new_size); + if (new == NULL) + 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 == 0) + 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; + for (i = 0; crtc_icc_keys[i] != NULL; 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 == NULL)) + if ((r = make_slaves()) < 0) + return cleanup(r); + + if (icc_pathname != NULL) + { + 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 == NULL) + return cleanup(-1); + depths = malloc(crtcs_n * sizeof(*depths)); + if (depths == NULL) + 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 == NULL) + { + /* TOOD remove CRTC */ + } + 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, icc_pathname); + 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 != NULL) + 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 != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; 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: + for (;;) + if (pause() < 0) + return cleanup(-1); +} + |