diff --git a/App/Source/HelloWorld/HelloWorld.cpp b/App/Source/HelloWorld/HelloWorld.cpp index c3659e03..b11b9843 100644 --- a/App/Source/HelloWorld/HelloWorld.cpp +++ b/App/Source/HelloWorld/HelloWorld.cpp @@ -1,8 +1,8 @@ #include "lvgl.h" #include "lvgl/Toolbar.h" -static void onShow(tt::app::App& app, lv_obj_t* parent) { - lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, app); +static void onShow(tt::app::AppContext& context, lv_obj_t* parent) { + lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, context); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); lv_obj_t* label = lv_label_create(parent); @@ -10,7 +10,7 @@ static void onShow(tt::app::App& app, lv_obj_t* parent) { lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); } -extern const tt::app::Manifest hello_world_app = { +extern const tt::app::AppManifest hello_world_app = { .id = "HelloWorld", .name = "Hello World", .onShow = onShow, diff --git a/App/Source/Main.cpp b/App/Source/Main.cpp index 014bf2a5..6042c859 100644 --- a/App/Source/Main.cpp +++ b/App/Source/Main.cpp @@ -7,7 +7,7 @@ namespace tt::service::wifi { extern void wifi_main(void*); } -extern const tt::app::Manifest hello_world_app; +extern const tt::app::AppManifest hello_world_app; extern "C" { diff --git a/README.md b/README.md index f9b0cbb3..45dcc04a 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,9 @@ UI is created with [lvgl](https://github.com/lvgl/lvgl) which has lots of [widge Creating a touch-capable UI is [easy](https://docs.lvgl.io/9.0/get-started/quick-overview.html) and doesn't require your own render loop! ```C++ -static void onShow(tt::app::App app, lv_obj_t* parent) { +static void onShow(tt::app::AppContext context, lv_obj_t* parent) { // Default toolbar with app name and close button - lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, app); + lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, context); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); // Label widget @@ -50,7 +50,7 @@ static void onShow(tt::app::App app, lv_obj_t* parent) { // Widgets are auto-removed from the parent when the app is closed } -extern const tt::app::Manifest manifest = { +extern const tt::app::AppManifest manifest = { .id = "HelloWorld", // Used to identify and start an app .name = "Hello World", // Shown on the desktop and app's toolbar .onShow = onShow // A minimal setup sets the onShow() function diff --git a/Tactility/Private/app/AppInstance.h b/Tactility/Private/app/AppInstance.h index 83270995..36f4e537 100644 --- a/Tactility/Private/app/AppInstance.h +++ b/Tactility/Private/app/AppInstance.h @@ -1,10 +1,11 @@ #pragma once -#include "app/App.h" -#include "app/Manifest.h" +#include "app/AppContext.h" +#include "app/AppManifest.h" #include "Bundle.h" #include "Mutex.h" #include +#include namespace tt::app { @@ -17,70 +18,70 @@ typedef enum { } State; struct ResultHolder { - ResultHolder(Result result, const Bundle& resultData) { - this->result = result; - this->resultData = new Bundle(resultData); - } - - ~ResultHolder() { - delete resultData; - } - Result result; - Bundle* _Nullable resultData; + std::shared_ptr resultData; + + explicit ResultHolder(Result result) : result(result), resultData(nullptr) {} + + ResultHolder(Result result, std::shared_ptr resultData) : + result(result), + resultData(std::move(resultData)) + {} + }; /** * Thread-safe app instance. */ -class AppInstance : public App { +class AppInstance : public AppContext { private: Mutex mutex = Mutex(MutexTypeNormal); - const Manifest& manifest; + const AppManifest& manifest; State state = StateInitial; Flags flags = { .showStatusbar = true }; /** @brief Optional parameters to start the app with * When these are stored in the app struct, the struct takes ownership. * Do not mutate after app creation. */ - tt::Bundle parameters; + std::shared_ptr _Nullable parameters; /** @brief @brief Contextual data related to the running app's instance * The app can attach its data to this. * The lifecycle is determined by the on_start and on_stop methods in the AppManifest. * These manifest methods can optionally allocate/free data that is attached here. */ - void* _Nullable data = nullptr; - std::unique_ptr resultHolder; + std::shared_ptr _Nullable data; + std::unique_ptr _Nullable resultHolder; public: - AppInstance(const Manifest& manifest) : + explicit AppInstance(const AppManifest& manifest) : manifest(manifest) {} - AppInstance(const Manifest& manifest, const Bundle& parameters) : + AppInstance(const AppManifest& manifest, std::shared_ptr parameters) : manifest(manifest), - parameters(parameters) {} + parameters(std::move(parameters)) {} - ~AppInstance() {} + ~AppInstance() override = default; void setState(State state); - State getState() const; + [[nodiscard]] State getState() const; - const Manifest& getManifest() const; + [[nodiscard]] const AppManifest& getManifest() const override; - Flags getFlags() const; + [[nodiscard]] Flags getFlags() const override; void setFlags(Flags flags); - Flags& mutableFlags() { return flags; } + Flags& mutableFlags() { return flags; } // TODO: locking mechanism - _Nullable void* getData() const; - void setData(void* data); + [[nodiscard]] std::shared_ptr _Nullable getData() const override; + void setData(std::shared_ptr data) override; - const Bundle& getParameters() const; + [[nodiscard]] std::shared_ptr getParameters() const override; - void setResult(Result result, const Bundle& bundle); - bool hasResult() const; + void setResult(Result result) override; + void setResult(Result result, std::shared_ptr bundle) override; + [[nodiscard]] bool hasResult() const override; std::unique_ptr& getResult() { return resultHolder; } }; diff --git a/Tactility/Private/service/loader/Loader_i.h b/Tactility/Private/service/loader/Loader_i.h index c222f4b7..fdcdc65b 100644 --- a/Tactility/Private/service/loader/Loader_i.h +++ b/Tactility/Private/service/loader/Loader_i.h @@ -1,6 +1,6 @@ #pragma once -#include "app/Manifest.h" +#include "app/AppManifest.h" #include "app/AppInstance.h" #include "MessageQueue.h" #include "Pubsub.h" @@ -9,6 +9,7 @@ #include "service/loader/Loader.h" #include "RtosCompatSemaphore.h" #include +#include namespace tt::service::loader { @@ -35,7 +36,7 @@ typedef struct { } LoaderEventAppHiding; typedef struct { - const app::Manifest& manifest; + const app::AppManifest& manifest; } LoaderEventAppStopped; typedef struct { @@ -62,14 +63,14 @@ typedef enum { class LoaderMessageAppStart { public: std::string id; - Bundle bundle; + std::shared_ptr _Nullable parameters; LoaderMessageAppStart() = default; - LoaderMessageAppStart(const std::string& id, const Bundle& bundle) { - this->id = id; - this->bundle = bundle; - } + LoaderMessageAppStart(const std::string& id, std::shared_ptr parameters) : + id(id), + parameters(std::move(parameters)) + {} }; typedef struct { diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 4d2ffc00..4af506e2 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -15,13 +15,13 @@ static const Configuration* config_instance = nullptr; // region Default services namespace service { - namespace gui { extern const Manifest manifest; } - namespace loader { extern const Manifest manifest; } - namespace screenshot { extern const Manifest manifest; } - namespace statusbar { extern const Manifest manifest; } + namespace gui { extern const ServiceManifest manifest; } + namespace loader { extern const ServiceManifest manifest; } + namespace screenshot { extern const ServiceManifest manifest; } + namespace statusbar { extern const ServiceManifest manifest; } } -static const std::vector system_services = { +static const std::vector system_services = { &service::loader::manifest, &service::gui::manifest, // depends on loader service #ifndef ESP_PLATFORM // Screenshots don't work yet on ESP32 @@ -35,30 +35,30 @@ static const std::vector system_services = { // region Default apps namespace app { - namespace boot { extern const Manifest manifest; } - namespace desktop { extern const Manifest manifest; } - namespace files { extern const Manifest manifest; } - namespace gpio { extern const Manifest manifest; } - namespace imageviewer { extern const Manifest manifest; } - namespace screenshot { extern const Manifest manifest; } - namespace settings { extern const Manifest manifest; } - namespace display { extern const Manifest manifest; } - namespace i2cscanner { extern const Manifest manifest; } - namespace i2csettings { extern const Manifest manifest; } - namespace power { extern const Manifest manifest; } - namespace selectiondialog { extern const Manifest manifest; } - namespace systeminfo { extern const Manifest manifest; } - namespace textviewer { extern const Manifest manifest; } - namespace wifiapsettings { extern const Manifest manifest; } - namespace wificonnect { extern const Manifest manifest; } - namespace wifimanage { extern const Manifest manifest; } + namespace boot { extern const AppManifest manifest; } + namespace desktop { extern const AppManifest manifest; } + namespace files { extern const AppManifest manifest; } + namespace gpio { extern const AppManifest manifest; } + namespace imageviewer { extern const AppManifest manifest; } + namespace screenshot { extern const AppManifest manifest; } + namespace settings { extern const AppManifest manifest; } + namespace display { extern const AppManifest manifest; } + namespace i2cscanner { extern const AppManifest manifest; } + namespace i2csettings { extern const AppManifest manifest; } + namespace power { extern const AppManifest manifest; } + namespace selectiondialog { extern const AppManifest manifest; } + namespace systeminfo { extern const AppManifest manifest; } + namespace textviewer { extern const AppManifest manifest; } + namespace wifiapsettings { extern const AppManifest manifest; } + namespace wificonnect { extern const AppManifest manifest; } + namespace wifimanage { extern const AppManifest manifest; } } #ifndef ESP_PLATFORM -extern const app::Manifest screenshot_app; +extern const app::AppManifest screenshot_app; #endif -static const std::vector system_apps = { +static const std::vector system_apps = { &app::boot::manifest, &app::desktop::manifest, &app::display::manifest, @@ -92,10 +92,10 @@ static void register_system_apps() { } } -static void register_user_apps(const app::Manifest* const apps[TT_CONFIG_APPS_LIMIT]) { +static void register_user_apps(const app::AppManifest* const apps[TT_CONFIG_APPS_LIMIT]) { TT_LOG_I(TAG, "Registering user apps"); for (size_t i = 0; i < TT_CONFIG_APPS_LIMIT; i++) { - const app::Manifest* manifest = apps[i]; + const app::AppManifest* manifest = apps[i]; if (manifest != nullptr) { addApp(manifest); } else { @@ -113,10 +113,10 @@ static void register_and_start_system_services() { } } -static void register_and_start_user_services(const service::Manifest* const services[TT_CONFIG_SERVICES_LIMIT]) { +static void register_and_start_user_services(const service::ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT]) { TT_LOG_I(TAG, "Registering and starting user services"); for (size_t i = 0; i < TT_CONFIG_SERVICES_LIMIT; i++) { - const service::Manifest* manifest = services[i]; + const service::ServiceManifest* manifest = services[i]; if (manifest != nullptr) { addService(manifest); tt_check(service::startService(manifest->id)); @@ -152,11 +152,11 @@ void init(const Configuration& config) { register_user_apps(config.apps); TT_LOG_I(TAG, "init starting desktop app"); - service::loader::startApp(app::boot::manifest.id, true, Bundle()); + service::loader::startApp(app::boot::manifest.id, true); if (config.auto_start_app_id) { TT_LOG_I(TAG, "init auto-starting %s", config.auto_start_app_id); - service::loader::startApp(config.auto_start_app_id, true, Bundle()); + service::loader::startApp(config.auto_start_app_id, true); } TT_LOG_I(TAG, "init complete"); diff --git a/Tactility/Source/Tactility.h b/Tactility/Source/Tactility.h index 067b7b6a..521616d5 100644 --- a/Tactility/Source/Tactility.h +++ b/Tactility/Source/Tactility.h @@ -1,8 +1,8 @@ #pragma once -#include "app/Manifest.h" +#include "app/AppManifest.h" #include "hal/Configuration.h" -#include "service/Manifest.h" +#include "service/ServiceManifest.h" #include "TactilityConfig.h" namespace tt { @@ -10,8 +10,8 @@ namespace tt { typedef struct { const hal::Configuration* hardware; // List of user applications - const app::Manifest* const apps[TT_CONFIG_APPS_LIMIT]; - const service::Manifest* const services[TT_CONFIG_SERVICES_LIMIT]; + const app::AppManifest* const apps[TT_CONFIG_APPS_LIMIT]; + const service::ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT]; const char* auto_start_app_id; } Configuration; diff --git a/Tactility/Source/app/App.h b/Tactility/Source/app/App.h deleted file mode 100644 index 96546bea..00000000 --- a/Tactility/Source/app/App.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "Manifest.h" -#include "Bundle.h" - -namespace tt::app { - -typedef union { - struct { - bool showStatusbar : 1; - }; - unsigned char flags; -} Flags; - -/** - * A limited representation of the application instance. - * Do not store references or pointers to these! - */ -class App { -public: - virtual ~App() {}; - virtual const Manifest& getManifest() const = 0; - virtual _Nullable void* getData() const = 0; - virtual void setData(void* data) = 0; - virtual const Bundle& getParameters() const = 0; - virtual Flags getFlags() const = 0; - virtual void setResult(Result result, const Bundle& bundle = Bundle()) = 0; -}; - -} diff --git a/Tactility/Source/app/AppContext.h b/Tactility/Source/app/AppContext.h new file mode 100644 index 00000000..90e3cb84 --- /dev/null +++ b/Tactility/Source/app/AppContext.h @@ -0,0 +1,38 @@ +#pragma once + +#include "AppManifest.h" +#include "Bundle.h" +#include + +namespace tt::app { + +typedef union { + struct { + bool showStatusbar : 1; + }; + unsigned char flags; +} Flags; + +/** + * A limited representation of the application instance. + * Do not store references or pointers to these! + */ +class AppContext { + +protected: + + virtual ~AppContext() = default; + +public: + + [[nodiscard]] virtual const AppManifest& getManifest() const = 0; + [[nodiscard]] virtual std::shared_ptr _Nullable getData() const = 0; + virtual void setData(std::shared_ptr data) = 0; + [[nodiscard]] virtual std::shared_ptr getParameters() const = 0; + [[nodiscard]] virtual Flags getFlags() const = 0; + virtual void setResult(Result result) = 0; + virtual void setResult(Result result, std::shared_ptr bundle)= 0; + [[nodiscard]] virtual bool hasResult() const = 0; +}; + +} diff --git a/Tactility/Source/app/AppInstance.cpp b/Tactility/Source/app/AppInstance.cpp index a0c3ace9..86296969 100644 --- a/Tactility/Source/app/AppInstance.cpp +++ b/Tactility/Source/app/AppInstance.cpp @@ -23,7 +23,7 @@ State AppInstance::getState() const { * Consider creating MutableBundle vs Bundle. * Consider not exposing bundle, but expose `app_get_bundle_int(key)` methods with locking in it. */ -const Manifest& AppInstance::getManifest() const { +const AppManifest& AppInstance::getManifest() const { return manifest; } @@ -40,26 +40,45 @@ void AppInstance::setFlags(Flags newFlags) { mutex.release(); } -_Nullable void* AppInstance::getData() const { +std::shared_ptr _Nullable AppInstance::getData() const { mutex.acquire(TtWaitForever); auto result = data; mutex.release(); return result; } -void AppInstance::setData(void* newData) { +void AppInstance::setData(std::shared_ptr newData) { mutex.acquire(TtWaitForever); data = newData; mutex.release(); } -const Bundle& AppInstance::getParameters() const { - return parameters; +std::shared_ptr AppInstance::getParameters() const { + mutex.acquire(TtWaitForever); + std::shared_ptr result = parameters; + mutex.release(); + return result; } -void AppInstance::setResult(Result result, const Bundle& bundle) { - std::unique_ptr new_holder(new ResultHolder(result, bundle)); +void AppInstance::setResult(Result result) { + std::unique_ptr new_holder(new ResultHolder(result)); + mutex.acquire(TtWaitForever); resultHolder = std::move(new_holder); + mutex.release(); +} + +void AppInstance::setResult(Result result, std::shared_ptr bundle) { + std::unique_ptr new_holder(new ResultHolder(result, std::move(bundle))); + mutex.acquire(TtWaitForever); + resultHolder = std::move(new_holder); + mutex.release(); +} + +bool AppInstance::hasResult() const { + mutex.acquire(TtWaitForever); + bool has_result = resultHolder != nullptr; + mutex.release(); + return has_result; } } // namespace diff --git a/Tactility/Source/app/Manifest.h b/Tactility/Source/app/AppManifest.h similarity index 80% rename from Tactility/Source/app/Manifest.h rename to Tactility/Source/app/AppManifest.h index e3649133..d0095437 100644 --- a/Tactility/Source/app/Manifest.h +++ b/Tactility/Source/app/AppManifest.h @@ -9,7 +9,7 @@ typedef struct _lv_obj_t lv_obj_t; namespace tt::app { -class App; +class AppContext; typedef enum { /** Boot screen, shown before desktop is launched. */ @@ -32,13 +32,13 @@ typedef enum { ResultError } Result; -typedef void (*AppOnStart)(App& app); -typedef void (*AppOnStop)(App& app); -typedef void (*AppOnShow)(App& app, lv_obj_t* parent); -typedef void (*AppOnHide)(App& app); -typedef void (*AppOnResult)(App& app, Result result, const Bundle& resultData); +typedef void (*AppOnStart)(AppContext& app); +typedef void (*AppOnStop)(AppContext& app); +typedef void (*AppOnShow)(AppContext& app, lv_obj_t* parent); +typedef void (*AppOnHide)(AppContext& app); +typedef void (*AppOnResult)(AppContext& app, Result result, const Bundle& resultData); -struct Manifest { +struct AppManifest { /** * The identifier by which the app is launched by the system and other apps. */ @@ -86,7 +86,7 @@ struct Manifest { }; struct { - bool operator()(const Manifest* left, const Manifest* right) const { return left->name < right->name; } + bool operator()(const AppManifest* left, const AppManifest* right) const { return left->name < right->name; } } SortAppManifestByName; } // namespace diff --git a/Tactility/Source/app/ManifestRegistry.cpp b/Tactility/Source/app/ManifestRegistry.cpp index e25a6a92..b4a56bbb 100644 --- a/Tactility/Source/app/ManifestRegistry.cpp +++ b/Tactility/Source/app/ManifestRegistry.cpp @@ -7,12 +7,12 @@ namespace tt::app { -typedef std::unordered_map AppManifestMap; +typedef std::unordered_map AppManifestMap; static AppManifestMap app_manifest_map; static Mutex hash_mutex(MutexTypeNormal); -void addApp(const Manifest* manifest) { +void addApp(const AppManifest* manifest) { TT_LOG_I(TAG, "Registering manifest %s", manifest->id.c_str()); hash_mutex.acquire(TtWaitForever); @@ -20,16 +20,16 @@ void addApp(const Manifest* manifest) { hash_mutex.release(); } -_Nullable const Manifest * findAppById(const std::string& id) { +_Nullable const AppManifest * findAppById(const std::string& id) { hash_mutex.acquire(TtWaitForever); auto iterator = app_manifest_map.find(id); - _Nullable const Manifest* result = iterator != app_manifest_map.end() ? iterator->second : nullptr; + _Nullable const AppManifest* result = iterator != app_manifest_map.end() ? iterator->second : nullptr; hash_mutex.release(); return result; } -std::vector getApps() { - std::vector manifests; +std::vector getApps() { + std::vector manifests; hash_mutex.acquire(TtWaitForever); for (const auto& item: app_manifest_map) { manifests.push_back(item.second); diff --git a/Tactility/Source/app/ManifestRegistry.h b/Tactility/Source/app/ManifestRegistry.h index cf2b5821..72bcd3c5 100644 --- a/Tactility/Source/app/ManifestRegistry.h +++ b/Tactility/Source/app/ManifestRegistry.h @@ -1,13 +1,13 @@ #pragma once -#include "Manifest.h" +#include "AppManifest.h" #include #include namespace tt::app { -void addApp(const Manifest* manifest); -const Manifest _Nullable* findAppById(const std::string& id); -std::vector getApps(); +void addApp(const AppManifest* manifest); +const AppManifest _Nullable* findAppById(const std::string& id); +std::vector getApps(); } // namespace diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index f666f606..532595ef 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -2,7 +2,7 @@ #include #include #include "Assets.h" -#include "app/App.h" +#include "app/AppContext.h" #include "lvgl.h" #include "hal/Display.h" #include "service/loader/Loader.h" @@ -48,8 +48,8 @@ static int32_t threadCallback(TT_UNUSED void* context) { return 0; } -static void onShow(TT_UNUSED App& app, lv_obj_t* parent) { - Data* data = (Data*)app.getData(); +static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) { + auto data = std::static_pointer_cast(app.getData()); lv_obj_t* image = lv_image_create(parent); lv_obj_set_size(image, LV_PCT(100), LV_PCT(100)); @@ -59,19 +59,17 @@ static void onShow(TT_UNUSED App& app, lv_obj_t* parent) { data->thread.start(); } -static void onStart(App& app) { - Data* data = new Data(); +static void onStart(AppContext& app) { + auto data = std::shared_ptr(new Data()); app.setData(data); } -static void onStop(App& app) { - Data* data = (Data*)app.getData(); +static void onStop(AppContext& app) { + auto data = std::static_pointer_cast(app.getData()); data->thread.join(); - tt_assert(data); - delete data; } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "Boot", .name = "Boot", .type = TypeBoot, diff --git a/Tactility/Source/app/desktop/Desktop.cpp b/Tactility/Source/app/desktop/Desktop.cpp index 11df2180..2291c43a 100644 --- a/Tactility/Source/app/desktop/Desktop.cpp +++ b/Tactility/Source/app/desktop/Desktop.cpp @@ -10,12 +10,12 @@ namespace tt::app::desktop { static void onAppPressed(lv_event_t* e) { lv_event_code_t code = lv_event_get_code(e); if (code == LV_EVENT_CLICKED) { - const auto* manifest = static_cast(lv_event_get_user_data(e)); - service::loader::startApp(manifest->id, false, Bundle()); + const auto* manifest = static_cast(lv_event_get_user_data(e)); + service::loader::startApp(manifest->id, false); } } -static void createAppWidget(const Manifest* manifest, void* parent) { +static void createAppWidget(const AppManifest* manifest, void* parent) { tt_check(parent); auto* list = static_cast(parent); const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK; @@ -23,7 +23,7 @@ static void createAppWidget(const Manifest* manifest, void* parent) { lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest); } -static void onShow(TT_UNUSED App& app, lv_obj_t* parent) { +static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) { lv_obj_t* list = lv_list_create(parent); lv_obj_set_size(list, LV_PCT(100), LV_PCT(100)); lv_obj_center(list); @@ -46,7 +46,7 @@ static void onShow(TT_UNUSED App& app, lv_obj_t* parent) { } } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "Desktop", .name = "Desktop", .type = TypeDesktop, diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index 509e55a8..77575f70 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -1,4 +1,4 @@ -#include "app/App.h" +#include "app/AppContext.h" #include "Assets.h" #include "DisplaySettings.h" #include "Tactility.h" @@ -70,7 +70,7 @@ static void onOrientationSet(lv_event_t* event) { } } -static void onShow(App& app, lv_obj_t* parent) { +static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -121,13 +121,13 @@ static void onShow(App& app, lv_obj_t* parent) { lv_dropdown_set_selected(orientation_dropdown, orientation_selected); } -static void onHide(TT_UNUSED App& app) { +static void onHide(TT_UNUSED AppContext& app) { if (backlight_duty_set) { setBacklightDuty(backlight_duty); } } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "Display", .name = "Display", .icon = TT_ASSETS_APP_ICON_DISPLAY_SETTINGS, diff --git a/Tactility/Source/app/files/Files.cpp b/Tactility/Source/app/files/Files.cpp index b3627cc5..9fb056fc 100644 --- a/Tactility/Source/app/files/Files.cpp +++ b/Tactility/Source/app/files/Files.cpp @@ -1,6 +1,6 @@ #include "FilesData.h" #include "Tactility.h" -#include "app/App.h" +#include "app/AppContext.h" #include "Assets.h" #include "Check.h" #include "FileUtils.h" @@ -17,6 +17,18 @@ namespace tt::app::files { #define TAG "files_app" +extern const AppManifest manifest; + +/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */ +std::shared_ptr _Nullable optData() { + app::AppContext* app = service::loader::getCurrentApp(); + if (app->getManifest().id == manifest.id) { + return std::static_pointer_cast(app->getData()); + } else { + return nullptr; + } +} + /** * Case-insensitive check to see if the given file matches the provided file extension. * @param path the full path to the file @@ -57,10 +69,14 @@ static bool isSupportedTextFile(const char* filename) { // region Views -static void updateViews(Data* data); +static void updateViews(std::shared_ptr data); + +static void onNavigateUpPressed(TT_UNUSED lv_event_t* event) { + auto files_data = optData(); + if (files_data == nullptr) { + return; + } -static void onNavigateUpPressed(lv_event_t* event) { - auto* files_data = (Data*)lv_event_get_user_data(event); if (strcmp(files_data->current_path, "/") != 0) { TT_LOG_I(TAG, "Navigating upwards"); char new_absolute_path[MAX_PATH_LENGTH]; @@ -68,6 +84,7 @@ static void onNavigateUpPressed(lv_event_t* event) { data_set_entries_for_path(files_data, new_absolute_path); } } + updateViews(files_data); } @@ -103,16 +120,16 @@ static void viewFile(const char* path, const char* filename) { TT_LOG_I(TAG, "Clicked %s", filepath); if (isSupportedImageFile(filename)) { - Bundle bundle; - bundle.putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath); + auto bundle = std::shared_ptr(new Bundle()); + bundle->putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath); service::loader::startApp("ImageViewer", false, bundle); } else if (isSupportedTextFile(filename)) { - Bundle bundle; + auto bundle = std::shared_ptr(new Bundle()); if (get_platform() == PlatformEsp) { - bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath); + bundle->putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath); } else { // Remove forward slash, because we need a relative path - bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1); + bundle->putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1); } service::loader::startApp("TextViewer", false, bundle); } else { @@ -123,11 +140,13 @@ static void viewFile(const char* path, const char* filename) { } static void onFilePressed(lv_event_t* event) { + auto files_data = optData(); + if (files_data == nullptr) { + return; + } + lv_event_code_t code = lv_event_get_code(event); if (code == LV_EVENT_CLICKED) { - lv_obj_t* button = lv_event_get_current_target_obj(event); - auto* files_data = static_cast(lv_obj_get_user_data(button)); - auto* dir_entry = static_cast(lv_event_get_user_data(event)); TT_LOG_I(TAG, "Pressed %s %d", dir_entry->d_name, dir_entry->d_type); @@ -152,7 +171,7 @@ static void onFilePressed(lv_event_t* event) { } } -static void createFileWidget(Data* files_data, lv_obj_t* parent, struct dirent* dir_entry) { +static void createFileWidget(lv_obj_t* parent, struct dirent* dir_entry) { tt_check(parent); auto* list = (lv_obj_t*)parent; const char* symbol; @@ -166,15 +185,14 @@ static void createFileWidget(Data* files_data, lv_obj_t* parent, struct dirent* symbol = LV_SYMBOL_FILE; } lv_obj_t* button = lv_list_add_button(list, symbol, dir_entry->d_name); - lv_obj_set_user_data(button, files_data); lv_obj_add_event_cb(button, &onFilePressed, LV_EVENT_CLICKED, (void*)dir_entry); } -static void updateViews(Data* data) { +static void updateViews(std::shared_ptr data) { lv_obj_clean(data->list); for (int i = 0; i < data->dir_entries_count; ++i) { TT_LOG_I(TAG, "Entry: %s %d", data->dir_entries[i]->d_name, data->dir_entries[i]->d_type); - createFileWidget(data, data->list, data->dir_entries[i]); + createFileWidget(data->list, data->dir_entries[i]); } } @@ -182,14 +200,14 @@ static void updateViews(Data* data) { // region Lifecycle -static void onShow(App& app, lv_obj_t* parent) { - auto* data = static_cast(app.getData()); +static void onShow(AppContext& app, lv_obj_t* parent) { + auto data = std::static_pointer_cast(app.getData()); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_t* toolbar = lvgl::toolbar_create(parent, "Files"); lvgl::toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &onExitAppPressed, nullptr); - lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressed, data); + lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressed, nullptr); data->list = lv_list_create(parent); lv_obj_set_width(data->list, LV_PCT(100)); @@ -198,8 +216,8 @@ static void onShow(App& app, lv_obj_t* parent) { updateViews(data); } -static void onStart(App& app) { - auto* data = data_alloc(); +static void onStart(AppContext& app) { + auto data = std::shared_ptr(new Data()); // PC platform is bound to current work directory because of the LVGL file system mapping if (get_platform() == PlatformSimulator) { char cwd[PATH_MAX]; @@ -216,20 +234,14 @@ static void onStart(App& app) { app.setData(data); } -static void onStop(App& app) { - auto* data = static_cast(app.getData()); - data_free(data); -} - // endregion Lifecycle -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "Files", .name = "Files", .icon = TT_ASSETS_APP_ICON_FILES, .type = TypeSystem, .onStart = onStart, - .onStop = onStop, .onShow = onShow, }; diff --git a/Tactility/Source/app/files/FilesData.cpp b/Tactility/Source/app/files/FilesData.cpp index a3a11de1..5aa4caf2 100644 --- a/Tactility/Source/app/files/FilesData.cpp +++ b/Tactility/Source/app/files/FilesData.cpp @@ -26,40 +26,16 @@ static bool get_child_path(char* base_path, const char* child_path, char* out_pa } } -Data* data_alloc() { - auto* data = static_cast(malloc(sizeof(Data))); - *data = (Data) { - .current_path = { 0x00 }, - .dir_entries = nullptr, - .dir_entries_count = 0 - }; - return data; -} - -void data_free(Data* data) { - data_free_entries(data); - free(data); -} - -void data_free_entries(Data* data) { - for (int i = 0; i < data->dir_entries_count; ++i) { - free(data->dir_entries[i]); - } - free(data->dir_entries); - data->dir_entries = nullptr; - data->dir_entries_count = 0; -} - -static void data_set_entries(Data* data, struct dirent** entries, int count) { +static void data_set_entries(std::shared_ptr data, struct dirent** entries, int count) { if (data->dir_entries != nullptr) { - data_free_entries(data); + data->freeEntries(); } data->dir_entries = entries; data->dir_entries_count = count; } -bool data_set_entries_for_path(Data* data, const char* path) { +bool data_set_entries_for_path(std::shared_ptr data, const char* path) { TT_LOG_I(TAG, "Changing path: %s -> %s", data->current_path, path); /** @@ -100,7 +76,7 @@ bool data_set_entries_for_path(Data* data, const char* path) { } } -bool data_set_entries_for_child_path(Data* data, const char* child_path) { +bool data_set_entries_for_child_path(std::shared_ptr data, const char* child_path) { char new_absolute_path[MAX_PATH_LENGTH + 1]; if (get_child_path(data->current_path, child_path, new_absolute_path, MAX_PATH_LENGTH)) { TT_LOG_I(TAG, "Navigating from %s to %s", data->current_path, new_absolute_path); diff --git a/Tactility/Source/app/files/FilesData.h b/Tactility/Source/app/files/FilesData.h index ca959c4d..43d14160 100644 --- a/Tactility/Source/app/files/FilesData.h +++ b/Tactility/Source/app/files/FilesData.h @@ -1,23 +1,36 @@ #pragma once -#include #include "lvgl.h" +#include +#include namespace tt::app::files { #define MAX_PATH_LENGTH 256 -typedef struct { - char current_path[MAX_PATH_LENGTH]; - struct dirent** dir_entries; - int dir_entries_count; - lv_obj_t* list; -} Data; +struct Data { + char current_path[MAX_PATH_LENGTH] = { 0 }; + struct dirent** dir_entries = nullptr; + int dir_entries_count = 0; + lv_obj_t* list = nullptr; -Data* data_alloc(); -void data_free(Data* data); -void data_free_entries(Data* data); -bool data_set_entries_for_child_path(Data* data, const char* child_path); -bool data_set_entries_for_path(Data* data, const char* path); + void freeEntries() { + for (int i = 0; i < dir_entries_count; ++i) { + free(dir_entries[i]); + } + free(dir_entries); + dir_entries = nullptr; + dir_entries_count = 0; + } + + ~Data() { + freeEntries(); + } +}; + +void data_free(std::shared_ptr data); +void data_free_entries(std::shared_ptr data); +bool data_set_entries_for_child_path(std::shared_ptr data, const char* child_path); +bool data_set_entries_for_path(std::shared_ptr data, const char* path); } // namespace diff --git a/Tactility/Source/app/gpio/Gpio.cpp b/Tactility/Source/app/gpio/Gpio.cpp index c5ecacef..75525a37 100644 --- a/Tactility/Source/app/gpio/Gpio.cpp +++ b/Tactility/Source/app/gpio/Gpio.cpp @@ -29,8 +29,8 @@ public: tt_check(mutex.release() == TtStatusOk); } - void onShow(App& app, lv_obj_t* parent); - void onHide(App& app); + void onShow(AppContext& app, lv_obj_t* parent); + void onHide(AppContext& app); void startTask(); void stopTask(); @@ -135,8 +135,8 @@ void Gpio::stopTask() { // endregion Task -void Gpio::onShow(App& app, lv_obj_t* parent) { - auto* gpio = (Gpio*)app.getData(); +void Gpio::onShow(AppContext& app, lv_obj_t* parent) { + auto gpio = std::static_pointer_cast(app.getData()); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_t* toolbar = lvgl::toolbar_create(parent, app); @@ -191,42 +191,36 @@ void Gpio::onShow(App& app, lv_obj_t* parent) { gpio->startTask(); } -void Gpio::onHide(App& app) { - auto* gpio = (Gpio*)app.getData(); +void Gpio::onHide(AppContext& app) { + auto gpio = std::static_pointer_cast(app.getData()); gpio->stopTask(); } // region App lifecycle -static void onShow(App& app, lv_obj_t* parent) { - auto* gpio = (Gpio*)app.getData(); +static void onShow(AppContext& app, lv_obj_t* parent) { + auto gpio = std::static_pointer_cast(app.getData()); gpio->onShow(app, parent); } -static void onHide(App& app) { - auto* gpio = (Gpio*)app.getData(); +static void onHide(AppContext& app) { + auto gpio = std::static_pointer_cast(app.getData()); gpio->onHide(app); } -static void onStart(App& app) { - auto* gpio = new Gpio(); +static void onStart(AppContext& app) { + auto gpio = std::shared_ptr(new Gpio()); app.setData(gpio); } -static void onStop(App& app) { - auto* gpio = (Gpio*)app.getData(); - delete gpio; -} - // endregion App lifecycle -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "Gpio", .name = "GPIO", .type = TypeSystem, .onStart = onStart, - .onStop = onStop, .onShow = onShow, .onHide = onHide }; diff --git a/Tactility/Source/app/i2cscanner/I2cHelpers.cpp b/Tactility/Source/app/i2cscanner/I2cHelpers.cpp index 1098dfd2..823b8636 100644 --- a/Tactility/Source/app/i2cscanner/I2cHelpers.cpp +++ b/Tactility/Source/app/i2cscanner/I2cHelpers.cpp @@ -14,7 +14,6 @@ std::string getAddressText(uint8_t address) { return stream.str(); } - std::string getPortNamesForDropdown() { std::vector config_names; size_t port_index = 0; diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index 6447f8f8..be75dfdb 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -4,21 +4,37 @@ #include "Assets.h" #include "Tactility.h" -#include "app/App.h" +#include "app/AppContext.h" #include "lvgl/LvglSync.h" #include "lvgl/Toolbar.h" +#include "service/loader/Loader.h" #define START_SCAN_TEXT "Scan" #define STOP_SCAN_TEXT "Stop scan" namespace tt::app::i2cscanner { -static void updateViews(Data* data); +static void updateViews(std::shared_ptr data); +extern const AppManifest manifest; + +/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */ +std::shared_ptr _Nullable optData() { + app::AppContext* app = service::loader::getCurrentApp(); + if (app->getManifest().id == manifest.id) { + return std::static_pointer_cast(app->getData()); + } else { + return nullptr; + } +} static void onSelectBus(lv_event_t* event) { + auto data = optData(); + if (data == nullptr) { + return; + } + auto* dropdown = static_cast(lv_event_get_target(event)); uint32_t selected = lv_dropdown_get_selected(dropdown); - Data* data = (Data*) lv_event_get_user_data(event); auto i2c_devices = tt::getConfiguration()->hardware->i2c; assert(selected < i2c_devices.size()); @@ -35,16 +51,18 @@ static void onSelectBus(lv_event_t* event) { } static void onPressScan(lv_event_t* event) { - auto* data = (Data*)lv_event_get_user_data(event); - if (data->scanState == ScanStateScanning) { - stopScanning(data); - } else { - startScanning(data); + auto data = optData(); + if (data != nullptr) { + if (data->scanState == ScanStateScanning) { + stopScanning(data); + } else { + startScanning(data); + } + updateViews(data); } - updateViews(data); } -static void updateViews(Data* data) { +static void updateViews(std::shared_ptr data) { if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (data->scanState == ScanStateScanning) { lv_label_set_text(data->scanButtonLabelWidget, STOP_SCAN_TEXT); @@ -75,7 +93,7 @@ static void updateViews(Data* data) { } } -static void updateViewsSafely(Data* data) { +static void updateViewsSafely(std::shared_ptr data) { if (lvgl::lock(100 / portTICK_PERIOD_MS)) { updateViews(data); lvgl::unlock(); @@ -84,7 +102,7 @@ static void updateViewsSafely(Data* data) { } } -void onThreadFinished(Data* data) { +void onThreadFinished(std::shared_ptr data) { if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (data->scanState == ScanStateScanning) { data->scanState = ScanStateStopped; @@ -96,8 +114,8 @@ void onThreadFinished(Data* data) { } } -static void onShow(App& app, lv_obj_t* parent) { - auto* data = (Data*)app.getData(); +static void onShow(AppContext& app, lv_obj_t* parent) { + auto data = std::static_pointer_cast(app.getData()); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -116,7 +134,7 @@ static void onShow(App& app, lv_obj_t* parent) { lv_obj_t* scan_button = lv_button_create(wrapper); lv_obj_set_width(scan_button, LV_PCT(48)); lv_obj_align(scan_button, LV_ALIGN_TOP_LEFT, 0, 1); // Shift 1 pixel to align with selection box - lv_obj_add_event_cb(scan_button, &onPressScan, LV_EVENT_CLICKED, data); + lv_obj_add_event_cb(scan_button, &onPressScan, LV_EVENT_CLICKED, nullptr); lv_obj_t* scan_button_label = lv_label_create(scan_button); lv_obj_align(scan_button_label, LV_ALIGN_CENTER, 0, 0); lv_label_set_text(scan_button_label, START_SCAN_TEXT); @@ -127,7 +145,7 @@ static void onShow(App& app, lv_obj_t* parent) { lv_dropdown_set_options(port_dropdown, dropdown_items.c_str()); lv_obj_set_width(port_dropdown, LV_PCT(48)); lv_obj_align(port_dropdown, LV_ALIGN_TOP_RIGHT, 0, 0); - lv_obj_add_event_cb(port_dropdown, onSelectBus, LV_EVENT_VALUE_CHANGED, data); + lv_obj_add_event_cb(port_dropdown, onSelectBus, LV_EVENT_VALUE_CHANGED, nullptr); lv_dropdown_set_selected(port_dropdown, 0); data->portDropdownWidget = port_dropdown; @@ -139,30 +157,24 @@ static void onShow(App& app, lv_obj_t* parent) { data->scanListWidget = scan_list; } -static void onHide(App& app) { - auto* data = (Data*)app.getData(); +static void onHide(AppContext& app) { + auto data = std::static_pointer_cast(app.getData()); if (hasScanThread(data)) { stopScanning(data); } } -static void onStart(App& app) { - Data* data = new Data(); +static void onStart(AppContext& app) { + auto data = std::shared_ptr(new Data()); app.setData(data); } -static void onStop(App& app) { - Data* data = (Data*)app.getData(); - delete data; -} - -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "I2cScanner", .name = "I2C Scanner", .icon = TT_ASSETS_APP_ICON_I2C_SETTINGS, .type = TypeSystem, .onStart = onStart, - .onStop = onStop, .onShow = onShow, .onHide = onHide }; diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.h b/Tactility/Source/app/i2cscanner/I2cScanner.h index a1bf6cd4..ffb79640 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.h +++ b/Tactility/Source/app/i2cscanner/I2cScanner.h @@ -5,6 +5,7 @@ #include #include "lvgl.h" #include "hal/i2c/I2c.h" +#include namespace tt::app::i2cscanner { @@ -30,6 +31,6 @@ struct Data { lv_obj_t* scanListWidget = nullptr; }; -void onThreadFinished(Data* data); +void onThreadFinished(std::shared_ptr data); } diff --git a/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp b/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp index 952f52b8..64881219 100644 --- a/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp @@ -1,11 +1,12 @@ #include "I2cScannerThread.h" #include "lvgl.h" -#include "service/gui/Gui.h" -#include +#include "service/loader/Loader.h" namespace tt::app::i2cscanner { -static bool shouldStopScanThread(Data* data) { +std::shared_ptr _Nullable optData(); + +static bool shouldStopScanThread(std::shared_ptr data) { if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { bool is_scanning = data->scanState == ScanStateScanning; tt_check(data->mutex.release() == TtStatusOk); @@ -15,7 +16,7 @@ static bool shouldStopScanThread(Data* data) { } } -static bool getPort(Data* data, i2c_port_t* port) { +static bool getPort(std::shared_ptr data, i2c_port_t* port) { if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { *port = data->port; tt_assert(data->mutex.release() == TtStatusOk); @@ -26,7 +27,7 @@ static bool getPort(Data* data, i2c_port_t* port) { } } -static bool addAddressToList(Data* data, uint8_t address) { +static bool addAddressToList(std::shared_ptr data, uint8_t address) { if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { data->scannedAddresses.push_back(address); tt_assert(data->mutex.release() == TtStatusOk); @@ -37,8 +38,12 @@ static bool addAddressToList(Data* data, uint8_t address) { } } -static int32_t scanThread(void* context) { - Data* data = (Data*) context; +static int32_t scanThread(TT_UNUSED void* context) { + auto data = optData(); + if (data == nullptr) { + return -1; + } + TT_LOG_I(TAG, "Scan thread started"); for (uint8_t address = 0; address < 128; ++address) { @@ -69,7 +74,7 @@ static int32_t scanThread(void* context) { return 0; } -bool hasScanThread(Data* data) { +bool hasScanThread(std::shared_ptr data) { bool has_thread; if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { has_thread = data->scanThread != nullptr; @@ -82,7 +87,7 @@ bool hasScanThread(Data* data) { } } -void startScanning(Data* data) { +void startScanning(std::shared_ptr data) { if (hasScanThread(data)) { stopScanning(data); } @@ -99,7 +104,7 @@ void startScanning(Data* data) { "i2c scanner", 4096, scanThread, - data + nullptr ); data->scanThread->start(); tt_check(data->mutex.release() == TtStatusOk); @@ -108,7 +113,7 @@ void startScanning(Data* data) { } } -void stopScanning(Data* data) { +void stopScanning(std::shared_ptr data) { bool sent_halt; if (data->mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { tt_assert(data->scanThread != nullptr); diff --git a/Tactility/Source/app/i2cscanner/I2cScannerThread.h b/Tactility/Source/app/i2cscanner/I2cScannerThread.h index 45dc55ef..794f75a8 100644 --- a/Tactility/Source/app/i2cscanner/I2cScannerThread.h +++ b/Tactility/Source/app/i2cscanner/I2cScannerThread.h @@ -1,11 +1,12 @@ #pragma once #include "I2cScanner.h" +#include namespace tt::app::i2cscanner { -bool hasScanThread(Data* data); -void startScanning(Data* data); -void stopScanning(Data* data); +bool hasScanThread(std::shared_ptr data); +void startScanning(std::shared_ptr data); +void stopScanning(std::shared_ptr data); } diff --git a/Tactility/Source/app/i2csettings/I2cSettings.cpp b/Tactility/Source/app/i2csettings/I2cSettings.cpp index 9f4da220..5313c9b7 100644 --- a/Tactility/Source/app/i2csettings/I2cSettings.cpp +++ b/Tactility/Source/app/i2csettings/I2cSettings.cpp @@ -69,7 +69,7 @@ static void show(lv_obj_t* parent, const hal::i2c::Configuration& configuration) } } -static void onShow(App& app, lv_obj_t* parent) { +static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -85,7 +85,7 @@ static void onShow(App& app, lv_obj_t* parent) { } } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "I2cSettings", .name = "I2C", .icon = TT_ASSETS_APP_ICON_I2C_SETTINGS, diff --git a/Tactility/Source/app/imageviewer/ImageViewer.cpp b/Tactility/Source/app/imageviewer/ImageViewer.cpp index c4a79197..a712d9e4 100644 --- a/Tactility/Source/app/imageviewer/ImageViewer.cpp +++ b/Tactility/Source/app/imageviewer/ImageViewer.cpp @@ -1,5 +1,5 @@ +#include #include "ImageViewer.h" -#include "Log.h" #include "lvgl.h" #include "lvgl/Style.h" #include "lvgl/Toolbar.h" @@ -8,7 +8,7 @@ namespace tt::app::imageviewer { #define TAG "image_viewer" -static void onShow(App& app, lv_obj_t* parent) { +static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -22,16 +22,17 @@ static void onShow(App& app, lv_obj_t* parent) { lv_obj_t* image = lv_img_create(wrapper); lv_obj_align(image, LV_ALIGN_CENTER, 0, 0); - const Bundle& bundle = app.getParameters(); + std::shared_ptr bundle = app.getParameters(); + tt_check(bundle != nullptr, "Parameters not set"); std::string file_argument; - if (bundle.optString(IMAGE_VIEWER_FILE_ARGUMENT, file_argument)) { + if (bundle->optString(IMAGE_VIEWER_FILE_ARGUMENT, file_argument)) { std::string prefixed_path = "A:" + file_argument; TT_LOG_I(TAG, "Opening %s", prefixed_path.c_str()); lv_img_set_src(image, prefixed_path.c_str()); } } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "ImageViewer", .name = "Image Viewer", .type = TypeHidden, diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index 9a07d29a..9bc24ef7 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -1,4 +1,4 @@ -#include "app/App.h" +#include "app/AppContext.h" #include "Assets.h" #include "lvgl.h" #include "Tactility.h" @@ -6,22 +6,35 @@ #include "lvgl/LvglSync.h" #include "lvgl/Style.h" #include "lvgl/Toolbar.h" +#include "service/loader/Loader.h" namespace tt::app::power { #define TAG "power" -typedef struct { - Timer* update_timer; +extern const AppManifest manifest; +static void on_timer(TT_UNUSED void* context); + +struct Data { + std::unique_ptr update_timer = std::unique_ptr(new Timer(Timer::TypePeriodic, &on_timer, nullptr)); const hal::Power* power; lv_obj_t* enable_switch; lv_obj_t* charge_state; lv_obj_t* charge_level; lv_obj_t* current; -} Data; +}; -static void updateUi(void* callbackContext) { - auto* data = (Data*)callbackContext; +/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */ +std::shared_ptr _Nullable optData() { + app::AppContext* app = service::loader::getCurrentApp(); + if (app->getManifest().id == manifest.id) { + return std::static_pointer_cast(app->getData()); + } else { + return nullptr; + } +} + +static void updateUi(std::shared_ptr data) { bool charging_enabled = data->power->isChargingEnabled(); const char* charge_state = data->power->isCharging() ? "yes" : "no"; uint8_t charge_level = data->power->getChargeLevel(); @@ -40,20 +53,30 @@ static void updateUi(void* callbackContext) { lvgl::unlock(); } +static void on_timer(TT_UNUSED void* context) { + auto data = optData(); + if (data != nullptr) { + updateUi(data); + } +} + static void onPowerEnabledChanged(lv_event_t* event) { lv_event_code_t code = lv_event_get_code(event); auto* enable_switch = static_cast(lv_event_get_target(event)); if (code == LV_EVENT_VALUE_CHANGED) { bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED); - auto* data = static_cast(lv_event_get_user_data(event)); - if (data->power->isChargingEnabled() != is_on) { - data->power->setChargingEnabled(is_on); - updateUi(data); + + auto data = optData(); + if (data != nullptr) { + if (data->power->isChargingEnabled() != is_on) { + data->power->setChargingEnabled(is_on); + updateUi(data); + } } } } -static void onShow(App& app, lv_obj_t* parent) { +static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -64,7 +87,7 @@ static void onShow(App& app, lv_obj_t* parent) { lv_obj_set_flex_grow(wrapper, 1); lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); - auto* data = static_cast(app.getData()); + auto data = std::static_pointer_cast(app.getData()); // Top row: enable/disable lv_obj_t* switch_container = lv_obj_create(wrapper); @@ -78,7 +101,7 @@ static void onShow(App& app, lv_obj_t* parent) { lv_obj_set_align(enable_label, LV_ALIGN_LEFT_MID); lv_obj_t* enable_switch = lv_switch_create(switch_container); - lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_VALUE_CHANGED, data); + lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_VALUE_CHANGED, nullptr); lv_obj_set_align(enable_switch, LV_ALIGN_RIGHT_MID); data->enable_switch = enable_switch; @@ -90,32 +113,24 @@ static void onShow(App& app, lv_obj_t* parent) { data->update_timer->start(ms_to_ticks(1000)); } -static void onHide(TT_UNUSED App& app) { - auto* data = static_cast(app.getData()); +static void onHide(TT_UNUSED AppContext& app) { + auto data = std::static_pointer_cast(app.getData()); data->update_timer->stop(); } -static void onStart(App& app) { - auto* data = new Data(); - data->update_timer = new Timer(Timer::TypePeriodic, &updateUi, data); +static void onStart(AppContext& app) { + auto data = std::shared_ptr(new Data()); + app.setData(data); data->power = getConfiguration()->hardware->power; assert(data->power != nullptr); // The Power app only shows up on supported devices - app.setData(data); } -static void onStop(App& app) { - auto* data = static_cast(app.getData()); - delete data->update_timer; - delete data; -} - -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "Power", .name = "Power", .icon = TT_ASSETS_APP_ICON_POWER_SETTINGS, .type = TypeSettings, .onStart = onStart, - .onStop = onStop, .onShow = onShow, .onHide = onHide }; diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index d00c9aae..df3c6bee 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -1,29 +1,24 @@ #include "ScreenshotUi.h" +#include namespace tt::app::screenshot { -static void onShow(App& app, lv_obj_t* parent) { - auto* ui = static_cast(app.getData()); +static void onShow(AppContext& app, lv_obj_t* parent) { + auto ui = std::static_pointer_cast(app.getData()); create_ui(app, ui, parent); } -static void onStart(App& app) { - auto* ui = static_cast(malloc(sizeof(ScreenshotUi))); - app.setData(ui); +static void onStart(AppContext& app) { + auto ui = std::shared_ptr(new ScreenshotUi()); + app.setData(ui); // Ensure data gets deleted when no more in use } -static void onStop(App& app) { - auto* ui = static_cast(app.getData()); - free(ui); -} - -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "Screenshot", .name = "_Screenshot", // So it gets put at the bottom of the desktop and becomes less visible on small screen devices .icon = LV_SYMBOL_IMAGE, .type = TypeSystem, .onStart = onStart, - .onStop = onStop, .onShow = onShow, }; diff --git a/Tactility/Source/app/screenshot/ScreenshotUi.cpp b/Tactility/Source/app/screenshot/ScreenshotUi.cpp index 9f6507ce..08aee1c7 100644 --- a/Tactility/Source/app/screenshot/ScreenshotUi.cpp +++ b/Tactility/Source/app/screenshot/ScreenshotUi.cpp @@ -3,6 +3,7 @@ #include "TactilityCore.h" #include "hal/sdcard/Sdcard.h" #include "service/gui/Gui.h" +#include "service/loader/Loader.h" #include "service/screenshot/Screenshot.h" #include "lvgl/Toolbar.h" @@ -10,29 +11,24 @@ namespace tt::app::screenshot { #define TAG "screenshot_ui" -static void update_mode(ScreenshotUi* ui) { - lv_obj_t* label = ui->start_stop_button_label; - if (service::screenshot::isStarted()) { - lv_label_set_text(label, "Stop"); - } else { - lv_label_set_text(label, "Start"); - } +extern AppManifest manifest; +static void update_mode(std::shared_ptr ui); - uint32_t selected = lv_dropdown_get_selected(ui->mode_dropdown); - if (selected == 0) { // Timer - lv_obj_remove_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN); +/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */ +std::shared_ptr _Nullable optScreenshotUi() { + app::AppContext* app = service::loader::getCurrentApp(); + if (app->getManifest().id == manifest.id) { + return std::static_pointer_cast(app->getData()); } else { - lv_obj_add_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN); + return nullptr; } } -static void on_mode_set(lv_event_t* event) { - auto* ui = (ScreenshotUi*)lv_event_get_user_data(event); - update_mode(ui); -} - static void on_start_pressed(lv_event_t* event) { - auto* ui = static_cast(lv_event_get_user_data(event)); + auto ui = optScreenshotUi(); + if (ui == nullptr) { + return; + } if (service::screenshot::isStarted()) { TT_LOG_I(TAG, "Stop screenshot"); @@ -58,7 +54,30 @@ static void on_start_pressed(lv_event_t* event) { update_mode(ui); } -static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) { +static void update_mode(std::shared_ptr ui) { + lv_obj_t* label = ui->start_stop_button_label; + if (service::screenshot::isStarted()) { + lv_label_set_text(label, "Stop"); + } else { + lv_label_set_text(label, "Start"); + } + + uint32_t selected = lv_dropdown_get_selected(ui->mode_dropdown); + if (selected == 0) { // Timer + lv_obj_remove_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(ui->timer_wrapper, LV_OBJ_FLAG_HIDDEN); + } +} + +static void on_mode_set(lv_event_t* event) { + auto ui = optScreenshotUi(); + if (ui != nullptr) { + update_mode(ui); + } +} + +static void create_mode_setting_ui(std::shared_ptr ui, lv_obj_t* parent) { lv_obj_t* mode_wrapper = lv_obj_create(parent); lv_obj_set_size(mode_wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_style_pad_all(mode_wrapper, 0, 0); @@ -71,7 +90,7 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) { lv_obj_t* mode_dropdown = lv_dropdown_create(mode_wrapper); lv_dropdown_set_options(mode_dropdown, "Timer\nApp start"); lv_obj_align_to(mode_dropdown, mode_label, LV_ALIGN_OUT_RIGHT_MID, 8, 0); - lv_obj_add_event_cb(mode_dropdown, on_mode_set, LV_EVENT_VALUE_CHANGED, ui); + lv_obj_add_event_cb(mode_dropdown, on_mode_set, LV_EVENT_VALUE_CHANGED, nullptr); ui->mode_dropdown = mode_dropdown; service::screenshot::Mode mode = service::screenshot::getMode(); if (mode == service::screenshot::ScreenshotModeApps) { @@ -83,10 +102,10 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) { lv_obj_t* button_label = lv_label_create(button); lv_obj_align(button_label, LV_ALIGN_CENTER, 0, 0); ui->start_stop_button_label = button_label; - lv_obj_add_event_cb(button, &on_start_pressed, LV_EVENT_CLICKED, ui); + lv_obj_add_event_cb(button, &on_start_pressed, LV_EVENT_CLICKED, nullptr); } -static void create_path_ui(ScreenshotUi* ui, lv_obj_t* parent) { +static void create_path_ui(std::shared_ptr ui, lv_obj_t* parent) { lv_obj_t* path_wrapper = lv_obj_create(parent); lv_obj_set_size(path_wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_style_pad_all(path_wrapper, 0, 0); @@ -116,7 +135,7 @@ static void create_path_ui(ScreenshotUi* ui, lv_obj_t* parent) { } } -static void create_timer_settings_ui(ScreenshotUi* ui, lv_obj_t* parent) { +static void create_timer_settings_ui(std::shared_ptr ui, lv_obj_t* parent) { lv_obj_t* timer_wrapper = lv_obj_create(parent); lv_obj_set_size(timer_wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_style_pad_all(timer_wrapper, 0, 0); @@ -153,7 +172,7 @@ static void create_timer_settings_ui(ScreenshotUi* ui, lv_obj_t* parent) { lv_label_set_text(delay_unit_label, "seconds"); } -void create_ui(const App& app, ScreenshotUi* ui, lv_obj_t* parent) { +void create_ui(const AppContext& app, std::shared_ptr ui, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_t* toolbar = lvgl::toolbar_create(parent, app); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); diff --git a/Tactility/Source/app/screenshot/ScreenshotUi.h b/Tactility/Source/app/screenshot/ScreenshotUi.h index 282f6536..64be0a99 100644 --- a/Tactility/Source/app/screenshot/ScreenshotUi.h +++ b/Tactility/Source/app/screenshot/ScreenshotUi.h @@ -1,6 +1,6 @@ #pragma once -#include "app/App.h" +#include "app/AppContext.h" #include "lvgl.h" namespace tt::app::screenshot { @@ -13,6 +13,6 @@ typedef struct { lv_obj_t* delay_textarea; } ScreenshotUi; -void create_ui(const App& app, ScreenshotUi* ui, lv_obj_t* parent); +void create_ui(const AppContext& app, std::shared_ptr ui, lv_obj_t* parent); } // namespace diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp index acd652fa..2d97472e 100644 --- a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp +++ b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp @@ -1,9 +1,9 @@ #include "SelectionDialog.h" -#include "Log.h" #include "lvgl.h" #include "lvgl/Toolbar.h" #include "service/loader/Loader.h" #include +#include namespace tt::app::selectiondialog { @@ -27,17 +27,17 @@ int32_t getResultIndex(const Bundle& bundle) { return index; } -void setResultIndex(Bundle& bundle, int32_t index) { - bundle.putInt32(RESULT_BUNDLE_KEY_INDEX, index); +void setResultIndex(std::shared_ptr bundle, int32_t index) { + bundle->putInt32(RESULT_BUNDLE_KEY_INDEX, index); } -void setTitleParameter(Bundle& bundle, const std::string& title) { - bundle.putString(PARAMETER_BUNDLE_KEY_TITLE, title); +void setTitleParameter(std::shared_ptr bundle, const std::string& title) { + bundle->putString(PARAMETER_BUNDLE_KEY_TITLE, title); } -static std::string getTitleParameter(const Bundle& bundle) { +static std::string getTitleParameter(std::shared_ptr bundle) { std::string result; - if (bundle.optString(PARAMETER_BUNDLE_KEY_TITLE, result)) { + if (bundle->optString(PARAMETER_BUNDLE_KEY_TITLE, result)) { return result; } else { return DEFAULT_TITLE; @@ -49,8 +49,8 @@ static void onListItemSelected(lv_event_t* e) { if (code == LV_EVENT_CLICKED) { size_t index = (size_t)(e->user_data); TT_LOG_I(TAG, "Selected item at index %d", index); - tt::app::App* app = service::loader::getCurrentApp(); - Bundle bundle; + tt::app::AppContext* app = service::loader::getCurrentApp(); + auto bundle = std::shared_ptr(new Bundle()); setResultIndex(bundle, (int32_t)index); app->setResult(app::ResultOk, bundle); service::loader::stopApp(); @@ -63,7 +63,7 @@ static void createChoiceItem(void* parent, const std::string& title, size_t inde lv_obj_add_event_cb(btn, &onListItemSelected, LV_EVENT_CLICKED, (void*)index); } -static void onShow(App& app, lv_obj_t* parent) { +static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); std::string title = getTitleParameter(app.getParameters()); lvgl::toolbar_create(parent, title); @@ -72,16 +72,17 @@ static void onShow(App& app, lv_obj_t* parent) { lv_obj_set_width(list, LV_PCT(100)); lv_obj_set_flex_grow(list, 1); - const Bundle& parameters = app.getParameters(); + auto parameters = app.getParameters(); + tt_check(parameters != nullptr, "No parameters"); std::string items_concatenated; - if (parameters.optString(PARAMETER_BUNDLE_KEY_ITEMS, items_concatenated)) { + if (parameters->optString(PARAMETER_BUNDLE_KEY_ITEMS, items_concatenated)) { std::vector items = string_split(items_concatenated, PARAMETER_ITEM_CONCATENATION_TOKEN); if (items.empty() || items.front().empty()) { TT_LOG_E(TAG, "No items provided"); app.setResult(ResultError); service::loader::stopApp(); } else if (items.size() == 1) { - Bundle result_bundle; + auto result_bundle = std::shared_ptr(new Bundle()); setResultIndex(result_bundle, 0); app.setResult(ResultOk, result_bundle); service::loader::stopApp(); @@ -99,7 +100,7 @@ static void onShow(App& app, lv_obj_t* parent) { } } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "SelectionDialog", .name = "Selection Dialog", .type = TypeHidden, diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index d9962e74..56ea531e 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -11,12 +11,12 @@ namespace tt::app::settings { static void onAppPressed(lv_event_t* e) { lv_event_code_t code = lv_event_get_code(e); if (code == LV_EVENT_CLICKED) { - const auto* manifest = static_cast(lv_event_get_user_data(e)); + const auto* manifest = static_cast(lv_event_get_user_data(e)); service::loader::startApp(manifest->id); } } -static void createWidget(const Manifest* manifest, void* parent) { +static void createWidget(const AppManifest* manifest, void* parent) { tt_check(parent); auto* list = (lv_obj_t*)parent; const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK; @@ -24,7 +24,7 @@ static void createWidget(const Manifest* manifest, void* parent) { lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest); } -static void onShow(App& app, lv_obj_t* parent) { +static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -42,7 +42,7 @@ static void onShow(App& app, lv_obj_t* parent) { } } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "Settings", .name = "Settings", .icon = TT_ASSETS_APP_ICON_SETTINGS, diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index c74725a0..e22ca17f 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -108,7 +108,7 @@ static void addRtosTasks(lv_obj_t* parent) { #endif -static void onShow(App& app, lv_obj_t* parent) { +static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -151,7 +151,7 @@ static void onShow(App& app, lv_obj_t* parent) { #endif } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "SystemInfo", .name = "System Info", .icon = TT_ASSETS_APP_ICON_SYSTEM_INFO, diff --git a/Tactility/Source/app/textviewer/TextViewer.cpp b/Tactility/Source/app/textviewer/TextViewer.cpp index b162c54d..5a0636cb 100644 --- a/Tactility/Source/app/textviewer/TextViewer.cpp +++ b/Tactility/Source/app/textviewer/TextViewer.cpp @@ -1,4 +1,4 @@ -#include "Log.h" +#include #include "TextViewer.h" #include "lvgl.h" #include "lvgl/LabelUtils.h" @@ -9,7 +9,7 @@ namespace tt::app::textviewer { -static void onShow(App& app, lv_obj_t* parent) { +static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -22,9 +22,10 @@ static void onShow(App& app, lv_obj_t* parent) { lv_obj_t* label = lv_label_create(wrapper); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); - const Bundle& bundle = app.getParameters(); + auto parameters = app.getParameters(); + tt_check(parameters != nullptr, "No parameters"); std::string file_argument; - if (bundle.optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) { + if (parameters->optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) { TT_LOG_I(TAG, "Opening %s", file_argument.c_str()); lvgl::label_set_text_file(label, file_argument.c_str()); } else { @@ -32,7 +33,7 @@ static void onShow(App& app, lv_obj_t* parent) { } } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "TextViewer", .name = "Text Viewer", .type = TypeHidden, diff --git a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp index d93d35d5..3ea572aa 100644 --- a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp +++ b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp @@ -1,6 +1,6 @@ #include "WifiApSettings.h" #include "TactilityCore.h" -#include "app/App.h" +#include "app/AppContext.h" #include "lvgl.h" #include "lvgl/Style.h" #include "lvgl/Toolbar.h" @@ -11,11 +11,11 @@ namespace tt::app::wifiapsettings { #define TAG "wifi_ap_settings" -extern const Manifest manifest; +extern const AppManifest manifest; void start(const std::string& ssid) { - Bundle bundle; - bundle.putString("ssid", ssid); + auto bundle = std::shared_ptr(new Bundle()); + bundle->putString("ssid", ssid); service::loader::startApp(manifest.id, false, bundle); } @@ -39,9 +39,10 @@ static void onToggleAutoConnect(lv_event_t* event) { } } -static void onShow(App& app, lv_obj_t* parent) { - const Bundle& bundle = app.getParameters(); - std::string ssid = bundle.getString("ssid"); +static void onShow(AppContext& app, lv_obj_t* parent) { + auto paremeters = app.getParameters(); + tt_check(paremeters != nullptr, "No parameters"); + std::string ssid = paremeters->getString("ssid"); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, ssid); @@ -69,7 +70,7 @@ static void onShow(App& app, lv_obj_t* parent) { lv_obj_align(auto_connect_label, LV_ALIGN_TOP_LEFT, 0, 6); lv_obj_t* auto_connect_switch = lv_switch_create(wrapper); - lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, (void*)&bundle); + lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, (void*)&paremeters); lv_obj_align(auto_connect_switch, LV_ALIGN_TOP_RIGHT, 0, 0); service::wifi::settings::WifiApSettings settings {}; @@ -85,7 +86,7 @@ static void onShow(App& app, lv_obj_t* parent) { } } -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "WifiApSettings", .name = "Wi-Fi AP Settings", .icon = LV_SYMBOL_WIFI, diff --git a/Tactility/Source/app/wificonnect/View.cpp b/Tactility/Source/app/wificonnect/View.cpp index dc0f7df8..91923c0e 100644 --- a/Tactility/Source/app/wificonnect/View.cpp +++ b/Tactility/Source/app/wificonnect/View.cpp @@ -3,12 +3,12 @@ #include "Parameters.h" #include "WifiConnect.h" -#include "Log.h" #include "lvgl.h" #include "service/gui/Gui.h" #include "service/wifi/WifiSettings.h" #include "lvgl/Style.h" #include "lvgl/Toolbar.h" +#include namespace tt::app::wificonnect { @@ -107,7 +107,7 @@ void View::createBottomButtons(WifiConnect* wifi, lv_obj_t* parent) { } // TODO: Standardize dialogs -void View::init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent) { +void View::init(AppContext& app, WifiConnect* wifiConnect, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -187,15 +187,17 @@ void View::init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent) { service::gui::keyboardAddTextArea(password_textarea); // Init from app parameters - const Bundle& bundle = app.getParameters(); - std::string ssid; - if (bundle.optString(WIFI_CONNECT_PARAM_SSID, ssid)) { - lv_textarea_set_text(ssid_textarea, ssid.c_str()); - } + auto bundle = app.getParameters(); + if (bundle != nullptr) { + std::string ssid; + if (bundle->optString(WIFI_CONNECT_PARAM_SSID, ssid)) { + lv_textarea_set_text(ssid_textarea, ssid.c_str()); + } - std::string password; - if (bundle.optString(WIFI_CONNECT_PARAM_PASSWORD, password)) { - lv_textarea_set_text(password_textarea, password.c_str()); + std::string password; + if (bundle->optString(WIFI_CONNECT_PARAM_PASSWORD, password)) { + lv_textarea_set_text(password_textarea, password.c_str()); + } } } diff --git a/Tactility/Source/app/wificonnect/View.h b/Tactility/Source/app/wificonnect/View.h index 7d3761f2..e723c55b 100644 --- a/Tactility/Source/app/wificonnect/View.h +++ b/Tactility/Source/app/wificonnect/View.h @@ -3,7 +3,7 @@ #include "Bindings.h" #include "State.h" -#include "app/App.h" +#include "app/AppContext.h" #include "lvgl.h" namespace tt::app::wificonnect { @@ -25,7 +25,7 @@ public: lv_obj_t* connection_error = nullptr; lv_group_t* group = nullptr; - void init(App& app, WifiConnect* wifiConnect, lv_obj_t* parent); + void init(AppContext& app, WifiConnect* wifiConnect, lv_obj_t* parent); void update(Bindings* bindings, State* state); void createBottomButtons(WifiConnect* wifi, lv_obj_t* parent); diff --git a/Tactility/Source/app/wificonnect/WifiConnect.cpp b/Tactility/Source/app/wificonnect/WifiConnect.cpp index c6cd4880..88775f71 100644 --- a/Tactility/Source/app/wificonnect/WifiConnect.cpp +++ b/Tactility/Source/app/wificonnect/WifiConnect.cpp @@ -1,6 +1,6 @@ #include "WifiConnect.h" -#include "app/App.h" +#include "app/AppContext.h" #include "TactilityCore.h" #include "service/loader/Loader.h" #include "service/wifi/Wifi.h" @@ -10,6 +10,18 @@ namespace tt::app::wificonnect { #define TAG "wifi_connect" +extern const AppManifest manifest; + +/** Returns the app data if the app is active. Note that this could clash if the same app is started twice and a background thread is slow. */ +std::shared_ptr _Nullable optWifiConnect() { + app::AppContext* app = service::loader::getCurrentApp(); + if (app->getManifest().id == manifest.id) { + return std::static_pointer_cast(app->getData()); + } else { + return nullptr; + } +} + static void eventCallback(const void* message, void* context) { auto* event = static_cast(message); auto* wifi = static_cast(context); @@ -76,7 +88,7 @@ void WifiConnect::requestViewUpdate() { unlock(); } -void WifiConnect::onShow(App& app, lv_obj_t* parent) { +void WifiConnect::onShow(AppContext& app, lv_obj_t* parent) { lock(); view_enabled = true; view.init(app, this, parent); @@ -84,41 +96,35 @@ void WifiConnect::onShow(App& app, lv_obj_t* parent) { unlock(); } -void WifiConnect::onHide(App& app) { +void WifiConnect::onHide(AppContext& app) { // No need to lock view, as this is called from within Gui's LVGL context lock(); view_enabled = false; unlock(); } -static void onShow(App& app, lv_obj_t* parent) { - auto* wifi = static_cast(app.getData()); +static void onShow(AppContext& app, lv_obj_t* parent) { + auto wifi = std::static_pointer_cast(app.getData()); wifi->onShow(app, parent); } -static void onHide(App& app) { - auto* wifi = static_cast(app.getData()); +static void onHide(AppContext& app) { + auto wifi = std::static_pointer_cast(app.getData()); wifi->onHide(app); } -static void onStart(App& app) { - auto* wifi_connect = new WifiConnect(); - app.setData(wifi_connect); +static void onStart(AppContext& app) { + auto wifi = std::shared_ptr(new WifiConnect()); + app.setData(wifi); } -static void onStop(App& app) { - auto* wifi_connect = static_cast(app.getData()); - tt_assert(wifi_connect != nullptr); - delete wifi_connect; -} -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "WifiConnect", .name = "Wi-Fi Connect", .icon = LV_SYMBOL_WIFI, .type = TypeSettings, .onStart = &onStart, - .onStop = &onStop, .onShow = &onShow, .onHide = &onHide }; diff --git a/Tactility/Source/app/wificonnect/WifiConnect.h b/Tactility/Source/app/wificonnect/WifiConnect.h index 2f3a13d0..fbd10f52 100644 --- a/Tactility/Source/app/wificonnect/WifiConnect.h +++ b/Tactility/Source/app/wificonnect/WifiConnect.h @@ -28,8 +28,8 @@ public: void lock(); void unlock(); - void onShow(App& app, lv_obj_t* parent); - void onHide(App& app); + void onShow(AppContext& app, lv_obj_t* parent); + void onHide(AppContext& app); State& getState() { return state; } Bindings& getBindings() { return bindings; } diff --git a/Tactility/Source/app/wifimanage/View.cpp b/Tactility/Source/app/wifimanage/View.cpp index 99c8dcf8..e57ba307 100644 --- a/Tactility/Source/app/wifimanage/View.cpp +++ b/Tactility/Source/app/wifimanage/View.cpp @@ -214,7 +214,7 @@ void View::updateEnableOnBootToggle() { // region Main -void View::init(const App& app, Bindings* bindings, lv_obj_t* parent) { +void View::init(const AppContext& app, Bindings* bindings, lv_obj_t* parent) { root = parent; // Toolbar diff --git a/Tactility/Source/app/wifimanage/View.h b/Tactility/Source/app/wifimanage/View.h index 33c9aa19..2da3ea88 100644 --- a/Tactility/Source/app/wifimanage/View.h +++ b/Tactility/Source/app/wifimanage/View.h @@ -1,6 +1,6 @@ #pragma once -#include "app/App.h" +#include "app/AppContext.h" #include "Bindings.h" #include "State.h" #include "lvgl.h" @@ -16,7 +16,7 @@ private: lv_obj_t* networks_list = nullptr; public: View() {} - void init(const App& app, Bindings* bindings, lv_obj_t* parent); + void init(const AppContext& app, Bindings* bindings, lv_obj_t* parent); void update(Bindings* bindings, State* state); private: diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index 65f8d389..2890560b 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -2,7 +2,7 @@ #include "View.h" #include "State.h" -#include "app/App.h" +#include "app/AppContext.h" #include "app/wificonnect/Parameters.h" #include "app/wifiapsettings/WifiApSettings.h" #include "TactilityCore.h" @@ -21,9 +21,9 @@ static void onConnect(const char* ssid) { service::wifi::connect(&settings, false); } else { TT_LOG_I(TAG, "Starting connection dialog"); - Bundle bundle; - bundle.putString(WIFI_CONNECT_PARAM_SSID, ssid); - bundle.putString(WIFI_CONNECT_PARAM_PASSWORD, ""); + auto bundle = std::shared_ptr(new Bundle()); + bundle->putString(WIFI_CONNECT_PARAM_SSID, ssid); + bundle->putString(WIFI_CONNECT_PARAM_PASSWORD, ""); service::loader::startApp("WifiConnect", false, bundle); } } @@ -95,7 +95,7 @@ static void wifiManageEventCallback(const void* message, void* context) { wifi->requestViewUpdate(); } -void WifiManage::onShow(App& app, lv_obj_t* parent) { +void WifiManage::onShow(AppContext& app, lv_obj_t* parent) { PubSub* wifi_pubsub = service::wifi::getPubsub(); wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &wifiManageEventCallback, this); @@ -121,7 +121,7 @@ void WifiManage::onShow(App& app, lv_obj_t* parent) { } } -void WifiManage::onHide(TT_UNUSED App& app) { +void WifiManage::onHide(TT_UNUSED AppContext& app) { lock(); PubSub* wifi_pubsub = service::wifi::getPubsub(); tt_pubsub_unsubscribe(wifi_pubsub, wifiSubscription); @@ -132,37 +132,29 @@ void WifiManage::onHide(TT_UNUSED App& app) { // region Manifest methods -static void onStart(App& app) { - auto* wifi = new WifiManage(); +static void onStart(AppContext& app) { + auto wifi = std::shared_ptr(new WifiManage()); app.setData(wifi); } -static void onStop(App& app) { - auto* wifi = (WifiManage*)app.getData(); - tt_assert(wifi != nullptr); - delete wifi; -} - -static void onShow(App& app, lv_obj_t* parent) { - auto* wifi = (WifiManage*)app.getData(); +static void onShow(AppContext& app, lv_obj_t* parent) { + auto wifi = std::static_pointer_cast(app.getData()); wifi->onShow(app, parent); } -static void onHide(App& app) { - auto* wifi = (WifiManage*)app.getData(); +static void onHide(AppContext& app) { + auto wifi = std::static_pointer_cast(app.getData()); wifi->onHide(app); } - // endregion -extern const Manifest manifest = { +extern const AppManifest manifest = { .id = "WifiManage", .name = "Wi-Fi", .icon = LV_SYMBOL_WIFI, .type = TypeSettings, .onStart = onStart, - .onStop = onStop, .onShow = onShow, .onHide = onHide }; diff --git a/Tactility/Source/app/wifimanage/WifiManage.h b/Tactility/Source/app/wifimanage/WifiManage.h index 50043ff8..ff2b8f47 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.h +++ b/Tactility/Source/app/wifimanage/WifiManage.h @@ -23,8 +23,8 @@ public: void lock(); void unlock(); - void onShow(App& app, lv_obj_t* parent); - void onHide(App& app); + void onShow(AppContext& app, lv_obj_t* parent); + void onHide(AppContext& app); State& getState() { return state; } diff --git a/Tactility/Source/lvgl/Statusbar.h b/Tactility/Source/lvgl/Statusbar.h index 08ab90e0..56a9f787 100644 --- a/Tactility/Source/lvgl/Statusbar.h +++ b/Tactility/Source/lvgl/Statusbar.h @@ -1,7 +1,7 @@ #pragma once #include "lvgl.h" -#include "app/App.h" +#include "app/AppContext.h" namespace tt::lvgl { diff --git a/Tactility/Source/lvgl/Toolbar.cpp b/Tactility/Source/lvgl/Toolbar.cpp index bc7208a1..5e8aa625 100644 --- a/Tactility/Source/lvgl/Toolbar.cpp +++ b/Tactility/Source/lvgl/Toolbar.cpp @@ -91,7 +91,7 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) { return obj; } -lv_obj_t* toolbar_create(lv_obj_t* parent, const app::App& app) { +lv_obj_t* toolbar_create(lv_obj_t* parent, const app::AppContext& app) { return toolbar_create(parent, app.getManifest().name); } diff --git a/Tactility/Source/lvgl/Toolbar.h b/Tactility/Source/lvgl/Toolbar.h index 86dcb710..d5048cdf 100644 --- a/Tactility/Source/lvgl/Toolbar.h +++ b/Tactility/Source/lvgl/Toolbar.h @@ -1,7 +1,7 @@ #pragma once #include "lvgl.h" -#include "app/App.h" +#include "app/AppContext.h" namespace tt::lvgl { @@ -19,7 +19,7 @@ typedef struct { } ToolbarAction; lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title); -lv_obj_t* toolbar_create(lv_obj_t* parent, const app::App& app); +lv_obj_t* toolbar_create(lv_obj_t* parent, const app::AppContext& app); void toolbar_set_title(lv_obj_t* obj, const std::string& title); void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data); uint8_t toolbar_add_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data); diff --git a/Tactility/Source/service/gui/Gui.cpp b/Tactility/Source/service/gui/Gui.cpp index ead8408a..c6426f0a 100644 --- a/Tactility/Source/service/gui/Gui.cpp +++ b/Tactility/Source/service/gui/Gui.cpp @@ -18,8 +18,8 @@ Gui* gui = nullptr; void loader_callback(const void* message, TT_UNUSED void* context) { auto* event = static_cast(message); if (event->type == loader::LoaderEventTypeApplicationShowing) { - app::App& app = event->app_showing.app; - const app::Manifest& app_manifest = app.getManifest(); + app::AppContext& app = event->app_showing.app; + const app::AppManifest& app_manifest = app.getManifest(); showApp(app, app_manifest.onShow, app_manifest.onHide); } else if (event->type == loader::LoaderEventTypeApplicationHiding) { hideApp(); @@ -77,7 +77,7 @@ void requestDraw() { thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW); } -void showApp(app::App& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) { +void showApp(app::AppContext& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) { lock(); tt_check(gui->app_view_port == nullptr); gui->app_view_port = view_port_alloc(app, on_show, on_hide); @@ -128,14 +128,14 @@ static int32_t gui_main(TT_UNUSED void* p) { // region AppManifest -static void start(TT_UNUSED Service& service) { +static void start(TT_UNUSED ServiceContext& service) { gui = gui_alloc(); gui->thread->setPriority(THREAD_PRIORITY_SERVICE); gui->thread->start(); } -static void stop(TT_UNUSED Service& service) { +static void stop(TT_UNUSED ServiceContext& service) { lock(); ThreadId thread_id = gui->thread->getId(); @@ -148,7 +148,7 @@ static void stop(TT_UNUSED Service& service) { gui_free(gui); } -extern const Manifest manifest = { +extern const ServiceManifest manifest = { .id = "Gui", .onStart = &start, .onStop = &stop diff --git a/Tactility/Source/service/gui/Gui.h b/Tactility/Source/service/gui/Gui.h index 9e736fe8..a7dffe24 100644 --- a/Tactility/Source/service/gui/Gui.h +++ b/Tactility/Source/service/gui/Gui.h @@ -1,6 +1,6 @@ #pragma once -#include "app/App.h" +#include "app/AppContext.h" #include "ViewPort.h" namespace tt::service::gui { @@ -14,7 +14,7 @@ typedef struct Gui Gui; * @param on_show * @param on_hide */ -void showApp(app::App& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide); +void showApp(app::AppContext& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide); /** * Hide the current app's viewport. diff --git a/Tactility/Source/service/gui/GuiDraw.cpp b/Tactility/Source/service/gui/GuiDraw.cpp index 871a0525..876cdd51 100644 --- a/Tactility/Source/service/gui/GuiDraw.cpp +++ b/Tactility/Source/service/gui/GuiDraw.cpp @@ -9,7 +9,7 @@ namespace tt::service::gui { #define TAG "gui" -static lv_obj_t* create_app_views(Gui* gui, lv_obj_t* parent, app::App& app) { +static lv_obj_t* create_app_views(Gui* gui, lv_obj_t* parent, app::AppContext& app) { lvgl::obj_set_style_bg_blacken(parent); lv_obj_t* vertical_container = lv_obj_create(parent); @@ -49,7 +49,7 @@ void redraw(Gui* gui) { ViewPort* view_port = gui->app_view_port; if (view_port != nullptr) { - app::App& app = view_port->app; + app::AppContext& app = view_port->app; lv_obj_t* container = create_app_views(gui, gui->lvgl_parent, app); view_port_show(view_port, container); } else { diff --git a/Tactility/Source/service/gui/ViewPort.cpp b/Tactility/Source/service/gui/ViewPort.cpp index f45d07ab..c4e424c6 100644 --- a/Tactility/Source/service/gui/ViewPort.cpp +++ b/Tactility/Source/service/gui/ViewPort.cpp @@ -9,7 +9,7 @@ namespace tt::service::gui { #define TAG "viewport" ViewPort* view_port_alloc( - app::App& app, + app::AppContext& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide ) { diff --git a/Tactility/Source/service/gui/ViewPort.h b/Tactility/Source/service/gui/ViewPort.h index 9e2cb647..4c1d22b5 100644 --- a/Tactility/Source/service/gui/ViewPort.h +++ b/Tactility/Source/service/gui/ViewPort.h @@ -1,6 +1,6 @@ #pragma once -#include "app/App.h" +#include "app/AppContext.h" #include "lvgl.h" namespace tt::service::gui { @@ -8,18 +8,18 @@ namespace tt::service::gui { /** ViewPort Draw callback * @warning called from GUI thread */ -typedef void (*ViewPortShowCallback)(app::App& app, lv_obj_t* parent); -typedef void (*ViewPortHideCallback)(app::App& app); +typedef void (*ViewPortShowCallback)(app::AppContext& app, lv_obj_t* parent); +typedef void (*ViewPortHideCallback)(app::AppContext& app); // TODO: Move internally, use handle publicly typedef struct ViewPort { - app::App& app; + app::AppContext& app; ViewPortShowCallback onShow; ViewPortHideCallback _Nullable onHide; ViewPort( - app::App& app, + app::AppContext& app, ViewPortShowCallback on_show, ViewPortHideCallback _Nullable on_hide ) : app(app), onShow(on_show), onHide(on_hide) {} @@ -35,7 +35,7 @@ typedef struct ViewPort { * @return ViewPort instance */ ViewPort* view_port_alloc( - app::App& app, + app::AppContext& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide ); diff --git a/Tactility/Source/service/loader/Loader.cpp b/Tactility/Source/service/loader/Loader.cpp index d64ea36b..75892c95 100644 --- a/Tactility/Source/service/loader/Loader.cpp +++ b/Tactility/Source/service/loader/Loader.cpp @@ -1,8 +1,8 @@ #include "Tactility.h" #include -#include "app/Manifest.h" +#include "app/AppManifest.h" #include "app/ManifestRegistry.h" -#include "service/Manifest.h" +#include "service/ServiceManifest.h" #include "service/gui/Gui.h" #include "service/loader/Loader_i.h" #include "RtosCompat.h" @@ -63,7 +63,7 @@ static void loader_unlock() { tt_check(tt_mutex_release(loader_singleton->mutex) == TtStatusOk); } -LoaderStatus startApp(const std::string& id, bool blocking, const Bundle& arguments) { +LoaderStatus startApp(const std::string& id, bool blocking, std::shared_ptr parameters) { TT_LOG_I(TAG, "Start app %s", id.c_str()); tt_assert(loader_singleton); @@ -71,7 +71,7 @@ LoaderStatus startApp(const std::string& id, bool blocking, const Bundle& argume .value = LoaderStatusOk }; - auto* start_message = new LoaderMessageAppStart(id, arguments); + auto* start_message = new LoaderMessageAppStart(id, parameters); LoaderMessage message(start_message, result); EventFlag* event_flag = blocking ? new EventFlag() : nullptr; @@ -99,12 +99,12 @@ void stopApp() { loader_singleton->queue.put(&message, TtWaitForever); } -app::App* _Nullable getCurrentApp() { +app::AppContext* _Nullable getCurrentApp() { tt_assert(loader_singleton); loader_lock(); app::AppInstance* app = loader_singleton->app_stack.top(); loader_unlock(); - return dynamic_cast(app); + return dynamic_cast(app); } PubSub* getPubsub() { @@ -133,7 +133,7 @@ static const char* app_state_to_string(app::State state) { } static void app_transition_to_state(app::AppInstance& app, app::State state) { - const app::Manifest& manifest = app.getManifest(); + const app::AppManifest& manifest = app.getManifest(); const app::State old_state = app.getState(); TT_LOG_I( @@ -187,15 +187,15 @@ static void app_transition_to_state(app::AppInstance& app, app::State state) { } static LoaderStatus loader_do_start_app_with_manifest( - const app::Manifest* manifest, - const Bundle& bundle + const app::AppManifest* manifest, + std::shared_ptr _Nullable parameters ) { TT_LOG_I(TAG, "start with manifest %s", manifest->id.c_str()); loader_lock(); auto previous_app = !loader_singleton->app_stack.empty() ? loader_singleton->app_stack.top() : nullptr; - auto new_app = new app::AppInstance(*manifest, bundle); + auto new_app = new app::AppInstance(*manifest, parameters); new_app->mutableFlags().showStatusbar = (manifest->type != app::TypeBoot); loader_singleton->app_stack.push(new_app); @@ -227,16 +227,16 @@ static LoaderStatus loader_do_start_app_with_manifest( static LoaderStatus do_start_by_id( const std::string& id, - const Bundle& bundle + std::shared_ptr _Nullable parameters ) { TT_LOG_I(TAG, "Start by id %s", id.c_str()); - const app::Manifest* manifest = app::findAppById(id); + const app::AppManifest* manifest = app::findAppById(id); if (manifest == nullptr) { TT_LOG_E(TAG, "App not found: %s", id.c_str()); return LoaderStatusErrorUnknownApp; } else { - return loader_do_start_app_with_manifest(manifest, bundle); + return loader_do_start_app_with_manifest(manifest, parameters); } } @@ -263,7 +263,7 @@ static void do_stop_app() { std::unique_ptr result_holder = std::move(app_to_stop->getResult()); - const app::Manifest& manifest = app_to_stop->getManifest(); + const app::AppManifest& manifest = app_to_stop->getManifest(); app_transition_to_state(*app_to_stop, app::StateHiding); app_transition_to_state(*app_to_stop, app::StateStopped); @@ -283,7 +283,7 @@ static void do_stop_app() { auto on_result = app_to_resume->getManifest().onResult; if (on_result != nullptr) { if (result_holder != nullptr) { - Bundle* result_bundle = result_holder->resultData; + auto result_bundle = result_holder->resultData.get(); if (result_bundle != nullptr) { on_result( *app_to_resume, @@ -335,7 +335,7 @@ static int32_t loader_main(TT_UNUSED void* parameter) { case LoaderMessageTypeAppStart: message.result.status_value.value = do_start_by_id( message.payload.start->id, - message.payload.start->bundle + message.payload.start->parameters ); if (message.api_lock != nullptr) { message.api_lock->set(TT_API_LOCK_EVENT); @@ -359,7 +359,7 @@ static int32_t loader_main(TT_UNUSED void* parameter) { // region AppManifest -static void loader_start(TT_UNUSED Service& service) { +static void loader_start(TT_UNUSED ServiceContext& service) { tt_check(loader_singleton == nullptr); loader_singleton = loader_alloc(); @@ -367,7 +367,7 @@ static void loader_start(TT_UNUSED Service& service) { loader_singleton->thread->start(); } -static void loader_stop(TT_UNUSED Service& service) { +static void loader_stop(TT_UNUSED ServiceContext& service) { tt_check(loader_singleton != nullptr); // Send stop signal to thread and wait for thread to finish @@ -383,7 +383,7 @@ static void loader_stop(TT_UNUSED Service& service) { loader_singleton = nullptr; } -extern const Manifest manifest = { +extern const ServiceManifest manifest = { .id = "Loader", .onStart = &loader_start, .onStop = &loader_stop diff --git a/Tactility/Source/service/loader/Loader.h b/Tactility/Source/service/loader/Loader.h index ffb4b9e6..f3303c01 100644 --- a/Tactility/Source/service/loader/Loader.h +++ b/Tactility/Source/service/loader/Loader.h @@ -1,9 +1,10 @@ #pragma once -#include "app/Manifest.h" +#include "app/AppManifest.h" #include "Bundle.h" #include "Pubsub.h" -#include "service/Manifest.h" +#include "service/ServiceManifest.h" +#include namespace tt::service::loader { @@ -21,17 +22,17 @@ typedef enum { * @brief Start an app * @param[in] id application name or id * @param[in] blocking whether this call is blocking or not. You cannot call this from an LVGL thread. - * @param[in] arguments optional parameters to pass onto the application + * @param[in] parameters optional parameters to pass onto the application * @return LoaderStatus */ -LoaderStatus startApp(const std::string& id, bool blocking = false, const Bundle& arguments = Bundle()); +LoaderStatus startApp(const std::string& id, bool blocking = false, std::shared_ptr _Nullable parameters = nullptr); /** * @brief Stop the currently showing app. Show the previous app if any app was still running. */ void stopApp(); -app::App* _Nullable getCurrentApp(); +app::AppContext* _Nullable getCurrentApp(); /** * @brief PubSub for LoaderEvent diff --git a/Tactility/Source/service/screenshot/Screenshot.cpp b/Tactility/Source/service/screenshot/Screenshot.cpp index 4b4e5bde..5c13e7b0 100644 --- a/Tactility/Source/service/screenshot/Screenshot.cpp +++ b/Tactility/Source/service/screenshot/Screenshot.cpp @@ -3,7 +3,7 @@ #include "Mutex.h" #include "ScreenshotTask.h" -#include "service/Service.h" +#include "service/ServiceContext.h" #include "service/ServiceRegistry.h" #include "TactilityCore.h" @@ -11,7 +11,7 @@ namespace tt::service::screenshot { #define TAG "screenshot_service" -extern const Manifest manifest; +extern const ServiceManifest manifest; typedef struct { Mutex* mutex; @@ -41,12 +41,12 @@ static void service_data_unlock(ServiceData* data) { tt_check(tt_mutex_release(data->mutex) == TtStatusOk); } -static void on_start(Service& service) { +static void on_start(ServiceContext& service) { ServiceData* data = service_data_alloc(); service.setData(data); } -static void on_stop(Service& service) { +static void on_stop(ServiceContext& service) { auto* data = static_cast(service.getData()); if (data->task) { task::free(data->task); @@ -95,7 +95,7 @@ void startTimed(const char* path, uint8_t delay_in_seconds, uint8_t amount) { } void stop() { - _Nullable Service* service = findServiceById(manifest.id); + _Nullable ServiceContext* service = findServiceById(manifest.id); if (service == nullptr) { TT_LOG_E(TAG, "Service not found"); return; @@ -132,7 +132,7 @@ bool isStarted() { return getMode() != ScreenshotModeNone; } -extern const Manifest manifest = { +extern const ServiceManifest manifest = { .id = "Screenshot", .onStart = &on_start, .onStop = &on_stop diff --git a/Tactility/Source/service/screenshot/ScreenshotTask.cpp b/Tactility/Source/service/screenshot/ScreenshotTask.cpp index 1e4a2d8d..28fba052 100644 --- a/Tactility/Source/service/screenshot/ScreenshotTask.cpp +++ b/Tactility/Source/service/screenshot/ScreenshotTask.cpp @@ -1,7 +1,7 @@ #include "ScreenshotTask.h" #include "lv_screenshot.h" -#include "app/App.h" +#include "app/AppContext.h" #include "Mutex.h" #include "TactilityCore.h" #include "Thread.h" @@ -98,9 +98,9 @@ static int32_t screenshot_task(void* context) { break; // Interrupted loop } } else if (data->work.type == TASK_WORK_TYPE_APPS) { - app::App* _Nullable app = loader::getCurrentApp(); + app::AppContext* _Nullable app = loader::getCurrentApp(); if (app) { - const app::Manifest& manifest = app->getManifest(); + const app::AppManifest& manifest = app->getManifest(); if (manifest.id != last_app_id) { delay_ms(100); last_app_id = manifest.id; diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index cff1477e..f214a75f 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -2,7 +2,7 @@ #include "hal/Power.h" #include "hal/sdcard/Sdcard.h" #include "Mutex.h" -#include "service/Service.h" +#include "service/ServiceContext.h" #include "service/wifi/Wifi.h" #include "Tactility.h" #include "lvgl/Statusbar.h" @@ -184,7 +184,7 @@ int32_t service_main(TT_UNUSED void* parameter) { return 0; } -static void on_start(Service& service) { +static void on_start(ServiceContext& service) { ServiceData* data = service_data_alloc(); service.setData(data); @@ -195,7 +195,7 @@ static void on_start(Service& service) { data->thread->start(); } -static void on_stop(Service& service) { +static void on_stop(ServiceContext& service) { auto* data = static_cast(service.getData()); // Stop thread @@ -208,7 +208,7 @@ static void on_stop(Service& service) { service_data_free(data); } -extern const Manifest manifest = { +extern const ServiceManifest manifest = { .id = "Statusbar", .onStart = &on_start, .onStop = &on_stop diff --git a/TactilityHeadless/Private/service/ServiceInstance.h b/TactilityHeadless/Private/service/ServiceInstance.h new file mode 100644 index 00000000..ccd7aec7 --- /dev/null +++ b/TactilityHeadless/Private/service/ServiceInstance.h @@ -0,0 +1,25 @@ +#pragma once + +#include "service/ServiceContext.h" + +namespace tt::service { + +class ServiceInstance : public ServiceContext { + +private: + + Mutex mutex = Mutex(MutexTypeNormal); + const service::ServiceManifest& manifest; + void* data = nullptr; + +public: + + explicit ServiceInstance(const service::ServiceManifest& manifest); + ~ServiceInstance() override = default; + + [[nodiscard]] const service::ServiceManifest& getManifest() const override; + [[nodiscard]] void* getData() const override; + void setData(void* newData) override; +}; + +} diff --git a/TactilityHeadless/Source/TactilityHeadless.cpp b/TactilityHeadless/Source/TactilityHeadless.cpp index f5b17734..93f6db13 100644 --- a/TactilityHeadless/Source/TactilityHeadless.cpp +++ b/TactilityHeadless/Source/TactilityHeadless.cpp @@ -1,7 +1,7 @@ #include "TactilityHeadless.h" #include "hal/Configuration.h" #include "hal/Hal_i.h" -#include "service/Manifest.h" +#include "service/ServiceManifest.h" #include "service/ServiceRegistry.h" #ifdef ESP_PLATFORM @@ -12,10 +12,10 @@ namespace tt { #define TAG "tactility" -namespace service::wifi { extern const Manifest manifest; } -namespace service::sdcard { extern const Manifest manifest; } +namespace service::wifi { extern const ServiceManifest manifest; } +namespace service::sdcard { extern const ServiceManifest manifest; } -static const service::Manifest* const system_services[] = { +static const service::ServiceManifest* const system_services[] = { &service::sdcard::manifest, &service::wifi::manifest }; @@ -24,7 +24,7 @@ static const hal::Configuration* hardwareConfig = nullptr; static void register_and_start_system_services() { TT_LOG_I(TAG, "Registering and starting system services"); - int app_count = sizeof(system_services) / sizeof(service::Manifest*); + int app_count = sizeof(system_services) / sizeof(service::ServiceManifest*); for (int i = 0; i < app_count; ++i) { addService(system_services[i]); tt_check(service::startService(system_services[i]->id)); diff --git a/TactilityHeadless/Source/service/Service.cpp b/TactilityHeadless/Source/service/Service.cpp deleted file mode 100644 index 43568393..00000000 --- a/TactilityHeadless/Source/service/Service.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "Service.h" -#include "Manifest.h" - -namespace tt::service { - -Service::Service(const service::Manifest& manifest) : manifest(manifest) {} - -const service::Manifest& Service::getManifest() const { return manifest; } - -void* Service::getData() const { - mutex.acquire(TtWaitForever); - void* data_copy = data; - mutex.release(); - return data_copy; -} - -void Service::setData(void* newData) { - mutex.acquire(TtWaitForever); - data = newData; - mutex.release(); -} - -} // namespace diff --git a/TactilityHeadless/Source/service/Service.h b/TactilityHeadless/Source/service/Service.h deleted file mode 100644 index 8a6acbc4..00000000 --- a/TactilityHeadless/Source/service/Service.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "Mutex.h" -#include "Manifest.h" - -namespace tt::service { - -class Service { -private: - Mutex mutex = Mutex(MutexTypeNormal); - const service::Manifest& manifest; - void* data = nullptr; - -public: - Service(const service::Manifest& manifest); - - [[nodiscard]] const service::Manifest& getManifest() const; - [[nodiscard]] void* getData() const; - void setData(void* newData); -}; - -} // namespace diff --git a/TactilityHeadless/Source/service/ServiceContext.h b/TactilityHeadless/Source/service/ServiceContext.h new file mode 100644 index 00000000..8a9c8d4e --- /dev/null +++ b/TactilityHeadless/Source/service/ServiceContext.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Mutex.h" +#include "ServiceManifest.h" + +namespace tt::service { + +class ServiceContext { + +protected: + + virtual ~ServiceContext() = default; + +public: + + [[nodiscard]] virtual const service::ServiceManifest& getManifest() const = 0; + [[nodiscard]] virtual void* getData() const = 0; + virtual void setData(void* newData) = 0; +}; + +} // namespace diff --git a/TactilityHeadless/Source/service/ServiceInstance.cpp b/TactilityHeadless/Source/service/ServiceInstance.cpp new file mode 100644 index 00000000..ce22ad32 --- /dev/null +++ b/TactilityHeadless/Source/service/ServiceInstance.cpp @@ -0,0 +1,22 @@ +#include "service/ServiceInstance.h" + +namespace tt::service { + +ServiceInstance::ServiceInstance(const service::ServiceManifest&manifest) : manifest(manifest) {} + +const service::ServiceManifest& ServiceInstance::getManifest() const { return manifest; } + +void* ServiceInstance::getData() const { + mutex.acquire(TtWaitForever); + void* data_copy = data; + mutex.release(); + return data_copy; +} + +void ServiceInstance::setData(void* newData) { + mutex.acquire(TtWaitForever); + data = newData; + mutex.release(); +} + +} \ No newline at end of file diff --git a/TactilityHeadless/Source/service/Manifest.h b/TactilityHeadless/Source/service/ServiceManifest.h similarity index 72% rename from TactilityHeadless/Source/service/Manifest.h rename to TactilityHeadless/Source/service/ServiceManifest.h index 29da10fe..3ce32465 100644 --- a/TactilityHeadless/Source/service/Manifest.h +++ b/TactilityHeadless/Source/service/ServiceManifest.h @@ -4,12 +4,12 @@ namespace tt::service { -class Service; +class ServiceContext; -typedef void (*ServiceOnStart)(Service& service); -typedef void (*ServiceOnStop)(Service& service); +typedef void (*ServiceOnStart)(ServiceContext& service); +typedef void (*ServiceOnStop)(ServiceContext& service); -typedef struct Manifest { +struct ServiceManifest { /** * The identifier by which the app is launched by the system and other apps. */ @@ -25,6 +25,6 @@ typedef struct Manifest { */ const ServiceOnStop onStop = nullptr; -} Manifest; +}; } // namespace diff --git a/TactilityHeadless/Source/service/ServiceRegistry.cpp b/TactilityHeadless/Source/service/ServiceRegistry.cpp index fb9850a2..1fb0fbda 100644 --- a/TactilityHeadless/Source/service/ServiceRegistry.cpp +++ b/TactilityHeadless/Source/service/ServiceRegistry.cpp @@ -1,8 +1,8 @@ #include "ServiceRegistry.h" #include "Mutex.h" -#include "Service.h" -#include "Manifest.h" +#include "service/ServiceInstance.h" +#include "service/ServiceManifest.h" #include "TactilityCore.h" #include #include @@ -11,8 +11,8 @@ namespace tt::service { #define TAG "service_registry" -typedef std::unordered_map ManifestMap; -typedef std::unordered_map ServiceInstanceMap; +typedef std::unordered_map ManifestMap; +typedef std::unordered_map ServiceInstanceMap; static ManifestMap service_manifest_map; static ServiceInstanceMap service_instance_map; @@ -20,57 +20,57 @@ static ServiceInstanceMap service_instance_map; static Mutex manifest_mutex(MutexTypeNormal); static Mutex instance_mutex(MutexTypeNormal); -void addService(const Manifest* manifest) { - TT_LOG_I(TAG, "adding %s", manifest->id.c_str()); +void addService(const ServiceManifest* manifest) { + TT_LOG_I(TAG, "Adding %s", manifest->id.c_str()); manifest_mutex.acquire(TtWaitForever); service_manifest_map[manifest->id] = manifest; manifest_mutex.release(); } -const Manifest* _Nullable findManifestId(const std::string& id) { +const ServiceManifest* _Nullable findManifestId(const std::string& id) { manifest_mutex.acquire(TtWaitForever); auto iterator = service_manifest_map.find(id); - _Nullable const Manifest * manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr; + _Nullable const ServiceManifest * manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr; manifest_mutex.release(); return manifest; } -static Service* _Nullable service_registry_find_instance_by_id(const std::string& id) { +static ServiceInstance* _Nullable service_registry_find_instance_by_id(const std::string& id) { manifest_mutex.acquire(TtWaitForever); auto iterator = service_instance_map.find(id); - _Nullable Service* service = iterator != service_instance_map.end() ? iterator->second : nullptr; + _Nullable ServiceInstance* service = iterator != service_instance_map.end() ? iterator->second : nullptr; manifest_mutex.release(); return service; } -// TODO: return proper error/status instead of BOOL +// TODO: Return proper error/status instead of BOOL? bool startService(const std::string& id) { - TT_LOG_I(TAG, "starting %s", id.c_str()); - const Manifest* manifest = findManifestId(id); + TT_LOG_I(TAG, "Starting %s", id.c_str()); + const ServiceManifest* manifest = findManifestId(id); if (manifest == nullptr) { TT_LOG_E(TAG, "manifest not found for service %s", id.c_str()); return false; } - auto* service = new Service(*manifest); + auto* service = new ServiceInstance(*manifest); manifest->onStart(*service); instance_mutex.acquire(TtWaitForever); service_instance_map[manifest->id] = service; instance_mutex.release(); - TT_LOG_I(TAG, "started %s", id.c_str()); + TT_LOG_I(TAG, "Started %s", id.c_str()); return true; } -_Nullable Service* findServiceById(const std::string& service_id) { - return (Service*)service_registry_find_instance_by_id(service_id); +_Nullable ServiceContext* findServiceById(const std::string& service_id) { + return static_cast(service_registry_find_instance_by_id(service_id)); } bool stopService(const std::string& id) { - TT_LOG_I(TAG, "stopping %s", id.c_str()); - Service* service = service_registry_find_instance_by_id(id); + TT_LOG_I(TAG, "Stopping %s", id.c_str()); + ServiceInstance* service = service_registry_find_instance_by_id(id); if (service == nullptr) { TT_LOG_W(TAG, "service not running: %s", id.c_str()); return false; @@ -83,7 +83,7 @@ bool stopService(const std::string& id) { service_instance_map.erase(id); instance_mutex.release(); - TT_LOG_I(TAG, "stopped %s", id.c_str()); + TT_LOG_I(TAG, "Stopped %s", id.c_str()); return true; } diff --git a/TactilityHeadless/Source/service/ServiceRegistry.h b/TactilityHeadless/Source/service/ServiceRegistry.h index 888b47fd..366f13db 100644 --- a/TactilityHeadless/Source/service/ServiceRegistry.h +++ b/TactilityHeadless/Source/service/ServiceRegistry.h @@ -1,20 +1,20 @@ #pragma once -#include "Manifest.h" +#include "service/ServiceManifest.h" namespace tt::service { -typedef void (*ManifestCallback)(const Manifest*, void* context); +typedef void (*ManifestCallback)(const ServiceManifest*, void* context); void initRegistry(); -void addService(const Manifest* manifest); -void removeService(const Manifest* manifest); +void addService(const ServiceManifest* manifest); +void removeService(const ServiceManifest* manifest); bool startService(const std::string& id); bool stopService(const std::string& id); -const Manifest* _Nullable findManifestId(const std::string& id); -Service* _Nullable findServiceById(const std::string& id); +const ServiceManifest* _Nullable findManifestId(const std::string& id); +ServiceContext* _Nullable findServiceById(const std::string& id); } // namespace diff --git a/TactilityHeadless/Source/service/sdcard/Sdcard.cpp b/TactilityHeadless/Source/service/sdcard/Sdcard.cpp index 87c5610e..826c4f0a 100644 --- a/TactilityHeadless/Source/service/sdcard/Sdcard.cpp +++ b/TactilityHeadless/Source/service/sdcard/Sdcard.cpp @@ -1,7 +1,7 @@ #include #include "Mutex.h" -#include "service/Service.h" +#include "service/ServiceContext.h" #include "TactilityCore.h" #include "TactilityHeadless.h" @@ -75,7 +75,7 @@ static int32_t sdcard_task(void* context) { return 0; } -static void on_start(Service& service) { +static void on_start(ServiceContext& service) { if (hal::getConfiguration().sdcard != nullptr) { ServiceData* data = service_data_alloc(); service.setData(data); @@ -85,7 +85,7 @@ static void on_start(Service& service) { } } -static void on_stop(Service& service) { +static void on_stop(ServiceContext& service) { auto* data = static_cast(service.getData()); if (data != nullptr) { service_data_lock(data); @@ -98,7 +98,7 @@ static void on_stop(Service& service) { } } -extern const Manifest manifest = { +extern const ServiceManifest manifest = { .id = "sdcard", .onStart = &on_start, .onStop = &on_stop diff --git a/TactilityHeadless/Source/service/wifi/WifiEsp.cpp b/TactilityHeadless/Source/service/wifi/WifiEsp.cpp index 4dc5da23..264578dc 100644 --- a/TactilityHeadless/Source/service/wifi/WifiEsp.cpp +++ b/TactilityHeadless/Source/service/wifi/WifiEsp.cpp @@ -6,10 +6,9 @@ #include "Mutex.h" #include "Check.h" #include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" #include "Log.h" #include "Pubsub.h" -#include "service/Service.h" +#include "service/ServiceContext.h" #include "WifiSettings.h" #include #include @@ -790,13 +789,13 @@ _Noreturn int32_t wifi_main(TT_UNUSED void* parameter) { } } -static void service_start(Service& service) { +static void service_start(ServiceContext& service) { tt_assert(wifi_singleton == nullptr); wifi_singleton = new Wifi(); service.setData(wifi_singleton); } -static void service_stop(Service& service) { +static void service_stop(ServiceContext& service) { tt_assert(wifi_singleton != nullptr); WifiRadioState state = wifi_singleton->radio_state; @@ -812,7 +811,7 @@ static void service_stop(Service& service) { tt_crash("not fully implemented"); } -extern const Manifest manifest = { +extern const ServiceManifest manifest = { .id = "Wifi", .onStart = &service_start, .onStop = &service_stop diff --git a/TactilityHeadless/Source/service/wifi/WifiMock.cpp b/TactilityHeadless/Source/service/wifi/WifiMock.cpp index 5d6f80a6..96d5f0d2 100644 --- a/TactilityHeadless/Source/service/wifi/WifiMock.cpp +++ b/TactilityHeadless/Source/service/wifi/WifiMock.cpp @@ -7,7 +7,7 @@ #include "MessageQueue.h" #include "Mutex.h" #include "Pubsub.h" -#include "service/Service.h" +#include "service/ServiceContext.h" #include #include @@ -175,12 +175,12 @@ static void unlock(Wifi* wifi) { } -static void service_start(TT_UNUSED Service& service) { +static void service_start(TT_UNUSED ServiceContext& service) { tt_check(wifi == nullptr); wifi = wifi_alloc(); } -static void service_stop(TT_UNUSED Service& service) { +static void service_stop(TT_UNUSED ServiceContext& service) { tt_check(wifi != nullptr); wifi_free(wifi); @@ -191,7 +191,7 @@ void wifi_main(void*) { // NO-OP } -extern const Manifest manifest = { +extern const ServiceManifest manifest = { .id = "Wifi", .onStart = &service_start, .onStop = &service_stop