Merge branch 'main' of git.zander.im:Zander671/rpi4b-temp-humidity
This commit is contained in:
commit
8c638f00a5
8
Makefile
8
Makefile
@ -9,7 +9,7 @@ SRCS=src/main.c src/util.c src/lcd.c src/ths.c src/button.c src/ui/screen.c\
|
||||
src/ui/datesel.c src/ui/statsby.c src/ui/datapoints.c src/ui/timesel.c\
|
||||
src/ui/statrange.c src/config.c src/ui/blankscreen.c src/drive.c\
|
||||
src/ui/exportscreen.c src/ui/setdatescreen.c src/ui/viewdatescreen.c\
|
||||
src/ui/powerscreen.c
|
||||
src/ui/powerscreen.c src/ui/settzscreen.c src/ui/cleardatascreen.c
|
||||
PROG=rpi4b-temp-humidity
|
||||
|
||||
all: config.conf bin/${PROG}
|
||||
@ -24,16 +24,19 @@ bin/main.o bin/util.o bin/lcd.o bin/ths.o bin/button.o: src/util.h
|
||||
bin/ui/screen.o bin/ui/statsby.o bin/ui/datesel.o: src/util.h
|
||||
bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/util.h
|
||||
bin/ui/viewdatescreen.o bin/ui/setdatescreen.o bin/ui/powerscreen.o: src/util.h
|
||||
bin/ui/settzscreen.o bin/ui/cleardatascreen.o: src/util.h
|
||||
|
||||
bin/main.o bin/lcd.o bin/screen.o bin/ui/datesel.o: src/lcd.h
|
||||
bin/ui/statsby.o bin/ui/timesel.o bin/ui/statrange.o: src/lcd.h
|
||||
bin/ui/datapoints.o bin/ui/viewdatescreen.o: src/lcd.h
|
||||
bin/ui/setdatescreen.o bin/ui/powerscreen.o: src/lcd.h
|
||||
bin/ui/settzscreen.o bin/ui/cleardatascreen.o: src/lcd.h
|
||||
|
||||
bin/main.o bin/ui/screen.o bin/ui/statsby.o: src/ui/screen.h
|
||||
bin/ui/datesel.o bin/ui/datapoints.o bin/ui/timesel.o: src/ui/screen.h
|
||||
bin/ui/statrange.o bin/ui/setdatescreen.o: src/ui/screen.h
|
||||
bin/ui/viewdatescreen.o bin/ui/powerscreen.o: src/ui/screen.h
|
||||
bin/ui/settzscreen.o bin/ui/cleardatascreen.o: src/ui/screen.h
|
||||
|
||||
bin/main.o bin/ui/datesel.o bin/ui/statsby.o: src/ui/datesel.h
|
||||
bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/ui/datesel.h
|
||||
@ -53,6 +56,8 @@ bin/main.o bin/ui/exportscreen.o: src/ui/exportscreen.h
|
||||
bin/main.o bin/ui/setdatescreen.o: src/ui/setdatescreen.h
|
||||
bin/main.o bin/ui/viewdatescreen.o: src/ui/viewdatescreen.h
|
||||
bin/main.o bin/ui/powerscreen.o: src/ui/powerscreen.h
|
||||
bin/main.o bin/ui/settzscreen.o: src/ui/settzscreen.h
|
||||
bin/main.o bin/ui/cleardatascreen.o: src/ui/cleardatascreen.h
|
||||
bin/main.o bin/drive.o bin/ui/exportscreen.o: src/drive.h
|
||||
|
||||
.if "${DEFAULT_TEMP_UNIT}" != "F" && "${DEFAULT_TEMP_UNIT}" != "C" &&\
|
||||
@ -71,6 +76,7 @@ DEFINES:=\
|
||||
-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\
|
||||
-DDEFAULT_TEMP_UNIT="${DEFAULT_TEMP_UNIT}"\
|
||||
-DDEFAULT_EXPORT_FILE_NAME="${DEFAULT_EXPORT_FILE_NAME}"\
|
||||
-DDEFAULT_TIMEZONE="${DEFAULT_TIMEZONE}"\
|
||||
|
||||
${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
||||
@mkdir -p ${.TARGET:H}
|
||||
|
@ -47,3 +47,8 @@ down_pin = 23
|
||||
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
||||
# .csv will be appended for CSV exports
|
||||
#export_file_name = DEFAULT_EXPORT_FILE_NAME
|
||||
|
||||
# The timezone used. This is overridden by the environment variable TZ. See the
|
||||
# tzset(3) manual page for more information about the format of this. Leave
|
||||
# blank for system default
|
||||
#timezone = DEFAULT_TIMEZONE
|
||||
|
@ -34,3 +34,8 @@ DEFAULT_TEMP_UNIT=F
|
||||
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
||||
# .csv will be appended for CSV exports
|
||||
DEFAULT_EXPORT_FILE_NAME=env_data
|
||||
|
||||
# The timezone used. This is overridden by the environment variable TZ. See the
|
||||
# tzset(3) manual page for more information about the format of this. Leave
|
||||
# blank for system default
|
||||
DEFAULT_TIMEZONE=
|
||||
|
10
src/config.c
10
src/config.c
@ -205,6 +205,10 @@ static void set_options_from_entries(struct figpar_config *entries,
|
||||
GLOBAL_OPTS.export_file_name = steal_opt_if_set(entries[17].value.str);
|
||||
LOG_VERBOSE("Using export_file_name: \"%s\"\n",
|
||||
GLOBAL_OPTS.export_file_name);
|
||||
|
||||
entries[18].type = FIGPAR_TYPE_NONE;
|
||||
GLOBAL_OPTS.timezone = steal_opt_if_set(entries[18].value.str);
|
||||
LOG_VERBOSE("Using timezone: \"%s\"\n", GLOBAL_OPTS.timezone);
|
||||
}
|
||||
|
||||
static char *strdup_default_opt(const char *def) {
|
||||
@ -317,6 +321,12 @@ void parse_config_file(const char *path) {
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_EXPORT_FILE_NAME))},
|
||||
},
|
||||
{
|
||||
.directive = "timezone",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_TIMEZONE))},
|
||||
},
|
||||
{ .directive = NULL },
|
||||
};
|
||||
size_t entry_count = sizeof(entries) / sizeof(struct figpar_config);
|
||||
|
38
src/main.c
38
src/main.c
@ -22,6 +22,8 @@
|
||||
#include "ui/viewdatescreen.h"
|
||||
#include "ui/setdatescreen.h"
|
||||
#include "ui/powerscreen.h"
|
||||
#include "ui/settzscreen.h"
|
||||
#include "ui/cleardatascreen.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
@ -84,9 +86,22 @@ void lock_stat_globals(void);
|
||||
*/
|
||||
#define unlock_stat_globals() pthread_mutex_unlock(&STAT_MUTEX)
|
||||
|
||||
// should we restart instead of exiting
|
||||
bool SHOULD_RESTART = false;
|
||||
bool NEED_CLEAR_TZ = false;
|
||||
|
||||
int main(int argc, char *const *argv) {
|
||||
parse_arguments(argc, argv);
|
||||
parse_config_file(GLOBAL_OPTS.config_path);
|
||||
if (GLOBAL_OPTS.timezone) {
|
||||
if (!getenv("TZ")) {
|
||||
setenv("TZ", GLOBAL_OPTS.timezone, true);
|
||||
NEED_CLEAR_TZ = true;
|
||||
} else {
|
||||
LOG_VERBOSE("Config timezone option shadowed by local environment variable\n");
|
||||
}
|
||||
}
|
||||
tzset();
|
||||
|
||||
gpio_handle_t handle = gpio_open_device(GLOBAL_OPTS.gpio_path);
|
||||
if (handle == GPIO_INVALID_HANDLE) {
|
||||
@ -121,9 +136,11 @@ int main(int argc, char *const *argv) {
|
||||
if (GLOBAL_OPTS.bl_pin >= 0) {
|
||||
screen_manager_add(screen_manager, blank_screen_new());
|
||||
}
|
||||
screen_manager_add(screen_manager, (Screen *) power_screen_new());
|
||||
screen_manager_add(screen_manager, (Screen *) view_date_screen_new());
|
||||
screen_manager_add(screen_manager, (Screen *) set_date_screen_new());
|
||||
screen_manager_add(screen_manager, (Screen *) power_screen_new());
|
||||
screen_manager_add(screen_manager, (Screen *) set_tz_screen_new());
|
||||
screen_manager_add(screen_manager, (Screen *) clear_data_screen_new());
|
||||
while (RUNNING) {
|
||||
lock_stat_globals();
|
||||
uint32_t temp = LAST_TEMP;
|
||||
@ -133,8 +150,6 @@ int main(int argc, char *const *argv) {
|
||||
// 10ms is probably faster than anyone will press a button
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
// this is mostly about LCD and DB cleanup, not freeing memory, this leaks a
|
||||
// lot when caused from something other than the main screen
|
||||
screen_manager_delete(screen_manager);
|
||||
lcd_close(lcd);
|
||||
if (pthread_join(bg_update, NULL) != 0) {
|
||||
@ -147,6 +162,14 @@ int main(int argc, char *const *argv) {
|
||||
pthread_mutex_destroy(&STAT_MUTEX);
|
||||
gpio_close(handle);
|
||||
cleanup_options(&GLOBAL_OPTS);
|
||||
if (SHOULD_RESTART) {
|
||||
LOG_VERBOSE("Re-invoking...\n");
|
||||
if (NEED_CLEAR_TZ) {
|
||||
unsetenv("TZ");
|
||||
}
|
||||
execv(argv[0], argv);
|
||||
err(1, "re-invoke with execv(2) failed");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -263,8 +286,13 @@ void update_stats(THS *ths, sqlite3_stmt *insert_statement) {
|
||||
sqlite3_bind_int(insert_statement, 3, humid);
|
||||
int status = sqlite3_step(insert_statement);
|
||||
if (status != SQLITE_DONE) {
|
||||
errx(1, "failed to insert temp. and humid. data into database: %s",
|
||||
sqlite3_errstr(status));
|
||||
warnx("failed to insert temp. and humid. data into database: %s",
|
||||
sqlite3_errstr(status));
|
||||
lock_stat_globals();
|
||||
// this means error
|
||||
LAST_TEMP = UINT32_MAX;
|
||||
LAST_HUMID = UINT32_MAX;
|
||||
unlock_stat_globals();
|
||||
}
|
||||
}
|
||||
|
||||
|
72
src/ui/cleardatascreen.c
Normal file
72
src/ui/cleardatascreen.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* cleardatascreen.c - Screen for rebooting or halting
|
||||
* Copyright (C) 2024 Alexander Rosenberg
|
||||
*
|
||||
* This program 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. See the LICENSE file for more information.
|
||||
*/
|
||||
#include "cleardatascreen.h"
|
||||
|
||||
#include <err.h>
|
||||
|
||||
static bool clear_data_screen_display(ClearDataScreen *screen,
|
||||
SensorState *state) {
|
||||
if (state->back_down || (!screen->choice && state->sel_down) ||
|
||||
(screen->done && (state->sel_down || state->up_down ||
|
||||
state->down_down))) {
|
||||
screen->done = false;
|
||||
screen->error = false;
|
||||
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||
LCD_DISPLAY_ON);
|
||||
return true;
|
||||
}
|
||||
if (state->sel_down) {
|
||||
// TODO clear data
|
||||
char *errmsg;
|
||||
sqlite3_exec(state->db, "DELETE FROM env_data;", NULL, NULL, &errmsg);
|
||||
if (errmsg) {
|
||||
warnx("sqlite3 error when clearing database: \"%s\"\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
screen->error = true;
|
||||
} else {
|
||||
LOG_VERBOSE("Database cleared!\n");
|
||||
}
|
||||
screen->need_redraw = true;
|
||||
screen->done = true;
|
||||
return false;
|
||||
}
|
||||
if (screen->need_redraw || state->force_draw || state->up_down ||
|
||||
state->down_down) {
|
||||
screen->need_redraw = false;
|
||||
screen->choice = abs((screen->choice + state->up_down -
|
||||
state->down_down) % 2);
|
||||
lcd_clear(state->lcd);
|
||||
lcd_move_to(state->lcd, 0, 0);
|
||||
if (screen->done) {
|
||||
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||
LCD_DISPLAY_ON);
|
||||
lcd_write_string(state->lcd, screen->error ? "Error!" : "Done!");
|
||||
} else {
|
||||
lcd_write_string(state->lcd, "Really clear?");
|
||||
lcd_move_to(state->lcd, 1, 0);
|
||||
lcd_write_string(state->lcd, ">No >Yes");
|
||||
lcd_move_to(state->lcd, 1, 4 * screen->choice);
|
||||
lcd_display_control(state->lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON,
|
||||
LCD_DISPLAY_ON);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ClearDataScreen *clear_data_screen_new() {
|
||||
ClearDataScreen *s = malloc_checked(sizeof(ClearDataScreen));
|
||||
screen_init(&s->parent, "Clear data",
|
||||
(ScreenDispatchFunc) clear_data_screen_display,
|
||||
(ScreenCleanupFunc) free);
|
||||
s->choice = 0;
|
||||
s->need_redraw = false;
|
||||
s->done = false;
|
||||
return s;
|
||||
}
|
29
src/ui/cleardatascreen.h
Normal file
29
src/ui/cleardatascreen.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* cleardatascreen.c - Screen for rebooting or halting
|
||||
* Copyright (C) 2024 Alexander Rosenberg
|
||||
*
|
||||
* This program 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. See the LICENSE file for more information.
|
||||
*/
|
||||
#ifndef INCLUDED_CLEARDATASCREEN_H
|
||||
#define INCLUDED_CLEARDATASCREEN_H
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
typedef struct {
|
||||
Screen parent;
|
||||
int choice;
|
||||
bool done;
|
||||
bool need_redraw;
|
||||
bool error;
|
||||
} ClearDataScreen;
|
||||
|
||||
/*
|
||||
* Create a new ClearDataScreen that will allow the user to wipe data collected
|
||||
* by the device.
|
||||
*/
|
||||
ClearDataScreen *clear_data_screen_new(void);
|
||||
|
||||
#endif
|
@ -375,7 +375,9 @@ static bool export_screen_dispatch(ExportScreen *screen,
|
||||
if (confirm_status == EXPORT_CONFIRM_YES) {
|
||||
kill(0, SIGUSR1);
|
||||
pthread_cancel(screen->backgroud_oper);
|
||||
pthread_detach(screen->backgroud_oper);
|
||||
drive_unmount(cur_drive, true);
|
||||
screen->bg_inited = false;
|
||||
screen->stage = EXPORT_SCREEN_CANCELED;
|
||||
screen->need_redraw = true;
|
||||
} else if (confirm_status == EXPORT_CONFIRM_NO) {
|
||||
@ -395,11 +397,18 @@ static bool export_screen_dispatch(ExportScreen *screen,
|
||||
return false;
|
||||
}
|
||||
|
||||
static void export_screen_cleanup(ExportScreen *screen) {
|
||||
if (screen->bg_inited) {
|
||||
pthread_cancel(screen->backgroud_oper);
|
||||
pthread_detach(screen->backgroud_oper);
|
||||
}
|
||||
}
|
||||
|
||||
ExportScreen *export_screen_new() {
|
||||
ExportScreen *s = malloc_checked(sizeof(ExportScreen));
|
||||
screen_init(&s->parent, "Export",
|
||||
(ScreenDispatchFunc) export_screen_dispatch,
|
||||
(ScreenCleanupFunc) free);
|
||||
(ScreenCleanupFunc) export_screen_cleanup);
|
||||
s->read_drives = true;
|
||||
s->stage = EXPORT_SCREEN_FORMAT;
|
||||
s->format = EXPORT_FORMAT_SQLITE;
|
||||
|
@ -18,11 +18,14 @@ static bool power_screen_dispatch(PowerScreen *screen,
|
||||
if (state->back_down) {
|
||||
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||
LCD_DISPLAY_ON);
|
||||
screen->choice = POWER_SCREEN_REBOOT;
|
||||
screen->choice = POWER_SCREEN_RESTART_PROGRAM;
|
||||
return true;
|
||||
}
|
||||
if (state->sel_down) {
|
||||
switch (screen->choice) {
|
||||
case POWER_SCREEN_RESTART_PROGRAM:
|
||||
request_restart();
|
||||
break;
|
||||
case POWER_SCREEN_REBOOT:
|
||||
LOG_VERBOSE("Rebooting...");
|
||||
reboot(RB_AUTOBOOT);
|
||||
@ -51,6 +54,9 @@ static bool power_screen_dispatch(PowerScreen *screen,
|
||||
lcd_move_to(state->lcd, 1, 0);
|
||||
lcd_write_char(state->lcd, '>');
|
||||
switch (screen->choice) {
|
||||
case POWER_SCREEN_RESTART_PROGRAM:
|
||||
lcd_write_string(state->lcd, "REINIT");
|
||||
break;
|
||||
case POWER_SCREEN_REBOOT:
|
||||
lcd_write_string(state->lcd, "REBOOT");
|
||||
break;
|
||||
@ -70,6 +76,6 @@ PowerScreen *power_screen_new(void) {
|
||||
screen_init(&s->parent, "Power options",
|
||||
(ScreenDispatchFunc) power_screen_dispatch,
|
||||
(ScreenCleanupFunc) free);
|
||||
s->choice = POWER_SCREEN_REBOOT;
|
||||
s->choice = POWER_SCREEN_RESTART_PROGRAM;
|
||||
return s;
|
||||
}
|
||||
|
@ -15,7 +15,8 @@
|
||||
typedef struct {
|
||||
Screen parent;
|
||||
enum {
|
||||
POWER_SCREEN_REBOOT = 0,
|
||||
POWER_SCREEN_RESTART_PROGRAM = 0,
|
||||
POWER_SCREEN_REBOOT,
|
||||
POWER_SCREEN_POWEROFF,
|
||||
POWER_SCREEN_NCHOICE,
|
||||
};
|
||||
|
@ -149,9 +149,14 @@ static bool stats_screen_dispatch(StatsScreen *screen, SensorState *state) {
|
||||
lcd_clear(state->lcd);
|
||||
lcd_write_string(state->lcd, "temp humi time");
|
||||
char buff[17];
|
||||
int cur_len = snprintf(buff, sizeof(buff), "%-4.1f%c %3" PRIu32 "%% ",
|
||||
int cur_len;
|
||||
if (state->temp == UINT32_MAX || state->humid == UINT32_MAX) {
|
||||
cur_len = snprintf(buff, sizeof(buff), "ERROR! ");
|
||||
} else {
|
||||
cur_len = snprintf(buff, sizeof(buff), "%-4.1f%c %3" PRIu32 "%% ",
|
||||
convert_temperature(state->temp),
|
||||
GLOBAL_OPTS.temp_unit, state->humid);
|
||||
}
|
||||
struct tm lt;
|
||||
localtime_r(&cur_time, <);
|
||||
strftime(buff + cur_len, sizeof(buff) - cur_len,
|
||||
|
585
src/ui/settzscreen.c
Normal file
585
src/ui/settzscreen.c
Normal file
@ -0,0 +1,585 @@
|
||||
/*
|
||||
* settzscreen.c - Screen for setting the timezone
|
||||
* Copyright (C) 2024 Alexander Rosenberg
|
||||
*
|
||||
* This program 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. See the LICENSE file for more information.
|
||||
*/
|
||||
#include "settzscreen.h"
|
||||
|
||||
#include <spawn.h>
|
||||
#include <err.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
static void set_tz_screen_reset(SetTZScreen *screen, SensorState *state) {
|
||||
screen->has_dst = false;
|
||||
screen->stage = SET_TZ_OFFSET;
|
||||
screen->off_sel_stage = TZ_OFFSET_SEL_HOUR;
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_TYPE;
|
||||
tz_offset_reset(&screen->offset);
|
||||
tz_offset_reset(&screen->dst_offset);
|
||||
screen->did_set = false;
|
||||
if (state) {
|
||||
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||
LCD_DISPLAY_ON);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_tz_offset_normalize(TZOffset *offset, bool allow_negative) {
|
||||
if (offset->second < 0) {
|
||||
offset->second = 59;
|
||||
--offset->minute;
|
||||
} else if (offset->second > 59) {
|
||||
offset->second = 0;
|
||||
++offset->minute;
|
||||
}
|
||||
if (offset->minute < 0) {
|
||||
offset->minute = 59;
|
||||
--offset->hour;
|
||||
} else if (offset->minute > 59) {
|
||||
offset->minute = 0;
|
||||
++offset->hour;
|
||||
}
|
||||
if (allow_negative) {
|
||||
if (offset->hour < -24) {
|
||||
offset->hour = 24;
|
||||
} else if (offset->hour > 24) {
|
||||
offset->hour = -24;
|
||||
}
|
||||
} else {
|
||||
if (offset->hour < 0) {
|
||||
offset->hour = 24;
|
||||
} else if (offset->hour > 24) {
|
||||
offset->hour = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SetTZScreenAction set_tz_dispatch_select_offset(TZOffsetSelectStage *stage,
|
||||
SensorState *state,
|
||||
TZOffset *target,
|
||||
const char *label,
|
||||
bool force_redraw,
|
||||
bool allow_negative) {
|
||||
if (state->back_down) {
|
||||
if (*stage == TZ_OFFSET_SEL_HOUR) {
|
||||
*stage = TZ_OFFSET_SEL_SECOND;
|
||||
return SET_TZ_BACK;
|
||||
}
|
||||
--*stage;
|
||||
force_redraw = true;
|
||||
}
|
||||
if (state->up_down || state->down_down) {
|
||||
force_redraw = true;
|
||||
int change = state->up_down - state->down_down;
|
||||
switch (*stage) {
|
||||
case TZ_OFFSET_SEL_HOUR:
|
||||
target->hour += change;
|
||||
break;
|
||||
case TZ_OFFSET_SEL_MINUTE:
|
||||
target->minute += change;
|
||||
break;
|
||||
case TZ_OFFSET_SEL_SECOND:
|
||||
target->second += change;
|
||||
break;
|
||||
}
|
||||
set_tz_offset_normalize(target, allow_negative);
|
||||
}
|
||||
if (state->sel_down) {
|
||||
if (*stage == TZ_OFFSET_SEL_SECOND) {
|
||||
*stage = TZ_OFFSET_SEL_HOUR;
|
||||
return SET_TZ_NEXT;
|
||||
}
|
||||
++*stage;
|
||||
force_redraw = true;
|
||||
}
|
||||
if (force_redraw) {
|
||||
const char *format;
|
||||
int cursor_pos;
|
||||
switch (*stage) {
|
||||
case TZ_OFFSET_SEL_HOUR:
|
||||
format = ">%0*d:%02d:%02d";
|
||||
cursor_pos = 0;
|
||||
break;
|
||||
case TZ_OFFSET_SEL_MINUTE:
|
||||
format = "%0*d:>%02d:%02d";
|
||||
cursor_pos = target->hour < 0 ? 4 : 3;
|
||||
break;
|
||||
case TZ_OFFSET_SEL_SECOND:
|
||||
format = "%0*d:%02d:>%02d";
|
||||
cursor_pos = target->hour < 0 ? 7 : 6;
|
||||
break;
|
||||
default:
|
||||
format = "";
|
||||
cursor_pos = 0;
|
||||
break;
|
||||
}
|
||||
char time_str[17];
|
||||
snprintf(time_str, 17, format, target->hour < 0 ? 3 : 2, target->hour,
|
||||
target->minute, target->second);
|
||||
lcd_clear(state->lcd);
|
||||
lcd_move_to(state->lcd, 0, 0);
|
||||
lcd_write_string(state->lcd, label);
|
||||
lcd_move_to(state->lcd, 1, 0);
|
||||
lcd_write_string(state->lcd, time_str);
|
||||
lcd_move_to(state->lcd, 1, cursor_pos);
|
||||
lcd_display_control(state->lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON,
|
||||
LCD_DISPLAY_ON);
|
||||
}
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
|
||||
static void set_tz_prompt_has_dst(SetTZScreen *screen,
|
||||
SensorState *state) {
|
||||
if (state->back_down) {
|
||||
--screen->stage;
|
||||
screen->need_redraw = true;
|
||||
screen->off_sel_stage = TZ_OFFSET_SEL_SECOND;
|
||||
return;
|
||||
}
|
||||
if (state->sel_down) {
|
||||
if (screen->has_dst) {
|
||||
screen->stage = SET_TZ_DST_OFFSET;
|
||||
} else {
|
||||
screen->stage = SET_TZ_FINISH;
|
||||
}
|
||||
screen->need_redraw = true;
|
||||
screen->off_sel_stage = TZ_OFFSET_SEL_HOUR;
|
||||
return;
|
||||
}
|
||||
if (state->up_down || state->down_down) {
|
||||
screen->has_dst = abs((((int) screen->has_dst) + state->up_down -
|
||||
state->down_down) % 2);
|
||||
screen->need_redraw = true;
|
||||
}
|
||||
if (screen->need_redraw) {
|
||||
screen->need_redraw = false;
|
||||
lcd_clear(state->lcd);
|
||||
lcd_move_to(state->lcd, 0, 0);
|
||||
lcd_write_string(state->lcd, "Has DST?");
|
||||
lcd_move_to(state->lcd, 1, 0);
|
||||
lcd_write_string(state->lcd, ">No >Yes");
|
||||
lcd_move_to(state->lcd, 1, screen->has_dst ? 4 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *DST_BOUND_TYPE_LABELS[] = {
|
||||
"Exact Julian",
|
||||
"Leap Julian",
|
||||
"Month Day Week"
|
||||
};
|
||||
static SetTZScreenAction set_tz_bound_select_type(SetTZScreen *screen,
|
||||
SensorState *state,
|
||||
DSTBound *target,
|
||||
const char *label) {
|
||||
if (state->back_down) {
|
||||
screen->off_sel_stage = TZ_OFFSET_SEL_SECOND;
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_TIME;
|
||||
return SET_TZ_BACK;
|
||||
}
|
||||
if (state->sel_down) {
|
||||
switch (target->type) {
|
||||
case DST_BOUND_EXACT_JULIAN:
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_JULIAN;
|
||||
target->julian_day = 1;
|
||||
break;
|
||||
case DST_BOUND_LEAP_JULAIN:
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_JULIAN;
|
||||
target->julian_day = 0;
|
||||
break;
|
||||
case DST_BOUND_MND:
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_MONTH;
|
||||
target->month = 1;
|
||||
target->week = 1;
|
||||
target->day = 0;
|
||||
break;
|
||||
}
|
||||
target->time.hour = 2;
|
||||
target->time.minute = 0;
|
||||
target->time.second = 0;
|
||||
screen->julian_didgit = 3;
|
||||
screen->need_redraw = true;
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
if (state->up_down || state->down_down) {
|
||||
int new_type = (((int) target->type) + state->up_down - state->down_down) % 3;
|
||||
if (new_type < 0) {
|
||||
target->type = 2;
|
||||
} else {
|
||||
target->type = new_type;
|
||||
}
|
||||
screen->need_redraw = true;
|
||||
}
|
||||
if (screen->need_redraw) {
|
||||
screen->need_redraw = false;
|
||||
lcd_clear(state->lcd);
|
||||
lcd_move_to(state->lcd, 0, 0);
|
||||
lcd_write_string(state->lcd, label);
|
||||
lcd_write_string(state->lcd, " Type:");
|
||||
lcd_move_to(state->lcd, 1, 0);
|
||||
lcd_write_char(state->lcd, '>');
|
||||
lcd_write_string(state->lcd, DST_BOUND_TYPE_LABELS[target->type]);
|
||||
lcd_move_to(state->lcd, 1, 0);
|
||||
}
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
|
||||
static const char *MONTH_ABBREVS[] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
|
||||
"Dec",
|
||||
};
|
||||
static const char *WEEKDAY_ABBREVS[] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
|
||||
};
|
||||
static SetTZScreenAction set_tz_bound_select_mnd(SetTZScreen *screen,
|
||||
SensorState *state,
|
||||
DSTBound *target,
|
||||
const char *label) {
|
||||
if (state->back_down) {
|
||||
--screen->bound_sel_stage;
|
||||
screen->need_redraw = true;
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
if (state->sel_down) {
|
||||
screen->need_redraw = true;
|
||||
if (screen->bound_sel_stage == DST_BOUND_SEL_DAY) {
|
||||
screen->off_sel_stage = TZ_OFFSET_SEL_HOUR;
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_TIME;
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
++screen->bound_sel_stage;
|
||||
}
|
||||
if (state->up_down || state->down_down) {
|
||||
int change = state->up_down - state->down_down;
|
||||
switch (screen->bound_sel_stage) {
|
||||
case DST_BOUND_SEL_MONTH:
|
||||
target->month += change;
|
||||
if (target->month < 1) {
|
||||
target->month = 1;
|
||||
} else if (target->month > 12) {
|
||||
target->month = 12;
|
||||
}
|
||||
break;
|
||||
case DST_BOUND_SEL_WEEK:
|
||||
target->week += change;
|
||||
if (target->week < 1) {
|
||||
target->week = 1;
|
||||
} else if (target->week > 5) {
|
||||
target->week = 5;
|
||||
}
|
||||
break;
|
||||
case DST_BOUND_SEL_DAY:
|
||||
target->day += change;
|
||||
if (target->day < 0) {
|
||||
target->day = 0;
|
||||
} else if (target->day > 6) {
|
||||
target->day = 6;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break; // should not happen
|
||||
}
|
||||
screen->need_redraw = true;
|
||||
}
|
||||
if (screen->need_redraw) {
|
||||
screen->need_redraw = false;
|
||||
const char *format;
|
||||
int cursor_pos;
|
||||
switch(screen->bound_sel_stage) {
|
||||
case DST_BOUND_SEL_MONTH:
|
||||
format = ">%s-%d-%s";
|
||||
cursor_pos = 0;
|
||||
break;
|
||||
case DST_BOUND_SEL_WEEK:
|
||||
format = "%s->%d-%s";
|
||||
cursor_pos = 4;
|
||||
break;
|
||||
case DST_BOUND_SEL_DAY:
|
||||
format = "%s-%d->%s";
|
||||
cursor_pos = 7;
|
||||
break;
|
||||
default:
|
||||
// should not happen
|
||||
format = "";
|
||||
cursor_pos = 0;
|
||||
break;
|
||||
}
|
||||
char sel_str[17];
|
||||
snprintf(sel_str, 17, format, MONTH_ABBREVS[target->month - 1],
|
||||
target->week, WEEKDAY_ABBREVS[target->day]);
|
||||
lcd_clear(state->lcd);
|
||||
lcd_move_to(state->lcd, 0, 0);
|
||||
lcd_write_string(state->lcd, "Mon-Week-Day");
|
||||
lcd_move_to(state->lcd, 1, 0);
|
||||
lcd_write_string(state->lcd, sel_str);
|
||||
lcd_move_to(state->lcd, 1, cursor_pos);
|
||||
}
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
|
||||
static int set_tz_normalize_julian(int julian, bool allow_zero) {
|
||||
if (julian <= 0) {
|
||||
return allow_zero ? 0 : 1;
|
||||
} else if (julian > 365) {
|
||||
return 365;
|
||||
}
|
||||
return julian;
|
||||
}
|
||||
|
||||
static SetTZScreenAction set_tz_dispatch_select_julian(SetTZScreen *screen,
|
||||
SensorState *state,
|
||||
DSTBound *target,
|
||||
const char *label) {
|
||||
if (state->back_down) {
|
||||
screen->need_redraw = true;
|
||||
if (screen->julian_didgit == 3) {
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_TYPE;
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
++screen->julian_didgit;
|
||||
}
|
||||
if (state->sel_down) {
|
||||
screen->need_redraw = true;
|
||||
if (screen->julian_didgit == 1) {
|
||||
screen->off_sel_stage = TZ_OFFSET_SEL_HOUR;
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_TIME;
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
--screen->julian_didgit;
|
||||
}
|
||||
if (state->up_down || state->down_down) {
|
||||
screen->need_redraw = true;
|
||||
int change = state->up_down - state->down_down;
|
||||
int scale = 0;
|
||||
switch (screen->julian_didgit) {
|
||||
case 1: // ones
|
||||
scale = 1;
|
||||
break;
|
||||
case 2: // tens
|
||||
scale = 10;
|
||||
break;
|
||||
case 3: // hundreds
|
||||
scale = 100;
|
||||
break;
|
||||
}
|
||||
bool allow_zero = screen->off_sel_stage == DST_BOUND_LEAP_JULAIN;
|
||||
int new_day = target->julian_day + scale * change;
|
||||
target->julian_day = set_tz_normalize_julian(new_day, allow_zero);
|
||||
}
|
||||
if (screen->need_redraw) {
|
||||
screen->need_redraw = false;
|
||||
char jul_str[4];
|
||||
snprintf(jul_str, 4, "%03d", target->julian_day);
|
||||
lcd_clear(state->lcd);
|
||||
lcd_move_to(state->lcd, 0, 0);
|
||||
lcd_write_string(state->lcd, "Julian Day");
|
||||
lcd_move_to(state->lcd, 1, 0);
|
||||
lcd_write_string(state->lcd, jul_str);
|
||||
lcd_move_to(state->lcd, 1, 3 - screen->julian_didgit);
|
||||
}
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
|
||||
static SetTZScreenAction set_tz_dispatch_select_bound(SetTZScreen *screen,
|
||||
SensorState *state,
|
||||
DSTBound *target,
|
||||
const char *label) {
|
||||
SetTZScreenAction action;
|
||||
switch (screen->bound_sel_stage) {
|
||||
case DST_BOUND_SEL_TYPE:
|
||||
return set_tz_bound_select_type(screen, state, target, label);
|
||||
case DST_BOUND_SEL_MONTH:
|
||||
case DST_BOUND_SEL_WEEK:
|
||||
case DST_BOUND_SEL_DAY:
|
||||
return set_tz_bound_select_mnd(screen, state, target, label);
|
||||
case DST_BOUND_SEL_JULIAN:
|
||||
return set_tz_dispatch_select_julian(screen, state, target, label);
|
||||
case DST_BOUND_SEL_TIME:
|
||||
action = set_tz_dispatch_select_offset(&screen->off_sel_stage, state,
|
||||
&target->time, "Effect Time:",
|
||||
screen->need_redraw, false);
|
||||
switch (action) {
|
||||
case SET_TZ_NEXT:
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_TYPE;
|
||||
case SET_TZ_CONTINUE:
|
||||
screen->need_redraw = false;
|
||||
return action;
|
||||
case SET_TZ_BACK:
|
||||
if (target->type == DST_BOUND_MND) {
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_DAY;
|
||||
} else {
|
||||
screen->julian_didgit = 1;
|
||||
screen->bound_sel_stage = DST_BOUND_SEL_JULIAN;
|
||||
}
|
||||
screen->need_redraw = true;
|
||||
return SET_TZ_CONTINUE;
|
||||
}
|
||||
default:
|
||||
return SET_TZ_BACK;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_tz_write_dst_bound(FILE *tz, DSTBound *bound) {
|
||||
fputc(',', tz);
|
||||
switch (bound->type) {
|
||||
case DST_BOUND_EXACT_JULIAN:
|
||||
fprintf(tz, "J%d", bound->julian_day);
|
||||
break;
|
||||
case DST_BOUND_LEAP_JULAIN:
|
||||
fprintf(tz, "%d", bound->julian_day);
|
||||
break;
|
||||
case DST_BOUND_MND:
|
||||
fprintf(tz, "M%d.%d.%d", bound->month, bound->week, bound->day);
|
||||
break;
|
||||
}
|
||||
fprintf(tz, "/%02d:%02d:%02d", bound->time.hour, bound->time.minute,
|
||||
bound->time.second);
|
||||
}
|
||||
|
||||
static bool set_tz_screen_finish(SetTZScreen *screen,
|
||||
SensorState *state) {
|
||||
if (!screen->did_set) {
|
||||
screen->did_set = true;
|
||||
size_t len;
|
||||
char *buf;
|
||||
FILE *tz = open_memstream(&buf, &len);
|
||||
TZOffset *off = &screen->offset;
|
||||
fprintf(tz, "timezone=CTZ%02d:%02d:%02d", off->hour, off->minute, off->second);
|
||||
if (screen->has_dst) {
|
||||
TZOffset *dst_off = &screen->dst_offset;
|
||||
fprintf(tz, "CDST%02d:%02d:%02d", dst_off->hour, dst_off->minute,
|
||||
dst_off->second);
|
||||
set_tz_write_dst_bound(tz, &screen->start_bound);
|
||||
set_tz_write_dst_bound(tz, &screen->end_bound);
|
||||
}
|
||||
fclose(tz);
|
||||
char *const args[] = {
|
||||
"sysrc",
|
||||
"-f",
|
||||
GLOBAL_OPTS.config_path,
|
||||
buf,
|
||||
NULL
|
||||
};
|
||||
pid_t pid;
|
||||
int exit_status;
|
||||
if (posix_spawnp(&pid, "sysrc", NULL, NULL, args, environ) != 0) {
|
||||
warnx("failed to start sysrc(8)");
|
||||
} else if (waitpid(pid, &exit_status, 0) < 0) {
|
||||
warn("waitpid(2) for sysrc(8) failed");
|
||||
} else if (exit_status != 0) {
|
||||
warnx("sysrc(8) failed to set timezone");
|
||||
} else {
|
||||
LOG_VERBOSE("Set timezone to: \"%s\"\n", buf);
|
||||
}
|
||||
free(buf);
|
||||
request_restart();
|
||||
}
|
||||
if (state->up_down || state->back_down ||
|
||||
state->sel_down || state->down_down) {
|
||||
set_tz_screen_reset(screen, state);
|
||||
return true;
|
||||
}
|
||||
if (screen->need_redraw) {
|
||||
screen->need_redraw = false;
|
||||
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
||||
LCD_DISPLAY_ON);
|
||||
lcd_clear(state->lcd);
|
||||
lcd_move_to(state->lcd, 0, 0);
|
||||
lcd_write_string(state->lcd, "Done!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool set_tz_screen_dispatch(SetTZScreen *screen,
|
||||
SensorState *state) {
|
||||
if (state->force_draw) {
|
||||
screen->need_redraw = true;
|
||||
}
|
||||
SetTZScreenAction action;
|
||||
switch (screen->stage) {
|
||||
case SET_TZ_OFFSET:
|
||||
action = set_tz_dispatch_select_offset(&screen->off_sel_stage, state,
|
||||
&screen->offset, "Offset",
|
||||
screen->need_redraw, true);
|
||||
screen->need_redraw = false;
|
||||
switch (action) {
|
||||
case SET_TZ_BACK:
|
||||
set_tz_screen_reset(screen, state);
|
||||
return true;
|
||||
case SET_TZ_CONTINUE:
|
||||
break; // ignore
|
||||
case SET_TZ_NEXT:
|
||||
screen->need_redraw = true;
|
||||
++screen->stage;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SET_TZ_HAS_DST:
|
||||
set_tz_prompt_has_dst(screen, state);
|
||||
break;
|
||||
case SET_TZ_DST_OFFSET:
|
||||
action = set_tz_dispatch_select_offset(&screen->off_sel_stage, state,
|
||||
&screen->dst_offset,
|
||||
"DST Offset",
|
||||
screen->need_redraw, true);
|
||||
screen->need_redraw = false;
|
||||
switch (action) {
|
||||
case SET_TZ_BACK:
|
||||
--screen->stage;
|
||||
screen->need_redraw = true;
|
||||
case SET_TZ_CONTINUE:
|
||||
break; // ignore
|
||||
case SET_TZ_NEXT:
|
||||
screen->need_redraw = true;
|
||||
++screen->stage;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SET_TZ_DST_START:
|
||||
action = set_tz_dispatch_select_bound(screen, state,
|
||||
&screen->start_bound, "Start");
|
||||
switch (action) {
|
||||
case SET_TZ_BACK:
|
||||
--screen->stage;
|
||||
screen->need_redraw = true;
|
||||
case SET_TZ_CONTINUE:
|
||||
break; // ignore
|
||||
case SET_TZ_NEXT:
|
||||
screen->need_redraw = true;
|
||||
++screen->stage;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SET_TZ_DST_END:
|
||||
action = set_tz_dispatch_select_bound(screen, state,
|
||||
&screen->end_bound, "End");
|
||||
switch (action) {
|
||||
case SET_TZ_BACK:
|
||||
--screen->stage;
|
||||
screen->need_redraw = true;
|
||||
case SET_TZ_CONTINUE:
|
||||
break; // ignore
|
||||
case SET_TZ_NEXT:
|
||||
screen->need_redraw = true;
|
||||
++screen->stage;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SET_TZ_FINISH:
|
||||
return set_tz_screen_finish(screen, state);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SetTZScreen *set_tz_screen_new() {
|
||||
SetTZScreen *s = malloc_checked(sizeof(SetTZScreen));
|
||||
screen_init(&s->parent, "Set timezone",
|
||||
(ScreenDispatchFunc) set_tz_screen_dispatch,
|
||||
(ScreenCleanupFunc) free);
|
||||
set_tz_screen_reset(s, NULL);
|
||||
return s;
|
||||
}
|
88
src/ui/settzscreen.h
Normal file
88
src/ui/settzscreen.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* settzscreen.h - Screen for setting the timezone
|
||||
* Copyright (C) 2024 Alexander Rosenberg
|
||||
*
|
||||
* This program 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. See the LICENSE file for more information.
|
||||
*/
|
||||
#ifndef INCLUDED_SETTZSCREEN_H
|
||||
#define INCLUDED_SETTZSCREEN_H
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
typedef struct {
|
||||
int hour;
|
||||
int minute;
|
||||
int second;
|
||||
} TZOffset;
|
||||
|
||||
#define tz_offset_reset(o) ((o)->hour = (o)->minute = (o)->second = 0)
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
DST_BOUND_EXACT_JULIAN = 0,
|
||||
DST_BOUND_LEAP_JULAIN,
|
||||
DST_BOUND_MND,
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
int month;
|
||||
int week;
|
||||
int day;
|
||||
};
|
||||
int julian_day;
|
||||
};
|
||||
TZOffset time;
|
||||
} DSTBound;
|
||||
|
||||
typedef enum {
|
||||
TZ_OFFSET_SEL_HOUR,
|
||||
TZ_OFFSET_SEL_MINUTE,
|
||||
TZ_OFFSET_SEL_SECOND,
|
||||
} TZOffsetSelectStage;
|
||||
|
||||
typedef enum {
|
||||
DST_BOUND_SEL_TYPE,
|
||||
DST_BOUND_SEL_MONTH,
|
||||
DST_BOUND_SEL_WEEK,
|
||||
DST_BOUND_SEL_DAY,
|
||||
DST_BOUND_SEL_JULIAN,
|
||||
DST_BOUND_SEL_TIME,
|
||||
} DSTBoundSelectStage;
|
||||
|
||||
typedef enum {
|
||||
SET_TZ_BACK,
|
||||
SET_TZ_CONTINUE,
|
||||
SET_TZ_NEXT,
|
||||
} SetTZScreenAction;
|
||||
|
||||
typedef struct {
|
||||
Screen parent;
|
||||
enum {
|
||||
SET_TZ_OFFSET,
|
||||
SET_TZ_HAS_DST,
|
||||
SET_TZ_DST_OFFSET,
|
||||
SET_TZ_DST_START,
|
||||
SET_TZ_DST_END,
|
||||
SET_TZ_FINISH,
|
||||
} stage;
|
||||
bool need_redraw;
|
||||
TZOffsetSelectStage off_sel_stage;
|
||||
DSTBoundSelectStage bound_sel_stage;
|
||||
int julian_didgit;
|
||||
TZOffset offset;
|
||||
bool has_dst;
|
||||
TZOffset dst_offset;
|
||||
DSTBound start_bound;
|
||||
DSTBound end_bound;
|
||||
bool did_set;
|
||||
} SetTZScreen;
|
||||
|
||||
/*
|
||||
* Create a new SetTZScreen that will allow the user the set the time zone.
|
||||
*/
|
||||
SetTZScreen *set_tz_screen_new(void);
|
||||
|
||||
#endif
|
11
src/util.c
11
src/util.c
@ -19,6 +19,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
const char *PERIOD_LABELS[] = {
|
||||
"HOUR",
|
||||
@ -29,6 +30,10 @@ const char *PERIOD_LABELS[] = {
|
||||
};
|
||||
const size_t NPERIOD = sizeof(PERIOD_LABELS) / sizeof(char *);
|
||||
|
||||
// these are defined in main.c
|
||||
extern _Atomic bool RUNNING;
|
||||
extern bool SHOULD_RESTART;
|
||||
|
||||
Options GLOBAL_OPTS;
|
||||
|
||||
void cleanup_options(Options *opts) {
|
||||
@ -39,6 +44,7 @@ void cleanup_options(Options *opts) {
|
||||
free(opts->fail_key);
|
||||
free(opts->database_location);
|
||||
free(opts->export_file_name);
|
||||
free(opts->timezone);
|
||||
}
|
||||
|
||||
void *malloc_checked(size_t n) {
|
||||
@ -565,3 +571,8 @@ bool mkdirs(const char *path, mode_t mode) {
|
||||
free(copy);
|
||||
return true;
|
||||
}
|
||||
|
||||
void request_restart() {
|
||||
RUNNING = false;
|
||||
SHOULD_RESTART = true;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ typedef struct {
|
||||
TemperatureUnit temp_unit;
|
||||
|
||||
char *export_file_name; // file to export to
|
||||
char *timezone; // default timezone
|
||||
} Options;
|
||||
extern Options GLOBAL_OPTS;
|
||||
|
||||
@ -248,4 +249,10 @@ bool export_database_as_csv(sqlite3 *db, const char *dest);
|
||||
*/
|
||||
bool mkdirs(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Request that this program re-invoke itself with the same arguments. Used to
|
||||
* re-initialize configuration values.
|
||||
*/
|
||||
void request_restart(void);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user