diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | ggamma.c | 149 |
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; +} |