int main(int argc, char *argv[]) { int r; #ifdef ENABLE_NLS /* Init locale */ setlocale(LC_CTYPE, ""); setlocale(LC_MESSAGES, ""); /* Internationalisation */ bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif /* 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_day = NAN; float brightness_night = NAN; const gamma_method_t *method = NULL; char *method_args = NULL; const location_provider_t *provider = NULL; char *provider_args = NULL; int transition = -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, &brightness_day, &brightness_night); break; case 'c': if (config_filepath != NULL) free(config_filepath); config_filepath = strdup(optarg); break; case 'g': 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': 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(); 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(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(); 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(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': transition = 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'; temp_day = atoi(optarg); temp_night = 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; } } /* 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_day)) { brightness_day = atof(setting->value); } if (isnan(brightness_night)) { brightness_night = atof(setting->value); } } else if (strcasecmp(setting->name, "brightness-day") == 0) { if (isnan(brightness_day)) { brightness_day = atof(setting->value); } } else if (strcasecmp(setting->name, "brightness-night") == 0) { if (isnan(brightness_night)) { brightness_night = atof(setting->value); } } else if (strcasecmp(setting->name, "elevation-high") == 0) { transition_high = atof(setting->value); } else if (strcasecmp(setting->name, "elevation-low") == 0) { transition_low = 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_day)) brightness_day = DEFAULT_BRIGHTNESS; if (isnan(brightness_night)) brightness_night = 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 and manual mode. */ 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, &config_state, provider_args); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all providers, use the first that works. */ for (int i = 0; location_providers[i].name != NULL; i++) { const location_provider_t *p = &location_providers[i]; fprintf(stderr, _("Trying location provider `%s'...\n"), p->name); r = provider_try_start(p, &location_state, &config_state, NULL); if (r < 0) { fputs(_("Trying next provider...\n"), stderr); continue; } /* Found provider that works. */ printf(_("Using provider `%s'.\n"), p->name); provider = p; break; } /* Failure if no providers were successful at this point. */ if (provider == NULL) { fputs(_("No more location providers" " to try.\n"), stderr); exit(EXIT_FAILURE); } } /* 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); } provider->free(&location_state); if (verbose) { /* TRANSLATORS: Append degree symbols if possible. */ printf(_("Location: %f, %f\n"), lat, lon); printf(_("Temperatures: %dK at day, %dK at night\n"), temp_day, temp_night); /* TRANSLATORS: Append degree symbols if possible. */ printf(_("Solar elevations: day above %.1f, night below %.1f\n"), transition_high, transition_low); } /* Latitude */ if (lat < MIN_LAT || lat > MAX_LAT) { /* 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, _("Temperature must be between %uK and %uK.\n"), MIN_TEMP, MAX_TEMP); exit(EXIT_FAILURE); } /* Solar elevations */ if (transition_high < transition_low) { fprintf(stderr, _("High transition elevation cannot be lower than" " the low transition elevation.\n")); exit(EXIT_FAILURE); } } if (mode == PROGRAM_MODE_MANUAL) { /* Check color temperature to be set */ if (temp_set < MIN_TEMP || temp_set > MAX_TEMP) { fprintf(stderr, _("Temperature must be between %uK and %uK.\n"), MIN_TEMP, MAX_TEMP); exit(EXIT_FAILURE); } } /* Brightness */ if (brightness_day < MIN_BRIGHTNESS || brightness_day > MAX_BRIGHTNESS || brightness_night < MIN_BRIGHTNESS || brightness_night > MAX_BRIGHTNESS) { fprintf(stderr, _("Brightness values must be between %.1f and %.1f.\n"), MIN_BRIGHTNESS, MAX_BRIGHTNESS); exit(EXIT_FAILURE); } if (verbose) { printf(_("Brightness: %.2f:%.2f\n"), brightness_day, brightness_night); } /* Gamma */ if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { fprintf(stderr, _("Gamma value must be between %.1f and %.1f.\n"), MIN_GAMMA, MAX_GAMMA); exit(EXIT_FAILURE); } if (verbose) { printf(_("Gamma: %.3f, %.3f, %.3f\n"), gamma[0], gamma[1], gamma[2]); } /* Initialize gamma adjustment method. If method is NULL try all methods until one that works is found. */ gamma_state_t state; /* Gamma adjustment not needed for print mode */ if (mode != PROGRAM_MODE_PRINT) { if (method != NULL) { /* Use method specified on command line. */ 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]; if (!m->autostart) continue; r = method_try_start(m, &state, &config_state, NULL); if (r < 0) { fputs(_("Trying next method...\n"), stderr); continue; } /* Found method that works. */ printf(_("Using method `%s'.\n"), m->name); method = m; break; } /* Failure if no methods were successful at this point. */ if (method == NULL) { fputs(_("No more methods to try.\n"), stderr); exit(EXIT_FAILURE); } } } config_ini_free(&config_state); switch (mode) { case PROGRAM_MODE_ONE_SHOT: case PROGRAM_MODE_PRINT: { /* Current angular elevation of the sun */ double now; r = systemtime_get_time(&now); if (r < 0) { fputs(_("Unable to read system time.\n"), stderr); method->free(&state); exit(EXIT_FAILURE); } double elevation = solar_elevation(now, lat, lon); if (verbose) { /* TRANSLATORS: Append degree symbol if possible. */ printf(_("Solar elevation: %f\n"), elevation); } /* Use elevation of sun to set color temperature */ int temp = (int)calculate_interpolated_value(elevation, temp_day, temp_night); float brightness = calculate_interpolated_value(elevation, brightness_day, brightness_night); if (verbose || mode == PROGRAM_MODE_PRINT) { print_period(elevation); printf(_("Color temperature: %uK\n"), temp); printf(_("Brightness: %.2f\n"), brightness); } if (mode == PROGRAM_MODE_PRINT) { exit(EXIT_SUCCESS); } /* Adjust temperature */ 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_day, 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, 1.0, gamma); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&state); exit(EXIT_FAILURE); } } break; case PROGRAM_MODE_CONTINUAL: { /* Make an initial transition from 6500K */ int short_trans_delta = -1; int short_trans_len = 10; /* Amount of adjustment to apply. At zero the color temperature will be exactly as calculated, and at one it will be exactly 6500K. */ double adjustment_alpha = 1.0; #if defined(HAVE_SIGNAL_H) && !defined(__WIN32__) struct sigaction sigact; sigset_t sigset; sigemptyset(&sigset); /* Install signal handler for INT and TERM signals */ sigact.sa_handler = sigexit; sigact.sa_mask = sigset; sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); /* Install signal handler for USR1 singal */ sigact.sa_handler = sigdisable; sigact.sa_mask = sigset; sigact.sa_flags = 0; sigaction(SIGUSR1, &sigact, NULL); #endif /* HAVE_SIGNAL_H && ! __WIN32__ */ if (verbose) { printf("Status: %s\n", "Enabled"); } /* Continuously adjust color temperature */ int done = 0; int disabled = 0; while (1) { /* Check to see if disable signal was caught */ if (disable) { short_trans_len = 2; if (!disabled) { /* Transition to disabled state */ short_trans_delta = 1; } else { /* Transition back to enabled */ short_trans_delta = -1; } disabled = !disabled; disable = 0; if (verbose) { printf("Status: %s\n", disabled ? "Disabled" : "Enabled"); } } /* Check to see if exit signal was caught */ if (exiting) { if (done) { /* On second signal stop the ongoing transition */ short_trans_delta = 0; adjustment_alpha = 0.0; } else { if (!disabled) { /* Make a short transition back to 6500K */ short_trans_delta = 1; short_trans_len = 2; } done = 1; } exiting = 0; } /* Read timestamp */ double now; r = systemtime_get_time(&now); if (r < 0) { fputs(_("Unable to read system time.\n"), stderr); method->free(&state); exit(EXIT_FAILURE); } /* Skip over transition if transitions are disabled */ int set_adjustments = 0; if (!transition) { if (short_trans_delta) { adjustment_alpha = short_trans_delta < 0 ? 0.0 : 1.0; short_trans_delta = 0; set_adjustments = 1; } } /* Current angular elevation of the sun */ double elevation = solar_elevation(now, lat, lon); /* Use elevation of sun to set color temperature */ int temp = (int)calculate_interpolated_value(elevation, temp_day, temp_night); float brightness = calculate_interpolated_value(elevation, brightness_day, brightness_night); if (verbose) print_period(elevation); /* Ongoing short transition */ if (short_trans_delta) { /* Calculate alpha */ adjustment_alpha += short_trans_delta * 0.1 / (float)short_trans_len; /* Stop transition when done */ if (adjustment_alpha <= 0.0 || adjustment_alpha >= 1.0) { short_trans_delta = 0; } /* Clamp alpha value */ adjustment_alpha = MAX(0.0, MIN(adjustment_alpha, 1.0)); } /* Interpolate between 6500K and calculated temperature */ temp = adjustment_alpha*6500 + (1.0-adjustment_alpha)*temp; brightness = adjustment_alpha*1.0 + (1.0-adjustment_alpha)*brightness; /* Quit loop when done */ if (done && !short_trans_delta) break; if (verbose) { printf(_("Color temperature: %uK\n"), temp); printf(_("Brightness: %.2f\n"), brightness); } /* Adjust temperature */ if (!disabled || short_trans_delta || set_adjustments) { r = method->set_temperature(&state, temp, brightness, gamma); if (r < 0) { fputs(_("Temperature adjustment" " failed.\n"), stderr); method->free(&state); exit(EXIT_FAILURE); } } /* Sleep for 5 seconds or 0.1 second. */ #ifndef _WIN32 if (short_trans_delta) usleep(100000); else usleep(5000000); #else /* ! _WIN32 */ if (short_trans_delta) Sleep(100); else Sleep(5000); #endif /* ! _WIN32 */ } /* Restore saved gamma ramps */ method->restore(&state); } break; } /* Clean up gamma adjustment state */ method->free(&state); return EXIT_SUCCESS; }
static int provider_try_start(const location_provider_t *provider, location_state_t *state, config_ini_state_t *config, char *args) { int r; r = provider->init(state); if (r < 0) { fprintf(stderr, _("Initialization of %s failed.\n"), provider->name); return -1; } /* 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. */ const char *manual_keys[] = { "lat", "lon" }; int i = 0; while (args != NULL) { char *next_arg = strchr(args, ':'); if (next_arg != NULL) *(next_arg++) = '\0'; const char *key = args; char *value = strchr(args, '='); if (value == NULL) { /* The options for the "manual" method can be set without keys on the command line for convencience and for backwards compatability. We add the proper keys here before calling set_option(). */ if (strcmp(provider->name, "manual") == 0 && i < sizeof(manual_keys)/sizeof(manual_keys[0])) { key = manual_keys[i]; value = args; } else { fprintf(stderr, _("Failed to parse option `%s'.\n"), args); return -1; } } else { *(value++) = '\0'; } r = provider->set_option(state, key, 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; } args = next_arg; i += 1; } /* Start provider. */ r = provider->start(state); if (r < 0) { provider->free(state); fprintf(stderr, _("Failed to start provider %s.\n"), provider->name); return -1; } return 0; }
static int method_try_start(const gamma_method_t *method, gamma_state_t *state, config_ini_state_t *config, char *args) { int r; r = method->init(state); if (r < 0) { fprintf(stderr, _("Initialization of %s failed.\n"), method->name); return -1; } /* 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'; const char *key = args; char *value = strchr(args, '='); if (value == NULL) { fprintf(stderr, _("Failed to parse option `%s'.\n"), args); return -1; } else { *(value++) = '\0'; } r = method->set_option(state, key, 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; } args = next_arg; } /* Start method. */ r = method->start(state); if (r < 0) { method->free(state); fprintf(stderr, _("Failed to start adjustment method %s.\n"), method->name); return -1; } return 0; }
int main(int argc, char *argv[]) { int r; #ifdef ENABLE_NLS /* Init locale */ setlocale(LC_CTYPE, ""); setlocale(LC_MESSAGES, ""); /* Internationalisation */ bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif /* Initialize settings to NULL values. */ char *config_filepath = NULL; /* Settings for day, night and transition. Initialized to indicate that the values are not set yet. */ transition_scheme_t scheme = { TRANSITION_HIGH, TRANSITION_LOW }; 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; const gamma_method_t *method = NULL; char *method_args = NULL; const location_provider_t *provider = NULL; char *provider_args = NULL; int transition = -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(); 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(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(); 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(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': transition = 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; } } /* 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); } 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 (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) { if (transition < 0) { transition = !!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( 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 (scheme.day.temperature < 0) { scheme.day.temperature = DEFAULT_DAY_TEMP; } if (scheme.night.temperature < 0) { scheme.night.temperature = DEFAULT_NIGHT_TEMP; } if (isnan(scheme.day.brightness)) { scheme.day.brightness = DEFAULT_BRIGHTNESS; } if (isnan(scheme.night.brightness)) { scheme.night.brightness = DEFAULT_BRIGHTNESS; } 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 (transition < 0) transition = 1; location_t loc = { NAN, 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 and manual mode. */ 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, &config_state, provider_args); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all providers, use the first that works. */ for (int i = 0; location_providers[i].name != NULL; i++) { const location_provider_t *p = &location_providers[i]; fprintf(stderr, _("Trying location provider `%s'...\n"), p->name); r = provider_try_start(p, &location_state, &config_state, NULL); if (r < 0) { fputs(_("Trying next provider...\n"), stderr); continue; } /* Found provider that works. */ printf(_("Using provider `%s'.\n"), p->name); provider = p; break; } /* Failure if no providers were successful at this point. */ if (provider == NULL) { fputs(_("No more location providers" " to try.\n"), stderr); exit(EXIT_FAILURE); } } /* Get current location. */ r = provider->get_location(&location_state, &loc); if (r < 0) { fputs(_("Unable to get location from provider.\n"), stderr); exit(EXIT_FAILURE); } provider->free(&location_state); if (verbose) { print_location(&loc); printf(_("Temperatures: %dK at day, %dK at night\n"), scheme.day.temperature, scheme.night.temperature); /* TRANSLATORS: Append degree symbols if possible. */ printf(_("Solar elevations: day above %.1f, night below %.1f\n"), scheme.high, scheme.low); } /* Latitude */ if (loc.lat < MIN_LAT || loc.lat > MAX_LAT) { /* 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 (loc.lon < MIN_LON || loc.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 */ if (scheme.day.temperature < MIN_TEMP || scheme.day.temperature > MAX_TEMP || scheme.night.temperature < MIN_TEMP || scheme.night.temperature > MAX_TEMP) { fprintf(stderr, _("Temperature must be between %uK and %uK.\n"), MIN_TEMP, MAX_TEMP); exit(EXIT_FAILURE); } /* Solar elevations */ if (scheme.high < scheme.low) { fprintf(stderr, _("High transition elevation cannot be lower than" " the low transition elevation.\n")); exit(EXIT_FAILURE); } } if (mode == PROGRAM_MODE_MANUAL) { /* Check color temperature to be set */ if (temp_set < MIN_TEMP || temp_set > MAX_TEMP) { fprintf(stderr, _("Temperature must be between %uK and %uK.\n"), MIN_TEMP, MAX_TEMP); exit(EXIT_FAILURE); } } /* Brightness */ if (scheme.day.brightness < MIN_BRIGHTNESS || scheme.day.brightness > MAX_BRIGHTNESS || scheme.night.brightness < MIN_BRIGHTNESS || 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) { printf(_("Brightness: %.2f:%.2f\n"), scheme.day.brightness, scheme.night.brightness); } /* Gamma */ if (!gamma_is_valid(scheme.day.gamma) || !gamma_is_valid(scheme.night.gamma)) { fprintf(stderr, _("Gamma value must be between %.1f and %.1f.\n"), MIN_GAMMA, MAX_GAMMA); exit(EXIT_FAILURE); } if (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]); printf(_("Gamma (%s): %.3f, %.3f, %.3f\n"), _("Night"), scheme.night.gamma[0], scheme.night.gamma[1], scheme.night.gamma[2]); } /* Initialize gamma adjustment method. If method is NULL try all methods until one that works is found. */ gamma_state_t state; /* Gamma adjustment not needed for print mode */ if (mode != PROGRAM_MODE_PRINT) { if (method != NULL) { /* Use method specified on command line. */ 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]; if (!m->autostart) continue; r = method_try_start(m, &state, &config_state, NULL); if (r < 0) { fputs(_("Trying next method...\n"), stderr); continue; } /* Found method that works. */ printf(_("Using method `%s'.\n"), m->name); method = m; break; } /* Failure if no methods were successful at this point. */ if (method == NULL) { fputs(_("No more methods to try.\n"), stderr); exit(EXIT_FAILURE); } } } config_ini_free(&config_state); switch (mode) { case PROGRAM_MODE_ONE_SHOT: case PROGRAM_MODE_PRINT: { /* Current angular elevation of the sun */ double now; r = systemtime_get_time(&now); if (r < 0) { fputs(_("Unable to read system time.\n"), stderr); method->free(&state); exit(EXIT_FAILURE); } double elevation = solar_elevation(now, loc.lat, loc.lon); if (verbose) { /* TRANSLATORS: Append degree symbol if possible. */ printf(_("Solar elevation: %f\n"), elevation); } /* Use elevation of sun to set color temperature */ color_setting_t interp; interpolate_color_settings(&scheme, elevation, &interp); if (verbose || mode == PROGRAM_MODE_PRINT) { period_t period = get_period(&scheme, elevation); double transition = get_transition_progress(&scheme, elevation); print_period(period, transition); printf(_("Color temperature: %uK\n"), interp.temperature); printf(_("Brightness: %.2f\n"), interp.brightness); } if (mode == PROGRAM_MODE_PRINT) { exit(EXIT_SUCCESS); } /* Adjust temperature */ r = method->set_temperature(&state, &interp); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&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) { fputs(_("Press ctrl-c to stop...\n"), stderr); pause(); } } break; case PROGRAM_MODE_MANUAL: { if (verbose) printf(_("Color temperature: %uK\n"), temp_set); /* Adjust temperature */ color_setting_t manual; memcpy(&manual, &scheme.day, sizeof(color_setting_t)); manual.temperature = temp_set; r = method->set_temperature(&state, &manual); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&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) { fputs(_("Press ctrl-c to stop...\n"), stderr); pause(); } } break; case PROGRAM_MODE_RESET: { /* Reset screen */ color_setting_t reset = { NEUTRAL_TEMP, { 1.0, 1.0, 1.0 }, 1.0 }; r = method->set_temperature(&state, &reset); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&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) { fputs(_("Press ctrl-c to stop...\n"), stderr); pause(); } } break; case PROGRAM_MODE_CONTINUAL: { r = run_continual_mode(&loc, &scheme, method, &state, transition, verbose); if (r < 0) exit(EXIT_FAILURE); } break; } /* Clean up gamma adjustment state */ method->free(&state); return EXIT_SUCCESS; }