From 96a6575e23b5baebcdd38269b80f47cc02a2627e Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Fri, 21 Mar 2025 16:50:15 +0100 Subject: Refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/Makefile | 4 +- src/colour.c | 10 +- src/common.h | 123 ++++++++++--- src/config-ini.c | 9 +- src/config.c | 49 +++-- src/gamma-coopgamma.c | 47 +++-- src/gamma-drm.c | 15 +- src/gamma-dummy.c | 11 +- src/gamma-quartz.c | 15 +- src/gamma-randr.c | 13 +- src/gamma-vidmode.c | 11 +- src/gamma-w32gdi.c | 11 +- src/gamma.c | 12 +- src/hooks.c | 7 +- src/location-corelocation.m | 52 +++++- src/location-geoclue2.c | 63 ++++++- src/location-manual.c | 30 ++-- src/location.c | 63 ++++--- src/redshift.c | 426 +++++++++++++++++++++----------------------- src/signals.c | 3 +- src/util.c | 61 ++----- 21 files changed, 619 insertions(+), 416 deletions(-) (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 36bf18b..693638c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ CONFIGFILE = config.mk include $(CONFIGFILE) -PACKAGE_STRING = $(PACKAGE) 1.13 +VERSION_STRING = redshift-ng 1.13 OBJ =\ @@ -28,7 +28,7 @@ OBJ =\ CPPFLAGS_STRINGS =\ -D'PACKAGE="$(PACKAGE)"'\ - -D'PACKAGE_STRING="$(PACKAGE_STRING)"'\ + -D'VERSION_STRING="$(VERSION_STRING)"'\ -D'LOCALEDIR="$(LOCALEDIR)"' diff --git a/src/colour.c b/src/colour.c index 0138980..88acf64 100644 --- a/src/colour.c +++ b/src/colour.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -43,11 +44,6 @@ colour_setting_diff_is_major(const struct colour_setting *a, const struct colour } -#if defined(__GNUC__) -# pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - - #define X(SUFFIX, TYPE, MAX, DEPTH)\ /** * Fill a gamma ramp @@ -65,7 +61,7 @@ colour_setting_diff_is_major(const struct colour_setting *a, const struct colour size_t i;\ double v;\ brightness /= (size - 1U);\ - if (gamma == 1.0) {\ + if (exact_eq(gamma, 1.0)) {\ brightness *= (MAX);\ for (i = 0; i < size; i++)\ ramp[i] = (TYPE)(i * brightness);\ diff --git a/src/common.h b/src/common.h index e3cc37b..d9a7c3a 100644 --- a/src/common.h +++ b/src/common.h @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -51,7 +52,9 @@ #ifdef WINDOWS # include # define localtime_r(T, TM) localtime_s((TM), (T)) +# define pause() millisleep(100U) #else +# include # include # include #endif @@ -84,8 +87,13 @@ #if defined(__clang__) # pragma clang diagnostic ignored "-Wunsafe-buffer-usage" /* broken in clang 19.1.7 */ +# pragma clang diagnostic ignored "-Wdisabled-macro-expansion" /* warns about system headers (also a stupid warning) */ +# pragma clang diagnostic ignored "-Wassign-enum" /* warns about bit field enums */ +# pragma clang diagnostic ignored "-Wpadded" /* only relevant for library headers */ +# pragma clang diagnostic ignored "-Wcomma" /* comma is useful in loop conditions */ #elif defined(__GNUC__) -# pragma GCC diagnostic ignored "-Wunsuffixed-float-constants" +# pragma GCC diagnostic ignored "-Wunsuffixed-float-constants" /* stupid warning */ +# pragma GCC diagnostic ignored "-Wpadded" /* only relevant for library headers */ #endif @@ -126,6 +134,11 @@ */ #define BETWEEN(LO, X, UP) ((LO) < (X) && (X) < (UP)) +/** + * Quiet not-a-number `double` + */ +#define FNAN ((double)NAN) /* because clang warns when implicitly promoted to double */ + /** * Symbol used to delimit paths in environment @@ -598,8 +611,8 @@ struct settings { struct setting_f brightness; struct setting_f3 gamma; } day, night; - struct setting_i preserve_gamma; /* Whether to preserve gamma ramps if supported by gamma method. */ - struct setting_i use_fade; /* Whether to fade between large skips in colour temperature. */ + struct setting_i preserve_gamma; + struct setting_i use_fade; /* Whether to fade between large skips in colour temperature */ struct setting_f elevation_high; /* TODO no cmdline option */ struct setting_f elevation_low; /* TODO no cmdline option */ @@ -608,14 +621,14 @@ struct settings { struct setting_time end; } dawn, dusk; /* TODO no cmdline option */ - /* Selected gamma method. */ + /* Selected gamma method */ const struct gamma_method *method; - /* Arguments for gamma method. */ + /* Arguments for gamma method */ char *method_args; - /* Selected location provider. */ + /* Selected location provider */ const struct location_provider *provider; - /* Arguments for location provider. */ + /* Arguments for location provider */ char *provider_args; }; @@ -635,6 +648,12 @@ struct gamma_method { */ int autostart; + /** + * 1 if the method automatically resets the adjustments when disconnected, + * 0 otherwise + */ + int autoreset; + /** * Create an initialised state object * @@ -703,13 +722,15 @@ struct gamma_method { * * @param NAME:const char * Value for `.name` * @param AUTOSTART:int Value for `.autostart` + * @param AUTORESET:int Value for `.autoreset` * @param PREFIX:identifier The text, sans terminal underscore (_), prefixed to the * names of each function implementing the adjustment method */ -#define GAMMA_METHOD_INIT(NAME, AUTOSTART, PREFIX)\ +#define GAMMA_METHOD_INIT(NAME, AUTOSTART, AUTORESET, PREFIX)\ {\ .name = (NAME),\ .autostart = (AUTOSTART),\ + .autoreset = (AUTORESET),\ .create = &PREFIX##_create,\ .set_option = &PREFIX##_set_option,\ .print_help = &PREFIX##_print_help,\ @@ -857,14 +878,26 @@ extern struct colour_setting night_settings; extern union scheme scheme; /** - * Whether the application is in verbose mode + * The mode the application is running in */ -extern int verbose; +extern enum program_mode mode; /** - * The mode the application is running in + * Whether initially applied adjustments (assumed + * to be colour calibration) shall remain applied */ -extern enum program_mode mode; +extern int preserve_gamma; + +/** + * Whether smooth transitions shall be applied when + * a large change in colour settings occurs + */ +extern int use_fade; + +/** + * Whether the application is in verbose mode + */ +extern int verbose; /* colour.c */ @@ -963,13 +996,44 @@ void load_settings(struct settings *settings, int argc, char *argv[]); /* gamma.c */ +/** + * Get and configure adjustment method + * + * @param settings The loaded application settings, will be updated + * to point `settings->method` to the adjustment method + * @param method_state_out Output parameter for the state of the adjustment method + * + * The function will print an error message and exit the + * process if no adjustment method is available + */ void acquire_adjustment_method(struct settings *settings, GAMMA_STATE **method_state_out); /* location.c */ -int get_location(const struct location_provider *provider, LOCATION_STATE *state, int timeout, struct location *loc); +/** + * Get the current location from the location provider + * + * @param provider The location provider functions + * @param state The location provider state + * @param timeout The number of milliseconds to wait, -1 for indefinitely + * @param location_out Output parameter for the location, in GPS coordinates + * @return 1 if `*location_out` was updated, + * 0 if the timeout was reached, + * -1 on error + */ +int get_location(const struct location_provider *provider, LOCATION_STATE *state, int timeout, struct location *location_out); +/** + * Get and configure location provider + * + * @param settings The loaded application settings, will be updated + * to point `settings->provider` to the location provider + * @param location_state_out Output parameter for the state of the location provider + * + * The function will print an error message and exit the + * process if no location provider is available + */ void acquire_location_provider(struct settings *settings, LOCATION_STATE **location_state_out); /** @@ -1064,55 +1128,58 @@ DIR *try_path_opendir(const struct env_path *path_spec, const char **path_out, c #ifndef WINDOWS /** - * Create a pipe(7) where both ends have `O_CLOEXEC` and, - * if available for pipes, `O_DIRECT`, applied, the read-end - * will also have `O_NONBLOCK` applied + * Create a pipe(7) where both ends have `O_CLOEXEC`, + * the read-end will also have `O_NONBLOCK` applied * * @param pipefds Output parameter for the pipe's file descriptors: * 0) reading file descriptor, and * 1) writing file descriptor - * @return 0 on success, -1 on failure */ -int pipe_rdnonblock(int pipefds[2]); +void pipe_rdnonblock(int pipefds[2]); #endif extern const struct gamma_method dummy_gamma_method; - #ifdef ENABLE_COOPGAMMA extern const struct gamma_method coopgamma_gamma_method; #endif - #ifdef ENABLE_RANDR extern const struct gamma_method randr_gamma_method; #endif - #ifdef ENABLE_VIDMODE extern const struct gamma_method vidmode_gamma_method; #endif - #ifdef ENABLE_DRM extern const struct gamma_method drm_gamma_method; #endif - #ifdef ENABLE_QUARTZ extern const struct gamma_method quartz_gamma_method; #endif - #ifdef ENABLE_W32GDI extern const struct gamma_method w32gdi_gamma_method; #endif - extern const struct location_provider manual_location_provider; - #ifdef ENABLE_GEOCLUE2 extern const struct location_provider geoclue2_location_provider; #endif - #ifdef ENABLE_CORELOCATION extern const struct location_provider corelocation_location_provider; #endif +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif +static inline int +exact_eq(double a, double b) +{ + return a == b; +} +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + #endif diff --git a/src/config-ini.c b/src/config-ini.c index 1a78903..015668f 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -27,7 +28,7 @@ static const struct env_path paths[] = { {0, "XDG_CONFIG_HOME", "/redshift-ng/redshift.conf"}, {0, "XDG_CONFIG_HOME", "/redshift/redshift.conf"}, {0, "XDG_CONFIG_HOME", "/redshift.conf"}, -#ifdef WINDOWS +#if defined(WINDOWS) {0, "localappdata", "/redshift-ng/redshift.conf"}, {0, "localappdata", "/redshift/redshift.conf"}, {0, "localappdata", "/redshift.conf"}, @@ -43,9 +44,11 @@ static const struct env_path paths[] = { {1, "XDG_CONFIG_DIRS", "/redshift-ng/redshift.conf"}, {1, "XDG_CONFIG_DIRS", "/redshift/redshift.conf"}, {1, "XDG_CONFIG_DIRS", "/redshift.conf"}, +#if !defined(WINDOWS) {0, "", "/etc/redshift-ng/redshift.conf"}, {0, "", "/etc/redshift/redshift.conf"}, {0, "", "/etc/redshift.conf"} +#endif }; @@ -95,7 +98,7 @@ config_ini_init(struct config_ini_state *state, const char *path) char *line = NULL, *s, *p, *value, *end, *pathbuf; char *next_line = NULL, *name; size_t size = 0; - ssize_t len; + ssize_t len = 0; /* initialised to silence false warning from clang */ FILE *f; state->sections = NULL; diff --git a/src/config.c b/src/config.c index 1613770..5762a9c 100644 --- a/src/config.c +++ b/src/config.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -21,13 +22,15 @@ /* TODO missing translation */ USAGE("[-b day:night] [-c file] [-g r:g:b] [-l latitude:longitude | -l provider[:options]]" - " [-m method[:options]] [-o | -O temperature | -t day:night | -x] [-pPrv] | -hV"); + " [-m method[:options]] [-o | -O temperature | -t day:night | -x] [-pPrv] | -h | -V"); struct colour_setting day_settings; struct colour_setting night_settings; union scheme scheme = {.type = SOLAR_SCHEME}; enum program_mode mode = PROGRAM_MODE_CONTINUAL; +int preserve_gamma; +int use_fade; int verbose = 0; @@ -266,7 +269,7 @@ set_temperature(char *str, struct setting_lu *day, struct setting_lu *night, con } errno = 0; - for (i = 0, j = 0; i < ELEMSOF(settings); i++, j += settings[i] ? 1 : 0) { + for (i = 0, j = 0; i < ELEMSOF(settings); j += settings[i++] ? 1 : 0) { if (!settings[i] || !strs[j] || settings[i]->source > source) continue; if (settings[i]->source & SETTING_CONFIGFILE) { @@ -311,7 +314,7 @@ set_brightness(char *str, struct setting_f *day, struct setting_f *night, const } errno = 0; - for (i = 0, j = 0; i < ELEMSOF(settings); i++, j += settings[i] ? 1 : 0) { + for (i = 0, j = 0; i < ELEMSOF(settings); j += settings[i++] ? 1 : 0) { if (!settings[i] || !strs[j] || settings[i]->source > source) continue; if (settings[i]->source & SETTING_CONFIGFILE) { @@ -356,7 +359,7 @@ set_gamma(char *str, struct setting_f3 *day, struct setting_f3 *night, const cha } errno = 0; - for (i = 0, j = 0; i < ELEMSOF(settings); i++, j += settings[i] ? 3 : 0) { + for (i = 0, j = 0; i < ELEMSOF(settings); j += settings[i++] ? 3 : 0) { if (!settings[i] || !strs[j] || settings[i]->source > source) continue; if (settings[i]->source & SETTING_CONFIGFILE) { @@ -445,7 +448,7 @@ set_transition_time(char *str, struct setting_time *start, struct setting_time * if (!get_strings(str, ELEMSOF(strs), strs, '-')) return 0; - for (i = 0; i < ELEMSOF(settings); i++) { + for (i = 0; i < (int)ELEMSOF(settings); i++) { if (!strs[i] || settings[i]->source > source) continue; if (settings[i]->source & SETTING_CONFIGFILE) { @@ -703,9 +706,8 @@ load_from_cmdline(struct settings *settings, int argc, char *argv[]) break; case 'V': - printf("%s\n", PACKAGE_STRING); + printf("%s\n", VERSION_STRING); exit(0); - break; case 'x': mode = PROGRAM_MODE_RESET; @@ -815,6 +817,10 @@ load_settings(struct settings *settings, int argc, char *argv[]) int i, j, n; time_t duration; + /* Clear unused bit so they do not interfere with comparsion */ + memset(&day_settings, 0, sizeof(day_settings)); + memset(&night_settings, 0, sizeof(night_settings)); + /* Load settings; some validation takes place */ load_defaults(settings); load_from_cmdline(settings, argc, argv); @@ -824,8 +830,8 @@ load_settings(struct settings *settings, int argc, char *argv[]) load_from_config_ini(settings, setting->name, setting->value); /* Further validate settings */ - n = !settings->dawn.start.source + !settings->dawn.end.source; - n += !settings->dusk.start.source + !settings->dusk.end.source; + n = !!settings->dawn.start.source + !!settings->dawn.end.source; + n += !!settings->dusk.start.source + !!settings->dusk.end.source; if (n) { scheme.type = CLOCK_SCHEME; if (n != 4) @@ -852,22 +858,6 @@ load_settings(struct settings *settings, int argc, char *argv[]) if (settings->elevation_high.value < settings->elevation_low.value) eprintf(_("High transition elevation cannot be lower than the low transition elevation.")); - /* If the effects are the same throughout the day, do not use a transition scheme */ -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - if (settings->day.temperature.value == settings->night.temperature.value && - settings->day.brightness.value == settings->night.brightness.value && - settings->day.gamma.value[0] == settings->night.gamma.value[0] && - settings->day.gamma.value[1] == settings->night.gamma.value[1] && - settings->day.gamma.value[2] == settings->night.gamma.value[2]) { - scheme.type = STATIC_SCHEME; - } -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif - /* If reseting effects, use neutral colour settings (static scheme) and do not preserve gamma */ if (mode == PROGRAM_MODE_RESET) { scheme.type = STATIC_SCHEME; @@ -878,6 +868,8 @@ load_settings(struct settings *settings, int argc, char *argv[]) } /* Publish loaded settings */ + preserve_gamma = settings->preserve_gamma.value; + use_fade = settings->use_fade.value; day_settings.temperature = settings->day.temperature.value; day_settings.brightness = settings->day.brightness.value; day_settings.gamma[0] = settings->day.gamma.value[0]; @@ -888,7 +880,10 @@ load_settings(struct settings *settings, int argc, char *argv[]) night_settings.gamma[0] = settings->night.gamma.value[0]; night_settings.gamma[1] = settings->night.gamma.value[1]; night_settings.gamma[2] = settings->night.gamma.value[2]; - if (scheme.type == SOLAR_SCHEME) { + if (!memcmp(&day_settings, &night_settings, sizeof(day_settings))) { + /* If the effects are the same throughout the day, do not use a transition scheme */ + scheme.type = STATIC_SCHEME; + } else if (scheme.type == SOLAR_SCHEME) { scheme.elevation.high = settings->elevation_high.value; scheme.elevation.low = settings->elevation_low.value; scheme.elevation.range = scheme.elevation.high - scheme.elevation.low; diff --git a/src/gamma-coopgamma.c b/src/gamma-coopgamma.c index a4e08ef..fab083b 100644 --- a/src/gamma-coopgamma.c +++ b/src/gamma-coopgamma.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -20,18 +21,24 @@ #include +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wkeyword-macro" +#endif + struct coopgamma_output_id { char *edid; size_t index; }; + struct coopgamma_crtc_state { libcoopgamma_filter_t filter; libcoopgamma_ramps_t plain_ramps; size_t rampsize; }; + struct gamma_state { libcoopgamma_context_t ctx; struct coopgamma_crtc_state *crtcs; @@ -49,6 +56,7 @@ struct gamma_state { struct signal_blockage {int dummy;}; + static int unblocked_signal(int signo, struct signal_blockage *prev) { @@ -145,6 +153,7 @@ coopgamma_create(struct gamma_state **state_out) return 0; } + static int coopgamma_start(struct gamma_state *state) { @@ -242,12 +251,12 @@ coopgamma_start(struct gamma_state *state) crtc->filter.priority = state->priority; crtc->filter.crtc = state->outputs[i].edid; crtc->filter.lifespan = lifespan; -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" #endif crtc->filter.class = PACKAGE "::redshift::standard"; -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic pop #endif @@ -276,14 +285,15 @@ coopgamma_start(struct gamma_state *state) LIST_RAMPS_STOP_VALUE_TYPES(X, ;); #undef X default: - if (info.depth > 0) - fprintf(stderr, _("output `%s' uses an unsupported depth " - "for its gamma ramps: %i bits, skipping\n"), - outputs[i], info.depth); - else - fprintf(stderr, _("output `%s' uses an unrecognised depth, " - "for its gamma ramps, with the code %i, " - "skipping\n"), outputs[i], info.depth); + if (info.depth > 0) { + weprintf(_("output `%s' uses an unsupported depth " + "for its gamma ramps: %i bits, skipping\n"), + outputs[i], info.depth); + } else { + weprintf(_("output `%s' uses an unrecognised depth, " + "for its gamma ramps, with the code %i, " + "skipping\n"), outputs[i], info.depth); + } continue; } crtc->rampsize *= info.red_size + info.green_size + info.blue_size; @@ -347,6 +357,7 @@ coopgamma_start(struct gamma_state *state) return 0; } + static void coopgamma_free(struct gamma_state *state) { @@ -374,14 +385,14 @@ coopgamma_free(struct gamma_state *state) state->outputs = NULL; } + static void coopgamma_print_help(FILE *f) { fputs(_("Adjust gamma ramps with coopgamma.\n"), f); fputs("\n", f); - /* TRANSLATORS: coopgamma help output - left column must not be translated */ + /* TRANSLATORS: coopgamma help output left column must not be translated */ fputs(_(" edid=EDID \tEDID of monitor to apply adjustments to, enter " "`list' to list available monitors\n" " crtc=N \tIndex of CRTC to apply adjustments to\n" @@ -394,6 +405,7 @@ coopgamma_print_help(FILE *f) fputs("\n", f); } + static int coopgamma_set_option(struct gamma_state *state, const char *key, const char *value) { @@ -416,8 +428,7 @@ coopgamma_set_option(struct gamma_state *state, const char *key, const char *val return -1; } if (!strcasecmp(value, "list")) { - /* TRANSLATORS: coopgamma help output - the word "coopgamma" must not be translated */ + /* TRANSLATORS: coopgamma help output the word "coopgamma" must not be translated */ printf(_("Available adjustment methods for coopgamma:\n")); for (i = 0; state->methods[i]; i++) printf(" %s\n", state->methods[i]); @@ -454,13 +465,14 @@ coopgamma_set_option(struct gamma_state *state, const char *key, const char *val } state->n_outputs++; } else { - fprintf(stderr, _("Unknown method parameter: `%s'."), key); + weprintf(_("Unknown method parameter: `%s'."), key); return -1; } return 0; } + static void coopgamma_restore(struct gamma_state *state) { @@ -472,6 +484,7 @@ coopgamma_restore(struct gamma_state *state) state->crtcs[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; } + static int coopgamma_apply(struct gamma_state *state, const struct colour_setting *setting, int perserve) { @@ -517,4 +530,4 @@ coopgamma_apply(struct gamma_state *state, const struct colour_setting *setting, } -const struct gamma_method coopgamma_gamma_method = GAMMA_METHOD_INIT("coopgamma", 1, coopgamma); +const struct gamma_method coopgamma_gamma_method = GAMMA_METHOD_INIT("coopgamma", 1, 0, coopgamma); diff --git a/src/gamma-drm.c b/src/gamma-drm.c index 03454ae..2de0261 100644 --- a/src/gamma-drm.c +++ b/src/gamma-drm.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -35,6 +36,7 @@ struct drm_crtc_state { uint16_t *b_gamma; }; + struct gamma_state { int card_num; int crtc_num; @@ -57,12 +59,12 @@ drm_create(struct gamma_state **state_out) return 0; } + static int drm_start(struct gamma_state *state) { /* Acquire access to a graphics card. */ - long maxlen = strlen(DRM_DIR_NAME) + strlen(DRM_DEV_NAME) + 10; - char pathname[maxlen]; + char pathname[STRLEN(DRM_DIR_NAME"") + STRLEN(DRM_DEV_NAME"") + 3U * sizeof(int) + 2U]; int crtc_count; struct drm_crtc_state *crtcs; @@ -169,6 +171,7 @@ drm_start(struct gamma_state *state) return 0; } + static void drm_restore(struct gamma_state *state) { @@ -182,6 +185,7 @@ drm_restore(struct gamma_state *state) } } + static void drm_free(struct gamma_state *state) { @@ -207,6 +211,7 @@ drm_free(struct gamma_state *state) free(state); } + static void drm_print_help(FILE *f) { @@ -220,6 +225,7 @@ drm_print_help(FILE *f) fputs("\n", f); } + static int drm_set_option(struct gamma_state *state, const char *key, const char *value) { @@ -239,6 +245,7 @@ drm_set_option(struct gamma_state *state, const char *key, const char *value) return 0; } + static int drm_apply(struct gamma_state *state, const struct colour_setting *setting, int preserve) { @@ -283,4 +290,4 @@ drm_apply(struct gamma_state *state, const struct colour_setting *setting, int p } -const struct gamma_method drm_gamma_method = GAMMA_METHOD_INIT("drm", 0, drm); +const struct gamma_method drm_gamma_method = GAMMA_METHOD_INIT("drm", 0, 0, drm); diff --git a/src/gamma-dummy.c b/src/gamma-dummy.c index 1751bbd..7074cd9 100644 --- a/src/gamma-dummy.c +++ b/src/gamma-dummy.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -26,6 +27,7 @@ dummy_create(struct gamma_state **state_out) return 0; } + static int dummy_start(struct gamma_state *state) { @@ -34,18 +36,21 @@ dummy_start(struct gamma_state *state) return 0; } + static void dummy_restore(struct gamma_state *state) { (void) state; } + static void dummy_free(struct gamma_state *state) { (void) state; } + static void dummy_print_help(FILE *f) { @@ -53,6 +58,7 @@ dummy_print_help(FILE *f) fputs("\n", f); } + static int dummy_set_option(struct gamma_state *state, const char *key, const char *value) { @@ -62,6 +68,7 @@ dummy_set_option(struct gamma_state *state, const char *key, const char *value) return -1; } + static int dummy_apply(struct gamma_state *state, const struct colour_setting *setting, int preserve) { @@ -72,4 +79,4 @@ dummy_apply(struct gamma_state *state, const struct colour_setting *setting, int } -const struct gamma_method dummy_gamma_method = GAMMA_METHOD_INIT("dummy", 0, dummy); +const struct gamma_method dummy_gamma_method = GAMMA_METHOD_INIT("dummy", 0, 0, dummy); diff --git a/src/gamma-quartz.c b/src/gamma-quartz.c index d323d33..c05e72d 100644 --- a/src/gamma-quartz.c +++ b/src/gamma-quartz.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -27,6 +28,7 @@ struct quartz_display_state { float *saved_ramps; }; + struct gamma_state { struct quartz_display_state *displays; uint32_t display_count; @@ -41,6 +43,7 @@ quartz_create(struct gamma_state **state_out) return 0; } + static int quartz_start(struct gamma_state *state) { @@ -106,12 +109,14 @@ quartz_start(struct gamma_state *state) return 0; } + static void quartz_restore(struct gamma_state *state) { CGDisplayRestoreColorSyncSettings(); } + static void quartz_free(struct gamma_state *state) { @@ -123,6 +128,7 @@ quartz_free(struct gamma_state *state) free(state); } + static void quartz_print_help(FILE *f) { @@ -130,6 +136,7 @@ quartz_print_help(FILE *f) fputs("\n", f); } + static int quartz_set_option(struct gamma_state *state, const char *key, const char *value) { @@ -143,6 +150,7 @@ quartz_set_option(struct gamma_state *state, const char *key, const char *value) return 0; } + static void quartz_apply_for_display(struct gamma_state *state, int display_index, const colour_setting_t *setting, int preserve) { @@ -183,16 +191,15 @@ quartz_apply_for_display(struct gamma_state *state, int display_index, const col free(gamma_ramps); } + static int quartz_apply(struct gamma_state *state, const colour_setting_t *setting, int preserve) { uint32_t i; - for (i = 0; i < state->display_count; i++) quartz_apply_for_display(state, i, setting, preserve); - return 0; } -const struct gamma_method quartz_gamma_method = GAMMA_METHOD_INIT("quartz", 1, quartz); +const struct gamma_method quartz_gamma_method = GAMMA_METHOD_INIT("quartz", 1, 1, quartz); diff --git a/src/gamma-randr.c b/src/gamma-randr.c index 561a243..10ea277 100644 --- a/src/gamma-randr.c +++ b/src/gamma-randr.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -35,6 +36,7 @@ struct randr_crtc_state { uint16_t *saved_ramps; }; + struct gamma_state { xcb_connection_t *conn; xcb_screen_t *screen; @@ -97,6 +99,7 @@ fail: return -1; } + static int randr_start(struct gamma_state *state) { @@ -208,6 +211,7 @@ randr_start(struct gamma_state *state) return 0; } + static void randr_restore(struct gamma_state *state) { @@ -237,6 +241,7 @@ randr_restore(struct gamma_state *state) } } + static void randr_free(struct gamma_state *state) { @@ -254,6 +259,7 @@ randr_free(struct gamma_state *state) free(state); } + static void randr_print_help(FILE *f) { @@ -269,6 +275,7 @@ randr_print_help(FILE *f) fputs("\n", f); } + static int randr_set_option(struct gamma_state *state, const char *key, const char *value) { @@ -322,6 +329,7 @@ randr_set_option(struct gamma_state *state, const char *key, const char *value) return 0; } + static int randr_apply_for_crtc(struct gamma_state *state, int crtc_num, const struct colour_setting *setting, int preserve) { @@ -381,6 +389,7 @@ randr_apply_for_crtc(struct gamma_state *state, int crtc_num, const struct colou return 0; } + static int randr_apply(struct gamma_state *state, const struct colour_setting *setting, int preserve) { @@ -401,4 +410,4 @@ randr_apply(struct gamma_state *state, const struct colour_setting *setting, int } -const struct gamma_method randr_gamma_method = GAMMA_METHOD_INIT("randr", 1, randr); +const struct gamma_method randr_gamma_method = GAMMA_METHOD_INIT("randr", 1, 0, randr); diff --git a/src/gamma-vidmode.c b/src/gamma-vidmode.c index a855914..7c5321a 100644 --- a/src/gamma-vidmode.c +++ b/src/gamma-vidmode.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -48,6 +49,7 @@ vidmode_create(struct gamma_state **state_out) return 0; } + static int vidmode_start(struct gamma_state *state) { @@ -97,6 +99,7 @@ vidmode_start(struct gamma_state *state) return 0; } + static void vidmode_free(struct gamma_state *state) { @@ -105,6 +108,7 @@ vidmode_free(struct gamma_state *state) free(state); } + static void vidmode_print_help(FILE *f) { @@ -116,6 +120,7 @@ vidmode_print_help(FILE *f) fputs("\n", f); } + static int vidmode_set_option(struct gamma_state *state, const char *key, const char *value) { @@ -131,6 +136,7 @@ vidmode_set_option(struct gamma_state *state, const char *key, const char *value return 0; } + static void vidmode_restore(struct gamma_state *state) { @@ -146,6 +152,7 @@ vidmode_restore(struct gamma_state *state) weprintf(_("X request failed: %s"), "XF86VidModeSetGammaRamp"); } + static int vidmode_apply(struct gamma_state *state, const struct colour_setting *setting, int preserve) { @@ -189,4 +196,4 @@ vidmode_apply(struct gamma_state *state, const struct colour_setting *setting, i } -const struct gamma_method vidmode_gamma_method = GAMMA_METHOD_INIT("vidmode", 1, vidmode); +const struct gamma_method vidmode_gamma_method = GAMMA_METHOD_INIT("vidmode", 1, 0, vidmode); diff --git a/src/gamma-w32gdi.c b/src/gamma-w32gdi.c index 533365d..6912167 100644 --- a/src/gamma-w32gdi.c +++ b/src/gamma-w32gdi.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -40,6 +41,7 @@ w32gdi_create(struct gamma_state **state_out) return 0; } + static int w32gdi_start(struct gamma_state *state) { @@ -76,6 +78,7 @@ w32gdi_start(struct gamma_state *state) return 0; } + static void w32gdi_free(struct gamma_state *state) { @@ -84,6 +87,7 @@ w32gdi_free(struct gamma_state *state) } + static void w32gdi_print_help(FILE *f) { @@ -91,6 +95,7 @@ w32gdi_print_help(FILE *f) fputs("\n", f); } + static int w32gdi_set_option(struct gamma_state *state, const char *key, const char *value) { @@ -104,6 +109,7 @@ w32gdi_set_option(struct gamma_state *state, const char *key, const char *value) return 0; } + static void w32gdi_restore(struct gamma_state *state) { @@ -132,6 +138,7 @@ done: ReleaseDC(NULL, hDC); } + static int w32gdi_apply(struct gamma_state *state, const colour_setting_t *setting, int preserve) { @@ -191,4 +198,4 @@ done: } -const struct gamma_method w32gdi_gamma_method = GAMMA_METHOD_INIT("winfdi", 1, w32gdi); +const struct gamma_method w32gdi_gamma_method = GAMMA_METHOD_INIT("winfdi", 1, 0, w32gdi); diff --git a/src/gamma.c b/src/gamma.c index 628105b..5b64f8c 100644 --- a/src/gamma.c +++ b/src/gamma.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -43,6 +44,15 @@ const struct gamma_method *gamma_methods[] = { }; +/** + * Attempt to start a specific adjustment method + * + * @param method The adjustment method + * @param state_out Output parameter for the adjustment method state + * @param config Loaded information file + * @param args `NULL` or option part of the command line argument for the adjustment method + * @return 0 on success, -1 on failure + */ static int try_start(const struct gamma_method *method, GAMMA_STATE **state_out, struct config_ini_state *config, char *args) { diff --git a/src/hooks.c b/src/hooks.c index aaddd4f..e3abf44 100644 --- a/src/hooks.c +++ b/src/hooks.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -53,7 +54,7 @@ static size_t dirpathlen; static const struct env_path paths[] = { {0, "XDG_CONFIG_HOME", "/redshift-ng/hooks"}, {0, "XDG_CONFIG_HOME", "/redshift/hooks"}, -#ifdef WINDOWS +#if defined(WINDOWS) {0, "localappdata", "/redshift-ng/hooks"}, {0, "localappdata", "/redshift/hooks"}, #endif @@ -63,8 +64,10 @@ static const struct env_path paths[] = { {0, NULL, "/.config/redshift/hooks"}, {1, "XDG_CONFIG_DIRS", "/redshift-ng/hooks"}, {1, "XDG_CONFIG_DIRS", "/redshift/hooks"}, +#if !defined(WINDOWS) {0, "", "/etc/redshift-ng/hooks"}, {0, "", "/etc/redshift/hooks"} +#endif }; diff --git a/src/location-corelocation.m b/src/location-corelocation.m index 9f45899..c53ed14 100644 --- a/src/location-corelocation.m +++ b/src/location-corelocation.m @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -22,17 +23,53 @@ #import +/** + * Location data + */ struct location_data { + /** + * The user's geographical location + */ struct location location; + + /** + * Whether the location provider is available + */ int available; + + /** + * Whether an unrecoverable error has occurred + */ int error; }; + struct location_state { + /** + * Slave thread, used to receive location updates + */ NSThread *thread; + + /** + * Read-end of piped used to send location data + * from the slave thread to the master thread + */ int pipe_fd_read; + + /** + * Write-end of piped used to send location data + * from the slave thread to the master thread + */ int pipe_fd_write; + + /** + * Location data available from the slave thread + */ struct location_data data; + + /** + * Location data sent to the master thread + */ struct location_data saved_data; }; @@ -136,6 +173,7 @@ pipe_close_callback(CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void @property (nonatomic) struct location_state *state; @end + @implementation LocationThread; // Run loop for location provider thread. @@ -177,6 +215,7 @@ corelocation_create(struct location_state **state_out) return 0; } + static int corelocation_start(struct location_state *state) { @@ -192,11 +231,7 @@ corelocation_start(struct location_state *state) state->data.location.lon = 0; state->saved_data = state->data; - if (pipe_rdnonblock(pipefds)) { - weprintf(_("Failed to start CoreLocation provider!")); - return -1; - } - + pipe_rdnonblock(pipefds); state->pipe_fd_read = pipefds[0]; state->pipe_fd_write = pipefds[1]; @@ -210,6 +245,7 @@ corelocation_start(struct location_state *state) return 0; } + static void corelocation_free(struct location_state *state) { @@ -218,6 +254,7 @@ corelocation_free(struct location_state *state) free(state); } + static void corelocation_print_help(FILE *f) { @@ -225,6 +262,7 @@ corelocation_print_help(FILE *f) fputs("\n", f); } + static int corelocation_set_option(struct location_state *state, const char *key, const char *value) { @@ -234,12 +272,14 @@ corelocation_set_option(struct location_state *state, const char *key, const cha return -1; } + static int corelocation_get_fd(struct location_state *state) { return state->pipe_fd_read; } + static int corelocation_fetch(struct location_state *state, struct location *location_out, int *available_out) { diff --git a/src/location-geoclue2.c b/src/location-geoclue2.c index 84ef6b4..69323c2 100644 --- a/src/location-geoclue2.c +++ b/src/location-geoclue2.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -33,21 +34,62 @@ # pragma clang diagnostic pop #endif + +/** + * D-Bus error indicating denial of access + */ #define DBUS_ACCESS_ERROR "org.freedesktop.DBus.Error.AccessDenied" +/** + * Location data + */ struct location_data { + /** + * The user's geographical location + */ struct location location; + + /** + * Whether the location provider is available + */ int available; + + /** + * Whether an unrecoverable error has occurred + */ int error; }; + struct location_state { GMainLoop *loop; + + /** + * Slave thread, used to receive location updates + */ GThread *thread; + + /** + * Read-end of piped used to send location data + * from the slave thread to the master thread + */ int pipe_fd_read; + + /** + * Write-end of piped used to send location data + * from the slave thread to the master thread + */ int pipe_fd_write; + + /** + * Location data available from the slave thread + */ struct location_data data; + + /** + * Location data sent to the master thread + */ struct location_data saved_data; }; @@ -64,12 +106,14 @@ print_denial_message(void) "information.\n")); } + static void send_data(struct location_state *state) { while (write(state->pipe_fd_write, &state->data, sizeof(state->data)) == -1 && errno == EINTR); } + /* Indicate an unrecoverable error during GeoClue2 communication */ static void mark_error(struct location_state *state) @@ -78,6 +122,7 @@ mark_error(struct location_state *state) send_data(state); } + /* Handle position change callbacks */ static void geoclue_client_signal_cb(GDBusProxy *client, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data) @@ -119,6 +164,7 @@ geoclue_client_signal_cb(GDBusProxy *client, gchar *sender_name, gchar *signal_n send_data(state); } + /* Callback when GeoClue name appears on the bus */ static void on_name_appeared(GDBusConnection *conn, const gchar *name, const gchar *name_owner, gpointer user_data) @@ -230,6 +276,7 @@ on_name_appeared(GDBusConnection *conn, const gchar *name, const gchar *name_own g_variant_unref(ret_v); } + /* Callback when GeoClue disappears from the bus */ static void on_name_vanished(GDBusConnection *connection, const gchar *name, gpointer user_data) @@ -243,6 +290,7 @@ on_name_vanished(GDBusConnection *connection, const gchar *name, gpointer user_d send_data(state); } + /* Callback when the pipe to the main thread is closed */ static gboolean on_pipe_closed(GIOChannel *channel, GIOCondition condition, gpointer user_data) @@ -294,6 +342,7 @@ run_geoclue2_loop(void *state_) return NULL; } + static int geoclue2_create(struct location_state **state_out) { @@ -304,6 +353,7 @@ geoclue2_create(struct location_state **state_out) return 0; } + static int geoclue2_start(struct location_state *state) { @@ -318,11 +368,7 @@ geoclue2_start(struct location_state *state) state->data.location.longitude = 0; state->saved_data = state->data; - if (pipe_rdnonblock(pipefds)) { - weprintf(_("Failed to start GeoClue2 provider!")); - return -1; - } - + pipe_rdnonblock(pipefds); state->pipe_fd_read = pipefds[0]; state->pipe_fd_write = pipefds[1]; @@ -333,6 +379,7 @@ geoclue2_start(struct location_state *state) return 0; } + static void geoclue2_free(struct location_state *state) { @@ -346,6 +393,7 @@ geoclue2_free(struct location_state *state) free(state); } + static void geoclue2_print_help(FILE *f) { @@ -353,6 +401,7 @@ geoclue2_print_help(FILE *f) fputs("\n", f); } + static int geoclue2_set_option(struct location_state *state, const char *key, const char *value) { @@ -362,12 +411,14 @@ geoclue2_set_option(struct location_state *state, const char *key, const char *v return -1; } + static int geoclue2_get_fd(struct location_state *state) { return state->pipe_fd_read; } + static int geoclue2_fetch(struct location_state *state, struct location *location_out, int *available_out) { diff --git a/src/location-manual.c b/src/location-manual.c index a394f29..d1285f1 100644 --- a/src/location-manual.c +++ b/src/location-manual.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -20,7 +21,10 @@ struct location_state { - struct location loc; + /** + * The specified location, any unspecified coordinate is set to NAN + */ + struct location location; }; @@ -28,34 +32,36 @@ static int manual_create(struct location_state **state_out) { *state_out = emalloc(sizeof(**state_out)); - (*state_out)->loc.latitude = NAN; - (*state_out)->loc.longitude = NAN; + (*state_out)->location.latitude = FNAN; + (*state_out)->location.longitude = FNAN; return 0; } + GCC_ONLY(__attribute__((__pure__))) static int manual_start(struct location_state *state) { - if (isnan(state->loc.latitude) || isnan(state->loc.longitude)) + if (isnan(state->location.latitude) || isnan(state->location.longitude)) eprintf(_("Latitude and longitude must be set.")); return 0; } + static void manual_free(struct location_state *state) { free(state); } + static void manual_print_help(FILE *f) { fputs(_("Specify location manually.\n"), f); fputs("\n", f); - /* TRANSLATORS: Manual location help output - left column must not be translated */ + /* TRANSLATORS: Manual location help output left column must not be translated */ fputs(_(" lat=N\t\tLatitude\n" " lon=N\t\tLongitude\n"), f); fputs("\n", f); @@ -64,10 +70,10 @@ manual_print_help(FILE *f) fputs("\n", f); } + static int manual_set_option(struct location_state *state, const char *key, const char *value) { - /* Parse float value */ char *end; double v; @@ -79,9 +85,9 @@ manual_set_option(struct location_state *state, const char *key, const char *val } if (!strcasecmp(key, "lat")) { - state->loc.latitude = v; + state->location.latitude = v; } else if (!strcasecmp(key, "lon")) { - state->loc.longitude = v; + state->location.longitude = v; } else { weprintf(_("Unknown method parameter: `%s'."), key); return -1; @@ -90,6 +96,7 @@ manual_set_option(struct location_state *state, const char *key, const char *val return 0; } + static int manual_get_fd(struct location_state *state) { @@ -97,10 +104,11 @@ manual_get_fd(struct location_state *state) return -1; } + static int manual_fetch(struct location_state *state, struct location *location_out, int *available_out) { - *location_out = state->loc; + *location_out = state->location; *available_out = 1; return 0; } diff --git a/src/location.c b/src/location.c index 60a1ca0..a70be8c 100644 --- a/src/location.c +++ b/src/location.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -31,6 +32,34 @@ const struct location_provider *location_providers[] = { }; +/** + * Get the current monotonic time in milliseconds + * + * @return The number of milliseconds elapsed since some arbitrary fixed time + */ +static long long int +get_monotonic_millis(void) +{ +#if defined(WINDOWS) + return (long long int)GetTickCount64(); +#else + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now)) + eprintf("clock_gettime CLOCK_MONOTONIC:"); + return (long long int)now.tv_sec * 1000LL + (long long int)now.tv_nsec / 1000000LL; +#endif +} + + +/** + * Attempt to start a specific location provider + * + * @param provider The location provider + * @param state_out Output parameter for the location provider state + * @param config Loaded information file + * @param args `NULL` or option part of the command line argument for the location provider + * @return 0 on success, -1 on failure + */ static int try_start(const struct location_provider *provider, LOCATION_STATE **state_out, struct config_ini_state *config, char *args) { @@ -65,7 +94,7 @@ try_start(const struct location_provider *provider, LOCATION_STATE **state_out, * without keys on the command line for convencience * and for backwards compatability. We add the proper * keys here before calling set_option(). */ - if (!strcmp(provider->name, "manual") && i < ELEMSOF(manual_keys)) { + if (!strcmp(provider->name, "manual") && i < (int)ELEMSOF(manual_keys)) { key = manual_keys[i]; value = args; } else { @@ -103,28 +132,15 @@ fail: } -static long long int -get_monotonic_millis(void) -{ -#if defined(WINDOWS) - return (long long int)GetTickCount64(); -#else - struct timespec now; - if (clock_gettime(CLOCK_MONOTONIC, &now)) - eprintf("clock_gettime CLOCK_MONOTONIC:"); - return (long long int)now.tv_sec * 1000LL + (long long int)now.tv_nsec / 1000000LL; -#endif -} - - -/* Wait for location to become available from provider. - Waits until timeout (milliseconds) has elapsed or forever if timeout - is -1. Writes location to loc. Returns -1 on error, - 0 if timeout was reached, 1 if location became available. */ int -get_location(const struct location_provider *provider, LOCATION_STATE *state, int timeout, struct location *loc) +get_location(const struct location_provider *provider, LOCATION_STATE *state, int timeout, struct location *location_out) { - int r, available; +#ifdef WINDOWS /* we don't have poll on Windows, but neither do with have any dynamic location providers */ + int available; + return provider->fetch(state, location_out, &available) < 0 ? -1 : available; + +#else + int r, available = 0; struct pollfd pollfds[1]; long long int now = get_monotonic_millis(); long long int end = now + (long long int)timeout; @@ -150,11 +166,12 @@ get_location(const struct location_provider *provider, LOCATION_STATE *state, in } } - if (provider->fetch(state, loc, &available) < 0) + if (provider->fetch(state, location_out, &available) < 0) return -1; } while (!available); return 1; +#endif } diff --git a/src/redshift.c b/src/redshift.c index 04c6c77..77cabb8 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -18,42 +19,54 @@ */ #include "common.h" -/* poll.h is not available on Windows but there is no Windows location provider - * using polling. On Windows, we just define some stubs to make things compile. + +/** + * The number of milliseconds to sleep normally between colour updates */ -#ifndef WINDOWS -# include -#else -# define POLLIN 0 -struct pollfd { - int fd; - short events; - short revents; -}; -int poll(struct pollfd *fds, int nfds, int timeout) { abort(); } -#endif +#define SLEEP_DURATION 5000U +/** + * The number of milliseconds to sleep between each step during + * fade between large colour settings + */ +#define SLEEP_DURATION_SHORT 25U -/* Duration of sleep between screen updates (milliseconds). */ -#define SLEEP_DURATION 5000 -#define SLEEP_DURATION_SHORT 100 +/** + * The fade time, for when making large changes in colour + * settings, divided by `SLEEP_DURATION_SHORT` + */ +#define FADE_LENGTH 160U -/* Length of fade in numbers of short sleep durations. */ -#define FADE_LENGTH 40 +/** + * The user's current geographical location + */ +static struct location location; -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wfloat-equal" -#endif -static int -exact_eq(double a, double b) -{ - return a == b; -} -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif +/** + * Whether the location provider is available + */ +static int location_available; + +/** + * State of location provider, `NULL` if none + */ +static LOCATION_STATE *provider_state; + +/** + * Locaiton provider functions + */ +static const struct location_provider *provider; + +/** + * State of the gamma ramp adjustment method, `NULL` if none (print mode) + */ +static GAMMA_STATE *method_state; + +/** + * Gamma ramp adjustment functions + */ +static const struct gamma_method *method; /** @@ -124,14 +137,12 @@ print_period(enum period period, double day_level) * Get the current period of day and the colour settings * applicable to the current time of the day * - * @param location Geographical user location * @param colour_out Output parameter for the colour settings * @param period_out Output parameter for the period of the day * @param day_level_out Output parameter for the dayness level */ static void -get_colour_settings(const struct location *location, struct colour_setting *colour_out, - enum period *period_out, double *day_level_out) +get_colour_settings(struct colour_setting *colour_out, enum period *period_out, double *day_level_out) { time_t time_offset; double t, elevation; @@ -148,7 +159,7 @@ get_colour_settings(const struct location *location, struct colour_setting *colo *day_level_out = fma(t, scheme.time.periods->diff_over_duration, scheme.time.periods->day_level); } else if (scheme.type == SOLAR_SCHEME) { - if (libred_solar_elevation(location->latitude, location->longitude, &elevation)) + if (libred_solar_elevation(location.latitude, location.longitude, &elevation)) eprintf("libred_solar_elevation:"); if (verbose) { /* TRANSLATORS: Append degree symbol if possible. */ @@ -158,7 +169,7 @@ get_colour_settings(const struct location *location, struct colour_setting *colo } else { /* Static scheme, no dayness-level or peroid; day_settings == nigh_settings, use either */ - *day_level_out = NAN; + *day_level_out = FNAN; *period_out = PERIOD_NONE; *colour_out = day_settings; return; @@ -200,201 +211,167 @@ ease_fade(double t) } -/* Run continual mode loop - This is the main loop of the continual mode which keeps track of the - current time and continuously updates the screen to the appropriate - colour temperature. */ +#ifndef WINDOWS /* we don't have poll on Windows, but neither do with have any dynamic location providers */ +/** + * Get the current location + * + * The function will return once any of the following has occured: + * - the specified timeout duration has elapsed, + * - a location message has been received (could be location or error), or + * - a signal(7) was received + * + * @param timeout The number of milliseconds to wait before + * returning without updating the location + * @param location_fd File descriptor to wait on to receive input event + */ static void -run_continual_mode(const struct location_provider *provider, LOCATION_STATE *location_state, - const struct gamma_method *method, GAMMA_STATE *method_state, int use_fade, - int preserve_gamma) +pull_location(unsigned int timeout, int location_fd) { - int done = 0; - int prev_disabled = 1; - int disabled = 0; - int location_available = 1; - struct colour_setting fade_start_interp; - struct colour_setting prev_target_interp; - struct colour_setting interp; - struct location loc; - - /* Short fade parameters */ - int fade_length = 0; - int fade_time = 0; - - /* Save previous parameters so we can avoid printing status updates if - * the values did not change. */ - enum period prev_period = PERIOD_NONE; - - install_signal_handlers(); - - /* Previous target colour setting and current actual colour setting. - * Actual colour setting takes into account the current colour fade. */ - prev_target_interp = COLOUR_SETTING_NEUTRAL; - - interp = COLOUR_SETTING_NEUTRAL; - - loc = (struct location){NAN, NAN}; - if (scheme.type == SOLAR_SCHEME) { - weprintf(_("Waiting for initial location to become available...")); + struct pollfd pollfds[1]; + struct location new_location; + int r, new_available; + + /* Await new location information */ + pollfds[0].fd = location_fd; + pollfds[0].events = POLLIN; + r = poll(pollfds, 1, (int)timeout); + if (r < 0) { +#ifndef WINDOWS + if (errno == EINTR) + return; +#endif + weprintf("poll:"); + eprintf(_("Unable to get location from provider.")); + } else if (!r) { + return; + } - if (get_location(provider, location_state, -1, &loc) < 0) - eprintf(_("Unable to get location from provider.")); + /* Get new location and availability information */ + if (provider->fetch(provider_state, &new_location, &new_available) < 0) + eprintf(_("Unable to get location from provider.")); + if (new_available < location_available) { + weprintf(_("Location is temporarily unavailable; using previous" + " location until it becomes available...")); + location_available = 0; + return; + } - if (!location_is_valid(&loc)) + /* Store and announce new location */ + if (new_available > location_available || + !exact_eq(new_location.latitude, location.latitude) || + !exact_eq(new_location.longitude, location.longitude)) { + location_available = 1; + location = new_location; + print_location(&location); + if (!location_is_valid(&location)) eprintf(_("Invalid location returned from provider.")); - - print_location(&loc); } +} +#endif - if (verbose) { - printf(_("Color temperature: %luK\n"), interp.temperature); - printf(_("Brightness: %.2f\n"), interp.brightness); - } - /* Continuously adjust colour temperature */ - for (;;) { - enum period period; - double day_level; - struct colour_setting target_interp; - int delay, loc_fd; +/** + * Loop for `PROGRAM_MODE_CONTINUAL` + */ +static void +run_continual_mode(void) +{ + enum period period, prev_period = PERIOD_NONE; + double day_level = FNAN, prev_day_level = FNAN; + int disabled = 0, prev_disabled = !disabled; + int done = 0; + struct colour_setting colour; + struct colour_setting target_colour, prev_target_colour; + struct colour_setting fade_start_colour; + unsigned int fade_length = 0; + unsigned int fade_time = 0; + unsigned int delay; + double fade_progress, eased_fade_progress; +#ifndef WINDOWS + int location_fd; +#endif - /* Check to see if disable signal was caught */ + prev_target_colour = COLOUR_SETTING_NEUTRAL; + colour = COLOUR_SETTING_NEUTRAL; + + for (;;) { + /* Act on signals */ if (disable && !done) { - disabled = !disabled; + disabled ^= 1; disable = 0; } - - /* Check to see if exit signal was caught */ if (exiting) { - if (done) + if (done) /* TODO also if already disabled (fade complete) */ break; /* On second signal stop the ongoing fade */ done = 1; disabled = 1; exiting = 0; } - - /* Print status change */ if (verbose && disabled != prev_disabled) printf(_("Status: %s\n"), disabled ? _("Disabled") : _("Enabled")); - prev_disabled = disabled; - get_colour_settings(&loc, &target_interp, &period, &day_level); - + /* Get dayness level and corresponding colour settings */ if (disabled) { period = PERIOD_NONE; - target_interp = COLOUR_SETTING_NEUTRAL; + target_colour = COLOUR_SETTING_NEUTRAL; + } else { + get_colour_settings(&target_colour, &period, &day_level); } - - if (done) - period = PERIOD_NONE; - - /* Print period if it changed during this update, - * or if we are in the transition period. In transition we - * print the progress, so we always print it in - * that case. */ - if (verbose && (period != prev_period || period == PERIOD_TRANSITION)) + if (verbose && (period != prev_period || !exact_eq(day_level, prev_day_level))) print_period(period, day_level); - - /* Activate hooks if period changed */ if (period != prev_period) run_period_change_hooks(prev_period, period); + prev_period = period; + prev_day_level = day_level; + if (verbose) { + if (prev_target_colour.temperature != target_colour.temperature) + printf(_("Color temperature: %luK\n"), target_colour.temperature); + if (!exact_eq(prev_target_colour.brightness, target_colour.brightness)) + printf(_("Brightness: %.2f\n"), target_colour.brightness); + if (memcmp(prev_target_colour.gamma, target_colour.gamma, sizeof(target_colour.gamma))) { + printf(_("Gamma: %.3f, %.3f, %.3f\n"), + target_colour.gamma[0], target_colour.gamma[1], target_colour.gamma[2]); + } + } - /* Start fade if the parameter differences are too big to apply instantly */ - if (use_fade && colour_setting_diff_is_major(&target_interp, fade_length ? &prev_target_interp : &interp)) { + /* Fade if the parameter differences are too big to apply instantly */ + if (use_fade && colour_setting_diff_is_major(&target_colour, fade_length ? &prev_target_colour : &colour)) { fade_length = FADE_LENGTH; fade_time = 0; - fade_start_interp = interp; + fade_start_colour = colour; } - - /* Handle ongoing fade */ - if (fade_length != 0) { - double frac = ++fade_time / (double)fade_length; - double alpha = CLAMP(0.0, ease_fade(frac), 1.0); - - interpolate_colour_settings(&fade_start_interp, &target_interp, alpha, &interp); - - if (fade_time > fade_length) { + if (fade_length) { + fade_progress = ++fade_time / (double)fade_length; + eased_fade_progress = ease_fade(fade_progress); + interpolate_colour_settings(&fade_start_colour, &target_colour, eased_fade_progress, &colour); + if (fade_time == fade_length) { fade_time = 0; fade_length = 0; } } else { - interp = target_interp; + colour = target_colour; } + prev_target_colour = target_colour; /* Break loop when done and final fade is over */ if (done && fade_length == 0) break; - if (verbose) { - if (prev_target_interp.temperature != target_interp.temperature) - printf(_("Color temperature: %luK\n"), target_interp.temperature); - if (!exact_eq(prev_target_interp.brightness, target_interp.brightness)) - printf(_("Brightness: %.2f\n"), target_interp.brightness); - } - - /* Adjust temperature */ - if (method->apply(method_state, &interp, preserve_gamma) < 0) + /* Adjust temperature and sleep */ + if (method->apply(method_state, &colour, preserve_gamma) < 0) eprintf(_("Temperature adjustment failed.")); - - /* Save period and target colour setting as previous */ - prev_period = period; - prev_target_interp = target_interp; - - /* Sleep length depends on whether a fade is ongoing */ delay = fade_length ? SLEEP_DURATION_SHORT : SLEEP_DURATION; - - /* Update location */ - loc_fd = scheme.type == SOLAR_SCHEME ? provider->get_fd(location_state) : -1; - - if (loc_fd >= 0) { - struct pollfd pollfds[1]; - struct location new_loc; - int r, new_available; - - /* Provider is dynamic */ - pollfds[0].fd = loc_fd; - pollfds[0].events = POLLIN; - r = poll(pollfds, 1, delay); - if (r < 0) { #ifndef WINDOWS - if (errno == EINTR) - continue; + location_fd = scheme.type == SOLAR_SCHEME ? provider->get_fd(provider_state) : -1; + if (location_fd >= 0) + pull_location(delay, location_fd); + else #endif - weprintf("poll:"); - eprintf(_("Unable to get location from provider.")); - } else if (!r) { - continue; - } - - /* Get new location and availability information */ - if (provider->fetch(location_state, &new_loc, &new_available) < 0) - eprintf(_("Unable to get location from provider.")); - - if (!new_available && new_available != location_available) { - weprintf(_("Location is temporarily unavailable; using previous" - " location until it becomes available...")); - } - - if (new_available && - (!exact_eq(new_loc.latitude, loc.latitude) || - !exact_eq(new_loc.longitude, loc.longitude) || - new_available != location_available)) { - loc = new_loc; - print_location(&loc); - } - - location_available = new_available; - - if (!location_is_valid(&loc)) - eprintf(_("Invalid location returned from provider.")); - } else { millisleep(delay); - } + /* SLEEP_DURATION_SHORT is short enough for remaining time after interruption to be ignored */ } - /* Restore saved gamma ramps */ method->restore(method_state); } @@ -403,15 +380,13 @@ int main(int argc, char *argv[]) { struct settings settings; - GAMMA_STATE *method_state = NULL; - LOCATION_STATE *location_state = NULL; - struct location loc = {NAN, NAN}; double day_level; enum period period; struct colour_setting colour; argv0 = argv[0]; + /* Set up localisation */ #ifdef ENABLE_NLS setlocale(LC_CTYPE, ""); setlocale(LC_MESSAGES, ""); @@ -419,63 +394,68 @@ main(int argc, char *argv[]) textdomain(PACKAGE); #endif + /* Get configurations and configure */ load_settings(&settings, argc, argv); - if (scheme.type == SOLAR_SCHEME) - acquire_location_provider(&settings, &location_state); - if (mode != PROGRAM_MODE_PRINT) + if (scheme.type == SOLAR_SCHEME) { + acquire_location_provider(&settings, &provider_state); + provider = settings.provider; + } + if (mode != PROGRAM_MODE_PRINT) { acquire_adjustment_method(&settings, &method_state); + method = settings.method; + } config_ini_free(&settings.config); - switch (mode) { - case PROGRAM_MODE_ONE_SHOT: - case PROGRAM_MODE_PRINT: - case PROGRAM_MODE_RESET: - if (scheme.type == SOLAR_SCHEME) { - weprintf(_("Waiting for current location to become available...")); - - if (get_location(settings.provider, location_state, -1, &loc) < 0) - eprintf(_("Unable to get location from provider.")); - - if (!location_is_valid(&loc)) - exit(1); + /* Set up interprocess communication */ + install_signal_handlers(); - print_location(&loc); - } + /* Get location if required */ + if (scheme.type == SOLAR_SCHEME) { + if (provider->get_fd(provider_state) >= 0) + weprintf(_("Waiting for current location to become available...")); + if (get_location(provider, provider_state, -1, &location) < 0) + eprintf(_("Unable to get location from provider.")); + if (!location_is_valid(&location)) + eprintf(_("Invalid location returned from provider.")); + print_location(&location); + location_available = 1; + } - get_colour_settings(&loc, &colour, &period, &day_level); + /* Get and print colour to set or if continual mode the initial colour */ + get_colour_settings(&colour, &period, &day_level); /* needed in contiual mode for `period` and `day_level` */ + if (mode == PROGRAM_MODE_CONTINUAL) + colour = COLOUR_SETTING_NEUTRAL; + if (verbose || mode == PROGRAM_MODE_PRINT) { + if (scheme.type != STATIC_SCHEME) + print_period(period, day_level); + printf(_("Color temperature: %luK\n"), colour.temperature); + printf(_("Brightness: %.2f\n"), colour.brightness); + printf(_("Gamma: %.3f, %.3f, %.3f\n"), colour.gamma[0], colour.gamma[1], colour.gamma[2]); + } - if (verbose || mode == PROGRAM_MODE_PRINT) { - if (scheme.type != STATIC_SCHEME) - print_period(period, day_level); - printf(_("Color temperature: %luK\n"), colour.temperature); - printf(_("Brightness: %.2f\n"), colour.brightness); - if (mode == PROGRAM_MODE_PRINT) - break; - } + switch (mode) { + case PROGRAM_MODE_PRINT: + break; - if (settings.method->apply(method_state, &colour, settings.preserve_gamma.value) < 0) + case PROGRAM_MODE_ONE_SHOT: + case PROGRAM_MODE_RESET: + if (method->apply(method_state, &colour, preserve_gamma) < 0) eprintf(_("Temperature adjustment failed.")); - -#ifndef WINDOWS - /* In Quartz (OSX) the gamma adjustments will automatically revert when - * the process exits. Therefore, we have to loop until Ctrl+C is received. */ - if (!strcmp(settings.method->name, "quartz")) { + if (method->autoreset) { weprintf(_("Press ctrl-c to stop...")); while (!exiting) pause(); } -#endif break; case PROGRAM_MODE_CONTINUAL: - run_continual_mode(settings.provider, location_state, settings.method, method_state, - settings.use_fade.value, settings.preserve_gamma.value); + run_continual_mode(); break; } if (method_state) - settings.method->free(method_state); - if (location_state) - settings.provider->free(location_state); + method->free(method_state); + if (provider_state) + provider->free(provider_state); return 0; } diff --git a/src/signals.c b/src/signals.c index ca88425..3487d23 100644 --- a/src/signals.c +++ b/src/signals.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée diff --git a/src/util.c b/src/util.c index a562fd2..0c1adc9 100644 --- a/src/util.c +++ b/src/util.c @@ -1,4 +1,5 @@ -/* redshift-ng - Automatically adjust display colour temperature according the Sun +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée @@ -188,43 +189,28 @@ try_path_opendir(const struct env_path *path_spec, const char **path_out, char * #ifndef WINDOWS -int +void pipe_rdnonblock(int pipefds[2]) { int i, flags; - /* Try to use pipe2(2) create O_CLOEXEC pipe, preferably with O_DIRECT */ + /* Try to use pipe2(2) create O_CLOEXEC pipe */ # if defined(__linux__) && !defined(MISSING_PIPE2) - if (!pipe2(pipefds, O_CLOEXEC | O_DIRECT)) { + if (!pipe2(pipefds, O_CLOEXEC)) goto apply_nonblock; - } else if (errno == EINVAL) { - if (!pipe2(pipefds, O_CLOEXEC)) { - goto apply_nonblock; - } else if (errno != ENOSYS) { - weprintf("pipe2 O_CLOEXEC:"); - return -1; - } - } else if (errno != ENOSYS) { - weprintf("pipe2 O_CLOEXEC|O_DIRECT:"); - return -1; - } + else if (errno != ENOSYS) + eprintf("pipe2 O_CLOEXEC:"); # endif - /* Fallback for when pipe2(2) is not available (also indicates O_DIRECT cannot be used) */ - if (pipe(pipefds)) { - weprintf("pipe:"); - return -1; - } + /* Fallback for when pipe2(2) is not available */ + if (pipe(pipefds)) + eprintf("pipe:"); for (i = 0; i < 2; i++) { flags = fcntl(pipefds[i], F_GETFD); - if (flags == -1) { - weprintf("fcntl F_GETFD:"); - goto fail; - } - if (fcntl(pipefds[i], F_SETFD, flags | O_CLOEXEC)) { - weprintf("fcntl F_SETFD +O_CLOEXEC:"); - goto fail; - } + if (flags == -1) + eprintf("fcntl F_GETFD:"); + if (fcntl(pipefds[i], F_SETFD, flags | O_CLOEXEC)) + eprintf("fcntl F_SETFD +O_CLOEXEC:"); } /* Make the read-end non-blocking */ @@ -232,20 +218,9 @@ pipe_rdnonblock(int pipefds[2]) apply_nonblock: # endif flags = fcntl(pipefds[0], F_GETFL); - if (flags == -1) { - weprintf("fcntl F_GETFL:"); - goto fail; - } - if (fcntl(pipefds[0], F_SETFL, flags | O_NONBLOCK)) { - weprintf("fcntl F_SETFL +O_NONBLOCK:"); - goto fail; - } - - return 0; - -fail: - close(pipefds[0]); - close(pipefds[1]); - return -1; + if (flags == -1) + eprintf("fcntl F_GETFL:"); + if (fcntl(pipefds[0], F_SETFL, flags | O_NONBLOCK)) + eprintf("fcntl F_SETFL +O_NONBLOCK:"); } #endif -- cgit v1.2.3-70-g09d2