/* See LICENSE file for copyright and license details. */ #include "arg.h" #include #include #include #include #include #include #include /** * The libcoopgamma context */ static libcoopgamma_context_t cg; /** * Filter query */ static libcoopgamma_filter_query_t query; /** * The class of the filter to print */ static char* class = NULL; /** * Print usage information and exit */ static void usage(void) { fprintf(stderr, "Usage: %s [-M method] [-S site] [-h high] [-l low] [-f class] -c crtc\n", argv0); exit(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; } /** * Print, to stdout, information about * the selected CRTC * * @return Zero on success, -1 on error, -2 * on libcoopgamma error, -3 on error * with error message already printed */ static int print_info(void) { libcoopgamma_crtc_info_t info; libcoopgamma_filter_table_t table; const char* str; int saved_errno, ret = 0; size_t i; if (libcoopgamma_crtc_info_initialise(&info) < 0) return -1; if (libcoopgamma_filter_table_initialise(&table) < 0) { saved_errno = errno; libcoopgamma_crtc_info_destroy(&info); errno = saved_errno; return -1; } if (libcoopgamma_get_gamma_info_sync(query.crtc, &info, &cg) < 0) goto cg_fail; printf("Cooperative gamma server running: %s\n", info.cooperative ? "yes" : "no"); printf("Gamma adjustments supported: %s\n", info.supported == LIBCOOPGAMMA_MAYBE ? "maybe" : info.supported ? "yes" : "no"); printf("Gamma ramps stops (red green blue): %zu %zu %zu\n", info.red_size, info.green_size, info.blue_size); switch (info.depth) { case LIBCOOPGAMMA_DOUBLE: str = "double-precision floating-point"; break; case LIBCOOPGAMMA_FLOAT: str = "single-precision floating-point"; break; case LIBCOOPGAMMA_UINT8: str = "unsigned 8-bit integer"; break; case LIBCOOPGAMMA_UINT16: str = "unsigned 16-bit integer"; break; case LIBCOOPGAMMA_UINT32: str = "unsigned 32-bit integer"; break; case LIBCOOPGAMMA_UINT64: str = "unsigned 64-bit integer"; break; default: errno = EPROTO; goto fail; } printf("Gamma ramps stops value type: %s\n", str); if (info.colourspace != LIBCOOPGAMMA_UNKNOWN) { switch (info.colourspace) { case LIBCOOPGAMMA_SRGB: str = "sRGB"; break; case LIBCOOPGAMMA_RGB: str = "non-standard RGB"; break; case LIBCOOPGAMMA_NON_RGB: str = "non-RGB multicolour"; break; case LIBCOOPGAMMA_GREY: str = "monochrome or singlecolour scale"; break; default: errno = EPROTO; goto fail; } printf("Monitor's colourspace: %s\n", str); } if (info.have_gamut) { printf("Monitor's red colour (x, y): %lf, %lf\n", info.red_x / (double)1024, info.red_y / (double)1024); printf("Monitor's green colour (x, y): %lf, %lf\n", info.green_x / (double)1024, info.green_y / (double)1024); printf("Monitor's blue colour (x, y): %lf, %lf\n", info.blue_x / (double)1024, info.blue_y / (double)1024); printf("Monitor's white point (x, y): %lf, %lf\n", info.white_x / (double)1024, info.white_y / (double)1024); } if (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0) goto cg_fail; if ((table.red_size != info.red_size) || (table.green_size != info.green_size) || (table.blue_size != info.blue_size) || (table.depth != info.depth)) { fprintf(stderr, "%s: gamma ramp structure changed between queries\n", argv0); goto custom_fail; } printf("Filters: %zu\n", table.filter_count); for (i = 0; i < table.filter_count; i++) { printf(" Filter %zu:\n", i); printf(" Priority: %" PRIi64 "\n", table.filters[i].priority); printf(" Class: %s\n", table.filters[i].class); } done: saved_errno = errno; libcoopgamma_crtc_info_destroy(&info); libcoopgamma_filter_table_destroy(&table); errno = saved_errno; return ret; fail: ret = -1; goto done; cg_fail: ret = -2; goto done; custom_fail: ret = -3; goto done; } /** * Print, to stdout, the ramps of the select * filter on the select CRTC * * @return Zero on success, -1 on error, -2 * on libcoopgamma error, -3 on error * with error message already printed */ static int print_filter(void) { libcoopgamma_filter_table_t table; libcoopgamma_ramps_t* restrict ramps; int saved_errno, ret = 0; size_t i, n; if (libcoopgamma_filter_table_initialise(&table) < 0) return -1; if (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0) goto cg_fail; if (query.coalesce) i = 0; else for (i = 0; i < table.filter_count; i++) if (!strcmp(table.filters[i].class, class)) break; if (i == table.filter_count) { fprintf(stderr, "%s: selected filter does not exist on selected CRTC\n", argv0); goto custom_fail; } ramps = &(table.filters[i].ramps); n = table.red_size; if (n < table.green_size) n = table.green_size; if (n < table.blue_size) n = table.blue_size; switch (table.depth) { #define X(CONST, MEMBER, TYPE, FORMAT, DASH) \ case CONST: \ for (i = 0; i < n; i++) \ { \ if (i < ramps->MEMBER.red_size) \ printf("%" FORMAT " ", (TYPE)(ramps->MEMBER.red[i])); \ else \ printf(DASH " "); \ if (i < ramps->MEMBER.green_size) \ printf("%" FORMAT " ", (TYPE)(ramps->MEMBER.green[i])); \ else \ printf(DASH " "); \ if (i < ramps->MEMBER.blue_size) \ printf("%" FORMAT "\n", (TYPE)(ramps->MEMBER.blue[i])); \ else \ printf(DASH "\n"); \ } \ break X(LIBCOOPGAMMA_DOUBLE, d, double, "lf", "----"); X(LIBCOOPGAMMA_FLOAT, f, double, "lf", "----"); X(LIBCOOPGAMMA_UINT8, u8, uint8_t, "02" PRIx8, "--"); X(LIBCOOPGAMMA_UINT16, u16, uint16_t, "04" PRIx16, "----"); X(LIBCOOPGAMMA_UINT32, u32, uint32_t, "08" PRIx32, "--------"); X(LIBCOOPGAMMA_UINT64, u64, uint64_t, "016" PRIx64, "----------------"); #undef X default: errno = EPROTO; goto fail; } done: saved_errno = errno; libcoopgamma_filter_table_destroy(&table); errno = saved_errno; return ret; fail: ret = -1; goto done; cg_fail: ret = -2; goto done; custom_fail: ret = -3; goto done; } /** * -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. * * -h HIGH * Suppress filter with higher priority than HIGH. * * -l LOW * Suppress filter with lower priority than LOW. * * -f CLASS * Print gamma ramps of the filter with class CLASS * on the selected CRTC. If CLASS is "*" all filters * with a priority in [LOW, HIGH] are coalesced. * * @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, haveh = 0, havel = 0; int rc = 0; char* method = NULL; char* site = NULL; query.high_priority = INT64_MAX; query.low_priority = INT64_MIN; query.crtc = NULL; query.coalesce = 0; ARGBEGIN { case 'M': if (method != NULL) usage(); method = EARGF(usage()); break; case 'S': if (site != NULL) usage(); site = EARGF(usage()); break; case 'c': if (query.crtc != NULL) usage(); query.crtc = EARGF(usage()); break; case 'h': if (haveh++) usage(); query.high_priority = (int64_t)atoll(EARGF(usage())); break; case 'l': if (havel++) usage(); query.low_priority = (int64_t)atoll(EARGF(usage())); break; case 'f': if (class != NULL) usage(); class = EARGF(usage()); if ((class[0] == '*') && (class[1] == '\0')) query.coalesce = 1; break; default: usage(); } ARGEND; if (argc) usage(); if (initialise_proc() < 0) goto fail; if ((method != NULL) && !strcmp(method, "?")) { if ((site != NULL) || (query.crtc != NULL)) usage(); 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 (!(query.crtc)) usage(); if (!strcmp(query.crtc, "?")) switch (list_crtcs()) { case 0: goto done; case -1: goto fail; default: goto cg_fail; } switch (class ? print_filter() : print_info()) { case 0: goto done; case -1: goto fail; case -2: goto cg_fail; default: goto custom_fail; } fflush(stdout); if (ferror(stdout)) goto fail; if (fclose(stdout) < 0) goto fail; done: if (stage >= 1) libcoopgamma_context_destroy(&cg, stage >= 2); return rc; custom_fail: rc = 1; goto done; fail: rc = 1; 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; }