From 0ce0d8d6e0c420ccafa79e0203b928c3559a4311 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Fri, 5 Mar 2021 00:43:38 +0100 Subject: Split source files, merge public header files, eliminite use gpp, rewrite makefile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- gamma-linux-drm.c | 902 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 902 insertions(+) create mode 100644 gamma-linux-drm.c (limited to 'gamma-linux-drm.c') diff --git a/gamma-linux-drm.c b/gamma-linux-drm.c new file mode 100644 index 0000000..25a5861 --- /dev/null +++ b/gamma-linux-drm.c @@ -0,0 +1,902 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef HAVE_LIBGAMMA_METHOD_LINUX_DRM +# error Compiling gamma-linux-drm.c without HAVE_LIBGAMMA_METHOD_LINUX_DRM +#endif + +#define _GNU_SOURCE + +#include "gamma-linux-drm.h" + +#include "libgamma-error.h" +#include "edid.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef O_CLOEXEC +# define O_CLOEXEC 02000000 +#endif +#ifndef NGROUPS_MAX +# define NGROUPS_MAX 65536 +#endif +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + + + +/** + * Graphics card data for the Direct Rendering Manager adjustment method + */ +typedef struct libgamma_drm_card_data { + /** + * File descriptor for the connection to the graphics card + */ + int fd; + + /** + * The graphics card's mode resources + */ + drmModeRes *res; + + /** + * Resources for open connectors + */ + drmModeConnector **connectors; + + /** + * Resources for open encoders + */ + drmModeEncoder **encoders; + +} libgamma_drm_card_data_t; + + + +/** + * Return the capabilities of the adjustment method + * + * @param this The data structure to fill with the method's capabilities + */ +void libgamma_linux_drm_method_capabilities(libgamma_method_capabilities_t *restrict this) +{ + /* Support for all information except gamma ramp support */ + 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_ACTIVE + | LIBGAMMA_CRTC_INFO_MACRO_CONNECTOR; + /* DRM supports multiple partitions and CRTC:s but not sites */ + this->default_site_known = 1; + this->multiple_sites = 0; + this->multiple_partitions = 1; + this->multiple_crtcs = 1; + /* Partitions are graphics cards in DRM */ + this->partitions_are_graphics_cards = 1; + /* Linux 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; + /* DRM is a real non-faked adjustment method */ + this->real = 1; + this->fake = 0; + /* Gamma ramp adjustments are persistent */ + this->auto_restore = 0; +} + + +/** + * 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_linux_drm_site_initialise(libgamma_site_state_t *restrict this, char *restrict site) +{ + char pathname[PATH_MAX]; + struct stat _attr; + + if (site) + return LIBGAMMA_NO_SUCH_SITE; + + /* Count the number of available graphics cards by + `stat`:ing their existence in an API filesystem */ + this->partitions_available = 0; + for (;;) { + /* Construct pathname of graphics card device */ + snprintf(pathname, sizeof(pathname) / sizeof(char), + DRM_DEV_NAME, DRM_DIR_NAME, (int)(this->partitions_available)); + /* `stat` the graphics card's existence */ + if (stat(pathname, &_attr)) + break; + /* Move on to next graphics card */ + if (this->partitions_available++ > INT_MAX) + return LIBGAMMA_IMPOSSIBLE_AMOUNT; + } + return 0; +} + + +/** + * Release all resources held by a site state + * + * @param this The site state + */ +void +libgamma_linux_drm_site_destroy(libgamma_site_state_t *restrict this) +{ + (void) this; +} + + +/** + * 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_linux_drm_site_restore(libgamma_site_state_t *restrict this) +{ + (void) this; + errno = ENOTSUP; + return LIBGAMMA_ERRNO_SET; +} + + +/** + * Figure out why `open` failed for a graphics card + * + * @param pathname The pathname of the error card + * @return The error code to report + */ +static int +figure_out_card_open_error(const char *pathname) +{ + gid_t supplemental_groups[NGROUPS_MAX]; + struct group *group; + struct stat attr; + int i, n; + + + /* Check which the device exists */ + if (errno == ENXIO || errno == ENODEV) + return LIBGAMMA_NO_SUCH_PARTITION; + + + /* If we did not get access permission, figure out why */ + + if (errno != EACCES) { + /* If we could not figure out what went + * wrong, just return the error we got */ + return LIBGAMMA_ERRNO_SET; + } + +#define __test(R, W) ((attr.st_mode & ((R) | (W))) == ((R) | (W))) + + /* Get permission requirement for the file */ + if (stat(pathname, &attr)) + return errno == EACCES ? LIBGAMMA_NO_SUCH_PARTITION : LIBGAMMA_ERRNO_SET; + + /* Test owner's, group's and others' permissions */ + if ((attr.st_uid == geteuid() && __test(S_IRUSR, S_IWUSR)) || + (attr.st_gid == getegid() && __test(S_IRGRP, S_IWGRP)) || __test(S_IROTH, S_IWOTH)) + return LIBGAMMA_DEVICE_ACCESS_FAILED; + + /* The group should be "video", but perhaps + it is "root" to restrict users */ + if (!attr.st_gid /* root group */ || __test(S_IRGRP, S_IWGRP)) + return LIBGAMMA_DEVICE_RESTRICTED; + + + /* Get the user's supplemental group membership list */ + n = getgroups(NGROUPS_MAX, supplemental_groups); + if (n < 0) + return LIBGAMMA_ERRNO_SET; + + /* Test whether any of the supplemental + group should be satisfactory */ + for (i = 0; i < n; i++) + if (supplemental_groups[i] == attr.st_gid) + break; + + /* If one of the supplemental groups should be satisfactory, + then we do not know anything more than that access failed */ + if (i != n) + return LIBGAMMA_DEVICE_ACCESS_FAILED; + + /* Otherwise, try to get the name of the group that is + required and report the missing group membership */ + { + static __thread char buf[1024]; /* My output of `sysconf(_SC_GETGR_R_SIZE_MAX)`. */ + struct group _grp; + + errno = getgrgid_r(attr.st_gid, &_grp, buf, sizeof(buf) / sizeof(char), &group); + if (errno == ERANGE) + { + /* The lenght of the group's name is absurdly long, degrade to thread-unsafe. */ + errno = 0; + group = getgrgid(attr.st_gid); + } + else if (errno) + return LIBGAMMA_ERRNO_SET; + } + + libgamma_group_gid = attr.st_gid; + libgamma_group_name = group ? group->gr_name : NULL; + return LIBGAMMA_DEVICE_REQUIRE_GROUP; +#undef __test +} + + +/** + * 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_linux_drm_partition_initialise(libgamma_partition_state_t *restrict this, + libgamma_site_state_t *restrict site, size_t partition) +{ + int rc = 0; + libgamma_drm_card_data_t *restrict data; + char pathname[PATH_MAX]; + + (void) site; + + /* Check for partition index overflow */ + if (partition > INT_MAX) + return LIBGAMMA_NO_SUCH_PARTITION; + + /* Allocate and initialise graphics card data */ + this->data = NULL; + data = malloc(sizeof(libgamma_drm_card_data_t)); + if (!data) + return LIBGAMMA_ERRNO_SET; + data->fd = -1; + data->res = NULL; + data->encoders = NULL; + data->connectors = NULL; + + /* Get the pathname for the graphics card */ + snprintf(pathname, sizeof(pathname) / sizeof(char), DRM_DEV_NAME, DRM_DIR_NAME, (int)partition); + + /* Acquire access to the graphics card */ + data->fd = open(pathname, O_RDWR | O_CLOEXEC); + if (data->fd < 0) { + rc = figure_out_card_open_error(pathname); + goto fail_data; + } + + /* Acquire mode resources */ + data->res = drmModeGetResources(data->fd); + if (!data->res) { + rc = LIBGAMMA_ACQUIRING_MODE_RESOURCES_FAILED; + goto fail_fd; + } + + /* Get the number of CRTC:s that are available in the partition */ + if (data->res->count_crtcs < 0) { + rc = LIBGAMMA_NEGATIVE_CRTC_COUNT; + goto fail_res; + } + this->crtcs_available = (size_t)data->res->count_crtcs; + this->data = data; + return 0; + +fail_res: + drmModeFreeResources(data->res); +fail_fd: + close(data->fd); +fail_data: + free(data); + return rc; +} + + +/** + * Release all connectors and encoders + * + * @param this The graphics card data + */ +static void +release_connectors_and_encoders(libgamma_drm_card_data_t *restrict this) +{ + size_t i, n; + + /* Release individual encoders */ + if (this->encoders) + for (i = 0, n = (size_t)(this->res->count_connectors); i < n; i++) + if (this->encoders[i]) + drmModeFreeEncoder(this->encoders[i]); + /* Release encoder array */ + free(this->encoders); + this->encoders = NULL; + + /* Release individual connectors */ + if (this->connectors) + for (i = 0, n = (size_t)(this->res->count_connectors); i < n; i++) + if (this->connectors[i]) + drmModeFreeConnector(this->connectors[i]); + /* Release connector array */ + free(this->connectors); + this->connectors = NULL; +} + + +/** + * Release all resources held by a partition state + * + * @param this The partition state + */ +void +libgamma_linux_drm_partition_destroy(libgamma_partition_state_t *restrict this) +{ + libgamma_drm_card_data_t *restrict data = this->data; + release_connectors_and_encoders(data); + if (data->res) + drmModeFreeResources(data->res); + if (data->fd >= 0) + close(data->fd); + 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_linux_drm_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_linux_drm_crtc_initialise(libgamma_crtc_state_t *restrict this, + libgamma_partition_state_t *restrict partition, size_t crtc) +{ + libgamma_drm_card_data_t *restrict card = partition->data; + + if (crtc >= partition->crtcs_available) + return LIBGAMMA_NO_SUCH_CRTC; + this->data = (void*)(size_t)card->res->crtcs[crtc]; + return 0; +} + + +/** + * Release all resources held by a CRTC state + * + * @param this The CRTC state + */ +void +libgamma_linux_drm_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_linux_drm_crtc_restore(libgamma_crtc_state_t *restrict this) +{ + (void) this; + errno = ENOTSUP; + return LIBGAMMA_ERRNO_SET; +} + + +/** + * Find the connector that a CRTC belongs to + * + * @param this The CRTC state + * @param error Output of the error value to store of error report + * fields for data that requires the connector +'* @return The CRTC's conncetor, `NULL` on error + */ +static drmModeConnector * +find_connector(libgamma_crtc_state_t *restrict this, int *restrict error) +{ + uint32_t crtc_id = (uint32_t)(size_t)this->data; + libgamma_drm_card_data_t *restrict card = this->partition->data; + size_t i, n = (size_t)card->res->count_connectors; + /* Open connectors and encoders if not already opened */ + if (!card->connectors) { + /* Allocate connector and encoder arrays; we use `calloc` + so all non-loaded elements are `NULL` after an error */ + card->connectors = calloc(n, sizeof(drmModeConnector *)); + if (!card->connectors) + goto fail; + card->encoders = calloc(n, sizeof(drmModeEncoder *)); + if (!card->encoders) + goto fail; + /* Fill connector and encoder arrays */ + for (i = 0; i < n; i++) { + /* Get connector */ + card->connectors[i] = drmModeGetConnector(card->fd, card->res->connectors[i]); + if (!card->connectors[i]) + goto fail; + /* Get encoder if the connector is enabled. If it is disabled it + will not have an encoder, which is indicated by the encoder + ID being 0. In such case, leave the encoder to be `NULL`. */ + if (card->connectors[i]->encoder_id) { + card->encoders[i] = drmModeGetEncoder(card->fd, card->connectors[i]->encoder_id); + if (!card->encoders[i]) + goto fail; + } + } + } + /* No error has occurred yet */ + *error = 0; + /* Find connector */ + for (i = 0; i < n; i++) + if (card->encoders[i] && card->connectors[i] && card->encoders[i]->crtc_id == crtc_id) + return card->connectors[i]; + /* We did not find the connector */ + *error = LIBGAMMA_CONNECTOR_UNKNOWN; + return NULL; + +fail: + /* Report the error that got us here, release + resouces and exit with `NULL` for failure */ + *error = errno; + release_connectors_and_encoders(card); + return NULL; +} + + +/** + * Get the size of the gamma ramps for 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 + * @return The value stored in `out->gamma_size_error` + */ +static int +get_gamma_ramp_size(libgamma_crtc_information_t *restrict out, const libgamma_crtc_state_t *restrict crtc) +{ + libgamma_drm_card_data_t *restrict card = crtc->partition->data; + uint32_t crtc_id = card->res->crtcs[crtc->crtc]; + drmModeCrtc *restrict crtc_info; + /* Get CRTC information */ + errno = 0; + crtc_info = drmModeGetCrtc(card->fd, crtc_id); + out->gamma_size_error = crtc_info ? 0 : errno; + /* Get gamma ramp size */ + if (!out->gamma_size_error) { + /* Store gamma ramp size */ + out->red_gamma_size = out->green_gamma_size = out->blue_gamma_size = (size_t)crtc_info->gamma_size; + /* Sanity check gamma ramp size */ + out->gamma_size_error = crtc_info->gamma_size < 2 ? LIBGAMMA_SINGLETON_GAMMA_RAMP : 0; + /* Release CRTC information */ + drmModeFreeCrtc(crtc_info); + } + return out->gamma_size_error; +} + + +/** + * Get the a monitor's subpixel order + * + * @param out Instance of a data structure to fill with the information about the CRTC + * @param connector The connector + */ +static void +get_subpixel_order(libgamma_crtc_information_t *restrict out, const drmModeConnector *restrict connector) +{ +#define __select(value)\ + case DRM_MODE_SUBPIXEL_##value:\ + out->subpixel_order = LIBGAMMA_SUBPIXEL_ORDER_##value;\ + break + + switch (connector->subpixel) { + __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; + } + +#undef __select +} + + +/** + * Get a connector's type + * + * @param out Instance of a data structure to fill with the information about the CRTC + * @param connector The connector + * @param connector_name_base Output for the basename of the connector + */ +static void +get_connector_type(libgamma_crtc_information_t *restrict out, const drmModeConnector *restrict connector, + const char **restrict connector_name_base) +{ +#define __select(type, name)\ + case DRM_MODE_CONNECTOR_##type:\ + out->connector_type = LIBGAMMA_CONNECTOR_TYPE_##type;\ + *connector_name_base = name;\ + break + + /* These may not have been included by , + but they should be available. Becuase we define them + outself, it is best to test them last. */ +#ifndef DRM_MODE_CONNECTOR_VIRTUAL +# define DRM_MODE_CONNECTOR_VIRTUAL 15 +#endif +#ifndef DRM_MODE_CONNECTOR_DSI +# define DRM_MODE_CONNECTOR_DSI 16 +#endif + + /* Translate connector type from DRM to libgamma + and store connector basename */ + switch (connector->connector_type) { + __select (Unknown, "Unknown" ); + __select (VGA, "VGA" ); + __select (DVII, "DVI-I" ); + __select (DVID, "DVI-D" ); + __select (DVIA, "DVI-A" ); + __select (Composite, "Composite"); + __select (SVIDEO, "SVIDEO" ); + __select (LVDS, "LVDS" ); + __select (Component, "Component"); + __select (9PinDIN, "DIN" ); + __select (DisplayPort, "DP" ); + __select (HDMIA, "HDMI-A" ); + __select (HDMIB, "HDMI-B" ); + __select (TV, "TV" ); + __select (eDP, "eDP" ); + __select (VIRTUAL, "VIRTUAL" ); + __select (DSI, "DSI" ); + default: + out->connector_type_error = LIBGAMMA_CONNECTOR_TYPE_NOT_RECOGNISED; + out->connector_name_error = LIBGAMMA_CONNECTOR_TYPE_NOT_RECOGNISED; + break; + } + +#undef __select +} + + +/** + * Read information from the CRTC's conncetor + * + * @param crtc The state of the CRTC whose information should be read + * @param out Instance of a data structure to fill with the information about the CRTC + * @param connector The CRTC's connector + * @param fields OR:ed identifiers for the information about the CRTC that should be read + * @return Non-zero if at least on error occured + */ +static int +read_connector_data(libgamma_crtc_state_t *restrict crtc, libgamma_crtc_information_t *restrict out, + const drmModeConnector *restrict connector, int32_t fields) +{ + const char *connector_name_base = NULL; + libgamma_drm_card_data_t *restrict card; + uint32_t type; + size_t i, n, c; + + /* Get some information that does not require too much work */ + if (fields & (LIBGAMMA_CRTC_INFO_MACRO_ACTIVE | LIBGAMMA_CRTC_INFO_MACRO_CONNECTOR)) { + /* Get whether or not a monitor is plugged in */ + out->active = connector->connection == DRM_MODE_CONNECTED; + out->active_error = connector->connection == DRM_MODE_UNKNOWNCONNECTION ? LIBGAMMA_STATE_UNKNOWN : 0; + if (!out->active) { + if (fields & (LIBGAMMA_CRTC_INFO_MACRO_VIEWPORT | LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER)) + out->width_mm_error = out->height_mm_error = out->subpixel_order_error = LIBGAMMA_NOT_CONNECTED; + goto not_connected; + } + + /* Get viewport dimension */ + out->width_mm = connector->mmWidth; + out->height_mm = connector->mmHeight; + + /* Get subpixel order */ + get_subpixel_order(out, connector); + + not_connected: + + /* Get connector type */ + get_connector_type(out, connector, &connector_name_base); + } + + /* Get the connector's name */ + if ((fields & LIBGAMMA_CRTC_INFO_CONNECTOR_NAME) && !out->connector_name_error) { + card = crtc->partition->data; + type = connector->connector_type; + n = (size_t)card->res->count_connectors; + + /* Allocate memory for the name of the connector */ + out->connector_name = malloc((strlen(connector_name_base) + 12) * sizeof(char)); + if (!out->connector_name) + return (out->connector_name_error = errno); + + /* Get the number of connectors with the same type on the same graphics card */ + for (i = c = 0; i < n && card->connectors[i] != connector; i++) + if (card->connectors[i]->connector_type == type) + c++; + + /* Construct and store connect name that is unique to the graphics card */ + sprintf(out->connector_name, "%s-%" PRIu32, connector_name_base, (uint32_t)(c + 1)); + } + + /* Did something go wrong? */ + return out->subpixel_order_error | out->active_error | out->connector_name_error; +} + + +/** + * Get the extended display identification data for a monitor + * + * @param crtc The CRTC state + * @param out Instance of a data structure to fill with the information about the CRTC + * @param connector The CRTC's connector + * @reutnr Non-zero on error + */ +static int +get_edid(libgamma_crtc_state_t *restrict crtc, libgamma_crtc_information_t *restrict out, drmModeConnector *connector) +{ + libgamma_drm_card_data_t *restrict card = crtc->partition->data; + int prop_n = connector->count_props; + int prop_i; + drmModePropertyRes *restrict prop; + drmModePropertyBlobRes *restrict blob; + + /* Test all properies on the connector */ + for (prop_i = 0; prop_i < prop_n; prop_i++) { + /* Get output property */ + prop = drmModeGetProperty(card->fd, connector->props[prop_i]); + if (!prop) + continue; + /* Is this property the EDID? */ + if (!strcmp(prop->name, "EDID")) { + /* Get the property value */ + blob = drmModeGetPropertyBlob(card->fd, (uint32_t)connector->prop_values[prop_i]); + if (!blob) { + drmModeFreeProperty(prop); + return (out->edid_error = LIBGAMMA_PROPERTY_VALUE_QUERY_FAILED); + } + if (blob->data) { + /* Get and store the length of the EDID */ + out->edid_length = blob->length; + /* Allocate memory for a copy of the EDID that is under our memory control */ + out->edid = malloc(out->edid_length * sizeof(unsigned char)); + if (!out->edid) { + out->edid_error = errno; + } else { + /* Copy the EDID so we can free resources that got us here */ + memcpy(out->edid, blob->data, (size_t)out->edid_length * sizeof(char)); + } + /* Free the propriety value and the propery */ + drmModeFreePropertyBlob(blob); + drmModeFreeProperty(prop); + /* Were we successful? */ + return !out->edid; + } + /* Free the propriety value */ + drmModeFreePropertyBlob(blob); + } + /* Free the propriety */ + drmModeFreeProperty(prop); + } + /* If we get here, we did not find a EDID */ + 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_linux_drm_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, require_connector, free_edid, error; + drmModeConnector *restrict connector; + + /* 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); + + /* Figure out whether we require the connector to get all information we want */ + require_connector = fields & (LIBGAMMA_CRTC_INFO_MACRO_ACTIVE | LIBGAMMA_CRTC_INFO_MACRO_CONNECTOR); + + /* If we are not interested in the connector or monitor, jump */ + if (!require_connector) + goto cont; + /* Find connector. */ + connector = find_connector(crtc, &error); + if (!connector) { + /* Store reported error in affected fields */ + e |= this->width_mm_error = this->height_mm_error + = this->connector_type_error = this->subpixel_order_error + = this->active_error = this->connector_name_error + = this->edid_error = this->gamma_error + = this->width_mm_edid_error = this->height_mm_edid_error = error; + goto cont; + } + + /* Read connector data and monitor data, excluding EDID */ + e |= read_connector_data(crtc, this, connector, fields); + + /* 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_error || !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(crtc, this, connector); + 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; + /* DRM 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; + } + + 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_linux_drm_crtc_get_gamma_ramps16(libgamma_crtc_state_t *restrict this, libgamma_gamma_ramps16_t *restrict ramps) +{ + libgamma_drm_card_data_t *restrict card = this->partition->data; + int r; +#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 */ + r = drmModeCrtcGetGamma(card->fd, (uint32_t)(size_t)this->data, (uint32_t)ramps->red_size, + ramps->red, ramps->green, ramps->blue); + return r ? LIBGAMMA_GAMMA_RAMP_READ_FAILED : 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_linux_drm_crtc_set_gamma_ramps16(libgamma_crtc_state_t *restrict this, libgamma_gamma_ramps16_t ramps) +{ + libgamma_drm_card_data_t *restrict card = this->partition->data; + int r; +#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 */ + r = drmModeCrtcSetGamma(card->fd, (uint32_t)(size_t)this->data, + (uint32_t)ramps.red_size, ramps.red, ramps.green, ramps.blue); + /* Check for errors */ + if (r) { + switch (errno) { + case EACCES: + case EAGAIN: + case EIO: + /* Permission denied errors must be ignored, because we do not + * have permission to do this while a display server is active. + * We are also checking for some other error codes just in case. */ + case EBUSY: + case EINPROGRESS: + /* It is hard to find documentation for DRM (in fact all of this is + * just based on the functions names and some testing,) perhaps we + * could get this if we are updating to fast. */ + break; + case EBADF: + case ENODEV: + case ENXIO: + /* TODO: I have not actually tested removing my graphics card or, + * monitor but I imagine either of these is what would happen. */ + return LIBGAMMA_GRAPHICS_CARD_REMOVED; + + default: + return LIBGAMMA_ERRNO_SET; + } + } + return 0; +} -- cgit v1.2.3-70-g09d2