aboutsummaryrefslogtreecommitdiffstats
path: root/redshift/common.h
diff options
context:
space:
mode:
Diffstat (limited to 'redshift/common.h')
-rw-r--r--redshift/common.h1690
1 files changed, 1690 insertions, 0 deletions
diff --git a/redshift/common.h b/redshift/common.h
new file mode 100644
index 0000000..d9a268d
--- /dev/null
+++ b/redshift/common.h
@@ -0,0 +1,1690 @@
+/*-
+ * redshift-ng - Automatically adjust display colour temperature according the Sun
+ *
+ * Copyright (c) 2009-2018 Jon Lund Steffensen <jonlst@gmail.com>
+ * Copyright (c) 2014-2016, 2025 Mattias Andrée <m@maandree.se>
+ *
+ * redshift-ng is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * redshift-ng is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with redshift-ng. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef WINDOWS
+# if defined(__WIN32__) || defined(_WIN32)
+# define WINDOWS
+# endif
+#endif
+
+
+#include "arg.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef _POSIX_TIMERS
+# include <sys/time.h>
+#endif
+#ifdef WINDOWS
+# include <windows.h>
+# define localtime_r(T, TM) localtime_s((TM), (T))
+# define pause() millisleep(100U)
+#else
+# include <sys/file.h>
+# include <poll.h>
+# include <pwd.h>
+# include <time.h>
+#endif
+
+#include <libgamma.h>
+#include <libgeome.h>
+#include <libred.h>
+
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+#else
+# define gettext(s) s
+#endif
+
+/**
+ * List for translation, and translate in place
+ *
+ * @param s:string-literal Translatable string
+ * @return :const char * Translation of `s`
+ */
+#define _(s) gettext(s)
+
+/**
+ * List for translation without translating in place
+ *
+ * @param s:string-literal Translatable string
+ * @return :string-literal `s` as is
+ */
+#define N_(s) s
+
+
+#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 */
+# pragma clang diagnostic ignored "-Wcovered-switch-default" /* stupid warning: not necessary true */
+#elif defined(__GNUC__)
+# pragma GCC diagnostic ignored "-Wunsuffixed-float-constants" /* stupid warning */
+# pragma GCC diagnostic ignored "-Wpadded" /* only relevant for library headers */
+#endif
+
+
+#if defined(__GNUC__)
+# define GCC_ONLY(...) __VA_ARGS__
+#else
+# define GCC_ONLY(...)
+#endif
+
+
+/**
+ * Truncate a value into a closed range
+ *
+ * @param LO The lower bound
+ * @param X The value to truncated
+ * @param UP The upper bound
+ * @return `X` truncated such that it is at least `LO` and at most `UP`
+ */
+#define CLAMP(LO, X, UP) (((LO) > (X)) ? (LO) : (((X) < (UP)) ? (X) : (UP)))
+
+/**
+ * Check whether a value is within a closed range
+ *
+ * @param LO The lower bound
+ * @param X The value to check
+ * @param UP The upper bound
+ * @return :int 1 if `X` is within [`LO`, `UP`], 0 otherwise
+ */
+#define WITHIN(LO, X, UP) ((LO) <= (X) && (X) <= (UP))
+
+/**
+ * Check whether a value is within a bounded, open range
+ *
+ * @param LO The lower bound
+ * @param X The value to check
+ * @param UP The upper bound
+ * @return :int 1 if `X` is within (`LO`, `UP`), 0 otherwise
+ */
+#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 */
+
+/**
+ * Get the number of elements in an array
+ *
+ * @param ARR The array, must not be a pointer
+ * @return :size_t The number of elements in `ARR` (constant
+ * expression, unless its size is dynamic)
+ */
+#define ELEMSOF(ARR) (sizeof(ARR) / (sizeof(*(ARR))))
+
+/**
+ * Get the smallest of two numerical values
+ *
+ * @param A One of the values
+ * @param B The other value
+ * @return The smallest of `A` and `B`
+ */
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+
+/**
+ * Get the largest of two numerical values
+ *
+ * @param A One of the values
+ * @param B The other value
+ * @return The largest of `A` and `B`
+ */
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+
+
+/**
+ * Symbol used to delimit paths in environment
+ * variables listing multiple paths
+ */
+#ifdef WINDOWS
+# define PATH_DELIMITER ';'
+#else
+# define PATH_DELIMITER ':'
+#endif
+
+/**
+ * The number of seconds in a day
+ */
+#define ONE_DAY ((time_t)(24L * 60L * 60L))
+
+
+/**
+ * Minimum valid latitude
+ */
+#define MIN_LATITUDE -90.0
+
+/**
+ * Maximum valid latitude
+ */
+#define MAX_LATITUDE 90.0
+
+/**
+ * Minimum valid longitude
+ */
+#define MIN_LONGITUDE -180.0
+
+/**
+ * Maximum valid longitude
+ */
+#define MAX_LONGITUDE 180.0
+
+/**
+ * Minimum allowed colour temperature
+ */
+#define MIN_TEMPERATURE ((unsigned long int)LIBRED_LOWEST_TEMPERATURE)
+
+/**
+ * Maximum allowed colour temperature
+ */
+#define MAX_TEMPERATURE ULONG_MAX
+
+/**
+ * Minimum allowed whitepoint brightness
+ */
+#define MIN_BRIGHTNESS 0.1
+
+/**
+ * Maximum allowed whitepoint brightness
+ */
+#define MAX_BRIGHTNESS 1.0
+
+/**
+ * Minimum allowed gamma
+ */
+#define MIN_GAMMA 0.1
+
+/**
+ * Maximum allowed gamma
+ */
+#define MAX_GAMMA 10.0
+
+
+/**
+ * The colour temperature corresponding to no effect
+ */
+#define NEUTRAL_TEMPERATURE 6500UL
+
+/**
+ * The whitepoint brightness corresponding to
+ * full brightness (no effect)
+ */
+#define NEUTRAL_BRIGHTNESS 1.0
+
+/**
+ * The gamma corresponding to no effect (linear output level curve)
+ */
+#define NEUTRAL_GAMMA 1.0
+
+
+/**
+ * Default daytime colour temperature
+ */
+#define DEFAULT_DAY_TEMPERATURE 6500UL
+
+/**
+ * Default night colour temperature
+ */
+#define DEFAULT_NIGHT_TEMPERATURE 4500UL
+
+/**
+ * Default daytime whitepoint brightness level
+ */
+#define DEFAULT_DAY_BRIGHTNESS NEUTRAL_BRIGHTNESS
+
+/**
+ * Default night whitepoint brightness level
+ */
+#define DEFAULT_NIGHT_BRIGHTNESS NEUTRAL_BRIGHTNESS
+
+/**
+ * Default daytime gamma value
+ */
+#define DEFAULT_DAY_GAMMA NEUTRAL_GAMMA
+
+/**
+ * Default night gamma value
+ */
+#define DEFAULT_NIGHT_GAMMA NEUTRAL_GAMMA
+
+/**
+ * The solar elevation, in degrees, that marks the
+ * threshold to daytime
+ */
+#define DEFAULT_HIGH_ELEVATION 3.0
+
+/**
+ * The solar elevation, in degrees, that marks the
+ * threshold to night
+ */
+#define DEFAULT_LOW_ELEVATION LIBRED_SOLAR_ELEVATION_CIVIL_DUSK_DAWN
+
+
+/**
+ * Initialiser for `struct colour_setting`
+ *
+ * Sets all values to their neutral values (no effects applied)
+ */
+#define COLOUR_SETTING_NEUTRAL\
+ ((struct colour_setting){\
+ NEUTRAL_TEMPERATURE,\
+ NEUTRAL_BRIGHTNESS,\
+ {NEUTRAL_GAMMA, NEUTRAL_GAMMA, NEUTRAL_GAMMA}\
+ })
+
+
+/**
+ * State of an adjustment method
+ *
+ * Each method has their own definition of this structure
+ */
+typedef struct gamma_state GAMMA_STATE;
+
+/**
+ * State of a location provider
+ *
+ * Each provider has their own definition of this structure
+ */
+typedef struct location_state LOCATION_STATE;
+
+
+/**
+ * The time of the day: day, night, or twilight
+ */
+enum period {
+ /**
+ * None applied
+ */
+ PERIOD_NONE,
+
+ /**
+ * Full daytime
+ */
+ PERIOD_DAYTIME,
+
+ /**
+ * Full nighttime
+ */
+ PERIOD_NIGHT,
+
+ /**
+ * Transitioning between day and night
+ * (either direction) (twilight)
+ */
+ PERIOD_TRANSITION
+};
+
+
+/**
+ * The mode the program is running in
+ */
+enum program_mode {
+ /**
+ * Run in foreground continually update temperature
+ */
+ PROGRAM_MODE_CONTINUAL,
+
+ /**
+ * Update temperature once then exit
+ */
+ PROGRAM_MODE_ONE_SHOT,
+
+ /**
+ * Update temperature once and reset when killed
+ */
+ PROGRAM_MODE_UNTIL_DEATH,
+
+ /**
+ * Print setting and exit
+ */
+ PROGRAM_MODE_PRINT,
+
+ /**
+ * Remove effects and exit
+ */
+ PROGRAM_MODE_RESET
+};
+
+
+/**
+ * By what the effects of the application change
+ */
+enum scheme_type {
+ /**
+ * Effects are dependent on the Sun's elevation
+ */
+ SOLAR_SCHEME,
+
+ /**
+ * Effects are dependent on the wall clock time
+ */
+ CLOCK_SCHEME,
+
+ /**
+ * Effects do not change
+ */
+ STATIC_SCHEME
+};
+
+/**
+ * Scheme has not been specified
+ */
+#define UNSPECIFIED_SCHEME STATIC_SCHEME
+
+
+/**
+ * The sources where an setting was been loaded from
+ *
+ * Higher valued sources have higher priority
+ *
+ * This is a bitmask `enum`
+ */
+enum setting_source {
+ /**
+ * No setting loaded, default value set
+ */
+ SETTING_DEFAULT = 0x00,
+
+ /**
+ * Setting loaded from configuration file
+ */
+ SETTING_CONFIGFILE = 0x01,
+
+ /**
+ * Setting loaded from command line arguments
+ */
+ SETTING_CMDLINE = 0x02
+};
+
+
+/**
+ * SIGUSR2 signal values as bitmask values
+ */
+enum signals {
+ /**
+ * Block SIGUSR2 until all signals have been processed
+ */
+ SIGNAL_ORDER_BARRIER = 1 << 0,
+
+ /**
+ * Disable the effects of redshift
+ */
+ SIGNAL_DISABLE = 1 << 1,
+
+ /**
+ * Enable the effects of redshift
+ */
+ SIGNAL_ENABLE = 1 << 2,
+
+ /**
+ * Reload the configuration file
+ *
+ * Settings from the command line will be overriden
+ */
+ SIGNAL_RELOAD = 1 << 3,
+
+ /**
+ * Execute into the currently installed version of redshift
+ */
+ SIGNAL_REEXEC = 1 << 4,
+
+ /**
+ * Set the "fade" setting to off
+ */
+ SIGNAL_USE_FADE_OFF = 1 << 5,
+
+ /**
+ * Set the "fade" setting to on
+ */
+ SIGNAL_USE_FADE_ON = 1 << 6,
+
+ /**
+ * Set the "preserve-gamma" setting to off
+ */
+ SIGNAL_PRESERVE_GAMMA_OFF = 1 << 7,
+
+ /**
+ * Set the "preserve-gamma" setting to on
+ */
+ SIGNAL_PRESERVE_GAMMA_ON = 1 << 8,
+
+ /**
+ * Exit the process without removing the its effects
+ *
+ * If the used adjustment method does not support
+ * leaving the effects, they will be removed
+ */
+ SIGNAL_EXIT_WITHOUT_RESET = 1 << 9,
+
+ /**
+ * Do not terminate redshift the standard output
+ * and standard error are closed
+ */
+ SIGNAL_IGNORE_SIGPIPE = 1 << 10,
+
+ /**
+ * Enable verbose mode
+ */
+ SIGNAL_VERBOSE_ON = 1 << 11,
+
+ /**
+ * Disable verbose mode
+ *
+ * Ignore if started in verbose mode (-v option)
+ */
+ SIGNAL_VERBOSE_OFF = 1 << 12
+};
+
+
+/**
+ * Specification for a path that consists of a two parts:
+ * the first being defined by the environment, and the
+ * seocnd being a static string
+ */
+struct env_path {
+ /**
+ * Whether the environment variable referenced by `.prefix`
+ * should be split at each colon (:) into multiple paths to
+ * test
+ *
+ * On Windows semicolon (;) is used instead of colon
+ */
+ int multidir_env;
+
+ /**
+ * Environment variable to use as the first part of the path
+ *
+ * `NULL` if the user's home directory should be used
+ *
+ * The empty string if `.suffix` should be used as is
+ */
+ const char *prefix_env;
+
+ /**
+ * The second part of the path
+ */
+ const char *suffix;
+};
+
+
+/**
+ * Geographical location, using GPS coordinates
+ */
+struct location {
+ /**
+ * Degrees north of the equator
+ */
+ double latitude;
+
+ /**
+ * Degrees east of the prime meridian
+ */
+ double longitude;
+};
+
+
+/**
+ * Colour setting to apply
+ */
+struct colour_setting {
+ /**
+ * Colour temperature, in Kelvin
+ */
+ unsigned long int temperature;
+
+ /**
+ * Whitepoint brightness level
+ */
+ double brightness;
+
+ /**
+ * Gamma correct, for the each RGB channel
+ * in the order: red, green, and blue
+ */
+ double gamma[3];
+};
+
+
+/**
+ * Linked list of time periods for `CLOCK_SCHEME`
+ */
+struct time_period {
+ /**
+ * The number of seconds after midnight the period starts
+ */
+ time_t start;
+
+ /**
+ * 1 if at daytime at the the time `.start`,
+ * 0 if at nighttime at the the time `.start`
+ */
+ double day_level;
+
+ /**
+ * `.next->day_level - .day_level` dividied by
+ * the duration of the period, in seconds
+ *
+ * `.day_level` is added to the number of seconds
+ * elapsed since `.start` multiplied by this number
+ * to get the dayness level at that time
+ */
+ double diff_over_duration;
+
+ /**
+ * The following time period
+ */
+ const struct time_period *next;
+};
+
+
+/**
+ * Dayness level scheme
+ */
+union scheme {
+ /**
+ * The scheme type
+ *
+ * If `STATIC_SCHEME`, the union contains no scheme data
+ */
+ enum scheme_type type;
+
+ /**
+ * Used if `.type == SOLAR_SCHEME`
+ */
+ struct solar_scheme {
+ /**
+ * `SOLAR_SCHEME`
+ */
+ enum scheme_type type;
+
+ /**
+ * The lowest solar elevation of daytime
+ */
+ double high;
+
+ /**
+ * The highest solar elevation of nighttime
+ */
+ double low;
+
+ /**
+ * `.high - .low`
+ */
+ double range;
+ } elevation;
+
+ /**
+ * Used if `.type == CLOCK_SCHEME`
+ */
+ struct clock_scheme {
+ /**
+ * `CLOCK_SCHEME`
+ */
+ enum scheme_type type;
+
+ /**
+ * Circularly linked list of time periods
+ *
+ * The application will update this to always
+ * point to the current time period
+ */
+ const struct time_period *periods;
+
+ /**
+ * The memory allocation for the nodes in `.periods`
+ */
+ struct time_period periods_array[4];
+ } time;
+};
+
+
+struct config_ini_setting {
+ struct config_ini_setting *next;
+ char *name;
+ char *value;
+};
+
+
+struct config_ini_section {
+ struct config_ini_section *next;
+ char *name;
+ struct config_ini_setting *settings;
+};
+
+
+struct config_ini_state {
+ struct config_ini_section *sections;
+};
+
+
+/**
+ * `int` valued setting (used for booleans) with setting source
+ */
+struct setting_i {
+ enum setting_source source; /**< Setting source */
+ int value; /**< Setting value */
+};
+
+/**
+ * `unsigned long int` valued setting with setting source
+ */
+struct setting_lu {
+ enum setting_source source; /**< Setting source */
+ unsigned long int value; /**< Setting value */
+};
+
+/**
+ * `double` valued setting with setting source
+ */
+struct setting_f {
+ enum setting_source source; /**< Setting source */
+ double value; /**< Setting value */
+};
+
+/**
+ * `double[3]` valued setting with setting source
+ */
+struct setting_f3 {
+ enum setting_source source; /**< Setting source */
+ double value[3]; /**< Setting values */
+};
+
+/**
+ * `time_t` valued setting with setting source
+ */
+struct setting_time {
+ enum setting_source source; /**< Setting source */
+ time_t value; /**< Setting value */
+};
+
+/**
+ * `char *` valued setting with setting source
+ */
+struct setting_str {
+ enum setting_source source; /**< Setting source */
+ char *value; /**< Setting value */
+};
+
+/**
+ * Intermediate settings representation of colour settings
+ * (for a non-transitional period) with settings sources
+ * used for determining whether settings from the configuration
+ * file (which is parsed after the command line) applied or are
+ * specified multiple times in the configuration file
+ */
+struct setting_colour {
+ /**
+ * Colour temperature, in Kelvin
+ *
+ * Set by the "-t" and "-o" options and the "temperature"
+ * ("temp") settings, optionally suffixed "-day" if
+ * a daytime setting or "-night" if a nighttime setting
+ */
+ struct setting_lu temperature;
+
+ /**
+ * Whitepoint brightness level, as a [0, 1] value
+ *
+ * Set by the "-b" option and the "brightness" settings,
+ * optionally suffixed "-day" if a daytime setting or
+ * "-night" if a nighttime setting
+ */
+ struct setting_f brightness;
+
+ /**
+ * Gamma values, in the order red, green, blue
+ *
+ * Set by the "-g" option and the "gamma" settings,
+ * optionally suffixed "-day" if a daytime setting or
+ * "-night" if a nighttime setting
+ */
+ struct setting_f3 gamma;
+};
+
+/**
+ * Intermediate settings representation with settings sources
+ * used for determining whether settings from the configuration
+ * file (which is parsed after the command line) applied or
+ * are specified multiple times in the configuration file
+ *
+ * Settings without a source can only be specified in the
+ * command line
+ */
+struct settings {
+ /**
+ * The path to the configuration, `NULL` if unspecified
+ * (search default paths)
+ *
+ * This represents the "-c" option
+ */
+ const char *config_file;
+
+ /**
+ * Scheme type to use as according the the command line
+ * options, `UNSPECIFIED_SCHEME` if not unspecified
+ */
+ enum scheme_type scheme_type;
+
+ /**
+ * Whether the program should, if ran in one-shot mode,
+ * pause after applying the effect and reset them when
+ * killed
+ *
+ * This represents the "-d" option
+ */
+ int until_death;
+
+ /**
+ * The path to the hook file or hook directory, `NULL`
+ * if unspecified (search default paths)
+ *
+ * This represents the "hook" setting and "-H" option
+ */
+ struct setting_str hook_file;
+
+ /**
+ * Whether the program should preserve preapplied
+ * colour calibrations
+ *
+ * This represents the "preserve-gamma" setting and
+ * "-P" (off) and "+P" (on) options
+ */
+ struct setting_i preserve_gamma;
+
+ /**
+ * Whether smooth transitions shall be applied when
+ * a large change in colour settings occurs
+ *
+ * This represents the "fade" setting (or the deprecated
+ * alias "transition") and "-r" (off) and "+r" (on) options
+ */
+ struct setting_i use_fade;
+
+ /**
+ * Whether the program should start in disabled mode
+ *
+ * This represents the "start-disabled" setting and
+ * "-D" (off) and "+D" (on) options
+ */
+ struct setting_i disabled;
+
+ /**
+ * The colour settings to apply at daytime
+ */
+ struct setting_colour day;
+
+ /**
+ * The colour settings to apply at nighttime
+ */
+ struct setting_colour night;
+
+ /**
+ * The lowest solar elevation, in degrees, at daytime,
+ * when using solar scheme
+ *
+ * This represents the "elevation-high" setting and the
+ * left value of the -e option
+ */
+ struct setting_f elevation_high;
+
+ /**
+ * The highest solar elevation, in degrees, at nighttime,
+ * when using solar scheme
+ *
+ * This represents the "elevation-low" setting
+ *
+ * This represents the "elevation-low" setting and the
+ * right value of the -e option
+ */
+ struct setting_f elevation_low;
+
+ /**
+ * The wall-clock time that marks the end of nighttime,
+ * when using clock scheme
+ *
+ * This represents the left value of the "dawn-time" setting
+ */
+ struct setting_time dawn_start; /* TODO no cmdline option */
+
+ /**
+ * The wall-clock time that marks the start of daytime,
+ * when using clock scheme
+ *
+ * This represents the right value of the "dawn-time" setting
+ */
+ struct setting_time dawn_end; /* TODO no cmdline option */
+
+ /**
+ * The wall-clock time that marks the end of daytime,
+ * when using clock scheme
+ *
+ * This represents the left value of the "dusk-time" setting
+ */
+ struct setting_time dusk_start; /* TODO no cmdline option */
+
+ /**
+ * The wall-clock time that marks the start of nighttime,
+ * when using clock scheme
+ *
+ * This represents the right value of the "dusk-time" setting
+ */
+ struct setting_time dusk_end; /* TODO no cmdline option */
+
+ /* Selected gamma method */
+ const struct gamma_method *method;
+ /* Arguments for gamma method */
+ char *method_args;
+
+ /* Selected location provider */
+ const struct location_provider *provider;
+ /* Arguments for location provider */
+ char *provider_args;
+
+ struct config_ini_state config;
+};
+
+
+/**
+ * Adjustment method information and interface
+ */
+struct gamma_method {
+ /**
+ * The name of the adjustment method
+ */
+ const char *name;
+
+ /**
+ * 1 if the method should be tried if none is explicitly chosen,
+ * 0 otherwise
+ */
+ int autostart;
+
+ /**
+ * 1 if the method automatically resets the adjustments when disconnected,
+ * 0 otherwise
+ */
+ int autoreset;
+
+ /**
+ * Check if the adjustment method is available in the used backend
+ *
+ * @return 1 if the adjustment method is available, 0 otherwise
+ */
+ int (*is_available)(void);
+
+ /**
+ * Create an initialised state object
+ *
+ * @param state_out Output parameter for the state object
+ * @return 0 on success, -1 on failure
+ *
+ * `*state_out` is set (potentially to `NULL`) on failure
+ */
+ int (*create)(GAMMA_STATE **state_out);
+
+ /**
+ * Configure the adjustment method
+ *
+ * @param state State object for the adjustment method
+ * @param key Option to configure
+ * @param value Option value to set
+ * @return 0 on success, -1 on failure
+ */
+ int (*set_option)(GAMMA_STATE *state, const char *key, const char *value);
+
+ /**
+ * Print help on options for the adjustment method
+ */
+ void (*print_help)(void);
+
+ /**
+ * Finalise option-dependent initialisation and connections
+ *
+ * @param state State object for the adjustment method
+ * @return 0 on success, -1 on failure
+ */
+ int (*start)(GAMMA_STATE *state);
+
+ /**
+ * Apply colour settings
+ *
+ * @param state State object for the adjustment method
+ * @param settings The colour settings to apply
+ * @param preserve Whether currently applied adjustments (assumed
+ * to be colour calibration) shall remain applied
+ * @return 0 on success, -1 on failure
+ */
+ int (*apply)(GAMMA_STATE *state, const struct colour_setting *setting, int preserve);
+
+ /**
+ * Restore the adjustments to the `.state` before start was called
+ *
+ * @param state State object for the adjustment method
+ */
+ void (*restore)(GAMMA_STATE *state);
+
+ /**
+ * Close connections and deallocate all state resources
+ *
+ * @param state The state to terminate
+ *
+ * The pointer `state` will become invalid
+ */
+ void (*free)(GAMMA_STATE *state);
+};
+
+/**
+ * Initialiser for `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, AUTORESET, PREFIX)\
+ {\
+ .name = (NAME),\
+ .autostart = (AUTOSTART),\
+ .autoreset = (AUTORESET),\
+ .is_available = &PREFIX##_is_available,\
+ .create = &PREFIX##_create,\
+ .set_option = &PREFIX##_set_option,\
+ .print_help = &PREFIX##_print_help,\
+ .start = &PREFIX##_start,\
+ .free = &PREFIX##_free,\
+ .restore = &PREFIX##_restore,\
+ .apply = &PREFIX##_apply\
+ }
+
+
+/**
+ * Location provider information and interface
+ */
+struct location_provider {
+ /**
+ * The name of the location provider
+ */
+ const char *name;
+
+ /**
+ * Create an initialised state object
+ *
+ * @param state_out Output parameter for the state object
+ * @return 0 on success, -1 on failure
+ *
+ * `*state_out` is set (potentially to `NULL`) on failure
+ */
+ int (*create)(LOCATION_STATE **state_out);
+
+ /**
+ * Configure the location provider
+ *
+ * @param state State object for the location provider
+ * @param key Option to configure
+ * @param value Option value to set
+ * @return 0 on success, -1 on failure
+ */
+ int (*set_option)(LOCATION_STATE *state, const char *key, const char *value);
+
+ /**
+ * Print help on options for the location provider
+ */
+ void (*print_help)(void);
+
+ /**
+ * Finalise option-dependent initialisation and connections
+ *
+ * @param state State object for the location provider
+ * @return 0 on success, -1 on failure
+ */
+ int (*start)(LOCATION_STATE *state);
+
+ /**
+ * Get the file descriptor used by the location provider
+ *
+ * The application may use it for detecting when there
+ * is data available for `.handle` to act upon
+ *
+ * @param state State object for the location provider
+ * @return The file descriptor used by location provider, -1 if none
+ */
+ int (*get_fd)(LOCATION_STATE *state);
+
+ /**
+ * Get the current location
+ *
+ * This function shall only be caused if `.get_fd` returns -1
+ * or the file descriptor it returns has data available on it
+ * as indicated by input polling, otherwise `*location` and
+ * `*available` will not be set
+ *
+ * @param state State object for the location provider
+ * @param location_out Output parameter for the current location
+ * @param available_out Output parameter for whether the location provider
+ * is currently available
+ * @return 0 on success, -1 on unrecoverable failure
+ */
+ int (*fetch)(LOCATION_STATE *state, struct location *location, int *available);
+
+ /**
+ * Close connections and deallocate all state resources
+ *
+ * @param state The state to terminate
+ *
+ * The pointer `state` will become invalid
+ */
+ void (*free)(LOCATION_STATE *state);
+};
+
+/**
+ * Initialiser for `struct location_provider`
+ *
+ * @param NAME:const char * Value for `.name`
+ * @param PREFIX:identifier The text, sans terminal underscore (_), prefixed to the
+ * names of each function implementing the location provider
+ */
+#define LOCATION_PROVIDER_INIT(NAME, PREFIX)\
+ {\
+ .name = (NAME),\
+ .create = &PREFIX##_create,\
+ .set_option = &PREFIX##_set_option,\
+ .print_help = &PREFIX##_print_help,\
+ .start = &PREFIX##_start,\
+ .get_fd = &PREFIX##_get_fd,\
+ .fetch = &PREFIX##_fetch,\
+ .free = &PREFIX##_free\
+ }
+
+
+/**
+ * `NULL` terminated list of adjustment methods
+ */
+extern const struct gamma_method *gamma_methods[];
+
+/**
+ * `NULL` terminated list of location providers
+ */
+extern const struct location_provider *location_providers[];
+
+/**
+ * Set to 1 once the process has received a signal to terminate
+ */
+extern volatile sig_atomic_t exiting;
+
+/**
+ * Set to 1 once the process has received a signal to remove its effect
+ */
+extern volatile sig_atomic_t disable;
+
+/**
+ * Bitwise or OR of received SIGUSR2 signal values
+ */
+extern volatile enum signals signals;
+
+/**
+ * The colour settings applied at daytime
+ */
+extern struct colour_setting day_settings;
+
+/**
+ * The colour settings applied at nighttime
+ */
+extern struct colour_setting night_settings;
+
+/**
+ * The colour settings applied at nighttime
+ */
+extern union scheme scheme;
+
+/**
+ * The mode the application is running in
+ */
+extern enum program_mode mode;
+
+/**
+ * Whether initially applied adjustments (assumed
+ * to be colour calibration) shall remain applied
+ */
+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;
+
+/**
+ * The path to the hook file or hook directory, `NULL`
+ * if unspecified (search default paths)
+ */
+extern char *hook_file;
+
+
+/* backend-direct.c */
+
+/**
+ * Create an initialised state object for direct gamma adjustments
+ *
+ * @param state_out Output parameter for the state object
+ * @param method libgamma constant for the adjustment method
+ * @param method_name redshift's name for the adjustment method
+ * @return 0 on success, -1 on failure
+ *
+ * `*state_out` is set (potentially to `NULL`) on failure
+ */
+int direct_create(GAMMA_STATE **state_out, int method, const char *method_name);
+
+/**
+ * Print help on options for the adjustment method using direct gamma adjustments
+ *
+ * @param method libgamma constant for the adjustment method
+ */
+void direct_print_help(int method);
+
+/**
+ * Configure the adjustment method using direct gamma adjustments
+ *
+ * @param state State object for the adjustment method
+ * @param key Option to configure
+ * @param value Option value to set
+ * @return 0 on success, -1 on failure
+ */
+int direct_set_option(GAMMA_STATE *state, const char *key, const char *value);
+
+/**
+ * Select site to apply adjustments to using direct gamma adjustments
+ *
+ * @param state State object for the adjustment method
+ * @param key Option to configure
+ * @param value Option value to set
+ * @return 0 on success, -1 on failure
+ */
+int direct_set_site(GAMMA_STATE *state, const char *key, const char *value);
+
+/**
+ * Select partitions to apply adjustments to using direct gamma adjustments
+ *
+ * @param state State object for the adjustment method
+ * @param key Option to configure
+ * @param value Option value to set
+ * @return 0 on success, -1 on failure
+ */
+int direct_set_partitions(GAMMA_STATE *state, const char *key, const char *value);
+
+/**
+ * Select CRTCs to apply adjustments to using direct gamma adjustments
+ *
+ * @param state State object for the adjustment method
+ * @param key Option to configure
+ * @param value Option value to set
+ * @return 0 on success, -1 on failure
+ */
+int direct_set_crtcs(GAMMA_STATE *state, const char *key, const char *value);
+
+/**
+ * Select EDIDs of outputs to apply adjustments to using direct gamma adjustments
+ *
+ * @param state State object for the adjustment method
+ * @param key Option to configure
+ * @param value Option value to set
+ * @return 0 on success, -1 on failure
+ */
+int direct_set_edids(GAMMA_STATE *state, const char *key, const char *value);
+
+/**
+ * Finalise option-dependent initialisation and connections
+ * for direct gamma adjustments
+ *
+ * @param state State object for the adjustment method
+ * @return 0 on success, -1 on failure
+ */
+int direct_start(GAMMA_STATE *state);
+
+/**
+ * Apply colour settings using direct gamma adjustments
+ *
+ * @param state State object for the adjustment method
+ * @param settings The colour settings to apply
+ * @param preserve Whether currently applied adjustments (assumed
+ * to be colour calibration) shall remain applied
+ * @return 0 on success, -1 on failure
+ */
+int direct_apply(GAMMA_STATE *state, const struct colour_setting *setting, int preserve);
+
+/**
+ * Restore the adjustments to the `.state` before start was called
+ * using direct gamma adjustments
+ *
+ * @param state State object for the adjustment method
+ */
+void direct_restore(GAMMA_STATE *state);
+
+/**
+ * Close connections and deallocate all state resources
+ * for direct gamma adjustments
+ *
+ * @param state The state to terminate
+ *
+ * The pointer `state` will become invalid
+ */
+void direct_free(GAMMA_STATE *state);
+
+
+/* colour.c */
+
+/**
+ * Interpolate between two colour settings
+ *
+ * @param a The first colour setting, used wholly when `t` is 0
+ * @param b The second colour setting, used wholly when `t` is 1
+ * @param t The degree to which `second` second be applied
+ * @param result Output parameter for `(1 - t) * a + t * b`
+ */
+void interpolate_colour_settings(const struct colour_setting *a, const struct colour_setting *b,
+ double t, struct colour_setting *result);
+
+/**
+ * Check whether the differences between two colours settings
+ * are large enough to warrant fading between the two
+ *
+ * @param a The first colour setting
+ * @param b The second colour setting
+ * @return 1 if the difference between `a` and `b` is large, 0 otherwise
+ */
+GCC_ONLY(__attribute__((__pure__)))
+int colour_setting_diff_is_major(const struct colour_setting *a, const struct colour_setting *b);
+
+#define LIST_RAMPS_STOP_VALUE_TYPES(X, D)\
+ X(u8, ramps8, uint8_t, UINT8_MAX, 8) D \
+ X(u16, ramps16, uint16_t, UINT16_MAX, 16) D\
+ X(u32, ramps32, uint32_t, UINT32_MAX, 32) D\
+ X(u64, ramps64, uint64_t, UINT64_MAX, 64) D\
+ X(float, rampsf, float, 1, -1) D\
+ X(double, rampsd, double, 1, -2)
+
+#define X(SUFFIX, RAMPS, TYPE, MAX, DEPTH)\
+ /**
+ * Fill the gamma ramps
+ *
+ * @param gamma_r The gamma ramp for the red channel
+ * @param gamma_g The gamma ramp for the green channel
+ * @param gamma_b The gamma ramp for the blue channel
+ * @param saved_r Saved gamma ramp with calibrations for
+ * the red channel to preserve, or `NULL`
+ * @param saved_g Saved gamma ramp with calibrations for
+ * the green channel to preserve, or `NULL`
+ * @param saved_b Saved gamma ramp with calibrations for
+ * the blue channel to preserve, or `NULL`
+ * @param size_r The number of stops in `gamma_r`
+ * @param size_g The number of stops in `gamma_g`
+ * @param size_b The number of stops in `gamma_b`
+ * @param settings The colour settings to apply (temperature, brightness, gamma)
+ */\
+ void fill_ramps_##SUFFIX(TYPE *gamma_r, TYPE *gamma_g, TYPE *gamma_b,\
+ const TYPE *saved_r, const TYPE *saved_g, const TYPE *saved_b,\
+ size_t size_r, size_t size_g, size_t size_b,\
+ const struct colour_setting *setting)
+LIST_RAMPS_STOP_VALUE_TYPES(X, ;);
+#undef X
+
+
+/* config-ini.c */
+
+/**
+ * Load the configuration file
+ *
+ * @param state Output parameter for the configurations
+ * @param path The path to the configuration file, or `NULL` if the
+ * application should look for it in the default paths
+ * @param pathbuf_out Output parameter for the memory allocated for the
+ * return value
+ * @return The path to the loaded configuration file, `NULL` if none
+ */
+const char *config_ini_init(struct config_ini_state *state, const char *path, char **pathbuf_out);
+
+/**
+ * Deallocate the settings loaded
+ * from the configurations file
+ *
+ * @param state The configurations
+ */
+void config_ini_free(struct config_ini_state *state);
+
+/**
+ * Get a section from the configuration file
+ *
+ * @param state The configurations
+ * @param name The name of the section
+ * @return The section; `NULL` if missing
+ */
+GCC_ONLY(__attribute__((__pure__)))
+struct config_ini_section *config_ini_get_section(struct config_ini_state *state, const char *name);
+
+
+/* config.c */
+
+/**
+ * Load settings
+ *
+ * @param settings Output parameter for the settings
+ * @param argc Number of command line arguments
+ * @param argv `NULL` terminated list of command line arguments,
+ * including argument zero
+ */
+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 */
+
+/**
+ * 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);
+
+/**
+ * Check whether location is valid
+ *
+ * If the message is invalid, and error message is printed
+ *
+ * @param location The location to check
+ * @return 1 if the location is valid, 0 otherwise
+ */
+int location_is_valid(const struct location *location);
+
+/**
+ * Print the current location to standard output
+ *
+ * @param location The current location
+ */
+void print_location(const struct location *location);
+
+
+/* hooks.c */
+
+/**
+ * Run hooks with a signal that the period changed
+ *
+ * @param prev_period The previous period
+ * @param period The new current period
+ */
+void run_period_change_hooks(enum period prev_period, enum period period);
+
+
+/* signals.c */
+
+/**
+ * Install signal handlers for the process
+ */
+void install_signal_handlers(void);
+
+/**
+ * Install signal handlers for forcefully terminating
+ * the process
+ *
+ * This is useful if slave thread is blocked
+ *
+ * SIGINT, SIGTERM, and SIGQUIT are set to at the
+ * first arrival arm a SIGARLM timer with a short
+ * expiration time, and on the second arrival
+ * immediately terminate the process. SIGARLM is
+ * set to immediately terminate the process.
+ */
+#ifndef WINDOWS
+void install_forceful_exit_signal_handlers(void);
+#endif
+
+
+/* util.c */
+
+/**
+ * Remove trailing whitespace
+ *
+ * @param s The string to trim, will be truncated
+ * @param end The current end of `s`; will be looked up if `NULL`
+ * @return `s`
+ */
+char *rtrim(char *s, char *end);
+
+/**
+ * Remove leading whitespace
+ *
+ * @param s The string to trim (will not be modified)
+ * @return `s` with an offset
+ */
+GCC_ONLY(__attribute__((__warn_unused_result__, __pure__)))
+char *ltrim(char *s);
+
+/**
+ * Get the user's home directory
+ *
+ * This function looks up the user's home directory
+ * once and caches the result, later calls to this
+ * function will use the cached result
+ *
+ * @return The user's home directory; the empty string if not found
+ */
+const char *get_home(void);
+
+/**
+ * Search for a file and open it for reading
+ *
+ * @param path_spec Specification for the path to try
+ * @param path_out Output parameter for the found file
+ * @param pathbuf_out Output parameter for the memory allocation for `*path_out`;
+ * shall be free(3)d by the caller
+ * @return `FILE` object for the reading the file; `NULL` if not found
+ */
+FILE *try_path_fopen(const struct env_path *path_spec, const char **path_out, char **pathbuf_out);
+
+/**
+ * Search for a directory and open it for reading
+ *
+ * @param path_spec Specification for the path to try
+ * @param path_out Output parameter for the found directory
+ * @param pathbuf_out Output parameter for the memory allocation for `*path_out`;
+ * shall be free(3)d by the caller
+ * @return `DIR` object for the reading the directory; `NULL` if not found
+ */
+DIR *try_path_opendir(const struct env_path *path_spec, const char **path_out, char **pathbuf_out);
+
+#ifndef WINDOWS
+/**
+ * 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
+ */
+void pipe_rdnonblock(int pipefds[2]);
+#endif
+
+/**
+ * Wrapper for calloc(3) that prints and error message
+ * and terminates the process on failure
+ *
+ * @param n Number of elements to allocate memory for
+ * @param m The size, in bytes, of each element
+ * @return Pointer to zero-initialised storage of at least `n*m` bytes, with default alignment
+ */
+GCC_ONLY(__attribute__((__warn_unused_result__, __malloc__, __alloc_size__(1, 2), __returns_nonnull__)))
+void *ecalloc(size_t n, size_t m);
+
+/**
+ * Wrapper for malloc(3) that prints and error message
+ * and terminates the process on failure
+ *
+ * @param n Number of bytes to allocate
+ * @return Pointer to uninitialised storage of at least `n` bytes, with default alignment
+ */
+GCC_ONLY(__attribute__((__warn_unused_result__, __malloc__, __alloc_size__(1), __returns_nonnull__)))
+void *emalloc(size_t n);
+
+/**
+ * Wrapper for realloc(3) that prints and error message
+ * and terminates the process on failure
+ *
+ * @param ptr Pointer to reallocate
+ * @param n Despired allocation size in bytes
+ * @return Replacement pointer for `ptr`, pointing to storage of at least `n` bytes,
+ * any byte that could be copied from `ptr` is copied over, and additional
+ * memory is uninitialised
+ */
+GCC_ONLY(__attribute__((__warn_unused_result__, __returns_nonnull__, __alloc_size__(2))))
+void *erealloc(void *ptr, size_t n);
+
+/**
+ * Wrapper for strdup(3) that prints and error message
+ * and terminates the process on failure
+ *
+ * @param s String to copy
+ * @return Copy of `s`
+ */
+GCC_ONLY(__attribute__((__warn_unused_result__, __returns_nonnull__, __malloc__, __assume_aligned__(1), __nonnull__)))
+char *estrdup(const char *s);
+
+/**
+ * Print a message, prefixed with the process name (followed by ": "),
+ * to standard error
+ *
+ * @param fmt Message text format string, see fprintf(3)
+ * @param args Message text arguments
+ */
+GCC_ONLY(__attribute__((__nonnull__(1), __format__(__printf__, 1, 0))))
+void vweprintf(const char *fmt, va_list args);
+
+/**
+ * Print a message, prefixed with the process name (followed by ": "),
+ * to standard error
+ *
+ * @param fmt Message text format string, see fprintf(3)
+ * @param ... Message text arguments
+ */
+GCC_ONLY(__attribute__((__nonnull__(1), __format__(__printf__, 1, 2))))
+void weprintf(const char *fmt, ...);
+
+/**
+ * Print a message, prefixed with the process name (followed by ": "),
+ * to standard error and terminate the process with exit value
+ * indicating error
+ *
+ * @param fmt Message text format string, see fprintf(3)
+ * @param ... Message text arguments
+ */
+GCC_ONLY(__attribute__((__nonnull__(1), __format__(__printf__, 1, 2), __noreturn__)))
+void eprintf(const char *fmt, ...);
+
+
+extern const struct gamma_method dummy_gamma_method;
+#ifdef ENABLE_COOPGAMMA
+extern const struct gamma_method coopgamma_gamma_method;
+#endif
+extern const struct gamma_method randr_gamma_method;
+extern const struct gamma_method vidmode_gamma_method;
+extern const struct gamma_method drm_gamma_method;
+extern const struct gamma_method quartz_gamma_method;
+extern const struct gamma_method wingdi_gamma_method;
+
+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
+#ifndef WINDOWS
+extern const struct location_provider geofile_location_provider;
+extern const struct location_provider timezone_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