diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/app_manager.cpp | 152 | ||||
-rw-r--r-- | app/app_manager.hpp | 45 | ||||
-rw-r--r-- | app/base_app.hpp | 27 |
3 files changed, 224 insertions, 0 deletions
diff --git a/app/app_manager.cpp b/app/app_manager.cpp new file mode 100644 index 0000000..01bf3fd --- /dev/null +++ b/app/app_manager.cpp @@ -0,0 +1,152 @@ +#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->btnpressed(&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; +} diff --git a/app/app_manager.hpp b/app/app_manager.hpp new file mode 100644 index 0000000..b7783ea --- /dev/null +++ b/app/app_manager.hpp @@ -0,0 +1,45 @@ +#pragma once +#include <vector> +#include "base_app.hpp" + +// Interface to the app manager. These functions are accessible to other parts of the code, except open_apps, which is managed by the following functions. +namespace app_mgr { + // List of pointers to currently running apps. + extern std::vector<BaseApp*> open_apps; + + // Init a new app, that is not running. + BaseApp* app_init(int app_id); + + // Allow the running app, referenced by app_id, to invoke its render routine. + void app_render(BaseApp* app); + + // Delta is in ms, from time_since_button_press() + void app_btnpressed(BaseApp* app, uint gpio, unsigned long delta); + + // This should only be called by pico-watch.cpp before app rendering, to chage the current app. + void app_switch(BaseApp* app, int new_appid); + + // Requests the current app to be replaced by an other one. The replacement will be done at the right moment. + void app_switch_request(int to_appid); + + // Refresh each app + void app_all_bgrefresh(); + + // Private functions following. I tried using anonymous namespaces but it was too complicated. I might come back to this later. Just don't use the following internal functions. + + // Check if the specified app (via app_id) is already running. + // \return If app is init, pointer to app, else nullptr (more or less 0). + BaseApp* app_check_if_init(int app_id); + + // Check the return value of the called application method and act on it. + void app_act_on_return_value(BaseApp* app, BaseApp::AppReturnValues return_value); + + // Unconditionally set the new foreground app + void new_foreground_app(BaseApp* app); + + // Called by app_init to create the app object. + BaseApp* app_create(int app_id); + + // Quit the app referenced by the app_id. + void app_destroy(BaseApp* to_erase); +} diff --git a/app/base_app.hpp b/app/base_app.hpp new file mode 100644 index 0000000..c9367de --- /dev/null +++ b/app/base_app.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "../hal/api.hpp" + +// Base app +class BaseApp { + public: + struct AppAttributes { + uint id = 0; + bool destroy_on_exit = true; + uint render_interval = 500; // Interval in ms at which the render method is called. + }; + enum AppReturnValues { + OK = 0, + CLOSE, // Close app from foreground, not respecting AppAttribues::destroy_on_exit. + QUIT // Completely quit app; stop running in background + }; + + // CHECK: Following have to be overwritten by derived classes + virtual const AppAttributes& app_get_attributes() = 0; + // Could be implemented as: + // {return app_attributes;} + // where app_attribues is an instance of AppAttributes + + virtual AppReturnValues render(Api *app_api) = 0; // Has to be implemented + virtual AppReturnValues btnpressed(Api *app_api, uint gpio, unsigned long delta) {return AppReturnValues::OK;}; + virtual AppReturnValues bgrefresh(Api *app_api, bool in_foreground) {return AppReturnValues::OK;}; +}; |