aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorMattias Andrée <maandree@operamail.com>2014-05-31 07:32:09 +0200
committerMattias Andrée <maandree@operamail.com>2014-05-31 07:32:09 +0200
commitbdeb7f77f16b1e482904866ff21e43f8bfc01bb0 (patch)
treee5587a577db53a58ea52fd2579fa0cbb89007491 /src/lib
parentsplit out error identification for failure of open from libgamma_linux_drm_partition_initialise (diff)
downloadlibgamma-bdeb7f77f16b1e482904866ff21e43f8bfc01bb0.tar.gz
libgamma-bdeb7f77f16b1e482904866ff21e43f8bfc01bb0.tar.bz2
libgamma-bdeb7f77f16b1e482904866ff21e43f8bfc01bb0.tar.xz
move files around
Signed-off-by: Mattias Andrée <maandree@operamail.com>
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/edid.c79
-rw-r--r--src/lib/edid.h39
-rw-r--r--src/lib/fake-quartz-cg.c314
-rw-r--r--src/lib/fake-quartz-cg.h72
-rw-r--r--src/lib/fake-w32-gdi.c297
-rw-r--r--src/lib/fake-w32-gdi.h82
-rw-r--r--src/lib/gamma-dummy.c310
-rw-r--r--src/lib/gamma-dummy.h253
-rw-r--r--src/lib/gamma-helper.c258
-rw-r--r--src/lib/gamma-helper.h165
-rw-r--r--src/lib/gamma-linux-drm.c831
-rw-r--r--src/lib/gamma-linux-drm.h162
-rw-r--r--src/lib/gamma-quartz-cg.c353
-rw-r--r--src/lib/gamma-quartz-cg.h162
-rw-r--r--src/lib/gamma-w32-gdi.c333
-rw-r--r--src/lib/gamma-w32-gdi.h162
-rw-r--r--src/lib/gamma-x-randr.c916
-rw-r--r--src/lib/gamma-x-randr.h162
-rw-r--r--src/lib/gamma-x-vidmode.c292
-rw-r--r--src/lib/gamma-x-vidmode.h162
-rw-r--r--src/lib/libgamma-error.c.gpp133
-rw-r--r--src/lib/libgamma-error.h332
-rw-r--r--src/lib/libgamma-facade.c1644
-rw-r--r--src/lib/libgamma-facade.h510
-rw-r--r--src/lib/libgamma-method.c274
-rw-r--r--src/lib/libgamma-method.h1154
-rw-r--r--src/lib/libgamma.h30
27 files changed, 9481 insertions, 0 deletions
diff --git a/src/lib/edid.c b/src/lib/edid.c
new file mode 100644
index 0000000..76867c7
--- /dev/null
+++ b/src/lib/edid.c
@@ -0,0 +1,79 @@
+/**
+ * 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 "edid.h"
+
+#include "libgamma-method.h"
+#include "libgamma-error.h"
+
+#include <stdint.h>
+
+
+/**
+ * Parse the EDID of a monitor.
+ *
+ * @param this Instance of a data structure to fill with the information about the EDID.
+ * It must contain the EDID and its length.
+ * @param feilds OR:ed identifiers for the information about the EDID that should be parsed.
+ * Fields that do not have to do with EDID are ignored.
+ * @return Non-zero on error.
+ */
+int libgamma_parse_edid(libgamma_crtc_information_t* restrict this, int32_t fields)
+{
+#define __test_version(edid, major, minor) (((edid)[18] == major) && ((edid)[19] == minor))
+
+ int error = 0;
+ int checksum = 0;
+ size_t i;
+
+ if (this->edid_length != 128)
+ error = LIBGAMMA_EDID_LENGTH_UNSUPPORTED;
+ else if ((this->edid[0] != 0x00) || (this->edid[1] != 0xFF) || (this->edid[2] != 0xFF) || (this->edid[3] != 0xFF) ||
+ (this->edid[4] != 0xFF) || (this->edid[5] != 0xFF) || (this->edid[6] != 0xFF) || (this->edid[7] != 0x00))
+ error = LIBGAMMA_EDID_WRONG_MAGIC_NUMBER;
+ else if (__test_version(this->edid, 1, 3) == 0)
+ error = LIBGAMMA_EDID_REVISION_UNSUPPORTED;
+
+ this->width_mm_edid_error = this->height_mm_edid_error = this->gamma_error = error;
+
+ this->width_mm_edid = (size_t)(this->edid[21]) * 10;
+ this->height_mm_edid = (size_t)(this->edid[22]) * 10;
+
+ if ((fields & LIBGAMMA_CRTC_INFO_GAMMA) && (error == 0))
+ {
+ if (this->edid[23] == 0xFF)
+ this->gamma_error = LIBGAMMA_GAMMA_NOT_SPECIFIED;
+ else
+ this->gamma_red = this->gamma_green = this->gamma_blue = (float)((int)(this->edid[23]) + 100) / 100.f;
+ }
+
+ if (error == 0)
+ for (i = 0; i < this->edid_length; i++)
+ checksum += (int)(this->edid[i]);
+ if ((checksum & 255))
+ {
+ error = LIBGAMMA_EDID_CHECKSUM_ERROR;
+ this->edid_error = this->width_mm_edid_error = this->height_mm_edid_error = error;
+ this->gamma_error = this->gamma_error == LIBGAMMA_GAMMA_NOT_SPECIFIED
+ ? LIBGAMMA_GAMMA_NOT_SPECIFIED_AND_EDID_CHECKSUM_ERROR : 0;
+ }
+
+ return error | this->gamma_error;
+
+#undef __test_version
+}
+
diff --git a/src/lib/edid.h b/src/lib/edid.h
new file mode 100644
index 0000000..68df0e9
--- /dev/null
+++ b/src/lib/edid.h
@@ -0,0 +1,39 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_EDID_H
+#define LIBGAMMA_EDID_H
+
+#include "libgamma-method.h"
+
+#include <stdint.h>
+
+
+/**
+ * Parse the EDID of a monitor.
+ *
+ * @param this Instance of a data structure to fill with the information about the EDID.
+ * It must contain the EDID and its length.
+ * @param feilds OR:ed identifiers for the information about the EDID that should be parsed.
+ * Fields that do not have to do with EDID are ignored.
+ * @return Non-zero on error.
+ */
+int libgamma_parse_edid(libgamma_crtc_information_t* restrict out, int32_t fields);
+
+
+#endif
+
diff --git a/src/lib/fake-quartz-cg.c b/src/lib/fake-quartz-cg.c
new file mode 100644
index 0000000..f599dbb
--- /dev/null
+++ b/src/lib/fake-quartz-cg.c
@@ -0,0 +1,314 @@
+/**
+ * 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/>.
+ */
+#ifndef FAKE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+# error Compiling fake-quartz-cg.c without FAKE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+#endif
+
+/* This file very sloppily translates Mac OS X Quartz calls to X RandR calls.
+ * It should by no means be used, without additional modification, as a
+ * part of a compatibility layer. The purpose of this file is only to make
+ * it possible to test for logical errors in Max OS X specific code on
+ * a GNU/Linux system under X. */
+
+
+#include "fake-quartz-cg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#ifndef HAVE_LIBGAMMA_METHOD_X_RANDR
+
+CGError CGGetOnlineDisplayList(uint32_t max_size,
+ CGDirectDisplayID* restrict displays_out, uint32_t* restrict count_out)
+{
+ uint32_t i;
+ for (i = 0; (i < max_size) && (i < 2); i++)
+ displays_out[i] = (CGDirectDisplayID)i;
+ *count_out = i;
+
+ return kCGErrorSuccess;
+}
+
+
+CGError CGSetDisplayTransferByTable(CGDirectDisplayID display, uint32_t gamma_size, const CGGammaValue* red,
+ const CGGammaValue* green, const CGGammaValue* blue)
+{
+ (void) display;
+ (void) red;
+ (void) green;
+ (void) blue;
+
+ if (gamma_size != 256)
+ {
+ fprintf(stderr, "Gamma size should be 256.\n");
+ abort();
+ }
+
+ return kCGErrorSuccess;
+}
+
+
+CGError CGGetDisplayTransferByTable(CGDirectDisplayID display, uint32_t gamma_size,
+ CGGammaValue* restrict red, CGGammaValue* restrict green,
+ CGGammaValue* restrict blue, uint32_t* restrict gamma_size_out)
+{
+ long i;
+ (void) display;
+
+ if (gamma_size != 256)
+ {
+ fprintf(stderr, "Gamma size should be 256.\n");
+ abort();
+ }
+
+ *gamma_size_out = 256;
+
+ for (i = 0; i < 256; i++)
+ red[i] = green[i] = blue[i] = (CGGammaValue)i / 255;
+
+ return kCGErrorSuccess;
+}
+
+void CGDisplayRestoreColorSyncSettings(void)
+{
+ /* Do nothing. */
+}
+
+uint32_t CGDisplayGammaTableCapacity(CGDirectDisplayID display)
+{
+ (void) display;
+ return 256;
+}
+
+void close_fake_quartz(void)
+{
+ /* Do nothing. */
+}
+
+
+#else
+
+#include <xcb/xcb.h>
+#include <xcb/randr.h>
+
+
+
+static xcb_connection_t* restrict connection = NULL;
+static xcb_randr_get_screen_resources_current_reply_t* restrict res_reply = NULL;
+static uint32_t crtc_count = 0;
+static xcb_randr_crtc_t* restrict crtcs = NULL;
+static uint16_t* restrict original_ramps = NULL;
+
+
+
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Waggregate-return"
+
+CGError CGGetOnlineDisplayList(uint32_t max_size,
+ CGDirectDisplayID* restrict displays_out, uint32_t* restrict count_out)
+{
+ uint32_t i;
+
+ if (connection == NULL)
+ {
+ xcb_generic_error_t* error;
+ xcb_screen_iterator_t iter;
+ xcb_randr_get_screen_resources_current_cookie_t res_cookie;
+ xcb_randr_get_crtc_gamma_cookie_t gamma_cookie;
+ xcb_randr_get_crtc_gamma_reply_t* restrict gamma_reply;
+
+ connection = xcb_connect(NULL, NULL);
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
+ res_cookie = xcb_randr_get_screen_resources_current(connection, iter.data->root);
+ res_reply = xcb_randr_get_screen_resources_current_reply(connection, res_cookie, &error);
+
+ if (error)
+ {
+ fprintf(stderr, "Failed to open X connection.\n");
+ xcb_disconnect(connection);
+ crtc_count = 0;
+ return ~kCGErrorSuccess;
+ }
+
+ crtc_count = (uint32_t)(res_reply->num_crtcs);
+ crtcs = xcb_randr_get_screen_resources_current_crtcs(res_reply);
+
+ original_ramps = malloc(crtc_count * 3 * 256 * sizeof(uint16_t));
+ if (original_ramps == NULL)
+ {
+ perror("malloc");
+ xcb_disconnect(connection);
+ crtc_count = 0;
+ return ~kCGErrorSuccess;
+ }
+
+ for (i = 0; i < crtc_count; i++)
+ {
+ gamma_cookie = xcb_randr_get_crtc_gamma(connection, crtcs[i]);
+ gamma_reply = xcb_randr_get_crtc_gamma_reply(connection, gamma_cookie, &error);
+
+ if (error)
+ {
+ fprintf(stderr, "Failed to read gamma ramps.\n");
+ xcb_disconnect(connection);
+ crtc_count = 0;
+ return ~kCGErrorSuccess;
+ }
+
+#define __DEST(C) original_ramps + (C + 3 * i) * 256
+#define __SRC(C) xcb_randr_get_crtc_gamma_##C(gamma_reply)
+ memcpy(__DEST(0), __SRC(red), 256 * sizeof(uint16_t));
+ memcpy(__DEST(1), __SRC(green), 256 * sizeof(uint16_t));
+ memcpy(__DEST(2), __SRC(blue), 256 * sizeof(uint16_t));
+#undef __SRC
+#undef __DEST
+
+ free(gamma_reply);
+ }
+ }
+
+ for (i = 0; (i < max_size) && (i < crtc_count); i++)
+ *(displays_out + i) = (CGDirectDisplayID)i;
+
+ *count_out = i;
+ return kCGErrorSuccess;
+}
+
+
+CGError CGSetDisplayTransferByTable(CGDirectDisplayID display, uint32_t gamma_size, const CGGammaValue* red,
+ const CGGammaValue* green, const CGGammaValue* blue)
+{
+ xcb_void_cookie_t gamma_cookie;
+ uint16_t r_int[256];
+ uint16_t g_int[256];
+ uint16_t b_int[256];
+ long i;
+ int32_t v;
+
+ if (gamma_size != 256)
+ {
+ fprintf(stderr, "Gamma size should be 256.\n");
+ abort();
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ v = (int32_t)(red[i] * UINT16_MAX);
+ r_int[i] = (uint16_t)(v < 0 ? 0 : v > UINT16_MAX ? UINT16_MAX : v);
+
+ v = (int32_t)(green[i] * UINT16_MAX);
+ g_int[i] = (uint16_t)(v < 0 ? 0 : v > UINT16_MAX ? UINT16_MAX : v);
+
+ v = (int32_t)(blue[i] * UINT16_MAX);
+ b_int[i] = (uint16_t)(v < 0 ? 0 : v > UINT16_MAX ? UINT16_MAX : v);
+ }
+
+ gamma_cookie = xcb_randr_set_crtc_gamma_checked(connection, crtcs[display],
+ (uint16_t)gamma_size, r_int, g_int, b_int);
+ return xcb_request_check(connection, gamma_cookie) == NULL ? kCGErrorSuccess : ~kCGErrorSuccess;
+}
+
+
+CGError CGGetDisplayTransferByTable(CGDirectDisplayID display, uint32_t gamma_size,
+ CGGammaValue* restrict red, CGGammaValue* restrict green,
+ CGGammaValue* restrict blue, uint32_t* restrict gamma_size_out)
+{
+ xcb_randr_get_crtc_gamma_cookie_t gamma_cookie;
+ xcb_randr_get_crtc_gamma_reply_t* restrict gamma_reply;
+ xcb_generic_error_t* error;
+ uint16_t* restrict r_int;
+ uint16_t* restrict g_int;
+ uint16_t* restrict b_int;
+ long i;
+
+ if (gamma_size != 256)
+ {
+ fprintf(stderr, "Gamma size should be 256.\n");
+ abort();
+ }
+
+ *gamma_size_out = 256;
+
+ gamma_cookie = xcb_randr_get_crtc_gamma(connection, crtcs[display]);
+ gamma_reply = xcb_randr_get_crtc_gamma_reply(connection, gamma_cookie, &error);
+
+ if (error)
+ {
+ fprintf(stderr, "Failed to write gamma ramps.\n");
+ return ~kCGErrorSuccess;
+ }
+
+ r_int = xcb_randr_get_crtc_gamma_red(gamma_reply);
+ g_int = xcb_randr_get_crtc_gamma_green(gamma_reply);
+ b_int = xcb_randr_get_crtc_gamma_blue(gamma_reply);
+
+ for (i = 0; i < 256; i++)
+ {
+ red[i] = (CGGammaValue)(r_int[i]) / UINT16_MAX;
+ green[i] = (CGGammaValue)(g_int[i]) / UINT16_MAX;
+ blue[i] = (CGGammaValue)(b_int[i]) / UINT16_MAX;
+ }
+
+ free(gamma_reply);
+ return kCGErrorSuccess;
+}
+
+
+void CGDisplayRestoreColorSyncSettings(void)
+{
+ xcb_generic_error_t* error;
+ xcb_void_cookie_t gamma_cookie;
+ uint32_t i;
+
+ for (i = 0; i < crtc_count; i++)
+ {
+ gamma_cookie = xcb_randr_set_crtc_gamma_checked(connection, crtcs[i], 256,
+ original_ramps + (0 + 3 * i) * 256,
+ original_ramps + (1 + 3 * i) * 256,
+ original_ramps + (2 + 3 * i) * 256);
+ if ((error = xcb_request_check(connection, gamma_cookie)))
+ fprintf(stderr, "Quartz gamma reset emulation with RandR returned %i\n", error->error_code);
+ }
+}
+
+
+uint32_t CGDisplayGammaTableCapacity(CGDirectDisplayID display)
+{
+ (void) display;
+ return 256;
+}
+
+# pragma GCC diagnostic pop
+
+
+void close_fake_quartz_cg(void)
+{
+ free(res_reply);
+ res_reply = NULL;
+ if (connection != NULL)
+ xcb_disconnect(connection);
+ connection = NULL;
+ free(original_ramps);
+ original_ramps = NULL;
+}
+
+#endif
+
diff --git a/src/lib/fake-quartz-cg.h b/src/lib/fake-quartz-cg.h
new file mode 100644
index 0000000..8f995ea
--- /dev/null
+++ b/src/lib/fake-quartz-cg.h
@@ -0,0 +1,72 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_FAKE_QUARTZ_CG_H
+#define LIBGAMMA_FAKE_QUARTZ_CG_H
+
+#ifndef FAKE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+# error Including fake-quartz-cg.h without FAKE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+#endif
+
+
+/* This header file contains some capabilities of
+ * <CoreGraphics/CGDirectDisplay.h> and <CoreGraphics/CGError.h>,
+ * and can be used modify gamma ramps without Mac OS X and Quartz
+ * but with its API.
+ *
+ * The content of this file is based on the documentation found on:
+ *
+ * https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html
+ *
+ * https://developer.apple.com/library/mac/documentation/CoreGraphics/Reference/CoreGraphicsConstantsRef/Reference/reference.html#//apple_ref/c/tdef/CGError */
+
+
+#include <stdint.h>
+
+
+typedef int32_t CGError;
+#define kCGErrorSuccess 0
+
+typedef float CGGammaValue;
+typedef uint32_t CGDirectDisplayID;
+
+
+CGError CGGetOnlineDisplayList(uint32_t max_size,
+ CGDirectDisplayID* restrict displays_out, uint32_t* restrict count_out);
+
+CGError CGSetDisplayTransferByTable(CGDirectDisplayID display, uint32_t gamma_size, const CGGammaValue* red,
+ const CGGammaValue* green, const CGGammaValue* blue);
+
+CGError CGGetDisplayTransferByTable(CGDirectDisplayID display, uint32_t gamma_size,
+ CGGammaValue* restrict red, CGGammaValue* restrict green,
+ CGGammaValue* restrict blue, uint32_t* restrict gamma_size_out);
+
+void CGDisplayRestoreColorSyncSettings(void);
+
+uint32_t CGDisplayGammaTableCapacity(CGDirectDisplayID display) __attribute__((const));
+
+
+/* The follow part most only be used when this module is used,
+ * it cannot be used when the real CoreGraphics is used.
+ * CoreGraphics does not have this function, it is added so
+ * that there is a way to cleanly close the X connection
+ * and free resources needed by this module. */
+void close_fake_quartz_cg(void);
+
+
+#endif
+
diff --git a/src/lib/fake-w32-gdi.c b/src/lib/fake-w32-gdi.c
new file mode 100644
index 0000000..541b3c8
--- /dev/null
+++ b/src/lib/fake-w32-gdi.c
@@ -0,0 +1,297 @@
+/**
+ * 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/>.
+ */
+#ifndef HAVE_LIBGAMMA_METHOD_W32_GDI
+# error Compiling fake-w32-gdi.c without FAKE_LIBGAMMA_METHOD_W32_GDI
+#endif
+
+/* This file very sloppily translates Windows GDI calls to X RandR calls.
+ * It should by no means be used, without additional modification, as a
+ * part of a compatibility layer. The purpose of this file is only to make
+ * it possible to test for logical errors in Windows specific code on
+ * a GNU/Linux system under X. */
+
+
+#include "fake-w32-gdi.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#ifndef HAVE_LIBGAMMA_METHOD_X_RANDR
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */
+HDC GetDC(HWND hWnd)
+{
+ (void) hWnd;
+ return (HDC*)16;
+}
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */
+int ReleaseDC(HWND hWnd, HDC hDC)
+{
+ (void) hWnd;
+ (void) hDC;
+ return 1;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */
+int GetDeviceCaps(HDC hDC, int nIndex)
+{
+ (void) hDC;
+ return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS;
+}
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */
+BOOL SetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp)
+{
+ (void) hDC;
+ (void) lpRamp;
+ return TRUE;
+}
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */
+BOOL GetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp)
+{
+ (void) hDC;
+ (void) lpRamp;
+ return TRUE;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */
+HDC CreateDC(LPCTSTR restrict lpszDriver, LPCTSTR restrict lpszDevice,
+ void* restrict lpszOutput, void* restrict lpInitData)
+{
+ (void) lpszOutput;
+ (void) lpInitData;
+ (void) lpszDevice;
+ if (strcmp(lpszDriver, "DISPLAY"))
+ return NULL;
+ return (HDC*)16;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */
+BOOL EnumDisplayDevices(LPCTSTR lpDevice, restrict DWORD iDevNum,
+ PDISPLAY_DEVICE restrict lpDisplayDevice, DWORD dwFlags)
+{
+ (void) dwFlags;
+ if (lpDevice != NULL)
+ {
+ fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n");
+ abort();
+ return FALSE;
+ }
+ if (iDevNum >= 2)
+ return FALSE;
+ if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE))
+ {
+ fprintf(stderr,
+ "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n");
+ abort();
+ return FALSE;
+ }
+ strcmp(lpDisplayDevice->DeviceName, "some monitor");
+ lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE;
+ return TRUE;
+}
+
+#else
+
+
+#include <xcb/xcb.h>
+#include <xcb/randr.h>
+
+
+#define GAMMA_RAMP_SIZE 256
+
+
+static xcb_connection_t* restrict connection = NULL;
+static size_t dc_count = 0;
+static ssize_t crtc_count = -1;
+static xcb_randr_crtc_t* restrict crtcs = NULL;
+static xcb_randr_get_screen_resources_current_reply_t* restrict res_reply = NULL;
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */
+HDC GetDC(HWND hWnd)
+{
+ (void) hWnd;
+ return CreateDC(TEXT("DISPLAY"), "0", NULL, NULL);
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */
+int ReleaseDC(HWND hWnd, HDC hDC)
+{
+ (void) hWnd;
+ (void) hDC;
+ dc_count--;
+ if (dc_count == 0)
+ {
+ if (connection != NULL)
+ xcb_disconnect(connection);
+ connection = NULL;
+ free(res_reply);
+ res_reply = NULL;
+ }
+ return 1;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */
+int GetDeviceCaps(HDC hDC, int nIndex)
+{
+ (void) hDC;
+ return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS;
+}
+
+
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Waggregate-return"
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */
+BOOL SetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp)
+{
+ xcb_void_cookie_t gamma_cookie =
+ xcb_randr_set_crtc_gamma_checked(connection, *(xcb_randr_crtc_t*)hDC, GAMMA_RAMP_SIZE,
+ ((uint16_t*)lpRamp) + 0 * GAMMA_RAMP_SIZE,
+ ((uint16_t*)lpRamp) + 1 * GAMMA_RAMP_SIZE,
+ ((uint16_t*)lpRamp) + 2 * GAMMA_RAMP_SIZE);
+ xcb_generic_error_t* error = xcb_request_check(connection, gamma_cookie);
+ return error == NULL ? TRUE : FALSE;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */
+BOOL GetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp)
+{
+ xcb_randr_get_crtc_gamma_cookie_t gamma_cookie;
+ xcb_randr_get_crtc_gamma_reply_t* restrict gamma_reply;
+ xcb_generic_error_t* error;
+
+ gamma_cookie = xcb_randr_get_crtc_gamma(connection, *(xcb_randr_crtc_t*)hDC);
+ gamma_reply = xcb_randr_get_crtc_gamma_reply(connection, gamma_cookie, &error);
+
+ if (error)
+ return FALSE;
+
+#define DEST_RAMP(I) (((uint16_t*)lpRamp) + (I) * GAMMA_RAMP_SIZE)
+#define SRC_RAMP(C) (xcb_randr_get_crtc_gamma_##C(gamma_reply))
+
+ memcpy(DEST_RAMP(0), SRC_RAMP(red), GAMMA_RAMP_SIZE * sizeof(uint16_t));
+ memcpy(DEST_RAMP(1), SRC_RAMP(green), GAMMA_RAMP_SIZE * sizeof(uint16_t));
+ memcpy(DEST_RAMP(2), SRC_RAMP(blue), GAMMA_RAMP_SIZE * sizeof(uint16_t));
+
+#undef SRC_RAMP
+#undef DEST_RAMP
+
+ free(gamma_reply);
+ return TRUE;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */
+HDC CreateDC(LPCTSTR restrict lpszDriver, LPCTSTR restrict lpszDevice,
+ void* restrict lpszOutput, void* restrict lpInitData)
+{
+ int crtc_index = atoi(lpszDevice);
+
+ (void) lpszOutput;
+ (void) lpInitData;
+
+ if (strcmp(lpszDriver, "DISPLAY"))
+ return NULL;
+
+ if (dc_count == 0) {
+ xcb_generic_error_t* error;
+ xcb_screen_iterator_t iter;
+ xcb_randr_get_screen_resources_current_cookie_t res_cookie;
+
+ connection = xcb_connect(NULL, NULL);
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
+ res_cookie = xcb_randr_get_screen_resources_current(connection, iter.data->root);
+ res_reply = xcb_randr_get_screen_resources_current_reply(connection, res_cookie, &error);
+
+ if (error)
+ {
+ xcb_disconnect(connection);
+ crtc_count = -1;
+ return NULL;
+ }
+
+ crtc_count = res_reply->num_crtcs;
+ crtcs = xcb_randr_get_screen_resources_current_crtcs(res_reply);
+ }
+
+ if (crtc_index >= crtc_count)
+ {
+ if (dc_count == 0)
+ {
+ xcb_disconnect(connection);
+ crtc_count = -1;
+ }
+ return NULL;
+ }
+
+ dc_count++;
+ return crtcs + crtc_index;
+}
+
+# pragma GCC diagnostic pop
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */
+BOOL EnumDisplayDevices(LPCTSTR restrict lpDevice, DWORD iDevNum,
+ PDISPLAY_DEVICE restrict lpDisplayDevice, DWORD dwFlags)
+{
+ size_t count = (size_t)crtc_count;
+ (void) dwFlags;
+ if (lpDevice != NULL)
+ {
+ fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n");
+ abort();
+ return FALSE;
+ }
+ if (crtc_count < 0)
+ {
+ if (GetDC(NULL) == NULL)
+ return FALSE;
+ dc_count = 0;
+ count = (size_t)crtc_count;
+ ReleaseDC(NULL, NULL);
+ }
+ if (iDevNum >= count)
+ return FALSE;
+ if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE))
+ {
+ fprintf(stderr, "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n");
+ abort();
+ return FALSE;
+ }
+ sprintf(lpDisplayDevice->DeviceName, "%i", iDevNum);
+ lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE;
+ return TRUE;
+}
+
+
+#endif
+
diff --git a/src/lib/fake-w32-gdi.h b/src/lib/fake-w32-gdi.h
new file mode 100644
index 0000000..1995847
--- /dev/null
+++ b/src/lib/fake-w32-gdi.h
@@ -0,0 +1,82 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_FAKE_W32_GDI_H
+#define LIBGAMMA_FAKE_W32_GDI_H
+
+#ifndef FAKE_LIBGAMMA_METHOD_W32_GDI
+# error Including fake-w32-gdi.h without FAKE_LIBGAMMA_METHOD_W32_GDI
+#endif
+
+
+#include <stdint.h>
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx */
+typedef uint16_t WORD;
+typedef uint32_t DWORD;
+typedef int BOOL;
+typedef void* HDC;
+typedef void* HWND;
+typedef void* LPVOID;
+typedef const char* LPCTSTR;
+typedef char TCHAR;
+#define TRUE 1
+#define FALSE 0
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */
+HDC GetDC(HWND hWnd);
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */
+int ReleaseDC(HWND hWnd, HDC hDC);
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */
+int GetDeviceCaps(HDC hDC, int nIndex) __attribute__((const));
+#define COLORMGMTCAPS 1
+#define CM_GAMMA_RAMP 1
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */
+BOOL SetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp);
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */
+BOOL GetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp);
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */
+HDC CreateDC(LPCTSTR restrict lpszDriver, LPCTSTR restrict lpszDevice,
+ void* restrict lpszOutput, void* restrict lpInitData);
+#define TEXT(X) ((LPCTSTR)(X))
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183569(v=vs.85).aspx */
+typedef struct
+{
+ DWORD cb;
+ TCHAR DeviceName[32];
+ DWORD StateFlags;
+} DISPLAY_DEVICE;
+typedef DISPLAY_DEVICE* PDISPLAY_DEVICE;
+#define DISPLAY_DEVICE_ACTIVE 1
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */
+BOOL EnumDisplayDevices(LPCTSTR restrict lpDevice, DWORD iDevNum,
+ PDISPLAY_DEVICE restrict lpDisplayDevice, DWORD dwFlags);
+
+
+#endif
+
diff --git a/src/lib/gamma-dummy.c b/src/lib/gamma-dummy.c
new file mode 100644
index 0000000..e508b4a
--- /dev/null
+++ b/src/lib/gamma-dummy.c
@@ -0,0 +1,310 @@
+/**
+ * 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/>.
+ */
+#ifndef HAVE_LIBGAMMA_METHOD_DUMMY
+# error Compiling gamma-dummy.c without HAVE_LIBGAMMA_METHOD_DUMMY
+#endif
+
+#include "gamma-dummy.h"
+
+#include "libgamma-error.h"
+
+
+/**
+ * Return the capabilities of the adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ */
+void libgamma_dummy_method_capabilities(libgamma_method_capabilities_t* restrict this)
+{
+}
+
+
+/**
+ * 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_dummy_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site)
+{
+}
+
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_dummy_site_destroy(libgamma_site_state_t* restrict 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_dummy_site_restore(libgamma_site_state_t* restrict this)
+{
+}
+
+
+
+/**
+ * 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_dummy_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition)
+{
+}
+
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_dummy_partition_destroy(libgamma_partition_state_t* restrict this)
+{
+}
+
+
+/**
+ * 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_dummy_partition_restore(libgamma_partition_state_t* restrict this)
+{
+}
+
+
+
+/**
+ * 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_dummy_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc)
+{
+}
+
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_dummy_crtc_destroy(libgamma_crtc_state_t* restrict 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_dummy_crtc_restore(libgamma_crtc_state_t* restrict this)
+{
+}
+
+
+
+/**
+ * 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_dummy_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields)
+{
+}
+
+
+/**
+ * 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_dummy_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps)
+{
+}
+
+
+/**
+ * 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_dummy_crtc_set_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps)
+{
+}
+
+
+
+/**
+ * Get current the gamma ramps for a CRTC, 32-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_dummy_crtc_get_gamma_ramps32(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_t* restrict ramps)
+{
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, 32-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_dummy_crtc_set_gamma_ramps32(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_t ramps)
+{
+}
+
+
+
+/**
+ * Get current the gamma ramps for a CRTC, 64-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_dummy_crtc_get_gamma_ramps64(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_t* restrict ramps)
+{
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, 64-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_dummy_crtc_set_gamma_ramps64(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_t ramps)
+{
+}
+
+
+
+/**
+ * Get current the gamma ramps for a CRTC, `float` 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_dummy_crtc_get_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t* restrict ramps)
+{
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, `float` 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_dummy_crtc_set_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t ramps)
+{
+}
+
+
+/**
+ * Get current the gamma ramps for a CRTC, `double` 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_dummy_crtc_get_gamma_rampsd(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_t* restrict ramps)
+{
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, `double` 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_dummy_crtc_set_gamma_rampsd(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_t ramps)
+{
+}
+
diff --git a/src/lib/gamma-dummy.h b/src/lib/gamma-dummy.h
new file mode 100644
index 0000000..342d119
--- /dev/null
+++ b/src/lib/gamma-dummy.h
@@ -0,0 +1,253 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_GAMMA_DUMMY_H
+#define LIBGAMMA_GAMMA_DUMMY_H
+
+#ifndef HAVE_LIBGAMMA_METHOD_DUMMY
+# error Including gamma-dummy.h without HAVE_LIBGAMMA_METHOD_DUMMY
+#endif
+
+
+#include "libgamma-method.h"
+
+
+/**
+ * Return the capabilities of the adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ */
+void libgamma_dummy_method_capabilities(libgamma_method_capabilities_t* restrict this);
+
+/**
+ * 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_dummy_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site);
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_dummy_site_destroy(libgamma_site_state_t* restrict 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_dummy_site_restore(libgamma_site_state_t* restrict this);
+
+
+/**
+ * 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_dummy_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition);
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_dummy_partition_destroy(libgamma_partition_state_t* restrict this);
+
+/**
+ * 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_dummy_partition_restore(libgamma_partition_state_t* restrict this);
+
+
+/**
+ * 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_dummy_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc);
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_dummy_crtc_destroy(libgamma_crtc_state_t* restrict 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_dummy_crtc_restore(libgamma_crtc_state_t* restrict this);
+
+
+/**
+ * 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_dummy_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields);
+
+/**
+ * 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_dummy_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps);
+
+/**
+ * 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_dummy_crtc_set_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps);
+
+
+/**
+ * Get current the gamma ramps for a CRTC, 32-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_dummy_crtc_get_gamma_ramps32(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_t* restrict ramps);
+
+/**
+ * Set the gamma ramps for a CRTC, 32-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_dummy_crtc_set_gamma_ramps32(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_t ramps);
+
+
+/**
+ * Get current the gamma ramps for a CRTC, 64-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_dummy_crtc_get_gamma_ramps64(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_t* restrict ramps);
+
+/**
+ * Set the gamma ramps for a CRTC, 64-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_dummy_crtc_set_gamma_ramps64(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_t ramps);
+
+
+/**
+ * Get current the gamma ramps for a CRTC, `float` 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_dummy_crtc_get_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t* restrict ramps);
+
+/**
+ * Set the gamma ramps for a CRTC, `float` 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_dummy_crtc_set_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t ramps);
+
+/**
+ * Get current the gamma ramps for a CRTC, `double` 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_dummy_crtc_get_gamma_rampsd(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_t* restrict ramps);
+
+/**
+ * Set the gamma ramps for a CRTC, `double` 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_dummy_crtc_set_gamma_rampsd(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_t ramps);
+
+
+#endif
+
diff --git a/src/lib/gamma-helper.c b/src/lib/gamma-helper.c
new file mode 100644
index 0000000..dd4e027
--- /dev/null
+++ b/src/lib/gamma-helper.c
@@ -0,0 +1,258 @@
+/**
+ * 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 "gamma-helper.h"
+
+#include "libgamma-method.h"
+#include "libgamma-error.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+
+#define ANY bits64
+
+
+/**
+ * Convert any set of gamma ramps into a 64-bit integer array with all channels.
+ *
+ * @param depth The depth of the gamma ramp, `-1` for `float`, `-2` for `double`.
+ * @param n The grand size of gamma ramps (sum of all channels' sizes.)
+ * @param out Output array.
+ * @param in Input gamma ramps.
+ */
+static void translate_to_64(signed depth, size_t n, uint64_t* restrict out, libgamma_gamma_ramps_any_t in)
+{
+ size_t i;
+ switch (depth)
+ {
+ case 16:
+ for (i = 0; i < n; i++)
+ out[i] = (uint64_t)(in.bits16.red[i]) * 0x0001000100010001ULL;
+ break;
+
+ case 32:
+ for (i = 0; i < n; i++)
+ out[i] = (uint64_t)(in.bits32.red[i]) * 0x0000000100000001ULL;
+ break;
+
+ case 64:
+ for (i = 0; i < n; i++)
+ out[i] = in.bits64.red[i];
+ break;
+
+ case -1:
+ for (i = 0; i < n; i++)
+ out[i] = (uint64_t)(in.float_single.red[i] * (float)UINT64_MAX);
+ break;
+
+ case -2:
+ for (i = 0; i < n; i++)
+ out[i] = (uint64_t)(in.float_double.red[i] * (double)UINT64_MAX);
+ break;
+
+ default: /* This is not possible. */
+ abort();
+ break;
+ }
+}
+
+
+/**
+ * Undo the actions of `translate_to_64`.
+ *
+ * @param depth The depth of the gamma ramp, `-1` for `float`, `-2` for `double`.
+ * @param n The grand size of gamma ramps (sum of all channels' sizes.)
+ * @param out Output gamma ramps.
+ * @param in Input array, may be modified.
+ */
+static void translate_from_64(signed depth, size_t n, libgamma_gamma_ramps_any_t out, uint64_t* restrict in)
+{
+ size_t i;
+
+ switch (depth)
+ {
+ case 16:
+ for (i = 0; i < n; i++)
+ out.bits16.red[i] = (uint16_t)(in[i] / 0x0001000100010001ULL);
+ break;
+
+ case 32:
+ for (i = 0; i < n; i++)
+ out.bits32.red[i] = (uint32_t)(in[i] / 0x0000000100000001ULL);
+ break;
+
+ case 64:
+ for (i = 0; i < n; i++)
+ out.bits64.red[i] = in[i];
+ break;
+
+ case -1:
+ for (i = 0; i < n; i++)
+ out.float_single.red[i] = (float)(in[i]) / (float)UINT64_MAX;
+ break;
+
+ case -2:
+ for (i = 0; i < n; i++)
+ out.float_double.red[i] = (double)(in[i]) / (double)UINT64_MAX;
+ break;
+
+ default: /* This is not possible. */
+ abort();
+ break;
+ }
+}
+
+
+/**
+ * Allocate and initalise a gamma ramp with any depth.
+ *
+ * @param ramps_sys Output gamma ramps.
+ * @param ramps The gamma ramps whose sizes should be duplicated.
+ * @param depth The depth of the gamma ramps to allocate,
+ * `-1` for `float`, `-2` for `double`.
+ * @param elements Output reference for the grand size of the gamma ramps.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+static int allocated_any_ramp(libgamma_gamma_ramps_any_t* restrict ramps_sys,
+ libgamma_gamma_ramps_any_t ramps, signed depth, size_t* restrict elements)
+{
+ size_t n = 0;
+ size_t d;
+
+ switch (depth)
+ {
+ case 16: d = sizeof(uint16_t); break;
+ case 32: d = sizeof(uint32_t); break;
+ case 64: d = sizeof(uint64_t); break;
+ case -1: d = sizeof(float); break;
+ case -2: d = sizeof(double); break;
+ default:
+ return errno = EINVAL, LIBGAMMA_ERRNO_SET;
+ }
+
+ n += ramps_sys->ANY. red_size = ramps.ANY. red_size;
+ n += ramps_sys->ANY.green_size = ramps.ANY.green_size;
+ n += ramps_sys->ANY. blue_size = ramps.ANY. blue_size;
+
+ ramps_sys->ANY.red = malloc(n * d);
+ ramps_sys->ANY.green = (void*)(((char*)(ramps_sys->ANY. red)) + ramps.ANY. red_size * d / sizeof(char));
+ ramps_sys->ANY.blue = (void*)(((char*)(ramps_sys->ANY.green)) + ramps.ANY.green_size * d / sizeof(char));
+
+ *elements = n;
+ return ramps_sys->ANY.red ? 0 : LIBGAMMA_ERRNO_SET;
+}
+
+
+/**
+ * Get current the gamma ramps for a CRTC, re-encoding version.
+ *
+ * @param this The CRTC state.
+ * @param ramps The gamma ramps to fill with the current values.
+ * @param depth_user The depth of the gamma ramps that are provided by the user,
+ * `-1` for `float`, `-2` for `double`.
+ * @param depth_system The depth of the gamma ramps as required by the adjustment method,
+ * `-1` for `float`, `-2` for `double`.
+ * @param fun Function that is to be used read the ramps, its parameters have
+ * the same function as those of this function with the same names,
+ * and the return value too is identical.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+int libgamma_translated_ramp_get_(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_any_t* restrict ramps,
+ signed depth_user, signed depth_system,
+ libgamma_get_ramps_any_fun* fun)
+{
+ size_t n;
+ int r;
+ libgamma_gamma_ramps_any_t ramps_sys;
+ uint64_t* restrict ramps_full;
+
+ if ((r = allocated_any_ramp(&ramps_sys, *ramps, depth_system, &n)))
+ return r;
+
+ if ((r = fun(this, &ramps_sys)))
+ {
+ free(ramps_sys.ANY.red);
+ return r;
+ }
+
+ if ((ramps_full = malloc(n * sizeof(uint64_t))) == NULL)
+ {
+ free(ramps_sys.ANY.red);
+ return LIBGAMMA_ERRNO_SET;
+ }
+
+ translate_to_64(depth_system, n, ramps_full, ramps_sys);
+ free(ramps_sys.ANY.red);
+
+ translate_from_64(depth_user, n, *ramps, ramps_full);
+ free(ramps_full);
+ return 0;
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, re-encoding version.
+ *
+ * @param this The CRTC state.
+ * @param ramps The gamma ramps to apply.
+ * @param depth_user The depth of the gamma ramps that are provided by the user,
+ * `-1` for `float`, `-2` for `double`.
+ * @param depth_system The depth of the gamma ramps as required by the adjustment method,
+ * `-1` for `float`, `-2` for `double`.
+ * @param fun Function that is to be used write the ramps, its parameters have
+ * the same function as those of this function with the same names,
+ * and the return value too is identical.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+int libgamma_translated_ramp_set_(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_any_t ramps,
+ signed depth_user, signed depth_system,
+ libgamma_set_ramps_any_fun* fun)
+{
+ size_t n;
+ int r;
+ libgamma_gamma_ramps_any_t ramps_sys;
+ uint64_t* restrict ramps_full;
+
+ if ((r = allocated_any_ramp(&ramps_sys, ramps, depth_system, &n)))
+ return r;
+
+ if ((ramps_full = malloc(n * sizeof(uint64_t))) == NULL)
+ {
+ free(ramps_sys.ANY.red);
+ return LIBGAMMA_ERRNO_SET;
+ }
+
+ translate_to_64(depth_user, n, ramps_full, ramps);
+ translate_from_64(depth_system, n, ramps_sys, ramps_full);
+ free(ramps_full);
+
+ r = fun(this, ramps_sys);
+
+ free(ramps_sys.ANY.red);
+ return r;
+}
+
+
+#undef ANY
+
diff --git a/src/lib/gamma-helper.h b/src/lib/gamma-helper.h
new file mode 100644
index 0000000..e0711ff
--- /dev/null
+++ b/src/lib/gamma-helper.h
@@ -0,0 +1,165 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_GAMMA_HELPER_H
+#define LIBGAMMA_GAMMA_HELPER_H
+
+
+#include "libgamma-method.h"
+
+
+/**
+ * Gamma ramp structure union for different depths.
+ */
+typedef union libgamma_gamma_ramps_any
+{
+ /**
+ * 16-bit gamma ramps.
+ */
+ libgamma_gamma_ramps_t bits16;
+
+ /**
+ * 32-bit gamma ramps.
+ */
+ libgamma_gamma_ramps32_t bits32;
+
+ /**
+ * 64-bit gamma ramps.
+ */
+ libgamma_gamma_ramps64_t bits64;
+
+ /**
+ * Single precision float gamma ramps.
+ */
+ libgamma_gamma_rampsf_t float_single;
+
+ /**
+ * Double precision float gamma ramps.
+ */
+ libgamma_gamma_rampsd_t float_double;
+
+} libgamma_gamma_ramps_any_t;
+
+
+/**
+ * A function for reading the gamma ramps from a CRTC.
+ *
+ * @param this The CRTC state.
+ * @param ramps The store for the gamma ramps.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+typedef int libgamma_get_ramps_any_fun(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_any_t* restrict ramps);
+
+/**
+ * A function for writing the gamma ramps to a CRTC.
+ *
+ * @param this The CRTC state.
+ * @param ramps The gamma ramps.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+typedef int libgamma_set_ramps_any_fun(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_any_t ramps);
+
+
+
+/**
+ * Get current the gamma ramps for a CRTC, re-encoding versio.n
+ *
+ * @param this The CRTC state.
+ * @param ramps The gamma ramps to fill with the current values.
+ * @param depth_user The depth of the gamma ramps that are provided by the user,
+ * `-1` for `float`, `-2` for `double`.
+ * @param depth_system The depth of the gamma ramps as required by the adjustment method,
+ * `-1` for `float`, `-2` for `double`.
+ * @param fun Function that is to be used read the ramps, its parameters have
+ * the same function as those of this function with the same names,
+ * and the return value too is identical.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+#define libgamma_translated_ramp_get(this, ramps, depth_user, depth_system, fun) \
+ libgamma_translated_ramp_get_(this, ramps, depth_user, depth_system, \
+ (libgamma_get_ramps_any_fun*)(fun))
+
+
+/**
+ * Set the gamma ramps for a CRTC, re-encoding version
+ *
+ * @param this The CRTC state.
+ * @param ramps The gamma ramps to apply.
+ * @param depth_user The depth of the gamma ramps that are provided by the user,
+ * `-1` for `float`, `-2` for `double`.
+ * @param depth_system The depth of the gamma ramps as required by the adjustment method,
+ * `-1` for `float`, `-2` for `double`.
+ * @param fun Function that is to be used write the ramps, its parameters have
+ * the same function as those of this function with the same names,
+ * and the return value too is identical.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+#define libgamma_translated_ramp_set(this, ramps, depth_user, depth_system, fun) \
+ libgamma_translated_ramp_set_(this, ramps, depth_user, depth_system, \
+ (libgamma_set_ramps_any_fun*)(fun))
+
+
+/**
+ * Get current the gamma ramps for a CRTC, re-encoding version.
+ *
+ * @param this The CRTC state.
+ * @param ramps The gamma ramps to fill with the current values.
+ * @param depth_user The depth of the gamma ramps that are provided by the user,
+ * `-1` for `float`, `-2` for `double`.
+ * @param depth_system The depth of the gamma ramps as required by the adjustment method,
+ * `-1` for `float`, `-2` for `double`.
+ * @param fun Function that is to be used read the ramps, its parameters have
+ * the same function as those of this function with the same names,
+ * and the return value too is identical.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+int libgamma_translated_ramp_get_(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_any_t* restrict ramps,
+ signed depth_user, signed depth_system,
+ libgamma_get_ramps_any_fun* fun);
+
+
+/**
+ * Set the gamma ramps for a CRTC, re-encoding version.
+ *
+ * @param this The CRTC state.
+ * @param ramps The gamma ramps to apply.
+ * @param depth_user The depth of the gamma ramps that are provided by the user,
+ * `-1` for `float`, `-2` for `double`.
+ * @param depth_system The depth of the gamma ramps as required by the adjustment method,
+ * `-1` for `float`, `-2` for `double`.
+ * @param fun Function that is to be used write the ramps, its parameters have
+ * the same function as those of this function with the same names,
+ * and the return value too is identical.
+ * @return Zero on success, otherwise (negative) the value of an
+ * error identifier provided by this library.
+ */
+int libgamma_translated_ramp_set_(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_any_t ramps,
+ signed depth_user, signed depth_system,
+ libgamma_set_ramps_any_fun* fun);
+
+
+#endif
+
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 <http://www.gnu.org/licenses/>.
+ */
+#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 <limits.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#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;
+}
+
diff --git a/src/lib/gamma-linux-drm.h b/src/lib/gamma-linux-drm.h
new file mode 100644
index 0000000..3d244aa
--- /dev/null
+++ b/src/lib/gamma-linux-drm.h
@@ -0,0 +1,162 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_GAMMA_LINUX_DRM_H
+#define LIBGAMMA_GAMMA_LINUX_DRM_H
+
+#ifndef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+# error Including gamma-linux-drm.h without HAVE_LIBGAMMA_METHOD_LINUX_DRM
+#endif
+
+
+#include "libgamma-method.h"
+
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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) __attribute__((const));
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+/**
+ * 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) __attribute__((const));
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+
+#endif
+
diff --git a/src/lib/gamma-quartz-cg.c b/src/lib/gamma-quartz-cg.c
new file mode 100644
index 0000000..3832ca5
--- /dev/null
+++ b/src/lib/gamma-quartz-cg.c
@@ -0,0 +1,353 @@
+/**
+ * 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/>.
+ */
+#ifndef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+# error Compiling gamma-quartz-cg.c without HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+#endif
+
+#include "gamma-quartz-cg.h"
+
+#include "libgamma-error.h"
+
+#ifdef FAKE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+# include "fake-quartz-cg.h"
+#else
+# include <CoreGraphics/CGDirectDisplay.h>
+# define close_fake_quartz_cg() /* For compatibility with "fake-quartz-cg.h". */
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+
+/**
+ * Return the capabilities of the adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ */
+void libgamma_quartz_cg_method_capabilities(libgamma_method_capabilities_t* restrict this)
+{
+ this->crtc_information = LIBGAMMA_CRTC_INFO_GAMMA_SIZE
+ | LIBGAMMA_CRTC_INFO_GAMMA_DEPTH;
+ this->default_site_known = 1;
+ this->multiple_sites = 0;
+ this->multiple_partitions = 0;
+ this->multiple_crtcs = 1;
+ this->partitions_are_graphics_cards = 0;
+ this->site_restore = 1;
+ this->partition_restore = 1;
+ this->crtc_restore = 0;
+ this->identical_gamma_sizes = 1;
+ this->fixed_gamma_size = 0;
+ this->fixed_gamma_depth = 1;
+#ifdef FAKE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ this->fake = 1;
+# ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ this->real = 1;
+# else
+ this->real = 0;
+# endif
+#else
+ this->fake = 0;
+ this->real = 1;
+#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. 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_quartz_cg_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site)
+{
+ if (site != NULL)
+ return LIBGAMMA_NO_SUCH_SITE;
+
+ this->partitions_available = 1;
+ return 0;
+}
+
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_quartz_cg_site_destroy(libgamma_site_state_t* restrict this)
+{
+ (void) this;
+ close_fake_quartz_cg();
+}
+
+
+/**
+ * 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_quartz_cg_site_restore(libgamma_site_state_t* restrict this)
+{
+ (void) this;
+ CGDisplayRestoreColorSyncSettings();
+ return 0;
+}
+
+
+
+/**
+ * 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_quartz_cg_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition)
+{
+ uint32_t cap = 4, n;
+ CGDirectDisplayID* crtcs;
+ CGDirectDisplayID* crtcs_old;
+ CGError r;
+
+ (void) site;
+
+ this->data = NULL;
+
+ if (partition != 0)
+ return LIBGAMMA_NO_SUCH_PARTITION;
+
+ crtcs = malloc((size_t)cap * sizeof(CGDirectDisplayID));
+ if (crtcs == NULL)
+ return LIBGAMMA_ERRNO_SET;
+
+ for (;;)
+ {
+ r = CGGetOnlineDisplayList(cap, crtcs, &n);
+ if (r != kCGErrorSuccess)
+ {
+ free(crtcs);
+ return LIBGAMMA_LIST_CRTCS_FAILED;
+ }
+ if (n < cap)
+ break;
+ if (cap == 0) /* We could also test ~0, but it is still too many. */
+ {
+ free(crtcs);
+ return LIBGAMMA_IMPOSSIBLE_AMOUNT;
+ }
+ crtcs_old = crtcs;
+ crtcs = realloc(crtcs, (size_t)cap * sizeof(CGDirectDisplayID));
+ if (crtcs)
+ {
+ free(crtcs_old);
+ return LIBGAMMA_ERRNO_SET;
+ }
+ }
+
+ this->data = crtcs;
+ this->crtcs_available = (size_t)n;
+ return 0;
+}
+
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_quartz_cg_partition_destroy(libgamma_partition_state_t* restrict this)
+{
+ free(this->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_quartz_cg_partition_restore(libgamma_partition_state_t* restrict this)
+{
+ return libgamma_quartz_cg_site_restore(this->site);
+}
+
+
+
+/**
+ * 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_quartz_cg_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc)
+{
+ (void) this;
+ if (crtc >= partition->crtcs_available)
+ return LIBGAMMA_NO_SUCH_CRTC;
+ return 0;
+}
+
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_quartz_cg_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_quartz_cg_crtc_restore(libgamma_crtc_state_t* restrict this)
+{
+ (void) this;
+ return errno = ENOTSUP, LIBGAMMA_ERRNO_SET;
+}
+
+
+
+/**
+ * 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_quartz_cg_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields)
+{
+#define SUPPORTED_FIELDS (LIBGAMMA_CRTC_INFO_GAMMA_SIZE | LIBGAMMA_CRTC_INFO_GAMMA_DEPTH)
+
+#define _E(FIELD) ((fields & FIELD) ? LIBGAMMA_CRTC_INFO_NOT_SUPPORTED : 0)
+
+ this->edid_error = _E(LIBGAMMA_CRTC_INFO_EDID);
+ this->width_mm_error = _E(LIBGAMMA_CRTC_INFO_WIDTH_MM);
+ this->height_mm_error = _E(LIBGAMMA_CRTC_INFO_HEIGHT_MM);
+ this->width_mm_edid_error = _E(LIBGAMMA_CRTC_INFO_WIDTH_MM_EDID);
+ this->height_mm_edid_error = _E(LIBGAMMA_CRTC_INFO_HEIGHT_MM_EDID);
+ if ((fields & LIBGAMMA_CRTC_INFO_GAMMA_SIZE))
+ {
+ CGDirectDisplayID* crtcs = crtc->partition->data;
+ CGDirectDisplayID crtc_id = crtcs[crtc->crtc];
+ size_t gamma_size = CGDisplayGammaTableCapacity(crtc_id);;
+ this->red_gamma_size = (size_t)gamma_size;
+ this->green_gamma_size = this->red_gamma_size;
+ this->blue_gamma_size = this->red_gamma_size;
+ this->gamma_size_error = gamma_size < 2 ? LIBGAMMA_SINGLETON_GAMMA_RAMP : 0;
+ }
+ else
+ this->gamma_size_error = 0;
+ this->gamma_depth = -1;
+ this->gamma_depth_error = 0;
+ this->gamma_support_error = _E(LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT);
+ this->subpixel_order_error = _E(LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER);
+ this->active_error = _E(LIBGAMMA_CRTC_INFO_ACTIVE);
+ this->connector_name_error = _E(LIBGAMMA_CRTC_INFO_CONNECTOR_NAME);
+ this->connector_type_error = _E(LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE);
+ this->gamma_error = _E(LIBGAMMA_CRTC_INFO_GAMMA);
+
+#undef _E
+
+ return this->gamma_size_error ? -1 : (fields & ~SUPPORTED_FIELDS) ? -1 : 0;
+
+#undef SUPPORTED_FIELDS
+}
+
+
+/**
+ * Get current the gamma ramps for a CRTC, `float` 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_quartz_cg_crtc_get_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t* restrict ramps)
+{
+ CGDirectDisplayID* restrict crtcs = this->partition->data;
+ CGDirectDisplayID crtc_id = crtcs[this->crtc];
+ uint32_t gamma_size_out;
+ CGError r;
+#ifdef DEBUG
+ if ((ramps->red_size != ramps->green_size) ||
+ (ramps->red_size != ramps->blue_size))
+ return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE;
+#endif
+ r = CGGetDisplayTransferByTable(crtc_id, (uint32_t)(ramps->red_size),
+ ramps->red, ramps->green, ramps->blue, &gamma_size_out);
+ if (r != kCGErrorSuccess)
+ return LIBGAMMA_GAMMA_RAMP_READ_FAILED;
+ if (gamma_size_out != ramps->red_size)
+ return LIBGAMMA_GAMMA_RAMP_SIZE_CHANGED;
+ return 0;
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, `float` 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_quartz_cg_crtc_set_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t ramps)
+{
+ CGDirectDisplayID* restrict crtcs = this->partition->data;
+ CGDirectDisplayID crtc_id = crtcs[this->crtc];
+ CGError r;
+#ifdef DEBUG
+ if ((ramps.red_size != ramps.green_size) ||
+ (ramps.red_size != ramps.blue_size))
+ return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE;
+#endif
+ r = CGSetDisplayTransferByTable(crtc_id, (uint32_t)(ramps.red_size),
+ ramps.red, ramps.green, ramps.blue);
+ return r == kCGErrorSuccess ? 0 : LIBGAMMA_GAMMA_RAMP_WRITE_FAILED;
+}
+
diff --git a/src/lib/gamma-quartz-cg.h b/src/lib/gamma-quartz-cg.h
new file mode 100644
index 0000000..90f5244
--- /dev/null
+++ b/src/lib/gamma-quartz-cg.h
@@ -0,0 +1,162 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_GAMMA_QUARTZ_CG_H
+#define LIBGAMMA_GAMMA_QUARTZ_CG_H
+
+#ifndef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+# error Including gamma-quartz-cg.h without HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+#endif
+
+
+#include "libgamma-method.h"
+
+
+/**
+ * Return the capabilities of the adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ */
+void libgamma_quartz_cg_method_capabilities(libgamma_method_capabilities_t* restrict this);
+
+/**
+ * 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_quartz_cg_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site);
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_quartz_cg_site_destroy(libgamma_site_state_t* restrict 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_quartz_cg_site_restore(libgamma_site_state_t* restrict this);
+
+
+/**
+ * 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_quartz_cg_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition);
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_quartz_cg_partition_destroy(libgamma_partition_state_t* restrict this);
+
+/**
+ * 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_quartz_cg_partition_restore(libgamma_partition_state_t* restrict this);
+
+
+/**
+ * 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_quartz_cg_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc) __attribute__((pure));
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_quartz_cg_crtc_destroy(libgamma_crtc_state_t* restrict this) __attribute__((const));
+
+/**
+ * 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_quartz_cg_crtc_restore(libgamma_crtc_state_t* restrict this);
+
+
+/**
+ * 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_quartz_cg_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields);
+
+/**
+ * Get current the gamma ramps for a CRTC, `float` 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_quartz_cg_crtc_get_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t* restrict ramps);
+
+/**
+ * Set the gamma ramps for a CRTC, `float` 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_quartz_cg_crtc_set_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t ramps);
+
+
+#endif
+
diff --git a/src/lib/gamma-w32-gdi.c b/src/lib/gamma-w32-gdi.c
new file mode 100644
index 0000000..f8c00ee
--- /dev/null
+++ b/src/lib/gamma-w32-gdi.c
@@ -0,0 +1,333 @@
+/**
+ * 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/>.
+ */
+#ifndef HAVE_LIBGAMMA_METHOD_W32_GDI
+# error Compiling gamma-w32-gdi.c without HAVE_LIBGAMMA_METHOD_W32_GDI
+#endif
+
+#include "gamma-w32-gdi.h"
+
+#include "libgamma-error.h"
+
+#ifndef WINVER
+# define WINVER 0x0500
+#endif
+#ifdef FAKE_LIBGAMMA_METHOD_W32_GDI
+# include "fake-w32-gdi.h"
+#else
+# include <windows.h>
+# include <wingdi.h>
+#endif
+
+#include <errno.h>
+
+#define GAMMA_RAMP_SIZE 256
+
+
+/**
+ * Return the capabilities of the adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ */
+void libgamma_w32_gdi_method_capabilities(libgamma_method_capabilities_t* restrict this)
+{
+ this->crtc_information = LIBGAMMA_CRTC_INFO_GAMMA_SIZE
+ | LIBGAMMA_CRTC_INFO_GAMMA_DEPTH
+ | LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT;
+ this->default_site_known = 1;
+ this->multiple_sites = 0;
+ this->multiple_partitions = 0;
+ this->multiple_crtcs = 1;
+ this->partitions_are_graphics_cards = 0;
+ this->site_restore = 0;
+ this->partition_restore = 0;
+ this->crtc_restore = 0;
+ this->identical_gamma_sizes = 1;
+ this->fixed_gamma_size = 1;
+ this->fixed_gamma_depth = 1;
+#ifdef FAKE_LIBGAMMA_METHOD_W32_GDI
+ this->fake = 1;
+# ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ this->real = 1;
+# else
+ this->real = 0;
+# endif
+#else
+ this->fake = 0;
+ this->real = 1;
+#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. 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_w32_gdi_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site)
+{
+ if (site != NULL)
+ return LIBGAMMA_NO_SUCH_SITE;
+
+ this->partitions_available = 1;
+ return 0;
+}
+
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_w32_gdi_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_w32_gdi_site_restore(libgamma_site_state_t* restrict this)
+{
+ (void) this;
+ return errno = ENOTSUP, 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_w32_gdi_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition)
+{
+ DWORD n = 0;
+ DISPLAY_DEVICE display;
+
+ (void) site;
+
+ if (partition != 0)
+ return LIBGAMMA_NO_SUCH_PARTITION;
+
+ /* Count CRTC:s by iteration over all possible identifiers
+ until we reach on that does not exist. */
+ display.cb = sizeof(DISPLAY_DEVICE);
+ while (EnumDisplayDevices(NULL, n, &display, 0))
+ if (n++, n == 0)
+ return LIBGAMMA_IMPOSSIBLE_AMOUNT;
+ this->crtcs_available = (size_t)n;
+ return 0;
+}
+
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_w32_gdi_partition_destroy(libgamma_partition_state_t* restrict this)
+{
+ (void) this;
+}
+
+
+/**
+ * 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_w32_gdi_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_w32_gdi_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc)
+{
+ DISPLAY_DEVICE display;
+ HDC context;
+
+ (void) partition;
+
+ this->data = NULL;
+
+ display.cb = sizeof(DISPLAY_DEVICE); /* Windows's API mandates this... */
+ /* Get identifier for selected CRTC. */
+ if (!EnumDisplayDevices(NULL, (DWORD)crtc, &display, 0))
+ return LIBGAMMA_NO_SUCH_CRTC;
+ /* Check that the connector is enabled,
+ * newer versions of Windows will always pass. */
+ if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE))
+ return LIBGAMMA_CONNECTOR_DISABLED;
+ /* Acquire CRTC connection. */
+ context = CreateDC(TEXT("DISPLAY"), display.DeviceName, NULL, NULL);
+ if (context == NULL)
+ return LIBGAMMA_OPEN_CRTC_FAILED;
+ this->data = context;
+ return 0;
+}
+
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_w32_gdi_crtc_destroy(libgamma_crtc_state_t* restrict this)
+{
+ if (this->data)
+ ReleaseDC(NULL, this->data);
+}
+
+
+/**
+ * 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_w32_gdi_crtc_restore(libgamma_crtc_state_t* restrict this)
+{
+ (void) this;
+ return errno = ENOTSUP, LIBGAMMA_ERRNO_SET;
+}
+
+
+
+/**
+ * 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_w32_gdi_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields)
+{
+#define KNOWN_FIELDS (LIBGAMMA_CRTC_INFO_GAMMA_SIZE | LIBGAMMA_CRTC_INFO_GAMMA_DEPTH | LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT)
+
+#define _E(FIELD) ((fields & FIELD) ? LIBGAMMA_CRTC_INFO_NOT_SUPPORTED : 0)
+
+ this->edid_error = _E(LIBGAMMA_CRTC_INFO_EDID);
+ this->width_mm_error = _E(LIBGAMMA_CRTC_INFO_WIDTH_MM);
+ this->height_mm_error = _E(LIBGAMMA_CRTC_INFO_HEIGHT_MM);
+ this->width_mm_edid_error = _E(LIBGAMMA_CRTC_INFO_WIDTH_MM_EDID);
+ this->height_mm_edid_error = _E(LIBGAMMA_CRTC_INFO_HEIGHT_MM_EDID);
+ this->red_gamma_size = GAMMA_RAMP_SIZE;
+ this->green_gamma_size = GAMMA_RAMP_SIZE;
+ this->blue_gamma_size = GAMMA_RAMP_SIZE;
+ this->gamma_size_error = 0;
+ this->gamma_depth = 16;
+ this->gamma_depth_error = 0;
+ if ((fields & LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT))
+ this->gamma_support = GetDeviceCaps(crtc->data, COLORMGMTCAPS) == CM_GAMMA_RAMP;
+ this->gamma_support_error = 0;
+ this->subpixel_order_error = _E(LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER);
+ this->active_error = _E(LIBGAMMA_CRTC_INFO_ACTIVE);
+ this->connector_name_error = _E(LIBGAMMA_CRTC_INFO_CONNECTOR_NAME);
+ this->connector_type_error = _E(LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE);
+ this->gamma_error = _E(LIBGAMMA_CRTC_INFO_GAMMA);
+
+#undef _E
+
+ return (fields & ~KNOWN_FIELDS) ? -1 : 0;
+
+#undef KNOWN_FIELDS
+}
+
+
+/**
+ * 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_w32_gdi_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps)
+{
+#ifdef DEBUG
+ if ((ramps-> red_size != GAMMA_RAMP_SIZE) ||
+ (ramps->green_size != GAMMA_RAMP_SIZE) ||
+ (ramps-> blue_size != GAMMA_RAMP_SIZE))
+ return LIBGAMMA_WRONG_GAMMA_RAMP_SIZE;
+#endif
+ if (!GetDeviceGammaRamp(this->data, ramps->red))
+ return LIBGAMMA_GAMMA_RAMP_READ_FAILED;
+ 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_w32_gdi_crtc_set_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps)
+{
+#ifdef DEBUG
+ if ((ramps. red_size != GAMMA_RAMP_SIZE) ||
+ (ramps.green_size != GAMMA_RAMP_SIZE) ||
+ (ramps. blue_size != GAMMA_RAMP_SIZE))
+ return LIBGAMMA_WRONG_GAMMA_RAMP_SIZE;
+#endif
+ if (!SetDeviceGammaRamp(this->data, ramps.red))
+ return LIBGAMMA_GAMMA_RAMP_WRITE_FAILED;
+ return 0;
+}
+
diff --git a/src/lib/gamma-w32-gdi.h b/src/lib/gamma-w32-gdi.h
new file mode 100644
index 0000000..0e17720
--- /dev/null
+++ b/src/lib/gamma-w32-gdi.h
@@ -0,0 +1,162 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_GAMMA_W32_GDI_H
+#define LIBGAMMA_GAMMA_W32_GDI_H
+
+#ifndef HAVE_LIBGAMMA_METHOD_W32_GDI
+# error Including gamma-w32-gdi.h without HAVE_LIBGAMMA_METHOD_W32_GDI
+#endif
+
+
+#include "libgamma-method.h"
+
+
+/**
+ * Return the capabilities of the adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ */
+void libgamma_w32_gdi_method_capabilities(libgamma_method_capabilities_t* restrict this);
+
+/**
+ * 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_w32_gdi_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site);
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_w32_gdi_site_destroy(libgamma_site_state_t* restrict this) __attribute__((const));
+
+/**
+ * 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_w32_gdi_site_restore(libgamma_site_state_t* restrict this);
+
+
+/**
+ * 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_w32_gdi_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition);
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_w32_gdi_partition_destroy(libgamma_partition_state_t* restrict this) __attribute__((const));
+
+/**
+ * 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_w32_gdi_partition_restore(libgamma_partition_state_t* restrict this);
+
+
+/**
+ * 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_w32_gdi_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc);
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_w32_gdi_crtc_destroy(libgamma_crtc_state_t* restrict 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_w32_gdi_crtc_restore(libgamma_crtc_state_t* restrict this);
+
+
+/**
+ * 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_w32_gdi_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields);
+
+/**
+ * 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_w32_gdi_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps);
+
+/**
+ * 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_w32_gdi_crtc_set_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps);
+
+
+#endif
+
diff --git a/src/lib/gamma-x-randr.c b/src/lib/gamma-x-randr.c
new file mode 100644
index 0000000..9870f6b
--- /dev/null
+++ b/src/lib/gamma-x-randr.c
@@ -0,0 +1,916 @@
+/**
+ * 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/>.
+ */
+#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) : 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");
+ 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_CONNECTOR_NAME
+ | LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE
+ | LIBGAMMA_CRTC_INFO_GAMMA;
+ this->default_site_known = (display && *display) ? 1 : 0;
+ this->multiple_sites = 1;
+ this->multiple_partitions = 1;
+ this->multiple_crtcs = 1;
+ this->partitions_are_graphics_cards = 0;
+ 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;
+}
+
+
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Waggregate-return"
+
+
+/**
+ * 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_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;
+
+ this->data = connection = xcb_connect(site, NULL);
+ if (connection == NULL)
+ return LIBGAMMA_NO_SUCH_SITE;
+
+ cookie = xcb_randr_query_version(connection, RANDR_VERSION_MAJOR, RANDR_VERSION_MINOR);
+ reply = xcb_randr_query_version_reply(connection, cookie, &error);
+
+ if ((error != NULL) || (reply == NULL))
+ {
+ free(reply);
+ xcb_disconnect(connection);
+ if (error != NULL)
+ return translate_error(error->error_code, LIBGAMMA_PROTOCOL_VERSION_QUERY_FAILED, 0);
+ return LIBGAMMA_PROTOCOL_VERSION_QUERY_FAILED;
+ }
+
+ if ((reply->major_version != RANDR_VERSION_MAJOR) ||
+ (reply->minor_version < RANDR_VERSION_MINOR))
+ {
+#ifdef DEBUG
+ fprintf(stderr, "libgamma: RandR protocol version: %u.%u", reply->major_version, reply->minor_version);
+#endif
+ free(reply);
+ xcb_disconnect(connection);
+ return LIBGAMMA_PROTOCOL_VERSION_NOT_SUPPORTED;
+ }
+
+ free(reply);
+
+ if ((setup = xcb_get_setup(connection)) == NULL)
+ {
+ xcb_disconnect(connection);
+ return LIBGAMMA_LIST_PARTITIONS_FAILED;
+ }
+ iter = xcb_setup_roots_iterator(setup);
+ this->partitions_available = (size_t)(iter.rem);
+
+ return iter.rem < 0 ? LIBGAMMA_NEGATIVE_PARTITION_COUNT : 0;
+}
+
+
+/**
+ * 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;
+ return errno = ENOTSUP, LIBGAMMA_ERRNO_SET;
+}
+
+
+/**
+ * Duplicate a memory area.
+ *
+ * @param ptr The memory aree.
+ * @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 == 0)
+ return NULL;
+ if ((rc = malloc(bytes)) == NULL)
+ 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;
+ const xcb_setup_t* restrict setup = xcb_get_setup(connection);
+ xcb_screen_t* restrict screen = NULL;
+ xcb_generic_error_t* error = NULL;
+ 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;
+ size_t i;
+
+ if (setup == NULL)
+ return LIBGAMMA_LIST_PARTITIONS_FAILED;
+
+ for (i = 0; iter.rem > 0; i++, xcb_screen_next(&iter))
+ if (i == partition)
+ {
+ screen = iter.data;
+ break;
+ }
+ if (iter.rem == 0)
+ return LIBGAMMA_NO_SUCH_PARTITION;
+
+ if (screen == NULL)
+ return LIBGAMMA_NULL_PARTITION;
+
+ cookie = xcb_randr_get_screen_resources_current(connection, screen->root);
+ reply = xcb_randr_get_screen_resources_current_reply(connection, cookie, &error);
+ if (error != NULL)
+ return translate_error(error->error_code, LIBGAMMA_LIST_CRTCS_FAILED, 0);
+
+ this->crtcs_available = reply->num_crtcs;
+ crtcs = xcb_randr_get_screen_resources_current_crtcs(reply);
+ outputs = xcb_randr_get_screen_resources_current_outputs(reply);
+ if ((crtcs == NULL) || (outputs == NULL))
+ {
+ free(reply);
+ return LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED;
+ }
+
+ /* 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 == NULL)
+ 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 == NULL) && (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 == NULL) && (reply->num_outputs > 0))
+ goto fail;
+
+ data->outputs_count = (size_t)(reply->num_outputs);
+
+ data->crtc_to_output = malloc((size_t)(reply->num_crtcs) * sizeof(size_t));
+ if (data->crtc_to_output == NULL)
+ goto fail;
+ for (i = 0; i < (size_t)(reply->num_crtcs); i++)
+ data->crtc_to_output[i] = SIZE_MAX;
+ for (i = 0; i < (size_t)(reply->num_outputs); i++)
+ {
+ xcb_randr_get_output_info_cookie_t out_cookie;
+ xcb_randr_get_output_info_reply_t* out_reply;
+ uint16_t j;
+
+ 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 != NULL)
+ {
+ fail_rc = translate_error(error->error_code, LIBGAMMA_OUTPUT_INFORMATION_QUERY_FAILED, 0);
+ goto fail;
+ }
+
+ for (j = 0; j < reply->num_crtcs; j++)
+ if (crtcs[j] == out_reply->crtc)
+ {
+ data->crtc_to_output[j] = i;
+ break;
+ }
+
+ free(out_reply);
+ }
+
+ data->config_timestamp = reply->config_timestamp;
+ this->data = data;
+ free(reply);
+ return 0;
+
+ fail:
+ if (data != NULL)
+ {
+ 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;
+ 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_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;
+ if (crtc >= partition->crtcs_available)
+ return LIBGAMMA_NO_SUCH_CRTC;
+ this->data = crtc_ids + crtc;
+ return 0;
+}
+
+
+/**
+ * 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;
+ return errno = ENOTSUP, 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;
+
+ 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 != NULL)
+ return out->gamma_size_error = translate_error(error->error_code, LIBGAMMA_GAMMA_RAMPS_SIZE_QUERY_FAILED, 1);
+ if (reply->size < 2)
+ out->gamma_size_error = LIBGAMMA_SINGLETON_GAMMA_RAMP;
+ out->red_gamma_size = out->green_gamma_size = out->blue_gamma_size = reply->size;
+ 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)
+{
+ switch (output->connection)
+ {
+ case XCB_RANDR_CONNECTION_CONNECTED:
+ out->active = 1;
+ out->width_mm = output->mm_width;
+ out->height_mm = output->mm_height;
+#define __select(value) \
+ case XCB_RENDER_SUB_PIXEL_##value: out->subpixel_order = LIBGAMMA_SUBPIXEL_ORDER_##value; break
+ 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;
+ }
+#undef __select
+ return 0;
+
+ case XCB_RANDR_CONNECTION_DISCONNECTED:
+ out->active = 0;
+ out->width_mm_error = LIBGAMMA_NOT_CONNECTED;
+ out->height_mm_error = LIBGAMMA_NOT_CONNECTED;
+ out->subpixel_order_error = LIBGAMMA_NOT_CONNECTED;
+ return 0;
+
+ default:
+ out->active = 0;
+ out->active_error = LIBGAMMA_STATE_UNKNOWN;
+ out->width_mm_error = LIBGAMMA_NOT_CONNECTED;
+ out->height_mm_error = LIBGAMMA_NOT_CONNECTED;
+ out->subpixel_order_error = LIBGAMMA_NOT_CONNECTED;
+ return -1;
+ }
+}
+
+
+/**
+ * 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)
+{
+ if ((this->connector_type_error = this->connector_name_error))
+ return -1;
+
+#define __select(name, type) \
+ if (strstr(this->connector_name, name "-") == this->connector_name) \
+ return this->connector_type = LIBGAMMA_CONNECTOR_TYPE_##type, 0
+
+ __select ("None", Unknown);
+ __select ("VGA", VGA);
+ __select ("DVI-I", DVII);
+ __select ("DVI-D", DVID);
+ __select ("DVI-A", DVIA);
+ __select ("Composite", Composite);
+ __select ("S-Video", SVIDEO);
+ __select ("Component", Component);
+ __select ("LFP", LFP);
+ __select ("Proprietary", Unknown);
+ __select ("HDMI", HDMI);
+ __select ("DisplayPort", DisplayPort);
+
+#undef __select
+
+ 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;
+
+ name = xcb_randr_get_output_info_name(output);
+ length = output->name_len; /* There is no NUL-termination. */
+ if (name == NULL)
+ {
+ out->connector_name_error = LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED;
+ return -1;
+ }
+
+ store = malloc(((size_t)length + 1) * sizeof(char));
+ if (store == NULL)
+ {
+ 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;
+
+ /* 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 != NULL)
+ 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 == NULL)
+ {
+ free(prop_reply);
+ return out->edid_error = LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED;;
+ }
+
+ /* For each property */
+ for (; atoms != atoms_end; atoms++)
+ {
+ 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 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 == NULL) || /* (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 == NULL) || (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 = malloc((size_t)length * sizeof(unsigned char));
+ if (out->edid == NULL)
+ out->edid_error = errno;
+ else
+ memcpy(out->edid, atom_data, (size_t)length * sizeof(unsigned char));
+
+ 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;
+
+ /* 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_TYPE))
+ fields |= LIBGAMMA_CRTC_INFO_CONNECTOR_NAME;
+ if ((fields & (LIBGAMMA_CRTC_INFO_WIDTH_MM | LIBGAMMA_CRTC_INFO_HEIGHT_MM | LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER)))
+ fields |= LIBGAMMA_CRTC_INFO_ACTIVE;
+
+ /* Jump if the output information is not required. */
+ if ((fields & (LIBGAMMA_CRTC_INFO_ACTIVE | LIBGAMMA_CRTC_INFO_CONNECTOR_NAME)) == 0)
+ goto cont;
+
+ /* Get connector and connector information. */
+ {
+ xcb_connection_t* restrict connection = crtc->partition->site->data;
+ libgamma_x_randr_partition_data_t* restrict screen_data = crtc->partition->data;
+ size_t output_index = screen_data->crtc_to_output[crtc->crtc];
+ xcb_randr_get_output_info_cookie_t cookie;
+ xcb_generic_error_t* error;
+ 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;
+ }
+ output = screen_data->outputs[output_index];
+ 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 != NULL)
+ {
+ 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;
+ }
+ }
+
+ e |= get_output_name(this, output_info);
+ if ((fields & LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE))
+ e |= get_connector_type(this);
+ e |= read_output_data(this, output_info);
+ if ((fields & (LIBGAMMA_CRTC_INFO_WIDTH_MM | LIBGAMMA_CRTC_INFO_HEIGHT_MM)))
+ e |= this->width_mm_error | this->height_mm_error;
+ e |= (fields & LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER) ? this->subpixel_order_error : 0;
+
+ if ((fields & LIBGAMMA_CRTC_INFO_EDID) == 0)
+ goto cont;
+ if (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(this, crtc, output);
+ 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;
+ }
+
+ free(output_info);
+ 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_x_randr_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_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
+ if ((ramps->red_size != ramps->green_size) ||
+ (ramps->red_size != ramps->blue_size))
+ return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE;
+#endif
+
+ cookie = xcb_randr_get_crtc_gamma(connection, *(xcb_randr_crtc_t*)(this->data));
+ reply = xcb_randr_get_crtc_gamma_reply(connection, cookie, &error);
+
+ if (error != NULL)
+ return translate_error(error->error_code, LIBGAMMA_GAMMA_RAMP_READ_FAILED, 0);
+
+ red = xcb_randr_get_crtc_gamma_red(reply);
+ green = xcb_randr_get_crtc_gamma_green(reply);
+ blue = xcb_randr_get_crtc_gamma_blue(reply);
+ 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_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps)
+{
+ xcb_connection_t* restrict connection = this->partition->site->data;
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t* restrict error;
+#ifdef DEBUG
+ if ((ramps.red_size != ramps.green_size) ||
+ (ramps.red_size != ramps.blue_size))
+ return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE;
+#endif
+ 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);
+ if ((error = xcb_request_check(connection, cookie)) != NULL)
+ return translate_error(error->error_code, LIBGAMMA_GAMMA_RAMP_WRITE_FAILED, 0);
+ return 0;
+}
+
+
+# pragma GCC diagnostic pop
+
diff --git a/src/lib/gamma-x-randr.h b/src/lib/gamma-x-randr.h
new file mode 100644
index 0000000..dc1ecdc
--- /dev/null
+++ b/src/lib/gamma-x-randr.h
@@ -0,0 +1,162 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_GAMMA_X_RANDR_H
+#define LIBGAMMA_GAMMA_X_RANDR_H
+
+#ifndef HAVE_LIBGAMMA_METHOD_X_RANDR
+# error Including gamma-x-randr.h without HAVE_LIBGAMMA_METHOD_X_RANDR
+#endif
+
+
+#include "libgamma-method.h"
+
+
+/**
+ * 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);
+
+/**
+ * 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_x_randr_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+/**
+ * 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) __attribute__((const));
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+/**
+ * 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_x_randr_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps);
+
+/**
+ * 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_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps);
+
+
+#endif
+
diff --git a/src/lib/gamma-x-vidmode.c b/src/lib/gamma-x-vidmode.c
new file mode 100644
index 0000000..a73f36f
--- /dev/null
+++ b/src/lib/gamma-x-vidmode.c
@@ -0,0 +1,292 @@
+/**
+ * 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/>.
+ */
+#ifndef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+# error Compiling gamma-x-vidmode.c without HAVE_LIBGAMMA_METHOD_X_VIDMODE
+#endif
+
+#include "gamma-x-vidmode.h"
+
+#include "libgamma-error.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/xf86vmode.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+
+/**
+ * Return the capabilities of the adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ */
+void libgamma_x_vidmode_method_capabilities(libgamma_method_capabilities_t* restrict this)
+{
+ char* restrict display = getenv("DISPLAY");
+ this->crtc_information = LIBGAMMA_CRTC_INFO_GAMMA_SIZE
+ | LIBGAMMA_CRTC_INFO_GAMMA_DEPTH;
+ this->default_site_known = (display && *display);
+ this->multiple_sites = 1;
+ this->multiple_partitions = 1;
+ this->multiple_crtcs = 1;
+ this->partitions_are_graphics_cards = 0;
+ 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_x_vidmode_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site)
+{
+ Display* restrict connection = XOpenDisplay(site);
+ int _major, _minor, screens;
+ if ((this->data = connection) == NULL)
+ return LIBGAMMA_OPEN_SITE_FAILED;
+ if (!XF86VidModeQueryVersion(connection, &_major, &_minor))
+ return LIBGAMMA_PROTOCOL_VERSION_QUERY_FAILED;
+ if ((screens = ScreenCount(connection)) < 0)
+ return LIBGAMMA_NEGATIVE_PARTITION_COUNT;
+ this->partitions_available = (size_t)screens;
+ return 0;
+}
+
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_x_vidmode_site_destroy(libgamma_site_state_t* restrict this)
+{
+ XCloseDisplay((Display*)(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_vidmode_site_restore(libgamma_site_state_t* restrict this)
+{
+ (void) this;
+ return errno = ENOTSUP, 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_x_vidmode_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition)
+{
+ if (partition >= site->partitions_available)
+ return LIBGAMMA_NO_SUCH_PARTITION;
+ this->crtcs_available = 1;
+ return 0;
+}
+
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_x_vidmode_partition_destroy(libgamma_partition_state_t* restrict this)
+{
+ (void) this;
+}
+
+
+/**
+ * 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_vidmode_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_x_vidmode_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc)
+{
+ (void) this;
+ (void) partition;
+ return crtc == 0 ? 0 : LIBGAMMA_NO_SUCH_CRTC;
+}
+
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_x_vidmode_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_vidmode_crtc_restore(libgamma_crtc_state_t* restrict this)
+{
+ (void) this;
+ return errno = ENOTSUP, LIBGAMMA_ERRNO_SET;
+}
+
+
+
+/**
+ * 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_vidmode_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)
+
+ this->edid_error = _E(LIBGAMMA_CRTC_INFO_EDID);
+ this->width_mm_error = _E(LIBGAMMA_CRTC_INFO_WIDTH_MM);
+ this->height_mm_error = _E(LIBGAMMA_CRTC_INFO_HEIGHT_MM);
+ this->width_mm_edid_error = _E(LIBGAMMA_CRTC_INFO_WIDTH_MM_EDID);
+ this->height_mm_edid_error = _E(LIBGAMMA_CRTC_INFO_HEIGHT_MM_EDID);
+ this->gamma_size_error = 0;
+ if ((fields & LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT))
+ {
+ Display* restrict connection = crtc->partition->site->data;
+ int stops;
+ if (!XF86VidModeGetGammaRampSize(connection, (int)(crtc->partition->partition), &stops))
+ this->gamma_size_error = LIBGAMMA_GAMMA_RAMPS_SIZE_QUERY_FAILED;
+ else if (stops < 2)
+ this->gamma_size_error = LIBGAMMA_SINGLETON_GAMMA_RAMP;
+ else
+ this->red_gamma_size = this->green_gamma_size = this->blue_gamma_size = (size_t)stops;
+ }
+ this->gamma_depth = 16;
+ this->gamma_depth_error = 0;
+ this->gamma_support_error = _E(LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT);
+ this->subpixel_order_error = _E(LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER);
+ this->active_error = _E(LIBGAMMA_CRTC_INFO_ACTIVE);
+ this->connector_name_error = _E(LIBGAMMA_CRTC_INFO_CONNECTOR_NAME);
+ this->connector_type_error = _E(LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE);
+ this->gamma_error = _E(LIBGAMMA_CRTC_INFO_GAMMA);
+
+#undef _E
+
+ return (fields & ~(LIBGAMMA_CRTC_INFO_GAMMA_DEPTH | LIBGAMMA_CRTC_INFO_GAMMA_SIZE)) ? -1 : this->gamma_size_error;
+}
+
+
+/**
+ * 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_x_vidmode_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps)
+{
+#ifdef DEBUG
+ if ((ramps->red_size != ramps->green_size) ||
+ (ramps->red_size != ramps->blue_size))
+ return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE;
+#endif
+ if (!XF86VidModeGetGammaRamp((Display*)(this->partition->site->data), (int)(this->partition->partition),
+ (int)(ramps->red_size), ramps->red, ramps->green, ramps->blue))
+ return LIBGAMMA_GAMMA_RAMP_READ_FAILED;
+ 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_vidmode_crtc_set_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps)
+{
+#ifdef DEBUG
+ if ((ramps.red_size != ramps.green_size) ||
+ (ramps.red_size != ramps.blue_size))
+ return LIBGAMMA_MIXED_GAMMA_RAMP_SIZE;
+#endif
+ if (!XF86VidModeSetGammaRamp((Display*)(this->partition->site->data), (int)(this->partition->partition),
+ (int)(ramps.red_size), ramps.red, ramps.green, ramps.blue))
+ return LIBGAMMA_GAMMA_RAMP_WRITE_FAILED;
+ return 0;
+}
+
diff --git a/src/lib/gamma-x-vidmode.h b/src/lib/gamma-x-vidmode.h
new file mode 100644
index 0000000..8378726
--- /dev/null
+++ b/src/lib/gamma-x-vidmode.h
@@ -0,0 +1,162 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_GAMMA_X_VIDMODE_H
+#define LIBGAMMA_GAMMA_X_VIDMODE_H
+
+#ifndef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+# error Including gamma-x-vidmode.h without HAVE_LIBGAMMA_METHOD_X_VIDMODE
+#endif
+
+
+#include "libgamma-method.h"
+
+
+/**
+ * Return the capabilities of the adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ */
+void libgamma_x_vidmode_method_capabilities(libgamma_method_capabilities_t* restrict this);
+
+/**
+ * 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_x_vidmode_site_initialise(libgamma_site_state_t* restrict this,
+ char* restrict site);
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_x_vidmode_site_destroy(libgamma_site_state_t* restrict 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_x_vidmode_site_restore(libgamma_site_state_t* restrict this);
+
+
+/**
+ * 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_vidmode_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition);
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_x_vidmode_partition_destroy(libgamma_partition_state_t* restrict this) __attribute__((const));
+
+/**
+ * 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_vidmode_partition_restore(libgamma_partition_state_t* restrict this);
+
+
+/**
+ * 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_vidmode_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc) __attribute__((const));
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_x_vidmode_crtc_destroy(libgamma_crtc_state_t* restrict this) __attribute__((const));
+
+/**
+ * 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_vidmode_crtc_restore(libgamma_crtc_state_t* restrict this);
+
+
+/**
+ * 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_vidmode_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields);
+
+/**
+ * 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_x_vidmode_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps);
+
+/**
+ * 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_vidmode_crtc_set_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps);
+
+
+#endif
+
diff --git a/src/lib/libgamma-error.c.gpp b/src/lib/libgamma-error.c.gpp
new file mode 100644
index 0000000..d443f74
--- /dev/null
+++ b/src/lib/libgamma-error.c.gpp
@@ -0,0 +1,133 @@
+/* -*- c -*- */
+/**
+ * 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-error.h"
+
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+
+
+£>set -u
+£>cd src/extract
+£>export PATH=".:${PATH}"
+
+
+
+/**
+ * Group that the user needs to be a member of if `LIBGAMMA_DEVICE_REQUIRE_GROUP` is returned.
+ */
+gid_t libgamma_group_gid = 0;
+
+/**
+ * Group that the user needs to be a member of if `LIBGAMMA_DEVICE_REQUIRE_GROUP` is returned,
+ * `NULL` if the name of the group `libgamma_group_gid` cannot be determined.
+ */
+const char* libgamma_group_name = NULL;
+
+
+
+/**
+ * Prints an error to stderr in a `perror` fashion,
+ * however this function will not translate the `libgamma`
+ * errors into human-readable strings, it will simply
+ * print the name of the error. If the value `error_code`
+ * is the value of `LIBGAMMA_ERRNO_SET`, `perror` will be
+ * used to print the current error stored in `errno`.
+ * If `error_code` is non-negative (an `errno` value`), that
+ * value will be stored in `errno` and `perror` will be
+ * used to print it. Additionally, if the `error_code` is
+ * the value of `LIBGAMMA_DEVICE_REQUIRE_GROUP` the
+ * required group will be printed with its numerical value
+ * and, if known, its name.
+ *
+ * @param name The text to add at the beginning.
+ * @param value The error code, may be an `errno` value.
+ */
+void libgamma_perror(const char* name, int error_code)
+{
+ if (error_code >= 0)
+ {
+ errno = error_code;
+ perror(name);
+ }
+ else if (error_code == LIBGAMMA_ERRNO_SET)
+ perror(name);
+ else if (error_code == LIBGAMMA_DEVICE_REQUIRE_GROUP)
+ {
+ const char* error = libgamma_name_of_error(error_code);
+ long int gid = (long int)libgamma_group_gid;
+ if (libgamma_group_name == NULL)
+ fprintf(stderr, "%s: %s: %ld\n", name, error, gid);
+ else
+ fprintf(stderr, "%s: %s: %s (%lid)\n", name, error, libgamma_group_name, gid);
+ }
+ else if (error_code < LIBGAMMA_ERROR_MIN)
+ fprintf(stderr, "%s: (?)\n", name);
+ else
+ fprintf(stderr, "%s: %s\n", name, libgamma_name_of_error(error_code));
+}
+
+
+/**
+ * Returns the name of the definition associated with a `libgamma` error code.
+ *
+ * @param value The error code.
+ * @return The name of the definition associated with the error code,
+ * `NULL` if the error code does not exist. The return string
+ * should not be `free`:d.
+ */
+const char* libgamma_name_of_error(int value)
+{
+ static const char* error_names[] =
+ {
+£>for error in $(libgamma-error-extract --list); do
+ "£{error}",
+£>done
+ };
+
+ if ((value < LIBGAMMA_ERROR_MIN) || (value >= 0))
+ return NULL;
+
+ return error_names[-value - 1];
+}
+
+
+/**
+ * Return the value of a `libgamma` error definition refered to by name.
+ *
+ * @param name The name of the definition associated with the error code.
+ * @return The error code, zero if the name does is `NULL`
+ * or does not refer to a `libgamma` error.
+ */
+int libgamma_value_of_error(const char* name)
+{
+ if (name == NULL)
+ return 0;
+
+£>for error in $(libgamma-error-extract --list); do
+ if (!strcmp(name, "£{error}")) return £{error};
+£>done
+
+ return 0;
+}
+
diff --git a/src/lib/libgamma-error.h b/src/lib/libgamma-error.h
new file mode 100644
index 0000000..06a7077
--- /dev/null
+++ b/src/lib/libgamma-error.h
@@ -0,0 +1,332 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_ERROR_H
+#define LIBGAMMA_ERROR_H
+
+#if !defined(LIBGAMMA_CONFIG_H) && !defined(DEBUG)
+# error libgamma-error.h should not be included directly, include libgamma.h instead
+#endif
+
+#include <sys/types.h>
+
+
+/**
+ * Group that the user needs to be a member of if `LIBGAMMA_DEVICE_REQUIRE_GROUP` is returned.
+ */
+extern gid_t libgamma_group_gid;
+
+/**
+ * Group that the user needs to be a member of if `LIBGAMMA_DEVICE_REQUIRE_GROUP` is returned,
+ * `NULL` if the name of the group `libgamma_group_gid` cannot be determined.
+ */
+extern const char* libgamma_group_name;
+
+
+/**
+ * The selected adjustment method does not exist
+ * or has been excluded at compile-time.
+ */
+#define LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD (-1)
+
+/**
+ * `errno` has be set with a standard error number
+ * to indicate the what has gone wrong.
+ */
+#define LIBGAMMA_ERRNO_SET (-2)
+
+/**
+ * The selected site does not exist.
+ */
+#define LIBGAMMA_NO_SUCH_SITE (-3)
+
+/**
+ * The selected partition does not exist.
+ */
+#define LIBGAMMA_NO_SUCH_PARTITION (-4)
+
+/**
+ * The selected CRTC does not exist.
+ */
+#define LIBGAMMA_NO_SUCH_CRTC (-5)
+
+/**
+ * Counter overflowed when counting the number of available items.
+ */
+#define LIBGAMMA_IMPOSSIBLE_AMOUNT (-6)
+
+/**
+ * The selected connector is disabled, it does not have a CRTC.
+ */
+#define LIBGAMMA_CONNECTOR_DISABLED (-7)
+
+/**
+ * The selected CRTC could not be opened, reason unknown.
+ */
+#define LIBGAMMA_OPEN_CRTC_FAILED (-8)
+
+/**
+ * The CRTC information field is not supported by the adjustment method.
+ */
+#define LIBGAMMA_CRTC_INFO_NOT_SUPPORTED (-9)
+
+/**
+ * Failed to read the current gamma ramps for the selected CRTC, reason unknown.
+ */
+#define LIBGAMMA_GAMMA_RAMP_READ_FAILED (-10)
+
+/**
+ * Failed to write the current gamma ramps for the selected CRTC, reason unknown.
+ */
+#define LIBGAMMA_GAMMA_RAMP_WRITE_FAILED (-11)
+
+/**
+ * The specified ramp sizes does not match the ramps sizes returned by the
+ * adjustment methods in response to the query/command.
+ */
+#define LIBGAMMA_GAMMA_RAMP_SIZE_CHANGED (-12)
+
+/**
+ * The specified ramp sizes are not identical which is required by the adjustment method.
+ * (Only returned in debug mode.)
+ */
+#define LIBGAMMA_MIXED_GAMMA_RAMP_SIZE (-13)
+
+/**
+ * The specified ramp sizes are not supported by the adjustment method.
+ * (Only returned in debug mode.)
+ */
+#define LIBGAMMA_WRONG_GAMMA_RAMP_SIZE (-14)
+
+/**
+ * The adjustment method reported that the gamma ramps size is 1, or perhaps even zero or negative.
+ */
+#define LIBGAMMA_SINGLETON_GAMMA_RAMP (-15)
+
+/**
+ * The adjustment method failed to list available CRTC:s, reason unknown.
+ */
+#define LIBGAMMA_LIST_CRTCS_FAILED (-16)
+
+/**
+ * Failed to acquire mode resources from the adjustment method.
+ */
+#define LIBGAMMA_ACQUIRING_MODE_RESOURCES_FAILED (-17)
+
+/**
+ * The adjustment method reported that a negative number of partitions exists in the site.
+ */
+#define LIBGAMMA_NEGATIVE_PARTITION_COUNT (-18)
+
+/**
+ * The adjustment method reported that a negative number of CRTC:s exists in the partition.
+ */
+#define LIBGAMMA_NEGATIVE_CRTC_COUNT (-19)
+
+/**
+ * Device cannot be access becauses of insufficient permissions.
+ */
+#define LIBGAMMA_DEVICE_RESTRICTED (-20)
+
+/**
+ * Device cannot be access, reason unknown.
+ */
+#define LIBGAMMA_DEVICE_ACCESS_FAILED (-21)
+
+/**
+ * Device cannot be access, membership of the `libgamma_group_gid`
+ * (named by `libgamma_group_name` (can be `NULL`, if so `errno` may
+ * have been set to tell why)) is required.
+ */
+#define LIBGAMMA_DEVICE_REQUIRE_GROUP (-22)
+
+/**
+ * The graphics card appear to have been removed.
+ */
+#define LIBGAMMA_GRAPHICS_CARD_REMOVED (-23)
+
+/**
+ * The state of the requested information is unknown.
+ */
+#define LIBGAMMA_STATE_UNKNOWN (-24)
+
+/**
+ * Failed to determine which connector the CRTC belongs to.
+ */
+#define LIBGAMMA_CONNECTOR_UNKNOWN (-25)
+
+/**
+ * The detected connector type is not listed in this library and has to be updated.
+ */
+#define LIBGAMMA_CONNECTOR_TYPE_NOT_RECOGNISED (-26)
+
+/**
+ * The detected subpixel order is not listed in this library and has to be updated.
+ */
+#define LIBGAMMA_SUBPIXEL_ORDER_NOT_RECOGNISED (-27)
+
+/**
+ * The length of the EDID does not match that of any supported EDID structure revision.
+ */
+#define LIBGAMMA_EDID_LENGTH_UNSUPPORTED (-28)
+
+/**
+ * The magic number in the EDID does not match that of any supported EDID structure revision.
+ */
+#define LIBGAMMA_EDID_WRONG_MAGIC_NUMBER (-29)
+
+/**
+ * The EDID structure revision used by the monitor is not supported.
+ */
+#define LIBGAMMA_EDID_REVISION_UNSUPPORTED (-30)
+
+/**
+ * The gamma characteristics field in the EDID is left unspecified.
+ * (This could be considered a non-error.)
+ */
+#define LIBGAMMA_GAMMA_NOT_SPECIFIED (-31)
+
+/**
+ * The checksum in the EDID is incorrect, all request information has been provided
+ * by you cannot count on it.
+ */
+#define LIBGAMMA_EDID_CHECKSUM_ERROR (-32)
+
+/**
+ * Both of the errors `LIBGAMMA_GAMMA_NOT_SPECIFIED` and
+ * `LIBGAMMA_EDID_CHECKSUM_ERROR` have occurred.
+ */
+#define LIBGAMMA_GAMMA_NOT_SPECIFIED_AND_EDID_CHECKSUM_ERROR (-33)
+
+/**
+ * Failed to query the gamma ramps size from the adjustment method, reason unknown.
+ */
+#define LIBGAMMA_GAMMA_RAMPS_SIZE_QUERY_FAILED (-34)
+
+/**
+ * The selected partition could not be opened, reason unknown.
+ */
+#define LIBGAMMA_OPEN_PARTITION_FAILED (-35)
+
+/**
+ * The selected site could not be opened, reason unknown.
+ */
+#define LIBGAMMA_OPEN_SITE_FAILED (-36)
+
+/**
+ * Failed to query the adjustment method for its protocol version, reason unknown.
+ */
+#define LIBGAMMA_PROTOCOL_VERSION_QUERY_FAILED (-37)
+
+/**
+ * The adjustment method's version of its protocol is not supported.
+ */
+#define LIBGAMMA_PROTOCOL_VERSION_NOT_SUPPORTED (-38)
+
+/**
+ * The adjustment method failed to list available partitions, reason unknown.
+ */
+#define LIBGAMMA_LIST_PARTITIONS_FAILED (-39)
+
+/**
+ * Partition exists by index, but the partition at that index does not exist.
+ */
+#define LIBGAMMA_NULL_PARTITION (-40)
+
+/**
+ * There is not monitor connected to the connector of the selected CRTC.
+ */
+#define LIBGAMMA_NOT_CONNECTED (-41)
+
+/**
+ * Data extraction from a reply from the adjustment method failed, reason unknown.
+ */
+#define LIBGAMMA_REPLY_VALUE_EXTRACTION_FAILED (-42)
+
+/**
+ * No EDID property was found on the output.
+ */
+#define LIBGAMMA_EDID_NOT_FOUND (-43)
+
+/**
+ * Failed to list properties on the output, reason unknown.
+ */
+#define LIBGAMMA_LIST_PROPERTIES_FAILED (-44)
+
+/**
+ * Failed to query a property's value from the output, reason unknown.
+ */
+#define LIBGAMMA_PROPERTY_VALUE_QUERY_FAILED (-45)
+
+/**
+ * A request for information on an output failed, reason unknown.
+ */
+#define LIBGAMMA_OUTPUT_INFORMATION_QUERY_FAILED (-46)
+
+
+
+/**
+ * The number of the libgamma error with the lowest number.
+ * If this is lower than the number your program thinks it
+ * should be sould update your program for new errors.
+ */
+#define LIBGAMMA_ERROR_MIN (-46)
+
+
+
+/**
+ * Prints an error to stderr in a `perror` fashion,
+ * however this function will not translate the `libgamma`
+ * errors into human-readable strings, it will simply
+ * print the name of the error. If the value `error_code`
+ * is the value of `LIBGAMMA_ERRNO_SET`, `perror` will be
+ * used to print the current error stored in `errno`.
+ * If `error_code` is non-negative (an `errno` value`), that
+ * value will be stored in `errno` and `perror` will be
+ * used to print it. Additionally, if the `error_code` is
+ * the value of `LIBGAMMA_DEVICE_REQUIRE_GROUP` the
+ * required group will be printed with its numerical value
+ * and, if known, its name.
+ *
+ * @param name The text to add at the beginning.
+ * @param value The error code, may be an `errno` value.
+ */
+void libgamma_perror(const char* name, int error_code);
+
+/**
+ * Returns the name of the definition associated with a `libgamma` error code.
+ *
+ * @param value The error code.
+ * @return The name of the definition associated with the error code,
+ * `NULL` if the error code does not exist. The return string
+ * should not be `free`:d.
+ */
+const char* libgamma_name_of_error(int value) __attribute__((const));
+
+/**
+ * Return the value of a `libgamma` error definition refered to by name.
+ *
+ * @param name The name of the definition associated with the error code.
+ * @return The error code, zero if the name does is `NULL`
+ * or does not refer to a `libgamma` error.
+ */
+int libgamma_value_of_error(const char* name) __attribute__((const));
+
+
+
+#endif
+
diff --git a/src/lib/libgamma-facade.c b/src/lib/libgamma-facade.c
new file mode 100644
index 0000000..c94d9e3
--- /dev/null
+++ b/src/lib/libgamma-facade.c
@@ -0,0 +1,1644 @@
+/**
+ * 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-facade.h"
+
+#include "libgamma-error.h"
+#include "libgamma-method.h"
+#include "gamma-helper.h"
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+# include "gamma-dummy.h"
+# ifndef HAVE_LIBGAMMA_METHODS
+# define HAVE_LIBGAMMA_METHODS
+# endif
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+# include "gamma-x-randr.h"
+# ifndef HAVE_LIBGAMMA_METHODS
+# define HAVE_LIBGAMMA_METHODS
+# endif
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+# include "gamma-x-vidmode.h"
+# ifndef HAVE_LIBGAMMA_METHODS
+# define HAVE_LIBGAMMA_METHODS
+# endif
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+# include "gamma-linux-drm.h"
+# ifndef HAVE_LIBGAMMA_METHODS
+# define HAVE_LIBGAMMA_METHODS
+# endif
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+# include "gamma-w32-gdi.h"
+# ifndef HAVE_LIBGAMMA_METHODS
+# define HAVE_LIBGAMMA_METHODS
+# endif
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+# include "gamma-quartz-cg.h"
+# ifndef HAVE_LIBGAMMA_METHODS
+# define HAVE_LIBGAMMA_METHODS
+# endif
+#endif
+
+#include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+
+#ifndef HAVE_LIBGAMMA_METHODS
+# define HAVE_NO_LIBGAMMA_METHODS
+#endif
+
+
+#ifdef HAVE_NO_LIBGAMMA_METHODS
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
+#endif
+
+
+
+#ifdef HAVE_LIBGAMMA_METHODS
+# ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+/**
+ * Test whether a file descriptor refers to a VT.
+ *
+ * @param fd The file descriptor.
+ * @return Whether the file descriptor refers to a VT.
+ */
+static int libgamma_is_vt_proper(int fd)
+{
+ char buf[32];
+ char digit0;
+
+ if (ttyname_r(fd, buf, sizeof(buf) / sizeof(char)))
+ return 0;
+
+ if (strstr(buf, "/dev/tty") != buf)
+ return 0;
+
+ digit0 = buf[strlen("/dev/tty")];
+ return ('1' <= digit0) && (digit0 <= '9');
+}
+# endif
+
+
+/**
+ * Test the availability of an adjustment method.
+ *
+ * @param method The adjustment method.
+ * @param operation Allowed values:
+ * 0: Pass if the environment suggests it will work but is not fake.
+ * 1: Pass if the environment suggests it will work.
+ * 2: Pass if real and not fake.
+ * 3: Pass if real.
+ * 4: Always pass.
+ * Other values invoke undefined behaviour.
+ * @return Whether the test passed.
+ */
+static int libgamma_list_method_test(int method, int operation)
+{
+ libgamma_method_capabilities_t caps;
+ libgamma_method_capabilities(&caps, method);
+
+ switch (operation)
+ {
+ case 0: /* Methods that the environment suggests will work, excluding fake. */
+ if (caps.fake)
+ return 0;
+ /* Fall through. */
+
+ case 1: /* Methods that the environment suggests will work, including fake. */
+ if (caps.real == 0)
+ return 0;
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ if (method == LIBGAMMA_METHOD_LINUX_DRM)
+ return libgamma_is_vt_proper(STDIN_FILENO) ||
+ libgamma_is_vt_proper(STDOUT_FILENO) ||
+ libgamma_is_vt_proper(STDERR_FILENO);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ if (method == LIBGAMMA_METHOD_DUMMY)
+ return 0;
+#endif
+ return caps.default_site_known;
+
+ case 2: /* All real non-fake methods. */
+ return caps.real && (caps.fake == 0);
+
+ case 3: /* All real methods. */
+ return caps.real;
+
+ default: /* All methods. */
+ return 1;
+ }
+}
+#endif
+
+
+/**
+ * List available adjustment methods by their order of preference based on the environment.
+ *
+ * @param methods Output array of methods, should be able to hold `LIBGAMMA_METHOD_COUNT` elements
+ * @param buf_size The number of elements that fits in `methods`, it should be `LIBGAMMA_METHOD_COUNT`,
+ * This is used to avoid writing outside the output buffer if this library adds new
+ * adjustment methods without the users of the library recompiling.
+ * @param operation Allowed values:
+ * 0: Methods that the environment suggests will work, excluding fake.
+ * 1: Methods that the environment suggests will work, including fake.
+ * 2: All real non-fake methods.
+ * 3: All real methods.
+ * 4: All methods.
+ * Other values invoke undefined behaviour.
+ * @return The number of element that have been stored in `methods`, or should
+ * have been stored if the buffer was large enought.
+ */
+size_t libgamma_list_methods(int* restrict methods, size_t buf_size, int operation)
+{
+#ifdef HAVE_NO_LIBGAMMA_METHODS
+ (void) methods;
+ (void) operation;
+ return 0;
+#else
+ size_t n = 0;
+
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ if (libgamma_list_method_test(LIBGAMMA_METHOD_X_RANDR, operation) && (n++ < buf_size))
+ methods[n - 1] = LIBGAMMA_METHOD_X_RANDR;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ if (libgamma_list_method_test(LIBGAMMA_METHOD_X_VIDMODE, operation) && (n++ < buf_size))
+ methods[n - 1] = LIBGAMMA_METHOD_X_VIDMODE;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ if (libgamma_list_method_test(LIBGAMMA_METHOD_LINUX_DRM, operation) && (n++ < buf_size))
+ methods[n - 1] = LIBGAMMA_METHOD_LINUX_DRM;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ if (libgamma_list_method_test(LIBGAMMA_METHOD_W32_GDI, operation) && (n++ < buf_size))
+ methods[n - 1] = LIBGAMMA_METHOD_W32_GDI;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ if (libgamma_list_method_test(LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS, operation) && (n++ < buf_size))
+ methods[n - 1] = LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ if (libgamma_list_method_test(LIBGAMMA_METHOD_DUMMY, operation) && (n++ < buf_size))
+ methods[n - 1] = LIBGAMMA_METHOD_DUMMY;
+#endif
+
+ return n;
+#endif
+}
+
+
+/**
+ * Check whether an adjustment method is available, non-existing (invalid) methods will be
+ * identified as not available under the rationale that the library may be out of date.
+ *
+ * @param method The adjustment method.
+ * @return Whether the adjustment method is available.
+ */
+int libgamma_is_method_available(int method)
+{
+#ifdef HAVE_NO_LIBGAMMA_METHODS
+ (void) methods;
+ return 0;
+#else
+ switch (method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+#endif
+ return 1;
+
+ default:
+ return 0;
+ }
+#endif
+}
+
+
+/**
+ * Return the capabilities of an adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities.
+ * @param method The adjustment method (display server and protocol.)
+ */
+void libgamma_method_capabilities(libgamma_method_capabilities_t* restrict this, int method)
+{
+ memset(this, 0, sizeof(libgamma_method_capabilities_t));
+
+ switch (method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ libgamma_dummy_method_capabilities(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ libgamma_x_randr_method_capabilities(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ libgamma_x_vidmode_method_capabilities(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ libgamma_linux_drm_method_capabilities(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ libgamma_w32_gdi_method_capabilities(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ libgamma_quartz_cg_method_capabilities(this);
+ break;
+#endif
+
+ default:
+ /* Method does not exists/excluded at compile-time.
+ We will assume that this is not done... */
+ break;
+ }
+}
+
+
+/**
+ * Return the capabilities of an adjustment method.
+ *
+ * @param method The adjustment method (display server and protocol.)
+ * @return The default site, `NULL` if it cannot be determined or
+ * if multiple sites are not supported by the adjustment
+ * method. This value should not be free:d.
+ */
+char* libgamma_method_default_site(int method)
+{
+ const char* restrict var = libgamma_method_default_site_variable(method);
+ char* restrict env;
+
+ if (var == NULL)
+ return NULL;
+
+ env = getenv(var);
+ if ((env == NULL) || (*env == '\0'))
+ return NULL;
+
+ return env;
+}
+
+
+/**
+ * Return the capabilities of an adjustment method.
+ *
+ * @param method The adjustment method (display server and protocol.)
+ * @return The environ variables that is used to determine the
+ * default site. `NULL` if there is none, that is, if
+ * the method does not support multiple sites.
+ * This value should not be free:d.
+ */
+const char* libgamma_method_default_site_variable(int method)
+{
+ switch (method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return "DISPLAY";
+#endif
+
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return "DISPLAY";
+#endif
+
+ default:
+ return NULL;
+ }
+}
+
+
+
+/**
+ * Initialise an allocated site state.
+ *
+ * @param this The site state to initialise.
+ * @param method The adjustment method (display server and protocol.)
+ * @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_site_initialise(libgamma_site_state_t* restrict this,
+ int method, char* restrict site)
+{
+ this->method = method;
+ this->site = site;
+
+ switch (method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_site_initialise(this, site);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_site_initialise(this, site);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_site_initialise(this, site);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_site_initialise(this, site);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_site_initialise(this, site);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_site_initialise(this, site);
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_site_destroy(libgamma_site_state_t* restrict this)
+{
+ switch (this->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ libgamma_dummy_site_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ libgamma_x_randr_site_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ libgamma_x_vidmode_site_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ libgamma_linux_drm_site_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ libgamma_w32_gdi_site_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ libgamma_quartz_cg_site_destroy(this);
+ break;
+#endif
+
+ default:
+ /* Method does not exists/excluded at compile-time.
+ We will assume that this is not done... */
+ break;
+ }
+ free(this->site);
+}
+
+
+/**
+ * Release all resources held by a site state
+ * and free the site state pointer.
+ *
+ * @param this The site state.
+ */
+void libgamma_site_free(libgamma_site_state_t* restrict this)
+{
+ libgamma_site_destroy(this);
+ free(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_site_restore(libgamma_site_state_t* restrict this)
+{
+ switch (this->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_site_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_site_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_site_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_site_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_site_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_site_restore(this);
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+
+/**
+ * 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_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition)
+{
+ this->site = site;
+ this->partition = partition;
+
+ switch (site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_partition_initialise(this, site, partition);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_partition_initialise(this, site, partition);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_partition_initialise(this, site, partition);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_partition_initialise(this, site, partition);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_partition_initialise(this, site, partition);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_partition_initialise(this, site, partition);
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_partition_destroy(libgamma_partition_state_t* restrict this)
+{
+ switch (this->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ libgamma_dummy_partition_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ libgamma_x_randr_partition_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ libgamma_x_vidmode_partition_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ libgamma_linux_drm_partition_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ libgamma_w32_gdi_partition_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ libgamma_quartz_cg_partition_destroy(this);
+ break;
+#endif
+
+ default:
+ /* Method does not exists/excluded at compile-time.
+ We will assume that this is not done... */
+ break;
+ }
+}
+
+
+/**
+ * Release all resources held by a partition state
+ * and free the partition state pointer.
+ *
+ * @param this The partition state.
+ */
+void libgamma_partition_free(libgamma_partition_state_t* restrict this)
+{
+ libgamma_partition_destroy(this);
+ free(this);
+}
+
+
+/**
+ * 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_partition_restore(libgamma_partition_state_t* restrict this)
+{
+ switch (this->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_partition_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_partition_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_partition_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_partition_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_partition_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_partition_restore(this);
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+
+/**
+ * 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_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc)
+{
+ this->partition = partition;
+ this->crtc = crtc;
+
+ switch (partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_initialise(this, partition, crtc);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_crtc_initialise(this, partition, crtc);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_crtc_initialise(this, partition, crtc);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_crtc_initialise(this, partition, crtc);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_crtc_initialise(this, partition, crtc);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_crtc_initialise(this, partition, crtc);
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_crtc_destroy(libgamma_crtc_state_t* restrict this)
+{
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ libgamma_dummy_crtc_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ libgamma_x_randr_crtc_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ libgamma_x_vidmode_crtc_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ libgamma_linux_drm_crtc_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ libgamma_w32_gdi_crtc_destroy(this);
+ break;
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ libgamma_quartz_cg_crtc_destroy(this);
+ break;
+#endif
+
+ default:
+ /* Method does not exists/excluded at compile-time.
+ We will assume that this is not done... */
+ break;
+ }
+}
+
+
+/**
+ * Release all resources held by a CRTC state
+ * and free the CRTC state pointer.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_crtc_free(libgamma_crtc_state_t* restrict this)
+{
+ libgamma_crtc_destroy(this);
+ free(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_crtc_restore(libgamma_crtc_state_t* restrict this)
+{
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_crtc_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_crtc_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_crtc_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_crtc_restore(this);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_crtc_restore(this);
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+
+/**
+ * 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_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields)
+{
+#ifdef HAVE_NO_LIBGAMMA_METHODS
+ (void) fields;
+#endif
+
+ this->edid = NULL;
+ this->connector_name = NULL;
+
+ switch (crtc->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_get_crtc_information(this, crtc, fields);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_get_crtc_information(this, crtc, fields);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_get_crtc_information(this, crtc, fields);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_get_crtc_information(this, crtc, fields);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_get_crtc_information(this, crtc, fields);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_get_crtc_information(this, crtc, fields);
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+/**
+ * Release all resources in an information data structure for a CRTC.
+ *
+ * @param this The CRTC information.
+ */
+void libgamma_crtc_information_destroy(libgamma_crtc_information_t* restrict this)
+{
+ free(this->edid);
+ free(this->connector_name);
+}
+
+
+/**
+ * Release all resources in an information data structure for a CRTC
+ * and free the data structure pointer.
+ *
+ * @param this The CRTC information.
+ */
+void libgamma_crtc_information_free(libgamma_crtc_information_t* restrict this)
+{
+ libgamma_crtc_information_destroy(this);
+ free(this);
+}
+
+
+/**
+ * Convert a raw representation of an EDID to a lowercase hexadecimal representation.
+ *
+ * @param edid The EDID in raw representation.
+ * @param length The length of `edid`.
+ * @retrun The EDID in lowercase hexadecimal representation,
+ * `NULL` on allocation error, `errno` will be set accordingly.
+ */
+char* libgamma_behex_edid_lowercase(const unsigned char* restrict edid, size_t length)
+{
+ char* restrict out = malloc((length * 2 + 1) * sizeof(char));
+ size_t i;
+
+ if (out == NULL)
+ return NULL;
+
+ for (i = 0; i < length; i++)
+ {
+ out[i * 2 + 0] = "0123456789abcdef"[(edid[i] >> 4) & 15];
+ out[i * 2 + 1] = "0123456789abcdef"[(edid[i] >> 0) & 15];
+ }
+ out[length * 2] = '\0';
+
+ return out;
+}
+
+
+/**
+ * Convert a raw representation of an EDID to an uppercase hexadecimal representation.
+ *
+ * @param edid The EDID in raw representation.
+ * @param length The length of `edid`.
+ * @retrun The EDID in uppercase hexadecimal representation,
+ * NULL` on allocation error, `errno` will be set accordingly.
+ */
+char* libgamma_behex_edid_uppercase(const unsigned char* restrict edid, size_t length)
+{
+ char* restrict out = malloc((length * 2 + 1) * sizeof(char));
+ size_t i;
+
+ if (out == NULL)
+ return NULL;
+
+ for (i = 0; i < length; i++)
+ {
+ out[i * 2 + 0] = "0123456789ABCDEF"[(edid[i] >> 4) & 15];
+ out[i * 2 + 1] = "0123456789ABCDEF"[(edid[i] >> 0) & 15];
+ }
+ out[length * 2] = '\0';
+
+ return out;
+}
+
+
+/**
+ * Convert an hexadecimal representation of an EDID to a raw representation.
+ *
+ * @param edid The EDID in hexadecimal representation.
+ * @retrun The EDID in raw representation, it will be half the length
+ * of `edid` (the input value). `NULL` on allocation error or
+ * if the EDID is malformated, `errno` will be set accordingly.
+ */
+unsigned char* libgamma_unhex_edid(const char* restrict edid)
+{
+#define not_range(lower, V, upper) ((V < lower) || (upper < V))
+#define is_not_hex(V) (not_range('0', V, '9') && not_range('a', V, 'f') && not_range('A', V, 'F'))
+
+ unsigned char* restrict out = NULL;
+ size_t n = strlen(edid);
+ size_t i;
+
+ if ((n & 1))
+ return errno = EINVAL, NULL;
+
+ out = malloc(n / 2 * sizeof(unsigned char));
+ if (out == NULL)
+ return NULL;
+
+ for (i = 0; i < n; i++)
+ {
+ char a = edid[i * 2 + 0];
+ char b = edid[i * 2 + 1];
+
+ if (is_not_hex(a) || is_not_hex(b))
+ {
+ free(out);
+ return errno = EINVAL, NULL;
+ }
+
+ a = (char)((a & 15) + (a > '9' ? 9 : 0));
+ b = (char)((b & 15) + (b > '9' ? 9 : 0));
+
+ out[i] = (unsigned char)((a << 4) | b);
+ }
+
+ return out;
+
+#undef is_hex
+#undef not_range
+}
+
+
+
+/**
+ * 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_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps)
+{
+#ifdef HAVE_NO_LIBGAMMA_METHODS
+ (void) ramps;
+#endif
+
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_get_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_crtc_get_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_crtc_get_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_crtc_get_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_crtc_get_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ {
+ libgamma_gamma_ramps_any_t ramps_;
+ ramps_.bits16 = *ramps;
+ return libgamma_translated_ramp_get(this, &ramps_, 16, -1,
+ libgamma_crtc_get_gamma_ramps);
+ }
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+/**
+ * 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_crtc_set_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps)
+{
+#ifdef HAVE_NO_LIBGAMMA_METHODS
+ (void) ramps;
+#endif
+
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_set_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_RANDR
+ case LIBGAMMA_METHOD_X_RANDR:
+ return libgamma_x_randr_crtc_set_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_X_VIDMODE
+ case LIBGAMMA_METHOD_X_VIDMODE:
+ return libgamma_x_vidmode_crtc_set_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ case LIBGAMMA_METHOD_LINUX_DRM:
+ return libgamma_linux_drm_crtc_set_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_W32_GDI
+ case LIBGAMMA_METHOD_W32_GDI:
+ return libgamma_w32_gdi_crtc_set_gamma_ramps(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ {
+ libgamma_gamma_ramps_any_t ramps_;
+ ramps_.bits16 = ramps;
+ return libgamma_translated_ramp_set(this, ramps_, 16, -1,
+ libgamma_crtc_set_gamma_ramps);
+ }
+#endif
+
+ default:
+ return LIBGAMMA_NO_SUCH_ADJUSTMENT_METHOD;
+ }
+}
+
+
+
+/**
+ * Get current the gamma ramps for a CRTC, 32-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_crtc_get_gamma_ramps32(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_t* restrict ramps)
+{
+ libgamma_gamma_ramps_any_t ramps_;
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_get_gamma_ramps32(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ ramps_.bits32 = *ramps;
+ return libgamma_translated_ramp_get(this, &ramps_, 32, -1,
+ libgamma_crtc_get_gamma_ramps);
+#endif
+
+ default:
+ ramps_.bits32 = *ramps;
+ return libgamma_translated_ramp_get(this, &ramps_, 32, 16,
+ libgamma_crtc_get_gamma_ramps);
+ }
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, 32-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_crtc_set_gamma_ramps32(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_t ramps)
+{
+ libgamma_gamma_ramps_any_t ramps_;
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_set_gamma_ramps32(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ ramps_.bits32 = ramps;
+ return libgamma_translated_ramp_set(this, ramps_, 32, -1,
+ libgamma_crtc_set_gamma_ramps);
+#endif
+
+ default:
+ ramps_.bits32 = ramps;
+ return libgamma_translated_ramp_set(this, ramps_, 32, 16,
+ libgamma_crtc_set_gamma_ramps);
+ }
+}
+
+
+
+/**
+ * Get current the gamma ramps for a CRTC, 64-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_crtc_get_gamma_ramps64(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_t* restrict ramps)
+{
+ libgamma_gamma_ramps_any_t ramps_;
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_get_gamma_ramps64(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ ramps_.bits64 = *ramps;
+ return libgamma_translated_ramp_get(this, &ramps_, 64, -1,
+ libgamma_crtc_get_gamma_ramps);
+#endif
+
+ default:
+ ramps_.bits64 = *ramps;
+ return libgamma_translated_ramp_get(this, &ramps_, 64, 16,
+ libgamma_crtc_get_gamma_ramps);
+ }
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, 64-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_crtc_set_gamma_ramps64(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_t ramps)
+{
+ libgamma_gamma_ramps_any_t ramps_;
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_set_gamma_ramps64(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ ramps_.bits64 = ramps;
+ return libgamma_translated_ramp_set(this, ramps_, 64, -1,
+ libgamma_crtc_set_gamma_ramps);
+#endif
+
+ default:
+ ramps_.bits64 = ramps;
+ return libgamma_translated_ramp_set(this, ramps_, 64, 16,
+ libgamma_crtc_set_gamma_ramps);
+ }
+}
+
+
+
+/**
+ * Get current the gamma ramps for a CRTC, `float` 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_crtc_get_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t* restrict ramps)
+{
+ libgamma_gamma_ramps_any_t ramps_;
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_get_gamma_rampsf(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_crtc_get_gamma_rampsf(this, ramps);
+#endif
+
+ default:
+ ramps_.float_single = *ramps;
+ return libgamma_translated_ramp_get(this, &ramps_, -1, 16,
+ libgamma_crtc_get_gamma_ramps);
+ }
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, `float` 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_crtc_set_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t ramps)
+{
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_set_gamma_rampsf(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ return libgamma_quartz_cg_crtc_set_gamma_rampsf(this, ramps);
+#endif
+
+ default:
+ {
+ libgamma_gamma_ramps_any_t ramps_;
+ ramps_.float_single = ramps;
+ return libgamma_translated_ramp_set(this, ramps_, -1, 16,
+ libgamma_crtc_set_gamma_ramps);
+ }
+ }
+}
+
+
+
+/**
+ * Get current the gamma ramps for a CRTC, `double` 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_crtc_get_gamma_rampsd(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_t* restrict ramps)
+{
+ libgamma_gamma_ramps_any_t ramps_;
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_get_gamma_rampsd(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ ramps_.float_double = *ramps;
+ return libgamma_translated_ramp_get(this, &ramps_, -2, -1,
+ libgamma_crtc_get_gamma_ramps);
+#endif
+
+ default:
+ ramps_.float_double = *ramps;
+ return libgamma_translated_ramp_get(this, &ramps_, -2, 16,
+ libgamma_crtc_get_gamma_ramps);
+ }
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, `double` 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_crtc_set_gamma_rampsd(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_t ramps)
+{
+ libgamma_gamma_ramps_any_t ramps_;
+ switch (this->partition->site->method)
+ {
+#ifdef HAVE_LIBGAMMA_METHOD_DUMMY
+ case LIBGAMMA_METHOD_DUMMY:
+ return libgamma_dummy_crtc_set_gamma_rampsd(this, ramps);
+#endif
+#ifdef HAVE_LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ case LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS:
+ ramps_.float_double = ramps;
+ return libgamma_translated_ramp_set(this, ramps_, -2, -1,
+ libgamma_crtc_set_gamma_ramps);
+#endif
+
+ default:
+ ramps_.float_double = ramps;
+ return libgamma_translated_ramp_set(this, ramps_, -2, 16,
+ libgamma_crtc_set_gamma_ramps);
+ }
+}
+
+
+
+/**
+ * Set the gamma ramps for a CRTC, 16-bit gamma-depth function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_ramps_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_fun* red_function,
+ libgamma_gamma_ramps_fun* green_function,
+ libgamma_gamma_ramps_fun* blue_function)
+{
+ libgamma_crtc_information_t info;
+ libgamma_gamma_ramps_t ramps;
+ size_t i, n;
+ int e;
+
+ if (libgamma_get_crtc_information(&info, this, LIBGAMMA_CRTC_INFO_GAMMA_SIZE))
+ {
+ e = info.gamma_size_error;
+ if (e < 0)
+ return e;
+ return errno = e, LIBGAMMA_ERRNO_SET;
+ }
+
+ n = ramps. red_size = info. red_gamma_size;
+ n += ramps.green_size = info.green_gamma_size;
+ n += ramps. blue_size = info. blue_gamma_size;
+
+ ramps. red = malloc(n * sizeof(uint16_t));
+ ramps.green = ramps. red + ramps. red_size;
+ ramps. blue = ramps.green + ramps.green_size;
+ if (ramps.red == NULL)
+ return LIBGAMMA_ERRNO_SET;
+
+ for (i = 0, n = ramps.red_size; i < n; i++)
+ ramps.red[i] = red_function((float)i / (float)(n - 1));
+
+ for (i = 0, n = ramps.green_size; i < n; i++)
+ ramps.green[i] = green_function((float)i / (float)(n - 1));
+
+ for (i = 0, n = ramps.blue_size; i < n; i++)
+ ramps.blue[i] = blue_function((float)i / (float)(n - 1));
+
+ e = libgamma_crtc_set_gamma_ramps(this, ramps);
+ free(ramps.red);
+ return e;
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, 32-bit gamma-depth function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_ramps32_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_fun* red_function,
+ libgamma_gamma_ramps32_fun* green_function,
+ libgamma_gamma_ramps32_fun* blue_function)
+{
+ libgamma_crtc_information_t info;
+ libgamma_gamma_ramps32_t ramps;
+ size_t i, n;
+ int e;
+
+ if (libgamma_get_crtc_information(&info, this, LIBGAMMA_CRTC_INFO_GAMMA_SIZE))
+ {
+ e = info.gamma_size_error;
+ if (e < 0)
+ return e;
+ return errno = e, LIBGAMMA_ERRNO_SET;
+ }
+
+ n = ramps. red_size = info. red_gamma_size;
+ n += ramps.green_size = info.green_gamma_size;
+ n += ramps. blue_size = info. blue_gamma_size;
+
+ ramps. red = malloc(n * sizeof(uint32_t));
+ ramps.green = ramps. red + ramps. red_size;
+ ramps. blue = ramps.green + ramps.green_size;
+ if (ramps.red == NULL)
+ return LIBGAMMA_ERRNO_SET;
+
+ for (i = 0, n = ramps.red_size; i < n; i++)
+ ramps.red[i] = red_function((float)i / (float)(n - 1));
+
+ for (i = 0, n = ramps.green_size; i < n; i++)
+ ramps.green[i] = green_function((float)i / (float)(n - 1));
+
+ for (i = 0, n = ramps.blue_size; i < n; i++)
+ ramps.blue[i] = blue_function((float)i / (float)(n - 1));
+
+ e = libgamma_crtc_set_gamma_ramps32(this, ramps);
+ free(ramps.red);
+ return e;
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, 64-bit gamma-depth function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_ramps64_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_fun* red_function,
+ libgamma_gamma_ramps64_fun* green_function,
+ libgamma_gamma_ramps64_fun* blue_function)
+{
+ libgamma_crtc_information_t info;
+ libgamma_gamma_ramps64_t ramps;
+ size_t i, n;
+ int e;
+
+ if (libgamma_get_crtc_information(&info, this, LIBGAMMA_CRTC_INFO_GAMMA_SIZE))
+ {
+ e = info.gamma_size_error;
+ if (e < 0)
+ return e;
+ return errno = e, LIBGAMMA_ERRNO_SET;
+ }
+
+ n = ramps. red_size = info. red_gamma_size;
+ n += ramps.green_size = info.green_gamma_size;
+ n += ramps. blue_size = info. blue_gamma_size;
+
+ ramps. red = malloc(n * sizeof(uint64_t));
+ ramps.green = ramps. red + ramps. red_size;
+ ramps. blue = ramps.green + ramps.green_size;
+ if (ramps.red == NULL)
+ return LIBGAMMA_ERRNO_SET;
+
+ for (i = 0, n = ramps.red_size; i < n; i++)
+ ramps.red[i] = red_function((float)i / (float)(n - 1));
+
+ for (i = 0, n = ramps.green_size; i < n; i++)
+ ramps.green[i] = green_function((float)i / (float)(n - 1));
+
+ for (i = 0, n = ramps.blue_size; i < n; i++)
+ ramps.blue[i] = blue_function((float)i / (float)(n - 1));
+
+ e = libgamma_crtc_set_gamma_ramps64(this, ramps);
+ free(ramps.red);
+ return e;
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, `float` function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_rampsf_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_fun* red_function,
+ libgamma_gamma_rampsf_fun* green_function,
+ libgamma_gamma_rampsf_fun* blue_function)
+{
+ libgamma_crtc_information_t info;
+ libgamma_gamma_rampsf_t ramps;
+ size_t i, n;
+ int e;
+
+ if (libgamma_get_crtc_information(&info, this, LIBGAMMA_CRTC_INFO_GAMMA_SIZE))
+ {
+ e = info.gamma_size_error;
+ if (e < 0)
+ return e;
+ return errno = e, LIBGAMMA_ERRNO_SET;
+ }
+
+ n = ramps. red_size = info. red_gamma_size;
+ n += ramps.green_size = info.green_gamma_size;
+ n += ramps. blue_size = info. blue_gamma_size;
+
+ ramps. red = malloc(n * sizeof(float));
+ ramps.green = ramps. red + ramps. red_size;
+ ramps. blue = ramps.green + ramps.green_size;
+ if (ramps.red == NULL)
+ return LIBGAMMA_ERRNO_SET;
+
+ for (i = 0, n = ramps.red_size; i < n; i++)
+ ramps.red[i] = red_function((float)i / (float)(n - 1));
+
+ for (i = 0, n = ramps.green_size; i < n; i++)
+ ramps.green[i] = green_function((float)i / (float)(n - 1));
+
+ for (i = 0, n = ramps.blue_size; i < n; i++)
+ ramps.blue[i] = blue_function((float)i / (float)(n - 1));
+
+ e = libgamma_crtc_set_gamma_rampsf(this, ramps);
+ free(ramps.red);
+ return e;
+}
+
+
+/**
+ * Set the gamma ramps for a CRTC, `double` function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_rampsd_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_fun* red_function,
+ libgamma_gamma_rampsd_fun* green_function,
+ libgamma_gamma_rampsd_fun* blue_function)
+{
+ libgamma_crtc_information_t info;
+ libgamma_gamma_rampsd_t ramps;
+ size_t i, n;
+ int e;
+
+ if (libgamma_get_crtc_information(&info, this, LIBGAMMA_CRTC_INFO_GAMMA_SIZE))
+ {
+ e = info.gamma_size_error;
+ if (e < 0)
+ return e;
+ return errno = e, LIBGAMMA_ERRNO_SET;
+ }
+
+ n = ramps. red_size = info. red_gamma_size;
+ n += ramps.green_size = info.green_gamma_size;
+ n += ramps. blue_size = info. blue_gamma_size;
+
+ ramps. red = malloc(n * sizeof(double));
+ ramps.green = ramps. red + ramps. red_size;
+ ramps. blue = ramps.green + ramps.green_size;
+ if (ramps.red == NULL)
+ return LIBGAMMA_ERRNO_SET;
+
+ for (i = 0, n = ramps.red_size; i < n; i++)
+ ramps.red[i] = red_function((double)i / (double)(n - 1));
+
+ for (i = 0, n = ramps.green_size; i < n; i++)
+ ramps.green[i] = green_function((double)i / (double)(n - 1));
+
+ for (i = 0, n = ramps.blue_size; i < n; i++)
+ ramps.blue[i] = blue_function((double)i / (double)(n - 1));
+
+ e = libgamma_crtc_set_gamma_rampsd(this, ramps);
+ free(ramps.red);
+ return e;
+}
+
+
+
+#ifdef HAVE_NO_LIBGAMMA_METHODS
+# pragma GCC diagnostic pop
+#endif
+
diff --git a/src/lib/libgamma-facade.h b/src/lib/libgamma-facade.h
new file mode 100644
index 0000000..fdf46ae
--- /dev/null
+++ b/src/lib/libgamma-facade.h
@@ -0,0 +1,510 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_FACADE_H
+#define LIBGAMMA_FACADE_H
+
+#if !defined(LIBGAMMA_CONFIG_H) && !defined(DEBUG)
+# error libgamma-facade.h should not be included directly, include libgamma.h instead
+#endif
+
+
+#include "libgamma-method.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+
+/**
+ * Mapping function from [0, 1] float encoding value to [0, 2¹⁶ − 1] integer output value.
+ *
+ * @param encoding [0, 1] float encoding value.
+ * @return [0, 2¹⁶ − 1] integer output value.
+ */
+typedef uint16_t libgamma_gamma_ramps_fun(float encoding);
+
+/**
+ * Mapping function from [0, 1] float encoding value to [0, 2³² − 1] integer output value.
+ *
+ * @param encoding [0, 1] float encoding value.
+ * @return [0, 2³² − 1] integer output value.
+ */
+typedef uint32_t libgamma_gamma_ramps32_fun(float encoding);
+
+/**
+ * Mapping function from [0, 1] float encoding value to [0, 2⁶⁴ − 1] integer output value.
+ *
+ * @param encoding [0, 1] float encoding value.
+ * @return [0, 2⁶⁴ − 1] integer output value.
+ */
+typedef uint64_t libgamma_gamma_ramps64_fun(float encoding);
+
+/**
+ * Mapping function from [0, 1] float encoding value to [0, 1] float output value.
+ *
+ * @param encoding [0, 1] float encoding value.
+ * @return [0, 1] float output value.
+ */
+typedef float libgamma_gamma_rampsf_fun(float encoding);
+
+/**
+ * Mapping function from [0, 1] double precision float encoding
+ * value to [0, 1] double precision float output value.
+ *
+ * @param encoding [0, 1] float encoding value.
+ * @return [0, 1] float output value.
+ */
+typedef double libgamma_gamma_rampsd_fun(double encoding);
+
+
+
+/**
+ * List available adjustment methods by their order of preference based on the environment.
+ *
+ * @param methods Output array of methods, should be able to hold `LIBGAMMA_METHOD_COUNT` elements
+ * @param buf_size The number of elements that fits in `methods`, it should be `LIBGAMMA_METHOD_COUNT`,
+ * This is used to avoid writing outside the output buffer if this library adds new
+ * adjustment methods without the users of the library recompiling.
+ * @param operation Allowed values:
+ * 0: Methods that the environment suggests will work, excluding fake.
+ * 1: Methods that the environment suggests will work, including fake.
+ * 2: All real non-fake methods.
+ * 3: All real methods.
+ * 4: All methods.
+ * Other values invoke undefined behaviour.
+ * @return The number of element that have been stored in `methods`, or should
+ * have been stored if the buffer was large enought.
+ */
+size_t libgamma_list_methods(int* restrict methods, size_t buf_size, int operation);
+
+/**
+ * Check whether an adjustment method is available, non-existing (invalid) methods will be
+ * identified as not available under the rationale that the library may be out of date.
+ *
+ * @param method The adjustment method.
+ * @return Whether the adjustment method is available.
+ */
+int libgamma_is_method_available(int method) __attribute__((const));
+
+/**
+ * Return the capabilities of an adjustment method.
+ *
+ * @param this The data structure to fill with the method's capabilities
+ * @param method The adjustment method (display server and protocol).
+ */
+void libgamma_method_capabilities(libgamma_method_capabilities_t* restrict this, int method);
+
+/**
+ * Return the capabilities of an adjustment method.
+ *
+ * @param method The adjustment method (display server and protocol.)
+ * @return The default site, `NULL` if it cannot be determined or
+ * if multiple sites are not supported by the adjustment
+ * method. This value should not be free:d.
+ */
+char* libgamma_method_default_site(int method);
+
+/**
+ * Return the capabilities of an adjustment method.
+ *
+ * @param method The adjustment method (display server and protocol.)
+ * @return The environ variables that is used to determine the
+ * default site. `NULL` if there is none, that is, if
+ * the method does not support multiple sites.
+ * This value should not be free:d.
+ */
+const char* libgamma_method_default_site_variable(int method) __attribute__((const));
+
+
+/**
+ * Initialise an allocated site state.
+ *
+ * @param this The site state to initialise.
+ * @param method The adjustment method (display server and protocol.)
+ * @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_site_initialise(libgamma_site_state_t* restrict this,
+ int method, char* restrict site);
+
+/**
+ * Release all resources held by a site state.
+ *
+ * @param this The site state.
+ */
+void libgamma_site_destroy(libgamma_site_state_t* restrict this);
+
+/**
+ * Release all resources held by a site state
+ * and free the site state pointer.
+ *
+ * @param this The site state.
+ */
+void libgamma_site_free(libgamma_site_state_t* restrict 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_site_restore(libgamma_site_state_t* restrict this);
+
+
+/**
+ * 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_partition_initialise(libgamma_partition_state_t* restrict this,
+ libgamma_site_state_t* restrict site, size_t partition);
+
+/**
+ * Release all resources held by a partition state.
+ *
+ * @param this The partition state.
+ */
+void libgamma_partition_destroy(libgamma_partition_state_t* restrict this);
+
+/**
+ * Release all resources held by a partition state
+ * and free the partition state pointer.
+ *
+ * @param this The partition state.
+ */
+void libgamma_partition_free(libgamma_partition_state_t* restrict this);
+
+/**
+ * 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_partition_restore(libgamma_partition_state_t* restrict this);
+
+
+/**
+ * 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_crtc_initialise(libgamma_crtc_state_t* restrict this,
+ libgamma_partition_state_t* restrict partition, size_t crtc);
+
+/**
+ * Release all resources held by a CRTC state.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_crtc_destroy(libgamma_crtc_state_t* restrict this);
+
+/**
+ * Release all resources held by a CRTC state
+ * and free the CRTC state pointer.
+ *
+ * @param this The CRTC state.
+ */
+void libgamma_crtc_free(libgamma_crtc_state_t* restrict 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_crtc_restore(libgamma_crtc_state_t* restrict this);
+
+
+/**
+ * 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_get_crtc_information(libgamma_crtc_information_t* restrict this,
+ libgamma_crtc_state_t* restrict crtc, int32_t fields);
+
+/**
+ * Release all resources in an information data structure for a CRTC.
+ *
+ * @param this The CRTC information.
+ */
+void libgamma_crtc_information_destroy(libgamma_crtc_information_t* restrict this);
+
+/**
+ * Release all resources in an information data structure for a CRTC
+ * and free the data structure pointer.
+ *
+ * @param this The CRTC information.
+ */
+void libgamma_crtc_information_free(libgamma_crtc_information_t* restrict this);
+
+/**
+ * Convert a raw representation of an EDID to a lowercase hexadecimal representation.
+ *
+ * @param edid:const unsigned char* The EDID in raw representation.
+ * @param length:size_t The length of `edid`.
+ * @return :char* The EDID in lowercase hexadecimal representation,
+ * `NULL` on allocation error, `errno` will be set accordingly.
+ */
+#define libgamma_behex_edid(edid, length) \
+ libgamma_behex_edid_lowercase(edid, length)
+
+/**
+ * Convert a raw representation of an EDID to a lowercase hexadecimal representation.
+ *
+ * @param edid The EDID in raw representation.
+ * @param length The length of `edid`.
+ * @retrun The EDID in lowercase hexadecimal representation,
+ * `NULL` on allocation error, `errno` will be set accordingly.
+ */
+char* libgamma_behex_edid_lowercase(const unsigned char* restrict edid, size_t length);
+
+/**
+ * Convert a raw representation of an EDID to an uppercase hexadecimal representation.
+ *
+ * @param edid The EDID in raw representation.
+ * @param length The length of `edid`.
+ * @retrun The EDID in uppercase hexadecimal representation,
+ * NULL` on allocation error, `errno` will be set accordingly.
+ */
+char* libgamma_behex_edid_uppercase(const unsigned char* restrict edid, size_t length);
+
+/**
+ * Convert an hexadecimal representation of an EDID to a raw representation.
+ *
+ * @param edid The EDID in hexadecimal representation.
+ * @retrun The EDID in raw representation, it will be half the length
+ * of `edid` (the input value). `NULL` on allocation error or
+ * if the EDID is malformated, `errno` will be set accordingly.
+ */
+unsigned char* libgamma_unhex_edid(const char* restrict edid);
+
+
+/**
+ * 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_crtc_get_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t* restrict ramps);
+
+/**
+ * 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_crtc_set_gamma_ramps(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_t ramps) __attribute__((hot));
+
+
+/**
+ * Get current the gamma ramps for a CRTC, 32-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_crtc_get_gamma_ramps32(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_t* restrict ramps);
+
+/**
+ * Set the gamma ramps for a CRTC, 32-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_crtc_set_gamma_ramps32(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_t ramps);
+
+
+/**
+ * Get current the gamma ramps for a CRTC, 64-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_crtc_get_gamma_ramps64(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_t* restrict ramps);
+
+/**
+ * Set the gamma ramps for a CRTC, 64-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_crtc_set_gamma_ramps64(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_t ramps);
+
+
+/**
+ * Set the gamma ramps for a CRTC, `float` 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_crtc_set_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t ramps);
+
+/**
+ * Get current the gamma ramps for a CRTC, `float` 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_crtc_get_gamma_rampsf(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_t* restrict ramps);
+
+
+/**
+ * Get current the gamma ramps for a CRTC, `double` 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_crtc_get_gamma_rampsd(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_t* restrict ramps);
+
+/**
+ * Set the gamma ramps for a CRTC, `double` 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_crtc_set_gamma_rampsd(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_t ramps);
+
+
+/**
+ * Set the gamma ramps for a CRTC, 16-bit gamma-depth function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_ramps_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps_fun* red_function,
+ libgamma_gamma_ramps_fun* green_function,
+ libgamma_gamma_ramps_fun* blue_function) __attribute__((cold));
+
+/**
+ * Set the gamma ramps for a CRTC, 32-bit gamma-depth function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_ramps32_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps32_fun* red_function,
+ libgamma_gamma_ramps32_fun* green_function,
+ libgamma_gamma_ramps32_fun* blue_function) __attribute__((cold));
+
+/**
+ * Set the gamma ramps for a CRTC, 64-bit gamma-depth function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_ramps64_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_ramps64_fun* red_function,
+ libgamma_gamma_ramps64_fun* green_function,
+ libgamma_gamma_ramps64_fun* blue_function) __attribute__((cold));
+
+/**
+ * Set the gamma ramps for a CRTC, `float` function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_rampsf_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsf_fun* red_function,
+ libgamma_gamma_rampsf_fun* green_function,
+ libgamma_gamma_rampsf_fun* blue_function) __attribute__((cold));
+
+/**
+ * Set the gamma ramps for a CRTC, `double` function version.
+ *
+ * Note that this will probably involve the library allocating temporary data.
+ *
+ * @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_crtc_set_gamma_rampsd_f(libgamma_crtc_state_t* restrict this,
+ libgamma_gamma_rampsd_fun* red_function,
+ libgamma_gamma_rampsd_fun* green_function,
+ libgamma_gamma_rampsd_fun* blue_function) __attribute__((cold));
+
+
+#endif
+
diff --git a/src/lib/libgamma-method.c b/src/lib/libgamma-method.c
new file mode 100644
index 0000000..4c81549
--- /dev/null
+++ b/src/lib/libgamma-method.c
@@ -0,0 +1,274 @@
+/**
+ * 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-method.h"
+
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_ramps_initialise(libgamma_gamma_ramps_t* restrict this)
+{
+ size_t n = this->red_size + this->green_size + this->blue_size;
+ this->red = malloc(n * sizeof(uint16_t));
+ this->green = this-> red + this-> red_size;
+ this->blue = this->green + this->green_size;
+ return this->red ? 0 : -1;
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps_destroy(libgamma_gamma_ramps_t* restrict this)
+{
+ free(this->red);
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps_free(libgamma_gamma_ramps_t* restrict this)
+{
+ free(this->red);
+ free(this);
+}
+
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_ramps32_initialise(libgamma_gamma_ramps32_t* restrict this)
+{
+ size_t n = this->red_size + this->green_size + this->blue_size;
+ this->red = malloc(n * sizeof(uint32_t));
+ this->green = this-> red + this-> red_size;
+ this->blue = this->green + this->green_size;
+ return this->red ? 0 : -1;
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps32_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps32_destroy(libgamma_gamma_ramps32_t* restrict this)
+{
+ free(this->red);
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps32_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps32_free(libgamma_gamma_ramps32_t* restrict this)
+{
+ free(this->red);
+ free(this);
+}
+
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_ramps64_initialise(libgamma_gamma_ramps64_t* restrict this)
+{
+ size_t n = this->red_size + this->green_size + this->blue_size;
+ this->red = malloc(n * sizeof(uint64_t));
+ this->green = this-> red + this-> red_size;
+ this->blue = this->green + this->green_size;
+ return this->red ? 0 : -1;
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps64_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps64_destroy(libgamma_gamma_ramps64_t* restrict this)
+{
+ free(this->red);
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps64_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps64_free(libgamma_gamma_ramps64_t* restrict this)
+{
+ free(this->red);
+ free(this);
+}
+
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_rampsf_initialise(libgamma_gamma_rampsf_t* restrict this)
+{
+ size_t n = this->red_size + this->green_size + this->blue_size;
+ this->red = malloc(n * sizeof(float));
+ this->green = this-> red + this-> red_size;
+ this->blue = this->green + this->green_size;
+ return this->red ? 0 : -1;
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_rampsf_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_rampsf_destroy(libgamma_gamma_rampsf_t* restrict this)
+{
+ free(this->red);
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_rampsf_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_rampsf_free(libgamma_gamma_rampsf_t* restrict this)
+{
+ free(this->red);
+ free(this);
+}
+
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_rampsd_initialise(libgamma_gamma_rampsd_t* restrict this)
+{
+ size_t n = this->red_size + this->green_size + this->blue_size;
+#ifdef HAVE_LIBGAMMA_METHOD_LINUX_DRM
+ /* Valgrind complains about us reading uninitialize memory if we just use malloc. */
+ this->red = calloc(n, sizeof(double));
+#else
+ this->red = malloc(n * sizeof(double));
+#endif
+ this->green = this-> red + this-> red_size;
+ this->blue = this->green + this->green_size;
+ return this->red == NULL ? -1 : 0;
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_rampsd_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_rampsd_destroy(libgamma_gamma_rampsd_t* restrict this)
+{
+ free(this->red);
+}
+
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_rampsd_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_rampsd_free(libgamma_gamma_rampsd_t* restrict this)
+{
+ free(this->red);
+ free(this);
+}
+
diff --git a/src/lib/libgamma-method.h b/src/lib/libgamma-method.h
new file mode 100644
index 0000000..8496631
--- /dev/null
+++ b/src/lib/libgamma-method.h
@@ -0,0 +1,1154 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_METHOD_H
+#define LIBGAMMA_METHOD_H
+
+#if !defined(LIBGAMMA_CONFIG_H) && !defined(DEBUG)
+# error libgamma-method.h should not be included directly, include libgamma.h instead
+#endif
+
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+
+/**
+ * The identifier for the dummy adjustment method.
+ * This method can be configured and is useful for
+ * testing your program's ability to handle errors.
+ */
+#define LIBGAMMA_METHOD_DUMMY 0
+
+/**
+ * The identifier for the adjustment method with
+ * uses the RandR protocol under the X display server.
+ */
+#define LIBGAMMA_METHOD_X_RANDR 1
+
+/**
+ * The identifier for the adjustment method with
+ * uses the VidMode protocol under the X display server.
+ * This is an older alternative to RandR that can
+ * work on some drivers that are not supported by RandR,
+ * however it can only control the primary CRTC per
+ * screen (partition).
+ */
+#define LIBGAMMA_METHOD_X_VIDMODE 2
+
+/**
+ * The identifier for the Direct Rendering Manager
+ * adjustment method that is available in Linux
+ * (built in to the Linux kernel with a userland
+ * library for access) and is a part of the
+ * Direct Rendering Infrastructure. This adjustment
+ * method all work when you are in non-graphical
+ * mode; however a display server cannnot be
+ * started while this is running, but it can be
+ * started while a display server is running.
+ */
+#define LIBGAMMA_METHOD_LINUX_DRM 3
+
+/**
+ * The identifier for the Graphics Device Interface
+ * adjustment method that is available in Windows.
+ * This method is not well tested; it can be compiled
+ * to be available under X.org using a translation layer.
+ */
+#define LIBGAMMA_METHOD_W32_GDI 4
+
+/**
+ * The identifier for the CoreGraphics adjustment
+ * method that is available in Mac OS X that can
+ * adjust gamma ramps under the Quartz display server.
+ * This method is not well tested; it can be compiled
+ * to be available under X.org using a translation layer.
+ */
+#define LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS 5
+
+
+/**
+ * The index of the last gamma method, neither it
+ * nor any index before it may actually be supported
+ * as it could have been disabled at compile-time
+ */
+#define LIBGAMMA_METHOD_MAX 5
+
+/**
+ * The number adjustment methods provided by this library.
+ * Note however that this includes adjstment methods that
+ * have been removed at compile-time.
+ */
+#define LIBGAMMA_METHOD_COUNT (LIBGAMMA_METHOD_MAX + 1)
+
+
+
+/**
+ * Capabilities of adjustment methods
+ */
+typedef struct libgamma_method_capabilities
+{
+ /**
+ * OR of the CRTC information fields in `libgamma_crtc_information_t`
+ * that may (but can fail) be read successfully.
+ */
+ int32_t crtc_information;
+
+ /**
+ * Whether the default site is known, if true the site is integrated
+ * to the system or can be determined using environment variables.
+ */
+ unsigned default_site_known : 1;
+
+ /**
+ * Whether the adjustment method supports multiple sites rather
+ * than just the default site.
+ */
+ unsigned multiple_sites : 1;
+
+ /**
+ * Whether the adjustment method supports multiple partitions
+ * per site.
+ */
+ unsigned multiple_partitions : 1;
+
+ /**
+ * Whether the adjustment method supports multiple CRTC:s
+ * per partition per site.
+ */
+ unsigned multiple_crtcs : 1;
+
+ /**
+ * Whether the partition to graphics card is a bijection.
+ */
+ unsigned partitions_are_graphics_cards : 1;
+
+ /**
+ * Whether the adjustment method supports `libgamma_site_restore`.
+ */
+ unsigned site_restore : 1;
+
+ /**
+ * Whether the adjustment method supports `libgamma_partition_restore`.
+ */
+ unsigned partition_restore : 1;
+
+ /**
+ * Whether the adjustment method supports `libgamma_crtc_restore`.
+ */
+ unsigned crtc_restore : 1;
+
+ /**
+ * Whether the `red_gamma_size`, `green_gamma_size` and `blue_gamma_size`
+ * fields in `libgamma_crtc_information_t` will always have the same
+ * values as each other for the adjustment method.
+ */
+ unsigned identical_gamma_sizes : 1;
+
+ /**
+ * Whether the `red_gamma_size`, `green_gamma_size` and `blue_gamma_size`
+ * fields in `libgamma_crtc_information_t` will always be filled with the
+ * same value for the adjustment method.
+ */
+ unsigned fixed_gamma_size : 1;
+
+ /**
+ * Whether the `gamma_depth` field in `libgamma_crtc_information_t`
+ * will always be filled with the same value for the adjustment method.
+ */
+ unsigned fixed_gamma_depth : 1;
+
+ /**
+ * Whether the adjustment method will actually perform adjustments.
+ */
+ unsigned real : 1;
+
+ /**
+ * Whether the adjustment method is implement using a translation layer.
+ */
+ unsigned fake : 1;
+
+} libgamma_method_capabilities_t;
+
+
+/**
+ * Site state
+ *
+ * On operating systems that integrate a graphical environment
+ * there is usally just one site. However, one systems with
+ * pluggable graphics, like Unix-like systems such as GNU/Linux
+ * and the BSD:s, there can usually be any (feasible) number of
+ * sites. In X.org parlance they are called displays.
+ */
+typedef struct libgamma_site_state
+{
+ /**
+ * Adjustment method implementation specific data.
+ * You as a user of this library should not touch this.
+ */
+ void* data;
+
+ /**
+ * This field specifies, for the methods if this library,
+ * which adjustment method (display server and protocol)
+ * is used to adjust the gamma ramps.
+ */
+ int method;
+
+ /**
+ * The site identifier. It can either be `NULL` or a string.
+ * `NULL` indicates the default site. On systems like the
+ * Unix-like systems, where the graphics are pluggable, this
+ * is usally resolved by an environment variable, such as
+ * "DISPLAY" for X.org.
+ */
+ char* site;
+
+ /**
+ * The number of partitions that is available on this site.
+ * Probably the majority of display server only one partition
+ * per site. However, X.org can, and traditional used to have
+ * on multi-headed environments, multiple partitions per site.
+ * In X.org partitions are called 'screens'. It is not to be
+ * confused with monitor. A screen is a collection of monitors,
+ * and the mapping from monitors to screens is a surjection.
+ * On hardware-level adjustment methods, such as Direct
+ * Rendering Manager, a partition is a graphics card.
+ */
+ size_t partitions_available;
+
+} libgamma_site_state_t;
+
+
+/**
+ * Partition state.
+ *
+ * Probably the majority of display server only one partition
+ * per site. However, X.org can, and traditional used to have
+ * on multi-headed environments, multiple partitions per site.
+ * In X.org partitions are called 'screens'. It is not to be
+ * confused with monitor. A screen is a collection of monitors,
+ * and the mapping from monitors to screens is a surjection.
+ * On hardware-level adjustment methods, such as Direct
+ * Rendering Manager, a partition is a graphics card.
+ */
+typedef struct libgamma_partition_state
+{
+ /**
+ * Adjustment method implementation specific data.
+ * You as a user of this library should not touch this.
+ */
+ void* data;
+
+ /**
+ * The site this partition belongs to.
+ */
+ libgamma_site_state_t* site;
+
+ /**
+ * The index of the partition.
+ */
+ size_t partition;
+
+ /**
+ * The number of CRTC:s that are available under this
+ * partition. Note that the CRTC:s are not necessarily
+ * online.
+ */
+ size_t crtcs_available;
+
+} libgamma_partition_state_t;
+
+
+/**
+ * Cathode ray tube controller state.
+ *
+ * The CRTC controls the gamma ramps for the
+ * monitor that is plugged in to the connector
+ * that the CRTC belongs to.
+ */
+typedef struct libgamma_crtc_state
+{
+ /**
+ * Adjustment method implementation specific data.
+ * You as a user of this library should not touch this.
+ */
+ void* data;
+
+ /**
+ * The partition this CRTC belongs to.
+ */
+ libgamma_partition_state_t* partition;
+
+ /**
+ * The index of the CRTC within its partition.
+ */
+ size_t crtc;
+
+} libgamma_crtc_state_t;
+
+
+/**
+ * Types for connectors.
+ */
+typedef enum libgamma_connector_type
+ {
+ /**
+ * The adjustment method does not know the connector's type
+ * (This could be considered an error).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_Unknown,
+
+ /**
+ * Video Graphics Array (VGA).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_VGA,
+
+ /**
+ * Digital Visual Interface, integrated (DVI-I).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_DVII,
+
+ /**
+ * Digital Visual Interface, digital only (DVI-D).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_DVID,
+
+ /**
+ * Digital Visual Interface, analogue only (DVI-A).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_DVIA,
+
+ /**
+ * Composite video.
+ */
+ LIBGAMMA_CONNECTOR_TYPE_Composite,
+
+ /**
+ * Separate Video (S-video).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_SVIDEO,
+
+ /**
+ * Low-voltage differential signaling (LVDS).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_LVDS,
+
+ /**
+ * Component video, usally separate cables for each channel.
+ */
+ LIBGAMMA_CONNECTOR_TYPE_Component,
+
+ /**
+ * 9 pin DIN (Deutsches Institut für Normung) connector.
+ */
+ LIBGAMMA_CONNECTOR_TYPE_9PinDIN,
+
+ /**
+ * DisplayPort.
+ */
+ LIBGAMMA_CONNECTOR_TYPE_DisplayPort,
+
+ /**
+ * High-Definition Multimedia Interface (HDMI), unknown type.
+ */
+ LIBGAMMA_CONNECTOR_TYPE_HDMI,
+
+ /**
+ * High-Definition Multimedia Interface, type A (HDMI-A).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_HDMIA,
+
+ /**
+ * High-Definition Multimedia Interface, type B (HDMI-B).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_HDMIB,
+
+ /**
+ * Television, unknown connector.
+ */
+ LIBGAMMA_CONNECTOR_TYPE_TV,
+
+ /**
+ * Embedded DisplayPort (eDP).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_eDP,
+
+ /**
+ * A virtual connector.
+ */
+ LIBGAMMA_CONNECTOR_TYPE_VIRTUAL,
+
+ /**
+ * Display Serial Interface (DSI).
+ */
+ LIBGAMMA_CONNECTOR_TYPE_DSI,
+
+ /**
+ * LFP connector.
+ * (If you know what this is add it to Wikipedia.)
+ */
+ LIBGAMMA_CONNECTOR_TYPE_LFP
+
+ } libgamma_connector_type_t;
+
+/**
+ * The number of values defined in `libgamma_connector_type_t`.
+ */
+#define LIBGAMMA_CONNECTOR_TYPE_COUNT 19
+
+/**
+ * Orders for subpixels. Currently the possible values are
+ * very biased to LCD, Plasma and monochrome monitors.
+ */
+typedef enum libgamma_subpixel_order
+ {
+ /**
+ * The adjustment method does not know the order of the subpixels.
+ * (This could be considered an error.)
+ */
+ LIBGAMMA_SUBPIXEL_ORDER_UNKNOWN,
+
+ /**
+ * There are no subpixels in the monitor.
+ */
+ LIBGAMMA_SUBPIXEL_ORDER_NONE,
+
+ /**
+ * The subpixels are ordered red, green and then blue, from left to right.
+ */
+ LIBGAMMA_SUBPIXEL_ORDER_HORIZONTAL_RGB,
+
+ /**
+ * The subpixels are ordered blue, green and then red, from left to right.
+ */
+ LIBGAMMA_SUBPIXEL_ORDER_HORIZONTAL_BGR,
+
+ /**
+ * The subpixels are ordered red, green and then blue, from the top down.
+ */
+ LIBGAMMA_SUBPIXEL_ORDER_VERTICAL_RGB,
+
+ /**
+ * The subpixels are ordered blue, green and then red, from the top down.
+ */
+ LIBGAMMA_SUBPIXEL_ORDER_VERTICAL_BGR
+
+ } libgamma_subpixel_order_t;
+
+/**
+ * The number of values defined in `libgamma_subpixel_order_t`.
+ */
+#define LIBGAMMA_SUBPIXEL_ORDER_COUNT 6
+
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * values for `edid` and `edid_length` and report errors to `edid_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_EDID (1 << 0)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `width_mm` and report errors to `width_mm_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_WIDTH_MM (1 << 1)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `height_mm` and report errors to `height_mm_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_HEIGHT_MM (1 << 2)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `width_mm_edid` and report errors to `width_mm_edid_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_WIDTH_MM_EDID (1 << 3)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `height_mm_edid` and report errors to `height_mm_edid_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_HEIGHT_MM_EDID (1 << 4)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * values for `red_gamma_size`, `green_gamma_size` and `blue_gamma_size`.
+ * and report errors to `gamma_size_error`
+ */
+#define LIBGAMMA_CRTC_INFO_GAMMA_SIZE (1 << 5)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `gamma_depth` and report errors to `gamma_depth_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_GAMMA_DEPTH (1 << 6)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `gamma_support` and report errors to `gamma_support_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_GAMMA_SUPPORT (1 << 7)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `subpixel_order` and report errors to `subpixel_order_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_SUBPIXEL_ORDER (1 << 8)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `active` and report errors to `active_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_ACTIVE (1 << 9)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `connector_name` and report errors to `connector_name_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_CONNECTOR_NAME (1 << 10)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * value for `connector_type` and report errors to `connector_type_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_CONNECTOR_TYPE (1 << 11)
+
+/**
+ * For a `libgamma_crtc_information_t` fill in the
+ * values for `gamma_red`, `gamma_green` and `gamma_blue`
+ * and report errors to `connector_type_error`.
+ */
+#define LIBGAMMA_CRTC_INFO_GAMMA (1 << 12)
+
+/**
+ * The number of `LIBGAMMA_CRTC_INFO_*` values defined.
+ */
+#define LIBGAMMA_CRTC_INFO_COUNT 13
+
+
+/**
+ * Cathode ray tube controller information data structure.
+ */
+typedef struct libgamma_crtc_information
+{
+ /**
+ * The Extended Display Identification Data associated with
+ * the attached monitor. This is raw byte array that is usually
+ * 128 bytes long. It is not NUL-terminate, rather its length
+ * is stored in `edid_size`.
+ */
+ unsigned char* edid;
+
+ /**
+ * The length of `edid`.
+ */
+ size_t edid_length;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int edid_error;
+
+
+ /**
+ * The phyical width, in millimetres, of the viewport of the
+ * attached monitor, as reported by the adjustment method. This
+ * value may be incorrect, which is a known issue with the X
+ * server where it is the result of the X server attempting
+ * the estimate the size on its own.
+ * Zero means that its is not applicable, which is the case
+ * for projectors.
+ */
+ size_t width_mm;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int width_mm_error;
+
+
+ /**
+ * The phyical height, in millimetres, of the viewport of the
+ * attached monitor, as reported by the adjustment method. This
+ * value may be incorrect, which is a known issue with the X
+ * server where it is the result of the X server attempting
+ * the estimate the size on its own.
+ * Zero means that its is not applicable, which is the case
+ * for projectors.
+ */
+ size_t height_mm;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int height_mm_error;
+
+
+ /**
+ * The phyical width, in millimetres, of the viewport of the
+ * attached monitor, as reported by it the monitor's Extended
+ * Display Information Data. This value can only contain whole
+ * centimetres, which means that the result is always zero
+ * modulus ten. However, this could change with revisions of
+ * the EDID structure.
+ * Zero means that its is not applicable, which is the case
+ * for projectors.
+ */
+ size_t width_mm_edid;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int width_mm_edid_error;
+
+
+ /**
+ * The phyical height, in millimetres, of the viewport of the
+ * attached monitor, as reported by it the monitor's Extended
+ * Display Information Data. This value can only contain whole
+ * centimetres, which means that the result is always zero
+ * modulus ten. However, this could change with revisions of
+ * the EDID structure.
+ * Zero means that its is not applicable, which is the case
+ * for projectors.
+ */
+ size_t height_mm_edid;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int height_mm_edid_error;
+
+
+ /**
+ * The size of the encoding axis of the red gamma ramp.
+ */
+ size_t red_gamma_size;
+
+ /**
+ * The size of the encoding axis of the green gamma ramp.
+ */
+ size_t green_gamma_size;
+
+ /**
+ * The size of the encoding axis of the blue gamma ramp.
+ */
+ size_t blue_gamma_size;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int gamma_size_error;
+
+
+ /**
+ * The bit-depth of the value axes of gamma ramps,
+ * -1 for single precision floating point, and -2 for
+ * double precision floating point.
+ */
+ signed gamma_depth;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int gamma_depth_error;
+
+
+ /**
+ * Non-zero gamma ramp adjustments are supported.
+ */
+ int gamma_support;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int gamma_support_error;
+
+
+ /**
+ * The layout of the subpixels.
+ * You cannot count on this value — especially for CRT:s —
+ * but it is provided anyway as a means of distinguishing monitors.
+ */
+ libgamma_subpixel_order_t subpixel_order;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int subpixel_order_error;
+
+
+ /**
+ * Whether there is a monitors connected to the CRTC.
+ */
+ int active;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int active_error;
+
+
+ /**
+ * The name of the connector as designated by the display
+ * server or as give by this library in case the display
+ * server lacks this feature.
+ */
+ char* connector_name;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int connector_name_error;
+
+
+ /**
+ * The type of the connector that is associated with the CRTC.
+ */
+ libgamma_connector_type_t connector_type;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int connector_type_error;
+
+
+ /**
+ * The gamma characteristics of the monitor as reported
+ * in its Extended Display Information Data. The value
+ * holds the value for the red channel. If you do not have
+ * and more accurate measurement of the gamma for the
+ * monitor this could be used to give a rought gamma
+ * correction; simply divide the value with 2.2 and use
+ * the result for the red channel in the gamma correction.
+ */
+ float gamma_red;
+
+ /**
+ * The gamma characteristics of the monitor as reported
+ * in its Extended Display Information Data. The value
+ * holds the value for the green channel. If you do not have
+ * and more accurate measurement of the gamma for the
+ * monitor this could be used to give a rought gamma
+ * correction; simply divide the value with 2.2 and use
+ * the result for the green channel in the gamma correction.
+ */
+ float gamma_green;
+
+ /**
+ * The gamma characteristics of the monitor as reported
+ * in its Extended Display Information Data. The value
+ * holds the value for the blue channel. If you do not have
+ * and more accurate measurement of the gamma for the
+ * monitor this could be used to give a rought gamma
+ * correction; simply divide the value with 2.2 and use
+ * the result for the blue channel in the gamma correction.
+ */
+ float gamma_blue;
+
+ /**
+ * Zero on success, positive it holds the value `errno` had
+ * when the reading failed, otherwise (negative) the value
+ * of an error identifier provided by this library.
+ */
+ int gamma_error;
+
+} libgamma_crtc_information_t;
+
+
+
+/**
+ * Gamma ramp structure for 16-bit gamma ramps.
+ */
+typedef struct libgamma_gamma_ramps
+{
+ /**
+ * The size of `red`.
+ */
+ size_t red_size;
+
+ /**
+ * The size of `green`.
+ */
+ size_t green_size;
+
+ /**
+ * The size of `blue`.
+ */
+ size_t blue_size;
+
+ /**
+ * The gamma ramp for the red channel.
+ */
+ uint16_t* red;
+
+ /**
+ * The gamma ramp for the green channel.
+ */
+ uint16_t* green;
+
+ /**
+ * The gamma ramp for the blue channel.
+ */
+ uint16_t* blue;
+
+} libgamma_gamma_ramps_t;
+
+
+/**
+ * Gamma ramp structure for 32-bit gamma ramps.
+ */
+typedef struct libgamma_gamma_ramps32
+{
+ /**
+ * The size of `red`.
+ */
+ size_t red_size;
+
+ /**
+ * The size of `green`.
+ */
+ size_t green_size;
+
+ /**
+ * The size of `blue`.
+ */
+ size_t blue_size;
+
+ /**
+ * The gamma ramp for the red channel.
+ */
+ uint32_t* red;
+
+ /**
+ * The gamma ramp for the green channel.
+ */
+ uint32_t* green;
+
+ /**
+ * The gamma ramp for the blue channel.
+ */
+ uint32_t* blue;
+
+} libgamma_gamma_ramps32_t;
+
+
+/**
+ * Gamma ramp structure for 64-bit gamma ramps.
+ */
+typedef struct libgamma_gamma_ramps64
+{
+ /**
+ * The size of `red`.
+ */
+ size_t red_size;
+
+ /**
+ * The size of `green`.
+ */
+ size_t green_size;
+
+ /**
+ * The size of `blue`.
+ */
+ size_t blue_size;
+
+ /**
+ * The gamma ramp for the red channel.
+ */
+ uint64_t* red;
+
+ /**
+ * The gamma ramp for the green channel.
+ */
+ uint64_t* green;
+
+ /**
+ * The gamma ramp for the blue channel.
+ */
+ uint64_t* blue;
+
+} libgamma_gamma_ramps64_t;
+
+
+/**
+ * Gamma ramp structure for `float` gamma ramps.
+ */
+typedef struct libgamma_gamma_rampsf
+{
+ /**
+ * The size of `red`.
+ */
+ size_t red_size;
+
+ /**
+ * The size of `green`.
+ */
+ size_t green_size;
+
+ /**
+ * The size of `blue`.
+ */
+ size_t blue_size;
+
+ /**
+ * The gamma ramp for the red channel.
+ */
+ float* red;
+
+ /**
+ * The gamma ramp for the green channel.
+ */
+ float* green;
+
+ /**
+ * The gamma ramp for the blue channel.
+ */
+ float* blue;
+
+} libgamma_gamma_rampsf_t;
+
+
+/**
+ * Gamma ramp structure for `double` gamma ramps.
+ */
+typedef struct libgamma_gamma_rampsd
+{
+ /**
+ * The size of `red`.
+ */
+ size_t red_size;
+
+ /**
+ * The size of `green`.
+ */
+ size_t green_size;
+
+ /**
+ * The size of `blue`.
+ */
+ size_t blue_size;
+
+ /**
+ * The gamma ramp for the red channel.
+ */
+ double* red;
+
+ /**
+ * The gamma ramp for the green channel.
+ */
+ double* green;
+
+ /**
+ * The gamma ramp for the blue channel.
+ */
+ double* blue;
+
+} libgamma_gamma_rampsd_t;
+
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_ramps_initialise(libgamma_gamma_ramps_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps_destroy(libgamma_gamma_ramps_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps_free(libgamma_gamma_ramps_t* restrict this);
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_ramps32_initialise(libgamma_gamma_ramps32_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps32_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps32_destroy(libgamma_gamma_ramps32_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps32_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps32_free(libgamma_gamma_ramps32_t* restrict this);
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_ramps64_initialise(libgamma_gamma_ramps64_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps64_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps64_destroy(libgamma_gamma_ramps64_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_ramps64_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_ramps64_free(libgamma_gamma_ramps64_t* restrict this);
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps.
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_rampsf_initialise(libgamma_gamma_rampsf_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_rampsf_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_rampsf_destroy(libgamma_gamma_rampsf_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_rampsf_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_rampsf_free(libgamma_gamma_rampsf_t* restrict this);
+
+
+/**
+ * Initialise a gamma ramp in the proper way that allows all adjustment
+ * methods to read from and write to it without causing segmentation violation.
+ *
+ * The input must have `red_size`, `green_size` and `blue_size` set to the
+ * sizes of the gamma ramps that should be allocated.
+ *
+ * @param this The gamma ramps
+ * @return Zero on success, -1 on allocation error, `errno` will be set accordingly.
+ */
+int libgamma_gamma_rampsd_initialise(libgamma_gamma_rampsd_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_rampsd_initialise` or otherwise
+ * initialises in the proper manner.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_rampsd_destroy(libgamma_gamma_rampsd_t* restrict this);
+
+/**
+ * Release resources that are held by a gamma ramp strcuture that
+ * has been allocated by `libgamma_gamma_rampsd_initialise` or otherwise
+ * initialises in the proper manner, as well as release the pointer
+ * to the structure.
+ *
+ * @param this The gamma ramps.
+ */
+void libgamma_gamma_rampsd_free(libgamma_gamma_rampsd_t* restrict this);
+
+
+
+#endif
+
diff --git a/src/lib/libgamma.h b/src/lib/libgamma.h
new file mode 100644
index 0000000..9a5b9be
--- /dev/null
+++ b/src/lib/libgamma.h
@@ -0,0 +1,30 @@
+/**
+ * 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/>.
+ */
+#ifndef LIBGAMMA_H
+#define LIBGAMMA_H
+
+
+#include "libgamma-config.h" /* Must be first. */
+
+#include "libgamma-method.h"
+#include "libgamma-facade.h"
+#include "libgamma-error.h"
+
+
+#endif
+