Add date set/view and power screen
This commit is contained in:
		| @ -19,6 +19,9 @@ | ||||
| #include "ui/statrange.h" | ||||
| #include "ui/blankscreen.h" | ||||
| #include "ui/exportscreen.h" | ||||
| #include "ui/viewdatescreen.h" | ||||
| #include "ui/setdatescreen.h" | ||||
| #include "ui/powerscreen.h" | ||||
|  | ||||
| #include <unistd.h> | ||||
| #include <err.h> | ||||
| @ -118,6 +121,9 @@ 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 *) view_date_screen_new()); | ||||
|     screen_manager_add(screen_manager, (Screen *) set_date_screen_new()); | ||||
|     screen_manager_add(screen_manager, (Screen *) power_screen_new()); | ||||
|     while (RUNNING) { | ||||
|         lock_stat_globals(); | ||||
|         uint32_t temp = LAST_TEMP; | ||||
| @ -127,6 +133,8 @@ 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) { | ||||
|  | ||||
| @ -13,8 +13,7 @@ | ||||
| #include <err.h> | ||||
|  | ||||
| void date_sel_init(DateSelection *ds, UtilPeriod limit_period) { | ||||
|     memset(&ds->start_time, 0, sizeof(UtilDate)); // min date | ||||
|     memset(&ds->end_time, 0xff, sizeof(UtilDate)); // max date | ||||
|     ds->clamp_time = true; | ||||
|     date_sel_set_period(ds, limit_period); | ||||
|     date_sel_reset(ds); | ||||
| } | ||||
| @ -42,36 +41,38 @@ void date_sel_set_period(DateSelection *ds, UtilPeriod limit_period) { | ||||
| } | ||||
|  | ||||
| static void date_sel_clamp_time(DateSelection *ds) { | ||||
|     if (ds->year > ds->end_time.local_year) { | ||||
|         ds->year = ds->end_time.local_year; | ||||
|     } | ||||
|     if (ds->year == ds->end_time.local_year && | ||||
|         ds->month > ds->end_time.local_month) { | ||||
|         ds->month = ds->end_time.local_month; | ||||
|         if (ds->stage == DATE_SEL_DAY) { | ||||
|             // last days of previous month | ||||
|             ds->day = days_in_month(ds->month, ds->year); | ||||
|     if (ds->clamp_time) { | ||||
|         if (ds->year > ds->end_time.local_year) { | ||||
|             ds->year = ds->end_time.local_year; | ||||
|         } | ||||
|     } | ||||
|     if (ds->year == ds->end_time.local_year && | ||||
|         ds->month == ds->end_time.local_month && | ||||
|         ds->day > ds->end_time.local_day) { | ||||
|         ds->day = ds->end_time.local_day; | ||||
|     } | ||||
|     if (ds->year < ds->start_time.local_year) { | ||||
|         ds->year = ds->start_time.local_year; | ||||
|     } | ||||
|     if (ds->year == ds->start_time.local_year && | ||||
|         ds->month < ds->start_time.local_month) { | ||||
|         ds->month = ds->start_time.local_month; | ||||
|         if (ds->stage == DATE_SEL_DAY) { | ||||
|             ds->day = 1; // first day of the next month | ||||
|         if (ds->year == ds->end_time.local_year && | ||||
|             ds->month > ds->end_time.local_month) { | ||||
|             ds->month = ds->end_time.local_month; | ||||
|             if (ds->stage == DATE_SEL_DAY) { | ||||
|                 // last days of previous month | ||||
|                 ds->day = days_in_month(ds->month, ds->year); | ||||
|             } | ||||
|         } | ||||
|         if (ds->year == ds->end_time.local_year && | ||||
|             ds->month == ds->end_time.local_month && | ||||
|             ds->day > ds->end_time.local_day) { | ||||
|             ds->day = ds->end_time.local_day; | ||||
|         } | ||||
|         if (ds->year < ds->start_time.local_year) { | ||||
|             ds->year = ds->start_time.local_year; | ||||
|         } | ||||
|         if (ds->year == ds->start_time.local_year && | ||||
|             ds->month < ds->start_time.local_month) { | ||||
|             ds->month = ds->start_time.local_month; | ||||
|             if (ds->stage == DATE_SEL_DAY) { | ||||
|                 ds->day = 1; // first day of the next month | ||||
|             } | ||||
|         } | ||||
|         if (ds->year == ds->start_time.local_year && | ||||
|             ds->month == ds->start_time.local_month && | ||||
|             ds->day < ds->start_time.local_day) { | ||||
|             ds->day = ds->start_time.local_day; | ||||
|         } | ||||
|     } | ||||
|     if (ds->year == ds->start_time.local_year && | ||||
|         ds->month == ds->start_time.local_month && | ||||
|         ds->day < ds->start_time.local_day) { | ||||
|         ds->day = ds->start_time.local_day; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -96,7 +97,6 @@ static void date_sel_cleanup(DateSelection *ds) { | ||||
|         ++ds->month; | ||||
|         date_sel_cleanup(ds); | ||||
|     } | ||||
|     date_sel_clamp_time(ds); | ||||
| } | ||||
|  | ||||
| static void date_sel_add_units(DateSelection *ds, int n) { | ||||
| @ -117,15 +117,17 @@ static void date_sel_add_units(DateSelection *ds, int n) { | ||||
| DateSelectionState date_sel_dispatch(DateSelection *ds, | ||||
|                                      SensorState *state, | ||||
|                                      const char *label) { | ||||
|     UtilDate start, end; | ||||
|     if (!get_database_limits(state->db, ds->limit_period, &start, &end)) { | ||||
|         warnx("failed to query database limits"); | ||||
|         lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, | ||||
|                             LCD_DISPLAY_ON); | ||||
|         return DATE_SEL_ERROR; | ||||
|     if (ds->clamp_time) { | ||||
|         UtilDate start, end; | ||||
|         if (!get_database_limits(state->db, ds->limit_period, &start, &end)) { | ||||
|             warnx("failed to query database limits"); | ||||
|             lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, | ||||
|                                 LCD_DISPLAY_ON); | ||||
|             return DATE_SEL_ERROR; | ||||
|         } | ||||
|         ds->start_time = start; | ||||
|         ds->end_time = end; | ||||
|     } | ||||
|     ds->start_time = start; | ||||
|     ds->end_time = end; | ||||
|     lcd_clear(state->lcd); | ||||
|     lcd_move_to(state->lcd, 0, 0); | ||||
|     lcd_write_string(state->lcd, label); | ||||
| @ -199,3 +201,7 @@ DateSelectionState date_sel_dispatch(DateSelection *ds, | ||||
|     lcd_move_to(state->lcd, 1, cursor_pos); | ||||
|     return DATE_SEL_CONTINUE; | ||||
| } | ||||
|  | ||||
| void date_sel_set_clamp_time(DateSelection *ds, bool clamp_time) { | ||||
|     ds->clamp_time = clamp_time; | ||||
| } | ||||
|  | ||||
| @ -28,6 +28,7 @@ typedef struct { | ||||
|     UtilPeriod limit_period; | ||||
|     UtilDate start_time; | ||||
|     UtilDate end_time; | ||||
|     bool clamp_time; | ||||
| } DateSelection; | ||||
|  | ||||
| typedef enum { | ||||
| @ -61,4 +62,9 @@ DateSelectionState date_sel_dispatch(DateSelection *ds, | ||||
|                                      SensorState *state, | ||||
|                                      const char *label); | ||||
|  | ||||
| /* | ||||
|  * Set weather or not DS clamps time according to CLAMP_TIME. | ||||
|  */ | ||||
| void date_sel_set_clamp_time(DateSelection *ds, bool clamp_time); | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										75
									
								
								src/ui/powerscreen.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/ui/powerscreen.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| /* | ||||
|  * powerscreen.h - 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 "powerscreen.h" | ||||
|  | ||||
| #include <err.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/reboot.h> | ||||
|  | ||||
| static bool power_screen_dispatch(PowerScreen *screen, | ||||
|                                   SensorState *state) { | ||||
|     if (state->back_down) { | ||||
|         lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, | ||||
|                             LCD_DISPLAY_ON); | ||||
|         screen->choice = POWER_SCREEN_REBOOT; | ||||
|         return true; | ||||
|     } | ||||
|     if (state->sel_down) { | ||||
|         switch (screen->choice) { | ||||
|         case POWER_SCREEN_REBOOT: | ||||
|             LOG_VERBOSE("Rebooting..."); | ||||
|             reboot(RB_AUTOBOOT); | ||||
|             abort(); | ||||
|             // how did we even get here??? | ||||
|             break; | ||||
|         case POWER_SCREEN_POWEROFF: | ||||
|             LOG_VERBOSE("Powering off..."); | ||||
|             reboot(RB_POWEROFF); | ||||
|             abort(); | ||||
|             // how did we even get here??? | ||||
|             break; | ||||
|         default: | ||||
|             warnx("invalid power screen choice"); | ||||
|             lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, | ||||
|                                 LCD_DISPLAY_ON); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     screen->choice = abs((screen->choice + state->up_down - state->down_down) % | ||||
|                          POWER_SCREEN_NCHOICE); | ||||
|     if (state->force_draw || state->up_down || state->down_down) { | ||||
|         lcd_clear(state->lcd); | ||||
|         lcd_move_to(state->lcd, 0, 0); | ||||
|         lcd_write_string(state->lcd, "Type:"); | ||||
|         lcd_move_to(state->lcd, 1, 0); | ||||
|         lcd_write_char(state->lcd, '>'); | ||||
|         switch (screen->choice) { | ||||
|         case POWER_SCREEN_REBOOT: | ||||
|             lcd_write_string(state->lcd, "REBOOT"); | ||||
|             break; | ||||
|         case POWER_SCREEN_POWEROFF: | ||||
|             lcd_write_string(state->lcd, "POWEROFF"); | ||||
|             break; | ||||
|         } | ||||
|         lcd_display_control(state->lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON, | ||||
|                             LCD_DISPLAY_ON); | ||||
|         lcd_move_to(state->lcd, 1, 0); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| PowerScreen *power_screen_new(void) { | ||||
|     PowerScreen *s = malloc_checked(sizeof(PowerScreen)); | ||||
|     screen_init(&s->parent, "Power options", | ||||
|                 (ScreenDispatchFunc) power_screen_dispatch, | ||||
|                 (ScreenCleanupFunc) free); | ||||
|     s->choice = POWER_SCREEN_REBOOT; | ||||
|     return s; | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/ui/powerscreen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/ui/powerscreen.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| /* | ||||
|  * powerscreen.h - 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_POWERSCREEN_H | ||||
| #define INCLUDED_POWERSCREEN_H | ||||
|  | ||||
| #include "screen.h" | ||||
|  | ||||
| typedef struct { | ||||
|     Screen parent; | ||||
|     enum { | ||||
|         POWER_SCREEN_REBOOT = 0, | ||||
|         POWER_SCREEN_POWEROFF, | ||||
|         POWER_SCREEN_NCHOICE, | ||||
|     }; | ||||
|     int choice; | ||||
| } PowerScreen; | ||||
|  | ||||
| /* | ||||
|  * Create a new power screen that can be used to reboot or halt the device. | ||||
|  */ | ||||
| PowerScreen *power_screen_new(void); | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										157
									
								
								src/ui/setdatescreen.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/ui/setdatescreen.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,157 @@ | ||||
| /* | ||||
|  * setdatescreen.c - Screen for setting the date and time | ||||
|  * 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 "setdatescreen.h" | ||||
|  | ||||
| #include <err.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/time.h> | ||||
|  | ||||
| static void set_date_reset(SetDateScreen *screen, | ||||
|                            SensorState *state) { | ||||
|     date_sel_reset(&screen->ds); | ||||
|     time_sel_reset(&screen->ts); | ||||
|     screen->need_redraw = true; | ||||
|     screen->stage = SET_DATE_SELECT_DATE; | ||||
|     lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, | ||||
|                         LCD_DISPLAY_ON); | ||||
| } | ||||
|  | ||||
| static bool set_date_screen_select_date(SetDateScreen *screen, | ||||
|                                         SensorState *state) { | ||||
|     if (state->sel_down || state->back_down || state->up_down || | ||||
|         state->down_down) { | ||||
|         screen->need_redraw = true; | ||||
|     } | ||||
|     if (screen->need_redraw) { | ||||
|         screen->need_redraw = false; | ||||
|         DateSelectionState dss = date_sel_dispatch(&screen->ds, state, "New Date:"); | ||||
|         switch (dss) { | ||||
|         case DATE_SEL_CONTINUE: | ||||
|             return false; // ignore | ||||
|         case DATE_SEL_BACK: | ||||
|             return true; | ||||
|         case DATE_SEL_DONE: | ||||
|             screen->stage = SET_DATE_SELECT_TIME; | ||||
|             break; | ||||
|         case DATE_SEL_ERROR: | ||||
|             screen->stage = SET_DATE_FAIL; | ||||
|             break; | ||||
|         } | ||||
|         screen->need_redraw = true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| static bool set_date_and_time(DateSelection *ds, TimeSelection *ts) { | ||||
|     int64_t utc_time = timedate_sel_to_uts_timestamp(ds, ts); | ||||
|     struct timeval new_time = { | ||||
|         .tv_sec = utc_time, | ||||
|         .tv_usec = 0, // can't set, user not fast enough (lol) | ||||
|     }; | ||||
|     if (settimeofday(&new_time, NULL) < 0) { | ||||
|         warn("settimeofday(2) failed"); | ||||
|         return false; | ||||
|     } | ||||
|     LOG_VERBOSE("Set system date and time to %04d/%02d/%02d %02d:%02d:%02d\n", | ||||
|                 ds->year, ds->month, ds->day, ts->hour, ts->minute, ts->second); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| static void set_date_screen_select_time(SetDateScreen *screen, | ||||
|                                         SensorState *state) { | ||||
|     if (state->sel_down || state->back_down || state->up_down || | ||||
|         state->down_down) { | ||||
|         screen->need_redraw = true; | ||||
|     } | ||||
|     if (screen->need_redraw) { | ||||
|         screen->need_redraw = false; | ||||
|         TimeSelState tss = time_sel_dispatch(&screen->ts, state, "New Time"); | ||||
|         switch (tss) { | ||||
|         case TIME_SEL_CONTINUE: | ||||
|             return; // ignore | ||||
|         case TIME_SEL_BACK: | ||||
|             --screen->stage; | ||||
|             break; | ||||
|         case TIME_SEL_DONE: | ||||
|             if (set_date_and_time(&screen->ds, &screen->ts)) { | ||||
|                 screen->stage = SET_DATE_SUCCESS; | ||||
|             } else { | ||||
|                 screen->stage = SET_DATE_FAIL; | ||||
|             } | ||||
|             break; | ||||
|         case TIME_SEL_ERROR: | ||||
|             screen->stage = SET_DATE_FAIL; | ||||
|             break; | ||||
|         } | ||||
|         screen->need_redraw = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static bool set_date_screen_message(SetDateScreen *screen, | ||||
|                                     SensorState *state, | ||||
|                                     const char *msg) { | ||||
|     if (state->up_down || state->down_down || state->sel_down || | ||||
|         state->back_down) { | ||||
|         return 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, msg); | ||||
|         lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, | ||||
|                             LCD_DISPLAY_ON); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| static bool set_date_screen_dispatch(SetDateScreen *screen, | ||||
|                                      SensorState *state) { | ||||
|     if (state->force_draw) { | ||||
|         screen->need_redraw = true; | ||||
|     } | ||||
|     switch (screen->stage) { | ||||
|     case SET_DATE_SELECT_DATE: | ||||
|         if (set_date_screen_select_date(screen, state)) { | ||||
|             set_date_reset(screen, state); | ||||
|             return true; | ||||
|         } | ||||
|         break; | ||||
|     case SET_DATE_SELECT_TIME: | ||||
|         set_date_screen_select_time(screen, state); | ||||
|         break; | ||||
|     case SET_DATE_FAIL: | ||||
|         if (set_date_screen_message(screen, state, "Fail!")) { | ||||
|             set_date_reset(screen, state); | ||||
|             return true; | ||||
|         } | ||||
|         break; | ||||
|     case SET_DATE_SUCCESS: | ||||
|         if (set_date_screen_message(screen, state, "Success!")) { | ||||
|             set_date_reset(screen, state); | ||||
|             return true; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| SetDateScreen *set_date_screen_new() { | ||||
|     SetDateScreen *s = malloc_checked(sizeof(SetDateScreen)); | ||||
|     screen_init(&s->parent, "Set date/time", | ||||
|                 (ScreenDispatchFunc) set_date_screen_dispatch, | ||||
|                 (ScreenCleanupFunc) free); | ||||
|     date_sel_init(&s->ds, PERIOD_DAY); | ||||
|     date_sel_set_clamp_time(&s->ds, false); | ||||
|     time_sel_init(&s->ts, NULL, true); | ||||
|     s->need_redraw = true; | ||||
|     s->stage = SET_DATE_SELECT_DATE; | ||||
|     return s; | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/ui/setdatescreen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/ui/setdatescreen.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| /* | ||||
|  * setdatescreen.h - Screen for setting the date and time | ||||
|  * 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_SETDATESCREEN_H | ||||
| #define INCLUDED_SETDATESCREEN_H | ||||
|  | ||||
| #include "screen.h" | ||||
| #include "timesel.h" | ||||
|  | ||||
| typedef struct { | ||||
|     Screen parent; | ||||
|     enum { | ||||
|         SET_DATE_SELECT_DATE, | ||||
|         SET_DATE_SELECT_TIME, | ||||
|         SET_DATE_FAIL, | ||||
|         SET_DATE_SUCCESS | ||||
|     } stage; | ||||
|     bool need_redraw; | ||||
|     DateSelection ds; | ||||
|     TimeSelection ts; | ||||
| } SetDateScreen; | ||||
|  | ||||
| /* | ||||
|  * Create a new SetDateScreen. This screen will allow the user to configure the | ||||
|  * date and time. | ||||
|  */ | ||||
| SetDateScreen *set_date_screen_new(void); | ||||
|  | ||||
| #endif | ||||
| @ -65,24 +65,6 @@ static void stat_range_handle_time(StatRangeScreen *screen, TimeSelection *ts, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static uint64_t to_uts_timestamp(DateSelection *ds, TimeSelection *ts) { | ||||
|     struct tm broken_time = { | ||||
|         .tm_sec = ts->second, | ||||
|         .tm_min = ts->minute, | ||||
|         .tm_hour = ts->hour, | ||||
|         .tm_mday = ds->day, | ||||
|         .tm_mon = ds->month - 1, | ||||
|         .tm_year = ds->year - 1900, | ||||
|         .tm_isdst = -1, | ||||
|     }; | ||||
|     time_t it = mktime(&broken_time); | ||||
|     if (it < 0) { | ||||
|         warnx("failed to convert to UTC timestamp"); | ||||
|         return 0; | ||||
|     } | ||||
|     return it; | ||||
| } | ||||
|  | ||||
| static void stat_range_show_data(StatRangeScreen *screen, SensorState *state) { | ||||
|     if (state->back_down) { | ||||
|         screen->need_redraw = true; | ||||
| @ -99,10 +81,10 @@ static void stat_range_show_data(StatRangeScreen *screen, SensorState *state) { | ||||
|     } | ||||
|     if (screen->need_redraw) { | ||||
|         screen->need_redraw = false; | ||||
|         uint64_t start = to_uts_timestamp(&screen->sds, &screen->sts); | ||||
|         uint64_t end = to_uts_timestamp(&screen->eds, &screen->ets); | ||||
|         int64_t start = timedate_sel_to_uts_timestamp(&screen->sds, &screen->sts); | ||||
|         int64_t end = timedate_sel_to_uts_timestamp(&screen->eds, &screen->ets); | ||||
|         if (start > end) { | ||||
|             uint64_t t = end; | ||||
|             int64_t t = end; | ||||
|             end = start; | ||||
|             start = t; | ||||
|         } | ||||
|  | ||||
| @ -173,3 +173,21 @@ void time_sel_reset(TimeSelection *ts) { | ||||
|     ts->minute = 0; | ||||
|     ts->stage = TS_STAGE_HOUR; | ||||
| } | ||||
|  | ||||
| int64_t timedate_sel_to_uts_timestamp(DateSelection *ds, TimeSelection *ts) { | ||||
|     struct tm broken_time = { | ||||
|         .tm_sec = ts->second, | ||||
|         .tm_min = ts->minute, | ||||
|         .tm_hour = ts->hour, | ||||
|         .tm_mday = ds->day, | ||||
|         .tm_mon = ds->month - 1, | ||||
|         .tm_year = ds->year - 1900, | ||||
|         .tm_isdst = -1, | ||||
|     }; | ||||
|     time_t it = mktime(&broken_time); | ||||
|     if (it < 0) { | ||||
|         warnx("failed to convert to UTC timestamp"); | ||||
|         return 0; | ||||
|     } | ||||
|     return it; | ||||
| } | ||||
|  | ||||
| @ -52,4 +52,9 @@ TimeSelState time_sel_dispatch(TimeSelection *ts, SensorState *state, | ||||
|  */ | ||||
| void time_sel_reset(TimeSelection *ts); | ||||
|  | ||||
| /* | ||||
|  * Convert DS and TS to a UTC timestamp; | ||||
|  */ | ||||
| int64_t timedate_sel_to_uts_timestamp(DateSelection *ds, TimeSelection *ts); | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										45
									
								
								src/ui/viewdatescreen.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/ui/viewdatescreen.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| /* | ||||
|  * viewdatescreen.c - Screen for viewing the date and time | ||||
|  * 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 "viewdatescreen.h" | ||||
|  | ||||
| #include <limits.h> | ||||
|  | ||||
| static bool view_date_screen_dispatch(ViewDateScreen *screen, | ||||
|                                       SensorState *state) { | ||||
|     time_t now = time(NULL); | ||||
|     if (state->back_down || state->sel_down || state->up_down || | ||||
|         state->down_down) { | ||||
|         return true; | ||||
|     } else if (state->force_draw || now != screen->last_time) { | ||||
|         screen->last_time = now; | ||||
|         lcd_clear(state->lcd); | ||||
|         struct tm broken_time; | ||||
|         localtime_r(&now, &broken_time); | ||||
|         char buf[17]; | ||||
|         snprintf(buf, 17, "%04d/%02d/%02d", broken_time.tm_year + 1900, | ||||
|                  broken_time.tm_mon + 1, broken_time.tm_mday); | ||||
|         lcd_move_to(state->lcd, 0, 0); | ||||
|         lcd_write_string(state->lcd, buf); | ||||
|         snprintf(buf, 17, "%02d:%02d:%02d", broken_time.tm_hour, | ||||
|                  broken_time.tm_min, broken_time.tm_sec); | ||||
|         lcd_move_to(state->lcd, 1, 0); | ||||
|         lcd_write_string(state->lcd, buf); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| ViewDateScreen *view_date_screen_new() { | ||||
|     ViewDateScreen *s = malloc_checked(sizeof(ViewDateScreen)); | ||||
|     screen_init(&s->parent, "View date/time", | ||||
|                 (ScreenDispatchFunc) view_date_screen_dispatch, | ||||
|                 (ScreenCleanupFunc) free); | ||||
|     s->last_time = INT64_MIN; | ||||
|     return s; | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/ui/viewdatescreen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/ui/viewdatescreen.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * viewdatescreen.h - Screen for viewing the date and time | ||||
|  * 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_VIEWDATESCREEN_H | ||||
| #define INCLUDED_VIEWDATESCREEN_H | ||||
|  | ||||
| #include "screen.h" | ||||
|  | ||||
| typedef struct { | ||||
|     Screen parent; | ||||
|     int64_t last_time; | ||||
| } ViewDateScreen; | ||||
|  | ||||
| /* | ||||
|  * Create a new view date screen that shows the user the current date and time. | ||||
|  */ | ||||
| ViewDateScreen *view_date_screen_new(void); | ||||
|  | ||||
| #endif | ||||
| @ -357,7 +357,7 @@ bool get_data_point_info(sqlite3 *db, int64_t time, UtilDataPointInfo *info) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, | ||||
| bool get_average_for_range(sqlite3 *db, int64_t start, int64_t end, | ||||
|                            UtilAverageRange *data) { | ||||
|     bool success = true; | ||||
|     sqlite3_bind_int64(AVG_FOR_RANGE_QUERY, 1, start); | ||||
|  | ||||
| @ -203,7 +203,7 @@ typedef struct { | ||||
|  * Get the average DATA for the range of UTC times START to END (inclusive). | ||||
|  * Return: false if an error occurred, true otherwise. | ||||
|  */ | ||||
| bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, | ||||
| bool get_average_for_range(sqlite3 *db, int64_t start, int64_t end, | ||||
|                            UtilAverageRange *data); | ||||
|  | ||||
| /* | ||||
|  | ||||
		Reference in New Issue
	
	Block a user