summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile12
-rw-r--r--ggamma.c149
3 files changed, 163 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a7c195f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/ggamma
+*.o
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..492bf31
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
+CFLAGS += -std=gnu99 -Wall -Werror -Wextra -g
+
+CFLAGS += $(shell pkg-config --cflags x11)
+LDFLAGS += $(shell pkg-config --libs x11)
+
+CFLAGS += $(shell pkg-config --cflags xrandr)
+LDFLAGS += $(shell pkg-config --libs xrandr)
+
+CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
+LDFLAGS += $(shell pkg-config --libs gtk+-2.0)
+
+ggamma: ggamma.o
diff --git a/ggamma.c b/ggamma.c
new file mode 100644
index 0000000..3cbc5d7
--- /dev/null
+++ b/ggamma.c
@@ -0,0 +1,149 @@
+#include <stdlib.h> /* for getenv(3) */
+#include <error.h> /* for error(3) */
+#include <stdint.h> /* for uint16_t */
+#include <stdio.h>
+
+#include <X11/Xlib.h> /* for Display, XOpenDisplay(3) */
+
+#include <X11/extensions/Xrandr.h> /* for XRRQueryExtension(3) */
+
+#include <gtk/gtk.h>
+
+#define UNUSED __attribute__((unused))
+
+struct curve_data {
+ int gamma_size;
+ void (*cb)(uint16_t *ivec);
+};
+
+void curve_handle_update(GtkWidget *raw_curve, UNUSED GdkEvent *event, struct curve_data *data) {
+ gfloat fvec[data->gamma_size];
+ uint16_t ivec[data->gamma_size];
+
+ gtk_curve_get_vector(GTK_CURVE(raw_curve), data->gamma_size, fvec);
+ for (int i = 0; i < data->gamma_size; i++)
+ ivec[i] = (uint16_t)(fvec[i] * 65535.0 / data->gamma_size);
+
+ if (data->cb)
+ data->cb(ivec);
+}
+
+GtkWidget *curve_new(int gamma_size, uint16_t *ivec, void (*cb)(uint16_t *ivec)) {
+ GtkWidget *curve = gtk_gamma_curve_new();
+ GtkWidget *raw_curve = GTK_GAMMA_CURVE(curve)->curve;
+
+ /* Technically, the range of the curve should be [0,2¹⁶), but
+ * the X server will just divide it to fit in [0,gamma_size).
+ * So there's no point in making it awkwardly tall to get the
+ * extra precision; just make it a square, we're not really
+ * loosing anything. */
+
+ gtk_curve_set_range(GTK_CURVE(raw_curve),
+ /* x */0, gamma_size-1,
+ /* y */0, gamma_size-1);
+
+ gfloat fvec[gamma_size];
+ for (int i = 0; i < gamma_size; i++)
+ fvec[i] = ivec[i] * gamma_size / 65535.0;
+ gtk_curve_set_vector(GTK_CURVE(raw_curve),
+ gamma_size, fvec);
+
+ // TODO: this memory gets leaked
+ struct curve_data *data = malloc(sizeof(struct curve_data));
+ data->gamma_size = gamma_size;
+ data->cb = cb;
+ // TODO: find a better signal
+ g_signal_connect(raw_curve, "event-after", G_CALLBACK(curve_handle_update), data);
+
+ return curve;
+}
+
+int main(int argc, char *argv[]) {
+ char *display_name = getenv("DISPLAY");
+ GError *err = NULL;
+ GOptionEntry options[] = {
+ {"display", 'd', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &display_name, "X display to set gamma for", "DISPLAY"},
+ {0},
+ };
+ if (!gtk_init_with_args(&argc, &argv, "", options, NULL, &err))
+ error(1, 0, "Unable to initialize GTK+: %s", err->message);
+
+ Display *display = XOpenDisplay(display_name);
+ if (!display)
+ error(1, 0, "Cannot open target display %s", display_name);
+
+ /* RRGetScreenResources[Current] takes a Window, and operates
+ * on the Screen associated with that Window, because taking
+ * the Screen itself as an argument just makes too much
+ * sense. */
+ XRRScreenResources *res = XRRGetScreenResourcesCurrent(display, RootWindow(display, DefaultScreen(display)));
+ if (res->ncrtc < 1)
+ error(1, 0, "There aren't any CRTCs on display %s's default screen (%d)", display_name, DefaultScreen(display));
+ for (int i = 0; i < res->ncrtc; i++) {
+ RRCrtc crtc = res->crtcs[i];
+ printf("crtc xid: %ld gamma_size: %d\n", crtc, XRRGetCrtcGammaSize(display, crtc));
+ }
+ RRCrtc crtc = res->crtcs[0]; // TODO: real CTRC selection
+ int gamma_size = XRRGetCrtcGammaSize(display, crtc);
+ XRRCrtcGamma *gamma = XRRAllocGamma(gamma_size);
+
+ { GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
+ g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
+
+ // TODO: add a xrandr(1) emulation pane
+
+ { GtkWidget *layout = gtk_hbox_new(TRUE, 0);
+ gtk_container_add(GTK_CONTAINER(window), layout);
+ gtk_widget_show(layout);
+
+ XRRCrtcGamma *orig_gamma = XRRGetCrtcGamma(display, crtc);
+
+ /* red */
+ {
+ void set_red(uint16_t *vec) {
+ for (int i = 0; i < gamma_size; i++)
+ gamma->red[i] = vec[i];
+ XRRSetCrtcGamma(display, crtc, gamma);
+ }
+ GtkWidget *curve = curve_new(gamma_size, orig_gamma->red, set_red);
+ gtk_container_add(GTK_CONTAINER(layout), curve);
+ gtk_widget_show(curve);
+ }
+
+ /* green */
+ {
+ void set_green(uint16_t *vec) {
+ for (int i = 0; i < gamma_size; i++)
+ gamma->green[i] = vec[i];
+ XRRSetCrtcGamma(display, crtc, gamma);
+ }
+ GtkWidget *curve = curve_new(gamma_size, orig_gamma->green, set_green);
+ gtk_container_add(GTK_CONTAINER(layout), curve);
+ gtk_widget_show(curve);
+ }
+
+ /* blue */
+ {
+ void set_blue(uint16_t *vec) {
+ for (int i = 0; i < gamma_size; i++)
+ gamma->blue[i] = vec[i];
+ XRRSetCrtcGamma(display, crtc, gamma);
+ }
+ GtkWidget *curve = curve_new(gamma_size, orig_gamma->blue, set_blue);
+ gtk_container_add(GTK_CONTAINER(layout), curve);
+ gtk_widget_show(curve);
+ }
+
+ XRRFreeGamma(orig_gamma);
+
+ }
+
+ gtk_widget_show(window);
+ }
+
+ gtk_main();
+ XFree(gamma);
+ XCloseDisplay(display);
+ return 0;
+}