1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hardware/sync.h"
#include "hardware/rtc.h"
#include "pico/util/datetime.h"
#include "init.hpp"
#include "api.hpp"
#include "buttons.hpp"
#include "apps/main_clock.hpp"
#include "apps/home_menu.hpp"
int current_app = 0;
bool is_sleeping = false;
bool app_ready = true;
bool app_rendering = false;
Api app_api;
#define NUMBER_OF_APPS 2
#define APP_DATA_BUFFER_LEN 256
int (*APPS_FUNC_INIT[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::init, app_main_clock::init};
int (*APPS_FUNC_RENDER[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::render, app_main_clock::render};
int (*APPS_FUNC_BTNPRESS[NUMBER_OF_APPS])(Api *app_api, uint gpio) = {app_home_menu::btnpressed, app_main_clock::btnpressed};
int (*APPS_FUNC_BGREFRESH[NUMBER_OF_APPS])(Api *app_api, bool in_foreground) = {app_home_menu::bgrefresh, app_main_clock::bgrefresh};
int (*APPS_FUNC_DESTROY[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::destroy, app_main_clock::destroy};
int APPS_DESTROY_ON_EXIT[NUMBER_OF_APPS] = {0, 1};
int APPS_IS_INIT[NUMBER_OF_APPS] = {0, 0}; // Only run in background if init
int app_init(int app_id) {
app_api.display_fill(0,1); // Clear OLED
app_api.performance_render_interval_set(500); // Reset interval
if (app_id > NUMBER_OF_APPS-1 or app_id < 0) {
printf("Tried to init app %d", app_id);
return app_init(0);
}
if (!APPS_IS_INIT[app_id]) {
int status = (*APPS_FUNC_INIT[app_id])(&app_api);
switch (status) {
case Api::app_init_return_status::MALLOC_FAILED:
printf("App init failed (alloc), ");
for (int i=0; i<10; i++) {
if ((*APPS_FUNC_INIT[app_id])(&app_api) != Api::app_init_return_status::MALLOC_FAILED) {
printf("worked after %d tries\n", i);
APPS_IS_INIT[app_id] = 1;
return app_id;
}
}
// Instead, the current app could continue running: return current_app
printf("gave up, launching app 0\n");
return app_init(0); // Note: this has to work (and should)
default: // OK and unhandled status codes
printf("App init, status: %d\n", status);
break;
}
APPS_IS_INIT[app_id] = 1;
}
return app_id;
}
int app_render(int app_id) {
return (*APPS_FUNC_RENDER[app_id])(&app_api);
}
int app_btnpressed(int app_id, uint gpio) {
if (app_api.m_send_button_press_to_app)
return (*APPS_FUNC_BTNPRESS[app_id])(&app_api, gpio);
return 2;
}
int app_destroy(int app_id) {
if (APPS_IS_INIT[app_id]) {
APPS_IS_INIT[app_id] = 0;
return (*APPS_FUNC_DESTROY[app_id])(&app_api);
}
}
int app_bgrefresh(int app_id) {
if (APPS_IS_INIT[app_id])
return (*APPS_FUNC_BGREFRESH[app_id])(&app_api, app_id==current_app);
}
bool repeating_callback(struct repeating_timer *t) {
// Enter shallow sleep mode when needed
uint32_t time_since_last_press = to_ms_since_boot(get_absolute_time())-button_last_pressed_time;
if (!is_sleeping && time_since_last_press > ENTER_SLEEP_DELAY) {
is_sleeping = true;
app_api.performance_set(Api::perf_modes::ENTER_SHALLOW_SLEEP);
app_api.display_power(false);
} else if (is_sleeping && time_since_last_press < ENTER_SLEEP_DELAY) {
is_sleeping = false;
app_api.performance_set(Api::perf_modes::EXIT_SHALLOW_SLEEP);
app_api.display_power(true);
}
// Refresh each app, but should it be done when sleeping?
for (int i=0; i < NUMBER_OF_APPS; i++) {
app_bgrefresh(i);
}
return true;
}
void app_switch(int old_appid, int new_appid) {
app_ready = false;
// FIXME: race condition when pressing on HOME while app is rendering!
// The system is blocked waiting for the app to finish rendering, which will never happen. To fix the problem, app switching has to be a flag (c.f struct) that is set, and checked before rendering app. "if (app_switching.requested) app_switch(...);" We will not need anymore the app_rendering flag, as the check is done while the app is not rendering.
while (app_rendering); // Wait for the app to finish rendering cycle
if (APPS_DESTROY_ON_EXIT[old_appid])
app_destroy(old_appid);
current_app = app_init(new_appid);
app_ready = true;
}
int main() {
init_all();
printf("~~~==~~~");
init_buttons();
app_api.init();
struct repeating_timer timer;
add_repeating_timer_ms(250, repeating_callback, NULL, &timer); // TODO: Execute on core1
app_init(current_app);
while (1) {
if (app_ready && !is_sleeping) {
app_rendering = true;
app_render(current_app);
app_api.display_write_backbuffer();
app_rendering = false;
}
if (is_sleeping) __wfi();
else sleep_ms(app_api.performance_render_interval_get());
}
return 0;
}
|