2
2
#include <furi_hal.h>
3
3
4
4
#include <gui/gui.h>
5
+ #include <gui/elements.h>
5
6
#include <input/input.h>
6
7
7
8
/* Magic happens here -- this file is generated by fbt.
8
9
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
9
10
#include "flipp_pomodoro_icons.h"
10
11
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
+
11
31
typedef 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 ;
14
37
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
+ }
16
75
17
76
// Screen is 128x64 px
18
77
static void app_draw_callback (Canvas * canvas , void * ctx ) {
19
- UNUSED (ctx );
20
-
78
+ // WARNING: place no side-effects into rener cycle
21
79
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 );
23
101
}
24
102
25
103
static void app_input_callback (InputEvent * input_event , void * ctx ) {
26
104
furi_assert (ctx );
27
105
106
+ Action action = {.type = InputEventType , .payload = input_event };
107
+
28
108
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 ;
30
127
}
31
128
32
129
int32_t flipp_pomodoro_main (void * p ) {
33
130
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 ();
35
134
36
135
// Configure view port
37
136
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 );
39
138
view_port_input_callback_set (view_port , app_input_callback , event_queue );
40
139
140
+ FuriTimer * timer = furi_timer_alloc (clock_tick_callback , FuriTimerTypePeriodic , & event_queue );
141
+
41
142
// Register view port in GUI
42
143
Gui * gui = furi_record_open (RECORD_GUI );
43
144
gui_add_view_port (gui , view_port , GuiLayerFullscreen );
44
145
45
- InputEvent event ;
146
+ furi_timer_start (timer , 500 );
147
+
148
+ Action action ;
46
149
47
150
bool running = true;
48
151
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 ;
68
162
}
163
+ view_port_update (view_port );
69
164
}
70
- view_port_update (view_port );
71
165
}
72
166
73
167
view_port_enabled_set (view_port , false);
74
168
gui_remove_view_port (gui , view_port );
75
169
view_port_free (view_port );
76
170
furi_message_queue_free (event_queue );
77
-
78
171
furi_record_close (RECORD_GUI );
172
+ furi_timer_free (timer );
173
+ flipp_pomodoro__destroy (& state );
79
174
80
175
return 0 ;
81
176
}
0 commit comments