diff --git a/src/options.c b/src/options.c index 33bf623a..b38ac346 100644 --- a/src/options.c +++ b/src/options.c @@ -193,6 +193,10 @@ print_help(const char *program_name) " color effect\n" " -x\t\tReset mode (remove adjustment from screen)\n" " -r\t\tDisable fading between color temperatures\n" + " -e\t\tSet the fading mode to use during a fade between color temperatures.\n" + " \t\tUse \"ease-in\", \"ease-out\" or \"ease-in-out\" to make fades pleasant for your eyes.\n" + " \t\tOtherwise, use \"linear\".\n" + " \t\tFor details about the supported easing functions, see https://easings.net/\n" " -t DAY:NIGHT\tColor temperature to set at daytime/night\n"), stdout); fputs("\n", stdout); @@ -320,6 +324,7 @@ options_init(options_t *options) options->provider_args = NULL; options->use_fade = -1; + options->fade_mode = FADE_MODE_NOT_SPECIFIED; options->preserve_gamma = 1; options->mode = PROGRAM_MODE_CONTINUAL; options->verbose = 0; @@ -456,10 +461,27 @@ parse_command_line_option( case 'r': options->use_fade = 0; break; + case 'e': + if(strlen(value) == 0) { + fprintf(stderr, _("No value was passed for option -e (fade mode).\nTry \"redshift -h\" for more information.\n")); + return -1; + } else if (strcmp(value, "linear") == 0) { + options->fade_mode = FADE_MODE_LINEAR; + } else if (strcmp(value, "ease-in") == 0) { + options->fade_mode = FADE_MODE_EASE_IN; + } else if (strcmp(value, "ease-out") == 0) { + options->fade_mode = FADE_MODE_EASE_OUT; + } else if (strcmp(value, "ease-in-out") == 0) { + options->fade_mode = FADE_MODE_EASE_IN_OUT; + } else { + fprintf(stderr, _("Value \"%s\" for option -e (fade mode) is not supported.\nTry \"redshift -h\" for more information."), value); + return -1; + } + break; case 't': - s = strchr(value, ':'); - if (s == NULL) { - fputs(_("Malformed temperature argument.\n"), stderr); + s = strchr(value, ':'); + if (s == NULL) { + fputs(_("Malformed temperature argument.\n"), stderr); fputs(_("Try `-h' for more information.\n"), stderr); return -1; } @@ -495,7 +517,7 @@ options_parse_args( { const char* program_name = argv[0]; int opt; - while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:pPrt:vVx")) != -1) { + while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:pPrt:e:vVx")) != -1) { char option = opt; int r = parse_command_line_option( option, optarg, options, program_name, gamma_methods, @@ -526,7 +548,26 @@ parse_config_file_option( if (options->use_fade < 0) { options->use_fade = !!atoi(value); } - } else if (strcasecmp(key, "brightness") == 0) { + } else if (strcasecmp(key, "fade-mode") == 0) { + if (options->fade_mode == FADE_MODE_NOT_SPECIFIED) { + if(strlen(value) == 0) { + fprintf(stderr, _("fade-mode was set to an empty value in config file. Please execute \"redshift -h\" to see supported values.\n")); + return -1; + } else if (strcmp(value, "linear") == 0) { + options->fade_mode = FADE_MODE_LINEAR; + } else if (strcmp(value, "ease-in") == 0) { + options->fade_mode = FADE_MODE_EASE_IN; + } else if (strcmp(value, "ease-out") == 0) { + options->fade_mode = FADE_MODE_EASE_OUT; + } else if (strcmp(value, "ease-in-out") == 0) { + options->fade_mode = FADE_MODE_EASE_IN_OUT; + } else { + fprintf(stderr, _("Fade mode \"%s\" set in config file is not supported.\n"), value); + return -1; + } + } + } + else if (strcasecmp(key, "brightness") == 0) { if (isnan(options->scheme.day.brightness)) { options->scheme.day.brightness = atof(value); } @@ -676,4 +717,6 @@ options_set_defaults(options_t *options) } if (options->use_fade < 0) options->use_fade = 1; + + if (options->fade_mode == FADE_MODE_NOT_SPECIFIED) options->fade_mode = FADE_MODE_LINEAR; } diff --git a/src/options.h b/src/options.h index 9993a07f..9fdf9b5f 100644 --- a/src/options.h +++ b/src/options.h @@ -34,6 +34,8 @@ typedef struct { int temp_set; /* Whether to fade between large skips in color temperature. */ int use_fade; + /* The fading function to use when transitioning between color temperatures. */ + fade_mode_t fade_mode; /* Whether to preserve gamma ramps if supported by gamma method. */ int preserve_gamma; diff --git a/src/redshift.c b/src/redshift.c index d2ba577c..69ce4464 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -586,15 +586,23 @@ provider_get_location( return 1; } -/* Easing function for fade. - See https://github.com/mietek/ease-tween */ +/* Easing function for fade.*/ static double -ease_fade(double t) +ease_fade(double t, fade_mode_t fade_mode) { + // Mathematical functions for easing were adapted from https://github.com/d3/d3-ease/blob/a0919680efc6f8e667275ba1d6330bf6a4cc9301/src/sin.js if (t <= 0) return 0; if (t >= 1) return 1; - return 1.0042954579734844 * exp( - -6.4041738958415664 * exp(-7.2908241330981340 * t)); + switch (fade_mode) { + case FADE_MODE_LINEAR: + return t; + case FADE_MODE_EASE_IN: + return 1 - cos(t * M_PI_2); + case FADE_MODE_EASE_OUT: + return sin(t * M_PI_2); + case FADE_MODE_EASE_IN_OUT: + return (1 - cos (M_PI * t)) / 2; + } } @@ -608,7 +616,8 @@ run_continual_mode(const location_provider_t *provider, const transition_scheme_t *scheme, const gamma_method_t *method, gamma_state_t *method_state, - int use_fade, int preserve_gamma, int verbose) + int use_fade, fade_mode_t fade_mode, + int preserve_gamma, int verbose) { int r; @@ -771,7 +780,7 @@ run_continual_mode(const location_provider_t *provider, if (fade_length != 0) { fade_time += 1; double frac = fade_time / (double)fade_length; - double alpha = CLAMP(0.0, ease_fade(frac), 1.0); + double alpha = CLAMP(0.0, ease_fade(frac, fade_mode), 1.0); interpolate_color_settings( &fade_start_interp, &target_interp, alpha, @@ -1307,7 +1316,8 @@ main(int argc, char *argv[]) r = run_continual_mode( options.provider, location_state, scheme, options.method, method_state, - options.use_fade, options.preserve_gamma, + options.use_fade, options.fade_mode, + options.preserve_gamma, options.verbose); if (r < 0) exit(EXIT_FAILURE); } diff --git a/src/redshift.h b/src/redshift.h index 0282d839..d5f4a8ac 100644 --- a/src/redshift.h +++ b/src/redshift.h @@ -57,6 +57,16 @@ typedef enum { PROGRAM_MODE_MANUAL } program_mode_t; +/* Fade modes. */ +typedef enum { + FADE_MODE_NOT_SPECIFIED, + FADE_MODE_LINEAR, + FADE_MODE_EASE_IN, + FADE_MODE_EASE_OUT, + FADE_MODE_EASE_IN_OUT, +} fade_mode_t; + + /* Time range. Fields are offsets from midnight in seconds. */ typedef struct {