sct

set color temperature
git clone anongit@rnpnr.xyz:sct.git
Log | Files | Refs | Feed | README | LICENSE

Commit: d8fbaa90d50211fac83f2e198c2dacaedd266726
Parent: a38a0df3f85e0c437de3a28cf1627bcae68ef4ff
Author: Fabian Foerg
Date:   Tue, 13 Aug 2019 21:43:09 -0700

Merge pull request #10 from X11-good-tools/master

fix #9: 1.5: Approximation of the redshift table (no limits)
Diffstat:
MCHANGELOG | 9+++++++++
MLICENSE | 37++++++++++++++++++++++++++++---------
MMakefile | 6+++---
MREADME.md | 11++++++++---
Dsct.c | 176-------------------------------------------------------------------------------
Mxsct.1 | 35++++++++++++++++++-----------------
Axsct.c | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 298 insertions(+), 208 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG @@ -1,5 +1,14 @@ PROJECT: https://github.com/faf0/sct +1.5: zvezdochiot on 08 Aug 2019 +* Option --verbose to display debugging information + +1.5: zvezdochiot on 07 Aug 2019 +* Approximation of the `redshift` table from https://github.com/jonls/redshift/blob/04760afe31bff5b26cf18fe51606e7bdeac15504/src/colorramp.c#L30-L273 without limits + +1.4: zvezdochiot on 04 Aug 2019 +* Read current temperature + 1.3: zvezdochiot on 02 Jan 2019 * Rename utilites for X11 * Add -h option to print usage information diff --git a/LICENSE b/LICENSE @@ -1,11 +1,30 @@ -This project is public domain, do as you wish +Public Domain Mark 1.0 + No Copyright -The whitepoints data within sct.c has been released into the public domain -by Ingo Thies: -"I have calculated the table by following mathematical rules of color - integration and conversion from the CIE 1931 color space to sRGB. + This work has been identified as being free of known restrictions + under copyright law, including all related and neighboring rights. - I doubt that a numerically computed color table is copyrightable at all - (in contrast to the actual software implementation). However, if it is - indeed copyrightable, I have no problem with releasing it into the - public domain." + You can copy, modify, distribute and perform the work, even for + commercial purposes, all without asking permission. See Other + Information below. + + Other Information + + The work may not be free of known copyright restrictions in all + jurisdictions. + + Persons may have other rights in or related to the work, such as + patent or trademark rights, and others may have rights in how the + work is used, such as publicity or privacy rights. + + In some jurisdictions moral rights of the author may persist beyond + the term of copyright. These rights may include the right to be + identified as the author and the right to object to derogatory + treatments. + + Unless expressly stated otherwise, the person who identified the work + makes no warranties about the work, and disclaims liability for all + uses of the work, to the fullest extent permitted by applicable law. + + When using or citing the work, you should not imply endorsement by + the author or the person who identified the work. diff --git a/Makefile b/Makefile @@ -1,8 +1,8 @@ PROG = xsct CC = gcc -CFLAGS = -Wall -std=c99 -O2 -I /usr/X11R6/include -LDFLAGS = -L /usr/X11R6/lib -lX11 -lXrandr -SRCS = sct.c +CFLAGS = -Wall -Wextra -Werror -pedantic -std=c99 -O2 -I /usr/X11R6/include +LDFLAGS = -L /usr/X11R6/lib -lX11 -lXrandr -lm -s +SRCS = xsct.c PREFIX = /usr BIN = $(PREFIX)/bin MAN = $(PREFIX)/share/man/man1 diff --git a/README.md b/README.md @@ -20,7 +20,7 @@ Minor modifications were made in order to get sct to: Compile the code using the following command: ~~~ -cc -std=c99 -O2 -I /usr/X11R6/include sct.c -o xsct -L /usr/X11R6/lib -lX11 -lXrandr +gcc -Wall -Wextra -Werror -pedantic -std=c99 -O2 -I /usr/X11R6/include sct.c -o xsct -L /usr/X11R6/lib -lX11 -lXrandr -lm -s ~~~ Execute sct using the following command: @@ -29,11 +29,16 @@ Execute sct using the following command: ./xsct 3700 ~~~ -The first parameter (`3700` above) denotes the color temperature and can be -between `1000` and `10000`. +The first parameter (`3700` above) denotes the color temperature. If `xsct` is called with parameter 0, the color temperature is set to `6500`. If `xsct` is called without parameters, the current display temperature is estimated. +Test sct using the following command: + +~~~ +./xsct 3700 && ./xsct +~~~ + --- https://github.com/faf0/sct/ diff --git a/sct.c b/sct.c @@ -1,176 +0,0 @@ -/* - * xsct - X11 set color temperature - * - * Compile the code using the following command: - * cc -std=c99 -O2 -I /usr/X11R6/include sct.c -o xsct -L /usr/X11R6/lib -lX11 -lXrandr - * - * Original code published by Ted Unangst: - * http://www.tedunangst.com/flak/post/sct-set-color-temperature - * - * Modified by Fabian Foerg in order to: - * - compile on Ubuntu 14.04 - * - iterate over all screens of the default display and change the color - * temperature - * - fix memleaks - * - clean up code - * - return EXIT_SUCCESS - * - * Public domain, do as you wish. - */ - -#include <X11/Xatom.h> -#include <X11/Xlib.h> -#include <X11/Xproto.h> -#include <X11/extensions/Xrandr.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -static void usage() -{ - printf("Usage: xsct [temperature]\n" - "Temperatures must be in a range from 1000-10000\n" - "If the argument is 0, xsct resets the display to the default temperature (6500K)\n" - "If no arguments are passed, xsct estimates the current display temperature\n" - "If -h or --help is passed xsct will display this usage information\n"); -} - -/* cribbed from redshift, but truncated with 500K steps */ -static const struct { float r; float g; float b; } whitepoints[] = { - { 1.00000000, 0.18172716, 0.00000000, }, /* 1000K */ - { 1.00000000, 0.42322816, 0.00000000, }, - { 1.00000000, 0.54360078, 0.08679949, }, - { 1.00000000, 0.64373109, 0.28819679, }, - { 1.00000000, 0.71976951, 0.42860152, }, - { 1.00000000, 0.77987699, 0.54642268, }, - { 1.00000000, 0.82854786, 0.64816570, }, - { 1.00000000, 0.86860704, 0.73688797, }, - { 1.00000000, 0.90198230, 0.81465502, }, - { 1.00000000, 0.93853986, 0.88130458, }, - { 1.00000000, 0.97107439, 0.94305985, }, - { 1.00000000, 1.00000000, 1.00000000, }, /* 6500K */ - { 0.95160805, 0.96983355, 1.00000000, }, - { 0.91194747, 0.94470005, 1.00000000, }, - { 0.87906581, 0.92357340, 1.00000000, }, - { 0.85139976, 0.90559011, 1.00000000, }, - { 0.82782969, 0.89011714, 1.00000000, }, - { 0.80753191, 0.87667891, 1.00000000, }, - { 0.78988728, 0.86491137, 1.00000000, }, /* 10000K */ - { 0.77442176, 0.85453121, 1.00000000, }, -}; -#define TEMPERATURE_DEFAULT 6500 -#define TEMPERATURE_MIN 1000 -#define TEMPERATURE_MAX 10000 -#define TEMPERATURE_STEP 500 -#define GAMMA_MULT 65535.0 -#define AVG(c,temp,ratio) whitepoints[(temp) / TEMPERATURE_STEP].c * (1 - (ratio)) + whitepoints[(temp) / TEMPERATURE_STEP + 1].c * (ratio) - -static int get_sct_for_screen(Display *dpy, int screen) -{ - Window root = RootWindow(dpy, screen); - XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root); - - int temp = 0; - double t, ts; - double gammar, gammag, gammab; - - int n = res->ncrtc; - ts = 0.0; - for (int c = 0; c < n; c++) - { - int crtcxid = res->crtcs[c]; - XRRCrtcGamma *crtc_gamma = XRRGetCrtcGamma(dpy, crtcxid); - int size = crtc_gamma->size; - - if (size > 0) - { - double g_inv = (size + 1) / size / GAMMA_MULT; - gammar = crtc_gamma->red[size - 1] * g_inv; - gammag = crtc_gamma->green[size - 1] * g_inv; - gammab = crtc_gamma->blue[size - 1] * g_inv; - t = (64465 - 109049 * gammar + 46013 * gammar * gammar - - 4322 * gammag + 10708 * gammag * gammag - - 2662 * gammab + 1355 * gammab * gammab); - ts += t; - } - - XFree(crtc_gamma); - } - temp = (int)(ts / (double)n); - - XFree(res); - return temp; -} - -static void sct_for_screen(Display *dpy, int screen, int temp) -{ - Window root = RootWindow(dpy, screen); - XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root); - - temp -= TEMPERATURE_MIN; - double ratio = temp % TEMPERATURE_STEP / TEMPERATURE_STEP; - double gammar = AVG(r, temp, ratio); - double gammag = AVG(g, temp, ratio); - double gammab = AVG(b, temp, ratio); - - int n = res->ncrtc; - for (int c = 0; c < n; c++) - { - int crtcxid = res->crtcs[c]; - int size = XRRGetCrtcGammaSize(dpy, crtcxid); - - XRRCrtcGamma *crtc_gamma = XRRAllocGamma(size); - - for (int i = 0; i < size; i++) - { - double g = GAMMA_MULT * i / size; - crtc_gamma->red[i] = g * gammar; - crtc_gamma->green[i] = g * gammag; - crtc_gamma->blue[i] = g * gammab; - } - - XRRSetCrtcGamma(dpy, crtcxid, crtc_gamma); - XFree(crtc_gamma); - } - - XFree(res); -} - -int main(int argc, char **argv) -{ - Display *dpy = XOpenDisplay(NULL); - if (!dpy) { - perror("XOpenDisplay(NULL) failed"); - fprintf(stderr, "Make sure DISPLAY is set correctly.\n"); - return EXIT_FAILURE; - } - int screens = XScreenCount(dpy); - - int temp = TEMPERATURE_DEFAULT; - if (argc > 1) - { - if (!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")) - { - usage(); - } else { - temp = atoi(argv[1]); - if (temp < TEMPERATURE_MIN || temp > TEMPERATURE_MAX) - temp = TEMPERATURE_DEFAULT; - - for (int screen = 0; screen < screens; screen++) - sct_for_screen(dpy, screen, temp); - } - } else { - for (int screen = 0; screen < screens; screen++) - { - temp = get_sct_for_screen(dpy, screen); - printf("Screen %d: temperature ~ %d\n", screen, temp); - } - } - - XCloseDisplay(dpy); - - return EXIT_SUCCESS; -} - diff --git a/xsct.1 b/xsct.1 @@ -1,31 +1,32 @@ -.TH xsct 1 "Aug 2019" "1.4" "User Manual" +.TH xsct 1 "Aug 2019" "1.5" "User Manual" .SH NAME xsct \- X11 set screen color temperature .SH SYNOPSIS .B xsct -[temperature] - -or - -.B xsct -[-h || --help] - +[options] +.I [temperature] .SH DESCRIPTION .B xsct -sets the screen's color temperature in a range from 1000 to 10000 +sets the screen's color temperature. .SH OPTIONS -.IP temperature -If passed a value in the correct range (see above) -.B xsct -will set the current screen temperature to this value -.IP 0 -If the value is 0, xsct sets the color temperature to the default of 6500 -.IP -h,--help +.TP +.B -h, --help Display usage information and exit -.IP none +.TP +.B -v, --verbose +Display debugging information +.TP +.I [temperature] +Black body temperature +.br +If the value is 0, xsct sets the color temperature to the default of 6500 +.br If no arguments are passed, xsct estimates the current display temperature .SH AUTHOR xsct was written by Ted Unangst <tedu@openbsd.org> + +.SH SEE ALSO +redshift(1), xtemp(1) diff --git a/xsct.c b/xsct.c @@ -0,0 +1,232 @@ +/* + * xsct - X11 set color temperature + * + * Original code published by Ted Unangst: + * http://www.tedunangst.com/flak/post/sct-set-color-temperature + * + * Modified by Fabian Foerg in order to: + * - compile on Ubuntu 14.04 + * - iterate over all screens of the default display and change the color + * temperature + * - fix memleaks + * - clean up code + * - return EXIT_SUCCESS + * + * Public domain, do as you wish. + * + * Compile the code using the following command: + * gcc -Wall -Wextra -Werror -pedantic -std=c99 -O2 -I /usr/X11R6/include xsct.c -o xsct -L /usr/X11R6/lib -lX11 -lXrandr -lm -s + * + */ + +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#include <X11/Xproto.h> +#include <X11/extensions/Xrandr.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +static void usage(char * pname) +{ + printf("Xsct (1.5)\n" + "Usage: %s [options] [temperature]\n" + "\tIf the argument is 0, xsct resets the display to the default temperature (6500K)\n" + "\tIf no arguments are passed, xsct estimates the current display temperature\n" + "Options:\n" + "\t-v, --verbose \t xsct will display debugging information\n" + "\t-h, --help \t xsct will display this usage information\n", pname); +} + +#define TEMPERATURE_NORM 6500 +#define TEMPERATURE_ZERO 700 +#define GAMMA_MULT 65535.0 +// Approximation of the `redshift` table from +// https://github.com/jonls/redshift/blob/04760afe31bff5b26cf18fe51606e7bdeac15504/src/colorramp.c#L30-L273 +// without limits: +// GAMMA = K0 + K1 * ln(T - T0) +// Red range (T0 = TEMPERATURE_ZERO) +// Green color +#define GAMMA_K0GR -1.47751309139817 +#define GAMMA_K1GR 0.28590164772055 +// Blue color +#define GAMMA_K0BR -4.38321650114872 +#define GAMMA_K1BR 0.6212158769447 +// Blue range (T0 = TEMPERATURE_NORM - TEMPERATURE_ZERO) +// Red color +#define GAMMA_K0RB 1.75390204039018 +#define GAMMA_K1RB -0.1150805671482 +// Green color +#define GAMMA_K0GB 1.49221604915144 +#define GAMMA_K1GB -0.07513509588921 + +static double DoubleTrim(double x, double a, double b) +{ + double buff[3] = {a, x, b}; + return buff[ (int)(x > a) + (int)(x > b) ]; +} + +static int get_sct_for_screen(Display *dpy, int screen, int fdebug) +{ + Window root = RootWindow(dpy, screen); + XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root); + + int temp = 0, n, c; + double t = 0.0; + double gammar = 0.0, gammag = 0.0, gammab = 0.0, gammam = 1.0, gammad = 0.0; + + n = res->ncrtc; + for (c = 0; c < n; c++) + { + RRCrtc crtcxid; + int size; + XRRCrtcGamma *crtc_gamma; + crtcxid = res->crtcs[c]; + crtc_gamma = XRRGetCrtcGamma(dpy, crtcxid); + size = crtc_gamma->size; + gammar += crtc_gamma->red[size - 1]; + gammag += crtc_gamma->green[size - 1]; + gammab += crtc_gamma->blue[size - 1]; + + XRRFreeGamma(crtc_gamma); + } + XFree(res); + gammam = (gammar > gammag) ? gammar : gammag; + gammam = (gammab > gammam) ? gammab : gammam; + if (gammam > 0.0) + { + gammar /= gammam; + gammag /= gammam; + gammab /= gammam; + if (fdebug > 0) fprintf(stderr, "DEBUG: Gamma: %f, %f, %f\n", gammar, gammag, gammab); + gammad = gammab - gammar; + if (gammad < 0.0) + { + if (gammab > 0.0) + { + t = exp((gammag + 1.0 + gammad - (GAMMA_K0GR + GAMMA_K0BR)) / (GAMMA_K1GR + GAMMA_K1BR)) + TEMPERATURE_ZERO; + } + else + { + t = (gammag > 0.0) ? (exp((gammag - GAMMA_K0GR) / GAMMA_K1GR) + TEMPERATURE_ZERO) : TEMPERATURE_ZERO; + } + } + else + { + t = exp((gammag + 1.0 - gammad - (GAMMA_K0GB + GAMMA_K0RB)) / (GAMMA_K1GB + GAMMA_K1RB)) + (TEMPERATURE_NORM - TEMPERATURE_ZERO); + } + } + + temp = (int)(t + 0.5); + + return temp; +} + +static void sct_for_screen(Display *dpy, int screen, int temp, int fdebug) +{ + double t = 0.0, g = 0.0, gammar, gammag, gammab; + int n, c; + Window root = RootWindow(dpy, screen); + XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root); + + t = (double)temp; + if (temp < TEMPERATURE_NORM) + { + gammar = 1.0; + if (temp < TEMPERATURE_ZERO) + { + gammag = 0.0; + gammab = 0.0; + } + else + { + g = log(t - TEMPERATURE_ZERO); + gammag = DoubleTrim(GAMMA_K0GR + GAMMA_K1GR * g, 0.0, 1.0); + gammab = DoubleTrim(GAMMA_K0BR + GAMMA_K1BR * g, 0.0, 1.0); + } + } + else + { + g = log(t - (TEMPERATURE_NORM - TEMPERATURE_ZERO)); + gammar = DoubleTrim(GAMMA_K0RB + GAMMA_K1RB * g, 0.0, 1.0); + gammag = DoubleTrim(GAMMA_K0GB + GAMMA_K1GB * g, 0.0, 1.0); + gammab = 1.0; + } + if (fdebug > 0) fprintf(stderr, "DEBUG: Gamma: %f, %f, %f\n", gammar, gammag, gammab); + + n = res->ncrtc; + for (c = 0; c < n; c++) + { + int size, i; + RRCrtc crtcxid; + XRRCrtcGamma *crtc_gamma; + crtcxid = res->crtcs[c]; + size = XRRGetCrtcGammaSize(dpy, crtcxid); + + crtc_gamma = XRRAllocGamma(size); + + for (i = 0; i < size; i++) + { + g = GAMMA_MULT * (double)i / (double)size; + crtc_gamma->red[i] = (unsigned short int)(g * gammar + 0.5); + crtc_gamma->green[i] = (unsigned short int)(g * gammag + 0.5); + crtc_gamma->blue[i] = (unsigned short int)(g * gammab + 0.5); + } + + XRRSetCrtcGamma(dpy, crtcxid, crtc_gamma); + XRRFreeGamma(crtc_gamma); + } + + XFree(res); +} + +int main(int argc, char **argv) +{ + int i, screen, screens, temp; + int fdebug = 0, fhelp = 0; + Display *dpy = XOpenDisplay(NULL); + if (!dpy) + { + perror("XOpenDisplay(NULL) failed"); + fprintf(stderr, "Make sure DISPLAY is set correctly.\n"); + return EXIT_FAILURE; + } + screens = XScreenCount(dpy); + + temp = -1; + for (i = 1; i < argc; i++) + { + if ((strcmp(argv[i],"-v") == 0) || (strcmp(argv[i],"--verbose") == 0)) fdebug = 1; + else if ((strcmp(argv[i],"-h") == 0) || (strcmp(argv[i],"--help") == 0)) fhelp = 1; + else temp = atoi(argv[i]); + } + if (fhelp > 0) + { + usage(argv[0]); + } + else + { + if (temp < 0) + { + for (screen = 0; screen < screens; screen++) + { + temp = get_sct_for_screen(dpy, screen, fdebug); + printf("Screen %d: temperature ~ %d\n", screen, temp); + } + } + else + { + temp = (temp == 0) ? TEMPERATURE_NORM : temp; + + for (screen = 0; screen < screens; screen++) + sct_for_screen(dpy, screen, temp, fdebug); + } + } + + XCloseDisplay(dpy); + + return EXIT_SUCCESS; +} +