aboutsummaryrefslogtreecommitdiffstats
path: root/gamma-x-randr.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gamma-x-randr.c985
1 files changed, 0 insertions, 985 deletions
diff --git a/gamma-x-randr.c b/gamma-x-randr.c
deleted file mode 100644
index 95d730c..0000000
--- a/gamma-x-randr.c
+++ /dev/null
@@ -1,985 +0,0 @@
-/* See LICENSE file for copyright and license details. */
-#ifndef HAVE_LIBGAMMA_METHOD_X_RANDR
-# error Compiling gamma-x-randr.c without HAVE_LIBGAMMA_METHOD_X_RANDR
-#endif
-
-#include "gamma-x-randr.h"
-
-#include "libgamma-error.h"
-#include "edid.h"
-
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-#ifdef DEBUG
-# include <stdio.h>
-#endif
-
-#include <xcb/xcb.h>
-#include <xcb/randr.h>
-
-
-/**
- * The major version of RandR the library expects
- */
-#define RANDR_VERSION_MAJOR 1
-
-/**
- * The minor version of RandR the library expects
- */
-#define RANDR_VERSION_MINOR 3
-
-
-
-/**
- * Data structure for partition data
- */
-typedef struct libgamma_x_randr_partition_data {
- /**
- * Mapping from CRTC indices to CRTC identifiers
- */
- xcb_randr_crtc_t *crtcs;
-
- /**
- * Mapping from output indices to output identifiers
- */
- xcb_randr_output_t *outputs;
-
- /**
- * The number of outputs available
- */
- size_t outputs_count;
-
- /**
- * Mapping from CRTC indices to output indices.
- * CRTC's without an output (should be impossible)
- * have the value `SIZE_MAX` which is impossible
- * for an existing mapping
- */
- size_t *crtc_to_output;
-
- /**
- * Screen configuration timestamp
- */
- xcb_timestamp_t config_timestamp;
-
-} libgamma_x_randr_partition_data_t;
-
-
-
-/**
- * Translate an xcb error into a libgamma error
- *
- * @param error_code The xcb error
- * @param default_error The libgamma error to use if the xcb error is not recognised
- * @return The libgamma error
- */
-static int
-translate_error_(int error_code, int default_error)
-{
- switch (error_code) {
- case XCB_CONN_ERROR: return errno = ECONNABORTED, LIBGAMMA_ERRNO_SET;
- case XCB_CONN_CLOSED_EXT_NOTSUPPORTED: return errno = ENOPROTOOPT, LIBGAMMA_ERRNO_SET;
- case XCB_CONN_CLOSED_MEM_INSUFFICIENT: return errno = ENOMEM, LIBGAMMA_ERRNO_SET;
- case XCB_CONN_CLOSED_REQ_LEN_EXCEED: return errno = EMSGSIZE, LIBGAMMA_ERRNO_SET;
- case XCB_CONN_CLOSED_PARSE_ERR: return LIBGAMMA_NO_SUCH_SITE;
- case XCB_CONN_CLOSED_INVALID_SCREEN: return LIBGAMMA_NO_SUCH_PARTITION;
- case XCB_CONN_CLOSED_FDPASSING_FAILED: return errno = EIO, LIBGAMMA_ERRNO_SET;
- default:
- return default_error;
- }
-}
-
-
-/**
- * Translate an xcb error into a libgamma error
- *
- * @param error_code The xcb error
- * @param default_error The libgamma error to use if the xcb error is not recognised
- * @param return_errno Whether an `errno` value may be returned
- * @return The libgamma error
- */
-static int
-translate_error(int error_code, int default_error, int return_errno)
-{
- int r = translate_error_(error_code, default_error);
- return (return_errno && r > 0) ? errno : r;
-}
-
-
-/**
- * Return the capabilities of the adjustment method
- *
- * @param this The data structure to fill with the method's capabilities
- */
-void
-libgamma_x_randr_method_capabilities(libgamma_method_capabilities_t *restrict this)
-{
- char *display = getenv("DISPLAY");
- /* Support for all information except active status and gamma ramp support.
- Active status can be queried but it is not guaranteed produces an up to date result. */
- this->crtc_information = LIBGAMMA_CRTC_INFO_MACRO_EDID
- | LIBGAMMA_CRTC_INFO_MACRO_VIEWPORT
- | LIBGAMMA_CRTC_INFO_MACRO_RAMP
- | LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER
- | LIBGAMMA_CRTC_INFO_MACRO_CONNECTOR;
- /* X RandR supports multiple sites, partitions and CRTC:s */
- this->default_site_known = display && *display;
- this->multiple_sites = 1;
- this->multiple_partitions = 1;
- this->multiple_crtcs = 1;
- /* Partitions are screens and not graphics cards in X */
- this->partitions_are_graphics_cards = 0;
- /* X does not have system restore capabilities */
- this->site_restore = 0;
- this->partition_restore = 0;
- this->crtc_restore = 0;
- /* Gamma ramp sizes are identical but not fixed */
- this->identical_gamma_sizes = 1;
- this->fixed_gamma_size = 0;
- /* Gamma ramp depths are fixed */
- this->fixed_gamma_depth = 1;
- /* X RandR is a real non-faked adjustment method */
- this->real = 1;
- this->fake = 0;
- /* Gamma ramp adjustments are persistent */
- this->auto_restore = 0;
-}
-
-
-/* xcb violates the rule to never return struct:s */
-#ifdef __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Waggregate-return"
-#endif
-
-
-/**
- * Initialise an allocated site state
- *
- * @param this The site state to initialise
- * @param site The site identifier, unless it is `NULL` it must a
- * `free`:able. Once the state is destroyed the library
- * will attempt to free it. There you should not free
- * it yourself, and it must not be a string constant
- * or allocate on the stack. Note however that it will
- * not be free:d if this function fails.
- * @return Zero on success, otherwise (negative) the value of an
- * error identifier provided by this library
- */
-int
-libgamma_x_randr_site_initialise(libgamma_site_state_t *restrict this, char *restrict site)
-{
- xcb_generic_error_t *error = NULL;
- xcb_connection_t *restrict connection;
- xcb_randr_query_version_cookie_t cookie;
- xcb_randr_query_version_reply_t *restrict reply;
- const xcb_setup_t *restrict setup;
- xcb_screen_iterator_t iter;
-
- /* Connect to the display server */
- this->data = connection = xcb_connect(site, NULL);
- if (!connection || xcb_connection_has_error(connection))
- return LIBGAMMA_OPEN_SITE_FAILED;
-
- /* Query the version of the X RandR extension protocol */
- cookie = xcb_randr_query_version(connection, RANDR_VERSION_MAJOR, RANDR_VERSION_MINOR);
- reply = xcb_randr_query_version_reply(connection, cookie, &error);
-
- /* Check for version query failure */
- if (error || !reply) {
- /* Release resources */
- free(reply);
- /* If `xcb_connect` failed, both `error` and `reply` will be `NULL`.
- * TODO: Can both be `NULL` for any other reason? */
- if (!error && !reply)
- return LIBGAMMA_OPEN_SITE_FAILED;
- xcb_disconnect(connection);
- /* Translate and report error. */
- if (error != NULL)
- return translate_error(error->error_code, LIBGAMMA_PROTOCOL_VERSION_QUERY_FAILED, 0);
- return LIBGAMMA_PROTOCOL_VERSION_QUERY_FAILED;
- }
-
- /* Check protocol compatibility,
- we require 1.3 but 2.x may not be backwards compatible */
- if (reply->major_version != RANDR_VERSION_MAJOR || reply->minor_version < RANDR_VERSION_MINOR) {
-#ifdef DEBUG
- /* Print used protocol */
- fprintf(stderr, "libgamma: RandR protocol version: %u.%u", reply->major_version, reply->minor_version);
-#endif
- /* Release resources */
- free(reply);
- xcb_disconnect(connection);
- /* Report error */
- return LIBGAMMA_PROTOCOL_VERSION_NOT_SUPPORTED;
- }
-
- /* We do not longer need to know the version of the protocol */
- free(reply);
-
- /* Get available screens */
- setup = xcb_get_setup(connection);
- if (!setup) {
- xcb_disconnect(connection);
- return LIBGAMMA_LIST_PARTITIONS_FAILED;
- }
- iter = xcb_setup_roots_iterator(setup);
- /* Get the number of available screens */
- this->partitions_available = (size_t)iter.rem;
-
- /* Sanity check the number of available screens. */
- return iter.rem >= ? 0 : LIBGAMMA_NEGATIVE_PARTITION_COUNT;
-}
-
-
-/**
- * Release all resources held by a site state
- *
- * @param this The site state
- */
-void
-libgamma_x_randr_site_destroy(libgamma_site_state_t *restrict this)
-{
- xcb_disconnect((xcb_connection_t *)this->data);
-}
-
-
-/**
- * Restore the gamma ramps all CRTC:s with a site to the system settings
- *
- * @param this The site state
- * @return Zero on success, otherwise (negative) the value of an
- * error identifier provided by this library
- */
-int
-libgamma_x_randr_site_restore(libgamma_site_state_t *restrict this)
-{
- (void) this;
- errno = ENOTSUP;
- return LIBGAMMA_ERRNO_SET;
-}
-
-
-/**
- * Duplicate a memory area
- *
- * @param ptr The memory area
- * @param bytes The size, in bytes, of the memory area
- * @return A duplication of the memory, `NULL` if zero-length or on error
- */
-static inline void *
-memdup(void *restrict ptr, size_t bytes)
-{
- char *restrict rc;
- if (!bytes)
- return NULL;
- rc = malloc(bytes);
- if (!rc)
- return NULL;
- memcpy(rc, ptr, bytes);
- return rc;
-}
-
-
-/**
- * Initialise an allocated partition state
- *
- * @param this The partition state to initialise
- * @param site The site state for the site that the partition belongs to.
- * @param partition The the index of the partition within the site
- * @return Zero on success, otherwise (negative) the value of an
- * error identifier provided by this library
- */
-int
-libgamma_x_randr_partition_initialise(libgamma_partition_state_t *restrict this,
- libgamma_site_state_t *restrict site, size_t partition)
-{
- int fail_rc = LIBGAMMA_ERRNO_SET;
- xcb_connection_t *restrict connection = site->data;
- xcb_screen_t *restrict screen = NULL;
- xcb_generic_error_t *error = NULL;
- const xcb_setup_t *restrict setup;
- xcb_screen_iterator_t iter;
- xcb_randr_get_screen_resources_current_cookie_t cookie;
- xcb_randr_get_screen_resources_current_reply_t *restrict reply;
- xcb_randr_crtc_t *restrict crtcs;
- xcb_randr_output_t *restrict outputs;
- libgamma_x_randr_partition_data_t *restrict data;
- xcb_randr_get_output_info_cookie_t out_cookie;
- xcb_randr_get_output_info_reply_t *out_reply;
- size_t i;
- uint16_t j;
-
- /* Get screen list */
- setup = xcb_get_setup(connection);
- if (!setup)
- return LIBGAMMA_LIST_PARTITIONS_FAILED;
- iter = xcb_setup_roots_iterator(setup);
-
- /* Get the screen */
- for (i = 0; iter.rem > 0; i++, xcb_screen_next(&iter))
- if (i == partition) {
- screen = iter.data;
- break;
- }
- /* Report failure if we did not find the screen */
- if (!iter.rem)
- return LIBGAMMA_NO_SUCH_PARTITION;
-
- /* Check that the screen is not `NULL`.
- * (Do not think this can happen, but why not.) */
- if (!screen)
- return LIBGAMMA_NULL_PARTITION;
-
- /* Get the current resources of the screen */
- cookie = xcb_randr_get_screen_resources_current(connection, screen->root);
- reply = xcb_randr_get_screen_resources_current_reply(connection, cookie, &error);
- if (error)
- return translate_error(error->error_code, LIBGAMMA_LIST_CRTCS_FAILED, 0);
-
- /* Get the number of available CRTC:s */
- this->crtcs_available = reply->num_crtcs;
- /* Get the CRTC and output lists */
- crtcs = xcb_randr_get_screen_resources_current_crtcs(reply);
- outputs = xcb_randr_get_screen_resources_current_outputs(reply);
- if (!crtcs || !outputs) {
- free(reply);
- return LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED;
- }
-
- /* Allocate adjustment method dependent data memory area.
- We use `calloc` because we want `data`'s pointers to be `NULL` if not allocated at `fail`. */
- data = calloc(1, sizeof(libgamma_x_randr_partition_data_t));
- if (!data)
- goto fail;
-
- /* Copy the CRTC:s, just so we do not have to keep the reply in memory */
- data->crtcs = memdup(crtcs, (size_t)(reply->num_crtcs) * sizeof(xcb_randr_crtc_t));
- if (!data->crtcs && reply->num_crtcs > 0)
- goto fail;
-
- /* Copy the outputs as well */
- data->outputs = memdup(outputs, (size_t)reply->num_outputs * sizeof(xcb_randr_output_t));
- if (!data->outputs && reply->num_outputs > 0)
- goto fail;
-
- /* Get the number of available outputs */
- data->outputs_count = (size_t)reply->num_outputs;
-
- /* Create mapping table from CRTC indices to output indicies. (injection) */
- data->crtc_to_output = malloc((size_t)reply->num_crtcs * sizeof(size_t));
- if (!data->crtc)
- goto fail;
- /* All CRTC:s should be mapped, but incase they are not, all unmapped CRTC:s should have
- an invalid target, namely `SIZE_MAX`, which is 1 more than the theoretical limit */
- for (i = 0; i < (size_t)reply->num_crtcs; i++)
- data->crtc_to_output[i] = SIZE_MAX;
- /* Fill the table */
- for (i = 0; i < (size_t)reply->num_outputs; i++) {
- /* Query output (target) information */
- out_cookie = xcb_randr_get_output_info(connection, outputs[i], reply->config_timestamp);
- out_reply = xcb_randr_get_output_info_reply(connection, out_cookie, &error);
- if (error) {
- fail_rc = translate_error(error->error_code, LIBGAMMA_OUTPUT_INFORMATION_QUERY_FAILED, 0);
- goto fail;
- }
-
- /* Find CRTC (source) */
- for (j = 0; j < reply->num_crtcs; j++) {
- if (crtcs[j] == out_reply->crtc) {
- data->crtc_to_output[j] = i;
- break;
- }
- }
-
- /* Release output information */
- free(out_reply);
- }
-
- /* Store the configuration timestamp */
- data->config_timestamp = reply->config_timestamp;
- /* Store the adjustment method dependent data */
- this->data = data;
- /* Release resources and return successfully */
- free(reply);
- return 0;
-
-fail:
- /* Release resources and return with an error */
- if (data) {
- free(data->crtcs);
- free(data->outputs);
- free(data->crtc_to_output);
- free(data);
- }
- free(reply);
- return fail_rc;
-}
-
-
-/**
- * Release all resources held by a partition state
- *
- * @param this The partition state
- */
-void
-libgamma_x_randr_partition_destroy(libgamma_partition_state_t *restrict this)
-{
- libgamma_x_randr_partition_data_t *restrict data = this->data;
- free(data->crtcs);
- free(data->outputs);
- free(data->crtc_to_output);
- free(data);
-}
-
-
-/**
- * Restore the gamma ramps all CRTC:s with a partition to the system settings
- *
- * @param this The partition state
- * @return Zero on success, otherwise (negative) the value of an
- * error identifier provided by this library
- */
-int
-libgamma_x_randr_partition_restore(libgamma_partition_state_t *restrict this)
-{
- (void) this;
- errno = ENOTSUP;
- return LIBGAMMA_ERRNO_SET;
-}
-
-
-
-/**
- * Initialise an allocated CRTC state
- *
- * @param this The CRTC state to initialise
- * @param partition The partition state for the partition that the CRTC belongs to.
- * @param crtc The the index of the CRTC within the site
- * @return Zero on success, otherwise (negative) the value of an
- * error identifier provided by this library
- */
-int
-libgamma_x_randr_crtc_initialise(libgamma_crtc_state_t *restrict this,
- libgamma_partition_state_t *restrict partition, size_t crtc)
-{
- libgamma_x_randr_partition_data_t *restrict screen_data = partition->data;
- xcb_randr_crtc_t *restrict crtc_ids = screen_data->crtcs;
- this->data = crtc_ids + crtc;
- return crtc < partition->crtcs_available ? 0 : LIBGAMMA_NO_SUCH_CRTC;
-}
-
-
-/**
- * Release all resources held by a CRTC state
- *
- * @param this The CRTC state
- */
-void
-libgamma_x_randr_crtc_destroy(libgamma_crtc_state_t *restrict this)
-{
- (void) this;
-}
-
-
-/**
- * Restore the gamma ramps for a CRTC to the system settings for that CRTC
- *
- * @param this The CRTC state
- * @return Zero on success, otherwise (negative) the value of an
- * error identifier provided by this library
- */
-int
-libgamma_x_randr_crtc_restore(libgamma_crtc_state_t *restrict this)
-{
- (void) this;
- errno = ENOTSUP;
- return LIBGAMMA_ERRNO_SET;
-}
-
-
-
-/**
- * Get the gamma ramp size of a CRTC
- *
- * @param this Instance of a data structure to fill with the information about the CRTC
- * @param crtc The state of the CRTC whose information should be read
- * @return Non-zero on error
- */
-static int
-get_gamma_ramp_size(libgamma_crtc_information_t *restrict out, libgamma_crtc_state_t *restrict crtc)
-{
- xcb_connection_t *restrict connection = crtc->partition->site->data;
- xcb_randr_crtc_t *restrict crtc_id = crtc->data;
- xcb_randr_get_crtc_gamma_size_cookie_t cookie;
- xcb_randr_get_crtc_gamma_size_reply_t *restrict reply;
- xcb_generic_error_t *error;
-
- /* Query gamma ramp size */
- out->gamma_size_error = 0;
- cookie = xcb_randr_get_crtc_gamma_size(connection, *crtc_id);
- reply = xcb_randr_get_crtc_gamma_size_reply(connection, cookie, &error);
- if (error) {
- out->gamma_size_error = translate_error(error->error_code, LIBGAMMA_GAMMA_RAMPS_SIZE_QUERY_FAILED, 1);
- return out->gamma_size_error;
- }
- /* Sanity check gamma ramp size */
- if (reply->size < 2)
- out->gamma_size_error = LIBGAMMA_SINGLETON_GAMMA_RAMP;
- /* Store gamma ramp size */
- out->red_gamma_size = out->green_gamma_size = out->blue_gamma_size = reply->size;
- /* Release resources and return successfulnes */
- free(reply);
- return out->gamma_size_error;
-}
-
-
-/**
- * Read information from the CRTC's output
- *
- * @param out Instance of a data structure to fill with the information about the CRTC
- * @param output The CRTC's output information
- * @return Non-zero if at least on error occured
- */
-static int
-read_output_data(libgamma_crtc_information_t *restrict out, xcb_randr_get_output_info_reply_t *restrict output)
-{
-#define __select(value)\
- case XCB_RENDER_SUB_PIXEL_##value: out->subpixel_order = LIBGAMMA_SUBPIXEL_ORDER_##value; break
-
- switch (output->connection) {
- case XCB_RANDR_CONNECTION_CONNECTED:
- /* We have a monitor connected, report that and store information that is provided to us */
- out->active = 1;
- out->width_mm = output->mm_width;
- out->height_mm = output->mm_height;
- switch (output->subpixel_order) {
- __select (UNKNOWN);
- __select (HORIZONTAL_RGB);
- __select (HORIZONTAL_BGR);
- __select (VERTICAL_RGB);
- __select (VERTICAL_BGR);
- __select (NONE);
- default:
- out->subpixel_order_error = LIBGAMMA_SUBPIXEL_ORDER_NOT_RECOGNISED;
- break;
- }
- return 0;
-
- case XCB_RANDR_CONNECTION_UNKNOWN:
- /* If we do know whether a monitor is connected report that and assume it is not */
- out->active_error = LIBGAMMA_STATE_UNKNOWN;
- /* fall through */
- default:
- /* If no monitor is connected, report that on fails that require it */
- out->width_mm_error = LIBGAMMA_NOT_CONNECTED;
- out->height_mm_error = LIBGAMMA_NOT_CONNECTED;
- out->subpixel_order_error = LIBGAMMA_NOT_CONNECTED;
- /* And store that we are not connected */
- out->active = 0;
- /* This fuction only failed if we could not figure out whether a monitor is connected */
- return output->connection != XCB_RANDR_CONNECTION_UNKNOWN ? 0 : -1;
- }
-
-#undef __select
-}
-
-
-/**
- * Determine the connector type from the connector name
- *
- * @param this The CRTC information to use and extend
- * @param Non-zero on error
- */
-static int
-get_connector_type(libgamma_crtc_information_t *restrict this)
-{
- /* Since we require the name of the output of get the type of the connected,
- copy any reported error on the output's name to the connector's type,
- and report failure if there was an error */
- if ((this->connector_type_error = this->connector_name_error))
- return -1;
-
-#define __select(name, type)\
- do {\
- if (strstr(this->connector_name, name "-") == this->connector_name) {\
- this->connector_type = LIBGAMMA_CONNECTOR_TYPE_##type\
- return 0;\
- }\
- } while (0)
-
- /* Check begin on the name of the output to find out what type the connector is of */
- __select ("None", Unknown);
- __select ("VGA", VGA);
- __select ("DVI-I", DVII);
- __select ("DVI-D", DVID);
- __select ("DVI-A", DVIA);
- __select ("DVI", DVI);
- __select ("Composite", Composite);
- __select ("S-Video", SVIDEO);
- __select ("Component", Component);
- __select ("LFP", LFP);
- __select ("Proprietary", Unknown);
- __select ("HDMI", HDMI);
- __select ("DisplayPort", DisplayPort);
-
-#undef __select
-
- /* If there was no matching output name pattern report that and exit with an error */
- this->connector_name_error = LIBGAMMA_CONNECTOR_TYPE_NOT_RECOGNISED;
- return -1;
-}
-
-
-/**
- * Get the output name of a CRTC
- *
- * @param this Instance of a data structure to fill with the information about the CRTC
- * @param output The CRTC's output's information
- * @return Non-zero on error
- */
-static int
-get_output_name(libgamma_crtc_information_t *restrict out, xcb_randr_get_output_info_reply_t *restrict output)
-{
- char *restrict store;
- uint8_t *restrict name;
- uint16_t length;
- size_t i;
-
- /* Get the name of the output and the length of that name */
- name = xcb_randr_get_output_info_name(output);
- length = output->name_len; /* There is no NUL-termination */
- if (!name)
- return out->connector_name_error = LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED;
-
- /* Allocate a memory area for a NUL-terminated copy of the name */
- store = out->connector_name = malloc(((size_t)length + 1) * sizeof(char));
- if (!store) {
- out->connector_name_error = errno;
- return -1;
- }
-
- /* char is guaranteed to be (u)int_least8_t, but it is only guaranteed to be (u)int8_t
- * on POSIX, so to be truly portable we will not assume that char is (u)int8_t */
- for (i = 0; i < (size_t)length; i++)
- store[i] = (char)name[i];
- store[length] = '\0';
-
- return 0;
-}
-
-
-/**
- * Get the Extended Display Information Data of the monitor connected to the connector of a CRTC
- *
- * @param out Instance of a data structure to fill with the information about the CRTC
- * @param crtc The state of the CRTC whose information should be read
- * @param output The CRTC's output
- * @return Non-zero on error
- */
-static int
-get_edid(libgamma_crtc_information_t *restrict out, libgamma_crtc_state_t *restrict crtc, xcb_randr_output_t output)
-{
- xcb_connection_t *restrict connection = crtc->partition->site->data;
- xcb_randr_list_output_properties_cookie_t prop_cookie;
- xcb_randr_list_output_properties_reply_t *restrict prop_reply;
- xcb_atom_t *atoms;
- xcb_atom_t *atoms_end;
- xcb_generic_error_t *error;
- xcb_get_atom_name_cookie_t atom_name_cookie;
- xcb_get_atom_name_reply_t *restrict atom_name_reply;
- char *restrict atom_name;
- int atom_name_len;
- xcb_randr_get_output_property_cookie_t atom_cookie;
- xcb_randr_get_output_property_reply_t *restrict atom_reply;
- unsigned char* restrict atom_data;
- int length;
-
- /* Acquire a list of all properties of the output */
- prop_cookie = xcb_randr_list_output_properties(connection, output);
- prop_reply = xcb_randr_list_output_properties_reply(connection, prop_cookie, &error);
- if (error)
- return out->edid_error = translate_error(error->error_code, LIBGAMMA_LIST_PROPERTIES_FAILED, 1);
-
- /* Extract the properties form the data structure that holds them, */
- atoms = xcb_randr_list_output_properties_atoms(prop_reply);
- /* and get the last one so that we can iterate over them nicely */
- atoms_end = atoms + xcb_randr_list_output_properties_atoms_length(prop_reply);
-
- if (!atoms) {
- free(prop_reply);
- return out->edid_error = LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED;
- }
-
- /* For each property */
- for (; atoms != atoms_end; atoms++) {
- /* Acquire the atom name */
- atom_name_cookie = xcb_get_atom_name(connection, *atoms);
- atom_name_reply = xcb_get_atom_name_reply(connection, atom_name_cookie, &error);
- if (error)
- continue;
-
- /* Extract the atom name from the data structure that holds it */
- atom_name = xcb_get_atom_name_name(atom_name_reply);
- /* As well as the length of the name; it is not NUL-termianted */
- atom_name_len = xcb_get_atom_name_name_length(atom_name_reply);
-
- if (/* Check for errors */
- !atom_name || /* atom_name_len < 1 || */
- /* Check that the length is the expected length for the EDID property */
- atom_name_len != 4 ||
- /* Check that the property is the EDID property */
- atom_name[0] != 'E' || atom_name[1] != 'D' || atom_name[2] != 'I' || atom_name[3] != 'D') {
- free(atom_name_reply);
- continue;
- }
-
- /* Acquire the property's value, we know that it is either 128 or 256 byte long */
- atom_cookie = xcb_randr_get_output_property(connection, output, *atoms, XCB_GET_PROPERTY_TYPE_ANY, 0, 256, 0, 0);
- atom_reply = xcb_randr_get_output_property_reply(connection, atom_cookie, &error);
- /* (*) EDID version 1.0 through 1.4 define it as 128 bytes long,
- * but version 2.0 define it as 256 bytes long. However,
- * version 2.0 is rare(?) and has been deprecated and replaced
- * by version 1.3 (I guess that is with a new version epoch,
- * but I do not know.) */
- if (error) {
- free(atom_name_reply);
- free(prop_reply);
- return out->edid_error = LIBGAMMA_PROPERTY_VALUE_QUERY_FAILED;
- }
-
- /* Extract the property's value */
- atom_data = xcb_randr_get_output_property_data(atom_reply);
- /* and its actual length */
- length = xcb_randr_get_output_property_data_length(atom_reply);
- if (!atom_data || length < 1) {
- free(atom_reply);
- free(atom_name_reply);
- free(prop_reply);
- return out->edid_error = LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED;
- }
-
- /* Store the EDID */
- out->edid_length = (size_t)length;
- out->edid = malloc((size_t)length * sizeof(unsigned char));
- if (!out->edid)
- out->edid_error = errno;
- else
- memcpy(out->edid, atom_data, (size_t)length * sizeof(unsigned char));
-
- /* Release resouces */
- free(atom_reply);
- free(atom_name_reply);
- free(prop_reply);
-
- return out->edid_error;
- }
-
- return out->edid_error = LIBGAMMA_EDID_NOT_FOUND;
-}
-
-
-/**
- * Read information about a CRTC
- *
- * @param this Instance of a data structure to fill with the information about the CRTC
- * @param crtc The state of the CRTC whose information should be read
- * @param fields OR:ed identifiers for the information about the CRTC that should be read
- * @return Zero on success, -1 on error; on error refer to the error reports in `this`
- */
-int
-libgamma_x_randr_get_crtc_information(libgamma_crtc_information_t *restrict this,
- libgamma_crtc_state_t *restrict crtc, int32_t fields)
-{
-#define _E(FIELD) ((fields & FIELD) ? LIBGAMMA_CRTC_INFO_NOT_SUPPORTED : 0)
-
- int e = 0;
- xcb_randr_get_output_info_reply_t *restrict output_info = NULL;
- xcb_randr_output_t output;
- int free_edid, free_name;
- xcb_connection_t *restrict connection;
- libgamma_x_randr_partition_data_t *restrict screen_data;
- size_t output_index;
- xcb_randr_get_output_info_cookie_t cookie;
- xcb_generic_error_t *error;
-
- /* Wipe all error indicators */
- memset(this, 0, sizeof(libgamma_crtc_information_t));
-
- /* We need to free the EDID after us if it is not explicitly requested */
- free_edid = !(fields & LIBGAMMA_CRTC_INFO_EDID);
-
- /* We need to free the output's name after us if it is not explicitly requested */
- free_name = !(fields & LIBGAMMA_CRTC_INFO_CONNECTOR_NAME);
-
- /* Jump if the output information is not required */
- if (!(fields & (LIBGAMMA_CRTC_INFO_MACRO_ACTIVE | LIBGAMMA_CRTC_INFO_MACRO_CONNECTOR)))
- goto cont;
-
- /* Get connector and connector information */
- connection = crtc->partition->site->data;
- screen_data = crtc->partition->data;
- output_index = screen_data->crtc_to_output[crtc->crtc];
- /* `SIZE_MAX` is used for CRTC:s that misses mapping to its output (should not happen),
- * because `SIZE_MAX - 1` is the highest theoretical possible value */
- if (output_index == SIZE_MAX) {
- e |= this->edid_error = this->gamma_error = this->width_mm_edid_error
- = this->height_mm_edid_error = this->connector_type_error
- = this->connector_name_error = this->subpixel_order_error
- = this->width_mm_error = this->height_mm_error
- = this->active_error = LIBGAMMA_CONNECTOR_UNKNOWN;
- goto cont;
- }
- /* Get the output */
- output = screen_data->outputs[output_index];
- /* Query output information */
- cookie = xcb_randr_get_output_info(connection, output, screen_data->config_timestamp);
- output_info = xcb_randr_get_output_info_reply(connection, cookie, &error);
- if (error) {
- e |= this->edid_error = this->gamma_error = this->width_mm_edid_error
- = this->height_mm_edid_error = this->connector_type_error
- = this->connector_name_error = this->subpixel_order_error
- = this->width_mm_error = this->height_mm_error
- = this->active_error = LIBGAMMA_OUTPUT_INFORMATION_QUERY_FAILED;
- goto cont;
- }
-
- /* Get connector name */
- e |= get_output_name(this, output_info);
- /* Get connector type */
- if (fields & LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE)
- e |= get_connector_type(this);
- /* Get additional output data, excluding EDID */
- e |= read_output_data(this, output_info);
- if (fields & LIBGAMMA_CRTC_INFO_MACRO_VIEWPORT)
- e |= this->width_mm_error | this->height_mm_error;
- e |= (fields & LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER) ? this->subpixel_order_error : 0;
-
- /* If we do not want any EDID information, jump */
- if (!(fields & LIBGAMMA_CRTC_INFO_MACRO_EDID))
- goto cont;
- /* If there is not monitor that report error in EDID related fields */
- if (!this->active) {
- e |= this->edid_error = this->gamma_error = this->width_mm_edid_error
- = this->height_mm_edid_error = LIBGAMMA_NOT_CONNECTED;
- goto cont;
- }
- /* Get EDID */
- e |= get_edid(this, crtc, output);
- if (!this->edid) {
- this->gamma_error = this->width_mm_edid_error = this->height_mm_edid_error = this->edid_error;
- goto cont;
- }
- /* Parse EDID */
- if ((fields & (LIBGAMMA_CRTC_INFO_MACRO_EDID ^ LIBGAMMA_CRTC_INFO_EDID)))
- e |= libgamma_internal_parse_edid(this, fields);
-
-cont:
- /* Get gamma ramp size */
- e |= (fields & LIBGAMMA_CRTC_INFO_GAMMA_SIZE) ? get_gamma_ramp_size(this, crtc) : 0;
- /* Store gamma ramp depth. */
- this->gamma_depth = 16;
- /* X RandR does not support quering gamma ramp support. */
- e |= this->gamma_support_error = _E(LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT);
-
- /* Free the EDID after us */
- if (free_edid) {
- free(this->edid);
- this->edid = NULL;
- }
- /* Free the output name after us */
- if (free_name) {
- free(this->connector_name);
- this->connector_name = NULL;
- }
-
- free(output_info);
- return e ? -1 : 0;
-
-#undef _E
-}
-
-
-/**
- * Get the current gamma ramps for a CRTC, 16-bit gamma-depth version
- *
- * @param this The CRTC state
- * @param ramps The gamma ramps to fill with the current values
- * @return Zero on success, otherwise (negative) the value of an
- * error identifier provided by this library
- */
-int
-libgamma_x_randr_crtc_get_gamma_ramps16(libgamma_crtc_state_t *restrict this, libgamma_gamma_ramps16_t *restrict ramps)
-{
- xcb_connection_t *restrict connection = this->partition->site->data;
- xcb_randr_get_crtc_gamma_cookie_t cookie;
- xcb_randr_get_crtc_gamma_reply_t *restrict reply;
- xcb_generic_error_t *error;
- uint16_t *restrict red;
- uint16_t *restrict green;
- uint16_t *restrict blue;
-
-#ifdef DEBUG
- /* Gamma ramp sizes are identical but not fixed */
- if (ramps->red_size != ramps->green_size || ramps->red_size != ramps->blue_size)
- return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE;
-#endif
-
- /* Read current gamma ramps */
- cookie = xcb_randr_get_crtc_gamma(connection, *(xcb_randr_crtc_t *)this->data);
- reply = xcb_randr_get_crtc_gamma_reply(connection, cookie, &error);
-
- /* Check for errors */
- if (error)
- return translate_error(error->error_code, LIBGAMMA_GAMMA_RAMP_READ_FAILED, 0);
-
- /* Get current gamma ramps from response */
- red = xcb_randr_get_crtc_gamma_red(reply);
- green = xcb_randr_get_crtc_gamma_green(reply);
- blue = xcb_randr_get_crtc_gamma_blue(reply);
- /* Copy over the gamma ramps to our memory */
- memcpy(ramps->red, red, ramps->red_size * sizeof(uint16_t));
- memcpy(ramps->green, green, ramps->green_size * sizeof(uint16_t));
- memcpy(ramps->blue, blue, ramps->blue_size * sizeof(uint16_t));
-
- free(reply);
- return 0;
-}
-
-
-/**
- * Set the gamma ramps for a CRTC, 16-bit gamma-depth version
- *
- * @param this The CRTC state
- * @param ramps The gamma ramps to apply
- * @return Zero on success, otherwise (negative) the value of an
- * error identifier provided by this library
- */
-int
-libgamma_x_randr_crtc_set_gamma_ramps16(libgamma_crtc_state_t *restrict this, libgamma_gamma_ramps16_t ramps)
-{
- xcb_connection_t *restrict connection = this->partition->site->data;
- xcb_void_cookie_t cookie;
- xcb_generic_error_t *restrict error;
-#ifdef DEBUG
- /* Gamma ramp sizes are identical but not fixed */
- if (ramps.red_size != ramps.green_size || ramps.red_size != ramps.blue_size)
- return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE;
-#endif
-
- /* Apply gamma ramps */
- cookie = xcb_randr_set_crtc_gamma_checked(connection, *(xcb_randr_crtc_t*)this->data,
- (uint16_t)ramps.red_size, ramps.red, ramps.green, ramps.blue);
- /* Check for errors */
- error = xcb_request_check(connection, cookie);
- if (error)
- return translate_error(error->error_code, LIBGAMMA_GAMMA_RAMP_WRITE_FAILED, 0);
- return 0;
-}
-
-
-#ifdef __GNUC__
-# pragma GCC diagnostic pop
-#endif