/* redshift.c -- Main program source * This file is part of redshift-ng. * * redshift-ng is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * redshift-ng is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with redshift-ng. If not, see . * * Copyright (c) 2009-2017 Jon Lund Steffensen * Copyright (c) 2025 Mattias Andrée */ #include "common.h" #include "solar.h" /* poll.h is not available on Windows but there is no Windows location provider * using polling. On Windows, we just define some stubs to make things compile. */ #ifndef WINDOWS # include #else #define POLLIN 0 struct pollfd { int fd; short events; short revents; }; int poll(struct pollfd *fds, int nfds, int timeout) { abort(); return -1; } #endif /* Bounds for parameters. */ #define MIN_LAT -90.0 #define MAX_LAT 90.0 #define MIN_LON -180.0 #define MAX_LON 180.0 #define MIN_TEMP 1000 #define MAX_TEMP 25000 #define MIN_BRIGHTNESS 0.1 #define MAX_BRIGHTNESS 1.0 #define MIN_GAMMA 0.1 #define MAX_GAMMA 10.0 /* Duration of sleep between screen updates (milliseconds). */ #define SLEEP_DURATION 5000 #define SLEEP_DURATION_SHORT 100 /* Length of fade in numbers of short sleep durations. */ #define FADE_LENGTH 40 /* Names of periods of day */ static const char *period_names[] = { /* TRANSLATORS: Name printed when period of day is unknown */ N_("None"), N_("Daytime"), N_("Night"), N_("Transition") }; #if defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wfloat-equal" #endif static int exact_eq(double a, double b) { return a == b; } #if defined(__GNUC__) # pragma GCC diagnostic pop #endif /* Determine which period we are currently in based on time offset. */ static enum period get_period_from_time(const struct transition_scheme *transition, int time_offset) { if (time_offset < transition->dawn.start || time_offset >= transition->dusk.end) return PERIOD_NIGHT; else if (time_offset >= transition->dawn.end && time_offset < transition->dusk.start) return PERIOD_DAYTIME; else return PERIOD_TRANSITION; } /* Determine which period we are currently in based on solar elevation. */ static enum period get_period_from_elevation(const struct transition_scheme *transition, double elevation) { if (elevation < transition->low) return PERIOD_NIGHT; else if (elevation < transition->high) return PERIOD_TRANSITION; else return PERIOD_DAYTIME; } /* Determine how far through the transition we are based on time offset. */ static double get_transition_progress_from_time(const struct transition_scheme *transition, int time_offset) { if (time_offset < transition->dawn.start || time_offset >= transition->dusk.end) return 0.0; else if (time_offset < transition->dawn.end) return (transition->dawn.start - time_offset) / (double)(transition->dawn.start - transition->dawn.end); else if (time_offset > transition->dusk.start) return (transition->dusk.end - time_offset) / (double)(transition->dusk.end - transition->dusk.start); else return 1.0; } /* Determine how far through the transition we are based on elevation. */ static double get_transition_progress_from_elevation(const struct transition_scheme *transition, double elevation) { if (elevation < transition->low) return 0.0; else if (elevation < transition->high) return (transition->low - elevation) / (transition->low - transition->high); else return 1.0; } /* Return number of seconds since midnight from timestamp. */ static int get_seconds_since_midnight(double timestamp) { time_t t = (time_t)timestamp; struct tm tm; localtime_r(&t, &tm); return tm.tm_sec + tm.tm_min * 60 + tm.tm_hour * 3600; } /* Print verbose description of the given period. */ static void print_period(enum period period, double transition) { switch (period) { case PERIOD_NONE: case PERIOD_NIGHT: case PERIOD_DAYTIME: printf(_("Period: %s\n"), gettext(period_names[period])); break; case PERIOD_TRANSITION: printf(_("Period: %s (%.2f%% day)\n"), gettext(period_names[period]), transition * 100); break; } } /* Print location */ static void print_location(const struct location *location) { /* TRANSLATORS: Abbreviation for `north' */ const char *north = _("N"); /* TRANSLATORS: Abbreviation for `south' */ const char *south = _("S"); /* TRANSLATORS: Abbreviation for `east' */ const char *east = _("E"); /* TRANSLATORS: Abbreviation for `west' */ const char *west = _("W"); /* TRANSLATORS: Append degree symbols after %f if possible. The string following each number is an abreviation for north, source, east or west (N, S, E, W). */ printf(_("Location: %.2f %s, %.2f %s\n"), fabs(location->lat), location->lat >= 0.0 ? north : south, fabs(location->lon), location->lon >= 0.0 ? east : west); } /* Interpolate color setting structs given alpha. */ static void interpolate_color_settings(const struct color_setting *first, const struct color_setting *second, double alpha, struct color_setting *result) { int i; alpha = CLAMP(0.0, alpha, 1.0); result->temperature = (1.0 - alpha) * first->temperature + alpha * second->temperature; result->brightness = (1.0 - alpha) * first->brightness + alpha * second->brightness; for (i = 0; i < 3; i++) result->gamma[i] = (1.0 - alpha) * first->gamma[i] + alpha * second->gamma[i]; } /* Interpolate color setting structs transition scheme. */ static void interpolate_transition_scheme(const struct transition_scheme *transition, double alpha, struct color_setting *result) { interpolate_color_settings(&transition->night, &transition->day, alpha, result); } /* Return 1 if color settings have major differences, otherwise 0. Used to determine if a fade should be applied in continual mode. */ static int color_setting_diff_is_major(const struct color_setting *first, const struct color_setting *second) { return (abs(first->temperature - second->temperature) > 25 || fabs(first->brightness - second->brightness) > 0.1 || fabs(first->gamma[0] - second->gamma[0]) > 0.1 || fabs(first->gamma[1] - second->gamma[1]) > 0.1 || fabs(first->gamma[2] - second->gamma[2]) > 0.1); } /* Reset color setting to default values. */ static void color_setting_reset(struct color_setting *color) { color->temperature = NEUTRAL_TEMP; color->gamma[0] = 1.0; color->gamma[1] = 1.0; color->gamma[2] = 1.0; color->brightness = 1.0; } static int provider_try_start(const struct location_provider *provider, LOCATION_STATE **state, struct config_ini_state *config, char *args) { const char *manual_keys[] = {"lat", "lon"}; struct config_ini_section *section; struct config_ini_setting *setting; char *next_arg, *value; const char *key; int i; if (provider->init(state) < 0) { weprintf(_("Initialization of %s failed."), provider->name); return -1; } /* Set provider options from config file. */ if ((section = config_ini_get_section(config, provider->name))) { for (setting = section->settings; setting; setting = setting->next) { if (provider->set_option(*state, setting->name, setting->value) < 0) { provider->free(*state); weprintf(_("Failed to set %s option."), provider->name); /* TRANSLATORS: `help' must not be translated. */ weprintf(_("Try `-l %s:help' for more information."), provider->name); return -1; } } } /* Set provider options from command line. */ for (i = 0; args; i++) { next_arg = strchr(args, ':'); if (next_arg) *next_arg++ = '\0'; key = args; value = strchr(args, '='); if (!value) { /* 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") && i < ELEMSOF(manual_keys)) { key = manual_keys[i]; value = args; } else { weprintf(_("Failed to parse option `%s'."), args); return -1; } } else { *value++ = '\0'; } if (provider->set_option(*state, key, value) < 0) { provider->free(*state); weprintf(_("Failed to set %s option."), provider->name); /* TRANSLATORS: `help' must not be translated. */ weprintf(_("Try `-l %s:help' for more information."), provider->name); return -1; } args = next_arg; } /* Start provider. */ if (provider->start(*state) < 0) { provider->free(*state); weprintf(_("Failed to start provider %s.\n"), provider->name); return -1; } return 0; } static int method_try_start(const struct gamma_method *method, GAMMA_STATE **state, enum program_mode mode, struct config_ini_state *config, char *args) { struct config_ini_section *section; struct config_ini_setting *setting; char *next_arg, *value; const char *key; if (method->init(state) < 0) { weprintf(_("Initialization of %s failed."), method->name); return -1; } /* Set method options from config file. */ if ((section = config_ini_get_section(config, method->name))) { for (setting = section->settings; setting; setting = setting->next) { if (method->set_option(*state, setting->name, setting->value) < 0) { method->free(*state); weprintf(_("Failed to set %s option."), method->name); /* TRANSLATORS: `help' must not be translated. */ weprintf(_("Try `-m %s:help' for more information.\n"), method->name); /* TODO \n */ return -1; } } } /* Set method options from command line. */ while (args) { next_arg = strchr(args, ':'); if (next_arg) *next_arg++ = '\0'; key = args; value = strchr(args, '='); if (!value) { weprintf(_("Failed to parse option `%s'.\n"), args); /* TODO \n */ return -1; } *value++ = '\0'; if (method->set_option(*state, key, value) < 0) { method->free(*state); weprintf(_("Failed to set %s option.\n"), method->name); /* TODO \n */ /* TRANSLATORS: `help' must not be translated. */ weprintf(_("Try -m %s:help' for more information.\n"), method->name); /* TODO missing ` and \n */ return -1; } args = next_arg; } /* Start method. */ if (method->start(*state, mode) < 0) { method->free(*state); weprintf(_("Failed to start adjustment method %s.\n"), method->name); /* TODO \n */ return -1; } return 0; } /* Check whether gamma is within allowed levels. */ static int gamma_is_valid(const double gamma[3]) { return !(gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA); } /* Check whether location is valid. Prints error message on stderr and returns 0 if invalid, otherwise returns 1. */ static int location_is_valid(const struct location *location) { /* Latitude */ if (location->lat < MIN_LAT || location->lat > MAX_LAT) { /* TRANSLATORS: Append degree symbols if possible. */ weprintf(_("Latitude must be between %.1f and %.1f.\n"), MIN_LAT, MAX_LAT); /* TODO \n */ return 0; } /* Longitude */ if (location->lon < MIN_LON || location->lon > MAX_LON) { /* TRANSLATORS: Append degree symbols if possible. */ weprintf(_("Longitude must be between %.1f and %.1f.\n"), MIN_LON, MAX_LON); /* TODO \n */ return 0; } return 1; } /* Wait for location to become available from provider. Waits until timeout (milliseconds) has elapsed or forever if timeout is -1. Writes location to loc. Returns -1 on error, 0 if timeout was reached, 1 if location became available. */ static int provider_get_location(const struct location_provider *provider, LOCATION_STATE *state, int timeout, struct location *loc) { int r, available, loc_fd; struct pollfd pollfds[1]; double now, later; do { loc_fd = provider->get_fd(state); if (loc_fd >= 0) { /* Provider is dynamic. */ /* TODO: This should use a monotonic time source. */ now = systemtime_get_time(); /* Poll on file descriptor until ready. */ pollfds[0].fd = loc_fd; pollfds[0].events = POLLIN; r = poll(pollfds, 1, timeout); if (r < 0) { weprintf("poll {{.fd=, .events=EPOLLIN}} 1 %i:", timeout); return -1; } else if (r == 0) { return 0; } later = systemtime_get_time(); /* Adjust timeout by elapsed time */ if (timeout >= 0) { timeout -= (later - now) * 1000; timeout = MAX(timeout, 0); } } if (provider->handle(state, loc, &available) < 0) return -1; } while (!available); return 1; } /* Easing function for fade. See https://github.com/mietek/ease-tween */ static double ease_fade(double t) { if (t <= 0) return 0; if (t >= 1) return 1; return 1.0042954579734844 * exp( -6.4041738958415664 * exp(-7.2908241330981340 * t)); } /* Run continual mode loop This is the main loop of the continual mode which keeps track of the current time and continuously updates the screen to the appropriate color temperature. */ static void run_continual_mode(const struct location_provider *provider, LOCATION_STATE *location_state, const struct transition_scheme *scheme, const struct gamma_method *method, GAMMA_STATE *method_state, int use_fade, int preserve_gamma, int verbose) { int done = 0; int prev_disabled = 1; int disabled = 0; int location_available = 1; struct color_setting fade_start_interp; struct color_setting prev_target_interp; struct color_setting interp; struct location loc; int need_location; /* Short fade parameters */ int fade_length = 0; int fade_time = 0; /* Save previous parameters so we can avoid printing status updates if the values did not change. */ enum period prev_period = PERIOD_NONE; signals_install_handlers(); /* Previous target color setting and current actual color setting. Actual color setting takes into account the current color fade. */ color_setting_reset(&prev_target_interp); color_setting_reset(&interp); loc = (struct location){ NAN, NAN }; need_location = !scheme->use_time; if (need_location) { weprintf(_("Waiting for initial location to become available...\n")); /* TODO \n */ /* Get initial location from provider */ if (provider_get_location(provider, location_state, -1, &loc) < 0) eprintf(_("Unable to get location from provider.")); if (!location_is_valid(&loc)) eprintf(_("Invalid location returned from provider.\n")); /* TODO \n */ print_location(&loc); } if (verbose) { printf(_("Color temperature: %uK\n"), interp.temperature); printf(_("Brightness: %.2f\n"), interp.brightness); } /* Continuously adjust color temperature */ for (;;) { double now; enum period period; double transition_prog; struct color_setting target_interp; int delay, loc_fd; /* Check to see if disable signal was caught */ if (disable && !done) { disabled = !disabled; disable = 0; } /* Check to see if exit signal was caught */ if (exiting) { if (done) break; /* On second signal stop the ongoing fade. */ done = 1; disabled = 1; exiting = 0; } /* Print status change */ if (verbose && disabled != prev_disabled) printf(_("Status: %s\n"), disabled ? _("Disabled") : _("Enabled")); prev_disabled = disabled; /* Read timestamp */ now = systemtime_get_time(); if (scheme->use_time) { int time_offset = get_seconds_since_midnight(now); period = get_period_from_time(scheme, time_offset); transition_prog = get_transition_progress_from_time(scheme, time_offset); } else { /* Current angular elevation of the sun */ double elevation = solar_elevation(now, loc.lat, loc.lon); period = get_period_from_elevation(scheme, elevation); transition_prog = get_transition_progress_from_elevation(scheme, elevation); } /* Use transition progress to get target color temperature. */ interpolate_transition_scheme(scheme, transition_prog, &target_interp); if (disabled) { period = PERIOD_NONE; color_setting_reset(&target_interp); } if (done) period = PERIOD_NONE; /* Print period if it changed during this update, or if we are in the transition period. In transition we print the progress, so we always print it in that case. */ if (verbose && (period != prev_period || period == PERIOD_TRANSITION)) print_period(period, transition_prog); /* Activate hooks if period changed */ if (period != prev_period) hooks_signal_period_change(prev_period, period); /* Start fade if the parameter differences are too big to apply instantly. */ if (use_fade) { if (fade_length ? color_setting_diff_is_major(&target_interp, &prev_target_interp) : color_setting_diff_is_major(&interp, &target_interp)) { fade_length = FADE_LENGTH; fade_time = 0; fade_start_interp = interp; } } /* Handle ongoing fade */ if (fade_length != 0) { double frac = ++fade_time / (double)fade_length; double alpha = CLAMP(0.0, ease_fade(frac), 1.0); interpolate_color_settings(&fade_start_interp, &target_interp, alpha, &interp); if (fade_time > fade_length) { fade_time = 0; fade_length = 0; } } else { interp = target_interp; } /* Break loop when done and final fade is over */ if (done && fade_length == 0) break; if (verbose) { if (prev_target_interp.temperature != target_interp.temperature) printf(_("Color temperature: %uK\n"), target_interp.temperature); if (!exact_eq(prev_target_interp.brightness, target_interp.brightness)) printf(_("Brightness: %.2f\n"), target_interp.brightness); } /* Adjust temperature */ if (method->set_temperature(method_state, &interp, preserve_gamma) < 0) eprintf(_("Temperature adjustment failed.")); /* Save period and target color setting as previous */ prev_period = period; prev_target_interp = target_interp; /* Sleep length depends on whether a fade is ongoing. */ delay = fade_length ? SLEEP_DURATION_SHORT : SLEEP_DURATION; /* Update location. */ loc_fd = need_location ? provider->get_fd(location_state) : -1; if (loc_fd >= 0) { struct pollfd pollfds[1]; struct location new_loc; int r, new_available; /* Provider is dynamic. */ pollfds[0].fd = loc_fd; pollfds[0].events = POLLIN; r = poll(pollfds, 1, delay); if (r < 0) { if (errno == EINTR) continue; weprintf("poll:"); eprintf(_("Unable to get location from provider.")); } else if (!r) { continue; } /* Get new location and availability information. */ if (provider->handle(location_state, &new_loc, &new_available) < 0) eprintf(_("Unable to get location from provider.")); if (!new_available && new_available != location_available) { weprintf(_("Location is temporarily unavailable; Using previous" /* TODO captial U efter ; and \n*/ " location until it becomes available...\n")); } if (new_available && (!exact_eq(new_loc.lat, loc.lat) || !exact_eq(new_loc.lon, loc.lon) || new_available != location_available)) { loc = new_loc; print_location(&loc); } location_available = new_available; if (!location_is_valid(&loc)) eprintf(_("Invalid location returned from provider.")); } else { systemtime_msleep(delay); } } /* Restore saved gamma ramps */ method->restore(method_state); } int main(int argc, char *argv[]) { /* List of gamma methods. */ const struct gamma_method gamma_methods[] = { #ifdef ENABLE_COOPGAMMA coopgamma_gamma_method, #endif #ifdef ENABLE_DRM drm_gamma_method, #endif #ifdef ENABLE_RANDR randr_gamma_method, #endif #ifdef ENABLE_VIDMODE vidmode_gamma_method, #endif #ifdef ENABLE_QUARTZ quartz_gamma_method, #endif #ifdef ENABLE_WINGDI w32gdi_gamma_method, #endif dummy_gamma_method, { NULL } }; /* List of location providers. */ const struct location_provider location_providers[] = { #ifdef ENABLE_GEOCLUE2 geoclue2_location_provider, #endif #ifdef ENABLE_CORELOCATION corelocation_location_provider, #endif manual_location_provider, { NULL } }; struct options options; struct config_ini_state config_state; struct transition_scheme *scheme; GAMMA_STATE *method_state; LOCATION_STATE *location_state; int r, need_location; size_t i; struct location loc = { NAN, NAN }; double now, transition_prog; enum period period; struct color_setting color; argv0 = argv[0]; #ifdef ENABLE_NLS setlocale(LC_CTYPE, ""); setlocale(LC_MESSAGES, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif options_init(&options); options_parse_args(&options, argc, argv, gamma_methods, location_providers); /* Load settings from config file. */ config_ini_init(&config_state, options.config_filepath); free(options.config_filepath); options_parse_config_file(&options, &config_state, gamma_methods, location_providers); options_set_defaults(&options); 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) eprintf(_("Partial time-configuration not supported!")); 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) eprintf(_("Invalid dawn/dusk time configuration!")); options.scheme.use_time = 1; } /* Initialize location provider if needed. If provider is NULL try all providers until one that works is found. */ /* Location is not needed for reset mode and manual mode. */ need_location = options.mode != PROGRAM_MODE_RESET && options.mode != PROGRAM_MODE_MANUAL && !options.scheme.use_time; if (need_location) { if (options.provider) { /* Use provider specified on command line. */ if (provider_try_start(options.provider, &location_state, &config_state, options.provider_args) < 0) exit(EXIT_FAILURE); } else { /* Try all providers, use the first that works. */ for (i = 0; location_providers[i].name != NULL; i++) { const struct location_provider *p = &location_providers[i]; weprintf(_("Trying location provider `%s'..."), p->name); if (provider_try_start(p, &location_state, &config_state, NULL) < 0) { weprintf(_("Trying next provider...")); continue; } /* Found provider that works. */ printf(_("Using provider `%s'.\n"), p->name); options.provider = p; break; } /* Failure if no providers were successful at this point. */ if (!options.provider) eprintf(_("No more location providers to try.")); } /* Solar elevations */ if (options.scheme.high < options.scheme.low) { eprintf(_("High transition elevation cannot be lower than the low transition elevation.")); } if (options.verbose) { /* TRANSLATORS: Append degree symbols if possible. */ printf(_("Solar elevations: day above %.1f, night below %.1f\n"), options.scheme.high, options.scheme.low); } } if (options.mode != PROGRAM_MODE_RESET && options.mode != PROGRAM_MODE_MANUAL) { if (options.verbose) { printf(_("Temperatures: %iK at day, %iK at night\n"), options.scheme.day.temperature, options.scheme.night.temperature); } /* Color temperature */ 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) eprintf(_("Temperature must be between %uK and %uK."), MIN_TEMP, MAX_TEMP); } if (options.mode == PROGRAM_MODE_MANUAL) { /* Check color temperature to be set */ if (options.temp_set < MIN_TEMP || options.temp_set > MAX_TEMP) eprintf(_("Temperature must be between %uK and %uK."), MIN_TEMP, MAX_TEMP); } /* 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) eprintf(_("Brightness values must be between %.1f and %.1f."), MIN_BRIGHTNESS, MAX_BRIGHTNESS); if (options.verbose) printf(_("Brightness: %.2f:%.2f\n"), options.scheme.day.brightness, options.scheme.night.brightness); /* Gamma */ if (!gamma_is_valid(options.scheme.day.gamma) || !gamma_is_valid(options.scheme.night.gamma)) eprintf(_("Gamma value must be between %.1f and %.1f."), MIN_GAMMA, MAX_GAMMA); if (options.verbose) { /* TRANSLATORS: The string in parenthesis is either Daytime or Night (translated). */ printf(_("Gamma (%s): %.3f, %.3f, %.3f\n"), _("Daytime"), options.scheme.day.gamma[0], options.scheme.day.gamma[1], options.scheme.day.gamma[2]); printf(_("Gamma (%s): %.3f, %.3f, %.3f\n"), _("Night"), options.scheme.night.gamma[0], options.scheme.night.gamma[1], options.scheme.night.gamma[2]); } scheme = &options.scheme; /* Initialize gamma adjustment method. If method is NULL try all methods until one that works is found. */ /* Gamma adjustment not needed for print mode */ if (options.mode != PROGRAM_MODE_PRINT) { if (options.method) { /* Use method specified on command line. */ r = method_try_start(options.method, &method_state, options.mode, &config_state, options.method_args); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all methods, use the first that works. */ for (i = 0; gamma_methods[i].name; i++) { const struct gamma_method *m = &gamma_methods[i]; if (!m->autostart) continue; if (method_try_start(m, &method_state, options.mode, &config_state, NULL) < 0) { weprintf(_("Trying next method...")); continue; } /* Found method that works. */ printf(_("Using method `%s'.\n"), m->name); options.method = m; break; } /* Failure if no methods were successful at this point. */ if (!options.method) eprintf(_("No more methods to try.")); } } config_ini_free(&config_state); switch (options.mode) { case PROGRAM_MODE_ONE_SHOT: case PROGRAM_MODE_PRINT: if (need_location) { weprintf(_("Waiting for current location to become available...")); /* Wait for location provider. */ if (provider_get_location(options.provider, location_state, -1, &loc) < 0) eprintf(_("Unable to get location from provider.")); if (!location_is_valid(&loc)) exit(1); print_location(&loc); } now = systemtime_get_time(); if (options.scheme.use_time) { int time_offset = get_seconds_since_midnight(now); period = get_period_from_time(scheme, time_offset); transition_prog = get_transition_progress_from_time(scheme, time_offset); } else { /* Current angular elevation of the sun */ double elevation = solar_elevation(now, loc.lat, loc.lon); if (options.verbose) { /* TRANSLATORS: Append degree symbol if possible. */ printf(_("Solar elevation: %f\n"), elevation); } period = get_period_from_elevation(scheme, elevation); transition_prog = get_transition_progress_from_elevation(scheme, elevation); } /* Use transition progress to set color temperature */ interpolate_transition_scheme(scheme, transition_prog, &color); if (options.verbose || options.mode == PROGRAM_MODE_PRINT) { print_period(period, transition_prog); printf(_("Color temperature: %uK\n"), color.temperature); printf(_("Brightness: %.2f\n"), color.brightness); } if (options.mode == PROGRAM_MODE_PRINT) break; apply: if (options.method->set_temperature(method_state, &color, options.preserve_gamma) < 0) eprintf(_("Temperature adjustment failed.")); #ifndef WINDOWS /* In Quartz (OSX) the gamma adjustments will automatically revert when * the process exits. Therefore, we have to loop until CTRL-C is received. */ if (!strcmp(options.method->name, "quartz")) { weprintf(_("Press ctrl-c to stop...")); while (!exiting) pause(); } #endif break; case PROGRAM_MODE_MANUAL: if (options.verbose) printf(_("Color temperature: %uK\n"), options.temp_set); color = scheme->day; color.temperature = options.temp_set; goto apply; case PROGRAM_MODE_RESET: color_setting_reset(&color); options.preserve_gamma = 0; goto apply; case PROGRAM_MODE_CONTINUAL: run_continual_mode(options.provider, location_state, scheme, options.method, method_state, options.use_fade, options.preserve_gamma, options.verbose); break; } if (options.mode != PROGRAM_MODE_PRINT) options.method->free(method_state); if (need_location) options.provider->free(location_state); return 0; }