From bdeb7f77f16b1e482904866ff21e43f8bfc01bb0 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sat, 31 May 2014 07:32:09 +0200 Subject: move files around MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/lib/gamma-linux-drm.c | 831 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 831 insertions(+) create mode 100644 src/lib/gamma-linux-drm.c (limited to 'src/lib/gamma-linux-drm.c') diff --git a/src/lib/gamma-linux-drm.c b/src/lib/gamma-linux-drm.c new file mode 100644 index 0000000..36ba442 --- /dev/null +++ b/src/lib/gamma-linux-drm.c @@ -0,0 +1,831 @@ +/** + * 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 . + */ +#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) +{ + this->crtc_information = LIBGAMMA_CRTC_INFO_EDID + | LIBGAMMA_CRTC_INFO_WIDTH_MM + | LIBGAMMA_CRTC_INFO_HEIGHT_MM + | LIBGAMMA_CRTC_INFO_WIDTH_MM_EDID + | LIBGAMMA_CRTC_INFO_HEIGHT_MM_EDID + | LIBGAMMA_CRTC_INFO_GAMMA_SIZE + | LIBGAMMA_CRTC_INFO_GAMMA_DEPTH + | LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER + | LIBGAMMA_CRTC_INFO_ACTIVE + | LIBGAMMA_CRTC_INFO_CONNECTOR_NAME + | LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE + | LIBGAMMA_CRTC_INFO_GAMMA; + this->default_site_known = 1; + this->multiple_sites = 0; + this->multiple_partitions = 1; + this->multiple_crtcs = 1; + this->partitions_are_graphics_cards = 1; + this->site_restore = 0; + this->partition_restore = 0; + this->crtc_restore = 0; + this->identical_gamma_sizes = 1; + this->fixed_gamma_size = 0; + this->fixed_gamma_depth = 1; + this->real = 1; + this->fake = 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. One 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 != NULL) + return LIBGAMMA_NO_SUCH_SITE; + + /* Count the number of available graphics cards by + * stat:ing there existence in an API filesystem. */ + this->partitions_available = 0; + for (;;) + { + snprintf(pathname, sizeof(pathname) / sizeof(char), + DRM_DEV_NAME, DRM_DIR_NAME, (int)(this->partitions_available)); + if (stat(pathname, &_attr)) + break; + 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; + return errno = ENOTSUP, 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) +{ + if ((errno == ENXIO) || (errno == ENODEV)) + return LIBGAMMA_NO_SUCH_PARTITION; + + if (errno == EACCES) + { + gid_t supplemental_groups[NGROUPS_MAX]; + struct group* group; + int i, n; + struct stat attr; + int r; + + + r = stat(pathname, &attr); + +#define __test(R, W) ((attr.st_mode & (R | W)) == (R | W)) + if (r) + return errno == EACCES ? LIBGAMMA_NO_SUCH_PARTITION : LIBGAMMA_ERRNO_SET; + + 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; + + if (attr.st_gid == 0 /* root group */ || __test(S_IRGRP, S_IWGRP)) + return LIBGAMMA_DEVICE_RESTRICTED; + + + n = getgroups(NGROUPS_MAX, supplemental_groups); + if (n < 0) + return LIBGAMMA_ERRNO_SET; + + for (i = 0; i < n; i++) + if (supplemental_groups[i] == attr.st_gid) + break; + + if (i != n) + return LIBGAMMA_DEVICE_ACCESS_FAILED; + + errno = 0; + group = getgrgid(attr.st_gid); /* TODO: Not thread-safe. */ + libgamma_group_gid = attr.st_gid; + libgamma_group_name = group != NULL ? group->gr_name : NULL; + return LIBGAMMA_DEVICE_REQUIRE_GROUP; +#undef __test + } + + return LIBGAMMA_ERRNO_SET; +} + + +/** + * 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 == NULL) + return LIBGAMMA_ERRNO_SET; + data->fd = -1; + data->res = 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 == NULL) + { + 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); + 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; + if (this->encoders != NULL) + for (i = 0, n = (size_t)(this->res->count_connectors); i < n; i++) + if (this->encoders[i] != NULL) + drmModeFreeEncoder(this->encoders[i]); + free(this->encoders); + this->encoders = NULL; + + if (this->connectors != NULL) + for (i = 0, n = (size_t)(this->res->count_connectors); i < n; i++) + if (this->connectors[i] != NULL) + drmModeFreeConnector(this->connectors[i]); + 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 != NULL) + 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; + return errno = ENOTSUP, 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; + uint32_t crtc_id; + + if (crtc >= partition->crtcs_available) + return LIBGAMMA_NO_SUCH_CRTC; + + card = partition->data; + crtc_id = card->res->crtcs[crtc]; + this->data = (void*)(size_t)crtc_id; + 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; + return errno = ENOTSUP, 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 == NULL) + { + /* We use calloc so all non-loaded elements are `NULL` after an error. */ + if ((card->connectors = calloc(n, sizeof(drmModeConnector*))) == NULL) + goto fail; + if ((card->encoders = calloc(n, sizeof(drmModeEncoder*))) == NULL) + goto fail; + for (i = 0; i < n; i++) + if (((card->connectors[i] = drmModeGetConnector(card->fd, card->res->connectors[i])) == NULL) || + ((card->encoders[i] = drmModeGetEncoder(card->fd, card->connectors[i]->encoder_id)) == NULL)) + goto fail; + } + /* Find connector. */ + *error = 0; + for (i = 0; i < n; i++) + if (card->encoders[i]->crtc_id == crtc_id) + return card->connectors[i]; + *error = LIBGAMMA_CONNECTOR_UNKNOWN; + return NULL; + + fail: + *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; + errno = 0; + crtc_info = drmModeGetCrtc(card->fd, crtc_id); + out->gamma_size_error = crtc_info == NULL ? errno : 0; + if (out->gamma_size_error == 0) + { + out->red_gamma_size = (size_t)(crtc_info->gamma_size); + out->green_gamma_size = (size_t)(crtc_info->gamma_size); + out->blue_gamma_size = (size_t)(crtc_info->gamma_size); + out->gamma_size_error = crtc_info->gamma_size < 2 ? LIBGAMMA_SINGLETON_GAMMA_RAMP : 0; + 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 + + switch (connector->connector_type) + { +#ifndef DRM_MODE_CONNECTOR_VIRTUAL +# define DRM_MODE_CONNECTOR_VIRTUAL 15 +#endif +#ifndef DRM_MODE_CONNECTOR_DSI +# define DRM_MODE_CONNECTOR_DSI 16 +#endif + __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; + + /* Get some information that does not require too much work. */ + if ((fields & (LIBGAMMA_CRTC_INFO_WIDTH_MM | LIBGAMMA_CRTC_INFO_HEIGHT_MM | LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE | + LIBGAMMA_CRTC_INFO_ACTIVE | LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER))) + { + /* 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 == 0) + { + if ((fields & (LIBGAMMA_CRTC_INFO_WIDTH_MM | LIBGAMMA_CRTC_INFO_HEIGHT_MM | 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 == 0)) + { + libgamma_drm_card_data_t* restrict card = crtc->partition->data; + uint32_t type = connector->connector_type; + size_t i, n = (size_t)(card->res->count_connectors), c = 0; + + out->connector_name = malloc((strlen(connector_name_base) + 12) * sizeof(char)); + if (out->connector_name == NULL) + return (out->connector_name_error = errno); + + for (i = 0; (i < n) && (card->connectors[i] != connector); i++) + if (card->connectors[i]->connector_type == type) + c++; + 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; + + for (prop_i = 0; prop_i < prop_n; prop_i++) + { + prop = drmModeGetProperty(card->fd, connector->props[prop_i]); + if (prop == NULL) + continue; + if (!strcmp("EDID", prop->name)) + { + blob = drmModeGetPropertyBlob(card->fd, (uint32_t)(connector->prop_values[prop_i])); + if (blob == NULL) + { + drmModeFreeProperty(prop); + return out->edid_error = LIBGAMMA_PROPERTY_VALUE_QUERY_FAILED; + } + if (blob->data != NULL) + { + out->edid_length = blob->length; + out->edid = malloc(out->edid_length * sizeof(unsigned char)); + if (out->edid == NULL) + out->edid_error = errno; + else + memcpy(out->edid, blob->data, (size_t)(out->edid_length) * sizeof(char)); + drmModeFreePropertyBlob(blob); + return out->edid == NULL; + } + drmModeFreePropertyBlob(blob); + } + drmModeFreeProperty(prop); + } + 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; + drmModeConnector* restrict connector; + int require_connector; + int free_edid; + int 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) == 0; + + /* Figure out what fields we need to get the data for to get the data for other fields. */ + if ((fields & (LIBGAMMA_CRTC_INFO_WIDTH_MM_EDID | LIBGAMMA_CRTC_INFO_HEIGHT_MM_EDID | LIBGAMMA_CRTC_INFO_GAMMA))) + fields |= LIBGAMMA_CRTC_INFO_EDID; + if ((fields & LIBGAMMA_CRTC_INFO_CONNECTOR_NAME)) + fields |= LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE; + if (LIBGAMMA_CRTC_INFO_EDID | LIBGAMMA_CRTC_INFO_WIDTH_MM | LIBGAMMA_CRTC_INFO_HEIGHT_MM | LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER) + fields |= LIBGAMMA_CRTC_INFO_ACTIVE; + + /* Figure out whether we require the connector to get all information we want. */ + require_connector = fields & (LIBGAMMA_CRTC_INFO_ACTIVE | LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE); + + if (require_connector == 0) + goto cont; + if ((connector = find_connector(crtc, &error)) == NULL) + { + e |= this->width_mm_error = this->height_mm_error + = this->connector_type = 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; + } + e |= read_connector_data(crtc, this, connector, fields); + if ((fields & LIBGAMMA_CRTC_INFO_EDID) == 0) + goto cont; + if (this->active_error || (this->active == 0)) + { + e |= this->edid_error = this->gamma_error + = this->width_mm_edid_error = this->height_mm_edid_error + = LIBGAMMA_NOT_CONNECTED; + goto cont; + } + e |= get_edid(crtc, this, connector); + if (this->edid == NULL) + { + this->gamma_error = this->width_mm_edid_error = this->height_mm_edid_error = this->edid_error; + goto cont; + } + if ((fields & (LIBGAMMA_CRTC_INFO_WIDTH_MM_EDID | LIBGAMMA_CRTC_INFO_HEIGHT_MM_EDID | LIBGAMMA_CRTC_INFO_GAMMA))) + e |= libgamma_parse_edid(this, fields); + cont: + e |= (fields & LIBGAMMA_CRTC_INFO_GAMMA_SIZE) ? get_gamma_ramp_size(this, crtc) : 0; + this->gamma_depth = 16; + 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 current the 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_ramps(libgamma_crtc_state_t* restrict this, + libgamma_gamma_ramps_t* restrict ramps) +{ + libgamma_drm_card_data_t* restrict card = this->partition->data; + int r; +#ifdef DEBUG + if ((ramps->red_size != ramps->green_size) || + (ramps->red_size != ramps->blue_size)) + return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE; +#endif + 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_ramps(libgamma_crtc_state_t* restrict this, + libgamma_gamma_ramps_t ramps) +{ + libgamma_drm_card_data_t* restrict card = this->partition->data; + int r; +#ifdef DEBUG + if ((ramps.red_size != ramps.green_size) || + (ramps.red_size != ramps.blue_size)) + return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE; +#endif + r = drmModeCrtcSetGamma(card->fd, (uint32_t)(size_t)(this->data), + (uint32_t)(ramps.red_size), ramps.red, ramps.green, ramps.blue); + 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: + /* XXX: 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