6
6
7
7
#define TAG "Clock"
8
8
9
- bool timerStarted = false;
10
- int timerSecs = 0 ;
11
-
12
9
typedef enum {
13
10
EventTypeTick ,
14
11
EventTypeKey ,
@@ -20,7 +17,11 @@ typedef struct {
20
17
} PluginEvent ;
21
18
22
19
typedef struct {
23
- FuriHalRtcDateTime datetime ;
20
+ FuriMutex * mutex ;
21
+ FuriMessageQueue * event_queue ;
22
+ uint32_t timer_start_timestamp ;
23
+ uint32_t timer_stopped_seconds ;
24
+ bool timer_running ;
24
25
bool militaryTime ; // 24 hour
25
26
} ClockState ;
26
27
@@ -31,61 +32,60 @@ static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* even
31
32
}
32
33
33
34
static void clock_render_callback (Canvas * const canvas , void * ctx ) {
34
- canvas_clear ( canvas ) ;
35
- canvas_set_color ( canvas , ColorBlack );
36
- ClockState * state = ( ClockState * ) acquire_mutex (( ValueMutex * ) ctx , 25 );
37
- if ( state == NULL ) {
38
- FURI_LOG_E ( TAG , "state was NULL\r\n" );
35
+ ClockState * state = ctx ;
36
+ if ( furi_mutex_acquire ( state -> mutex , 200 ) != FuriStatusOk ) {
37
+ // Can't obtain mutex, requeue render
38
+ PluginEvent event = {. type = EventTypeTick };
39
+ furi_message_queue_put ( state -> event_queue , & event , 0 );
39
40
return ;
40
41
}
42
+
43
+ FuriHalRtcDateTime curr_dt ;
44
+ furi_hal_rtc_get_datetime (& curr_dt );
45
+ uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp (& curr_dt );
46
+
41
47
char strings [3 ][20 ];
42
- int curMin = (timerSecs / 60 );
43
- int curSec = timerSecs - (curMin * 60 );
44
- snprintf (
45
- strings [0 ],
46
- 20 ,
47
- "%.4d-%.2d-%.2d" ,
48
- state -> datetime .year ,
49
- state -> datetime .month ,
50
- state -> datetime .day );
51
-
52
- uint8_t hour = state -> datetime .hour ;
48
+ snprintf (strings [0 ], 20 , "%.4d-%.2d-%.2d" , curr_dt .year , curr_dt .month , curr_dt .day );
49
+
50
+ uint8_t hour = curr_dt .hour ;
53
51
// bool pm = false;
54
- if (!state -> militaryTime && hour > 12 ) {
52
+ if (!state -> militaryTime && hour > 12 ) {
55
53
hour -= 12 ;
56
54
// pm = true;
57
55
}
58
- snprintf (
59
- strings [1 ],
60
- 20 ,
61
- "%.2d:%.2d:%.2d" ,
62
- hour ,
63
- state -> datetime .minute ,
64
- state -> datetime .second );
65
- snprintf (strings [2 ], 20 , "%.2d:%.2d" , curMin , curSec );
66
- release_mutex ((ValueMutex * )ctx , state );
56
+ snprintf (strings [1 ], 20 , "%.2d:%.2d:%.2d" , hour , curr_dt .minute , curr_dt .second );
57
+
58
+ bool timer_running = state -> timer_running ;
59
+ uint32_t timer_start_timestamp = state -> timer_start_timestamp ;
60
+ uint32_t timer_stopped_seconds = state -> timer_stopped_seconds ;
61
+
62
+ furi_mutex_release (state -> mutex );
63
+
67
64
canvas_set_font (canvas , FontBigNumbers );
68
- if (timerStarted || timerSecs != 0 ) {
65
+ if (timer_start_timestamp != 0 ) {
66
+ int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp ) :
67
+ timer_stopped_seconds ;
68
+ snprintf (strings [2 ], 20 , "%.2ld:%.2ld" , elapsed_secs / 60 , elapsed_secs % 60 );
69
69
canvas_draw_str_aligned (canvas , 64 , 8 , AlignCenter , AlignCenter , strings [1 ]); // DRAW TIME
70
70
canvas_draw_str_aligned (canvas , 64 , 32 , AlignCenter , AlignTop , strings [2 ]); // DRAW TIMER
71
71
canvas_set_font (canvas , FontSecondary );
72
72
canvas_draw_str_aligned (canvas , 64 , 20 , AlignCenter , AlignTop , strings [0 ]); // DRAW DATE
73
+ elements_button_left (canvas , "Reset" );
73
74
} else {
74
75
canvas_draw_str_aligned (canvas , 64 , 26 , AlignCenter , AlignCenter , strings [1 ]); // DRAW TIME
75
76
canvas_set_font (canvas , FontSecondary );
76
77
canvas_draw_str_aligned (canvas , 64 , 38 , AlignCenter , AlignTop , strings [0 ]); // DRAW DATE
77
78
elements_button_left (canvas , state -> militaryTime ? "12h" : "24h" );
78
79
}
79
- if (timerStarted ) {
80
+ if (timer_running ) {
80
81
elements_button_center (canvas , "Stop" );
81
82
} else {
82
83
elements_button_center (canvas , "Start" );
83
84
}
84
- if (timerSecs != 0 ) elements_button_left (canvas , "Reset" );
85
85
}
86
86
87
87
static void clock_state_init (ClockState * const state ) {
88
- furi_hal_rtc_get_datetime ( & state -> datetime );
88
+ memset ( state , 0 , sizeof ( ClockState ) );
89
89
state -> militaryTime = true;
90
90
}
91
91
@@ -94,82 +94,113 @@ static void clock_tick(void* ctx) {
94
94
furi_assert (ctx );
95
95
FuriMessageQueue * event_queue = ctx ;
96
96
PluginEvent event = {.type = EventTypeTick };
97
- if (timerStarted ) {
98
- timerSecs = timerSecs + 1 ;
99
- }
100
- // It's OK to loose this event if system overloaded
97
+ // It's OK to lose this event if system overloaded
101
98
furi_message_queue_put (event_queue , & event , 0 );
102
99
}
103
100
104
101
int32_t clock_app (void * p ) {
105
102
UNUSED (p );
106
- timerStarted = false;
107
- timerSecs = 0 ;
108
- FuriMessageQueue * event_queue = furi_message_queue_alloc (8 , sizeof (PluginEvent ));
109
103
ClockState * plugin_state = malloc (sizeof (ClockState ));
110
104
clock_state_init (plugin_state );
111
- ValueMutex state_mutex ;
112
- if (!init_mutex (& state_mutex , plugin_state , sizeof (ClockState ))) {
113
- FURI_LOG_E (TAG , "cannot create mutex\r\n" );
114
- furi_message_queue_free (event_queue );
105
+ plugin_state -> event_queue = furi_message_queue_alloc (8 , sizeof (PluginEvent ));
106
+ if (plugin_state -> event_queue == NULL ) {
107
+ FURI_LOG_E (TAG , "cannot create event queue\n" );
108
+ free (plugin_state );
109
+ return 255 ;
110
+ }
111
+
112
+ plugin_state -> mutex = furi_mutex_alloc (FuriMutexTypeNormal );
113
+ if (plugin_state -> mutex == NULL ) {
114
+ FURI_LOG_E (TAG , "cannot create mutex\n" );
115
+ furi_message_queue_free (plugin_state -> event_queue );
116
+ free (plugin_state );
117
+ return 255 ;
118
+ }
119
+
120
+ FuriTimer * timer =
121
+ furi_timer_alloc (clock_tick , FuriTimerTypePeriodic , plugin_state -> event_queue );
122
+ if (timer == NULL ) {
123
+ FURI_LOG_E (TAG , "cannot create timer\n" );
124
+ furi_mutex_free (plugin_state -> mutex );
125
+ furi_message_queue_free (plugin_state -> event_queue );
115
126
free (plugin_state );
116
127
return 255 ;
117
128
}
129
+
118
130
// Set system callbacks
119
131
ViewPort * view_port = view_port_alloc ();
120
- view_port_draw_callback_set (view_port , clock_render_callback , & state_mutex );
121
- view_port_input_callback_set (view_port , clock_input_callback , event_queue );
122
- FuriTimer * timer = furi_timer_alloc (clock_tick , FuriTimerTypePeriodic , event_queue );
123
- furi_timer_start (timer , furi_kernel_get_tick_frequency ());
132
+ view_port_draw_callback_set (view_port , clock_render_callback , plugin_state );
133
+ view_port_input_callback_set (view_port , clock_input_callback , plugin_state -> event_queue );
134
+
124
135
// Open GUI and register view_port
125
136
Gui * gui = furi_record_open (RECORD_GUI );
126
137
gui_add_view_port (gui , view_port , GuiLayerFullscreen );
138
+ furi_timer_start (timer , furi_kernel_get_tick_frequency ());
139
+
127
140
// Main loop
128
141
PluginEvent event ;
129
142
for (bool processing = true; processing ;) {
130
- FuriStatus event_status = furi_message_queue_get (event_queue , & event , 100 );
131
- ClockState * plugin_state = (ClockState * )acquire_mutex_block (& state_mutex );
143
+ FuriStatus event_status = furi_message_queue_get (plugin_state -> event_queue , & event , 100 );
132
144
if (event_status == FuriStatusOk ) {
145
+ if (furi_mutex_acquire (plugin_state -> mutex , FuriWaitForever ) != FuriStatusOk ) continue ;
133
146
// press events
134
147
if (event .type == EventTypeKey ) {
135
148
if (event .input .type == InputTypeShort || event .input .type == InputTypeRepeat ) {
136
- if (event .input .key == InputKeyOk ) {
137
- // START/STOP TIMER
138
- if (timerStarted ) {
139
- timerStarted = false;
149
+ if (event .input .key == InputKeyOk ) {
150
+ // START/STOP TIMER
151
+ FuriHalRtcDateTime curr_dt ;
152
+ furi_hal_rtc_get_datetime (& curr_dt );
153
+ uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp (& curr_dt );
154
+
155
+ if (plugin_state -> timer_running ) {
156
+ // Update stopped seconds
157
+ plugin_state -> timer_stopped_seconds =
158
+ curr_ts - plugin_state -> timer_start_timestamp ;
140
159
} else {
141
- timerStarted = true;
160
+ if (plugin_state -> timer_start_timestamp == 0 ) {
161
+ // Set starting timestamp if this is first time
162
+ plugin_state -> timer_start_timestamp = curr_ts ;
163
+ } else {
164
+ // Timer was already running, need to slightly readjust so we don't
165
+ // count the intervening time
166
+ plugin_state -> timer_start_timestamp =
167
+ curr_ts - plugin_state -> timer_stopped_seconds ;
168
+ }
142
169
}
143
- } else if (event .input .key == InputKeyLeft ) {
144
- if (timerStarted || timerSecs != 0 ) {
145
- timerSecs = 0 ;
170
+ plugin_state -> timer_running = !plugin_state -> timer_running ;
171
+ } else if (event .input .key == InputKeyLeft ) {
172
+ if (plugin_state -> timer_start_timestamp != 0 ) {
173
+ // Reset seconds
174
+ plugin_state -> timer_running = false;
175
+ plugin_state -> timer_start_timestamp = 0 ;
176
+ plugin_state -> timer_stopped_seconds = 0 ;
146
177
} else {
178
+ // Toggle 12/24 hours
147
179
plugin_state -> militaryTime = !plugin_state -> militaryTime ;
148
180
}
149
- } else if (event .input .key == InputKeyBack ) {
181
+ } else if (event .input .key == InputKeyBack ) {
150
182
// Exit the plugin
151
183
processing = false;
152
- } else {
153
- // KEY NOT PROGRAMMED
154
- }
184
+ }
155
185
}
156
186
} else if (event .type == EventTypeTick ) {
157
- furi_hal_rtc_get_datetime ( & plugin_state -> datetime );
187
+ // Do nothing, just need to update viewport
158
188
}
189
+ view_port_update (view_port );
190
+ furi_mutex_release (plugin_state -> mutex );
159
191
} else {
160
- FURI_LOG_D (TAG , "osMessageQueue: event timeout" );
161
192
// event timeout
193
+ // FURI_LOG_D(TAG, "osMessageQueue: event timeout");
162
194
}
163
- view_port_update (view_port );
164
- release_mutex (& state_mutex , plugin_state );
165
195
}
196
+ // Cleanup
166
197
furi_timer_free (timer );
167
198
view_port_enabled_set (view_port , false);
168
199
gui_remove_view_port (gui , view_port );
169
200
furi_record_close (RECORD_GUI );
170
201
view_port_free (view_port );
171
- delete_mutex (& state_mutex );
202
+ furi_message_queue_free (plugin_state -> event_queue );
203
+ furi_mutex_free (plugin_state -> mutex );
172
204
free (plugin_state );
173
- furi_message_queue_free (event_queue );
174
205
return 0 ;
175
206
}
0 commit comments