Simplify state management

master
Kenny Levinsen 2020-10-07 21:55:46 +02:00
parent 7c22eade9a
commit 3186967e1d
1 changed files with 57 additions and 104 deletions

161
main.c
View File

@ -1,17 +1,17 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/types.h> #include <sys/types.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <wayland-client-protocol.h> #include <wayland-client-protocol.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <time.h>
#include <poll.h>
#include <signal.h>
#include "wlr-gamma-control-unstable-v1-client-protocol.h" #include "wlr-gamma-control-unstable-v1-client-protocol.h"
#include "color_math.h" #include "color_math.h"
@ -65,53 +65,34 @@ static int set_timer(timer_t timer, time_t deadline) {
static const int LONG_SLEEP = 600; static const int LONG_SLEEP = 600;
enum state {
HIGH_TEMP,
ANIMATING_TO_LOW,
LOW_TEMP,
ANIMATING_TO_HIGH,
};
static char *state_names[] = {
"day",
"dusk",
"night",
"dawn",
NULL
};
struct context { struct context {
double gamma;
int high_temp; int high_temp;
int low_temp; int low_temp;
int duration; int duration;
double longitude; double longitude;
double latitude; double latitude;
timer_t timer;
time_t dawn; time_t dawn;
time_t sunrise; time_t sunrise;
time_t sunset; time_t sunset;
time_t dusk; time_t dusk;
int cur_temp;
enum state state;
bool new_output; bool new_output;
struct wl_list outputs; struct wl_list outputs;
timer_t timer;
}; };
struct output { struct output {
struct wl_list link;
struct context *context; struct context *context;
struct wl_output *wl_output; struct wl_output *wl_output;
uint32_t id;
struct zwlr_gamma_control_v1 *gamma_control; struct zwlr_gamma_control_v1 *gamma_control;
uint32_t ramp_size;
int table_fd; int table_fd;
uint32_t id;
uint32_t ramp_size;
uint16_t *table; uint16_t *table;
struct wl_list link;
}; };
static struct zwlr_gamma_control_manager_v1 *gamma_control_manager = NULL; static struct zwlr_gamma_control_manager_v1 *gamma_control_manager = NULL;
@ -267,18 +248,18 @@ static void fill_gamma_table(uint16_t *table, uint32_t ramp_size, double rw, dou
} }
} }
static void set_temperature(struct context *ctx) { static void set_temperature(struct wl_list *outputs, int temp, int gamma) {
double rw, gw, bw; double rw, gw, bw;
calc_whitepoint(ctx->cur_temp, &rw, &gw, &bw); calc_whitepoint(temp, &rw, &gw, &bw);
fprintf(stderr, "setting temperature to %d K\n", ctx->cur_temp); fprintf(stderr, "setting temperature to %d K\n", temp);
struct output *output; struct output *output;
wl_list_for_each(output, &ctx->outputs, link) { wl_list_for_each(output, outputs, link) {
if (output->gamma_control == NULL || output->table_fd == -1) { if (output->gamma_control == NULL || output->table_fd == -1) {
continue; continue;
} }
fill_gamma_table(output->table, output->ramp_size, fill_gamma_table(output->table, output->ramp_size,
rw, gw, bw, ctx->gamma); rw, gw, bw, gamma);
lseek(output->table_fd, 0, SEEK_SET); lseek(output->table_fd, 0, SEEK_SET);
zwlr_gamma_control_v1_set_gamma(output->gamma_control, zwlr_gamma_control_v1_set_gamma(output->gamma_control,
output->table_fd); output->table_fd);
@ -325,51 +306,17 @@ static int interpolate_temperature(time_t now, time_t start, time_t stop, int te
return temp_start + temp_pos; return temp_start + temp_pos;
} }
static void update_temperature(struct context *ctx, time_t now) { static int get_temperature(const struct context *ctx, time_t now) {
recalc_stops(ctx, now); if (now < ctx->dawn) {
return ctx->low_temp;
int temp = 0; } else if (now < ctx->sunrise) {
enum state old_state = ctx->state; return interpolate_temperature(now, ctx->dawn, ctx->sunrise, ctx->low_temp, ctx->high_temp);
switch (ctx->state) { } else if (now < ctx->sunset) {
start: return ctx->high_temp;
case HIGH_TEMP: } else if (now < ctx->dusk) {
if (now < ctx->sunset && now >= ctx->sunrise) { return interpolate_temperature(now, ctx->sunset, ctx->dusk, ctx->high_temp, ctx->low_temp);
temp = ctx->high_temp; } else {
break; return ctx->low_temp;
}
ctx->state = ANIMATING_TO_LOW;
// fallthrough
case ANIMATING_TO_LOW:
if (now >= ctx->dawn && now < ctx->dusk) {
temp = interpolate_temperature(now, ctx->sunset, ctx->dusk, ctx->high_temp, ctx->low_temp);
break;
}
ctx->state = LOW_TEMP;
// fallthrough
case LOW_TEMP:
if (now >= ctx->dusk || now < ctx->dawn) {
temp = ctx->low_temp;
break;
}
ctx->state = ANIMATING_TO_HIGH;
// fallthrough
case ANIMATING_TO_HIGH:
if (now < ctx->sunrise) {
temp = interpolate_temperature(now, ctx->dawn, ctx->sunrise, ctx->low_temp, ctx->high_temp);
break;
}
ctx->state = HIGH_TEMP;
goto start;
}
if (ctx->state != old_state) {
fprintf(stderr, "changed state to %s\n", state_names[ctx->state]);
}
if (temp != ctx->cur_temp || ctx->new_output) {
ctx->cur_temp = temp;
ctx->new_output = false;
set_temperature(ctx);
} }
} }
@ -379,31 +326,23 @@ static int increments(int temp_diff, int duration) {
return time > LONG_SLEEP ? LONG_SLEEP : time; return time > LONG_SLEEP ? LONG_SLEEP : time;
} }
static void update_timer(struct context *ctx, time_t now) { static void update_timer(struct context *ctx, timer_t timer, time_t now) {
time_t deadline; time_t deadline;
switch (ctx->state) { if (now < ctx->dawn) {
case HIGH_TEMP:
deadline = ctx->sunset;
break;
case LOW_TEMP:
deadline = ctx->dawn; deadline = ctx->dawn;
if (deadline < now) { } else if (now < ctx->sunrise) {
deadline = ((deadline / 86400 + 1) * 86400);
}
break;
case ANIMATING_TO_HIGH:
deadline = now + increments(ctx->high_temp - ctx->low_temp, ctx->sunrise - ctx->dawn); deadline = now + increments(ctx->high_temp - ctx->low_temp, ctx->sunrise - ctx->dawn);
break; } else if (now < ctx->sunset) {
case ANIMATING_TO_LOW: deadline = ctx->sunset;
} else if (now < ctx->dusk) {
deadline = now + increments(ctx->high_temp - ctx->low_temp, ctx->dusk - ctx->sunset); deadline = now + increments(ctx->high_temp - ctx->low_temp, ctx->dusk - ctx->sunset);
break; } else {
default: deadline = ctx->dawn;
abort(); deadline = ((deadline / 86400 + 1) * 86400);
break;
} }
assert(deadline > now); assert(deadline > now);
set_timer(ctx->timer, deadline); set_timer(timer, deadline);
} }
static int display_poll(struct wl_display *display, short int events, int timeout) { static int display_poll(struct wl_display *display, short int events, int timeout) {
@ -473,15 +412,13 @@ int main(int argc, char *argv[]) {
// Initialize defaults // Initialize defaults
struct context ctx = { struct context ctx = {
.gamma = 1.0,
.high_temp = 6500, .high_temp = 6500,
.low_temp = 4000, .low_temp = 4000,
.duration = -1, .duration = -1,
.state = HIGH_TEMP,
}; };
double gamma = 1.0;
wl_list_init(&ctx.outputs); wl_list_init(&ctx.outputs);
struct sigaction timer_action = { struct sigaction timer_action = {
.sa_handler = timer_signal, .sa_handler = timer_signal,
.sa_flags = 0, .sa_flags = 0,
@ -520,7 +457,7 @@ int main(int argc, char *argv[]) {
ctx.duration = strtod(optarg, NULL) * 60; ctx.duration = strtod(optarg, NULL) * 60;
break; break;
case 'g': case 'g':
ctx.gamma = strtod(optarg, NULL); gamma = strtod(optarg, NULL);
break; break;
case 'h': case 'h':
default: default:
@ -557,15 +494,31 @@ int main(int argc, char *argv[]) {
} }
wl_display_roundtrip(display); wl_display_roundtrip(display);
time_t now = get_time_sec(NULL); time_t now = get_time_sec(NULL);
update_temperature(&ctx, now); recalc_stops(&ctx, now);
update_timer(&ctx, now);
int temp = get_temperature(&ctx, now);
set_temperature(&ctx.outputs, temp, gamma);
update_timer(&ctx, ctx.timer, now);
int old_temp = temp;
while (display_dispatch(display, -1) != -1) { while (display_dispatch(display, -1) != -1) {
if (timer_fired) { if (timer_fired) {
timer_fired = false; timer_fired = false;
now = get_time_sec(NULL); now = get_time_sec(NULL);
update_temperature(&ctx, now); recalc_stops(&ctx, now);
update_timer(&ctx, now); update_timer(&ctx, ctx.timer, now);
if ((temp = get_temperature(&ctx, now)) != old_temp) {
old_temp = temp;
ctx.new_output = false;
set_temperature(&ctx.outputs, temp, gamma);
}
} else if (ctx.new_output) {
ctx.new_output = false;
set_temperature(&ctx.outputs, temp, gamma);
} }
} }