aboutsummaryrefslogtreecommitdiffstats
path: root/src/redshift.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/redshift.c')
-rw-r--r--src/redshift.c889
1 files changed, 644 insertions, 245 deletions
diff --git a/src/redshift.c b/src/redshift.c
index 6eefd7d..f46853b 100644
--- a/src/redshift.c
+++ b/src/redshift.c
@@ -14,7 +14,7 @@
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) 2009-2015 Jon Lund Steffensen <jonlst@gmail.com>
+ Copyright (c) 2009-2017 Jon Lund Steffensen <jonlst@gmail.com>
*/
#ifdef HAVE_CONFIG_H
@@ -29,6 +29,21 @@
#include <locale.h>
#include <errno.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 _WIN32
+# include <poll.h>
+#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
+
#if defined(HAVE_SIGNAL_H) && !defined(__WIN32__)
# include <signal.h>
#endif
@@ -81,10 +96,6 @@
#include "location-manual.h"
-#ifdef ENABLE_GEOCLUE
-# include "location-geoclue.h"
-#endif
-
#ifdef ENABLE_GEOCLUE2
# include "location-geoclue2.h"
#endif
@@ -195,28 +206,17 @@ static const gamma_method_t gamma_methods[] = {
/* Union of state data for location providers */
typedef union {
location_manual_state_t manual;
-#ifdef ENABLE_GEOCLUE
- location_geoclue_state_t geoclue;
+#ifdef ENABLE_GEOCLUE2
+ location_geoclue2_state_t geoclue2;
+#endif
+#ifdef ENABLE_CORELOCATION
+ location_corelocation_state_t corelocation;
#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_GEOCLUE2
{
"geoclue2",
@@ -227,8 +227,8 @@ static const location_provider_t location_providers[] = {
location_geoclue2_print_help,
(location_provider_set_option_func *)
location_geoclue2_set_option,
- (location_provider_get_location_func *)
- location_geoclue2_get_location
+ (location_provider_get_fd_func *)location_geoclue2_get_fd,
+ (location_provider_handle_func *)location_geoclue2_handle
},
#endif
#ifdef ENABLE_CORELOCATION
@@ -241,8 +241,8 @@ static const location_provider_t location_providers[] = {
location_corelocation_print_help,
(location_provider_set_option_func *)
location_corelocation_set_option,
- (location_provider_get_location_func *)
- location_corelocation_get_location
+ (location_provider_get_fd_func *)location_corelocation_get_fd,
+ (location_provider_handle_func *)location_corelocation_handle
},
#endif
{
@@ -254,8 +254,8 @@ static const location_provider_t location_providers[] = {
location_manual_print_help,
(location_provider_set_option_func *)
location_manual_set_option,
- (location_provider_get_location_func *)
- location_manual_get_location
+ (location_provider_get_fd_func *)location_manual_get_fd,
+ (location_provider_handle_func *)location_manual_handle
},
{ NULL }
};
@@ -292,6 +292,9 @@ static const location_provider_t location_providers[] = {
#define SLEEP_DURATION 5000
#define SLEEP_DURATION_SHORT 100
+/* Length of fade in numbers of short sleep durations. */
+#define FADE_LENGTH 40
+
/* Program modes. */
typedef enum {
PROGRAM_MODE_CONTINUAL,
@@ -301,12 +304,22 @@ typedef enum {
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;
@@ -321,10 +334,25 @@ static const char *period_names[] = {
};
-/* Determine which period we are currently in. */
+/* Determine which period we are currently in based on time offset. */
static period_t
-get_period(const transition_scheme_t *transition,
- double elevation)
+get_period_from_time(const transition_scheme_t *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 period_t
+get_period_from_elevation(
+ const transition_scheme_t *transition, double elevation)
{
if (elevation < transition->low) {
return PERIOD_NIGHT;
@@ -335,10 +363,31 @@ get_period(const transition_scheme_t *transition,
}
}
-/* Determine how far through the transition we are. */
+/* Determine how far through the transition we are based on time offset. */
+static double
+get_transition_progress_from_time(
+ const transition_scheme_t *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(const transition_scheme_t *transition,
- double elevation)
+get_transition_progress_from_elevation(
+ const transition_scheme_t *transition, double elevation)
{
if (elevation < transition->low) {
return 0.0;
@@ -350,6 +399,16 @@ get_transition_progress(const transition_scheme_t *transition,
}
}
+/* 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(period_t period, double transition)
@@ -389,27 +448,52 @@ print_location(const location_t *location)
fabs(location->lon), location->lon >= 0.f ? east : west);
}
-/* Interpolate color setting structs based on solar elevation */
+/* Interpolate color setting structs given alpha. */
static void
-interpolate_color_settings(const transition_scheme_t *transition,
- double elevation,
- color_setting_t *result)
+interpolate_color_settings(
+ const color_setting_t *first,
+ const color_setting_t *second,
+ double alpha,
+ color_setting_t *result)
+{
+ 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 (int 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 transition_scheme_t *transition,
+ double alpha,
+ color_setting_t *result)
{
const color_setting_t *day = &transition->day;
const color_setting_t *night = &transition->night;
- double alpha = (transition->low - elevation) /
- (transition->low - transition->high);
alpha = CLAMP(0.0, alpha, 1.0);
+ interpolate_color_settings(night, day, alpha, result);
+}
- result->temperature = (1.0-alpha)*night->temperature +
- alpha*day->temperature;
- result->brightness = (1.0-alpha)*night->brightness +
- alpha*day->brightness;
- for (int i = 0; i < 3; i++) {
- result->gamma[i] = (1.0-alpha)*night->gamma[i] +
- alpha*day->gamma[i];
- }
+/* 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 color_setting_t *first,
+ const color_setting_t *second)
+{
+ return (abs(first->temperature - second->temperature) > 25 ||
+ fabsf(first->brightness - second->brightness) > 0.1 ||
+ fabsf(first->gamma[0] - second->gamma[0]) > 0.1 ||
+ fabsf(first->gamma[1] - second->gamma[1]) > 0.1 ||
+ fabsf(first->gamma[2] - second->gamma[2]) > 0.1);
}
@@ -435,14 +519,14 @@ print_help(const char *program_name)
no-wrap */
fputs(_(" -h\t\tDisplay this help message\n"
" -v\t\tVerbose output\n"
- " -V\t\tShow program version\n"), stdout);
+ " -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"
+ " -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"
@@ -455,17 +539,16 @@ print_help(const char *program_name)
" -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 temperature transitions\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\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"),
+ 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);
@@ -726,6 +809,57 @@ parse_brightness_string(const char *str, float *bright_day, float *bright_night)
}
}
+/* 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
gamma_is_valid(const float gamma[3])
@@ -736,7 +870,33 @@ gamma_is_valid(const float gamma[3])
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 location_t *location)
+{
+ /* Latitude */
+ if (location->lat < MIN_LAT || location->lat > MAX_LAT) {
+ /* TRANSLATORS: Append degree symbols if possible. */
+ fprintf(stderr,
+ _("Latitude must be between %.1f and %.1f.\n"),
+ MIN_LAT, MAX_LAT);
+ return 0;
+ }
+
+ /* Longitude */
+ if (location->lon < MIN_LON || location->lon > MAX_LON) {
+ /* TRANSLATORS: Append degree symbols if possible. */
+ fprintf(stderr,
+ _("Longitude must be between"
+ " %.1f and %.1f.\n"), MIN_LON, MAX_LON);
+ return 0;
+ }
+
+ return 1;
}
static const gamma_method_t *
@@ -769,88 +929,171 @@ find_location_provider(const char *name)
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,
+ 0 if timeout was reached, 1 if location became available. */
+static int
+provider_get_location(
+ const location_provider_t *provider, location_state_t *state,
+ int timeout, location_t *loc)
+{
+ int available = 0;
+ struct pollfd pollfds[1];
+ while (!available) {
+ int loc_fd = provider->get_fd(state);
+ if (loc_fd >= 0) {
+ /* Provider is dynamic. */
+ /* TODO: This should use a monotonic time source. */
+ double now;
+ int r = systemtime_get_time(&now);
+ if (r < 0) {
+ fputs(_("Unable to read system time.\n"),
+ stderr);
+ return -1;
+ }
+
+ /* Poll on file descriptor until ready. */
+ pollfds[0].fd = loc_fd;
+ pollfds[0].events = POLLIN;
+ r = poll(pollfds, 1, timeout);
+ if (r < 0) {
+ perror("poll");
+ return -1;
+ } else if (r == 0) {
+ return 0;
+ }
+
+ double later;
+ r = systemtime_get_time(&later);
+ if (r < 0) {
+ fputs(_("Unable to read system time.\n"),
+ stderr);
+ return -1;
+ }
+
+ /* Adjust timeout by elapsed time */
+ if (timeout >= 0) {
+ timeout -= (later - now) * 1000;
+ timeout = timeout < 0 ? 0 : timeout;
+ }
+ }
+
+
+ int r = provider->handle(state, loc, &available);
+ if (r < 0) return -1;
+ }
+
+ 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 int
-run_continual_mode(const location_t *loc,
+run_continual_mode(const location_provider_t *provider,
+ location_state_t *location_state,
const transition_scheme_t *scheme,
const gamma_method_t *method,
gamma_state_t *state,
- int transition, int verbose)
+ int use_fade, int verbose)
{
int r;
- /* 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;
+ /* Short fade parameters */
+ int fade_length = 0;
+ int fade_time = 0;
+ color_setting_t fade_start_interp;
r = signals_install_handlers();
if (r < 0) {
return r;
}
- if (verbose) {
- printf(_("Status: %s\n"), _("Enabled"));
+ /* Save previous parameters so we can avoid printing status updates if
+ the values did not change. */
+ period_t prev_period = PERIOD_NONE;
+
+ /* Previous target color setting and current actual color setting.
+ Actual color setting takes into account the current color fade. */
+ color_setting_t prev_target_interp =
+ { NEUTRAL_TEMP, { 1.0, 1.0, 1.0 }, 1.0 };
+ color_setting_t interp =
+ { NEUTRAL_TEMP, { 1.0, 1.0, 1.0 }, 1.0 };
+
+ location_t loc = { NAN, NAN };
+ int need_location = !scheme->use_time;
+ if (need_location) {
+ fputs(_("Waiting for initial location"
+ " to become available...\n"), stderr);
+
+ /* Get initial location from provider */
+ r = provider_get_location(provider, location_state, -1, &loc);
+ if (r < 0) {
+ fputs(_("Unable to get location"
+ " from provider.\n"), stderr);
+ return -1;
+ }
+
+ if (!location_is_valid(&loc)) {
+ fputs(_("Invalid location returned from provider.\n"),
+ stderr);
+ return -1;
+ }
+
+ print_location(&loc);
}
- /* Save previous colors so we can avoid
- printing status updates if the values
- did not change. */
- period_t prev_period = PERIOD_NONE;
- color_setting_t prev_interp =
- { -1, { NAN, NAN, NAN }, NAN };
+ if (verbose) {
+ printf(_("Color temperature: %uK\n"), interp.temperature);
+ printf(_("Brightness: %.2f\n"), interp.brightness);
+ }
/* Continuously adjust color temperature */
int done = 0;
+ int prev_disabled = 1;
int disabled = 0;
+ int location_available = 1;
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;
- }
+ if (disable && !done) {
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;
+ /* On second signal stop the ongoing fade. */
+ break;
} else {
- if (!disabled) {
- /* Make a short transition
- back to 6500K */
- short_trans_delta = 1;
- short_trans_len = 2;
- }
-
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 */
double now;
r = systemtime_get_time(&now);
@@ -859,36 +1102,51 @@ run_continual_mode(const location_t *loc,
return -1;
}
- /* 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;
- }
+ period_t period;
+ double transition_prog;
+ 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);
}
- /* Current angular elevation of the sun */
- double elevation = solar_elevation(now, loc->lat,
- loc->lon);
+ /* Use transition progress to get target color
+ temperature. */
+ color_setting_t target_interp;
+ interpolate_transition_scheme(
+ scheme, transition_prog, &target_interp);
+
+ if (disabled) {
+ /* Reset to neutral */
+ target_interp.temperature = NEUTRAL_TEMP;
+ target_interp.brightness = 1.0;
+ target_interp.gamma[0] = 1.0;
+ target_interp.gamma[1] = 1.0;
+ target_interp.gamma[2] = 1.0;
+ }
- /* Use elevation of sun to set color temperature */
- color_setting_t interp;
- interpolate_color_settings(scheme, elevation, &interp);
+ if (done) {
+ period = PERIOD_NONE;
+ }
/* Print period if it changed during this update,
- or if we are in transition. In transition we
+ or if we are in the transition period. In transition we
print the progress, so we always print it in
that case. */
- period_t period = get_period(scheme, elevation);
if (verbose && (period != prev_period ||
period == PERIOD_TRANSITION)) {
- double transition =
- get_transition_progress(scheme,
- elevation);
- print_period(period, transition);
+ print_period(period, transition_prog);
}
/* Activate hooks if period changed */
@@ -896,66 +1154,135 @@ run_continual_mode(const location_t *loc,
hooks_signal_period_change(prev_period, period);
}
- /* 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;
+ /* Start fade if the parameter differences are too big to apply
+ instantly. */
+ if (use_fade) {
+ if ((fade_length == 0 &&
+ color_setting_diff_is_major(
+ &interp,
+ &target_interp)) ||
+ (fade_length != 0 &&
+ color_setting_diff_is_major(
+ &target_interp,
+ &prev_target_interp))) {
+ fade_length = FADE_LENGTH;
+ fade_time = 0;
+ fade_start_interp = interp;
}
-
- /* Clamp alpha value */
- adjustment_alpha = CLAMP(0.0, adjustment_alpha, 1.0);
}
- /* Interpolate between 6500K and calculated
- temperature */
- interp.temperature = adjustment_alpha*6500 +
- (1.0-adjustment_alpha)*interp.temperature;
+ /* Handle ongoing fade */
+ if (fade_length != 0) {
+ fade_time += 1;
+ 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);
- interp.brightness = adjustment_alpha*1.0 +
- (1.0-adjustment_alpha)*interp.brightness;
+ if (fade_time > fade_length) {
+ fade_time = 0;
+ fade_length = 0;
+ }
+ } else {
+ interp = target_interp;
+ }
- /* Quit loop when done */
- if (done && !short_trans_delta) break;
+ /* Break loop when done and final fade is over */
+ if (done && fade_length == 0) break;
if (verbose) {
- if (interp.temperature !=
- prev_interp.temperature) {
+ if (prev_target_interp.temperature !=
+ target_interp.temperature) {
printf(_("Color temperature: %uK\n"),
- interp.temperature);
+ target_interp.temperature);
}
- if (interp.brightness !=
- prev_interp.brightness) {
+ if (prev_target_interp.brightness !=
+ target_interp.brightness) {
printf(_("Brightness: %.2f\n"),
- interp.brightness);
+ target_interp.brightness);
}
}
/* Adjust temperature */
- if (!disabled || short_trans_delta || set_adjustments) {
- r = method->set_temperature(state, &interp);
+ r = method->set_temperature(state, &interp);
+ if (r < 0) {
+ fputs(_("Temperature adjustment failed.\n"),
+ stderr);
+ return -1;
+ }
+
+ /* 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. */
+ int delay = SLEEP_DURATION;
+ if (fade_length != 0) {
+ delay = SLEEP_DURATION_SHORT;
+ }
+
+ /* Update location. */
+ int loc_fd = -1;
+ if (need_location) {
+ loc_fd = provider->get_fd(location_state);
+ }
+
+ if (loc_fd >= 0) {
+ /* Provider is dynamic. */
+ struct pollfd pollfds[1];
+ pollfds[0].fd = loc_fd;
+ pollfds[0].events = POLLIN;
+ int r = poll(pollfds, 1, delay);
if (r < 0) {
- fputs(_("Temperature adjustment"
- " failed.\n"), stderr);
+ if (errno == EINTR) continue;
+ perror("poll");
+ fputs(_("Unable to get location"
+ " from provider.\n"), stderr);
return -1;
+ } else if (r == 0) {
+ continue;
}
- }
- /* Save temperature as previous */
- prev_period = period;
- memcpy(&prev_interp, &interp,
- sizeof(color_setting_t));
+ /* Get new location and availability
+ information. */
+ location_t new_loc;
+ int new_available;
+ r = provider->handle(
+ location_state, &new_loc,
+ &new_available);
+ if (r < 0) {
+ fputs(_("Unable to get location"
+ " from provider.\n"), stderr);
+ return -1;
+ }
+
+ if (!new_available &&
+ new_available != location_available) {
+ fputs(_("Location is temporarily"
+ " unavailable; Using previous"
+ " location until it becomes"
+ " available...\n"), stderr);
+ }
- /* Sleep for 5 seconds or 0.1 second. */
- if (short_trans_delta) {
- systemtime_msleep(SLEEP_DURATION_SHORT);
+ if (new_available &&
+ (new_loc.lat != loc.lat ||
+ new_loc.lon != loc.lon ||
+ new_available != location_available)) {
+ loc = new_loc;
+ print_location(&loc);
+ }
+
+ location_available = new_available;
+
+ if (!location_is_valid(&loc)) {
+ fputs(_("Invalid location returned"
+ " from provider.\n"), stderr);
+ return -1;
+ }
} else {
- systemtime_msleep(SLEEP_DURATION);
+ systemtime_msleep(delay);
}
}
@@ -965,6 +1292,7 @@ run_continual_mode(const location_t *loc,
return 0;
}
+
int
main(int argc, char *argv[])
{
@@ -983,11 +1311,17 @@ main(int argc, char *argv[])
/* Initialize settings to NULL values. */
char *config_filepath = NULL;
- /* Settings for day, night and transition.
+ /* 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;
@@ -1005,7 +1339,7 @@ main(int argc, char *argv[])
const location_provider_t *provider = NULL;
char *provider_args = NULL;
- int transition = -1;
+ int use_fade = -1;
program_mode_t mode = PROGRAM_MODE_CONTINUAL;
int verbose = 0;
char *s;
@@ -1135,7 +1469,7 @@ main(int argc, char *argv[])
mode = PROGRAM_MODE_PRINT;
break;
case 'r':
- transition = 0;
+ use_fade = 0;
break;
case 't':
s = strchr(optarg, ':');
@@ -1194,10 +1528,13 @@ main(int argc, char *argv[])
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, "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) {
@@ -1290,6 +1627,34 @@ main(int argc, char *argv[])
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"),
@@ -1326,17 +1691,38 @@ main(int argc, char *argv[])
scheme.night.gamma[2] = DEFAULT_GAMMA;
}
- if (transition < 0) transition = 1;
+ if (use_fade < 0) use_fade = 1;
- location_t loc = { NAN, NAN };
+ 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) {
+ fputs(_("Partitial time-configuration not"
+ " supported!\n"), stderr);
+ exit(EXIT_FAILURE);
+ }
- /* Initialize location provider. If provider is NULL
+ if (scheme.dawn.start > scheme.dawn.end ||
+ scheme.dawn.end > scheme.dusk.start ||
+ scheme.dusk.start > scheme.dusk.end) {
+ fputs(_("Invalid dawn/dusk time configuration!\n"),
+ stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ scheme.use_time = 1;
+ }
+
+ /* Initialize location provider if needed. 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) {
+ int need_location =
+ mode != PROGRAM_MODE_RESET &&
+ mode != PROGRAM_MODE_MANUAL &&
+ !scheme.use_time;
+ if (need_location) {
if (provider != NULL) {
/* Use provider specified on command line. */
r = provider_try_start(provider, &location_state,
@@ -1374,44 +1760,27 @@ main(int argc, char *argv[])
}
}
- /* 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);
+ /* Solar elevations */
+ if (scheme.high < scheme.low) {
+ fprintf(stderr,
+ _("High transition elevation cannot be lower than"
+ " the low transition elevation.\n"));
+ 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. */
+ /* 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);
+ if (mode != PROGRAM_MODE_RESET &&
+ mode != PROGRAM_MODE_MANUAL) {
+ if (verbose) {
+ printf(_("Temperatures: %dK at day, %dK at night\n"),
+ scheme.day.temperature,
+ scheme.night.temperature);
}
/* Color temperature */
@@ -1424,14 +1793,6 @@ main(int argc, char *argv[])
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) {
@@ -1523,7 +1884,27 @@ main(int argc, char *argv[])
case PROGRAM_MODE_ONE_SHOT:
case PROGRAM_MODE_PRINT:
{
- /* Current angular elevation of the sun */
+ location_t loc = { NAN, NAN };
+ if (need_location) {
+ fputs(_("Waiting for current location"
+ " to become available...\n"), stderr);
+
+ /* Wait for location provider. */
+ int r = provider_get_location(
+ provider, &location_state, -1, &loc);
+ if (r < 0) {
+ fputs(_("Unable to get location"
+ " from provider.\n"), stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!location_is_valid(&loc)) {
+ exit(EXIT_FAILURE);
+ }
+
+ print_location(&loc);
+ }
+
double now;
r = systemtime_get_time(&now);
if (r < 0) {
@@ -1532,48 +1913,60 @@ main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
- double elevation = solar_elevation(now, loc.lat, loc.lon);
+ period_t period;
+ double transition_prog;
+ 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);
+ if (verbose) {
+ /* TRANSLATORS: Append degree symbol if
+ possible. */
+ printf(_("Solar elevation: %f\n"), elevation);
+ }
- if (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 elevation of sun to set color temperature */
+ /* Use transition progress to set color temperature */
color_setting_t interp;
- interpolate_color_settings(&scheme, elevation, &interp);
+ interpolate_transition_scheme(
+ &scheme, transition_prog, &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);
+ print_period(period, transition_prog);
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);
- }
+ if (mode != PROGRAM_MODE_PRINT) {
+ /* 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();
+ /* In Quartz (macOS) 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;
@@ -1582,8 +1975,7 @@ main(int argc, char *argv[])
if (verbose) printf(_("Color temperature: %uK\n"), temp_set);
/* Adjust temperature */
- color_setting_t manual;
- memcpy(&manual, &scheme.day, sizeof(color_setting_t));
+ color_setting_t manual = scheme.day;
manual.temperature = temp_set;
r = method->set_temperature(&state, &manual);
if (r < 0) {
@@ -1623,16 +2015,23 @@ main(int argc, char *argv[])
break;
case PROGRAM_MODE_CONTINUAL:
{
- r = run_continual_mode(&loc, &scheme,
+ r = run_continual_mode(provider, &location_state, &scheme,
method, &state,
- transition, verbose);
+ use_fade, verbose);
if (r < 0) exit(EXIT_FAILURE);
}
break;
}
/* Clean up gamma adjustment state */
- method->free(&state);
+ if (mode != PROGRAM_MODE_PRINT) {
+ method->free(&state);
+ }
+
+ /* Clean up location provider state */
+ if (need_location) {
+ provider->free(&location_state);
+ }
return EXIT_SUCCESS;
}