22#include <furi_hal.h>
33
44#include <gui/gui.h>
5+ #include <gui/elements.h>
56#include <input/input.h>
67
78/* Magic happens here -- this file is generated by fbt.
89 * Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
910#include "flipp_pomodoro_icons.h"
1011
12+ typedef enum {
13+ TimerTickType ,
14+ InputEventType ,
15+ } ActionType ;
16+
17+ typedef struct {
18+ ActionType type ;
19+ void * payload ;
20+ } Action ;
21+
22+ /**
23+ * Flipp Pomodoro state management
24+ */
25+
26+ typedef enum {
27+ Work ,
28+ Rest ,
29+ } PomodoroStage ;
30+
1131typedef struct {
12- uint8_t x , y ;
13- } ImagePosition ;
32+ uint8_t seconds ;
33+ uint8_t minutes ;
34+ uint8_t hours ;
35+ uint32_t total_seconds ;
36+ } TimeDifference ;
1437
15- static ImagePosition image_position = {.x = 0 , .y = 0 };
38+ typedef struct {
39+ PomodoroStage stage ;
40+ uint32_t started_at_timestamp ;
41+ } FlippPomodoroState ;
42+
43+ /// @brief Calculates difference between two provided timestamps
44+ /// @param begin
45+ /// @param end
46+ /// @return
47+ static TimeDifference get_timestamp_difference_seconds (uint32_t begin , uint32_t end ) {
48+ const uint32_t duration_seconds = end - begin ;
49+ return (TimeDifference ){.total_seconds = duration_seconds };
50+ }
51+
52+ static void flipp_pomodoro__toggle_stage (FlippPomodoroState * state ) {
53+ state -> stage = state -> stage == Work ? Rest : Work ;
54+ state -> started_at_timestamp = furi_hal_rtc_get_timestamp ();
55+ }
56+
57+ static char * flipp_pomodoro__next_stage_label (FlippPomodoroState * state ) {
58+ return state -> stage == Work ? "To rest" : "To work" ;
59+ };
60+
61+ static TimeDifference flipp_pomodoro__stage_duration (FlippPomodoroState * state ) {
62+ const uint32_t now = furi_hal_rtc_get_timestamp ();
63+ return get_timestamp_difference_seconds (state -> started_at_timestamp , now );
64+ }
65+
66+ static void flipp_pomodoro__destroy (FlippPomodoroState * state ) {
67+ free (state );
68+ }
69+
70+ static FlippPomodoroState flipp_pomodoro__new () {
71+ const uint32_t now = furi_hal_rtc_get_timestamp ();
72+ const FlippPomodoroState new_state = {.stage = Work , .started_at_timestamp = now };
73+ return new_state ;
74+ }
1675
1776// Screen is 128x64 px
1877static void app_draw_callback (Canvas * canvas , void * ctx ) {
19- UNUSED (ctx );
20-
78+ // WARNING: place no side-effects into rener cycle
2179 canvas_clear (canvas );
22- canvas_draw_icon (canvas , image_position .x % 128 , image_position .y % 64 , & I_dolphin_71x25 );
80+ FlippPomodoroState * state = ctx ;
81+
82+ FuriString * timer_string = furi_string_alloc ();
83+
84+ const uint32_t now = flipp_pomodoro__stage_duration (state ).total_seconds ;
85+
86+ furi_string_printf (timer_string , "%lu" , now );
87+
88+ elements_text_box (canvas , 50 , 20 , 30 , 50 , AlignCenter , AlignCenter , furi_string_get_cstr (timer_string ), false);
89+ elements_button_right (canvas , flipp_pomodoro__next_stage_label (state ));
90+
91+ furi_string_free (timer_string );
92+ }
93+
94+ static void clock_tick_callback (void * ctx ) {
95+
96+ furi_assert (ctx );
97+ FuriMessageQueue * queue = ctx ;
98+ Action action = {.type = TimerTickType };
99+ // It's OK to loose this event if system overloaded
100+ furi_message_queue_put (queue , & action , 0 );
23101}
24102
25103static void app_input_callback (InputEvent * input_event , void * ctx ) {
26104 furi_assert (ctx );
27105
106+ Action action = {.type = InputEventType , .payload = input_event };
107+
28108 FuriMessageQueue * event_queue = ctx ;
29- furi_message_queue_put (event_queue , input_event , FuriWaitForever );
109+ furi_message_queue_put (event_queue , & action , FuriWaitForever );
110+ };
111+
112+ static bool input_events_reducer (FlippPomodoroState * state , InputEvent * input_event ) {
113+ bool keep_running = true;
114+ if ((input_event -> type == InputTypePress ) || (input_event -> type == InputTypeRepeat )) {
115+ switch (input_event -> key ) {
116+ case InputKeyRight :
117+ flipp_pomodoro__toggle_stage (state );
118+ break ;
119+ case InputKeyBack :
120+ keep_running = false;
121+ break ;
122+ default :
123+ break ;
124+ }
125+ }
126+ return keep_running ;
30127}
31128
32129int32_t flipp_pomodoro_main (void * p ) {
33130 UNUSED (p );
34- FuriMessageQueue * event_queue = furi_message_queue_alloc (8 , sizeof (InputEvent ));
131+ FuriMessageQueue * event_queue = furi_message_queue_alloc (8 , sizeof (Action ));
132+
133+ FlippPomodoroState state = flipp_pomodoro__new ();
35134
36135 // Configure view port
37136 ViewPort * view_port = view_port_alloc ();
38- view_port_draw_callback_set (view_port , app_draw_callback , view_port );
137+ view_port_draw_callback_set (view_port , app_draw_callback , & state );
39138 view_port_input_callback_set (view_port , app_input_callback , event_queue );
40139
140+ FuriTimer * timer = furi_timer_alloc (clock_tick_callback , FuriTimerTypePeriodic , & event_queue );
141+
41142 // Register view port in GUI
42143 Gui * gui = furi_record_open (RECORD_GUI );
43144 gui_add_view_port (gui , view_port , GuiLayerFullscreen );
44145
45- InputEvent event ;
146+ furi_timer_start (timer , 500 );
147+
148+ Action action ;
46149
47150 bool running = true;
48151 while (running ) {
49- if (furi_message_queue_get (event_queue , & event , 100 ) == FuriStatusOk ) {
50- if ((event .type == InputTypePress ) || (event .type == InputTypeRepeat )) {
51- switch (event .key ) {
52- case InputKeyLeft :
53- image_position .x -= 2 ;
54- break ;
55- case InputKeyRight :
56- image_position .x += 2 ;
57- break ;
58- case InputKeyUp :
59- image_position .y -= 2 ;
60- break ;
61- case InputKeyDown :
62- image_position .y += 2 ;
63- break ;
64- default :
65- running = false;
66- break ;
67- }
152+ if (furi_message_queue_get (event_queue , & action , 200 ) == FuriStatusOk ) {
153+ switch (action .type ) {
154+ case InputEventType :
155+ running = input_events_reducer (& state , action .payload );
156+ break ;
157+ case TimerTickType :
158+ // TODO: track time is over and make switch
159+ break ;
160+ default :
161+ break ;
68162 }
163+ view_port_update (view_port );
69164 }
70- view_port_update (view_port );
71165 }
72166
73167 view_port_enabled_set (view_port , false);
74168 gui_remove_view_port (gui , view_port );
75169 view_port_free (view_port );
76170 furi_message_queue_free (event_queue );
77-
78171 furi_record_close (RECORD_GUI );
172+ furi_timer_free (timer );
173+ flipp_pomodoro__destroy (& state );
79174
80175 return 0 ;
81176}
0 commit comments