diff options
Diffstat (limited to 'src/redshift.c')
-rw-r--r-- | src/redshift.c | 477 |
1 files changed, 369 insertions, 108 deletions
diff --git a/src/redshift.c b/src/redshift.c index 18cc2c5..af154b8 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -30,8 +30,8 @@ #include <locale.h> #include <errno.h> -#ifdef HAVE_SYS_SIGNAL_H -# include <sys/signal.h> +#ifdef HAVE_SIGNAL_H +# include <signal.h> #endif #ifdef ENABLE_NLS @@ -42,6 +42,7 @@ #endif #include "redshift.h" +#include "config-ini.h" #include "solar.h" #include "systemtime.h" @@ -75,6 +76,10 @@ # include "location-gnome-clock.h" #endif +#ifdef ENABLE_GEOCLUE +# include "location-geoclue.h" +#endif + /* Union of state data for gamma adjustment methods */ typedef union { @@ -138,11 +143,28 @@ typedef union { #ifdef ENABLE_GNOME_CLOCK location_gnome_clock_state_t gnome_clock; #endif +#ifdef ENABLE_GEOCLUE + location_geoclue_state_t geoclue; +#endif } location_state_t; /* Location provider method structs */ static const location_provider_t location_providers[] = { +#ifdef ENABLE_GEOCLUE + { + "geoclue", + (location_provider_init_func *)location_geoclue_init, + (location_provider_start_func *)location_geoclue_start, + (location_provider_free_func *)location_geoclue_free, + (location_provider_print_help_func *) + location_geoclue_print_help, + (location_provider_set_option_func *) + location_geoclue_set_option, + (location_provider_get_location_func *) + location_geoclue_get_location + }, +#endif #ifdef ENABLE_GNOME_CLOCK { "gnome-clock", @@ -179,12 +201,15 @@ static const location_provider_t location_providers[] = { #define MAX_LON 180.0 #define MIN_TEMP 1000 #define MAX_TEMP 10000 +#define MIN_BRIGHTNESS 0.1 +#define MAX_BRIGHTNESS 1.0 #define MIN_GAMMA 0.1 #define MAX_GAMMA 10.0 /* Default values for parameters. */ #define DEFAULT_DAY_TEMP 5500 #define DEFAULT_NIGHT_TEMP 3700 +#define DEFAULT_BRIGHTNESS 1.0 #define DEFAULT_GAMMA 1.0 /* The color temperature when no adjustment is applied. */ @@ -201,11 +226,12 @@ static const location_provider_t location_providers[] = { typedef enum { PROGRAM_MODE_CONTINUAL, PROGRAM_MODE_ONE_SHOT, - PROGRAM_MODE_RESET + PROGRAM_MODE_RESET, + PROGRAM_MODE_MANUAL } program_mode_t; -#ifdef HAVE_SYS_SIGNAL_H +#ifdef HAVE_SIGNAL_H static volatile sig_atomic_t exiting = 0; static volatile sig_atomic_t disable = 0; @@ -224,12 +250,12 @@ sigdisable(int signo) disable = 1; } -#else /* ! HAVE_SYS_SIGNAL_H */ +#else /* ! HAVE_SIGNAL_H */ static int exiting = 0; static int disable = 0; -#endif /* ! HAVE_SYS_SIGNAL_H */ +#endif /* ! HAVE_SIGNAL_H */ /* Calculate color temperature for the specified solar elevation. */ @@ -285,7 +311,8 @@ print_help(const char *program_name) /* TRANSLATORS: help output 4 `list' must not be translated no-wrap */ - fputs(_(" -g R:G:B\tAdditional gamma correction to apply\n" + fputs(_(" -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" @@ -294,6 +321,7 @@ print_help(const char *program_name) " \t\t(Type `list' to see available methods)\n" " -o\t\tOne shot mode (do not continously adjust" " color temperature)\n" + " -O TEMP\tOne shot manual mode (set color temperature)\n" " -x\t\tReset mode (remove adjustment from screen)\n" " -r\t\tDisable temperature transitions\n" " -t DAY:NIGHT\tColor temperature to set at daytime/night\n"), @@ -301,6 +329,16 @@ print_help(const char *program_name) fputs("\n", stdout); /* TRANSLATORS: help output 5 */ + printf(_("The neutral temperature is %uK. Using this value will not\n" + "change the color temperature of the display. Setting the\n" + "color temperature to a value higher than this results in\n" + "more blue light, and setting a lower value will result in\n" + "more 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"), @@ -308,7 +346,7 @@ print_help(const char *program_name) fputs("\n", stdout); - /* TRANSLATORS: help output 6 */ + /* TRANSLATORS: help output 7 */ printf(_("Please report bugs to <%s>\n"), PACKAGE_BUGREPORT); } @@ -345,7 +383,8 @@ print_provider_list() static int provider_try_start(const location_provider_t *provider, - location_state_t *state, char *args) + location_state_t *state, + config_ini_state_t *config, char *args) { int r; @@ -356,7 +395,31 @@ provider_try_start(const location_provider_t *provider, return -1; } - /* Set provider options. */ + /* Set provider options from config file. */ + config_ini_section_t *section = + config_ini_get_section(config, provider->name); + if (section != NULL) { + config_ini_setting_t *setting = section->settings; + while (setting != NULL) { + r = provider->set_option(state, setting->name, + setting->value); + if (r < 0) { + provider->free(state); + fprintf(stderr, _("Failed to set %s" + " option.\n"), + provider->name); + /* TRANSLATORS: `help' must not be + translated. */ + fprintf(stderr, _("Try `-l %s:help' for more" + " information.\n"), + provider->name); + return -1; + } + setting = setting->next; + } + } + + /* Set provider options from command line. */ while (args != NULL) { char *next_arg = strchr(args, ':'); if (next_arg != NULL) *(next_arg++) = '\0'; @@ -398,7 +461,8 @@ provider_try_start(const location_provider_t *provider, static int method_try_start(const gamma_method_t *method, - gamma_state_t *state, char *args) + gamma_state_t *state, + config_ini_state_t *config, char *args) { int r; @@ -409,7 +473,31 @@ method_try_start(const gamma_method_t *method, return -1; } - /* Set method options. */ + /* Set method options from config file. */ + config_ini_section_t *section = + config_ini_get_section(config, method->name); + if (section != NULL) { + config_ini_setting_t *setting = section->settings; + while (setting != NULL) { + r = method->set_option(state, setting->name, + setting->value); + if (r < 0) { + method->free(state); + fprintf(stderr, _("Failed to set %s" + " option.\n"), + method->name); + /* TRANSLATORS: `help' must not be + translated. */ + fprintf(stderr, _("Try `-m %s:help' for more" + " information.\n"), + method->name); + return -1; + } + setting = setting->next; + } + } + + /* Set method options from command line. */ while (args != NULL) { char *next_arg = strchr(args, ':'); if (next_arg != NULL) *(next_arg++) = '\0'; @@ -449,6 +537,62 @@ 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; +} + +static const gamma_method_t * +find_gamma_method(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 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; +} + int main(int argc, char *argv[]) @@ -465,10 +609,14 @@ main(int argc, char *argv[]) textdomain(PACKAGE); #endif - /* Initialize to defaults */ - int temp_day = DEFAULT_DAY_TEMP; - int temp_night = DEFAULT_NIGHT_TEMP; - float gamma[3] = { DEFAULT_GAMMA, DEFAULT_GAMMA, DEFAULT_GAMMA }; + /* Initialize settings to NULL values. */ + char *config_filepath = NULL; + + int temp_set = -1; + int temp_day = -1; + int temp_night = -1; + float gamma[3] = { NAN, NAN, NAN }; + float brightness = NAN; const gamma_method_t *method = NULL; char *method_args = NULL; @@ -476,38 +624,30 @@ main(int argc, char *argv[]) const location_provider_t *provider = NULL; char *provider_args = NULL; - int transition = 1; + int transition = -1; program_mode_t mode = PROGRAM_MODE_CONTINUAL; int verbose = 0; char *s; - /* Parse arguments. */ + /* Parse command line arguments. */ int opt; - while ((opt = getopt(argc, argv, "g:hl:m:ort:vx")) != -1) { + while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:rt:vx")) != -1) { switch (opt) { + case 'b': + brightness = atof(optarg); + break; + case 'c': + if (config_filepath != NULL) free(config_filepath); + config_filepath = strdup(optarg); + break; case 'g': - s = strchr(optarg, ':'); - if (s == NULL) { - /* Use value for all channels */ - float g = atof(optarg); - gamma[0] = gamma[1] = gamma[2] = g; - } else { - /* Parse separate value for each channel */ - *(s++) = '\0'; - gamma[0] = atof(optarg); /* Red */ - char *g_s = s; - s = strchr(s, ':'); - if (s == NULL) { - fputs(_("Malformed gamma argument.\n"), - stderr); - fputs(_("Try `-h' for more" - " information.\n"), stderr); - exit(EXIT_FAILURE); - } - - *(s++) = '\0'; - gamma[1] = atof(g_s); /* Blue */ - gamma[2] = atof(s); /* Green */ + r = parse_gamma_string(optarg, gamma); + if (r < 0) { + fputs(_("Malformed gamma argument.\n"), + stderr); + fputs(_("Try `-h' for more" + " information.\n"), stderr); + exit(EXIT_FAILURE); } break; case 'h': @@ -543,16 +683,8 @@ main(int argc, char *argv[]) provider_name = optarg; } - /* Lookup argument in location provider table */ - for (int i = 0; location_providers[i].name != NULL; - i++) { - const location_provider_t *p = - &location_providers[i]; - if (strcasecmp(provider_name, p->name) == 0) { - provider = p; - } - } - + /* Lookup provider from name. */ + provider = find_location_provider(provider_name); if (provider == NULL) { fprintf(stderr, _("Unknown location provider" " `%s'.\n"), provider_name); @@ -563,7 +695,7 @@ main(int argc, char *argv[]) if (provider_args != NULL && strcasecmp(provider_args, "help") == 0) { provider->print_help(stdout); - exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); } break; case 'm': @@ -580,20 +712,13 @@ main(int argc, char *argv[]) method_args = s; } - /* Lookup argument in gamma methods table */ - for (int i = 0; gamma_methods[i].name != NULL; i++) { - const gamma_method_t *m = - &gamma_methods[i]; - if (strcasecmp(optarg, m->name) == 0) { - method = m; - } - } - + /* Find adjustment method by name. */ + method = find_gamma_method(optarg); if (method == NULL) { /* TRANSLATORS: This refers to the method used to adjust colors e.g VidMode */ - fprintf(stderr, _("Unknown method `%s'.\n"), - optarg); + fprintf(stderr, _("Unknown adjustment method" + " `%s'.\n"), optarg); exit(EXIT_FAILURE); } @@ -601,12 +726,16 @@ main(int argc, char *argv[]) if (method_args != NULL && strcasecmp(method_args, "help") == 0) { method->print_help(stdout); - exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); } break; case 'o': mode = PROGRAM_MODE_ONE_SHOT; break; + case 'O': + mode = PROGRAM_MODE_MANUAL; + temp_set = atoi(optarg); + break; case 'r': transition = 0; break; @@ -636,16 +765,111 @@ main(int argc, char *argv[]) } } + /* Load settings from config file. */ + config_ini_state_t config_state; + r = config_ini_init(&config_state, config_filepath); + if (r < 0) { + fputs("Unable to load config file.\n", stderr); + exit(EXIT_FAILURE); + } + + if (config_filepath != NULL) free(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 (temp_day < 0) { + temp_day = atoi(setting->value); + } + } else if (strcasecmp(setting->name, + "temp-night") == 0) { + if (temp_night < 0) { + temp_night = atoi(setting->value); + } + } else if (strcasecmp(setting->name, + "transition") == 0) { + if (transition < 0) { + transition = !!atoi(setting->value); + } + } else if (strcasecmp(setting->name, + "brightness") == 0) { + if (isnan(brightness)) { + brightness = atof(setting->value); + } + } else if (strcasecmp(setting->name, "gamma") == 0) { + if (isnan(gamma[0])) { + r = parse_gamma_string(setting->value, + 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( + 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( + setting->value); + if (provider == NULL) { + fprintf(stderr, _("Unknown" + " location" + " provider" + " `%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 (temp_day < 0) temp_day = DEFAULT_DAY_TEMP; + if (temp_night < 0) temp_night = DEFAULT_NIGHT_TEMP; + if (isnan(brightness)) brightness = DEFAULT_BRIGHTNESS; + if (isnan(gamma[0])) gamma[0] = gamma[1] = gamma[2] = DEFAULT_GAMMA; + if (transition < 0) transition = 1; + + float lat = NAN; + float lon = NAN; + /* Initialize location provider. If provider is NULL try all providers until one that works is found. */ location_state_t location_state; - /* Location is not needed for reset mode. */ - if (mode != PROGRAM_MODE_RESET) { + /* Location is not needed for reset mode + or for manual temperature setting. */ + if (mode != PROGRAM_MODE_RESET && mode != PROGRAM_MODE_MANUAL) { if (provider != NULL) { /* Use provider specified on command line. */ r = provider_try_start(provider, &location_state, - provider_args); + &config_state, provider_args); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all providers, use the first that works. */ @@ -654,13 +878,15 @@ main(int argc, char *argv[]) const location_provider_t *p = &location_providers[i]; r = provider_try_start(p, &location_state, - NULL); + &config_state, NULL); if (r < 0) { - fputs(_("Trying other provider...\n"), + fputs(_("Trying next provider...\n"), stderr); continue; } + /* Found provider that works. */ + printf(_("Using provider `%s'.\n"), p->name); provider = p; break; } @@ -673,60 +899,77 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } } - } - - float lat = NAN; - float lon = NAN; - if (mode != PROGRAM_MODE_RESET) { /* Get current location. */ r = provider->get_location(&location_state, &lat, &lon); if (r < 0) { - fputs(_("Unable to get location from provider.\n"), - stderr); - exit(EXIT_FAILURE); + fputs(_("Unable to get location from provider.\n"), + stderr); + exit(EXIT_FAILURE); } - + provider->free(&location_state); - + if (verbose) { - /* TRANSLATORS: Append degree symbols if possible. */ - printf(_("Location: %f, %f\n"), lat, lon); + /* TRANSLATORS: Append degree symbols if possible. */ + printf(_("Location: %f, %f\n"), lat, lon); } - + /* Latitude */ if (lat < MIN_LAT || lat > MAX_LAT) { - /* TRANSLATORS: Append degree symbols if possible. */ + /* TRANSLATORS: Append degree symbols if possible. */ + fprintf(stderr, + _("Latitude must be between %.1f and %.1f.\n"), + MIN_LAT, MAX_LAT); + exit(EXIT_FAILURE); + } + + /* Longitude */ + if (lon < MIN_LON || lon > MAX_LON) { + /* TRANSLATORS: Append degree symbols if possible. */ + fprintf(stderr, + _("Longitude must be between" + " %.1f and %.1f.\n"), MIN_LON, MAX_LON); + exit(EXIT_FAILURE); + } + + /* Color temperature at daytime */ + if (temp_day < MIN_TEMP || temp_day >= MAX_TEMP) { + fprintf(stderr, + _("Temperature must be between %uK and %uK.\n"), + MIN_TEMP, MAX_TEMP); + exit(EXIT_FAILURE); + } + + /* Color temperature at night */ + if (temp_night < MIN_TEMP || temp_night >= MAX_TEMP) { fprintf(stderr, - _("Latitude must be between %.1f and %.1f.\n"), - MIN_LAT, MAX_LAT); + _("Temperature must be between %uK and %uK.\n"), + MIN_TEMP, MAX_TEMP); exit(EXIT_FAILURE); } + } - /* Longitude */ - if (lon < MIN_LON || lon > MAX_LON) { - /* TRANSLATORS: Append degree symbols if possible. */ + if (mode == PROGRAM_MODE_MANUAL) { + /* Check color temperature to be set */ + if (temp_set < MIN_TEMP || temp_set >= MAX_TEMP) { fprintf(stderr, - _("Longitude must be between" - " %.1f and %.1f.\n"), MIN_LON, MAX_LON); + _("Temperature must be between %uK and %uK.\n"), + MIN_TEMP, MAX_TEMP); exit(EXIT_FAILURE); } } - /* Color temperature at daytime */ - if (temp_day < MIN_TEMP || temp_day >= MAX_TEMP) { + /* Brightness */ + if (brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS) { fprintf(stderr, - _("Temperature must be between %uK and %uK.\n"), - MIN_TEMP, MAX_TEMP); + _("Brightness value must be between %.1f and %.1f.\n"), + MIN_BRIGHTNESS, MAX_BRIGHTNESS); exit(EXIT_FAILURE); } - /* Color temperature at night */ - if (temp_night < MIN_TEMP || temp_night >= MAX_TEMP) { - fprintf(stderr, - _("Temperature must be between %uK and %uK.\n"), - MIN_TEMP, MAX_TEMP); - exit(EXIT_FAILURE); + if (verbose) { + printf(_("Brightness: %.2f\n"), brightness); } /* Gamma */ @@ -750,18 +993,21 @@ main(int argc, char *argv[]) if (method != NULL) { /* Use method specified on command line. */ - r = method_try_start(method, &state, method_args); + r = method_try_start(method, &state, &config_state, + method_args); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all methods, use the first that works. */ for (int i = 0; gamma_methods[i].name != NULL; i++) { const gamma_method_t *m = &gamma_methods[i]; - r = method_try_start(m, &state, NULL); + r = method_try_start(m, &state, &config_state, NULL); if (r < 0) { - fputs(_("Trying other method...\n"), stderr); + fputs(_("Trying next method...\n"), stderr); continue; } + /* Found method that works. */ + printf(_("Using method `%s'.\n"), m->name); method = m; break; } @@ -799,18 +1045,32 @@ main(int argc, char *argv[]) if (verbose) printf(_("Color temperature: %uK\n"), temp); /* Adjust temperature */ - r = method->set_temperature(&state, temp, gamma); + r = method->set_temperature(&state, temp, brightness, gamma); + if (r < 0) { + fputs(_("Temperature adjustment failed.\n"), stderr); + method->free(&state); + exit(EXIT_FAILURE); + } + } + break; + case PROGRAM_MODE_MANUAL: + { + if (verbose) printf(_("Color temperature: %uK\n"), temp_set); + + /* Adjust temperature */ + r = method->set_temperature(&state, temp_set, brightness, gamma); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&state); exit(EXIT_FAILURE); } + } break; case PROGRAM_MODE_RESET: { /* Reset screen */ - r = method->set_temperature(&state, NEUTRAL_TEMP, gamma); + r = method->set_temperature(&state, NEUTRAL_TEMP, 1.0, gamma); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&state); @@ -835,7 +1095,7 @@ main(int argc, char *argv[]) will be exactly 6500K. */ float adjustment_alpha = 0.0; -#ifdef HAVE_SYS_SIGNAL_H +#ifdef HAVE_SIGNAL_H struct sigaction sigact; sigset_t sigset; sigemptyset(&sigset); @@ -852,7 +1112,7 @@ main(int argc, char *argv[]) sigact.sa_mask = sigset; sigact.sa_flags = 0; sigaction(SIGUSR1, &sigact, NULL); -#endif /* HAVE_SYS_SIGNAL_H */ +#endif /* HAVE_SIGNAL_H */ /* Continously adjust color temperature */ int done = 0; @@ -974,7 +1234,8 @@ main(int argc, char *argv[]) /* Adjust temperature */ if (!disabled || short_trans) { r = method->set_temperature(&state, - temp, gamma); + temp, brightness, + gamma); if (r < 0) { fputs(_("Temperature adjustment" " failed.\n"), stderr); |