diff options
-rw-r--r-- | po/POTFILES.in | 2 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/options.c | 672 | ||||
-rw-r--r-- | src/options.h | 61 | ||||
-rw-r--r-- | src/redshift.c | 818 | ||||
-rw-r--r-- | src/redshift.h | 32 |
6 files changed, 870 insertions, 716 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in index e6b9c4d..5ef8dac 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,7 +5,7 @@ data/applications/redshift.desktop.in data/applications/redshift-gtk.desktop.in src/redshift.c - +src/options.c src/config-ini.c src/gamma-drm.c diff --git a/src/Makefile.am b/src/Makefile.am index 99c8a2e..8aa96ea 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,6 +14,7 @@ redshift_SOURCES = \ gamma-dummy.c gamma-dummy.h \ hooks.c hooks.h \ location-manual.c location-manual.h \ + options.c options.h \ pipeutils.c pipeutils.h \ redshift.c redshift.h \ signals.c signals.h \ diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..8a0a0d2 --- /dev/null +++ b/src/options.c @@ -0,0 +1,672 @@ +/* options.c -- Program options + This file is part of Redshift. + + Redshift 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 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. If not, see <http://www.gnu.org/licenses/>. + + Copyright (c) 2017 Jon Lund Steffensen <jonlst@gmail.com> +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <math.h> + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(s) gettext(s) +#else +# define _(s) s +#endif + +#include "redshift.h" +#include "config-ini.h" +#include "options.h" +#include "solar.h" + +/* Angular elevation of the sun at which the color temperature + transition period starts and ends (in degress). + Transition during twilight, and while the sun is lower than + 3.0 degrees above the horizon. */ +#define TRANSITION_LOW SOLAR_CIVIL_TWILIGHT_ELEV +#define TRANSITION_HIGH 3.0 + +/* Default values for parameters. */ +#define DEFAULT_DAY_TEMP 6500 +#define DEFAULT_NIGHT_TEMP 4500 +#define DEFAULT_BRIGHTNESS 1.0 +#define DEFAULT_GAMMA 1.0 + + +/* A brightness string contains either one floating point value, + or two values separated by a colon. */ +static void +parse_brightness_string( + const char *str, float *bright_day, float *bright_night) +{ + char *s = strchr(str, ':'); + if (s == NULL) { + /* Same value for day and night. */ + *bright_day = *bright_night = atof(str); + } else { + *(s++) = '\0'; + *bright_day = atof(str); + *bright_night = atof(s); + } +} + +/* A gamma string contains either one floating point value, + or three values separated by colon. */ +static int +parse_gamma_string(const char *str, float gamma[]) +{ + char *s = strchr(str, ':'); + if (s == NULL) { + /* Use value for all channels */ + float g = atof(str); + gamma[0] = gamma[1] = gamma[2] = g; + } else { + /* Parse separate value for each channel */ + *(s++) = '\0'; + char *g_s = s; + s = strchr(s, ':'); + if (s == NULL) return -1; + + *(s++) = '\0'; + gamma[0] = atof(str); /* Red */ + gamma[1] = atof(g_s); /* Blue */ + gamma[2] = atof(s); /* Green */ + } + + return 0; +} + +/* Parse transition time string e.g. "04:50". Returns negative on failure, + otherwise the parsed time is returned as seconds since midnight. */ +static int +parse_transition_time(const char *str, const char **end) +{ + const char *min = NULL; + errno = 0; + long hours = strtol(str, (char **)&min, 10); + if (errno != 0 || min == str || min[0] != ':' || + hours < 0 || hours >= 24) { + return -1; + } + + min += 1; + errno = 0; + long minutes = strtol(min, (char **)end, 10); + if (errno != 0 || *end == min || minutes < 0 || minutes >= 60) { + return -1; + } + + return minutes * 60 + hours * 3600; +} + +/* Parse transition range string e.g. "04:50-6:20". Returns negative on + failure, otherwise zero. Parsed start and end times are returned as seconds + since midnight. */ +static int +parse_transition_range(const char *str, time_range_t *range) +{ + const char *next = NULL; + int start_time = parse_transition_time(str, &next); + if (start_time < 0) return -1; + + int end_time; + if (next[0] == '\0') { + end_time = start_time; + } else if (next[0] == '-') { + next += 1; + const char *end = NULL; + end_time = parse_transition_time(next, &end); + if (end_time < 0 || end[0] != '\0') return -1; + } else { + return -1; + } + + range->start = start_time; + range->end = end_time; + + return 0; +} + +/* Print help text. */ +static void +print_help(const char *program_name) +{ + /* TRANSLATORS: help output 1 + LAT is latitude, LON is longitude, + DAY is temperature at daytime, + NIGHT is temperature at night + no-wrap */ + printf(_("Usage: %s -l LAT:LON -t DAY:NIGHT [OPTIONS...]\n"), + program_name); + fputs("\n", stdout); + + /* TRANSLATORS: help output 2 + no-wrap */ + fputs(_("Set color temperature of display" + " according to time of day.\n"), stdout); + fputs("\n", stdout); + + /* TRANSLATORS: help output 3 + no-wrap */ + fputs(_(" -h\t\tDisplay this help message\n" + " -v\t\tVerbose output\n" + " -V\t\tShow program version\n"), stdout); + fputs("\n", stdout); + + /* TRANSLATORS: help output 4 + `list' must not be translated + no-wrap */ + fputs(_(" -b DAY:NIGHT\tScreen brightness to apply (between 0.1 and 1.0)\n" + " -c FILE\tLoad settings from specified configuration file\n" + " -g R:G:B\tAdditional gamma correction to apply\n" + " -l LAT:LON\tYour current location\n" + " -l PROVIDER\tSelect provider for automatic" + " location updates\n" + " \t\t(Type `list' to see available providers)\n" + " -m METHOD\tMethod to use to set color temperature\n" + " \t\t(Type `list' to see available methods)\n" + " -o\t\tOne shot mode (do not continuously adjust" + " color temperature)\n" + " -O TEMP\tOne shot manual mode (set color temperature)\n" + " -p\t\tPrint mode (only print parameters and exit)\n" + " -x\t\tReset mode (remove adjustment from screen)\n" + " -r\t\tDisable fading between color temperatures\n" + " -t DAY:NIGHT\tColor temperature to set at daytime/night\n"), + stdout); + fputs("\n", stdout); + + /* TRANSLATORS: help output 5 */ + printf(_("The neutral temperature is %uK. Using this value will not change " + "the color\ntemperature of the display. Setting the color temperature " + "to a value higher\nthan this results in more blue light, and setting " + "a lower value will result in\nmore red light.\n"), + NEUTRAL_TEMP); + + fputs("\n", stdout); + + /* TRANSLATORS: help output 6 */ + printf(_("Default values:\n\n" + " Daytime temperature: %uK\n" + " Night temperature: %uK\n"), + DEFAULT_DAY_TEMP, DEFAULT_NIGHT_TEMP); + + fputs("\n", stdout); + + /* TRANSLATORS: help output 7 */ + printf(_("Please report bugs to <%s>\n"), PACKAGE_BUGREPORT); +} + +/* Print list of adjustment methods. */ +static void +print_method_list(const gamma_method_t *gamma_methods) +{ + fputs(_("Available adjustment methods:\n"), stdout); + for (int i = 0; gamma_methods[i].name != NULL; i++) { + printf(" %s\n", gamma_methods[i].name); + } + + fputs("\n", stdout); + fputs(_("Specify colon-separated options with" + " `-m METHOD:OPTIONS'.\n"), stdout); + /* TRANSLATORS: `help' must not be translated. */ + fputs(_("Try `-m METHOD:help' for help.\n"), stdout); +} + +/* Print list of location providers. */ +static void +print_provider_list(const location_provider_t location_providers[]) +{ + fputs(_("Available location providers:\n"), stdout); + for (int i = 0; location_providers[i].name != NULL; i++) { + printf(" %s\n", location_providers[i].name); + } + + fputs("\n", stdout); + fputs(_("Specify colon-separated options with" + "`-l PROVIDER:OPTIONS'.\n"), stdout); + /* TRANSLATORS: `help' must not be translated. */ + fputs(_("Try `-l PROVIDER:help' for help.\n"), stdout); +} + +/* Return the gamma method with the given name. */ +static const gamma_method_t * +find_gamma_method(const gamma_method_t gamma_methods[], const char *name) +{ + const gamma_method_t *method = NULL; + for (int i = 0; gamma_methods[i].name != NULL; i++) { + const gamma_method_t *m = &gamma_methods[i]; + if (strcasecmp(name, m->name) == 0) { + method = m; + break; + } + } + + return method; +} + +/* Return location provider with the given name. */ +static const location_provider_t * +find_location_provider( + const location_provider_t location_providers[], const char *name) +{ + const location_provider_t *provider = NULL; + for (int i = 0; location_providers[i].name != NULL; i++) { + const location_provider_t *p = &location_providers[i]; + if (strcasecmp(name, p->name) == 0) { + provider = p; + break; + } + } + + return provider; +} + + +/* Initialize options struct. */ +void +options_init(options_t *options) +{ + options->config_filepath = NULL; + + /* Default elevation values. */ + options->scheme.high = TRANSITION_HIGH; + options->scheme.low = TRANSITION_LOW; + + /* Settings for day, night and transition period. + Initialized to indicate that the values are not set yet. */ + options->scheme.use_time = 0; + options->scheme.dawn.start = -1; + options->scheme.dawn.end = -1; + options->scheme.dusk.start = -1; + options->scheme.dusk.end = -1; + + options->scheme.day.temperature = -1; + options->scheme.day.gamma[0] = NAN; + options->scheme.day.brightness = NAN; + + options->scheme.night.temperature = -1; + options->scheme.night.gamma[0] = NAN; + options->scheme.night.brightness = NAN; + + /* Temperature for manual mode */ + options->temp_set = -1; + + options->method = NULL; + options->method_args = NULL; + + options->provider = NULL; + options->provider_args = NULL; + + options->use_fade = -1; + options->mode = PROGRAM_MODE_CONTINUAL; + options->verbose = 0; +} + +/* Parse command line arguments. */ +void +options_parse_args( + options_t *options, int argc, char *argv[], + const gamma_method_t *gamma_methods, + const location_provider_t *location_providers) +{ + int r; + char *s; + + /* Parse command line arguments. */ + int opt; + while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx")) != -1) { + switch (opt) { + case 'b': + parse_brightness_string( + optarg, + &options->scheme.day.brightness, + &options->scheme.night.brightness); + break; + case 'c': + free(options->config_filepath); + options->config_filepath = strdup(optarg); + break; + case 'g': + r = parse_gamma_string( + optarg, options->scheme.day.gamma); + if (r < 0) { + fputs(_("Malformed gamma argument.\n"), + stderr); + fputs(_("Try `-h' for more" + " information.\n"), stderr); + exit(EXIT_FAILURE); + } + + /* Set night gamma to the same value as day gamma. + To set these to distinct values use the config + file. */ + memcpy(options->scheme.night.gamma, + options->scheme.day.gamma, + sizeof(options->scheme.night.gamma)); + break; + case 'h': + print_help(argv[0]); + exit(EXIT_SUCCESS); + break; + case 'l': + /* Print list of providers if argument is `list' */ + if (strcasecmp(optarg, "list") == 0) { + print_provider_list(location_providers); + exit(EXIT_SUCCESS); + } + + char *provider_name = NULL; + + /* Don't save the result of strtof(); we simply want + to know if optarg can be parsed as a float. */ + errno = 0; + char *end; + strtof(optarg, &end); + if (errno == 0 && *end == ':') { + /* Use instead as arguments to `manual'. */ + provider_name = "manual"; + options->provider_args = optarg; + } else { + /* Split off provider arguments. */ + s = strchr(optarg, ':'); + if (s != NULL) { + *(s++) = '\0'; + options->provider_args = s; + } + + provider_name = optarg; + } + + /* Lookup provider from name. */ + options->provider = find_location_provider( + location_providers, provider_name); + if (options->provider == NULL) { + fprintf(stderr, _("Unknown location provider" + " `%s'.\n"), provider_name); + exit(EXIT_FAILURE); + } + + /* Print provider help if arg is `help'. */ + if (options->provider_args != NULL && + strcasecmp(options->provider_args, "help") == 0) { + options->provider->print_help(stdout); + exit(EXIT_SUCCESS); + } + break; + case 'm': + /* Print list of methods if argument is `list' */ + if (strcasecmp(optarg, "list") == 0) { + print_method_list(gamma_methods); + exit(EXIT_SUCCESS); + } + + /* Split off method arguments. */ + s = strchr(optarg, ':'); + if (s != NULL) { + *(s++) = '\0'; + options->method_args = s; + } + + /* Find adjustment method by name. */ + options->method = find_gamma_method( + gamma_methods, optarg); + if (options->method == NULL) { + /* TRANSLATORS: This refers to the method + used to adjust colors e.g VidMode */ + fprintf(stderr, _("Unknown adjustment method" + " `%s'.\n"), optarg); + exit(EXIT_FAILURE); + } + + /* Print method help if arg is `help'. */ + if (options->method_args != NULL && + strcasecmp(options->method_args, "help") == 0) { + options->method->print_help(stdout); + exit(EXIT_SUCCESS); + } + break; + case 'o': + options->mode = PROGRAM_MODE_ONE_SHOT; + break; + case 'O': + options->mode = PROGRAM_MODE_MANUAL; + options->temp_set = atoi(optarg); + break; + case 'p': + options->mode = PROGRAM_MODE_PRINT; + break; + case 'r': + options->use_fade = 0; + break; + case 't': + s = strchr(optarg, ':'); + if (s == NULL) { + fputs(_("Malformed temperature argument.\n"), + stderr); + fputs(_("Try `-h' for more information.\n"), + stderr); + exit(EXIT_FAILURE); + } + *(s++) = '\0'; + options->scheme.day.temperature = atoi(optarg); + options->scheme.night.temperature = atoi(s); + break; + case 'v': + options->verbose = 1; + break; + case 'V': + printf("%s\n", PACKAGE_STRING); + exit(EXIT_SUCCESS); + break; + case 'x': + options->mode = PROGRAM_MODE_RESET; + break; + case '?': + fputs(_("Try `-h' for more information.\n"), stderr); + exit(EXIT_FAILURE); + break; + } + } +} + +/* Parse options defined in the config file. */ +void +options_parse_config_file( + options_t *options, config_ini_state_t *config_state, + const gamma_method_t *gamma_methods, + const location_provider_t *location_providers) +{ + int r; + + /* Read global config settings. */ + config_ini_section_t *section = config_ini_get_section( + config_state, "redshift"); + if (section == NULL) return; + + config_ini_setting_t *setting = section->settings; + while (setting != NULL) { + if (strcasecmp(setting->name, "temp-day") == 0) { + if (options->scheme.day.temperature < 0) { + options->scheme.day.temperature = + atoi(setting->value); + } + } else if (strcasecmp(setting->name, "temp-night") == 0) { + if (options->scheme.night.temperature < 0) { + options->scheme.night.temperature = + atoi(setting->value); + } + } else if (strcasecmp(setting->name, "transition") == 0 || + strcasecmp(setting->name, "fade") == 0) { + /* "fade" is preferred, "transition" is + deprecated as the setting key. */ + if (options->use_fade < 0) { + options->use_fade = !!atoi(setting->value); + } + } else if (strcasecmp(setting->name, "brightness") == 0) { + if (isnan(options->scheme.day.brightness)) { + options->scheme.day.brightness = + atof(setting->value); + } + if (isnan(options->scheme.night.brightness)) { + options->scheme.night.brightness = + atof(setting->value); + } + } else if (strcasecmp(setting->name, "brightness-day") == 0) { + if (isnan(options->scheme.day.brightness)) { + options->scheme.day.brightness = + atof(setting->value); + } + } else if (strcasecmp(setting->name, + "brightness-night") == 0) { + if (isnan(options->scheme.night.brightness)) { + options->scheme.night.brightness = + atof(setting->value); + } + } else if (strcasecmp(setting->name, "elevation-high") == 0) { + options->scheme.high = atof(setting->value); + } else if (strcasecmp(setting->name, "elevation-low") == 0) { + options->scheme.low = atof(setting->value); + } else if (strcasecmp(setting->name, "gamma") == 0) { + if (isnan(options->scheme.day.gamma[0])) { + r = parse_gamma_string( + setting->value, + options->scheme.day.gamma); + if (r < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + exit(EXIT_FAILURE); + } + memcpy(options->scheme.night.gamma, + options->scheme.day.gamma, + sizeof(options->scheme.night.gamma)); + } + } else if (strcasecmp(setting->name, "gamma-day") == 0) { + if (isnan(options->scheme.day.gamma[0])) { + r = parse_gamma_string( + setting->value, + options->scheme.day.gamma); + if (r < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + exit(EXIT_FAILURE); + } + } + } else if (strcasecmp(setting->name, "gamma-night") == 0) { + if (isnan(options->scheme.night.gamma[0])) { + r = parse_gamma_string( + setting->value, + options->scheme.night.gamma); + if (r < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + exit(EXIT_FAILURE); + } + } + } else if (strcasecmp(setting->name, + "adjustment-method") == 0) { + if (options->method == NULL) { + options->method = find_gamma_method( + gamma_methods, setting->value); + if (options->method == NULL) { + fprintf(stderr, _("Unknown adjustment" + " method `%s'.\n"), + setting->value); + exit(EXIT_FAILURE); + } + } + } else if (strcasecmp(setting->name, + "location-provider") == 0) { + if (options->provider == NULL) { + options->provider = find_location_provider( + location_providers, + setting->value); + if (options->provider == NULL) { + fprintf(stderr, _("Unknown location" + " provider `%s'.\n"), + setting->value); + exit(EXIT_FAILURE); + } + } + } else if (strcasecmp(setting->name, "dawn-time") == 0) { + if (options->scheme.dawn.start < 0) { + int r = parse_transition_range( + setting->value, &options->scheme.dawn); + if (r < 0) { + fprintf(stderr, _("Malformed dawn-time" + " setting `%s'.\n"), + setting->value); + exit(EXIT_FAILURE); + } + } + } else if (strcasecmp(setting->name, "dusk-time") == 0) { + if (options->scheme.dusk.start < 0) { + int r = parse_transition_range( + setting->value, &options->scheme.dusk); + if (r < 0) { + fprintf(stderr, _("Malformed dusk-time" + " setting `%s'.\n"), + setting->value); + exit(EXIT_FAILURE); + } + } + } else { + fprintf(stderr, _("Unknown configuration" + " setting `%s'.\n"), + setting->name); + } + + setting = setting->next; + } +} + +/* Replace unspecified options with default values. */ +void +options_set_defaults(options_t *options) +{ + if (options->scheme.day.temperature < 0) { + options->scheme.day.temperature = DEFAULT_DAY_TEMP; + } + if (options->scheme.night.temperature < 0) { + options->scheme.night.temperature = DEFAULT_NIGHT_TEMP; + } + + if (isnan(options->scheme.day.brightness)) { + options->scheme.day.brightness = DEFAULT_BRIGHTNESS; + } + if (isnan(options->scheme.night.brightness)) { + options->scheme.night.brightness = DEFAULT_BRIGHTNESS; + } + + if (isnan(options->scheme.day.gamma[0])) { + options->scheme.day.gamma[0] = DEFAULT_GAMMA; + options->scheme.day.gamma[1] = DEFAULT_GAMMA; + options->scheme.day.gamma[2] = DEFAULT_GAMMA; + } + if (isnan(options->scheme.night.gamma[0])) { + options->scheme.night.gamma[0] = DEFAULT_GAMMA; + options->scheme.night.gamma[1] = DEFAULT_GAMMA; + options->scheme.night.gamma[2] = DEFAULT_GAMMA; + } + + if (options->use_fade < 0) options->use_fade = 1; +} diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..95a31b1 --- /dev/null +++ b/src/options.h @@ -0,0 +1,61 @@ +/* options.h -- Program options header + This file is part of Redshift. + + Redshift 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 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. If not, see <http://www.gnu.org/licenses/>. + + Copyright (c) 2017 Jon Lund Steffensen <jonlst@gmail.com> +*/ + +#ifndef REDSHIFT_OPTIONS_H +#define REDSHIFT_OPTIONS_H + +#include "redshift.h" + +typedef struct { + /* Path to config file */ + char *config_filepath; + + transition_scheme_t scheme; + program_mode_t mode; + int verbose; + + /* Temperature to set in manual mode. */ + int temp_set; + /* Whether to fade between large skips in color temperature. */ + int use_fade; + + /* Selected gamma method. */ + const gamma_method_t *method; + /* Arguments for gamma method. */ + char *method_args; + + /* Selected location provider. */ + const location_provider_t *provider; + /* Arguments for location provider. */ + char *provider_args; +} options_t; + + +void options_init(options_t *options); +void options_parse_args( + options_t *options, int argc, char *argv[], + const gamma_method_t *gamma_methods, + const location_provider_t *location_providers); +void options_parse_config_file( + options_t *options, config_ini_state_t *config_state, + const gamma_method_t *gamma_methods, + const location_provider_t *location_providers); +void options_set_defaults(options_t *options); + +#endif /* ! REDSHIFT_OPTIONS_H */ diff --git a/src/redshift.c b/src/redshift.c index 6e63b2c..1b31647 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -64,6 +64,7 @@ int poll(struct pollfd *fds, int nfds, int timeout) { abort(); return -1; } #include "systemtime.h" #include "hooks.h" #include "signals.h" +#include "options.h" /* pause() is not defined on windows platform but is not needed either. Use a noop macro instead. */ @@ -120,22 +121,6 @@ int poll(struct pollfd *fds, int nfds, int timeout) { abort(); return -1; } #define MIN_GAMMA 0.1 #define MAX_GAMMA 10.0 -/* Default values for parameters. */ -#define DEFAULT_DAY_TEMP 6500 -#define DEFAULT_NIGHT_TEMP 4500 -#define DEFAULT_BRIGHTNESS 1.0 -#define DEFAULT_GAMMA 1.0 - -/* The color temperature when no adjustment is applied. */ -#define NEUTRAL_TEMP 6500 - -/* Angular elevation of the sun at which the color temperature - transition period starts and ends (in degress). - Transition during twilight, and while the sun is lower than - 3.0 degrees above the horizon. */ -#define TRANSITION_LOW SOLAR_CIVIL_TWILIGHT_ELEV -#define TRANSITION_HIGH 3.0 - /* Duration of sleep between screen updates (milliseconds). */ #define SLEEP_DURATION 5000 #define SLEEP_DURATION_SHORT 100 @@ -143,34 +128,6 @@ int poll(struct pollfd *fds, int nfds, int timeout) { abort(); return -1; } /* Length of fade in numbers of short sleep durations. */ #define FADE_LENGTH 40 -/* Program modes. */ -typedef enum { - PROGRAM_MODE_CONTINUAL, - PROGRAM_MODE_ONE_SHOT, - PROGRAM_MODE_PRINT, - PROGRAM_MODE_RESET, - PROGRAM_MODE_MANUAL -} program_mode_t; - -/* Time range. - Fields are offsets from midnight in seconds. */ -typedef struct { - int start; - int end; -} time_range_t; - -/* Transition scheme. - The solar elevations at which the transition begins/ends, - and the association color settings. */ -typedef struct { - double high; - double low; - int use_time; /* When enabled, ignore elevation and use time ranges. */ - time_range_t dawn; - time_range_t dusk; - color_setting_t day; - color_setting_t night; -} transition_scheme_t; /* Names of periods of day */ static const char *period_names[] = { @@ -345,105 +302,6 @@ color_setting_diff_is_major( } -static void -print_help(const char *program_name) -{ - /* TRANSLATORS: help output 1 - LAT is latitude, LON is longitude, - DAY is temperature at daytime, - NIGHT is temperature at night - no-wrap */ - printf(_("Usage: %s -l LAT:LON -t DAY:NIGHT [OPTIONS...]\n"), - program_name); - fputs("\n", stdout); - - /* TRANSLATORS: help output 2 - no-wrap */ - fputs(_("Set color temperature of display" - " according to time of day.\n"), stdout); - fputs("\n", stdout); - - /* TRANSLATORS: help output 3 - no-wrap */ - fputs(_(" -h\t\tDisplay this help message\n" - " -v\t\tVerbose output\n" - " -V\t\tShow program version\n"), stdout); - fputs("\n", stdout); - - /* TRANSLATORS: help output 4 - `list' must not be translated - no-wrap */ - fputs(_(" -b DAY:NIGHT\tScreen brightness to apply (between 0.1 and 1.0)\n" - " -c FILE\tLoad settings from specified configuration file\n" - " -g R:G:B\tAdditional gamma correction to apply\n" - " -l LAT:LON\tYour current location\n" - " -l PROVIDER\tSelect provider for automatic" - " location updates\n" - " \t\t(Type `list' to see available providers)\n" - " -m METHOD\tMethod to use to set color temperature\n" - " \t\t(Type `list' to see available methods)\n" - " -o\t\tOne shot mode (do not continuously adjust" - " color temperature)\n" - " -O TEMP\tOne shot manual mode (set color temperature)\n" - " -p\t\tPrint mode (only print parameters and exit)\n" - " -x\t\tReset mode (remove adjustment from screen)\n" - " -r\t\tDisable fading between color temperatures\n" - " -t DAY:NIGHT\tColor temperature to set at daytime/night\n"), - stdout); - fputs("\n", stdout); - - /* TRANSLATORS: help output 5 */ - printf(_("The neutral temperature is %uK. Using this value will not change " - "the color\ntemperature of the display. Setting the color temperature " - "to a value higher\nthan this results in more blue light, and setting " - "a lower value will result in\nmore red light.\n"), - NEUTRAL_TEMP); - - fputs("\n", stdout); - - /* TRANSLATORS: help output 6 */ - printf(_("Default values:\n\n" - " Daytime temperature: %uK\n" - " Night temperature: %uK\n"), - DEFAULT_DAY_TEMP, DEFAULT_NIGHT_TEMP); - - fputs("\n", stdout); - - /* TRANSLATORS: help output 7 */ - printf(_("Please report bugs to <%s>\n"), PACKAGE_BUGREPORT); -} - -static void -print_method_list(const gamma_method_t *gamma_methods) -{ - fputs(_("Available adjustment methods:\n"), stdout); - for (int i = 0; gamma_methods[i].name != NULL; i++) { - printf(" %s\n", gamma_methods[i].name); - } - - fputs("\n", stdout); - fputs(_("Specify colon-separated options with" - " `-m METHOD:OPTIONS'.\n"), stdout); - /* TRANSLATORS: `help' must not be translated. */ - fputs(_("Try `-m METHOD:help' for help.\n"), stdout); -} - -static void -print_provider_list(const location_provider_t location_providers[]) -{ - fputs(_("Available location providers:\n"), stdout); - for (int i = 0; location_providers[i].name != NULL; i++) { - printf(" %s\n", location_providers[i].name); - } - - fputs("\n", stdout); - fputs(_("Specify colon-separated options with" - "`-l PROVIDER:OPTIONS'.\n"), stdout); - /* TRANSLATORS: `help' must not be translated. */ - fputs(_("Try `-l PROVIDER:help' for help.\n"), stdout); -} - - static int provider_try_start(const location_provider_t *provider, location_state_t **state, config_ini_state_t *config, @@ -614,98 +472,6 @@ method_try_start(const gamma_method_t *method, return 0; } -/* A gamma string contains either one floating point value, - or three values separated by colon. */ -static int -parse_gamma_string(const char *str, float gamma[]) -{ - char *s = strchr(str, ':'); - if (s == NULL) { - /* Use value for all channels */ - float g = atof(str); - gamma[0] = gamma[1] = gamma[2] = g; - } else { - /* Parse separate value for each channel */ - *(s++) = '\0'; - char *g_s = s; - s = strchr(s, ':'); - if (s == NULL) return -1; - - *(s++) = '\0'; - gamma[0] = atof(str); /* Red */ - gamma[1] = atof(g_s); /* Blue */ - gamma[2] = atof(s); /* Green */ - } - - return 0; -} - -/* A brightness string contains either one floating point value, - or two values separated by a colon. */ -static void -parse_brightness_string(const char *str, float *bright_day, float *bright_night) -{ - char *s = strchr(str, ':'); - if (s == NULL) { - /* Same value for day and night. */ - *bright_day = *bright_night = atof(str); - } else { - *(s++) = '\0'; - *bright_day = atof(str); - *bright_night = atof(s); - } -} - -/* Parse transition time string e.g. "04:50". Returns negative on failure, - otherwise the parsed time is returned as seconds since midnight. */ -static int -parse_transition_time(const char *str, const char **end) -{ - const char *min = NULL; - errno = 0; - long hours = strtol(str, (char **)&min, 10); - if (errno != 0 || min == str || min[0] != ':' || - hours < 0 || hours >= 24) { - return -1; - } - - min += 1; - errno = 0; - long minutes = strtol(min, (char **)end, 10); - if (errno != 0 || *end == min || minutes < 0 || minutes >= 60) { - return -1; - } - - return minutes * 60 + hours * 3600; -} - -/* Parse transition range string e.g. "04:50-6:20". Returns negative on - failure, otherwise zero. Parsed start and end times are returned as seconds - since midnight. */ -static int -parse_transition_range(const char *str, time_range_t *range) -{ - const char *next = NULL; - int start_time = parse_transition_time(str, &next); - if (start_time < 0) return -1; - - int end_time; - if (next[0] == '\0') { - end_time = start_time; - } else if (next[0] == '-') { - next += 1; - const char *end = NULL; - end_time = parse_transition_time(next, &end); - if (end_time < 0 || end[0] != '\0') return -1; - } else { - return -1; - } - - range->start = start_time; - range->end = end_time; - - return 0; -} /* Check whether gamma is within allowed levels. */ static int @@ -746,37 +512,6 @@ location_is_valid(const location_t *location) return 1; } -static const gamma_method_t * -find_gamma_method(const gamma_method_t gamma_methods[], const char *name) -{ - const gamma_method_t *method = NULL; - for (int i = 0; gamma_methods[i].name != NULL; i++) { - const gamma_method_t *m = &gamma_methods[i]; - if (strcasecmp(name, m->name) == 0) { - method = m; - break; - } - } - - return method; -} - -static const location_provider_t * -find_location_provider( - const location_provider_t location_providers[], const char *name) -{ - const location_provider_t *provider = NULL; - for (int i = 0; location_providers[i].name != NULL; i++) { - const location_provider_t *p = &location_providers[i]; - if (strcasecmp(name, p->name) == 0) { - provider = p; - break; - } - } - - return provider; -} - /* 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, @@ -1156,31 +891,6 @@ main(int argc, char *argv[]) textdomain(PACKAGE); #endif - /* Initialize settings to NULL values. */ - char *config_filepath = NULL; - - /* Settings for day, night and transition period. - Initialized to indicate that the values are not set yet. */ - transition_scheme_t scheme = - { TRANSITION_HIGH, TRANSITION_LOW }; - - scheme.use_time = 0; - scheme.dawn.start = -1; - scheme.dawn.end = -1; - scheme.dusk.start = -1; - scheme.dusk.end = -1; - - scheme.day.temperature = -1; - scheme.day.gamma[0] = NAN; - scheme.day.brightness = NAN; - - scheme.night.temperature = -1; - scheme.night.gamma[0] = NAN; - scheme.night.brightness = NAN; - - /* Temperature for manual mode */ - int temp_set = -1; - /* List of gamma methods. */ const gamma_method_t gamma_methods[] = { #ifdef ENABLE_DRM @@ -1202,9 +912,6 @@ main(int argc, char *argv[]) { NULL } }; - const gamma_method_t *method = NULL; - char *method_args = NULL; - /* List of location providers. */ const location_provider_t location_providers[] = { #ifdef ENABLE_GEOCLUE2 @@ -1217,383 +924,52 @@ main(int argc, char *argv[]) { NULL } }; - const location_provider_t *provider = NULL; - char *provider_args = NULL; - - int use_fade = -1; - program_mode_t mode = PROGRAM_MODE_CONTINUAL; - int verbose = 0; - char *s; - /* Flush messages consistently even if redirected to a pipe or file. Change the flush behaviour to line-buffered, without changing the actual buffers being used. */ setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IOLBF, 0); - /* Parse command line arguments. */ - int opt; - while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx")) != -1) { - switch (opt) { - case 'b': - parse_brightness_string(optarg, - &scheme.day.brightness, - &scheme.night.brightness); - break; - case 'c': - free(config_filepath); - config_filepath = strdup(optarg); - break; - case 'g': - r = parse_gamma_string(optarg, scheme.day.gamma); - if (r < 0) { - fputs(_("Malformed gamma argument.\n"), - stderr); - fputs(_("Try `-h' for more" - " information.\n"), stderr); - exit(EXIT_FAILURE); - } - - /* Set night gamma to the same value as day gamma. - To set these to distinct values use the config - file. */ - memcpy(scheme.night.gamma, scheme.day.gamma, - sizeof(scheme.night.gamma)); - break; - case 'h': - print_help(argv[0]); - exit(EXIT_SUCCESS); - break; - case 'l': - /* Print list of providers if argument is `list' */ - if (strcasecmp(optarg, "list") == 0) { - print_provider_list(location_providers); - exit(EXIT_SUCCESS); - } - - char *provider_name = NULL; - - /* Don't save the result of strtof(); we simply want - to know if optarg can be parsed as a float. */ - errno = 0; - char *end; - strtof(optarg, &end); - if (errno == 0 && *end == ':') { - /* Use instead as arguments to `manual'. */ - provider_name = "manual"; - provider_args = optarg; - } else { - /* Split off provider arguments. */ - s = strchr(optarg, ':'); - if (s != NULL) { - *(s++) = '\0'; - provider_args = s; - } - - provider_name = optarg; - } - - /* Lookup provider from name. */ - provider = find_location_provider( - location_providers, provider_name); - if (provider == NULL) { - fprintf(stderr, _("Unknown location provider" - " `%s'.\n"), provider_name); - exit(EXIT_FAILURE); - } - - /* Print provider help if arg is `help'. */ - if (provider_args != NULL && - strcasecmp(provider_args, "help") == 0) { - provider->print_help(stdout); - exit(EXIT_SUCCESS); - } - break; - case 'm': - /* Print list of methods if argument is `list' */ - if (strcasecmp(optarg, "list") == 0) { - print_method_list(gamma_methods); - exit(EXIT_SUCCESS); - } - - /* Split off method arguments. */ - s = strchr(optarg, ':'); - if (s != NULL) { - *(s++) = '\0'; - method_args = s; - } - - /* Find adjustment method by name. */ - method = find_gamma_method(gamma_methods, optarg); - if (method == NULL) { - /* TRANSLATORS: This refers to the method - used to adjust colors e.g VidMode */ - fprintf(stderr, _("Unknown adjustment method" - " `%s'.\n"), optarg); - exit(EXIT_FAILURE); - } - - /* Print method help if arg is `help'. */ - if (method_args != NULL && - strcasecmp(method_args, "help") == 0) { - method->print_help(stdout); - exit(EXIT_SUCCESS); - } - break; - case 'o': - mode = PROGRAM_MODE_ONE_SHOT; - break; - case 'O': - mode = PROGRAM_MODE_MANUAL; - temp_set = atoi(optarg); - break; - case 'p': - mode = PROGRAM_MODE_PRINT; - break; - case 'r': - use_fade = 0; - break; - case 't': - s = strchr(optarg, ':'); - if (s == NULL) { - fputs(_("Malformed temperature argument.\n"), - stderr); - fputs(_("Try `-h' for more information.\n"), - stderr); - exit(EXIT_FAILURE); - } - *(s++) = '\0'; - scheme.day.temperature = atoi(optarg); - scheme.night.temperature = atoi(s); - break; - case 'v': - verbose = 1; - break; - case 'V': - printf("%s\n", PACKAGE_STRING); - exit(EXIT_SUCCESS); - break; - case 'x': - mode = PROGRAM_MODE_RESET; - break; - case '?': - fputs(_("Try `-h' for more information.\n"), stderr); - exit(EXIT_FAILURE); - break; - } - } + options_t options; + options_init(&options); + options_parse_args( + &options, argc, argv, gamma_methods, location_providers); /* Load settings from config file. */ config_ini_state_t config_state; - r = config_ini_init(&config_state, config_filepath); + r = config_ini_init(&config_state, options.config_filepath); if (r < 0) { fputs("Unable to load config file.\n", stderr); exit(EXIT_FAILURE); } - free(config_filepath); + free(options.config_filepath); - /* Read global config settings. */ - config_ini_section_t *section = config_ini_get_section(&config_state, - "redshift"); - if (section != NULL) { - config_ini_setting_t *setting = section->settings; - while (setting != NULL) { - if (strcasecmp(setting->name, "temp-day") == 0) { - if (scheme.day.temperature < 0) { - scheme.day.temperature = - atoi(setting->value); - } - } else if (strcasecmp(setting->name, - "temp-night") == 0) { - if (scheme.night.temperature < 0) { - scheme.night.temperature = - atoi(setting->value); - } - } else if (strcasecmp( - setting->name, "transition") == 0 || - strcasecmp(setting->name, "fade") == 0) { - /* "fade" is preferred, "transition" is - deprecated as the setting key. */ - if (use_fade < 0) { - use_fade = !!atoi(setting->value); - } - } else if (strcasecmp(setting->name, - "brightness") == 0) { - if (isnan(scheme.day.brightness)) { - scheme.day.brightness = - atof(setting->value); - } - if (isnan(scheme.night.brightness)) { - scheme.night.brightness = - atof(setting->value); - } - } else if (strcasecmp(setting->name, - "brightness-day") == 0) { - if (isnan(scheme.day.brightness)) { - scheme.day.brightness = - atof(setting->value); - } - } else if (strcasecmp(setting->name, - "brightness-night") == 0) { - if (isnan(scheme.night.brightness)) { - scheme.night.brightness = - atof(setting->value); - } - } else if (strcasecmp(setting->name, - "elevation-high") == 0) { - scheme.high = atof(setting->value); - } else if (strcasecmp(setting->name, - "elevation-low") == 0) { - scheme.low = atof(setting->value); - } else if (strcasecmp(setting->name, "gamma") == 0) { - if (isnan(scheme.day.gamma[0])) { - r = parse_gamma_string(setting->value, - scheme.day.gamma); - if (r < 0) { - fputs(_("Malformed gamma" - " setting.\n"), - stderr); - exit(EXIT_FAILURE); - } - memcpy(scheme.night.gamma, scheme.day.gamma, - sizeof(scheme.night.gamma)); - } - } else if (strcasecmp(setting->name, "gamma-day") == 0) { - if (isnan(scheme.day.gamma[0])) { - r = parse_gamma_string(setting->value, - scheme.day.gamma); - if (r < 0) { - fputs(_("Malformed gamma" - " setting.\n"), - stderr); - exit(EXIT_FAILURE); - } - } - } else if (strcasecmp(setting->name, "gamma-night") == 0) { - if (isnan(scheme.night.gamma[0])) { - r = parse_gamma_string(setting->value, - scheme.night.gamma); - if (r < 0) { - fputs(_("Malformed gamma" - " setting.\n"), - stderr); - exit(EXIT_FAILURE); - } - } - } else if (strcasecmp(setting->name, - "adjustment-method") == 0) { - if (method == NULL) { - method = find_gamma_method( - gamma_methods, setting->value); - if (method == NULL) { - fprintf(stderr, _("Unknown" - " adjustment" - " method" - " `%s'.\n"), - setting->value); - exit(EXIT_FAILURE); - } - } - } else if (strcasecmp(setting->name, - "location-provider") == 0) { - if (provider == NULL) { - provider = find_location_provider( - location_providers, - setting->value); - if (provider == NULL) { - fprintf(stderr, _("Unknown" - " location" - " provider" - " `%s'.\n"), - setting->value); - exit(EXIT_FAILURE); - } - } - } else if (strcasecmp(setting->name, - "dawn-time") == 0) { - if (scheme.dawn.start < 0) { - int r = parse_transition_range( - setting->value, &scheme.dawn); - if (r < 0) { - fprintf(stderr, _("Malformed" - " dawn-time" - " setting" - " `%s'.\n"), - setting->value); - exit(EXIT_FAILURE); - } - } - } else if (strcasecmp(setting->name, - "dusk-time") == 0) { - if (scheme.dusk.start < 0) { - int r = parse_transition_range( - setting->value, &scheme.dusk); - if (r < 0) { - fprintf(stderr, _("Malformed" - " dusk-time" - " setting" - " `%s'.\n"), - setting->value); - exit(EXIT_FAILURE); - } - } - } else { - fprintf(stderr, _("Unknown configuration" - " setting `%s'.\n"), - setting->name); - } - setting = setting->next; - } - } - - /* Use default values for settings that were neither defined in - the config file nor on the command line. */ - if (scheme.day.temperature < 0) { - scheme.day.temperature = DEFAULT_DAY_TEMP; - } - if (scheme.night.temperature < 0) { - scheme.night.temperature = DEFAULT_NIGHT_TEMP; - } + options_parse_config_file( + &options, &config_state, gamma_methods, location_providers); - if (isnan(scheme.day.brightness)) { - scheme.day.brightness = DEFAULT_BRIGHTNESS; - } - if (isnan(scheme.night.brightness)) { - scheme.night.brightness = DEFAULT_BRIGHTNESS; - } + options_set_defaults(&options); - if (isnan(scheme.day.gamma[0])) { - scheme.day.gamma[0] = DEFAULT_GAMMA; - scheme.day.gamma[1] = DEFAULT_GAMMA; - scheme.day.gamma[2] = DEFAULT_GAMMA; - } - if (isnan(scheme.night.gamma[0])) { - scheme.night.gamma[0] = DEFAULT_GAMMA; - scheme.night.gamma[1] = DEFAULT_GAMMA; - scheme.night.gamma[2] = DEFAULT_GAMMA; - } - - if (use_fade < 0) use_fade = 1; - - if (scheme.dawn.start >= 0 || scheme.dawn.end >= 0 || - scheme.dusk.start >= 0 || scheme.dusk.end >= 0) { - if (scheme.dawn.start < 0 || scheme.dawn.end < 0 || - scheme.dusk.start < 0 || scheme.dusk.end < 0) { + if (options.scheme.dawn.start >= 0 || options.scheme.dawn.end >= 0 || + options.scheme.dusk.start >= 0 || options.scheme.dusk.end >= 0) { + if (options.scheme.dawn.start < 0 || + options.scheme.dawn.end < 0 || + options.scheme.dusk.start < 0 || + options.scheme.dusk.end < 0) { fputs(_("Partitial time-configuration not" " supported!\n"), stderr); exit(EXIT_FAILURE); } - if (scheme.dawn.start > scheme.dawn.end || - scheme.dawn.end > scheme.dusk.start || - scheme.dusk.start > scheme.dusk.end) { + if (options.scheme.dawn.start > options.scheme.dawn.end || + options.scheme.dawn.end > options.scheme.dusk.start || + options.scheme.dusk.start > options.scheme.dusk.end) { fputs(_("Invalid dawn/dusk time configuration!\n"), stderr); exit(EXIT_FAILURE); } - scheme.use_time = 1; + options.scheme.use_time = 1; } /* Initialize location provider if needed. If provider is NULL @@ -1602,14 +978,15 @@ main(int argc, char *argv[]) /* Location is not needed for reset mode and manual mode. */ int need_location = - mode != PROGRAM_MODE_RESET && - mode != PROGRAM_MODE_MANUAL && - !scheme.use_time; + options.mode != PROGRAM_MODE_RESET && + options.mode != PROGRAM_MODE_MANUAL && + !options.scheme.use_time; if (need_location) { - if (provider != NULL) { + if (options.provider != NULL) { /* Use provider specified on command line. */ - r = provider_try_start(provider, &location_state, - &config_state, provider_args); + r = provider_try_start( + options.provider, &location_state, + &config_state, options.provider_args); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all providers, use the first that works. */ @@ -1630,13 +1007,13 @@ main(int argc, char *argv[]) /* Found provider that works. */ printf(_("Using provider `%s'.\n"), p->name); - provider = p; + options.provider = p; break; } /* Failure if no providers were successful at this point. */ - if (provider == NULL) { + if (options.provider == NULL) { fputs(_("No more location providers" " to try.\n"), stderr); exit(EXIT_FAILURE); @@ -1644,33 +1021,33 @@ main(int argc, char *argv[]) } /* Solar elevations */ - if (scheme.high < scheme.low) { + if (options.scheme.high < options.scheme.low) { fprintf(stderr, _("High transition elevation cannot be lower than" " the low transition elevation.\n")); exit(EXIT_FAILURE); } - if (verbose) { + if (options.verbose) { /* TRANSLATORS: Append degree symbols if possible. */ printf(_("Solar elevations: day above %.1f, night below %.1f\n"), - scheme.high, scheme.low); + options.scheme.high, options.scheme.low); } } - if (mode != PROGRAM_MODE_RESET && - mode != PROGRAM_MODE_MANUAL) { - if (verbose) { + if (options.mode != PROGRAM_MODE_RESET && + options.mode != PROGRAM_MODE_MANUAL) { + if (options.verbose) { printf(_("Temperatures: %dK at day, %dK at night\n"), - scheme.day.temperature, - scheme.night.temperature); + options.scheme.day.temperature, + options.scheme.night.temperature); } /* Color temperature */ - if (scheme.day.temperature < MIN_TEMP || - scheme.day.temperature > MAX_TEMP || - scheme.night.temperature < MIN_TEMP || - scheme.night.temperature > MAX_TEMP) { + if (options.scheme.day.temperature < MIN_TEMP || + options.scheme.day.temperature > MAX_TEMP || + options.scheme.night.temperature < MIN_TEMP || + options.scheme.night.temperature > MAX_TEMP) { fprintf(stderr, _("Temperature must be between %uK and %uK.\n"), MIN_TEMP, MAX_TEMP); @@ -1678,9 +1055,10 @@ main(int argc, char *argv[]) } } - if (mode == PROGRAM_MODE_MANUAL) { + if (options.mode == PROGRAM_MODE_MANUAL) { /* Check color temperature to be set */ - if (temp_set < MIN_TEMP || temp_set > MAX_TEMP) { + if (options.temp_set < MIN_TEMP || + options.temp_set > MAX_TEMP) { fprintf(stderr, _("Temperature must be between %uK and %uK.\n"), MIN_TEMP, MAX_TEMP); @@ -1689,52 +1067,57 @@ main(int argc, char *argv[]) } /* Brightness */ - if (scheme.day.brightness < MIN_BRIGHTNESS || - scheme.day.brightness > MAX_BRIGHTNESS || - scheme.night.brightness < MIN_BRIGHTNESS || - scheme.night.brightness > MAX_BRIGHTNESS) { + if (options.scheme.day.brightness < MIN_BRIGHTNESS || + options.scheme.day.brightness > MAX_BRIGHTNESS || + options.scheme.night.brightness < MIN_BRIGHTNESS || + options.scheme.night.brightness > MAX_BRIGHTNESS) { fprintf(stderr, _("Brightness values must be between %.1f and %.1f.\n"), MIN_BRIGHTNESS, MAX_BRIGHTNESS); exit(EXIT_FAILURE); } - if (verbose) { + if (options.verbose) { printf(_("Brightness: %.2f:%.2f\n"), - scheme.day.brightness, scheme.night.brightness); + options.scheme.day.brightness, + options.scheme.night.brightness); } /* Gamma */ - if (!gamma_is_valid(scheme.day.gamma) || - !gamma_is_valid(scheme.night.gamma)) { + if (!gamma_is_valid(options.scheme.day.gamma) || + !gamma_is_valid(options.scheme.night.gamma)) { fprintf(stderr, _("Gamma value must be between %.1f and %.1f.\n"), MIN_GAMMA, MAX_GAMMA); exit(EXIT_FAILURE); } - if (verbose) { + if (options.verbose) { /* TRANSLATORS: The string in parenthesis is either Daytime or Night (translated). */ printf(_("Gamma (%s): %.3f, %.3f, %.3f\n"), - _("Daytime"), scheme.day.gamma[0], - scheme.day.gamma[1], scheme.day.gamma[2]); + _("Daytime"), options.scheme.day.gamma[0], + options.scheme.day.gamma[1], + options.scheme.day.gamma[2]); printf(_("Gamma (%s): %.3f, %.3f, %.3f\n"), - _("Night"), scheme.night.gamma[0], - scheme.night.gamma[1], scheme.night.gamma[2]); + _("Night"), options.scheme.night.gamma[0], + options.scheme.night.gamma[1], + options.scheme.night.gamma[2]); } + transition_scheme_t *scheme = &options.scheme; + /* Initialize gamma adjustment method. If method is NULL try all methods until one that works is found. */ gamma_state_t *method_state; /* Gamma adjustment not needed for print mode */ - if (mode != PROGRAM_MODE_PRINT) { - if (method != NULL) { + if (options.mode != PROGRAM_MODE_PRINT) { + if (options.method != NULL) { /* Use method specified on command line. */ r = method_try_start( - method, &method_state, &config_state, - method_args); + options.method, &method_state, &config_state, + options.method_args); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all methods, use the first that works. */ @@ -1751,12 +1134,12 @@ main(int argc, char *argv[]) /* Found method that works. */ printf(_("Using method `%s'.\n"), m->name); - method = m; + options.method = m; break; } /* Failure if no methods were successful at this point. */ - if (method == NULL) { + if (options.method == NULL) { fputs(_("No more methods to try.\n"), stderr); exit(EXIT_FAILURE); } @@ -1765,7 +1148,7 @@ main(int argc, char *argv[]) config_ini_free(&config_state); - switch (mode) { + switch (options.mode) { case PROGRAM_MODE_ONE_SHOT: case PROGRAM_MODE_PRINT: { @@ -1776,7 +1159,7 @@ main(int argc, char *argv[]) /* Wait for location provider. */ int r = provider_get_location( - provider, location_state, -1, &loc); + options.provider, location_state, -1, &loc); if (r < 0) { fputs(_("Unable to get location" " from provider.\n"), stderr); @@ -1794,39 +1177,39 @@ main(int argc, char *argv[]) r = systemtime_get_time(&now); if (r < 0) { fputs(_("Unable to read system time.\n"), stderr); - method->free(method_state); + options.method->free(method_state); exit(EXIT_FAILURE); } period_t period; double transition_prog; - if (scheme.use_time) { + if (options.scheme.use_time) { int time_offset = get_seconds_since_midnight(now); - period = get_period_from_time(&scheme, time_offset); + period = get_period_from_time(scheme, time_offset); transition_prog = get_transition_progress_from_time( - &scheme, time_offset); + scheme, time_offset); } else { /* Current angular elevation of the sun */ double elevation = solar_elevation( now, loc.lat, loc.lon); - if (verbose) { + if (options.verbose) { /* TRANSLATORS: Append degree symbol if possible. */ printf(_("Solar elevation: %f\n"), elevation); } - period = get_period_from_elevation(&scheme, elevation); + period = get_period_from_elevation(scheme, elevation); transition_prog = get_transition_progress_from_elevation( - &scheme, elevation); + scheme, elevation); } /* Use transition progress to set color temperature */ color_setting_t interp; interpolate_transition_scheme( - &scheme, transition_prog, &interp); + scheme, transition_prog, &interp); - if (verbose || mode == PROGRAM_MODE_PRINT) { + if (options.verbose || options.mode == PROGRAM_MODE_PRINT) { print_period(period, transition_prog); printf(_("Color temperature: %uK\n"), interp.temperature); @@ -1834,13 +1217,14 @@ main(int argc, char *argv[]) interp.brightness); } - if (mode != PROGRAM_MODE_PRINT) { + if (options.mode != PROGRAM_MODE_PRINT) { /* Adjust temperature */ - r = method->set_temperature(method_state, &interp); + r = options.method->set_temperature( + method_state, &interp); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); - method->free(method_state); + options.method->free(method_state); exit(EXIT_FAILURE); } @@ -1848,7 +1232,7 @@ main(int argc, char *argv[]) automatically revert when the process exits. Therefore, we have to loop until CTRL-C is received. */ - if (strcmp(method->name, "quartz") == 0) { + if (strcmp(options.method->name, "quartz") == 0) { fputs(_("Press ctrl-c to stop...\n"), stderr); pause(); } @@ -1857,22 +1241,25 @@ main(int argc, char *argv[]) break; case PROGRAM_MODE_MANUAL: { - if (verbose) printf(_("Color temperature: %uK\n"), temp_set); + if (options.verbose) { + printf(_("Color temperature: %uK\n"), + options.temp_set); + } /* Adjust temperature */ - color_setting_t manual = scheme.day; - manual.temperature = temp_set; - r = method->set_temperature(method_state, &manual); + color_setting_t manual = scheme->day; + manual.temperature = options.temp_set; + r = options.method->set_temperature(method_state, &manual); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); - method->free(method_state); + options.method->free(method_state); exit(EXIT_FAILURE); } /* 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(method->name, "quartz") == 0) { + if (strcmp(options.method->name, "quartz") == 0) { fputs(_("Press ctrl-c to stop...\n"), stderr); pause(); } @@ -1882,17 +1269,17 @@ main(int argc, char *argv[]) { /* Reset screen */ color_setting_t reset = { NEUTRAL_TEMP, { 1.0, 1.0, 1.0 }, 1.0 }; - r = method->set_temperature(method_state, &reset); + r = options.method->set_temperature(method_state, &reset); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); - method->free(method_state); + options.method->free(method_state); exit(EXIT_FAILURE); } /* 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(method->name, "quartz") == 0) { + if (strcmp(options.method->name, "quartz") == 0) { fputs(_("Press ctrl-c to stop...\n"), stderr); pause(); } @@ -1900,22 +1287,23 @@ main(int argc, char *argv[]) break; case PROGRAM_MODE_CONTINUAL: { - r = run_continual_mode(provider, location_state, &scheme, - method, method_state, - use_fade, verbose); + r = run_continual_mode( + options.provider, location_state, scheme, + options.method, method_state, + options.use_fade, options.verbose); if (r < 0) exit(EXIT_FAILURE); } break; } /* Clean up gamma adjustment state */ - if (mode != PROGRAM_MODE_PRINT) { - method->free(method_state); + if (options.mode != PROGRAM_MODE_PRINT) { + options.method->free(method_state); } /* Clean up location provider state */ if (need_location) { - provider->free(location_state); + options.provider->free(location_state); } return EXIT_SUCCESS; diff --git a/src/redshift.h b/src/redshift.h index 98f9f37..f597c67 100644 --- a/src/redshift.h +++ b/src/redshift.h @@ -23,6 +23,9 @@ #include <stdio.h> #include <stdlib.h> +/* The color temperature when no adjustment is applied. */ +#define NEUTRAL_TEMP 6500 + /* Location */ typedef struct { @@ -45,6 +48,35 @@ typedef struct { float brightness; } color_setting_t; +/* Program modes. */ +typedef enum { + PROGRAM_MODE_CONTINUAL, + PROGRAM_MODE_ONE_SHOT, + PROGRAM_MODE_PRINT, + PROGRAM_MODE_RESET, + PROGRAM_MODE_MANUAL +} program_mode_t; + +/* Time range. + Fields are offsets from midnight in seconds. */ +typedef struct { + int start; + int end; +} time_range_t; + +/* Transition scheme. + The solar elevations at which the transition begins/ends, + and the association color settings. */ +typedef struct { + double high; + double low; + int use_time; /* When enabled, ignore elevation and use time ranges. */ + time_range_t dawn; + time_range_t dusk; + color_setting_t day; + color_setting_t night; +} transition_scheme_t; + /* Gamma adjustment method */ typedef struct gamma_state gamma_state_t; |