diff --git a/src/common.c b/src/common.c index 5137f5e..0eea5a3 100644 --- a/src/common.c +++ b/src/common.c @@ -47,9 +47,9 @@ graphics_draw_text(ctx, text, GTextAlignmentLeft, NULL); } -void timer_draw_row(Timer* timer, GContext* ctx) { +void timer_draw_row(Timer* timer, TimerTimestamp timestamp, GContext* ctx) { char* time_left = malloc(12); - timer_time_str(timer->current_time, settings()->timers_hours, time_left, 12); + timer_display_str(timer, timestamp, settings()->timers_hours, time_left, 12); graphics_context_set_text_color(ctx, GColorBlack); graphics_context_set_fill_color(ctx, GColorBlack); @@ -95,7 +95,7 @@ void timer_draw_row(Timer* timer, GContext* ctx) { } if (timer->type == TIMER_TYPE_TIMER) { - uint8_t width = (144 * timer->current_time) / timer->length; + uint8_t width = (144 * timer_get_display_time(timer, timestamp)) / timer->length; graphics_fill_rect(ctx, GRect(0, 31, width, 2), 0, GCornerNone); } diff --git a/src/common.h b/src/common.h index 8e98e6f..4847dc6 100644 --- a/src/common.h +++ b/src/common.h @@ -35,6 +35,6 @@ src/common.h #include "timer.h" void menu_draw_row_icon_text(GContext* ctx, char* text, GBitmap* icon); -void timer_draw_row(Timer* timer, GContext* ctx); +void timer_draw_row(Timer* timer, TimerTimestamp reference, GContext* ctx); void menu_draw_option(GContext* ctx, char* option, char* value); void uppercase(char* str); diff --git a/src/migration.h b/src/migration.h index 400954b..bca61af 100644 --- a/src/migration.h +++ b/src/migration.h @@ -93,6 +93,27 @@ typedef struct { time_t save_time; } TimerBlockTiny; +typedef struct TimerV3 { + uint16_t id; + TimerType type; + uint32_t length; + uint32_t current_time; + TimerStatus status; + TimerVibration vibration; + uint8_t repeat; + uint8_t repeat_count; + AppTimer* timer; + WakeupId wakeup_id; + char label[24]; +} TimerV3; + +typedef struct { + TimerV3 timers[TIMER_BLOCK_SIZE]; + uint8_t total_timers; + time_t save_time; +} TimerBlockV3; + + // Struct of the original settings. typedef struct { bool save_timers_auto; // Automatically save timers on exit and load them when the app restarts? diff --git a/src/timer.c b/src/timer.c index c217ea6..b4b669d 100644 --- a/src/timer.c +++ b/src/timer.c @@ -36,14 +36,76 @@ src/timer.c #include "settings.h" #include "windows/win-vibrate.h" -static void timer_tick(void* context); -static void timer_finish(Timer* timer); -static void timer_schedule_tick(Timer* timer); -static void timer_cancel_tick(Timer* timer); +static void timer_finish(Timer* timer, TimerTimestamp reference); static void timer_schedule_wakeup(Timer* timer, uint16_t offset); static void timer_cancel_wakeup(Timer* timer); static void timer_set_id(Timer* timer); static void timer_completed_action(Timer* timer); +static void timer_transition_to_stop(Timer* timer, TimerTimestamp reference_time); +static void timer_transition_to_start(Timer* timer, TimerTimestamp reference_time); +static void timer_transition_to(Timer* timer, TimerTimestamp reference_time, TimerStatus new_status); +static void timer_set_running_time(Timer* timer, TimerTimestamp reference_time, int32_t running_time); +static void timer_add_running_time(Timer* timer, int32_t add_seconds); +static bool timer_has_finished(Timer* timer, TimerTimestamp reference); +static uint32_t timer_display2running_time(Timer* timer, uint32_t display_time); + +TimerTimestamp timer_get_end_timestamp(Timer* timer) { + switch (timer->type) { + case TIMER_TYPE_TIMER: return timer->start_timestamp + timer->length - timer->paused_offset; + default: return 0; + } +} + +uint32_t timer_get_running_time(Timer* timer, TimerTimestamp reference_time) { + if (TIMER_STATUS_RUNNING == timer->status) { + return reference_time - timer->start_timestamp + timer->paused_offset; + } else { + // Both reference_time and timer->start_timestamp are meaningless. + return timer->paused_offset; + } +} + +static void timer_transition_to_stop(Timer* timer, TimerTimestamp reference_time) { + timer->paused_offset += reference_time - timer->start_timestamp; +} + +static void timer_transition_to_start(Timer* timer, TimerTimestamp reference_time) { + timer->start_timestamp = reference_time; +} + +static void timer_transition_to(Timer* timer, TimerTimestamp reference_time, TimerStatus new_status) { + if (TIMER_STATUS_RUNNING != timer->status && TIMER_STATUS_RUNNING == new_status) { + timer_transition_to_start(timer, reference_time); + } else if (TIMER_STATUS_RUNNING == timer-> status && TIMER_STATUS_RUNNING != new_status) { + timer_transition_to_stop(timer, reference_time); + } + timer->status = new_status; +} + +static void timer_set_running_time(Timer* timer, TimerTimestamp reference_time, int32_t running_time) { + timer->start_timestamp = reference_time; + timer->paused_offset = running_time; +} + +int32_t timer_get_display_time(Timer* timer, TimerTimestamp reference_time) { + switch (timer->type) { + case TIMER_TYPE_TIMER: return timer->length - timer_get_running_time(timer, reference_time); + case TIMER_TYPE_STOPWATCH: return timer_get_running_time(timer, reference_time); + default: return 0; + } +} + +static uint32_t timer_display2running_time(Timer* timer, uint32_t display_time) { + switch (timer->type) { + case TIMER_TYPE_TIMER: return timer->length - display_time; + case TIMER_TYPE_STOPWATCH: return display_time; + default: return 0; + } +} + +static void timer_add_running_time(Timer* timer, int32_t add_seconds) { + timer->paused_offset += add_seconds; +} void timer_time_str(uint32_t timer_time, bool showHours, char* str, int str_len) { int hours = timer_time / 3600; @@ -57,71 +119,63 @@ void timer_time_str(uint32_t timer_time, bool showHours, char* str, int str_len) } } -void timer_start(Timer* timer) { - switch (timer->type) { - case TIMER_TYPE_TIMER: - timer->current_time = timer->length; - break; - case TIMER_TYPE_STOPWATCH: - timer->current_time = 0; - break; - } - timer->status = TIMER_STATUS_RUNNING; - timer_schedule_tick(timer); - timer_schedule_wakeup(timer, 0); - timers_mark_updated(); +void timer_display_str(Timer* timer, TimerTimestamp reference, bool showHours, char* str, int str_len) { + int32_t current_time = timer_get_display_time(timer, reference); + return timer_time_str(current_time, showHours, str, str_len); +} + +void timer_start(Timer* timer, TimerTimestamp reference) { + timer_reset(timer); + timer_resume(timer, reference); } -void timer_pause(Timer* timer) { - timer->status = TIMER_STATUS_PAUSED; - timer_cancel_tick(timer); +void timer_pause(Timer* timer, TimerTimestamp reference) { + timer_transition_to(timer, reference, TIMER_STATUS_PAUSED); timer_cancel_wakeup(timer); timers_mark_updated(); } -void timer_resume(Timer* timer) { - timer->status = TIMER_STATUS_RUNNING; - timer_schedule_tick(timer); +void timer_resume(Timer* timer, TimerTimestamp reference) { + timer_transition_to(timer, reference, TIMER_STATUS_RUNNING); timer_schedule_wakeup(timer, 0); timers_mark_updated(); } void timer_reset(Timer* timer) { - timer_pause(timer); - switch (timer->type) { - case TIMER_TYPE_TIMER: - timer->current_time = timer->length; - break; - case TIMER_TYPE_STOPWATCH: - timer->current_time = 0; - break; - } - timer->status = TIMER_STATUS_STOPPED; + // Magic starts + timer_transition_to(timer, 0, TIMER_STATUS_STOPPED); + timer_set_running_time(timer, 0, 0); + // The next timer_transition_to(timer, reference, TIMER_STATUS_RUNNING) + // call on this timer will work correctly. This is because the reference + // time doesn't matter on a stop -> start transition. + // Magic ends + timer_cancel_wakeup(timer); + timers_mark_updated(); +} + +void timer_restart(Timer* timer, TimerTimestamp reference) { + timer_transition_to(timer, reference, TIMER_STATUS_RUNNING); + uint32_t running_time = timer_get_running_time(timer, reference); + timer_set_running_time(timer, reference, running_time % timer->length); + timer_schedule_wakeup(timer, 0); timers_mark_updated(); } -void timer_restore(Timer* timer, uint16_t seconds_elapsed) { - timer->timer = NULL; +void timer_restore(Timer* timer, TimerTimestamp reference) { + timer_tick(timer, reference); + if (timer->status == TIMER_STATUS_RUNNING) { - switch (timer->type) { - case TIMER_TYPE_STOPWATCH: - timer->current_time += seconds_elapsed; - break; - case TIMER_TYPE_TIMER: { - if (seconds_elapsed >= timer->current_time) { - timer->current_time = 0; - timer->status = TIMER_STATUS_DONE; - } - else { - timer->current_time -= seconds_elapsed; - } - break; - } - } + timer_resume(timer, reference); } - if (timer->status == TIMER_STATUS_RUNNING) { - timer_resume(timer); +} + +void timer_restore_legacy(Timer* timer, TimerTimestamp reference, uint32_t offset, uint32_t display_time) { + uint32_t running_time = timer_display2running_time(timer, display_time); + timer_set_running_time(timer, reference, running_time); + if (TIMER_STATUS_RUNNING == timer->status) { + timer_add_running_time(timer, offset); } + timer_restore(timer, reference); } Timer* timer_clone(Timer* timer) { @@ -153,8 +207,8 @@ Timer* timer_create_timer(void) { timer->type = TIMER_TYPE_TIMER; timer->vibration = settings()->timers_vibration; timer->length = settings()->timers_duration; + timer->paused_offset = 0; timer->wakeup_id = -1; - timer->timer = NULL; timer->repeat = 0; timer->label[0] = 0; timer->status = TIMER_STATUS_STOPPED; @@ -165,51 +219,37 @@ Timer* timer_create_timer(void) { Timer* timer_create_stopwatch(void) { Timer* stopwatch = malloc(sizeof(Timer)); stopwatch->type = TIMER_TYPE_STOPWATCH; - stopwatch->length = stopwatch->current_time = 0; + stopwatch->length = 0; + stopwatch->paused_offset = 0; stopwatch->label[0] = 0; stopwatch->status = TIMER_STATUS_STOPPED; timer_set_id(stopwatch); return stopwatch; } -static void timer_tick(void* context) { - Timer* timer = (Timer*)context; - timer->timer = NULL; - switch (timer->type) { - case TIMER_TYPE_STOPWATCH: - timer->current_time += 1; - break; - case TIMER_TYPE_TIMER: - timer->current_time -= 1; - if (timer->current_time <= 0) { - timer_finish(timer); - } - break; +void timer_tick(Timer* timer, TimerTimestamp reference) { + if (TIMER_STATUS_RUNNING == timer->status && + TIMER_TYPE_TIMER == timer->type && + timer_has_finished(timer, reference)) { + timer_finish(timer, reference); } - if (timer->status == TIMER_STATUS_RUNNING) { - timer_schedule_tick(timer); + if (TIMER_STATUS_RUNNING == timer->status) { + timers_mark_updated(); } - timers_mark_updated(); -} - -static void timer_finish(Timer* timer) { - timer->status = TIMER_STATUS_DONE; - timer_completed_action(timer); } -static void timer_schedule_tick(Timer* timer) { - timer_cancel_tick(timer); - timer->timer = app_timer_register(1000, timer_tick, (void*)timer); +static bool timer_has_finished(Timer* timer, TimerTimestamp reference) { + return reference >= timer_get_end_timestamp(timer); } -static void timer_cancel_tick(Timer* timer) { - if (! timer) { - return; - } - if (timer->timer) { - app_timer_cancel(timer->timer); - timer->timer = NULL; +static void timer_finish(Timer* timer, TimerTimestamp reference) { + timer_transition_to(timer, reference, TIMER_STATUS_DONE); + if (timer->repeat == TIMER_REPEAT_INFINITE) { + timer_restart(timer, reference); + } else { + timer_set_running_time(timer, reference, timer->length); } + timer_completed_action(timer); } static void timer_schedule_wakeup(Timer* timer, uint16_t offset) { @@ -220,8 +260,7 @@ static void timer_schedule_wakeup(Timer* timer, uint16_t offset) { return; } timer_cancel_wakeup(timer); - time_t wakeup_time = time(NULL); - wakeup_time += timer->current_time; + TimerTimestamp wakeup_time = timer_get_end_timestamp(timer); wakeup_time -= 2; wakeup_time -= offset; timer->wakeup_id = wakeup_schedule(wakeup_time, timer->id, false); @@ -318,8 +357,5 @@ static void timer_completed_action(Timer* timer) { default: break; } - if (timer->repeat == TIMER_REPEAT_INFINITE) { - timer_start(timer); - } timers_highlight(timer); } diff --git a/src/timer.h b/src/timer.h index 8913045..b97e681 100644 --- a/src/timer.h +++ b/src/timer.h @@ -54,16 +54,18 @@ typedef enum { TIMER_STATUS_DONE = 3, } TimerStatus; +typedef time_t TimerTimestamp; + typedef struct Timer { uint16_t id; TimerType type; uint32_t length; - uint32_t current_time; + int32_t paused_offset; + TimerTimestamp start_timestamp; TimerStatus status; TimerVibration vibration; uint8_t repeat; uint8_t repeat_count; - AppTimer* timer; WakeupId wakeup_id; char label[24]; } Timer; @@ -71,12 +73,19 @@ typedef struct Timer { #define TIMER_REPEAT_INFINITE 100 void timer_time_str(uint32_t timer_time, bool showHours, char* str, int str_len); -void timer_start(Timer* timer); -void timer_pause(Timer* timer); -void timer_resume(Timer* timer); +void timer_display_str(Timer* timer, TimerTimestamp reference, bool showHours, char* str, int str_len); +void timer_start(Timer* timer, TimerTimestamp reference); +void timer_pause(Timer* timer, TimerTimestamp reference); +void timer_resume(Timer* timer, TimerTimestamp reference); void timer_reset(Timer* timer); -void timer_restore(Timer* timer, uint16_t seconds_elapsed); +void timer_restart(Timer* timer, TimerTimestamp reference); +void timer_restore(Timer* timer, TimerTimestamp reference); +void timer_restore_legacy(Timer* timer, TimerTimestamp reference, uint32_t offset, uint32_t display_time); Timer* timer_clone(Timer* timer); char* timer_vibe_str(TimerVibration vibe, bool shortStr); Timer* timer_create_timer(void); Timer* timer_create_stopwatch(void); +void timer_tick(Timer* timer, TimerTimestamp reference); +uint32_t timer_get_running_time(Timer* timer, TimerTimestamp reference); +TimerTimestamp timer_get_end_timestamp(Timer* timer); +int32_t timer_get_display_time(Timer* timer, TimerTimestamp reference); diff --git a/src/timers.c b/src/timers.c index 6236001..c756986 100644 --- a/src/timers.c +++ b/src/timers.c @@ -42,22 +42,31 @@ src/timers.c typedef struct { Timer timers[TIMER_BLOCK_SIZE]; uint8_t total_timers; - time_t save_time; } TimerBlock; static void timers_cleanup(void); static void timers_migrate_1(void); static void timers_migrate_2(void); +static void timers_migrate_3(void); +static TimerTimestamp new_timestamp(void); +static void timers_fire_update_handlers(void); LinkedRoot* timers = NULL; LinkedRoot* update_handlers = NULL; LinkedRoot* highlight_handlers = NULL; +static TimerTimestamp current_time; +static bool update_marked; +static bool queue_updates; + void timers_init(void) { timers_cleanup(); timers = linked_list_create_root(); update_handlers = linked_list_create_root(); highlight_handlers = linked_list_create_root(); + current_time = new_timestamp(); + queue_updates = false; + update_marked = false; } uint8_t timers_count(void) { @@ -103,7 +112,7 @@ bool timers_remove(uint8_t position) { if (NULL == timer) { return false; } - timer_pause(timer); + timer_reset(timer); linked_list_remove(timers, position); free(timer); timers_mark_updated(); @@ -112,6 +121,7 @@ bool timers_remove(uint8_t position) { Timer* timers_find_last_wakeup(void) { Timer* last = NULL; + uint32_t wakeup_time; uint16_t last_wakeup_time = 0; uint8_t count = timers_count(); for (uint8_t c = 0; c < count; c += 1) { @@ -119,9 +129,10 @@ Timer* timers_find_last_wakeup(void) { if (timer->wakeup_id < 0) { continue; } - if (timer->current_time > last_wakeup_time) { + wakeup_time = timer_get_end_timestamp(timer); + if (wakeup_time > last_wakeup_time) { last = timer; - last_wakeup_time = timer->current_time; + last_wakeup_time = wakeup_time; } } return last; @@ -160,13 +171,23 @@ void timers_clear(void) { } void timers_mark_updated(void) { - uint8_t handler_count = linked_list_count(update_handlers); - for (uint8_t h = 0; h < handler_count; h += 1) { - TimersUpdatedHandler handler = linked_list_get(update_handlers, h); - handler(); + update_marked = true; + if (!queue_updates) { + timers_fire_update_handlers(); } } +void timers_fire_update_handlers(void) { + if (update_marked) { + uint8_t handler_count = linked_list_count(update_handlers); + for (uint8_t h = 0; h < handler_count; h += 1) { + TimersUpdatedHandler handler = linked_list_get(update_handlers, h); + handler(); + } + } + update_marked = false; +} + void timers_highlight(Timer* timer) { uint8_t handler_count = linked_list_count(highlight_handlers); for (uint8_t h = 0; h < handler_count; h += 1) { @@ -183,6 +204,26 @@ void timers_register_highlight_handler(TimerHighlightHandler handler) { linked_list_append(highlight_handlers, handler); } +static TimerTimestamp new_timestamp() { + return time(NULL); +} + +void timers_update_timestamp(void) { + current_time = new_timestamp(); + queue_updates = true; + const uint8_t count = timers_count(); + for (uint8_t t = 0; t < count; ++t) { + Timer* timer = timers_get(t); + timer_tick(timer, current_time); + } + queue_updates = false; + timers_fire_update_handlers(); +} + +TimerTimestamp timers_current_timestamp() { + return current_time; +} + static void timers_cleanup(void) { timers_clear(); free(timers); @@ -200,7 +241,6 @@ void timers_save(void) { if (NULL == block) { block = malloc(sizeof(TimerBlock)); block->total_timers = timers_count(); - block->save_time = time(NULL); } uint8_t timer_block_pos = b % TIMER_BLOCK_SIZE; @@ -232,10 +272,12 @@ void timers_restore(void) { return; } - timers_clear(); + if (TIMERS_VERSION_V3 == persist_read_int(PERSIST_TIMERS_VERSION)) { + timers_migrate_3(); + return; + } - time_t now = time(NULL); - uint16_t seconds_elapsed = 0; + timers_clear(); TimerBlock* block = NULL; if (persist_exists(PERSIST_TIMER_START)) { @@ -243,7 +285,6 @@ void timers_restore(void) { persist_read_data(PERSIST_TIMER_START, block, sizeof(TimerBlock)); uint8_t num_timers = block->total_timers; uint8_t block_offset = 0; - seconds_elapsed = now - block->save_time; for (uint8_t t = 0; t < num_timers; t += 1) { if (! block) { @@ -252,7 +293,7 @@ void timers_restore(void) { } Timer* timer = timer_clone(&block->timers[t % TIMER_BLOCK_SIZE]); timers_add(timer); - timer_restore(timer, seconds_elapsed); + timer_restore(timer, timers_current_timestamp()); if (t % TIMER_BLOCK_SIZE == (TIMER_BLOCK_SIZE - 1)) { free(block); block = NULL; @@ -296,7 +337,6 @@ static void timers_migrate_1(void) { Timer* timer = malloc(sizeof(Timer)); timer->id = old_timer->id; - timer->current_time = old_timer->time_left; timer->length = old_timer->length; timer->repeat = old_timer->repeat ? TIMER_REPEAT_INFINITE : 0; switch (old_timer->status) { @@ -342,8 +382,7 @@ static void timers_migrate_1(void) { break; } timer->wakeup_id = -1; - timer->timer = NULL; - timer_restore(timer, seconds_elapsed); + timer_restore_legacy(timer, timers_current_timestamp(), seconds_elapsed, old_timer->time_left); timers_add(timer); } if (NULL != timerBlock) { @@ -381,7 +420,6 @@ static void timers_migrate_2(void) { Timer* timer = malloc(sizeof(Timer)); timer->id = timer_tiny->id; - timer->current_time = timer_tiny->current_time; timer->length = timer_tiny->length; timer->repeat = timer_tiny->repeat; timer->repeat_count = timer_tiny->repeat_count; @@ -389,12 +427,63 @@ static void timers_migrate_2(void) { timer->vibration = timer_tiny->vibration; timer->type = timer_tiny->type; timer->wakeup_id = timer_tiny->wakeup_id; - timer->timer = NULL; strcpy(timer->label, timer_tiny->label); - timer_restore(timer, seconds_elapsed); + timer_restore_legacy(timer, timers_current_timestamp(), seconds_elapsed, timer_tiny->current_time); timers_add(timer); } if (NULL != block) { free(block); } } + +static void timers_migrate_3(void) { + + if (! persist_exists(PERSIST_TIMER_START)) { + return; + } + + time_t now = time(NULL); + uint16_t seconds_elapsed = 0; + + TimerBlockV3* block = NULL; + if (persist_exists(PERSIST_TIMER_START)) { + block = malloc(sizeof(TimerBlockV3)); + persist_read_data(PERSIST_TIMER_START, block, sizeof(TimerBlockV3)); + uint8_t num_timers = block->total_timers; + uint8_t block_offset = 0; + seconds_elapsed = now - block->save_time; + + for (uint8_t t = 0; t < num_timers; t += 1) { + if (! block) { + block = malloc(sizeof(TimerBlockV3)); + persist_read_data(PERSIST_TIMER_START + block_offset, block, sizeof(TimerBlockV3)); + } + TimerV3* timer_v3 = &block->timers[t % TIMER_BLOCK_SIZE]; + Timer* timer = malloc(sizeof(Timer)); + + timer->id = timer_v3->id; + timer->type = timer_v3->type; + timer->length = timer_v3->length; + //timer->paused_offset is set by timer_restore_legacy + //timer->start_timestamp is set by timer_restore_legacy + timer->status = timer_v3->status; + timer->vibration = timer_v3->vibration; + timer->repeat = timer_v3->repeat; + timer->repeat_count = timer_v3->repeat_count; + timer->wakeup_id = timer_v3->wakeup_id; + strncpy(timer->label, timer_v3->label, 24); + timer_restore_legacy(timer, timers_current_timestamp(), seconds_elapsed, timer_v3->current_time); + timers_add(timer); + + if (t % TIMER_BLOCK_SIZE == (TIMER_BLOCK_SIZE - 1)) { + free(block); + block = NULL; + block_offset += 1; + } + } + if (block) { + free(block); + block = NULL; + } + } +} diff --git a/src/timers.h b/src/timers.h index ffef389..86d4fd8 100644 --- a/src/timers.h +++ b/src/timers.h @@ -34,7 +34,8 @@ src/timers.h #define TIMER_BLOCK_SIZE 4 -#define TIMERS_VERSION_CURRENT 3 +#define TIMERS_VERSION_CURRENT 4 +#define TIMERS_VERSION_V3 3 #define TIMERS_VERSION_TINY 2 typedef void (*TimersUpdatedHandler)(void); @@ -78,6 +79,9 @@ Timer* timers_find_wakeup_collision(Timer* timer); // Empty list the timers. void timers_clear(void); +void timers_update_timestamp(void); +TimerTimestamp timers_current_timestamp(); + void timers_mark_updated(void); void timers_highlight(Timer* timer); void timers_register_update_handler(TimersUpdatedHandler handler); diff --git a/src/windows/win-controls.c b/src/windows/win-controls.c index ebd15ba..e6d1000 100644 --- a/src/windows/win-controls.c +++ b/src/windows/win-controls.c @@ -113,6 +113,7 @@ static void menu_draw_row(GContext* ctx, const Layer* cell_layer, MenuIndex* cel static void menu_select(struct MenuLayer* menu, MenuIndex* cell_index, void* callback_context) { uint8_t num_timers = timers_count(); + TimerTimestamp timestamp = timers_current_timestamp(); switch (cell_index->row) { case MENU_ROW_RESUME: for (uint8_t t = 0; t < num_timers; t += 1) { @@ -121,14 +122,14 @@ static void menu_select(struct MenuLayer* menu, MenuIndex* cell_index, void* cal case TIMER_STATUS_RUNNING: break; case TIMER_STATUS_PAUSED: - timer_resume(timer); + timer_resume(timer, timestamp); break; case TIMER_STATUS_DONE: timer_reset(timer); - timer_start(timer); + timer_start(timer, timestamp); break; case TIMER_STATUS_STOPPED: - timer_start(timer); + timer_start(timer, timestamp); break; } } @@ -137,7 +138,7 @@ static void menu_select(struct MenuLayer* menu, MenuIndex* cell_index, void* cal for (uint8_t t = 0; t < num_timers; t += 1) { Timer* timer = timers_get(t); if (timer->status == TIMER_STATUS_RUNNING) { - timer_pause(timer); + timer_pause(timer, timestamp); } } break; diff --git a/src/windows/win-main.c b/src/windows/win-main.c index 5cc30e6..30d138d 100644 --- a/src/windows/win-main.c +++ b/src/windows/win-main.c @@ -96,7 +96,7 @@ void win_main_init(void) { win_vibration_init(); win_duration_init(); win_vibrate_init(); - tick_timer_service_subscribe(MINUTE_UNIT, tick_handler); + tick_timer_service_subscribe(SECOND_UNIT, tick_handler); } void win_main_show(void) { @@ -121,9 +121,8 @@ static void window_unload(Window* window) { } static void tick_handler(struct tm *tick_time, TimeUnits units_changed) { - if (settings()->show_clock) { - menu_layer_reload_data(s_menu); - } + timers_update_timestamp(); + menu_layer_reload_data(s_menu); } static uint16_t menu_num_sections(struct MenuLayer* menu, void* callback_context) { @@ -193,7 +192,7 @@ static void menu_draw_row_clock(GContext* ctx, const Layer* cell_layer) { static void menu_draw_row_timers(GContext* ctx, const Layer* cell_layer, uint16_t row_index) { Timer* timer = timers_get(row_index); if (! timer) { return; } - timer_draw_row(timer, ctx); + timer_draw_row(timer, timers_current_timestamp(), ctx); } static void menu_draw_row_other(GContext* ctx, const Layer* cell_layer, uint16_t row_index) { @@ -229,18 +228,19 @@ static void menu_select(struct MenuLayer* menu, MenuIndex* cell_index, void* cal static void menu_select_timers(uint16_t row_index) { Timer* timer = timers_get(row_index); + TimerTimestamp timestamp = timers_current_timestamp(); if (! timer) { return; } switch (timer->status) { case TIMER_STATUS_STOPPED: { - timer_start(timer); + timer_start(timer, timestamp); break; } case TIMER_STATUS_RUNNING: - timer_pause(timer); + timer_pause(timer, timestamp); break; case TIMER_STATUS_PAUSED: - timer_resume(timer); + timer_resume(timer, timestamp); break; case TIMER_STATUS_DONE: timer_reset(timer); @@ -249,6 +249,7 @@ static void menu_select_timers(uint16_t row_index) { } static void menu_select_other(uint16_t row_index) { + TimerTimestamp timestamp = timers_current_timestamp(); switch (row_index) { case MENU_ROW_OTHER_ADD_TIMER: win_timer_add_show_new(); @@ -256,7 +257,7 @@ static void menu_select_other(uint16_t row_index) { case MENU_ROW_OTHER_ADD_STOPWATCH: { Timer* stopwatch = timer_create_stopwatch(); if (settings()->timers_start_auto) { - timer_start(stopwatch); + timer_start(stopwatch, timestamp); } timers_add(stopwatch); timers_mark_updated(); diff --git a/src/windows/win-timer-add.c b/src/windows/win-timer-add.c index 04204ab..00aa5eb 100644 --- a/src/windows/win-timer-add.c +++ b/src/windows/win-timer-add.c @@ -209,6 +209,7 @@ static void menu_select_main(uint16_t row) { } static void menu_select_footer(void) { + TimerTimestamp timestamp = timers_current_timestamp(); if (s_timer->length == 0) { vibes_short_pulse(); menu_layer_set_selected_index(s_menu, (MenuIndex) { MENU_SECTION_MAIN, MENU_ROW_DURATION }, MenuRowAlignCenter, true); @@ -228,7 +229,7 @@ static void menu_select_footer(void) { Timer* timer = timer_clone(s_timer); timer_reset(timer); if (settings()->timers_start_auto) { - timer_start(timer); + timer_start(timer, timestamp); } timers_add(timer); window_stack_pop(true); diff --git a/src/windows/win-timer.c b/src/windows/win-timer.c index 98b12f0..7e4d243 100644 --- a/src/windows/win-timer.c +++ b/src/windows/win-timer.c @@ -110,7 +110,7 @@ static void window_unload(Window* window) { } static void layer_header_update(Layer* layer, GContext* ctx) { - timer_draw_row(s_timer, ctx); + timer_draw_row(s_timer, timers_current_timestamp(), ctx); } static uint16_t menu_num_sections(struct MenuLayer* menu, void* callback_context) { @@ -155,18 +155,19 @@ static void menu_draw_row(GContext* ctx, const Layer* cell_layer, MenuIndex* cel } static void menu_select(struct MenuLayer* menu, MenuIndex* cell_index, void* callback_context) { + TimerTimestamp timestamp = timers_current_timestamp(); switch (cell_index->row) { case MENU_ROW_PAUSE: { switch (s_timer->status) { case TIMER_STATUS_RUNNING: - timer_pause(s_timer); + timer_pause(s_timer, timestamp); break; case TIMER_STATUS_PAUSED: - timer_resume(s_timer); + timer_resume(s_timer, timestamp); break; case TIMER_STATUS_DONE: case TIMER_STATUS_STOPPED: - timer_start(s_timer); + timer_start(s_timer, timestamp); break; } break; diff --git a/tests/timers.c b/tests/timers.c index 7685713..53fb09f 100644 --- a/tests/timers.c +++ b/tests/timers.c @@ -201,7 +201,7 @@ char* timer_str(void) { return 0; } -char* timers_migrate_from_1_to_3(void) { +char* timers_migrate_from_1_to_4(void) { OldTimerBlock* block = malloc(sizeof(OldTimerBlock)); block->count = 2; block->time = time(NULL); @@ -218,25 +218,25 @@ char* timers_migrate_from_1_to_3(void) { free(block); timers_restore(); - mu_assert(2 == timers_count(), "Migrate 1->3: Incorrect number of timers"); + mu_assert(2 == timers_count(), "Migrate 1->4: Incorrect number of timers"); Timer* tmr1 = timers_get(0); Timer* tmr2 = timers_get(1); - mu_assert(tmr1->type == TIMER_TYPE_TIMER, "Migrate 1->3: Timer 1 wrong type"); - mu_assert(tmr1->length == 3000, "Migrate 1->3: Timer 1 wrong length"); - mu_assert(tmr1->current_time == 200, "Migrate 1->3: Timer 1 wrong time"); - mu_assert(tmr1->status == TIMER_STATUS_PAUSED, "Migrate 1->3: Timer 1 wrong status"); - mu_assert(tmr1->repeat == TIMER_REPEAT_INFINITE, "Migrate 1->3: Timer 1 wrong repeat"); - mu_assert(tmr1->vibration == TIMER_VIBE_DOUBLE , "Migrate 1->3: Timer 1 wrong vibration"); - mu_assert(tmr2->type == TIMER_TYPE_STOPWATCH , "Migrate 1->3: Timer 2 wrong type"); - mu_assert(tmr2->current_time == 50, "Migrate 1->3: Timer 2 wrong time"); - mu_assert(tmr2->status == TIMER_STATUS_RUNNING , "Migrate 1->3: Timer 2 wrong status"); + mu_assert(tmr1->type == TIMER_TYPE_TIMER, "Migrate 1->4: Timer 1 wrong type"); + mu_assert(tmr1->length == 3000, "Migrate 1->4: Timer 1 wrong length"); + mu_assert(timer_get_display_time(tmr1, timers_current_timestamp()) == 200, "Migrate 1->4: Timer 1 wrong time"); + mu_assert(tmr1->status == TIMER_STATUS_PAUSED, "Migrate 1->4: Timer 1 wrong status"); + mu_assert(tmr1->repeat == TIMER_REPEAT_INFINITE, "Migrate 1->4: Timer 1 wrong repeat"); + mu_assert(tmr1->vibration == TIMER_VIBE_DOUBLE , "Migrate 1->4: Timer 1 wrong vibration"); + mu_assert(tmr2->type == TIMER_TYPE_STOPWATCH , "Migrate 1->4: Timer 2 wrong type"); + mu_assert(timer_get_display_time(tmr2, timers_current_timestamp()) == 50, "Migrate 1->4: Timer 2 wrong time"); + mu_assert(tmr2->status == TIMER_STATUS_RUNNING , "Migrate 1->4: Timer 2 wrong status"); return 0; } -char* timers_migrate_from_2_to_3(void) { +char* timers_migrate_from_2_to_4(void) { TimerBlockTiny* block = malloc(sizeof(TimerBlockTiny)); block->total_timers = 2; block->save_time = time(NULL); @@ -261,27 +261,78 @@ char* timers_migrate_from_2_to_3(void) { free(block); timers_restore(); - mu_assert(2 == timers_count(), "Migrate 2->3: Incorrect number of timers"); + mu_assert(2 == timers_count(), "Migrate 2->4: Incorrect number of timers"); Timer* tmr1 = timers_get(0); Timer* tmr2 = timers_get(1); - mu_assert(tmr1->type == TIMER_TYPE_TIMER, "Migrate 2->3: Timer 1 wrong type"); - mu_assert(tmr1->length == 60, "Migrate 2->3: Timer 1 wrong length"); - mu_assert(tmr1->current_time == 45, "Migrate 2->3: Timer 1 wrong current time"); - mu_assert(tmr1->id == 5, "Migrate 2->3: Timer 1 wrong ID"); - mu_assert(0 == strcmp(tmr1->label, "LABEL #1"), "Migrate 2->3: Timer 1 wrong label"); - mu_assert(tmr1->repeat == TIMER_REPEAT_INFINITE, "Migrate 2->3: Timer 1 wrong repeat"); - mu_assert(tmr1->vibration == TIMER_VIBE_SOLID, "Migrate 2->3: Timer 1 wrong vibration"); - mu_assert(tmr1->status == TIMER_STATUS_PAUSED, "Migrate 2->3: Timer 1 wrong status"); - mu_assert(tmr1->wakeup_id == 70, "Migrate 2->3: Timer 1 wrong wakeup ID"); - - mu_assert(tmr2->type == TIMER_TYPE_STOPWATCH, "Migrate 2->3: Timer 2 wrong wakeup type"); - mu_assert(tmr2->current_time == 500, "Migrate 2->3: Timer 2 wrong current time"); - mu_assert(tmr2->id == 10, "Migrate 2->3: Timer 2 wrong id"); - mu_assert(0 == strcmp(tmr2->label, "LABEL #2"), "Migrate 2->3: Timer 2 wrong label"); - mu_assert(tmr2->status == TIMER_STATUS_RUNNING, "Migrate 2->3: Timer 2 wrong status"); - mu_assert(tmr2->wakeup_id == 60, "Migrate 2->3: Timer 2 wrong wakeup ID"); + mu_assert(tmr1->type == TIMER_TYPE_TIMER, "Migrate 2->4: Timer 1 wrong type"); + mu_assert(tmr1->length == 60, "Migrate 2->4: Timer 1 wrong length"); + mu_assert(timer_get_display_time(tmr1, timers_current_timestamp()) == 45, "Migrate 2->4: Timer 1 wrong display time"); + mu_assert(tmr1->id == 5, "Migrate 2->4: Timer 1 wrong ID"); + mu_assert(0 == strcmp(tmr1->label, "LABEL #1"), "Migrate 2->4: Timer 1 wrong label"); + mu_assert(tmr1->repeat == TIMER_REPEAT_INFINITE, "Migrate 2->4: Timer 1 wrong repeat"); + mu_assert(tmr1->vibration == TIMER_VIBE_SOLID, "Migrate 2->4: Timer 1 wrong vibration"); + mu_assert(tmr1->status == TIMER_STATUS_PAUSED, "Migrate 2->4: Timer 1 wrong status"); + mu_assert(tmr1->wakeup_id == 70, "Migrate 2->4: Timer 1 wrong wakeup ID"); + + mu_assert(tmr2->type == TIMER_TYPE_STOPWATCH, "Migrate 2->4: Timer 2 wrong wakeup type"); + mu_assert(timer_get_display_time(tmr2, timers_current_timestamp()) == 500, "Migrate 2->4: Timer 2 wrong display time"); + mu_assert(tmr2->id == 10, "Migrate 2->4: Timer 2 wrong id"); + mu_assert(0 == strcmp(tmr2->label, "LABEL #2"), "Migrate 2->4: Timer 2 wrong label"); + mu_assert(tmr2->status == TIMER_STATUS_RUNNING, "Migrate 2->4: Timer 2 wrong status"); + mu_assert(tmr2->wakeup_id == 60, "Migrate 2->4: Timer 2 wrong wakeup ID"); + + return 0; +} + +char* timers_migrate_from_3_to_4(void) { + + TimerBlockV3* block = malloc(sizeof(TimerBlockV3)); + block->total_timers = 2; + block->save_time = time(NULL); + block->timers[0].type = TIMER_TYPE_TIMER; + block->timers[0].length = 75; + block->timers[0].current_time = 45; + block->timers[0].id = 20; + strcpy(block->timers[0].label, "LABEL #1"); + block->timers[0].repeat = TIMER_REPEAT_INFINITE; + block->timers[0].vibration = TIMER_VIBE_SOLID; + block->timers[0].status = TIMER_STATUS_PAUSED; + block->timers[0].wakeup_id = 70; + block->timers[1].type = TIMER_TYPE_STOPWATCH; + block->timers[1].current_time = 500; + block->timers[1].id = 10; + strcpy(block->timers[1].label, "LABEL #2"); + block->timers[1].status = TIMER_STATUS_RUNNING; + block->timers[1].wakeup_id = 60; + + persist_write_data(PERSIST_TIMER_START, block, sizeof(TimerBlockV3)); + persist_write_int(PERSIST_TIMERS_VERSION, TIMERS_VERSION_V3); + free(block); + + timers_restore(); + mu_assert(2 == timers_count(), "Migrate 3->4: Incorrect number of timers"); + + Timer *tmr1 = timers_get(0); + Timer *tmr2 = timers_get(1); + + mu_assert(tmr1->type == TIMER_TYPE_TIMER, "Migrate 3->4: Timer 1 wrong type"); + mu_assert(tmr1->length == 75, "Migrate 3->4: Timer 1 wrong length"); + mu_assert(timer_get_display_time(tmr1, timers_current_timestamp()) == 45, "Migrate 3->4: Timer 1 wrong display time"); + mu_assert(tmr1->id == 20, "Migrate 3->4: Timer 1 wrong id"); + mu_assert(0 == strcmp(tmr1->label, "LABEL #1"), "Migrate 3->4: Timer 1 wrong label"); + mu_assert(tmr1->repeat == TIMER_REPEAT_INFINITE, "Migrate 3->4: Timer 1 wrong repeat"); + mu_assert(tmr1->vibration == TIMER_VIBE_SOLID, "Migrate 3->4: Timer 1 wrong vibration"); + mu_assert(tmr1->status == TIMER_STATUS_PAUSED, "Migrate 3->4: Timer 1 wrong status"); + mu_assert(tmr1->wakeup_id == 70, "Migrate 3->4: Timer 1 wrong wakeup_id"); + + mu_assert(tmr2->type == TIMER_TYPE_STOPWATCH, "Migrate 3->4: Timer 2 wrong type"); + mu_assert(timer_get_display_time(tmr2, timers_current_timestamp()) == 500, "Migrate 3->4: Timer 2 wrong display time"); + mu_assert(tmr2->id == 10, "Migrate 3->4: Timer 2 wrong id"); + mu_assert(0 == strcmp(tmr2->label, "LABEL #2"), "Migrate 3->4: Timer 2 wrong label"); + mu_assert(tmr2->status == TIMER_STATUS_RUNNING, "Migrate 3->4: Timer 2 wrong status"); + mu_assert(tmr2->wakeup_id == 60, "Migrate 3->4: Timer 2 wrong wakeup_id"); return 0; } @@ -303,7 +354,8 @@ char* timers_tests(void) { mu_run_test(timers_save_persists_one_timer); mu_run_test(timers_save_persists_multiple_timers); mu_run_test(timer_str); - mu_run_test(timers_migrate_from_1_to_3); - mu_run_test(timers_migrate_from_2_to_3); + mu_run_test(timers_migrate_from_1_to_4); + mu_run_test(timers_migrate_from_2_to_4); + mu_run_test(timers_migrate_from_3_to_4); return 0; }