The recent fuss about f.lux on iPhone made me take another look at desktop solutions for shifting the screen’s color temperature. f.lux is only available as a linux binary, but there’s a program called redshift that may work.
Let’s try installing redshift package, on my regular desktop machine with other desktop like software on it.
redshift-1.10p2:py-crypto-2.6.1p0: ok redshift-1.10p2:py-beaker-1.6.2p4: ok redshift-1.10p2:py-MarkupSafe-0.23p0: ok redshift-1.10p2:py-mako-0.9.1p2: ok redshift-1.10p2:gobject-introspection-1.46.0: ok redshift-1.10p2:py-gobject3-common-3.18.2: ok redshift-1.10p2:python-3.4.3: ok redshift-1.10p2:py3-cairo-1.10.0p2: ok redshift-1.10p2:py3-gobject3-3.18.2: ok redshift-1.10p2:json-glib-1.0.4: ok redshift-1.10p2:dconf-0.24.0p1: ok redshift-1.10p2:gsettings-desktop-schemas-3.18.1: ok redshift-1.10p2:libproxy-0.4.11p3: ok redshift-1.10p2:glib2-networking-2.46.1: ok redshift-1.10p2:libsoup-2.52.2: ok redshift-1.10p2:geoclue2-2.4.0: ok redshift-1.10p2:adwaita-icon-theme-3.18.0: ok redshift-1.10p2:at-spi2-core-2.18.2: ok redshift-1.10p2:at-spi2-atk-2.18.1: ok redshift-1.10p2:gtk+3-3.18.3: ok redshift-1.10p2:py3-xdg-0.25p2: ok redshift-1.10p2: ok
Woah. I mean ok, I can totally see why I’d need to install python 3 to change color temps, but another icon theme is a bridge too far. Now to be fair most of this is the result of OpenBSD ports choosing to build and package optional dependencies, including the GUI. I could probably pare some of it down by compiling from source after divining the requisite options for autoconf. It just doesn’t seem like such a hard problem, though. (Not knocking redshift; I happen to prefer something very simple.)
The mechanism redshift uses to change the color temperature is XRandR, the X Resize and Rotate protocol. There’s even an official xrandr configuration utility. It has a gamma option! But it’s almost wholly unsuitable for this purpose. Specifying an RGB gamma of 1.0:0.9:0.4 doesn’t make the screen “warm”. It turns gray into an ugly puke color, but leaves white just as bright and cold as before. I don’t just want to bend the gamma curve, I want to compress/truncate it such that there aren’t any blue=255 pixels.
The function which xrandr uses for this purpose is XRRSetCrtcGamma which has no documentation because why would it. The protocol documents that the request exists, but not much about its operation and the Xlib style C function takes different arguments. So here’s how that works.
You start by getting information for each Canadian Radio-television and Telecommunications Commission (CRTC) with the XRRGetCrtcInfo function. Then you need the size of the gamma, (XRRGetCrtcGammaSize), and memory (XRRAllocGamma). This gives you a structure with three arrays in it, suitably named red, green, and blue. The index for each array is the “input” color value. The value at each index is the “output” color value, always scaled between 0 and 65535. A common configuration would have a gamma size of 256 (8 bits), and a flat gamma ramp would then be index times 256 for each value. If we wanted to really tone down the blues, we might do index times 128 for that channel, which results in a white (255,255,255) pixel looking like a (255,255,128) pixel on an uncorrected screen.
Setting the color temp of the screen really only requires about 40 lines of C (80 or so all inclusive). sct is a crude utility which does roughly that. I’d say exactly that, but some of the calculations aren’t actually exact. In any case, it looks much, much, better than anything xrandr is capable of delivering. It takes temperature values in the range 1000 to 10000.
cc -std=c99 -O2 -I /usr/X11R6/include -o sct sct.c -L /usr/X11R6/lib -lm -lX11 -lXrandr
Campfire style: sct 4500
Dust storm on Mars style: sct 2000
Coffee free all nighter style: sct 8000