diff options
Diffstat (limited to '')
-rw-r--r-- | .gitignore | 9 | ||||
-rw-r--r-- | Makefile | 80 | ||||
-rw-r--r-- | config.mk | 8 | ||||
-rw-r--r-- | libradharc.c | 171 | ||||
-rw-r--r-- | libradharc.h | 796 | ||||
-rw-r--r-- | mk/linux.mk | 6 | ||||
-rw-r--r-- | mk/macos.mk | 6 | ||||
-rw-r--r-- | mk/windows.mk | 6 | ||||
-rw-r--r-- | radharc-ipc.c | 50 | ||||
-rw-r--r-- | radharc.1 | 4 | ||||
-rw-r--r-- | radharc.c | 38 |
11 files changed, 1141 insertions, 33 deletions
@@ -3,5 +3,14 @@ *.o *.a *.su +*.lo +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda radharc radharc-ipc @@ -3,50 +3,106 @@ CONFIGFILE = config.mk include $(CONFIGFILE) +OS = linux +# Linux: linux +# Mac OS: macos +# Windows: windows +include mk/$(OS).mk + + +LIB_MAJOR = 1 +LIB_MINOR = 3 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) +LIB_NAME = radharc + + OBJ_RADHARC =\ cg-base.o\ radharc.o +HDR_RADHARC =\ + cg-base.h\ + libradharc.h + + OBJ_RADHARC_IPC =\ radharc-ipc.o -HDR_RADHARC =\ - cg-base.h +HDR_RADHARC_IPC =\ + libradharc.h + + +OBJ_LIBRADHARC =\ + libradharc.o + +HDR_LIBRADHARC =\ + libradharc.h + + +LOBJ = $(OBJ_LIBRADHARC:.o=.lo) + CPPFLAGS_MACROS =\ -D'PACKAGE_NAME="$(PACKAGE_NAME)"'\ -D'COMMAND_NAME="$(COMMAND_NAME)"' -all: radharc radharc-ipc + +all: libradharc.a libradharc.$(LIBEXT) radharc radharc-ipc $(OBJ_RADHARC): $(HDR_RADHARC) +$(OBJ_RADHARC_IPC): $(HDR_RADHARC_IPC) +$(OBJ_LIBRADHARC): $(HDR_LIBRADHARC) .c.o: $(CC) -c -o $@ $< $(CPPFLAGS_MACROS) $(CPPFLAGS) $(CFLAGS) +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + radharc: $(OBJ_RADHARC) $(CC) -o $@ $(OBJ_RADHARC) $(LDFLAGS) -radharc-ipc: $(OBJ_RADHARC_IPC) - $(CC) -o $@ $(OBJ_RADHARC_IPC) $(LDFLAGS) +radharc-ipc: $(OBJ_RADHARC_IPC) libradharc.a + $(CC) -o $@ $(OBJ_RADHARC_IPC) libradharc.a $(LDFLAGS_IPC) + +libradharc.a: $(OBJ_LIBRADHARC) + @rm -f -- $@ + $(AR) rc $@ $(OBJ_LIBRADHARC) + $(AR) ts $@ > /dev/null + +libradharc.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS_LIB) -install: radharc radharc-ipc +install: libradharc.a libradharc.$(LIBEXT) radharc radharc-ipc mkdir -p -- "$(DESTDIR)$(PREFIX)/bin" mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1" - cp -- radharc "$(DESTDIR)$(PREFIX)/bin/" - cp -- radharc-ipc "$(DESTDIR)$(PREFIX)/bin/" - cp -- radharc.1 "$(DESTDIR)$(MANPREFIX)/man1/" - cp -- radharc-ipc.1 "$(DESTDIR)$(MANPREFIX)/man1/" + cp -- radharc radharc-ipc "$(DESTDIR)$(PREFIX)/bin/" + cp -- radharc.1 radharc-ipc.1 "$(DESTDIR)$(MANPREFIX)/man1/" + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libradharc.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libradharc.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libradharc.$(LIBMINOREXT)" + $(FIX_INSTALL_NAME) "$(DESTDIR)$(PREFIX)/lib/libradharc.$(LIBMINOREXT)" + ln -sf -- libradharc.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libradharc.$(LIBMAJOREXT)" + ln -sf -- libradharc.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libradharc.$(LIBEXT)" + cp -- libradharc.h "$(DESTDIR)$(PREFIX)/include/" uninstall: -rm -f -- "$(DESTDIR)$(PREFIX)/bin/radharc" -rm -f -- "$(DESTDIR)$(PREFIX)/bin/radharc-ipc" -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/radharc.1" -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/radharc-ipc.1" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libradharc.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libradharc.$(LIBMAJOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libradharc.$(LIBMINOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libradharc.$(LIBEXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/libradharc.h" clean: - -rm -f -- radharc radharc-ipc *.o + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.dll *.dylib + -rm -f -- *.gch *.gcov *.gcno *.gcda *.$(LIBEXT) + -rm -f -- radharc radharc-ipc .SUFFIXES: -.SUFFIXES: .c .o +.SUFFIXES: .lo .o .c .PHONY: all check install uninstall clean @@ -3,9 +3,11 @@ MANPREFIX = $(PREFIX)/share/man CC = c99 -CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -CFLAGS = -Wall -O2 -LDFLAGS = -lcoopgamma -lred -lm -s +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = -Wall -O2 +LDFLAGS = -lcoopgamma -lred -lm -s +LDFLAGS_IPC = -s +LDFLAGS_LIB = PACKAGE_NAME = radharc COMMAND_NAME = radharc diff --git a/libradharc.c b/libradharc.c new file mode 100644 index 0000000..32b47d9 --- /dev/null +++ b/libradharc.c @@ -0,0 +1,171 @@ +/* See LICENSE file for copyright and license details. */ +#include "libradharc.h" +#include <stdarg.h> + + +int +libradharc_parse_response(const void *msg, size_t msglen, struct libradharc_response *response_out) +{ + const unsigned char *m = msg; + int enum_value; + int64_t i64; + int32_t i32; + + if (!msg || !msglen || !response_out) { + errno = EINVAL; + return -1; + } + + response_out->type = (enum libradharc_response_type)*m++; + msglen--; + + switch (response_out->type) { + case LIBRADHARC_RESPONSE_UNSUPPORTED: + case LIBRADHARC_RESPONSE_INVALID_REQUEST: + case LIBRADHARC_RESPONSE_ACK: + case LIBRADHARC_RESPONSE_ACK_NOOP: + case LIBRADHARC_RESPONSE_UNSET: + case LIBRADHARC_RESPONSE_YES: + case LIBRADHARC_RESPONSE_NO: + if (msglen) + goto ebadmsg; + break; + + case LIBRADHARC_RESPONSE_ERROR_STRING: + case LIBRADHARC_RESPONSE_SET_STRING: + if (!msglen || m[--msglen] || memchr(m, 0, msglen)) + goto ebadmsg; + response_out->value.string = (const char *)m; + break; + + case LIBRADHARC_RESPONSE_SET_TIME: + if (msglen != sizeof(i64) + sizeof(i32)) + goto ebadmsg; + memcpy(&i64, &m[0], sizeof(i64)); + memcpy(&i32, &m[sizeof(i64)], sizeof(i32)); + response_out->value.time.tv_sec = (time_t)i64; + response_out->value.time.tv_nsec = i32; + if (response_out->value.time.tv_sec < 0 || + response_out->value.time.tv_nsec < 0 || + response_out->value.time.tv_nsec >= 1000000000L) + goto ebadmsg; + break; + + case LIBRADHARC_RESPONSE_SET_REAL: + if (msglen != sizeof(response_out->value.real)) + goto ebadmsg; + goto raw; + + case LIBRADHARC_RESPONSE_SET_REAL_PAIR: + if (msglen != sizeof(response_out->value.real_pair)) + goto ebadmsg; + goto raw; + + case LIBRADHARC_RESPONSE_SET_SIGNED64: + if (msglen != sizeof(response_out->value.signed64)) + goto ebadmsg; + raw: + memcpy(&response_out->value, m, msglen); + break; + + case LIBRADHARC_RESPONSE_SET_ENUM: + if (msglen != sizeof(enum_value)) + goto ebadmsg; + memcpy(&enum_value, m, sizeof(enum_value)); + response_out->value.operation_mode = (enum libradharc_operation_mode)enum_value; /* any enum */ + break; + + default: + ebadmsg: + errno = EBADMSG; + return -1; + } + + return 0; +} + + +size_t +libradharc_format_request__n__(void *buffer, enum libradharc_request_type type, int what, size_t n, ...) +{ + char *buf = buffer;; + size_t off = 0; + const char *value; + size_t size; + va_list args; + va_start(args, n); + + if (buf) + buf[off] = (char)((n << 4) | type); + off++; + + if (buf) + buf[off] = (char)what; + off++; + + while (n--) { + value = va_arg(args, const char *); + size = va_arg(args, size_t); + if (buf) + memcpy(&buf[off], value, size); + off += size; + } + + va_end(args); + return off; +} + + +extern inline size_t libradharc_format_request__0__(void *, enum libradharc_request_type, int); +extern inline size_t libradharc_format_request__1__(void *, enum libradharc_request_type, int, const void *, size_t); +extern inline size_t libradharc_format_request__2__(void *, enum libradharc_request_type, int, const void *, size_t, + const void *, size_t); +extern inline size_t libradharc_format_request__3__(void *, enum libradharc_request_type, int, const void *, size_t, + const void *, size_t, const void *, size_t); + +extern inline size_t libradharc_format_get_request__0__(void *, enum libradharc_property ); +extern inline size_t libradharc_format_get_request__s__(void *, enum libradharc_property , const char *); +extern inline size_t libradharc_format_set_request__64i__(void *, enum libradharc_property, int64_t); +extern inline size_t libradharc_format_set_request__i__(void *, enum libradharc_property, int); +extern inline size_t libradharc_format_set_request__r__(void *, enum libradharc_property, double); +extern inline size_t libradharc_format_set_request__rt__(void *, enum libradharc_property, double, const struct timespec *); +extern inline size_t libradharc_format_set_request__rr__(void *, enum libradharc_property, double, double); +extern inline size_t libradharc_format_set_request__s__(void *, enum libradharc_property, const char *); +extern inline size_t libradharc_format_set_request__ss__(void *, enum libradharc_property, const char *, const char *); +extern inline size_t libradharc_format_set_request__t__(void *, enum libradharc_property, const struct timespec *); +extern inline size_t libradharc_format_unset_request__0__(void *, enum libradharc_property); +extern inline size_t libradharc_format_signal_request__0__(void *, int); +extern inline size_t libradharc_format_signal_request__t__(void *, int, const struct timespec *); + +extern inline size_t libradharc_format_set_request__rnt__(void *, enum libradharc_property, double, const struct timespec *); +extern inline size_t libradharc_format_set_request__sns__(void *, enum libradharc_property, const char *, const char *); +extern inline size_t libradharc_format_signal_request__nt__(void *, int, const struct timespec *); + +extern inline size_t libradharc_format_set_fade_in(void *, const struct timespec *); +extern inline size_t libradharc_format_get_fade_in(void *); +extern inline size_t libradharc_format_set_fade_out(void *, const struct timespec *); +extern inline size_t libradharc_format_get_fade_out(void *); +extern inline size_t libradharc_format_set_high_temperature(void *, double); +extern inline size_t libradharc_format_get_high_temperature(void *); +extern inline size_t libradharc_format_set_low_temperature(void *, double); +extern inline size_t libradharc_format_get_low_temperature(void *); +extern inline size_t libradharc_format_set_high_elevation(void *, double); +extern inline size_t libradharc_format_get_high_elevation(void *); +extern inline size_t libradharc_format_set_low_elevation(void *, double); +extern inline size_t libradharc_format_get_low_elevation(void *); +extern inline size_t libradharc_format_set_location(void *, double, double); +extern inline size_t libradharc_format_get_location(void *); +extern inline size_t libradharc_format_unset_location(void *); +extern inline size_t libradharc_format_set_operation_mode(void *, enum libradharc_operation_mode); +extern inline size_t libradharc_format_get_operation_mode(void *); +extern inline size_t libradharc_format_set_temperature(void *, double, const struct timespec *); +extern inline size_t libradharc_format_get_temperature(void *); +extern inline size_t libradharc_format_set_priority(void *, int64_t); +extern inline size_t libradharc_format_get_priority(void *); +extern inline size_t libradharc_format_set_vendor_option(void *, const char *, const char *); +extern inline size_t libradharc_format_get_vendor_option(void *, const char *); +extern inline size_t libradharc_format_get_status(void *); +extern inline size_t libradharc_format_smooth_terminate(void *, const struct timespec *); +extern inline size_t libradharc_format_freeze_terminate(void *); +extern inline size_t libradharc_format_disable(void *, const struct timespec *); +extern inline size_t libradharc_format_enable(void *, const struct timespec *); diff --git a/libradharc.h b/libradharc.h new file mode 100644 index 0000000..ee00b09 --- /dev/null +++ b/libradharc.h @@ -0,0 +1,796 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBRADHARC_H +#define LIBRADHARC_H + +#include <errno.h> +#include <signal.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <time.h> + + +enum libradharc_property { + LIBRADHARC_PROPERTY_FADE_IN, + LIBRADHARC_PROPERTY_FADE_OUT, + LIBRADHARC_PROPERTY_HIGH_TEMPERATURE, + LIBRADHARC_PROPERTY_LOW_TEMPERATURE, + LIBRADHARC_PROPERTY_HIGH_ELEVATION, + LIBRADHARC_PROPERTY_LOW_ELEVATION, + LIBRADHARC_PROPERTY_LOCATION, + LIBRADHARC_PROPERTY_OPERATION_MODE, + LIBRADHARC_PROPERTY_TEMPERATURE, + LIBRADHARC_PROPERTY_PRIORITY, + LIBRADHARC_PROPERTY_VENDOR_OPTION, + LIBRADHARC_PROPERTY_STATUS +}; + +enum libradharc_request_type { + LIBRADHARC_REQUEST_GET, + LIBRADHARC_REQUEST_SET, + LIBRADHARC_REQUEST_UNSET, + LIBRADHARC_REQUEST_SIGNAL +}; + +/** + * How radharc determines which colour temperature to apply + */ +enum libradharc_operation_mode { + /** + * A specific colour temperature has been selected + */ + LIBRADHARC_OPERATION_STATIC, + + /** + * A colour temperature range has been selected + * along with a range of solar elevations and the + * user's location. The temperature is changed + * gradually by mapping to current solar elevation's + * position in the range of solar elevations + * (clips to the range's boundaries) to a colour + * temperature, in corresponding position, the + * colour temperature range. + */ + LIBRADHARC_OPERATION_BY_SOLAR_ELEVATION +}; + +/** + * Request response type + */ +enum libradharc_response_type { + /** + * The made request was invalid + */ + LIBRADHARC_RESPONSE_INVALID_REQUEST, + + /** + * Value sent with the request was unsupported + */ + LIBRADHARC_RESPONSE_UNSUPPORTED, + + /** + * Action performed + */ + LIBRADHARC_RESPONSE_ACK, + + /** + * No action to perform + */ + LIBRADHARC_RESPONSE_ACK_NOOP, + + /** + * The requested property is not set + */ + LIBRADHARC_RESPONSE_UNSET, + + /** + * The requested property is set to the true boolean value + */ + LIBRADHARC_RESPONSE_YES, + + /** + * The requested property is set to the false boolean value + */ + LIBRADHARC_RESPONSE_NO, + + /** + * A server-side error was encountered + */ + LIBRADHARC_RESPONSE_ERROR_STRING, + + /** + * The requested property is set, and has a string value + */ + LIBRADHARC_RESPONSE_SET_STRING, + + /** + * The requested property is set, and has a time duration + */ + LIBRADHARC_RESPONSE_SET_TIME, + + /** + * The requested property is set, and has a real value + */ + LIBRADHARC_RESPONSE_SET_REAL, + + /** + * The requested property is set, and has two real values + * + * For the geographical location, this would be the the + * latitude as the first value, and a the longitude as + * the second value + */ + LIBRADHARC_RESPONSE_SET_REAL_PAIR, + + /** + * The requested property is set, and has a signed + * 64-bit integer value + */ + LIBRADHARC_RESPONSE_SET_SIGNED64, + + /** + * The requested property is set, and has a named value + */ + LIBRADHARC_RESPONSE_SET_ENUM +}; + +/** + * Response from radharc + */ +struct libradharc_response { + /** + * Response from radharc or indicate of + * type used for the response + */ + enum libradharc_response_type type; + + /** + * Value send back by radharc + * + * Only set if `.type` has one of the values + * listed the union member fields. The following + * values of `.type` do not have an associated + * value: + * - `LIBRADHARC_RESPONSE_INVALID_REQUEST` + * - `LIBRADHARC_RESPONSE_UNSUPPORTED` + * - `LIBRADHARC_RESPONSE_ACK` + * - `LIBRADHARC_RESPONSE_ACK_NOOP` + * - `LIBRADHARC_RESPONSE_UNSET` + * - `LIBRADHARC_RESPONSE_YES` + * - `LIBRADHARC_RESPONSE_NO` + */ + union { + /** + * Use if `.type` is `LIBRADHARC_RESPONSE_SET_STRING` or + * `LIBRADHARC_RESPONSE_ERROR_STRING` + */ + const char *string; + + /** + * Use if `.type` is `LIBRADHARC_RESPONSE_SET_TIME` + */ + struct timespec time; + + /** + * Use if `.type` is `LIBRADHARC_RESPONSE_SET_REAL` + */ + double real; + + /** + * Use if `.type` is `LIBRADHARC_RESPONSE_SET_REAL_PAIR` + */ + double real_pair[2]; + + /** + * Use if `.type` is `LIBRADHARC_RESPONSE_SET_SIGNED64` + */ + int64_t signed64; + + /** + * Use if `.type` is `LIBRADHARC_RESPONSE_SET_ENUM`, + * and the response is for a get-request for radharc's + * operation mode + */ + enum libradharc_operation_mode operation_mode; + } value; +}; + + +/** + * Parse a response from radharc + * + * All requests generate a response, which are sent in order + * + * @param msg The received message + * @param msglen The length of the message + * @param response_out Output parameter for the prased message + * @return 0 on success, -1 on failure + * + * @throws EINVAL `msglen` is 0 + * @throws EINVAL `msg` is `NULL` + * @throws EINVAL `response_out` is `NULL` + * @throws EBADMSG The received message is invalid + */ +int libradharc_parse_response(const void *msg, size_t msglen, struct libradharc_response *response_out); + + +size_t libradharc_format_request__n__(void *buffer, enum libradharc_request_type type, int what, size_t n, ...); + +inline size_t +libradharc_format_request__0__(void *buffer, enum libradharc_request_type type, int what) +{ + return libradharc_format_request__n__(buffer, type, what, 0); +} + +inline size_t +libradharc_format_request__1__(void *buffer, enum libradharc_request_type type, int what, + const void *value1, size_t size1) +{ + return libradharc_format_request__n__(buffer, type, what, 1, value1, size1); +} + +inline size_t +libradharc_format_request__2__(void *buffer, enum libradharc_request_type type, int what, + const void *value1, size_t size1, + const void *value2, size_t size2) +{ + return libradharc_format_request__n__(buffer, type, what, 2, value1, size1, value2, size2); +} + +inline size_t +libradharc_format_request__3__(void *buffer, enum libradharc_request_type type, int what, + const void *value1, size_t size1, + const void *value2, size_t size2, + const void *value3, size_t size3) +{ + return libradharc_format_request__n__(buffer, type, what, 3, value1, size1, value2, size2, value3, size3); +} + +inline size_t +libradharc_format_get_request__0__(void *buffer, enum libradharc_property property) +{ + return libradharc_format_request__0__(buffer, LIBRADHARC_REQUEST_GET, (int)property); +} + +inline size_t +libradharc_format_get_request__s__(void *buffer, enum libradharc_property property, const char *arg1) +{ + if (!arg1) { + errno = EINVAL; + return 0; + } + return libradharc_format_request__1__(buffer, LIBRADHARC_REQUEST_GET, (int)property, arg1, strlen(arg1) + 1U); +} + +inline size_t +libradharc_format_set_request__64i__(void *buffer, enum libradharc_property property, int64_t arg1) +{ + return libradharc_format_request__1__(buffer, LIBRADHARC_REQUEST_SET, (int)property, &arg1, sizeof(arg1)); +} + +inline size_t +libradharc_format_set_request__i__(void *buffer, enum libradharc_property property, int arg1) +{ + return libradharc_format_request__1__(buffer, LIBRADHARC_REQUEST_SET, (int)property, &arg1, sizeof(arg1)); +} + +inline size_t +libradharc_format_set_request__r__(void *buffer, enum libradharc_property property, double arg1) +{ + return libradharc_format_request__1__(buffer, LIBRADHARC_REQUEST_SET, (int)property, &arg1, sizeof(arg1)); +} + +inline size_t +libradharc_format_set_request__rt__(void *buffer, enum libradharc_property property, double arg1, const struct timespec *arg2) +{ + if (!arg2 || arg2->tv_sec < 0 || arg2->tv_nsec < 0 || arg2->tv_nsec >= 1000000000L) { + errno = EINVAL; + return 0; + } + return libradharc_format_request__3__(buffer, LIBRADHARC_REQUEST_SET, property, + &arg1, sizeof(arg1), + &arg2->tv_sec, sizeof(arg2->tv_sec), + &arg2->tv_nsec, sizeof(arg2->tv_nsec)); +} + +inline size_t +libradharc_format_set_request__rr__(void *buffer, enum libradharc_property property, double arg1, double arg2) +{ + return libradharc_format_request__2__(buffer, LIBRADHARC_REQUEST_SET, (int)property, + &arg1, sizeof(arg1), &arg2, sizeof(arg2)); +} + +inline size_t +libradharc_format_set_request__s__(void *buffer, enum libradharc_property property, const char *arg1) +{ + if (!arg1) { + errno = EINVAL; + return 0; + } + return libradharc_format_request__1__(buffer, LIBRADHARC_REQUEST_SET, (int)property, + arg1, strlen(arg1) + 1U); +} + +inline size_t +libradharc_format_set_request__ss__(void *buffer, enum libradharc_property property, const char *arg1, const char *arg2) +{ + if (!arg1 || !arg2) { + errno = EINVAL; + return 0; + } + return libradharc_format_request__2__(buffer, LIBRADHARC_REQUEST_SET, (int)property, + arg1, strlen(arg1) + 1U, arg2, strlen(arg2) + 1U); +} + +inline size_t +libradharc_format_set_request__t__(void *buffer, enum libradharc_property property, const struct timespec *arg1) +{ + if (!arg1 || arg1->tv_sec < 0 || arg1->tv_nsec < 0 || arg1->tv_nsec >= 1000000000L) { + errno = EINVAL; + return 0; + } + return libradharc_format_request__2__(buffer, LIBRADHARC_REQUEST_SET, (int)property, + &arg1->tv_sec, sizeof(arg1->tv_sec), + &arg1->tv_nsec, sizeof(arg1->tv_nsec)); +} + +inline size_t +libradharc_format_unset_request__0__(void *buffer, enum libradharc_property property) +{ + return libradharc_format_request__0__(buffer, LIBRADHARC_REQUEST_UNSET, (int)property); +} + +inline size_t +libradharc_format_signal_request__0__(void *buffer, int signal) +{ + return libradharc_format_request__0__(buffer, LIBRADHARC_REQUEST_SIGNAL, signal); +} + +inline size_t +libradharc_format_signal_request__t__(void *buffer, int signal, const struct timespec *arg1) +{ + if (!arg1 || arg1->tv_sec < 0 || arg1->tv_nsec < 0 || arg1->tv_nsec >= 1000000000L) { + errno = EINVAL; + return 0; + } + return libradharc_format_request__2__(buffer, LIBRADHARC_REQUEST_SIGNAL, signal, + &arg1->tv_sec, sizeof(arg1->tv_sec), + &arg1->tv_nsec, sizeof(arg1->tv_nsec)); +} + + +inline size_t +libradharc_format_set_request__rnt__(void *buffer, enum libradharc_property property, double arg1, const struct timespec *arg2) +{ + if (arg2) + return libradharc_format_set_request__rt__(buffer, property, arg1, arg2); + else + return libradharc_format_set_request__r__(buffer, property, arg1); +} + +inline size_t +libradharc_format_set_request__sns__(void *buffer, enum libradharc_property property, const char *arg1, const char *arg2) +{ + if (arg2) + return libradharc_format_set_request__ss__(buffer, property, arg1, arg2); + else + return libradharc_format_set_request__s__(buffer, property, arg1); +} + +inline size_t +libradharc_format_signal_request__nt__(void *buffer, int signal, const struct timespec *arg1) +{ + if (arg1) + return libradharc_format_signal_request__t__(buffer, signal, arg1); + else + return libradharc_format_signal_request__0__(buffer, signal); +} + + +/** + * Format a message that instructs radharc to reconfigure + * its effect fade-in time + * + * It is unspecified how this effects any ongoing fade-in + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param duration The new fade duration + * @return The size of the message, 0 on failure + * + * @throws EINVAL `duration` is `NULL` + * @throws EINVAL `duration->tv_nsec` is invalid + * @throws EINVAL `duration->tv_sec` is negative + */ +inline size_t +libradharc_format_set_fade_in(void *buffer, const struct timespec *duration) +{ return libradharc_format_set_request__t__(buffer, LIBRADHARC_PROPERTY_FADE_IN, duration); } + +/** + * Format a message that instructs radharc to send back + * its currently configured effect fade-in time + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_fade_in(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_FADE_IN); } + + +/** + * Format a message that instructs radharc to reconfigure + * its effect fade-out time + * + * It is unspecified how this effects any ongoing fade-out + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param duration The new fade duration + * @return The size of the message, 0 on failure + * + * @throws EINVAL `duration` is `NULL` + * @throws EINVAL `duration->tv_nsec` is invalid + * @throws EINVAL `duration->tv_sec` is negative + */ +inline size_t +libradharc_format_set_fade_out(void *buffer, const struct timespec *duration) +{ return libradharc_format_set_request__t__(buffer, LIBRADHARC_PROPERTY_FADE_OUT, duration); } + +/** + * Format a message that instructs radharc to send back + * its currently configured effect fade-out time + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_fade_out(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_FADE_OUT); } + + +/** + * Format a message that instructs radharc to configure + * the colour temperature that shall be applied + * when the Sun's elevation at least at the configured + * upper threshold of twilight, that at daytime after + * daybreak + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param temperature The colour temperature, in Kelvins, must be at least 1000 + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_set_high_temperature(void *buffer, double temperature) +{ return libradharc_format_set_request__r__(buffer, LIBRADHARC_PROPERTY_HIGH_TEMPERATURE, temperature); } + +/** + * Format a message that instructs radharc to send back + * its configured colour temperature that shall be applied + * when the Sun's elevation at least at the configured + * upper threshold of twilight, that at daytime after + * daybreak + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_high_temperature(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_HIGH_TEMPERATURE); } + + +/** + * Format a message that instructs radharc to configure + * the colour temperature that shall be applied + * when the Sun's elevation at most at the configured + * lower threshold of twilight, that at nighttime after + * nightfall + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param temperature The colour temperature, in Kelvins, must be at least 1000 + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_set_low_temperature(void *buffer, double temperature) +{ return libradharc_format_set_request__r__(buffer, LIBRADHARC_PROPERTY_LOW_TEMPERATURE, temperature); } + +/** + * Format a message that instructs radharc to send back + * its configured colour temperature that shall be applied + * when the Sun's elevation at most at the configured + * lower threshold of twilight, that at nighttime after + * nightfall + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_low_temperature(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_LOW_TEMPERATURE); } + + +/** + * Format a message that instructs radharc to configure + * the upper theshold of twilight, that is, the solar + * elevation that marks the end of daybreak and the + * beginning of nightfall + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param degrees The solar elevation, in degrees + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_set_high_elevation(void *buffer, double degrees) +{ return libradharc_format_set_request__r__(buffer, LIBRADHARC_PROPERTY_HIGH_ELEVATION, degrees); } + +/** + * Format a message that instructs radharc to send back + * its configured upper theshold of twilight, that is, + * the solar elevation that marks the end of daybreak + * and the beginning of nightfall + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_high_elevation(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_HIGH_ELEVATION); } + + +/** + * Format a message that instructs radharc to configure + * the lower theshold of twilight, that is, the solar + * elevation that marks the end of nightfall and the + * beginning of daybreak + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param degrees The solar elevation, in degrees + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_set_low_elevation(void *buffer, double degrees) +{ return libradharc_format_set_request__r__(buffer, LIBRADHARC_PROPERTY_LOW_ELEVATION, degrees); } + +/** + * Format a message that instructs radharc to send back + * its configured lower theshold of twilight, that is, + * the solar elevation that marks the end of nightfall + * and the beginning of daybreak + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_low_elevation(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_LOW_ELEVATION); } + + +/** + * Format a message that instructs radharc to reconfigured + * the geographical location + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param latitude The new latitude, in degrees north of the equator according to GPS + * @param longitude The new longitude, in degrees east of the prime meridian according to GPS + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_set_location(void *buffer, double latitude, double longitude) +{ return libradharc_format_set_request__rr__(buffer, LIBRADHARC_PROPERTY_LOCATION, latitude, longitude); } + +/** + * Format a message that instructs radharc to send back + * its currently configured geographical location + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_location(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_LOCATION); } + +/** + * Format a message that instructs radharc remove its + * currently configured geolocation, causing it to + * freeze at the current temperature, until modified + * or a new location is set, unless another operation + * mode that doesn't need the user's location is being + * used + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_unset_location(void *buffer) +{ return libradharc_format_unset_request__0__(buffer, LIBRADHARC_PROPERTY_LOCATION); } + + +/** + * Format a message that instructs radharc to use another + * operation mode + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param mode The new operation mode + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_set_operation_mode(void *buffer, enum libradharc_operation_mode mode) +{ return libradharc_format_set_request__i__(buffer, LIBRADHARC_PROPERTY_OPERATION_MODE, (int)mode); } + +/** + * Format a message that instructs radharc to send back + * its currently configured operation mode + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_operation_mode(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_OPERATION_MODE); } + + +/** + * Format a message that instructs radharc to apply + * a specific colour temperature + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param temperature The temperature to apply, in Kelvins, must be at least 1000 + * @param transition_duration The transition time, or `NULL` to use the configured fade time + * (it's implementation specific how it is defined) + * @return The size of the message, 0 on failure + * + * Implementations of radharc need not implement support for + * smooth temperature transitions, so `transition_duration` + * may be ignored + */ +inline size_t +libradharc_format_set_temperature(void *buffer, double temperature, const struct timespec *transition_duration) +{ return libradharc_format_set_request__rnt__(buffer, LIBRADHARC_PROPERTY_TEMPERATURE, temperature, transition_duration); } + +/** + * Format a message that instructs radharc to send back + * its currently applied temperature + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_temperature(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_TEMPERATURE); } + + +/** + * Format a message that instructs radharc to reconfigure + * its effect priority + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param priority The new priority + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_set_priority(void *buffer, int64_t priority) +{ return libradharc_format_set_request__64i__(buffer, LIBRADHARC_PROPERTY_PRIORITY, priority); } + +/** + * Format a message that instructs radharc to send back + * its currently configured effect priority + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_priority(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_PRIORITY); } + + +/** + * Format a message that instructs radharc to apply + * or modify an implemenation specific setting + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param option The name of the option + * @param value The value for the option, or `NULL` if there is none + * @return The size of the message, 0 on failure + * + * @throws EINVAL `option` is `NULL` + */ +inline size_t +libradharc_format_set_vendor_option(void *buffer, const char *option, const char *value) +{ return libradharc_format_set_request__sns__(buffer, LIBRADHARC_PROPERTY_VENDOR_OPTION, option, value); } + +/** + * Format a message that instructs radharc to send + * back the value of a implemenation specific setting + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param option The name of the option + * @return The size of the message, 0 on failure + * + * @throws EINVAL `option` is `NULL` + */ +inline size_t +libradharc_format_get_vendor_option(void *buffer, const char *option) +{ return libradharc_format_get_request__s__(buffer, LIBRADHARC_PROPERTY_VENDOR_OPTION, option); } + + +/** + * Format a message that instructs radharc to send back + * its current status + * + * radharc will respond with an with a single-line string, + * but it's content is implementation defined + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_get_status(void *buffer) +{ return libradharc_format_get_request__0__(buffer, LIBRADHARC_PROPERTY_STATUS); } + + +/** + * Format a message that instructs radharc to fade out its + * effect and terminate + * + * Sending this message is equivalent to sending SIGINT once, + * except the transition duration can be overridden ad hoc, + * or twice if `transition_duration` is `{0, 0}` + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param transition_duration The transition time, or `NULL` to use the configured fade out time + * @return The size of the message, 0 on failure + * + * @throws EINVAL `transition_duration->tv_nsec` is invalid + * @throws EINVAL `transition_duration->tv_sec` is negative + */ +inline size_t +libradharc_format_smooth_terminate(void *buffer, const struct timespec *transition_duration) +{ return libradharc_format_signal_request__nt__(buffer, SIGINT, transition_duration); } + +/** + * Format a message that instructs radharc to, once any + * current transition has take place, terminate without + * restoring the colour temperature + * + * Sending this message is equivalent to sending SIGHUP + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @return The size of the message, 0 on failure + */ +inline size_t +libradharc_format_freeze_terminate(void *buffer) +{ return libradharc_format_signal_request__0__(buffer, SIGHUP); } + +/** + * Format a message that instructs radharc to disable itself + * + * Sending this message is equivalent to sending SIGUSR1, + * except the transition duration can be overridden ad hoc + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param transition_duration The transition time, or `NULL` to use the configured fade out time + * @return The size of the message, 0 on failure + * + * @throws EINVAL `transition_duration->tv_nsec` is invalid + * @throws EINVAL `transition_duration->tv_sec` is negative + */ +inline size_t +libradharc_format_disable(void *buffer, const struct timespec *transition_duration) +{ return libradharc_format_signal_request__nt__(buffer, SIGUSR1, transition_duration); } + +/** + * Format a message that instructs radharc to enable itself + * + * Sending this message is equivalent to sending SIGUSR2, + * except the transition duration can be overridden ad hoc + * + * @param buffer Output buffer for the message, or `NULL` to measure the required size + * @param transition_duration The transition time, or `NULL` to use the configured fade in time + * @return The size of the message, 0 on failure + * + * @throws EINVAL `transition_duration->tv_nsec` is invalid + * @throws EINVAL `transition_duration->tv_sec` is negative + */ +inline size_t +libradharc_format_enable(void *buffer, const struct timespec *transition_duration) +{ return libradharc_format_signal_request__nt__(buffer, SIGUSR2, transition_duration); } + +#endif diff --git a/mk/linux.mk b/mk/linux.mk new file mode 100644 index 0000000..ad58f69 --- /dev/null +++ b/mk/linux.mk @@ -0,0 +1,6 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,lib$(LIB_NAME).$(LIBEXT).$(LIB_MAJOR) +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) + +FIX_INSTALL_NAME = : diff --git a/mk/macos.mk b/mk/macos.mk new file mode 100644 index 0000000..670ad0d --- /dev/null +++ b/mk/macos.mk @@ -0,0 +1,6 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib -Wl,-compatibility_version,$(LIB_MAJOR) -Wl,-current_version,$(LIB_VERSION) +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = install_name_tool -id "$(PREFIX)/lib/libradharc.$(LIBMAJOREXT)" diff --git a/mk/windows.mk b/mk/windows.mk new file mode 100644 index 0000000..ed5ec8d --- /dev/null +++ b/mk/windows.mk @@ -0,0 +1,6 @@ +LIBEXT = dll +LIBFLAGS = -shared +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = : diff --git a/radharc-ipc.c b/radharc-ipc.c index 83f436f..3787d2d 100644 --- a/radharc-ipc.c +++ b/radharc-ipc.c @@ -11,11 +11,13 @@ #include <string.h> #include <unistd.h> +#include "libradharc.h" + USAGE("[-R rule] (pid | @name | &fd | address) ..."); struct message { - char *text; + void *text; size_t len; }; @@ -46,7 +48,7 @@ main(int argc, char *argv[]) size_t i, j; struct sockaddr_un addr; size_t addrlen; - int sock; + int sock, *socks; struct message *messages = NULL; size_t nmessages = 0; @@ -70,9 +72,24 @@ main(int argc, char *argv[]) if (!addresses) goto fail; + sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (sock < 0) + goto fail; + + /* we need an address, otherwise the peer will not get an address to send the response to */ + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + if (bind(sock, (void *)&addr, (socklen_t)offsetof(struct sockaddr_un, sun_path))) + goto fail; + + socks = calloc((size_t)argc, sizeof(int)); + if (!socks) + goto fail; + for (i = 0; i < (size_t)argc; i++) { const char *arg = argv[i]; uintmax_t num; + socks[i] = sock; if (*arg == '@' || strchr(arg, '/')) { addresses[i] = strdup(arg); if (addresses[i]) @@ -83,11 +100,8 @@ main(int argc, char *argv[]) if (*arg == '&') { if (num > (uintmax_t)INT_MAX) usage(); - /* TODO whoops, this should be send with that fd, not to it */ - addresses[i] = malloc(sizeof("/proc/self/fd/") + strlen(&arg[1])); - if (!addresses[i]) - goto fail; - stpcpy(stpcpy(addresses[i], "/proc/self/fd/"), &arg[1]); + addresses[i] = NULL; + socks[i] = (int)num; } else { addresses[i] = malloc(sizeof("@/proc//") + strlen(arg) + strlen(class)); if (!addresses[i]) @@ -105,15 +119,11 @@ main(int argc, char *argv[]) } } - sock = socket(PF_UNIX, SOCK_DGRAM, 0); - if (sock < 0) - goto fail; - for (j = 0; j < nmessages; j++) { for (i = 0; i < (size_t)argc; i++) { size_t len; - if (!addresses[i]) + if (socks[i] < 0) continue; len = strlen(&addresses[i][1]) + 1U; @@ -122,15 +132,26 @@ main(int argc, char *argv[]) memcpy(addr.sun_path, addresses[i], len); addrlen = offsetof(struct sockaddr_un, sun_path) + len; - if (sendto(sock, messages[j].text, messages[j].len, MSG_NOSIGNAL, + if (socks[i] != sock) { + if (send(socks[i], messages[j].text, messages[j].len, MSG_NOSIGNAL) < 0) + goto send_failed; + } else if (sendto(sock, messages[j].text, messages[j].len, MSG_NOSIGNAL, (void *)&addr, (socklen_t)addrlen) < 0) { + send_failed: fprintf(stderr, "%s: error while sending to '%s': %s\n", argv0, argv[i], strerror(errno)); ret = 1; - addresses[i] = NULL; + socks[i] = -1; } } free(messages[j].text); + + for (i = 0; i < (size_t)argc; i++) { + if (socks[i] < 0) + continue; + + /* TODO get response */ + } } close(sock); @@ -140,6 +161,7 @@ main(int argc, char *argv[]) free(addresses); free(messages); free(class); + free(socks); return ret; fail: @@ -43,7 +43,7 @@ but uses .BR libcoopgamma (7) to allow it to be used alongside other programs that also modify the monitors colour output, as long as the also use -.RB libcoopgamma (7). +.BR libcoopgamma (7). .SH OPTIONS The following options are supported: @@ -234,7 +234,7 @@ sent twice, the effect is removed immediately. .TP .B SIGHUP Terminate the process after any current fade in or fade -out action, but leaf the effect in place. +out action, but leave the effect in place. .TP .B SIGUSR1 Gradually fade out the effect but do not terminate the process. @@ -1,5 +1,6 @@ /* See LICENSE file for copyright and license details. */ #include "cg-base.h" +#include "libradharc.h" #include <sys/socket.h> #include <sys/timerfd.h> @@ -20,6 +21,20 @@ /** + * Possible states of the program + */ +enum state { + INITIALISING, + INITIAL_FADE_IN, + TERMINAL_FADE_OUT, + ENABLE_FADE_IN, + DISABLE_FADE_OUT, + ENABLED_NORMAL, + DISABLED +}; + + +/** * The default filter priority for the program */ const int64_t default_priority = (int64_t)7 << 61; @@ -133,6 +148,11 @@ static const char *sockname = NULL; */ static int no_temperature_change = 0; +/** + * The state the program is in + */ +static enum state current_state = INITIALISING; + /** * If 0, SIGINT has not been received, @@ -694,12 +714,15 @@ read_socket(int sock) { char buffer[64]; ssize_t r; + struct sockaddr_un addr; + socklen_t addrlen; if (sock < 0) return; for (;;) { - r = recv(sock, buffer, sizeof(buffer), MSG_DONTWAIT | MSG_TRUNC); + addrlen = SOCKLEN(addr); + r = recvfrom(sock, buffer, sizeof(buffer), MSG_DONTWAIT | MSG_TRUNC, &addr, &addrlen); if (r < 0) { if (errno == EAGAIN) break; @@ -710,11 +733,17 @@ read_socket(int sock) } if (!r || (size_t)r > sizeof(buffer)) { - fprintf(stderr, "%s: invalid length of received message: %zi\n", argv0, r); + if (addrlen) { + /* TODO send LIBRADHARC_RESPONSE_INVALID_REQUEST */ + } continue; } /* TODO parse message */ + + if (addrlen) { + /* TODO send response */ + } } } @@ -877,11 +906,13 @@ fade_in: fade_cs = (size_t)fade_in_cs; if (initial_fade_in) { + current_state = INITIAL_FADE_IN; initial_fade_in = 0; if ((r = get_temperature(&target_temperature)) < 0) return r; } else { fade_in_have_timer: + current_state = ENABLE_FADE_IN; original_temperature = 6500; } for (i = 0; i < fade_cs;) { @@ -923,6 +954,7 @@ skip_fade_in: faded_in: + current_state = ENABLED_NORMAL; for (;;) { read_socket(sock); if (sigint_received) { @@ -975,6 +1007,7 @@ fade_out: fade_cs = (size_t)fade_out_cs; fade_out_have_timer: + current_state = (sigint_received || sighup_received) ? TERMINAL_FADE_OUT : DISABLE_FADE_OUT; original_temperature = current_temperature; for (i = 0; i < fade_cs;) { read_socket(sock); @@ -1010,6 +1043,7 @@ skip_fade_out: faded_out: + current_state = DISABLED; for (i = 0; i < filters_n; i++) crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; if ((r = set_ramps(red, green, blue)) < 0) |