/** * libgamma — Display server abstraction layer for gamma ramp adjustments * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) * * This library 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 library 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 library. If not, see <http://www.gnu.org/licenses/>. */ #include <libgamma.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #ifdef __GNUC__ # if LIBGAMMA_ERROR_MIN < -46 # warning New error codes have been added to libgamma. # endif # if LIBGAMMA_METHOD_COUNT > 6 # warning New adjust methods has been added to libgamma # endif # if LIBGAMMA_CONNECTOR_TYPE_COUNT > 20 # warning New connector types have been added to libgamma. # endif # if LIBGAMMA_SUBPIXEL_ORDER_COUNT > 6 # warning New subpixel orders have been added to libgamma. # endif # if LIBGAMMA_CRTC_INFO_COUNT > 13 # warning New CRTC information fields have been added to libgamma. # endif #endif static const char* method_name(int method) { switch (method) { case LIBGAMMA_METHOD_DUMMY: return "dummy"; case LIBGAMMA_METHOD_X_RANDR: return "RandR X extension"; case LIBGAMMA_METHOD_X_VIDMODE: return "VidMode X extension"; case LIBGAMMA_METHOD_LINUX_DRM: return "Linux DRM"; case LIBGAMMA_METHOD_W32_GDI: return "Windows GDI"; case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS: return "Quartz using CoreGraphics"; default: return "(unknown)"; } } static void list_methods(const char* description, int* methods, int operation) { size_t i, n = libgamma_list_methods(methods, LIBGAMMA_METHOD_COUNT, operation); printf("%s:\n", description); for (i = 0; i < n; i++) printf(" %s\n", method_name(methods[i])); printf("\n"); } static void list_methods_lists(void) { int* methods = malloc(LIBGAMMA_METHOD_COUNT * sizeof(int)); size_t n = libgamma_list_methods(methods, LIBGAMMA_METHOD_COUNT, 4); if (n > LIBGAMMA_METHOD_COUNT) { printf("Warning: you should to recompile the program, libgamma has been updated.\n"); methods = realloc(methods, n * sizeof(int)); } list_methods("Available adjustment methods", methods, 4); list_methods("Available real adjustment methods", methods, 3); list_methods("Available real non-fake adjustment methods", methods, 2); list_methods("Recommended adjustment methods", methods, 1); list_methods("Recommended non-fake adjustment methods", methods, 0); free(methods); } static void method_availability(void) { int method; printf("Testing the availability of a non-existing adjustment method: "); printf("%s\n", libgamma_is_method_available(9999) ? "available" : "not available"); for (method = 0; method < LIBGAMMA_METHOD_COUNT; method++) { printf("Testing the availability of %s: ", method_name(method)); printf("%s\n", libgamma_is_method_available(method) ? "available" : "not available"); } printf("\n"); } static void list_default_sites(void) { int method; for (method = 0; method < LIBGAMMA_METHOD_COUNT; method++) if (libgamma_is_method_available(method)) { printf("Default site for %s:\n", method_name(method)); printf(" Variable: %s\n", libgamma_method_default_site_variable(method)); printf(" Site name: %s\n", libgamma_method_default_site(method)); printf("\n"); } } static void method_capabilities(void) { libgamma_method_capabilities_t caps; int method; for (method = 0; method < LIBGAMMA_METHOD_COUNT; method++) if (libgamma_is_method_available(method)) { printf("Capabilities of %s:\n", method_name(method)); libgamma_method_capabilities(&caps, method); printf(" %s: %X\n", "CRTC information", caps.crtc_information); printf(" %s: %s\n", "Default site known", caps.default_site_known ? "yes" : "no"); printf(" %s: %s\n", "Multiple sites", caps.multiple_sites ? "yes" : "no"); printf(" %s: %s\n", "Multiple partitions", caps.multiple_partitions ? "yes" : "no"); printf(" %s: %s\n", "Multiple crtcs", caps.multiple_crtcs ? "yes" : "no"); printf(" %s: %s\n", "Graphics cards", caps.partitions_are_graphics_cards ? "yes" : "no"); printf(" %s: %s\n", "Site restore", caps.site_restore ? "yes" : "no"); printf(" %s: %s\n", "Partition restore", caps.partition_restore ? "yes" : "no"); printf(" %s: %s\n", "CRTC restore", caps.crtc_restore ? "yes" : "no"); printf(" %s: %s\n", "Identical gamma sizes", caps.identical_gamma_sizes ? "yes" : "no"); printf(" %s: %s\n", "Fixed gamma size", caps.fixed_gamma_size ? "yes" : "no"); printf(" %s: %s\n", "Fixed gamma depth", caps.fixed_gamma_depth ? "yes" : "no"); printf(" %s: %s\n", "Real method", caps.real ? "yes" : "no"); printf(" %s: %s\n", "Fake method", caps.fake ? "yes" : "no"); printf("\n"); } } static void error_test(void) { int i; printf("Testing error API using LIBGAMMA_STATE_UNKNOWN:\n"); printf(" Expecting %i: %i\n", LIBGAMMA_STATE_UNKNOWN, libgamma_value_of_error("LIBGAMMA_STATE_UNKNOWN")); printf(" Expecting %s: %s\n", "LIBGAMMA_STATE_UNKNOWN", libgamma_name_of_error(LIBGAMMA_STATE_UNKNOWN)); printf("\n"); printf("Testing libgamma_perror:\n"); libgamma_perror(" Expecting LIBGAMMA_STATE_UNKNOWN", LIBGAMMA_STATE_UNKNOWN); libgamma_perror(" Expecting a description for ENOMEM", ENOMEM); libgamma_perror(" Expecting a description for successfulness", 0); libgamma_perror(" Expecting a description for ENOMEM", (errno = ENOMEM, LIBGAMMA_ERRNO_SET)); libgamma_group_gid = 10; libgamma_group_name = "test"; libgamma_perror(" Expecting 'LIBGAMMA_DEVICE_REQUIRE_GROUP: test (10)'", LIBGAMMA_DEVICE_REQUIRE_GROUP); libgamma_group_name = NULL; libgamma_perror(" Expecting 'LIBGAMMA_DEVICE_REQUIRE_GROUP: 10'", LIBGAMMA_DEVICE_REQUIRE_GROUP); printf("\n"); printf("Testing error code uniqueness: "); for (i = -1; i >= LIBGAMMA_ERROR_MIN; i--) if (libgamma_value_of_error(libgamma_name_of_error(i)) != i) { printf("failed\n"); goto not_unique; } printf("passed\n"); not_unique: printf("\n"); } static int select_monitor(libgamma_site_state_t* restrict site_state, libgamma_partition_state_t* restrict part_state, libgamma_crtc_state_t* restrict crtc_state) { int method; char* site; char* tmp; char buf[256]; int r; printf("Select adjustment method:\n"); for (method = 0; method < LIBGAMMA_METHOD_COUNT; method++) printf(" %i: %s\n", method, method_name(method)); printf("> "); fflush(stdout); fgets(buf, sizeof(buf) / sizeof(char), stdin); method = atoi(buf); printf("Select site: "); fflush(stdout); fgets(buf, sizeof(buf) / sizeof(char), stdin); tmp = strchr(buf, '\n'); if (tmp != NULL) *tmp = '\0'; if (buf[0] == '\0') site = NULL; else { site = malloc((strlen(buf) + 1) * sizeof(char)); memcpy(site, buf, strlen(buf) + 1); } if ((r = libgamma_site_initialise(site_state, method, site))) { free(site); return libgamma_perror("error", r), 1; } if (site_state->partitions_available == 0) { libgamma_site_free(site_state); return printf("No partitions found\n"), 1; } printf("Select partition [0, %lu]: ", site_state->partitions_available - 1); fflush(stdout); fgets(buf, sizeof(buf) / sizeof(char), stdin); if ((r = libgamma_partition_initialise(part_state, site_state, (size_t)atoll(buf)))) { libgamma_site_free(site_state); return libgamma_perror("error", r), 1; } if (part_state->crtcs_available == 0) { libgamma_partition_free(part_state); libgamma_site_free(site_state); return printf("No CRTC:s found\n"), 1; } printf("Select CRTC [0, %lu]: ", part_state->crtcs_available - 1); fflush(stdout); fgets(buf, sizeof(buf) / sizeof(char), stdin); if ((r = libgamma_crtc_initialise(crtc_state, part_state, (size_t)atoll(buf)))) { libgamma_partition_free(part_state); libgamma_site_free(site_state); return libgamma_perror("error", r), 1; } printf("\n"); return 0; } #define print_crtc_information_(type, notation) \ static void print_crtc_information_##type(int do_print, const char* description, int error, type value) \ { \ char buf[256]; \ if (do_print) \ { \ if (error) \ { \ snprintf(buf, sizeof(buf) / sizeof(char), " (error) %s", description); \ libgamma_perror(buf, error); \ } \ else \ printf(" %s: %" notation "\n", description, value); \ } \ } typedef const char* str; print_crtc_information_(size_t, "lu") print_crtc_information_(signed, "i") print_crtc_information_(int, "i") #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdouble-promotion" #endif print_crtc_information_(float, "f") #ifdef __GNUC__ # pragma GCC diagnostic pop #endif print_crtc_information_(str, "s") #undef print_crtc_information_ #define __case(VALUE) case VALUE: return #VALUE; static const char* subpixel_order_str(libgamma_subpixel_order_t value) { switch (value) { __case (LIBGAMMA_SUBPIXEL_ORDER_UNKNOWN) __case (LIBGAMMA_SUBPIXEL_ORDER_NONE) __case (LIBGAMMA_SUBPIXEL_ORDER_HORIZONTAL_RGB) __case (LIBGAMMA_SUBPIXEL_ORDER_HORIZONTAL_BGR) __case (LIBGAMMA_SUBPIXEL_ORDER_VERTICAL_RGB) __case (LIBGAMMA_SUBPIXEL_ORDER_VERTICAL_BGR) default: return "(unknown)"; } } static const char* connector_type_str(libgamma_connector_type_t value) { switch (value) { __case (LIBGAMMA_CONNECTOR_TYPE_Unknown) __case (LIBGAMMA_CONNECTOR_TYPE_VGA) __case (LIBGAMMA_CONNECTOR_TYPE_DVI) __case (LIBGAMMA_CONNECTOR_TYPE_DVII) __case (LIBGAMMA_CONNECTOR_TYPE_DVID) __case (LIBGAMMA_CONNECTOR_TYPE_DVIA) __case (LIBGAMMA_CONNECTOR_TYPE_Composite) __case (LIBGAMMA_CONNECTOR_TYPE_SVIDEO) __case (LIBGAMMA_CONNECTOR_TYPE_LVDS) __case (LIBGAMMA_CONNECTOR_TYPE_Component) __case (LIBGAMMA_CONNECTOR_TYPE_9PinDIN) __case (LIBGAMMA_CONNECTOR_TYPE_DisplayPort) __case (LIBGAMMA_CONNECTOR_TYPE_HDMI) __case (LIBGAMMA_CONNECTOR_TYPE_HDMIA) __case (LIBGAMMA_CONNECTOR_TYPE_HDMIB) __case (LIBGAMMA_CONNECTOR_TYPE_TV) __case (LIBGAMMA_CONNECTOR_TYPE_eDP) __case (LIBGAMMA_CONNECTOR_TYPE_VIRTUAL) __case (LIBGAMMA_CONNECTOR_TYPE_DSI) __case (LIBGAMMA_CONNECTOR_TYPE_LFP) default: return "(unknown)"; } } #undef __case static void crtc_information(libgamma_crtc_state_t* restrict crtc) { libgamma_method_capabilities_t caps; libgamma_crtc_information_t info; int fields; int field; libgamma_method_capabilities(&caps, crtc->partition->site->method); for (fields = caps.crtc_information; field = fields & -fields, fields; fields ^= field) { if (libgamma_get_crtc_information(&info, crtc, field)) printf("Could not read CRTC information field %i\n", field); free(info.edid); free(info.connector_name); } fields = caps.crtc_information; if (libgamma_get_crtc_information(&info, crtc, fields)) printf("An error occurred while reading CRTC information\n"); #define print2(TYPE, FIELD_ID, DESCRIPTION, FIELD_VAR, ERROR_VAR) \ print_crtc_information_##TYPE(fields & FIELD_ID, DESCRIPTION, info.ERROR_VAR, info.FIELD_VAR); #define print(TYPE, FIELD_ID, DESCRIPTION, FIELD_VAR) \ print2(TYPE, FIELD_ID, DESCRIPTION, FIELD_VAR, FIELD_VAR##_error); printf("CRTC information:\n"); if ((fields & LIBGAMMA_CRTC_INFO_EDID)) { if (info.edid_error) libgamma_perror(" (error) EDID", info.edid_error); else { char* edid_lc = libgamma_behex_edid(info.edid, info.edid_length); unsigned char* edid_raw = libgamma_unhex_edid(edid_lc); char* edid_uc = libgamma_behex_edid_uppercase(edid_raw, info.edid_length);; printf(" EDID: %s\n", edid_lc); printf(" EDID (uppercase): %s\n", edid_uc); printf(" EDID (length): %lu\n", info.edid_length); free(edid_lc); free(edid_raw); free(edid_uc); } } print(size_t, LIBGAMMA_CRTC_INFO_WIDTH_MM, "width", width_mm); print(size_t, LIBGAMMA_CRTC_INFO_HEIGHT_MM, "height", height_mm); print(size_t, LIBGAMMA_CRTC_INFO_WIDTH_MM_EDID, "width per EDID", width_mm_edid); print(size_t, LIBGAMMA_CRTC_INFO_HEIGHT_MM_EDID, "height per EDID", height_mm_edid); print2(size_t, LIBGAMMA_CRTC_INFO_GAMMA_SIZE, "red gamma ramp size", red_gamma_size, gamma_size_error); print2(size_t, LIBGAMMA_CRTC_INFO_GAMMA_SIZE, "green gamma ramp size", green_gamma_size, gamma_size_error); print2(size_t, LIBGAMMA_CRTC_INFO_GAMMA_SIZE, "blue gamma ramp size", blue_gamma_size, gamma_size_error); print(signed, LIBGAMMA_CRTC_INFO_GAMMA_DEPTH, "gamma ramp depth", gamma_depth); print(int, LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT, "gamma support", gamma_support); if ((fields & LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER)) { if (info.subpixel_order_error) libgamma_perror(" (error) subpixel order", info.subpixel_order_error); else printf(" subpixel order: %s\n", subpixel_order_str(info.subpixel_order)); } print(int, LIBGAMMA_CRTC_INFO_ACTIVE, "active", active); print(str, LIBGAMMA_CRTC_INFO_CONNECTOR_NAME, "connector name", connector_name); if ((fields & LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE)) { if (info.connector_type_error) libgamma_perror(" (error) subpixel order", info.connector_type_error); else printf(" subpixel order: %s\n", connector_type_str(info.connector_type)); } print2(float, LIBGAMMA_CRTC_INFO_GAMMA, "red gamma characteristics", gamma_red, gamma_error); print2(float, LIBGAMMA_CRTC_INFO_GAMMA, "green gamma characteristics", gamma_green, gamma_error); print2(float, LIBGAMMA_CRTC_INFO_GAMMA, "blue gamma characteristics", gamma_blue, gamma_error); #undef print free(info.edid); free(info.connector_name); printf("\n"); } int main(void) { libgamma_site_state_t* restrict site_state = malloc(sizeof(libgamma_site_state_t)); libgamma_partition_state_t* restrict part_state = malloc(sizeof(libgamma_partition_state_t)); libgamma_crtc_state_t* restrict crtc_state = malloc(sizeof(libgamma_crtc_state_t)); libgamma_crtc_information_t info; libgamma_gamma_ramps_t old_ramps; libgamma_gamma_ramps_t ramps; size_t i, n; int r; list_methods_lists(); method_availability(); list_default_sites(); method_capabilities(); error_test(); if (select_monitor(site_state, part_state, crtc_state)) return 1; crtc_information(crtc_state); libgamma_get_crtc_information(&info, crtc_state, LIBGAMMA_CRTC_INFO_GAMMA_SIZE); old_ramps.red_size = info.red_gamma_size; old_ramps.green_size = info.green_gamma_size; old_ramps.blue_size = info.blue_gamma_size; ramps = old_ramps; libgamma_gamma_ramps_initialise(&old_ramps); libgamma_gamma_ramps_initialise(&ramps); libgamma_crtc_get_gamma_ramps(crtc_state, &old_ramps); r = libgamma_crtc_get_gamma_ramps(crtc_state, &ramps); if (r) libgamma_perror("libgamma_crtc_get_gamma_ramps", r); else { n = ramps.red_size; n = n < ramps.green_size ? n : ramps.green_size; n = n < ramps.blue_size ? n : ramps.blue_size; printf("Current gamma ramps:\n"); for (i = 0; i < n; i++) { if (i < ramps.red_size) printf(" \033[31m%04X\033[00m", ramps.red[i]); else printf(" "); if (i < ramps.green_size) printf(" \033[32m%04X\033[00m", ramps.green[i]); else printf(" "); if (i < ramps.blue_size) printf(" \033[34m%04X\033[00m", ramps.blue[i]); else printf(" "); printf("\n"); } printf("\n"); for (i = 0; i < ramps.red_size + ramps.green_size + ramps.blue_size; i++) ramps.red[i] /= 2; printf("Dimming monitor for 1 second...\n"); r = libgamma_crtc_set_gamma_ramps(crtc_state, ramps); if (r) libgamma_perror("libgamma_crtc_set_gamma_ramps", r); sleep(1); r = libgamma_crtc_set_gamma_ramps(crtc_state, old_ramps); if (r) libgamma_perror("libgamma_crtc_set_gamma_ramps", r); printf("Done!\n"); } /* TODO Test gamma ramp restore functions. */ libgamma_gamma_ramps_destroy(&ramps); libgamma_gamma_ramps_destroy(&old_ramps); libgamma_crtc_free(crtc_state); libgamma_partition_free(part_state); libgamma_site_free(site_state); return r; }