@@ 1,253 @@
+/*
+ * drmgamma.c
+ * Copyright (C) 2020 Adrian Perez de Castro <aperez@igalia.com>
+ *
+ * Distributed under terms of the MIT license.
+ */
+
+#define _POSIX_C_SOURCE 2
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+static int
+usage(int code)
+{
+ FILE *f = (code == EXIT_SUCCESS) ? stdout : stderr;
+ fputs("usage: drmgamma -d DEVICE [-r NUM] [-g NUM] [-b NUM]\n", f);
+ return code;
+}
+
+static int
+error(const char *fmt, ...)
+{
+ assert(fmt);
+
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+ fflush(stderr);
+ return EXIT_FAILURE;
+}
+
+static bool
+optdouble(double *opt, const char *str)
+{
+ assert(opt);
+ assert(str);
+
+ errno = 0;
+ char *endptr = NULL;
+ *opt = strtod(str, &endptr);
+
+ /* Conversion error */
+ if (errno != 0 || str == endptr || *endptr != '\0')
+ return false;
+
+ /* Invalid values for gamma */
+ if (isinf(*opt) || isnan(*opt) || *opt <= 0.0e-12)
+ return false;
+
+ return true;
+}
+
+static const char*
+connector_type_string (uint32_t type)
+{
+ switch (type) {
+#ifdef DRM_MODE_CONNECTOR_DPI
+ case DRM_MODE_CONNECTOR_DPI: return "DPI";
+#endif /* DRM_MODE_CONNECTOR_DPI */
+#ifdef DRM_MODE_CONNECTOR_DSI
+ case DRM_MODE_CONNECTOR_DSI: return "DSI";
+#endif /* DRM_MODE_CONNECTOR_DSI */
+#ifdef DRM_MODE_CONNECTOR_VIRTUAL
+ case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";
+#endif /* DRM_MODE_CONNECTOR_VIRTUAL */
+#ifdef DRM_MODE_CONNECTOR_eDP
+ case DRM_MODE_CONNECTOR_eDP: return "eDP";
+#endif /* DRM_MODE_CONNECTOR_eDP */
+#ifdef DRM_MODE_CONNECTOR_TV
+ case DRM_MODE_CONNECTOR_TV: return "TV";
+#endif /* DRM_MODE_CONNECTOR_TV */
+#ifdef DRM_MODE_CONNECTOR_HDMIB
+ case DRM_MODE_CONNECTOR_HDMIB: return "HDMI-B";
+#endif /* DRM_MODE_CONNECTOR_HDMIB */
+#ifdef DRM_MODE_CONNECTOR_HDMIA
+ case DRM_MODE_CONNECTOR_HDMIA: return "HDMI-A";
+#endif /* DRM_MODE_CONNECTOR_HDMIA */
+#ifdef DRM_MODE_CONNECTOR_DisplayPort
+ case DRM_MODE_CONNECTOR_DisplayPort: return "DisplayPort";
+#endif /* DRM_MODE_CONNECTOR_DisplayPort */
+#ifdef DRM_MODE_CONNECTOR_9PinDIN
+ case DRM_MODE_CONNECTOR_9PinDIN: return "9-pin DIN";
+#endif /* DRM_MODE_CONNECTOR_9PinDIN */
+#ifdef DRM_MODE_CONNECTOR_Component
+ case DRM_MODE_CONNECTOR_Component: return "Component";
+#endif /* DRM_MODE_CONNECTOR_Component */
+#ifdef DRM_MODE_CONNECTOR_LVDS
+ case DRM_MODE_CONNECTOR_LVDS: return "LVDS";
+#endif /* DRM_MODE_CONNECTOR_LVDS */
+#ifdef DRM_MODE_CONNECTOR_SVIDEO
+ case DRM_MODE_CONNECTOR_SVIDEO: return "S-Video";
+#endif /* DRM_MODE_CONNECTOR_SVIDEO */
+#ifdef DRM_MODE_CONNECTOR_Composite
+ case DRM_MODE_CONNECTOR_Composite: return "Composite";
+#endif /* DRM_MODE_CONNECTOR_Composite */
+#ifdef DRM_MODE_CONNECTOR_DVIA
+ case DRM_MODE_CONNECTOR_DVIA: return "DVI-A";
+#endif /* DRM_MODE_CONNECTOR_DVIA */
+#ifdef DRM_MODE_CONNECTOR_DVID
+ case DRM_MODE_CONNECTOR_DVID: return "DVI-D";
+#endif /* DRM_MODE_CONNECTOR_DVID */
+#ifdef DRM_MODE_CONNECTOR_DVII
+ case DRM_MODE_CONNECTOR_DVII: return "DVI-I";
+#endif /* DRM_MODE_CONNECTOR_DVII */
+#ifdef DRM_MODE_CONNECTOR_VGA
+ case DRM_MODE_CONNECTOR_VGA: return "VGA";
+#endif /* DRM_MODE_CONNECTOR_VGA */
+#ifdef DRM_MODE_CONNECTOR_Unknown
+ case DRM_MODE_CONNECTOR_Unknown:
+#endif /* DRM_MODE_CONNECTOR_Unknown */
+ default: return "Unknown";
+ }
+}
+
+static bool opt_verbose = false;
+
+static void
+verbose(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if (opt_verbose)
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *devnode = NULL;
+
+ double gamma_r = 1.0;
+ double gamma_g = 1.0;
+ double gamma_b = 1.0;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "hvd:r:g:b:")) != -1) {
+ switch (opt) {
+ case 'v':
+ opt_verbose = true;
+ break;
+ case 'd':
+ devnode = optarg;
+ break;
+ case 'r':
+ if (!optdouble(&gamma_r, optarg))
+ return error("Invalid number: '%s'", optarg);
+ break;
+ case 'g':
+ if (!optdouble(&gamma_g, optarg))
+ return error("Invalid number: '%s'", optarg);
+ break;
+ case 'b':
+ if (!optdouble(&gamma_b, optarg))
+ return error("Invalid number: '%s'", optarg);
+ break;
+ case 'h':
+ return usage(EXIT_SUCCESS);
+ default:
+ return usage(EXIT_FAILURE);
+ }
+ }
+
+ if (!devnode)
+ return error("DRM device not specified.");
+
+ if (!drmAvailable())
+ return error("DRM unavailable.");
+
+ int fd = open(devnode, O_RDWR, 0);
+ if (fd < 0)
+ return error("Cannot open '%s': %s.", devnode, strerror(errno));
+
+ if (!drmIsMaster(fd)) {
+ if (drmSetMaster(fd))
+ return error("Could not become DRM master: %s.", strerror(errno));
+ }
+
+ drmModeRes *resources = drmModeGetResources(fd);
+ if (resources && resources->count_connectors) {
+ for (int i = 0; i < resources->count_connectors; ++i) {
+ drmModeConnector *connector =
+ drmModeGetConnector(fd, resources->connectors[i]);
+ if (connector->connection != DRM_MODE_CONNECTED) {
+ verbose("Skipping %s: disconnected.\n",
+ connector_type_string(connector->connector_type));
+ } else if (connector->count_modes == 0) {
+ verbose("Skipping %s: no usable modes.\n",
+ connector_type_string(connector->connector_type));
+ } else {
+ /* Find the encoder that is driving this connector. */
+ drmModeEncoder *encoder = NULL;
+ for (int j = 0; j < resources->count_encoders; ++j) {
+ drmModeEncoder *cur_encoder =
+ drmModeGetEncoder(fd, resources->encoders[j]);
+ if (cur_encoder->encoder_id == connector->encoder_id) {
+ encoder = cur_encoder;
+ break;
+ }
+ drmModeFreeEncoder(cur_encoder);
+ }
+
+ if (encoder) {
+ drmModeCrtc *crtc = drmModeGetCrtc(fd, encoder->crtc_id);
+ verbose("Setting %s, CRTC %" PRIu32 ", %d gamma stops.\n",
+ connector_type_string(connector->connector_type),
+ encoder->crtc_id, crtc->gamma_size);
+
+ const uint32_t size = crtc->gamma_size;
+
+ /* Calculate gamma ramp LUTs. */
+ uint16_t lut_r[size], lut_g[size], lut_b[size];
+
+ for (size_t k = 0; k < size; ++k) {
+ const double g = (double) UINT16_MAX * k / size;
+ lut_r[k] = g * gamma_r;
+ lut_g[k] = g * gamma_g;
+ lut_b[k] = g * gamma_b;
+ }
+
+ if (drmModeCrtcSetGamma(fd, crtc->crtc_id, size,
+ lut_r, lut_g, lut_b) != 0) {
+ verbose("Could not set gamma: %s.\n", strerror(errno));
+ }
+
+ drmModeFreeCrtc(crtc);
+ drmModeFreeEncoder(encoder);
+ } else {
+ verbose("Skipping %s: could not find encoder.\n",
+ connector_type_string(connector->connector_type));
+ }
+ }
+ drmModeFreeConnector(connector);
+ }
+ }
+
+ close(fd);
+ return EXIT_SUCCESS;
+}
@@ 1,14 @@
+project('drmgamma', 'c',
+ meson_version: '>=0.49',
+ default_options: [
+ 'b_ndebug=if-release',
+ 'c_std=c99',
+ ],
+ license: 'MIT',
+ version: '1',
+)
+
+executable('drmgamma',
+ 'drmgamma.c',
+ dependencies: dependency('libdrm'),
+)