/* See LICENSE file for copyright and license details. */
#include "cg-base.h"
#include <sys/socket.h>
#include <sys/timerfd.h>
#include <sys/un.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libclut.h>
#include <libred.h>
#define IF_LINEARISING(...) do { if (linearise) { __VA_ARGS__; } } while (0)
#define SOCKLEN (socklen_t)sizeof
/**
* The default filter priority for the program
*/
const int64_t default_priority = (int64_t)7 << 61;
/**
* The default class for the program
*/
char default_class[] = PACKAGE_NAME"::"COMMAND_NAME"::standard";
/**
* Class suffixes
*/
const char *const *class_suffixes = (const char *const[]){NULL};
/**
* The effect fade-in time, in centiseconds
*/
static unsigned long int fade_in_cs = 0;
/**
* The effect fade-out time, in centiseconds
*/
static unsigned long int fade_out_cs = 0;
/**
* The highest elevation of the Sun where the lowest
* colour temperature is applied
*/
static double low_elev = -6;
/**
* The lowest colour temperature that may be applied
*/
static double low_temp = 2500;
/**
* The lowest elevation of the Sun where the highest
* colour temperature is applied
*/
static double high_elev = 3;
/**
* The highest colour temperature that may be applied
*/
static double high_temp = 5000;
/**
* The temperature choosen with the -f flag, negative if none
*/
static double choosen_temperature = -1;
/**
* The latitude coordiate of the GPS coordiates of
* the user's location
*/
static double latitude;
/**
* The longitude coordiate of the GPS coordiates of
* the user's location
*/
static double longitude;
/**
* Whether the user's location has been specified
*/
static int have_location = 0;
/**
* Whether the -d flag (keep process running and remove
* effect when killed) has been specified
*/
static int dflag = 0;
/**
* Whether the -x flag (remove applied effect)
* has been specified
*/
static int xflag = 0;
/**
* Whether the currently applied temperature should be
* printed when the program starts
*/
static int print_temperature = 0;
/**
* Whether the effect shall be put into an embedded
* cg-linear(1) equivalent block. This is generally
* a good idea unless you are using cg-linear(1)
* explicitly.
*/
static int linearise = 1;
/**
* The abstract address the socket shall have,
* if empty, no socket will be created, if
* `NULL` a default address derived from the
* process ID and the filter class will be used.
* If "-", standard input will be used and should
* already be bound.
*/
static const char *sockname = NULL;
/**
* Set to 1 by `handle_args` if the used arguments
* does not describe an action that requires the
* temperature to be modified; that is, only "-t get"
* (or "-t ?" is specified)
*/
static int no_temperature_change = 0;
/**
* If 0, SIGINT has not been received,
* if 1, SIGINT has been received once as the effect
* should be faded out and then the effect should
* be removed and the program terminated,
* if 2, SIGINT has been received at least twice and
* the effect should be removed immediately
* and the program terminated
*/
static volatile sig_atomic_t sigint_received = 0;
/**
* If 0, SIGHUP has not been received,
* if 1, SIGHUP has been received and the effect
* shall be made `LIBCOOPGAMMA_UNTIL_REMOVAL` and
* the program terminated
*/
static volatile sig_atomic_t sighup_received = 0;
/**
* If 0, SIGUSR1 has not been received,
* if 1, SIGUSR1 has been received once as the effect
* should be faded out and removed, but the
* the process shall not be terminated
* if 2, SIGUSR1 has been received at least twice and
* the effect be removed immediately, but the
* the process shall not be terminated
*/
static volatile sig_atomic_t sigusr1_received = 0;
/**
* If 0, SIGUSR2 has not been received,
* if 1, SIGUSR2 has been received once as the effect
* should be faded back in (from SIGUSR1), or
* if during initial fade-in, the fade-in should
* be skipped over
* if 2, SIGUSR2 has been received at least twice and
* the effect be immediately restored
*/
static volatile sig_atomic_t sigusr2_received = 0;
/**
* Print usage information and exit
*/
void
usage(void)
{
fprintf(stderr,
"usage: %s [-M method] [-S site] [-c crtc] ... [-R rule] [-p priority]"
" [-f fade-in] [-F fade-out] [-h [high-temp][@high-elev]] [-l [low-temp][@low-elev]]"
" [-W options] ... (-L latitude:longitude | -t temperature [-d] | -x)\n", argv0);
exit(1);
}
/**
* Parse a non-negative double encoded as a string
*
* @param out Output parameter for the value
* @param str The string
* @return Zero on success, -1 if the string is invalid
*/
static int
parse_double(double *out, const char *str)
{
char *end;
errno = 0;
*out = strtod(str, &end);
if (errno || *out < 0 || isinf(*out) || isnan(*out) || *end)
return -1;
if (!*str || !strchr("0123456789.", *str))
return -1;
return 0;
}
/**
* Parse -W argument
*/
static void
vendor_options(char *arg)
{
char *next, *value;
for (; arg; arg = next) {
while (*arg == ',')
arg++;
if (!*arg)
break;
next = strchr(arg, ',');
if (next)
*next++ = '\0';
value = strchr(arg, '=');
if (value)
*value++ = '\0';
if (!strcmp(arg, "linear")) {
if (!value)
goto missing_value;
else if (!strcmp(value, "yes"))
linearise = 1;
else if (!strcmp(value, "no"))
linearise = 0;
else
goto invalid_value;
} else if (!strcmp(arg, "socket")) {
if (next) {
next[-1] = ',';
next = NULL;
}
if (!value)
goto missing_value;
else if (!*value)
goto invalid_value;
else
sockname = value;
} else if (!strcmp(arg, "no-socket")) {
if (value)
goto unexpected_value;
else
sockname = "";
} else {
fprintf(stderr, "%s: invalid -W option: %s\n", argv0, arg);
exit(1);
}
}
return;
unexpected_value:
fprintf(stderr, "%s: -W option '%s' has an associated value, but must not\n", argv0, arg);
exit(1);
missing_value:
fprintf(stderr, "%s: invalid -W option '%s' is missing associated value\n", argv0, arg);
exit(1);
invalid_value:
fprintf(stderr, "%s: invalid value on -W option '%s': %s\n", argv0, arg, value);
exit(1);
}
/**
* Handle a command line option
*
* @param opt The option, it is a NUL-terminate two-character
* string starting with either '-' or '+', if the
* argument is not recognised, call `usage`. This
* string will not be "-M", "-S", "-c", "-p", or "-R".
* @param arg The argument associated with `opt`,
* `NULL` there is no next argument, if this
* parameter is `NULL` but needed, call `usage`
* @return 0 if `arg` was not used,
* 1 if `arg` was used,
* -1 on error
*/
int
handle_opt(char *opt, char *arg)
{
double t;
char *p;
if (opt[0] == '-') {
switch (opt[1]) {
case 'd':
dflag = 1;
xflag = 0;
break;
case 'f':
if (parse_double(&t, arg))
usage();
fade_in_cs = (unsigned long int)(t * 100 + 0.5);
return 1;
case 'F':
if (parse_double(&t, arg))
usage();
fade_out_cs = (unsigned long int)(t * 100 + 0.5);
return 1;
case 'h':
p = strchr(arg, '@');
if (p)
*p++ = '\0';
if (*arg && parse_double(&high_temp, arg))
usage();
if (p && parse_double(&high_elev, p))
usage();
return 1;
case 'l':
p = strchr(arg, '@');
if (p)
*p++ = '\0';
if (*arg && parse_double(&low_temp, arg))
usage();
if (p && parse_double(&low_elev, p))
usage();
return 1;
case 'L':
p = strchr(arg, ':');
if (!p)
usage();
*p++ = '\0';
if (parse_double(&latitude, arg) || latitude < -90 || latitude > 90)
usage();
if (parse_double(&longitude, p) || longitude < -180 || longitude > 180)
usage();
choosen_temperature = -1;
have_location = 1;
dflag = 0;
xflag = 0;
return 1;
case 't':
if (!strcmp(arg, "get") || !strcmp(arg, "?")) {
print_temperature = 1;
} else {
if (parse_double(&choosen_temperature, arg))
usage();
xflag = 0;
}
return 1;
case 'W':
vendor_options(arg);
return 1;
case 'x':
xflag = 1;
dflag = 0;
break;
default:
usage();
}
} else {
usage();
}
return 0;
}
/**
* This function is called after the last
* call to `handle_opt`
*
* @param argc The number of unparsed arguments
* @param argv `NULL` terminated list of unparsed arguments
* @param prio The argument associated with the "-p" option
* @return Zero on success, -1 on error
*/
int
handle_args(int argc, char *argv[], char *prio)
{
no_temperature_change = (!xflag && !have_location && choosen_temperature < 0);
if (argc || (no_temperature_change && !print_temperature))
usage();
return 0;
(void) argv;
(void) prio;
}
/**
* Fill a filter
*
* @param filter The filter to fill
* @param red The red brightness
* @param green The green brightness
* @param blue The blue brightness
*/
static void
fill_filter(libcoopgamma_filter_t *restrict filter, double red, double green, double blue)
{
switch (filter->depth) {
#define X(CONST, MEMBER, MAX, TYPE)\
case CONST:\
libclut_start_over(&(filter->ramps.MEMBER), MAX, TYPE, 1, 1, 1);\
IF_LINEARISING(libclut_linearise(&(filter->ramps.MEMBER), (MAX), TYPE, 1, 1, 1));\
libclut_rgb_brightness(&(filter->ramps.MEMBER), MAX, TYPE, red, green, blue);\
IF_LINEARISING(libclut_standardise(&(filter->ramps.MEMBER), (MAX), TYPE, 1, 1, 1));\
break;
LIST_DEPTHS
#undef X
default:
abort();
}
}
/**
* Set the gamma ramps
*
* @param red The red brightness
* @param green The green brightness
* @param blue The blue brightness
* @return 0: Success
* -1: Error, `errno` set
* -2: Error, `cg.error` set
* -3: Error, message already printed
*/
static int
set_ramps(double red, double green, double blue)
{
int r;
size_t i, j;
for (i = 0, r = 1; i < filters_n; i++) {
if (!crtc_updates[i].master || !crtc_info[crtc_updates[i].crtc].supported)
continue;
fill_filter(&crtc_updates[i].filter, red, green, blue);
do {
r = update_filter(i, 0);
} while (r == -1 && errno == EINTR);
if (r == -2 || (r == -1 && errno != EAGAIN))
return r;
if (crtc_updates[i].slaves) {
for (j = 0; crtc_updates[i].slaves[j] != 0; j++) {
do {
r = update_filter(crtc_updates[i].slaves[j], 0);
} while (r == -1 && errno == EINTR);
if (r == -2 || (r == -1 && errno != EAGAIN))
return r;
}
}
}
while (r != 1)
if ((r = synchronise(-1)) < 0)
if (r != -1 || errno != EINTR)
return r;
return 0;
}
/**
* Get the colour of a temperature
*
* @param t The temperature, in Kelvin
* @param r_out Output parameter for the red channel multiplier
* @param g_out Output parameter for the green channel multiplier
* @param b_out Output parameter for the blue channel multiplier
* @return 0 on success, -1 on failure
*/
static int
get_colour(long int t, double *r_out, double *g_out, double *b_out)
{
double x, y, z, max;
if (libred_get_colour_xy(t, &x, &y))
return -1;
libclut_model_ciexyy_to_ciexyz(x, y, 1.0, &x, &z);
libclut_model_ciexyz_to_linear(x, 1.0, z, r_out, g_out, b_out);
*r_out = fmax(0.0, *r_out);
*g_out = fmax(0.0, *g_out);
*b_out = fmax(0.0, *b_out);
max = fmax(fmax(*r_out, *g_out), *b_out);
*r_out /= max;
*g_out /= max;
*b_out /= max;
return 0;
}
/**
* Get the colour temperature for the current time
*
* @param tp Output parameter for the colour temperature
* @return 0 on success, -1 on failure
*/
static int
get_temperature(double *tp)
{
if (choosen_temperature < 0) {
if (libred_solar_elevation(latitude, longitude, tp))
return -1;
if (*tp < low_elev)
*tp = low_elev;
if (*tp > high_elev)
*tp = high_elev;
*tp = (*tp - low_elev) / (high_elev - low_elev);
*tp = low_temp + *tp * (high_temp - low_temp);
} else {
*tp = choosen_temperature;
}
return 0;
}
/**
* Get the currently applied colour temperature
*
* @param tp Output parameter for the colour temperature
* @return 0: Success
* -1: Error, `errno` set
* -2: Error, `cg.error` set
*/
static int
get_applied_temperature(double *tp)
{
libcoopgamma_filter_table_t table;
libcoopgamma_filter_query_t query;
libcoopgamma_ramps_t *restrict ramps;
size_t filter_i, i, tn = 0;
long double lred, lgreen, lblue;
double red, green, blue, t, tsum = 0;
double x, y, z;
if (libcoopgamma_set_nonblocking(&cg, 0) < 0)
return -1;
if (libcoopgamma_filter_table_initialise(&table) < 0)
return -1;
if (libcoopgamma_filter_query_initialise(&query) < 0)
return -1;
query.coalesce = 0;
for (filter_i = 0; filter_i < filters_n; filter_i++) {
query.crtc = crtc_updates[filter_i].filter.crtc;
while (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0) {
if (errno == EINTR)
continue;
return -1;
}
for (i = 0; i < table.filter_count; i++)
if (!strcmp(table.filters[i].class, crtc_updates[filter_i].filter.class))
break;
if (i == table.filter_count) {
no_filter:
#if 0
fprintf(stderr, "%s: temperature on %s: not found\n", argv0, query.crtc);
#endif
continue;
}
ramps = &table.filters[i].ramps;
if (!table.red_size || !table.green_size || !table.blue_size)
goto no_filter;
switch (table.depth) {
#define X(CONST, MEMBER, MAX, TYPE)\
case CONST:\
IF_LINEARISING(libclut_linearise(&(ramps->MEMBER), (MAX), TYPE, 1, 1, 1));\
lred = (long double)ramps->MEMBER.red[table.red_size - 1U] / (MAX);\
lgreen = (long double)ramps->MEMBER.green[table.green_size - 1U] / (MAX);\
lblue = (long double)ramps->MEMBER.blue[table.blue_size - 1U] / (MAX);\
break
X(LIBCOOPGAMMA_DOUBLE, d, 1.0, double);
X(LIBCOOPGAMMA_FLOAT, f, 1.0, float);
X(LIBCOOPGAMMA_UINT8, u8, UINT8_MAX, uint8_t);
X(LIBCOOPGAMMA_UINT16, u16, UINT16_MAX, uint16_t);
X(LIBCOOPGAMMA_UINT32, u32, UINT32_MAX, uint32_t);
X(LIBCOOPGAMMA_UINT64, u64, UINT64_MAX, uint64_t);
#undef X
default:
errno = EPROTO;
return -1;
}
red = (double)lred;
green = (double)lgreen;
blue = (double)lblue;
if (red > 0.996 && blue < 0.004) { /* out of gamut (1000K to 1900K) */
long int t1 = LIBRED_LOWEST_TEMPERATURE, t2;
double g1 = 0, r2, g2, b2;
for (t2 = LIBRED_LOWEST_TEMPERATURE; t2 <= 1900; t2 += LIBRED_DELTA_TEMPERATURE) {
libred_get_colour_xy(t2, &x, &y);
libclut_model_ciexyy_to_ciexyz(x, y, 1.0, &x, &z);
libclut_model_ciexyz_to_linear(x, 1.0, z, &r2, &g2, &b2);
g2 /= fmax(fmax(r2, g2), b2);
if (green <= g2) {
t = (green - g1) / (g2 - g1);
t = t1 + (t2 - t1) * t;
goto estimated;
}
t1 = t2;
g1 = g2;
}
t = 1900;
}
libclut_model_linear_to_ciexyz(red, green, blue, &x, &y, &z);
libclut_model_ciexyz_to_ciexyy(x, y, z, &x, &y);
t = libred_get_temperature_xy(x, y, &x, &y);
#if 0
libred_get_colour_xy((long int)t, &x, &y);
x = sqrt(x*x + y*y);
fprintf(stderr, "%s: temperature on %s: %gK (error: %g)\n", argv0, query.crtc, t, x);
#endif
estimated:
tsum += t;
tn += 1U;
}
if (libcoopgamma_set_nonblocking(&cg, 1) < 0)
return -1;
*tp = tn ? tsum / (double)tn : 6500;
return 0;
}
/**
* Called when SIGINT is received
*
* @param sig Always `SIGINT`
*/
static void
sigint_handler(int sig)
{
(void) sig;
sigint_received = sigint_received ? 2 : 1;
}
/**
* Called when SIGHUP is received
*
* @param sig Always `SIGHUP`
*/
static void
sighup_handler(int sig)
{
(void) sig;
sighup_received = 1;
}
/**
* Called when SIGUSR1 is received
*
* @param sig Always `SIGUSR1`
*/
static void
sigusr1_handler(int sig)
{
sigusr1_received = sigusr1_received ? 2 : 1;
}
/**
* Called when SIGUSR2 is received
*
* @param sig Always `SIGUSR2`
*/
static void
sigusr2_handler(int sig)
{
sigusr2_received = sigusr2_received ? 2 : 1;
}
/**
* Called to pull all messages from the IPC socket
*
* @param sock The socket's file descriptor, or -1 if there is none
*/
static void
read_socket(int sock)
{
char buffer[64];
ssize_t r;
if (sock < 0)
return;
for (;;) {
r = recv(sock, buffer, sizeof(buffer), MSG_DONTWAIT | MSG_TRUNC);
if (r < 0) {
if (errno == EAGAIN)
break;
if (errno == EINTR || errno == ECONNREFUSED)
continue;
perror(argv0);
exit(1);
}
if (!r || (size_t)r > sizeof(buffer)) {
fprintf(stderr, "%s: invalid length of received message: %zi\n", argv0, r);
continue;
}
/* TODO parse message */
}
}
/**
* The main function for the program-specific code
*
* @return 0: Success
* -1: Error, `errno` set
* -2: Error, `cg.error` set
* -3: Error, message already printed
*/
int
start(void)
{
int r, tfd, initial_fade_in = 1;
size_t i;
double target_temperature;
long int original_temperature = 6500;
long int current_temperature = 6500;
double red = 1, green = 1, blue = 1;
uint64_t overrun;
struct sigaction sa;
struct timespec sleep_timeout;
const char *side;
size_t fade_cs;
double cs;
sigset_t empty_sigset;
int sock = -1;
struct sockaddr_un addr;
size_t addrlen;
struct f_owner_ex owner;
sigemptyset(&empty_sigset);
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &sigint_handler;
sigaction(SIGINT, &sa, NULL);
sa.sa_handler = &sighup_handler;
sigaction(SIGHUP, &sa, NULL);
sa.sa_handler = &sigusr1_handler;
sigaction(SIGUSR1, &sa, NULL);
sa.sa_handler = &sigusr2_handler;
sigaction(SIGUSR2, &sa, NULL);
if (xflag)
for (i = 0; i < filters_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE;
else if (choosen_temperature >= 0 && !dflag)
for (i = 0; i < filters_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL;
else
for (i = 0; i < filters_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH;
if (!xflag)
if (choosen_temperature < 0)
dflag = 1;
if (fade_in_cs || print_temperature) {
double t;
if ((r = get_applied_temperature(&t)))
return r;
original_temperature = (long int)(t + 0.5);
if (print_temperature)
printf("%liK\n", original_temperature);
}
if (no_temperature_change)
return 0;
if (xflag)
return set_ramps(1, 1, 1);
memset(&addr, 0, sizeof(addr));
addrlen = offsetof(struct sockaddr_un, sun_path);
addr.sun_family = AF_UNIX;
if (!sockname) {
size_t len = strlen(crtc_updates[0].filter.class);
size_t off = 1U;
off += (size_t)sprintf(&addr.sun_path[off], "/proc/%ju/", (uintmax_t)getpid());
if (len > sizeof(addr.sun_path) - 1U) {
fprintf(stderr, "%s: socket name is too long; you can may -R shorter,"
"select a name with -W socket, or use -W no-socket to skip the socket\n", argv0);
return -3;
}
memcpy(&addr.sun_path[off], crtc_updates[0].filter.class, len);
addrlen += off + len;
goto create_socket;
} else if (!strcmp(sockname, "-")) {
int opt;
socklen_t len = SOCKLEN(opt);
sock = STDIN_FILENO;
if (getsockopt(sock, SOL_SOCKET, SO_DOMAIN, &opt, &len)) {
if (errno != ENOTSOCK)
return -1;
fprintf(stderr, "%s: -W socket=- used but standard input is not socket\n", argv0);
return -3;
}
if (opt != PF_UNIX) {
fprintf(stderr, "%s: -W socket=- used but standard input is not a unix socket\n", argv0);
return -3;
}
if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &opt, &len))
return -1;
if (opt != SOCK_DGRAM) {
fprintf(stderr, "%s: -W socket=- used but standard input is not a datagram socket\n", argv0);
return -3;
}
len = SOCKLEN(addr);
if (getsockname(sock, &addr, &len))
return -1;
if (len <= offsetof(struct sockaddr_un, sun_path) || addr.sun_family != AF_UNIX) {
fprintf(stderr, "%s: -W socket=- used but standard input is not bound to a unix socket address\n", argv0);
return -3;
}
if (len > SOCKLEN(addr)) {
fprintf(stderr, "%s: stanard input is bound to an overly long address\n", argv0);
return -3;
}
addrlen = len;
} else if (*sockname) {
size_t len = strlen(sockname);
if (len > sizeof(addr.sun_path) - 1U) {
fprintf(stderr, "%s: selected socket name is too long\n", argv0);
return -3;
}
memcpy(&addr.sun_path[1U], sockname, len);
addrlen += len + 1U;
create_socket:
sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if (sock < 0)
return -1;
if (bind(sock, (void *)&addr, (socklen_t)addrlen))
return -1;
} else {
goto no_socket;
}
if (fcntl(sock, F_SETSIG, SIGIO) < 0)
return -1;
owner.type = F_OWNER_TID;
owner.pid = gettid();
if (fcntl(sock, F_SETOWN_EX, &owner) < 0)
return -1;
no_socket:
if ((r = make_slaves()) < 0)
return r;
fade_in:
if (!fade_in_cs)
goto faded_in;
tfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (tfd < 0)
return -1;
if (timerfd_settime(tfd, 0, &(struct itimerspec){{0, 10000000L}, {0, 10000000L}}, NULL))
return -1;
fade_cs = (size_t)fade_in_cs;
if (initial_fade_in) {
initial_fade_in = 0;
if ((r = get_temperature(&target_temperature)) < 0)
return r;
} else {
fade_in_have_timer:
original_temperature = 6500;
}
for (i = 0; i < fade_cs;) {
read_socket(sock);
if (sigint_received) {
goto reverse_fade_in;
} else if (sigusr2_received) {
sigusr2_received = 0;
goto skip_fade_in;
} else if (sigusr1_received) {
sigusr1_received -= 1;
reverse_fade_in:
cs = (double)i / fade_cs * fade_out_cs + 0.5;
fade_cs = (unsigned long int)cs;
goto fade_out_have_timer;
}
if (i % 600 == 0)
if ((r = get_temperature(&target_temperature)) < 0)
return r;
current_temperature = (long int)(original_temperature - (original_temperature - target_temperature) * i / fade_cs);
if (get_colour(current_temperature, &red, &green, &blue))
return -1;
if ((r = set_ramps(red, green, blue)) < 0)
return r;
while (read(tfd, &overrun, sizeof(overrun)) != sizeof(overrun)) {
if (errno == EINTR)
continue;
return -1;
}
if (overrun > fade_cs - i)
overrun = fade_cs - i;
i += overrun;
}
skip_fade_in:
close(tfd);
faded_in:
for (;;) {
read_socket(sock);
if (sigint_received) {
goto fade_out;
} else if (sighup_received) {
for (i = 0; i < filters_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL;
return set_ramps(red, green, blue);
}
sigusr2_received = 0;
if (sigusr1_received) {
sigusr1_received -= 1;
goto fade_out;
}
if ((r = get_temperature(&target_temperature)) < 0)
return r;
current_temperature = (long int)target_temperature;
if (get_colour(current_temperature, &red, &green, &blue))
return -1;
if ((r = set_ramps(red, green, blue)) < 0)
return r;
if (!dflag)
return 0;
sleep_timeout.tv_sec = 6;
sleep_timeout.tv_nsec = 0;
while ((errno = clock_nanosleep(CLOCK_BOOTTIME, 0, &sleep_timeout, &sleep_timeout))) {
if (errno == EINTR) {
read_socket(sock);
if (sigint_received || sighup_received || sigusr1_received || sigusr2_received)
break;
continue;
}
return -1;
}
}
fade_out:
if (!fade_out_cs)
goto faded_out;
tfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (tfd < 0)
goto fade_out_fail;
if (timerfd_settime(tfd, 0, &(struct itimerspec){{0, 10000000L}, {0, 10000000L}}, NULL))
goto fade_out_fail;
fade_cs = (size_t)fade_out_cs;
fade_out_have_timer:
original_temperature = current_temperature;
for (i = 0; i < fade_cs;) {
read_socket(sock);
if (sigint_received > 1 || sigusr1_received) {
goto skip_fade_out;
} else if (!sigint_received && !sighup_received) {
if (sigusr2_received) {
sigusr2_received -= 1;
cs = (double)i / fade_cs * fade_in_cs + 0.5;
fade_cs = (unsigned long int)cs;
goto fade_in_have_timer;
}
}
current_temperature = original_temperature + (double)(6500 - original_temperature) * i / fade_cs;
if (get_colour(current_temperature, &red, &green, &blue))
goto fade_out_fail;
if ((r = set_ramps(red, green, blue)) < 0)
goto fade_out_fail_use_r;
while (read(tfd, &overrun, sizeof(overrun)) != sizeof(overrun)) {
if (errno == EINTR)
continue;
goto fade_out_fail;
}
if (overrun > fade_cs - i)
overrun = fade_cs - i;
i += overrun;
}
skip_fade_out:
close(tfd);
faded_out:
for (i = 0; i < filters_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE;
if ((r = set_ramps(red, green, blue)) < 0)
return r;
for (i = 0; i < filters_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL;
for (;;) {
read_socket(sock);
sigusr1_received = 0;
if (sigint_received || sighup_received) {
for (i = 0; i < filters_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE;
if (sock >= 0)
close(sock);
return set_ramps(1, 1, 1);
} else if (sigusr2_received) {
sigusr2_received -= 1;
goto fade_in;
}
if (sigsuspend(&empty_sigset) && errno == EFAULT)
abort();
}
fade_out_fail_use_r:
switch (r) {
case -1:
fade_out_fail:
perror(argv0);
break;
case -2:
side = cg.error.server_side ? "server" : "client";
if (cg.error.custom) {
if (cg.error.number && cg.error.description) {
fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n",
argv0, side, cg.error.number, cg.error.description);
} else if (cg.error.number) {
fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number);
} else if (cg.error.description) {
fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description);
}
} else if (cg.error.description) {
fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description);
} else {
fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror((int)cg.error.number));
}
break;
default:
break;
}
for (i = 0; i < filters_n; i++)
crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE;
r = set_ramps(1, 1, 1);
return r ? r : -3;
}