/* $Id: sct.c 24 2020-01-28 23:38:44Z umaxx $
 * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
 * Copyright (c) 2016-2020 Joerg Jung <jung@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrandr.h>

#define S_V "0.5"
#define S_YR "2015-2020"

enum { S_RED, S_GREEN, S_BLUE, S_MAX };

static const float s_wp[][S_MAX] = { /* whitepoint values from redshift */
    { 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 } };

static double s_avg(int t, double r, int c) {
    return s_wp[t / 500][c] * (1 - r) + s_wp[t / 500 + 1][c] * r;
}

#define S_YELLOW "\x1b[93m"
#define S_RST "\x1b[0m"

static void s_run(const char *s) {
    Display *d;
    XRRScreenResources *sr = NULL;
    XRRCrtcGamma *cg;
    unsigned long t;
    double r, g;
    int i, j, sz;
    char *e;

    errno = 0, t = strtoul(s, &e, 10);
    if (s[0] == '\0' || *e != '\0' || (errno == ERANGE && t == ULONG_MAX))
        errx(1, "temp failed");
    if (t < 1000 || t > 10000)
        t = 6500, warnx("temp range");
    t -= 1000, r = t % 500 / 500.0;
    if (!(d = XOpenDisplay(NULL)) || !(sr = XRRGetScreenResourcesCurrent(d,
        RootWindow(d, DefaultScreen(d)))))
        err(1, "XOpenDisplay or XRRGetScreenResourcesCurrent failed");
    for (i = 0; i < sr->ncrtc; i++) {
        sz = XRRGetCrtcGammaSize(d, sr->crtcs[i]);
        if (!(cg = XRRAllocGamma(sz)))
            err(1, "XRRAllocGamma failed");
        for (j = 0; j < sz; j++) {
            g = 65535.0 * j / sz;
            cg->red[j] = g * s_avg(t, r, S_RED);
            cg->green[j] = g * s_avg(t, r, S_GREEN);
            cg->blue[j] = g * s_avg(t, r, S_BLUE);
        }
        XRRSetCrtcGamma(d, sr->crtcs[i], cg);
        XFree(cg);
    }
    printf(isatty(fileno(stdout)) ? S_YELLOW "Temperature: %luK" S_RST "\n" :
        "Temperature: %luK\n", t + 1000);
    XFree(sr), XCloseDisplay(d);
}

int main(int argc, char *argv[]) {
    if (argc == 2 && !strcmp(argv[1], "version")) {
        puts("sct "S_V" (c) "S_YR" Ted Unangst, Joerg Jung");
        return 0;
    }
    if (argc > 2)
        errx(1, "usage: sct [<temp>]\n%12ssct version", "");
    s_run((argc == 2) ? argv[1] : "6500");
    return 0;
}
