summaryrefslogtreecommitdiffstats
path: root/app/app_manager.cpp
blob: 07e68fad575e4280937b51aed83f80d5c80a5c1d (plain)
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <algorithm>

#include "app_manager.hpp"
#include "../hal/api.hpp"
#include "../globals.hpp"

// App classes following:
#include "../apps/main_clock/main.hpp"
#include "../apps/home_menu/main.hpp"
#include "../apps/settings/main.hpp"
#define NUMBER_OF_APPS 3

// From pico-watch.c:
extern Api app_api;

std::vector<BaseApp*> app_mgr::open_apps;

BaseApp* app_mgr::app_check_if_init(int app_id) {
    for (auto app : open_apps) {
        if (app_id == app->app_get_attributes().id)
            return app;
    }
    return nullptr;
}

void app_mgr::new_foreground_app(BaseApp* app) {
    app_api.display_fill(0,1); // Clear OLED
    g_s.foreground_app = app;
}

void app_mgr::app_act_on_return_value(BaseApp* app, BaseApp::AppReturnValues return_value) {
    switch (return_value) {
        case BaseApp::AppReturnValues::OK:
            break;

        case BaseApp::AppReturnValues::QUIT:
            app_destroy(app);
            // No break
        
        case BaseApp::AppReturnValues::CLOSE:
            new_foreground_app(open_apps.front()); // The app has to be in foreground as the current function is called by app_render and app_btnpress
            break;

        default:
            printf("Unidentified return value %d for app %d at %x", return_value, app->app_get_attributes().id, app);
    }
}

BaseApp* app_mgr::app_create(int app_id) {
    BaseApp* new_app;

    switch (app_id) {
        case 0: new_app = new app_home_menu(&app_api); break;
        case 1: new_app = new app_main_clock(&app_api); break;
        case 2: new_app = new app_settings(&app_api); break;
        default: __breakpoint(); return open_apps.front(); // Should be home_menu
    }

    if (new_app != nullptr)
        open_apps.push_back(new_app);
    else {
        printf("new failed for app %d. Not enough memory?", app_id);
        return open_apps.front(); // Should be home_menu
    }

    return open_apps.back();
}

BaseApp* app_mgr::app_init(int app_id) {
    BaseApp* new_app;

    if (app_id > NUMBER_OF_APPS-1 or app_id < 0) {
        printf("Tried to init app %d", app_id);
        return app_init(0);
    }

    // Check if the app is already running.
    // Should this be done instead in app_mgr::app_switch() ?
    auto app_ptr = app_check_if_init(app_id);
    if (app_ptr)
        new_app = app_ptr;
     else
        new_app = app_create(app_id);
        
    return new_app;
}

void app_mgr::app_render(BaseApp* app) {
    app_act_on_return_value(app, app->render(&app_api));
}

void app_mgr::app_btnpressed(BaseApp* app, uint gpio, unsigned long delta) {
    app_act_on_return_value(app, app->btn_pressed(&app_api, gpio, delta));
}

void app_mgr::app_btnreleased(BaseApp* app, uint gpio, unsigned long delta) {
    app_act_on_return_value(app, app->btn_released(&app_api, gpio, delta));
}

void app_mgr::app_destroy(BaseApp* to_erase) {
    auto erase_it = std::find(open_apps.begin(), open_apps.end(), to_erase); // "it" meaning iterator
    if (erase_it != open_apps.end()) {
        //assert(to_erase == erase_it);
        delete to_erase;
        open_apps.erase(erase_it);
    }
}

void app_mgr::app_all_bgrefresh() {
    std::vector<BaseApp*> to_erase;
    bool do_erase = false;

    for (auto app : open_apps) {
        bool is_foreground = app->app_get_attributes().id == g_s.foreground_app->app_get_attributes().id;

        switch (app->bgrefresh(&app_api, is_foreground)) {
            case BaseApp::AppReturnValues::OK:
                break;
            
            case BaseApp::AppReturnValues::QUIT:
                do_erase = true;
                to_erase.push_back(app);
                // No break here!
            
            case BaseApp::AppReturnValues::CLOSE:
                if (is_foreground)
                    new_foreground_app(open_apps.front());
                break;
            
            default:
                printf("Unidentified return value in bgrefresh for app %d at %x", app->app_get_attributes().id, app);
        }
    }

    if (do_erase) {
        for (auto app : to_erase) {
            app_destroy(app);
        }
    }
}

void app_mgr::app_switch_request(int to_appid) {
    if (!g_s.app_switch_requested)
        g_s.app_switch_to_app = to_appid;
    g_s.app_switch_requested = true;
}

void app_mgr::app_switch(BaseApp* app, int new_appid) {
    g_s.app_ready = false;

    if (app->app_get_attributes().destroy_on_exit)
        app_destroy(app);

    new_foreground_app(app_init(new_appid));    
    g_s.app_ready = true;
}