From f15154392f5c15436625684f46add493b0278606 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 24 Aug 2026 01:18:48 +0200 Subject: [PATCH 01/10] Refactored events --- .claude/rules/app-framework.md | 2 +- Documentation/ideas.md | 5 +- Modules/app-module/include/app/event.h | 45 +++-- Modules/app-module/source/event.cpp | 65 +++--- Modules/app-module/source/symbols.cpp | 11 +- .../tests/source/app_event_test.cpp | 64 ++++-- .../tests/source/app_manager_test.cpp | 38 +++- .../tests/source/combined_event_test.cpp | 72 +++++++ Platforms/platform-esp32/CMakeLists.txt | 2 +- .../source/drivers/esp32_wifi.cpp | 158 +++++++++------ .../source/drivers/esp32_wifi_pinned.cpp | 52 ++++- .../source/drivers/mock_wifi.cpp | 147 +++++++++----- .../Include/Tactility/service/wifi/Wifi.h | 6 - Tactility/Source/Tactility.cpp | 1 - Tactility/Source/app/addgps/AddGps.cpp | 32 +-- .../Source/app/alertdialog/AlertDialog.cpp | 20 +- .../Source/app/appdetails/AppDetails.cpp | 42 ++-- Tactility/Source/app/apphub/AppHubApp.cpp | 32 +-- .../app/apphubdetails/AppHubDetailsApp.cpp | 46 +++-- Tactility/Source/app/applist/AppList.cpp | 20 +- .../Source/app/appsettings/AppSettings.cpp | 26 ++- .../Source/app/apwebserver/ApWebServer.cpp | 26 ++- .../app/audiosettings/AudioSettings.cpp | 26 ++- Tactility/Source/app/boot/Boot.cpp | 20 +- Tactility/Source/app/btmanage/BtManage.cpp | 26 ++- .../app/btpeersettings/BtPeerSettings.cpp | 50 ++--- Tactility/Source/app/chat/ChatApp.cpp | 26 ++- .../app/crashdiagnostics/CrashDiagnostics.cpp | 26 ++- .../Source/app/development/Development.cpp | 26 ++- Tactility/Source/app/files/FilesApp.cpp | 34 ++-- .../app/fileselection/FileSelection.cpp | 20 +- .../Source/app/gpssettings/GpsSettings.cpp | 64 +++--- .../app/grovesettings/GroveSettings.cpp | 26 ++- .../Source/app/i2cscanner/I2cScanner.cpp | 28 +-- .../Source/app/imageviewer/ImageViewer.cpp | 26 ++- .../Source/app/inputdialog/InputDialog.cpp | 20 +- .../app/kerneldisplay/KernelDisplay.cpp | 28 +-- .../Source/app/keyboard/KeyboardSettings.cpp | 28 +-- Tactility/Source/app/launcher/Launcher.cpp | 20 +- .../app/localesettings/LocaleSettings.cpp | 26 ++- Tactility/Source/app/notes/Notes.cpp | 64 +++--- Tactility/Source/app/power/Power.cpp | 26 ++- Tactility/Source/app/poweroff/PowerOff.cpp | 14 +- .../Source/app/screenshot/Screenshot.cpp | 26 ++- .../app/selectiondialog/SelectionDialog.cpp | 20 +- Tactility/Source/app/settings/Settings.cpp | 20 +- Tactility/Source/app/setup/Setup.cpp | 42 ++-- .../Source/app/systeminfo/SystemInfo.cpp | 26 ++- .../app/timedatesettings/TimeDateSettings.cpp | 52 ++--- Tactility/Source/app/timezone/TimeZone.cpp | 20 +- .../app/touchcalibration/TouchCalibration.cpp | 28 +-- .../app/trackball/TrackballSettings.cpp | 28 +-- .../Source/app/usbsettings/UsbSettings.cpp | 26 ++- .../webserversettings/WebServerSettings.cpp | 26 ++- .../app/wifiapsettings/WifiApSettings.cpp | 187 +++++++++++------- .../Source/app/wificonnect/WifiConnect.cpp | 85 +++++--- .../Source/app/wifimanage/WifiManage.cpp | 105 +++++++--- .../service/espnow/EspNowHostedTransport.cpp | 10 +- Tactility/Source/service/wifi/Wifi.cpp | 136 ++++++++----- .../tactility/concurrent/task_event_group.h | 93 +++++++++ .../include/tactility/drivers/wifi.h | 121 ++++++++++-- .../include/tactility/system_event.h | 98 ++++----- .../source/concurrent/task_event_group.cpp | 76 +++++++ TactilityKernel/source/drivers/wifi.cpp | 45 +++-- TactilityKernel/source/symbols.c | 17 +- TactilityKernel/source/system_event.cpp | 147 +++----------- .../tests/source/system_event_test.cpp | 144 ++++++++------ .../tests/source/task_event_group_test.cpp | 42 ++++ Tests/SdkIntegration/main/Source/main.c | 18 +- 69 files changed, 2054 insertions(+), 1120 deletions(-) create mode 100644 Modules/app-module/tests/source/combined_event_test.cpp create mode 100644 TactilityKernel/include/tactility/concurrent/task_event_group.h create mode 100644 TactilityKernel/source/concurrent/task_event_group.cpp create mode 100644 TactilityKernel/tests/source/task_event_group_test.cpp diff --git a/.claude/rules/app-framework.md b/.claude/rules/app-framework.md index cd66dc27e..551fb9d16 100644 --- a/.claude/rules/app-framework.md +++ b/.claude/rules/app-framework.md @@ -4,7 +4,7 @@ Apps are event-driven, C API (`app-module`, ``), not a C++ class. Each Lifecycle and inter-app communication go through `app_manager_*()` (`app/manager.h`) and `app_event_*()` (`app/event.h`): - `app_manager_start()`/`app_manager_start_with_parameters()` launch a plain instance; `app_manager_start_for_result()` launches a modal child that reports back to a parent instance. -- An app subscribes with `app_event_subscribe()`/`app_event_await()` and reacts to `APP_EVENT_CLOSE` (terminate now) and `APP_EVENT_RESULT` (a child it started reported back). +- An app subscribes with `app_event_subscribe()`, blocks via `task_event_group_wait()`/`task_event_group_wait_any()`, and drains with `app_event_poll()`, reacting to `APP_EVENT_CLOSE` (terminate now) and `APP_EVENT_RESULT` (a child it started reported back). - An app closes itself by calling `app_manager_finish()` right before returning from `main()`; another instance is closed via `app_manager_stop()`. Apps are registered at startup via `app_manager_add()`. External apps can be loaded from SD card via `manifest.properties` files, or side-loaded as ELF binaries on ESP32 (see `app/loader.h`'s `AppLoaderApi`). diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 75151dbd9..f88418540 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -11,11 +11,10 @@ ## Higher Priority -- AppEventSubscription, SystemEventSubscription: should use TaskHandle_t notification. That way, there can be a single wait event for a task instead of X separate ones with each their timeout. +- Warn if file operations are done from prohibited tasks (e.g. lvgl task) - AppHubApp: Prevent download callbacks from accessing a destroyed view. - Move USB host task stacks to SPIRAM when available: esp32_usbhost*.cpp -- wifi: wifi_add_event_callback() and wifi_remove_event_callback() should be replaced by a subscribe/await pattern like system events. - When that's changed reduce LVGL callstack size in Tactility.cpp run() +- Get rid of WiFi service (Wifi.cpp/h) in Tactility.cpp - Make it more clear to end-users that an SD card is required to run Tactility - Make it possible to override stack size for an app via config file (loaded at boot), and make it possible to set preferred memory location (e.g. internal/external) - Wrap file operations like fopen/fclose with file_mutex diff --git a/Modules/app-module/include/app/event.h b/Modules/app-module/include/app/event.h index e5af8f119..2edd857e4 100644 --- a/Modules/app-module/include/app/event.h +++ b/Modules/app-module/include/app/event.h @@ -6,15 +6,14 @@ #include #include +#include #include -#include -#include #ifdef __cplusplus extern "C" { #endif -/** Identifies the kind of app-lifecycle event delivered through app_event_await(). */ +/** Identifies the kind of app-lifecycle event delivered through app_event_poll(). */ enum AppEventType { APP_EVENT_RESULT, // struct AppResultEventData APP_EVENT_CLOSE, // no data - terminate now, permanently @@ -51,20 +50,28 @@ struct AppEvent { * Caller-owned subscription node. Unlike TactilityKernel's system_event poll subscription * (which coalesces to the latest value), this queues events by value (FIFO) since dropping an * APP_EVENT_RESULT would be unacceptable. - * @warning Fields other than `app_instance_id` are for internal use only; do not read or write - * them directly. + * @warning Fields other than `app_instance_id` and `bit` are for internal use only; do not read + * or write them directly. */ struct AppEventSubscription { /** The app instance this subscription receives events for; set by the caller before app_event_subscribe(). */ AppInstanceId app_instance_id; - TaskHandle_t task; + /** Set by app_event_subscribe(). Read-only for the caller: OR it into a + * task_event_group_wait() mask (alongside other subscriptions sharing the same + * `event_group`) to block on this subscription and other event sources with one call. */ + uint32_t bit; - struct AppEvent queue[APP_EVENT_QUEUE_CAPACITY]; - uint8_t head; - uint8_t count; + struct { + /** Caller-owned, borrowed; set by app_event_subscribe(). */ + struct TaskEventGroup* event_group; - struct AppEventSubscription* next; + struct AppEvent queue[APP_EVENT_QUEUE_CAPACITY]; + uint8_t head; + uint8_t count; + + struct AppEventSubscription* next; + } internal; }; /** @@ -72,9 +79,14 @@ struct AppEventSubscription { * @warning Does not work in ISR context. * @param[in,out] sub subscription to register; caller sets @a sub->app_instance_id beforehand, * owns the storage, and must keep it alive (and stationary) until unsubscribed - * @return ERROR_NONE on success + * @param[in] event_group caller-owned group to wait on; must outlive @a sub (i.e. be + * destructed only after app_event_unsubscribe()). To block for an event, call + * task_event_group_wait()/task_event_group_wait_any() on this group (OR sub->bit into the mask, + * or use _wait_any() to include every subscription sharing it), then drain with app_event_poll(). + * @retval ERROR_NONE on success + * @retval ERROR_RESOURCE @a event_group has no free bits left to claim */ -error_t app_event_subscribe(struct AppEventSubscription* sub); +error_t app_event_subscribe(struct AppEventSubscription* sub, struct TaskEventGroup* event_group); /** * Remove a previously registered subscription. @@ -94,11 +106,14 @@ error_t app_event_unsubscribe(struct AppEventSubscription* sub); error_t app_event_emit(AppInstanceId app_instance_id, const struct AppEvent* event); /** - * Pop the next event for @a sub, blocking up to @a timeout if the queue is currently empty. + * Non-blocking: pop the next event for @a sub if one is already queued. + * @warning Never blocks. To wait for an event, block in task_event_group_wait()/ + * task_event_group_wait_any() on @a sub's event group first (see app_event_subscribe()), then + * drain with this in a loop. * @retval ERROR_NONE @a out_event was filled - * @retval ERROR_TIMEOUT no event arrived before the timeout elapsed + * @retval ERROR_TIMEOUT nothing queued right now */ -error_t app_event_await(struct AppEventSubscription* sub, struct AppEvent* out_event, TickType_t timeout); +error_t app_event_poll(struct AppEventSubscription* sub, struct AppEvent* out_event); #ifdef __cplusplus } diff --git a/Modules/app-module/source/event.cpp b/Modules/app-module/source/event.cpp index da24885c0..e5c963c4f 100644 --- a/Modules/app-module/source/event.cpp +++ b/Modules/app-module/source/event.cpp @@ -7,7 +7,7 @@ /** * Intrusive singly-linked list of subscriptions, keyed by app_instance_id. * Guarded by a single coarse-grained mutex, notifying a subscriber here never invokes caller code - * (just a struct copy and an xTaskNotifyGive), so there is no reentrancy concern requiring a snapshot-then-unlock dance. + * (just a struct copy and a task_event_group_signal), so there is no reentrancy concern requiring a snapshot-then-unlock dance. */ static AppEventSubscription* subscriptions = nullptr; @@ -21,13 +21,20 @@ static AppEventMutex subscriptions_mutex; extern "C" { -error_t app_event_subscribe(AppEventSubscription* sub) { - sub->task = xTaskGetCurrentTaskHandle(); - sub->head = 0; - sub->count = 0; +error_t app_event_subscribe(AppEventSubscription* sub, TaskEventGroup* event_group) { + uint32_t bit; + error_t claim_result = task_event_group_claim_bit(event_group, &bit); + if (claim_result != ERROR_NONE) { + return claim_result; + } + + sub->bit = bit; + sub->internal.event_group = event_group; + sub->internal.head = 0; + sub->internal.count = 0; mutex_lock(&subscriptions_mutex.handle); - sub->next = subscriptions; + sub->internal.next = subscriptions; subscriptions = sub; mutex_unlock(&subscriptions_mutex.handle); @@ -38,15 +45,19 @@ error_t app_event_unsubscribe(AppEventSubscription* sub) { error_t result = ERROR_NOT_FOUND; mutex_lock(&subscriptions_mutex.handle); - for (AppEventSubscription** link = &subscriptions; *link != nullptr; link = &(*link)->next) { + for (AppEventSubscription** link = &subscriptions; *link != nullptr; link = &(*link)->internal.next) { if (*link == sub) { - *link = sub->next; + *link = sub->internal.next; result = ERROR_NONE; break; } } mutex_unlock(&subscriptions_mutex.handle); + if (result == ERROR_NONE) { + task_event_group_release_bit(sub->internal.event_group, sub->bit); + } + return result; } @@ -57,23 +68,23 @@ error_t app_event_emit(AppInstanceId app_instance_id, const AppEvent* event) { error_t result = ERROR_NOT_FOUND; mutex_lock(&subscriptions_mutex.handle); - for (AppEventSubscription* sub = subscriptions; sub != nullptr; sub = sub->next) { + for (AppEventSubscription* sub = subscriptions; sub != nullptr; sub = sub->internal.next) { if (sub->app_instance_id != app_instance_id) { continue; } - if (sub->count >= APP_EVENT_QUEUE_CAPACITY) { + if (sub->internal.count >= APP_EVENT_QUEUE_CAPACITY) { result = ERROR_RESOURCE; continue; } - uint8_t tail = (sub->head + sub->count) % APP_EVENT_QUEUE_CAPACITY; - sub->queue[tail] = stamped_event; - sub->count++; + uint8_t tail = (sub->internal.head + sub->internal.count) % APP_EVENT_QUEUE_CAPACITY; + sub->internal.queue[tail] = stamped_event; + sub->internal.count++; if (result != ERROR_RESOURCE) { result = ERROR_NONE; } - xTaskNotifyGive(sub->task); + task_event_group_signal(sub->internal.event_group, sub->bit); } mutex_unlock(&subscriptions_mutex.handle); @@ -82,33 +93,17 @@ error_t app_event_emit(AppInstanceId app_instance_id, const AppEvent* event) { static bool try_pop(AppEventSubscription* sub, AppEvent* out_event) { mutex_lock(&subscriptions_mutex.handle); - bool has_event = sub->count > 0; + bool has_event = sub->internal.count > 0; if (has_event) { - *out_event = sub->queue[sub->head]; - sub->head = (sub->head + 1) % APP_EVENT_QUEUE_CAPACITY; - sub->count--; + *out_event = sub->internal.queue[sub->internal.head]; + sub->internal.head = (sub->internal.head + 1) % APP_EVENT_QUEUE_CAPACITY; + sub->internal.count--; } mutex_unlock(&subscriptions_mutex.handle); return has_event; } -error_t app_event_await(AppEventSubscription* sub, AppEvent* out_event, TickType_t timeout) { - if (try_pop(sub, out_event)) { - // Drain any notification credit this (or an earlier) push accumulated on this task's - // FreeRTOS notification value: each app_event_emit() calls xTaskNotifyGive() regardless - // of whether the consumer takes this fast path or the blocking path below, so without - // this the credit would carry over and cause a future ulTaskNotifyTake() below to - // return immediately for a notification that was already accounted for here. - ulTaskNotifyTake(pdTRUE, 0); - return ERROR_NONE; - } - - if (ulTaskNotifyTake(pdTRUE, timeout) == 0) { - return ERROR_TIMEOUT; - } - - // Single-consumer by design (one task per subscription), so a wakeup implies the event - // this call was notified for is still there for us to pop. +error_t app_event_poll(AppEventSubscription* sub, AppEvent* out_event) { return try_pop(sub, out_event) ? ERROR_NONE : ERROR_TIMEOUT; } diff --git a/Modules/app-module/source/symbols.cpp b/Modules/app-module/source/symbols.cpp index 0a7311255..9c79b5cb4 100644 --- a/Modules/app-module/source/symbols.cpp +++ b/Modules/app-module/source/symbols.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -20,7 +21,15 @@ const ModuleSymbol app_module_symbols[] = { DEFINE_MODULE_SYMBOL(app_event_subscribe), DEFINE_MODULE_SYMBOL(app_event_unsubscribe), DEFINE_MODULE_SYMBOL(app_event_emit), - DEFINE_MODULE_SYMBOL(app_event_await), + DEFINE_MODULE_SYMBOL(app_event_poll), + // tactility/concurrent/task_event_group - needed to construct the group app_event_subscribe() takes + DEFINE_MODULE_SYMBOL(task_event_group_construct), + DEFINE_MODULE_SYMBOL(task_event_group_destruct), + DEFINE_MODULE_SYMBOL(task_event_group_claim_bit), + DEFINE_MODULE_SYMBOL(task_event_group_release_bit), + DEFINE_MODULE_SYMBOL(task_event_group_signal), + DEFINE_MODULE_SYMBOL(task_event_group_wait), + DEFINE_MODULE_SYMBOL(task_event_group_wait_any), // app/install DEFINE_MODULE_SYMBOL(app_get_install_path), DEFINE_MODULE_SYMBOL(app_install), diff --git a/Modules/app-module/tests/source/app_event_test.cpp b/Modules/app-module/tests/source/app_event_test.cpp index e12a51296..4a6c06b08 100644 --- a/Modules/app-module/tests/source/app_event_test.cpp +++ b/Modules/app-module/tests/source/app_event_test.cpp @@ -6,10 +6,13 @@ #include #include -TEST_CASE("app_event_subscribe/_await deliver events in FIFO order") { +TEST_CASE("app_event_subscribe/_poll deliver events in FIFO order") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = 1; - CHECK_EQ(app_event_subscribe(&sub), ERROR_NONE); + CHECK_EQ(app_event_subscribe(&sub, &event_group), ERROR_NONE); for (uint32_t i = 0; i < 3; i++) { AppEvent event { .type = APP_EVENT_RESULT, .timestamp = 0, .result = { .launch_id = i, .result = 0 } }; @@ -18,32 +21,40 @@ TEST_CASE("app_event_subscribe/_await deliver events in FIFO order") { for (uint32_t i = 0; i < 3; i++) { AppEvent out {}; - CHECK_EQ(app_event_await(&sub, &out, 0), ERROR_NONE); + CHECK_EQ(app_event_poll(&sub, &out), ERROR_NONE); CHECK_EQ(out.type, APP_EVENT_RESULT); CHECK_EQ(out.result.launch_id, i); } app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } TEST_CASE("app_event_emit only delivers to subscriptions for that app_instance_id") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = 10; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; CHECK_EQ(app_event_emit(11, &event), ERROR_NOT_FOUND); AppEvent out {}; - CHECK_EQ(app_event_await(&sub, &out, 0), ERROR_TIMEOUT); + CHECK_EQ(app_event_poll(&sub, &out), ERROR_TIMEOUT); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } TEST_CASE("app_event_emit returns ERROR_RESOURCE and drops the newest event once a subscription's queue is full") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = 20; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); for (uint32_t i = 0; i < APP_EVENT_QUEUE_CAPACITY; i++) { AppEvent event { .type = APP_EVENT_RESULT, .timestamp = 0, .result = { .launch_id = i, .result = 0 } }; @@ -57,42 +68,55 @@ TEST_CASE("app_event_emit returns ERROR_RESOURCE and drops the newest event once // The already-queued events survive, in order, and the dropped one never arrives. for (uint32_t i = 0; i < APP_EVENT_QUEUE_CAPACITY; i++) { AppEvent out {}; - CHECK_EQ(app_event_await(&sub, &out, 0), ERROR_NONE); + CHECK_EQ(app_event_poll(&sub, &out), ERROR_NONE); CHECK_EQ(out.result.launch_id, i); } AppEvent out {}; - CHECK_EQ(app_event_await(&sub, &out, 0), ERROR_TIMEOUT); + CHECK_EQ(app_event_poll(&sub, &out), ERROR_TIMEOUT); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } TEST_CASE("app_event_unsubscribe stops further delivery") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = 30; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); CHECK_EQ(app_event_unsubscribe(&sub), ERROR_NONE); CHECK_EQ(app_event_unsubscribe(&sub), ERROR_NOT_FOUND); AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; CHECK_EQ(app_event_emit(30, &event), ERROR_NOT_FOUND); + + task_event_group_destruct(&event_group); } -TEST_CASE("app_event_await times out when no event has arrived") { +TEST_CASE("app_event_poll times out when no event has arrived") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = 40; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); AppEvent out {}; - CHECK_EQ(app_event_await(&sub, &out, 0), ERROR_TIMEOUT); + CHECK_EQ(app_event_poll(&sub, &out), ERROR_TIMEOUT); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } TEST_CASE("app_event_emit stamps the event with the current boot-relative time") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = 50; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); auto before = static_cast(get_micros_since_boot()); AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; @@ -100,17 +124,21 @@ TEST_CASE("app_event_emit stamps the event with the current boot-relative time") auto after = static_cast(get_micros_since_boot()); AppEvent out {}; - REQUIRE_EQ(app_event_await(&sub, &out, 0), ERROR_NONE); + REQUIRE_EQ(app_event_poll(&sub, &out), ERROR_NONE); CHECK_GE(out.timestamp, before); CHECK_LE(out.timestamp, after); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } -TEST_CASE("app_event_await wakes when the event is emitted from another task") { +TEST_CASE("task_event_group_wait wakes when the event is emitted from another task") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = 60; - CHECK_EQ(app_event_subscribe(&sub), ERROR_NONE); + CHECK_EQ(app_event_subscribe(&sub, &event_group), ERROR_NONE); auto* thread = thread_alloc_full( "app-event-emitter", @@ -126,12 +154,14 @@ TEST_CASE("app_event_await wakes when the event is emitted from another task") { ); CHECK_EQ(thread_start(thread), ERROR_NONE); + CHECK_EQ(task_event_group_wait(&event_group, sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE); AppEvent out {}; - CHECK_EQ(app_event_await(&sub, &out, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(app_event_poll(&sub, &out), ERROR_NONE); CHECK_EQ(out.type, APP_EVENT_CLOSE); CHECK_EQ(thread_join(thread, pdMS_TO_TICKS(2000), 1), ERROR_NONE); thread_free(thread); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } diff --git a/Modules/app-module/tests/source/app_manager_test.cpp b/Modules/app-module/tests/source/app_manager_test.cpp index 101a10481..e67f9d299 100644 --- a/Modules/app-module/tests/source/app_manager_test.cpp +++ b/Modules/app-module/tests/source/app_manager_test.cpp @@ -66,21 +66,31 @@ int32_t fake_run(void*, uint32_t app_instance_id, int argc, char* argv[]) { return static_cast(strtol(argv[0], nullptr, 10)); } + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); while (true) { - AppEvent event {}; - if (app_event_await(&sub, &event, pdMS_TO_TICKS(5000)) != ERROR_NONE) { + if (task_event_group_wait_any(&event_group, nullptr, pdMS_TO_TICKS(5000)) != ERROR_NONE) { break; // safety net so a bug here can't hang the test suite } - if (event.type == APP_EVENT_CLOSE) { - break; + + bool done = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + done = true; + break; + } } + if (done) break; } app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } @@ -329,9 +339,12 @@ TEST_CASE("app_manager_start_for_result delivers APP_EVENT_RESULT to the parent, REQUIRE_EQ(app_manager_start("test.app.parent", &parent_id), ERROR_NONE); CHECK(wait_for_state(parent_id, APP_INSTANCE_STATE_ACTIVE, 1000)); + TaskEventGroup parent_event_group {}; + task_event_group_construct(&parent_event_group); + AppEventSubscription parent_sub {}; parent_sub.app_instance_id = parent_id; - REQUIRE_EQ(app_event_subscribe(&parent_sub), ERROR_NONE); + REQUIRE_EQ(app_event_subscribe(&parent_sub, &parent_event_group), ERROR_NONE); const char* argv[] = { "42" }; uint32_t child_id = 0; @@ -340,13 +353,15 @@ TEST_CASE("app_manager_start_for_result delivers APP_EVENT_RESULT to the parent, // Launching a modal child never touches the parent's own task/state. CHECK_EQ(app_manager_get_state(parent_id), APP_INSTANCE_STATE_ACTIVE); + REQUIRE_EQ(task_event_group_wait(&parent_event_group, parent_sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE); AppEvent event {}; - REQUIRE_EQ(app_event_await(&parent_sub, &event, pdMS_TO_TICKS(2000)), ERROR_NONE); + REQUIRE_EQ(app_event_poll(&parent_sub, &event), ERROR_NONE); CHECK_EQ(event.type, APP_EVENT_RESULT); CHECK_EQ(event.result.launch_id, child_id); CHECK_EQ(event.result.result, 42); app_event_unsubscribe(&parent_sub); + task_event_group_destruct(&parent_event_group); app_manager_stop(child_id); app_manager_stop(parent_id); app_manager_remove("test.app.parent"); @@ -365,9 +380,12 @@ TEST_CASE("app_manager_start_for_result delivers the child's own return value as REQUIRE_EQ(app_manager_start("test.app.parent2", &parent_id), ERROR_NONE); CHECK(wait_for_state(parent_id, APP_INSTANCE_STATE_ACTIVE, 1000)); + TaskEventGroup parent_event_group {}; + task_event_group_construct(&parent_event_group); + AppEventSubscription parent_sub {}; parent_sub.app_instance_id = parent_id; - REQUIRE_EQ(app_event_subscribe(&parent_sub), ERROR_NONE); + REQUIRE_EQ(app_event_subscribe(&parent_sub, &parent_event_group), ERROR_NONE); uint32_t child_id = 0; // No parameters - fake_run falls through to its normal CLOSE loop instead of acting as a @@ -377,13 +395,15 @@ TEST_CASE("app_manager_start_for_result delivers the child's own return value as app_manager_stop(child_id); // force-close + REQUIRE_EQ(task_event_group_wait(&parent_event_group, parent_sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE); AppEvent event {}; - REQUIRE_EQ(app_event_await(&parent_sub, &event, pdMS_TO_TICKS(2000)), ERROR_NONE); + REQUIRE_EQ(app_event_poll(&parent_sub, &event), ERROR_NONE); CHECK_EQ(event.type, APP_EVENT_RESULT); CHECK_EQ(event.result.launch_id, child_id); CHECK_EQ(event.result.result, 0); // fake_run's CLOSE loop always returns 0 app_event_unsubscribe(&parent_sub); + task_event_group_destruct(&parent_event_group); app_manager_stop(parent_id); app_manager_remove("test.app.parent2"); app_manager_remove("test.app.child2"); diff --git a/Modules/app-module/tests/source/combined_event_test.cpp b/Modules/app-module/tests/source/combined_event_test.cpp new file mode 100644 index 000000000..25031c5b4 --- /dev/null +++ b/Modules/app-module/tests/source/combined_event_test.cpp @@ -0,0 +1,72 @@ +#include "doctest.h" + +#include + +#include + +// Regression coverage for the primary motivation behind TaskEventGroup: a task subscribed to +// both an app_event and a system_event must be able to block once and wake for either, without +// losing an event or regressing either subsystem's own delivery semantics (FIFO for app_event, +// coalescing for system_event). + +TEST_CASE("a task can wait on app_event and system_event together via one TaskEventGroup") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription app_sub {}; + app_sub.app_instance_id = 100; + CHECK_EQ(app_event_subscribe(&app_sub, &event_group), ERROR_NONE); + + SystemEventSubscription sys_sub {}; + sys_sub.event.type = KERNEL_EVENT_BOOT_COMPLETED; + CHECK_EQ(system_event_subscribe(&sys_sub, &event_group), ERROR_NONE); + + // Distinct bits, so a combined wait can tell (via out_flags) which source(s) fired. + CHECK_NE(app_sub.bit, sys_sub.bit); + + uint32_t both_bits = app_sub.bit | sys_sub.bit; + + // Only app_event fires: combined wait matches just that bit, and only app_event_poll() + // finds something to pop. + AppEvent emitted { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; + CHECK_EQ(app_event_emit(100, &emitted), ERROR_NONE); + + uint32_t out_flags = 0; + CHECK_EQ(task_event_group_wait(&event_group, both_bits, false, &out_flags, 0), ERROR_NONE); + CHECK_EQ(out_flags, app_sub.bit); + + AppEvent app_out {}; + CHECK_EQ(app_event_poll(&app_sub, &app_out), ERROR_NONE); + CHECK_EQ(app_out.type, APP_EVENT_CLOSE); + CHECK_EQ(system_event_poll(&sys_sub), ERROR_TIMEOUT); + + // Only system_event fires: combined wait matches just that bit, and only + // system_event_poll() finds something pending. + CHECK_EQ(system_event_emit(KERNEL_EVENT_BOOT_COMPLETED, nullptr, 0), ERROR_NONE); + + out_flags = 0; + CHECK_EQ(task_event_group_wait(&event_group, both_bits, false, &out_flags, 0), ERROR_NONE); + CHECK_EQ(out_flags, sys_sub.bit); + + CHECK_EQ(system_event_poll(&sys_sub), ERROR_NONE); + AppEvent app_out2 {}; + CHECK_EQ(app_event_poll(&app_sub, &app_out2), ERROR_TIMEOUT); + + // Both fire before the wait: combined wait matches both bits, and both subsystems' own + // polls (which re-check their own state rather than trusting the bit) still deliver. + CHECK_EQ(app_event_emit(100, &emitted), ERROR_NONE); + CHECK_EQ(system_event_emit(KERNEL_EVENT_BOOT_COMPLETED, nullptr, 0), ERROR_NONE); + + out_flags = 0; + CHECK_EQ(task_event_group_wait(&event_group, both_bits, false, &out_flags, 0), ERROR_NONE); + CHECK_EQ(out_flags, both_bits); + + CHECK_EQ(app_event_poll(&app_sub, &app_out), ERROR_NONE); + CHECK_EQ(system_event_poll(&sys_sub), ERROR_NONE); + + // Unsubscribe order (system_event before app_event) must not matter - the group outlives + // both and is destructed last. + system_event_unsubscribe(&sys_sub); + app_event_unsubscribe(&app_sub); + task_event_group_destruct(&event_group); +} diff --git a/Platforms/platform-esp32/CMakeLists.txt b/Platforms/platform-esp32/CMakeLists.txt index 419e0801a..4295d9c58 100644 --- a/Platforms/platform-esp32/CMakeLists.txt +++ b/Platforms/platform-esp32/CMakeLists.txt @@ -14,7 +14,7 @@ idf_component_register( SRCS ${SOURCES} INCLUDE_DIRS "include/" PRIV_INCLUDE_DIRS "private/" - REQUIRES TactilityKernel driver esp_adc esp_driver_i2c esp_lcd vfs fatfs esp_wifi esp_netif esp_event + REQUIRES TactilityKernel TactilityKernelCpp driver esp_adc esp_driver_i2c esp_lcd vfs fatfs esp_wifi esp_netif esp_event PRIV_REQUIRES ${PRIV_REQUIRES_LIST} ) diff --git a/Platforms/platform-esp32/source/drivers/esp32_wifi.cpp b/Platforms/platform-esp32/source/drivers/esp32_wifi.cpp index 3f9f22035..805cfff65 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_wifi.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_wifi.cpp @@ -18,6 +18,8 @@ #include #include +#include + #if defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED) #include #endif @@ -33,14 +35,8 @@ namespace { -constexpr size_t WIFI_MAX_CALLBACKS = 4; constexpr uint16_t WIFI_SCAN_RECORD_LIMIT = 32; -struct WifiCallbackEntry { - WifiEventCallback fn = nullptr; - void* ctx = nullptr; -}; - struct Esp32WifiCtx { Device* device = nullptr; @@ -66,9 +62,8 @@ struct Esp32WifiCtx { int32_t lastEventId = -1; TickType_t lastEventTick = 0; - Mutex callbackMutex{}; - WifiCallbackEntry callbacks[WIFI_MAX_CALLBACKS] = {}; - size_t callbackCount = 0; + Mutex subscriptionsMutex{}; + WifiEventSubscription* subscriptions = nullptr; }; #define GET_CTX(device) (static_cast(device_get_driver_data(device))) @@ -93,16 +88,18 @@ WifiAuthenticationType to_wifi_authentication_type(wifi_auth_mode_t mode) { } void fire_event(Esp32WifiCtx* ctx, WifiEvent event) { - WifiCallbackEntry local[WIFI_MAX_CALLBACKS]; - size_t count; - mutex_lock(&ctx->callbackMutex); - count = ctx->callbackCount; - memcpy(local, ctx->callbacks, count * sizeof(WifiCallbackEntry)); - mutex_unlock(&ctx->callbackMutex); - - for (size_t i = 0; i < count; i++) { - local[i].fn(ctx->device, local[i].ctx, event); + mutex_lock(&ctx->subscriptionsMutex); + for (WifiEventSubscription* sub = ctx->subscriptions; sub != nullptr; sub = sub->internal.next) { + mutex_lock(&sub->internal.ring_mutex); + if (sub->internal.count < WIFI_EVENT_QUEUE_CAPACITY) { + uint8_t tail = (sub->internal.head + sub->internal.count) % WIFI_EVENT_QUEUE_CAPACITY; + sub->internal.queue[tail] = event; + sub->internal.count++; + } + mutex_unlock(&sub->internal.ring_mutex); + task_event_group_signal(sub->internal.event_group, sub->bit); } + mutex_unlock(&ctx->subscriptionsMutex); } // ---- ESP-IDF event handling (runs on the esp_event task) ---- @@ -257,6 +254,11 @@ error_t bring_up_wifi(Esp32WifiCtx* ctx) { ctx->radioState = WIFI_RADIO_STATE_ON; mutex_unlock(&ctx->mutex); + WifiEvent radio_event = {}; + radio_event.type = WIFI_EVENT_TYPE_RADIO_STATE_CHANGED; + radio_event.radio_state = WIFI_RADIO_STATE_ON; + fire_event(ctx, radio_event); + LOG_I(TAG, "WiFi radio on"); return ERROR_NONE; } @@ -306,6 +308,11 @@ void bring_down_wifi(Esp32WifiCtx* ctx) { ctx->radioState = WIFI_RADIO_STATE_OFF; mutex_unlock(&ctx->mutex); + WifiEvent radio_event = {}; + radio_event.type = WIFI_EVENT_TYPE_RADIO_STATE_CHANGED; + radio_event.radio_state = WIFI_RADIO_STATE_OFF; + fire_event(ctx, radio_event); + LOG_I(TAG, "WiFi radio off"); } @@ -497,38 +504,69 @@ error_t api_station_get_rssi(Device* device, int32_t* rssi) { return ERROR_NONE; } -error_t api_add_event_callback(Device* device, void* callback_context, WifiEventCallback callback) { +error_t api_set_radio_on(Device* device) { + auto* ctx = GET_CTX(device); + if (ctx == nullptr) return ERROR_INVALID_STATE; + + mutex_lock(&ctx->mutex); + bool already_on = ctx->radioState == WIFI_RADIO_STATE_ON; + mutex_unlock(&ctx->mutex); + if (already_on) return ERROR_NONE; + + return bring_up_wifi(ctx); +} + +error_t api_set_radio_off(Device* device) { + auto* ctx = GET_CTX(device); + if (ctx == nullptr) return ERROR_INVALID_STATE; + + mutex_lock(&ctx->mutex); + bool already_off = ctx->radioState == WIFI_RADIO_STATE_OFF; + mutex_unlock(&ctx->mutex); + if (already_off) return ERROR_NONE; + + bring_down_wifi(ctx); + return ERROR_NONE; +} + +error_t api_event_subscribe(Device* device, WifiEventSubscription* sub, TaskEventGroup* event_group) { auto* ctx = GET_CTX(device); - if (ctx == nullptr || callback == nullptr) return ERROR_INVALID_ARGUMENT; + if (ctx == nullptr || sub == nullptr || event_group == nullptr) return ERROR_INVALID_ARGUMENT; - mutex_lock(&ctx->callbackMutex); - if (ctx->callbackCount >= WIFI_MAX_CALLBACKS) { - mutex_unlock(&ctx->callbackMutex); - return ERROR_OUT_OF_MEMORY; + uint32_t bit; + error_t claim_result = task_event_group_claim_bit(event_group, &bit); + if (claim_result != ERROR_NONE) { + return claim_result; } - ctx->callbacks[ctx->callbackCount] = { .fn = callback, .ctx = callback_context }; - ctx->callbackCount++; - mutex_unlock(&ctx->callbackMutex); + sub->internal.event_group = event_group; + sub->bit = bit; + + mutex_lock(&ctx->subscriptionsMutex); + sub->internal.next = ctx->subscriptions; + ctx->subscriptions = sub; + mutex_unlock(&ctx->subscriptionsMutex); return ERROR_NONE; } -error_t api_remove_event_callback(Device* device, WifiEventCallback callback) { +error_t api_event_unsubscribe(Device* device, WifiEventSubscription* sub) { auto* ctx = GET_CTX(device); - if (ctx == nullptr || callback == nullptr) return ERROR_INVALID_ARGUMENT; - - mutex_lock(&ctx->callbackMutex); - for (size_t i = 0; i < ctx->callbackCount; i++) { - if (ctx->callbacks[i].fn == callback) { - for (size_t j = i; j + 1 < ctx->callbackCount; j++) { - ctx->callbacks[j] = ctx->callbacks[j + 1]; - } - ctx->callbackCount--; - mutex_unlock(&ctx->callbackMutex); - return ERROR_NONE; + if (ctx == nullptr || sub == nullptr) return ERROR_INVALID_ARGUMENT; + + error_t result = ERROR_NOT_FOUND; + mutex_lock(&ctx->subscriptionsMutex); + for (WifiEventSubscription** link = &ctx->subscriptions; *link != nullptr; link = &(*link)->internal.next) { + if (*link == sub) { + *link = sub->internal.next; + result = ERROR_NONE; + break; } } - mutex_unlock(&ctx->callbackMutex); - return ERROR_NOT_FOUND; + mutex_unlock(&ctx->subscriptionsMutex); + + if (result == ERROR_NONE) { + task_event_group_release_bit(sub->internal.event_group, sub->bit); + } + return result; } error_t api_get_firmware_ops(Device* /*device*/, const FirmwareOps** ops, void** ctx) { @@ -550,6 +588,8 @@ error_t api_get_firmware_ops(Device* /*device*/, const FirmwareOps** ops, void** } const WifiApi esp32_wifi_api = { + .set_radio_on = api_set_radio_on, + .set_radio_off = api_set_radio_off, .get_radio_state = api_get_radio_state, .get_station_state = api_get_station_state, .get_access_point_state = api_get_access_point_state, @@ -561,34 +601,23 @@ const WifiApi esp32_wifi_api = { .station_connect = api_station_connect, .station_disconnect = api_station_disconnect, .station_get_rssi = api_station_get_rssi, - .add_event_callback = api_add_event_callback, - .remove_event_callback = api_remove_event_callback, + .event_subscribe = api_event_subscribe, + .event_unsubscribe = api_event_unsubscribe, .get_firmware_ops = api_get_firmware_ops }; -// ---- Driver lifecycle ---- -// The ESP-IDF WiFi stack isn't touched until the device is actually started: -// registering this driver (module start()) only makes it available for -// binding, it doesn't spin up any resources. - error_t start_device(Device* device) { - auto* ctx = new(std::nothrow) Esp32WifiCtx(); - if (ctx == nullptr) return ERROR_OUT_OF_MEMORY; + // Prefers PSRAM/SPIRAM, falling back to internal RAM when unavailable - aborts on true OOM + // (see OptExternalAllocator's own doc), so no null check here. + tt::OptExternalAllocator allocator; + auto* ctx = allocator.allocate(1); + new (ctx) Esp32WifiCtx(); ctx->device = device; mutex_construct(&ctx->mutex); - mutex_construct(&ctx->callbackMutex); + mutex_construct(&ctx->subscriptionsMutex); device_set_driver_data(device, ctx); - error_t result = bring_up_wifi(ctx); - if (result != ERROR_NONE) { - device_set_driver_data(device, nullptr); - mutex_destruct(&ctx->callbackMutex); - mutex_destruct(&ctx->mutex); - delete ctx; - return result; - } - return ERROR_NONE; } @@ -596,12 +625,15 @@ error_t stop_device(Device* device) { auto* ctx = GET_CTX(device); if (ctx == nullptr) return ERROR_NONE; - bring_down_wifi(ctx); + if (ctx->radioState == WIFI_RADIO_STATE_ON) { + bring_down_wifi(ctx); + } device_set_driver_data(device, nullptr); - mutex_destruct(&ctx->callbackMutex); + mutex_destruct(&ctx->subscriptionsMutex); mutex_destruct(&ctx->mutex); - delete ctx; + ctx->~Esp32WifiCtx(); + tt::OptExternalAllocator().deallocate(ctx, 1); return ERROR_NONE; } diff --git a/Platforms/platform-esp32/source/drivers/esp32_wifi_pinned.cpp b/Platforms/platform-esp32/source/drivers/esp32_wifi_pinned.cpp index f83c86691..340c55910 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_wifi_pinned.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_wifi_pinned.cpp @@ -41,6 +41,12 @@ struct Esp32WifiPinnedCtx { }; #define GET_CTX(device) (static_cast(device_get_driver_data(device))) +// Only for event_subscribe()/event_unsubscribe(): unlike the rest of WifiApi, have +// construct/destruct side effects on sub->ring_mutex at the wifi_event_subscribe()/ +// wifi_event_unsubscribe() kernel-wrapper layer (see wifi.cpp) - calling that wrapper AGAIN here +// (instead of the child driver's raw vtable entry) double-runs those side effects, corrupting the +// mutex. Every other WifiApi call below is a plain pass-through. +#define CHILD_WIFI_API(child) ((const struct WifiApi*)device_get_driver(child)->api) struct MarshalledCall { const std::function* work; @@ -176,20 +182,44 @@ error_t api_station_get_rssi(Device* device, int32_t* rssi) { return wifi_station_get_rssi(ctx->child, rssi); } -error_t api_add_event_callback(Device* device, void* callback_context, WifiEventCallback callback) { +error_t api_event_subscribe(Device* device, WifiEventSubscription* sub, TaskEventGroup* event_group) { auto* ctx = GET_CTX(device); if (ctx == nullptr || ctx->child == nullptr) return ERROR_INVALID_STATE; - return wifi_add_event_callback(ctx->child, callback_context, callback); + return CHILD_WIFI_API(ctx->child)->event_subscribe(ctx->child, sub, event_group); } -error_t api_remove_event_callback(Device* device, WifiEventCallback callback) { +error_t api_event_unsubscribe(Device* device, WifiEventSubscription* sub) { auto* ctx = GET_CTX(device); if (ctx == nullptr || ctx->child == nullptr) return ERROR_INVALID_STATE; - return wifi_remove_event_callback(ctx->child, callback); + return CHILD_WIFI_API(ctx->child)->event_unsubscribe(ctx->child, sub); } // ---- WifiApi: state-changing calls are marshalled onto the pinned WiFi task ---- +error_t api_set_radio_on(Device* device) { + auto* ctx = GET_CTX(device); + if (ctx == nullptr || ctx->child == nullptr) return ERROR_INVALID_STATE; + + error_t result = ERROR_NONE; + Device* child = ctx->child; + error_t err = run_on_pinned_thread(ctx, [child, &result]() { + result = wifi_set_radio_on(child); + }); + return err != ERROR_NONE ? err : result; +} + +error_t api_set_radio_off(Device* device) { + auto* ctx = GET_CTX(device); + if (ctx == nullptr || ctx->child == nullptr) return ERROR_INVALID_STATE; + + error_t result = ERROR_NONE; + Device* child = ctx->child; + error_t err = run_on_pinned_thread(ctx, [child, &result]() { + result = wifi_set_radio_off(child); + }); + return err != ERROR_NONE ? err : result; +} + error_t api_scan(Device* device) { auto* ctx = GET_CTX(device); if (ctx == nullptr || ctx->child == nullptr) return ERROR_INVALID_STATE; @@ -227,6 +257,8 @@ error_t api_station_disconnect(Device* device) { } const WifiApi esp32_wifi_pinned_api = { + .set_radio_on = api_set_radio_on, + .set_radio_off = api_set_radio_off, .get_radio_state = api_get_radio_state, .get_station_state = api_get_station_state, .get_access_point_state = api_get_access_point_state, @@ -238,15 +270,15 @@ const WifiApi esp32_wifi_pinned_api = { .station_connect = api_station_connect, .station_disconnect = api_station_disconnect, .station_get_rssi = api_station_get_rssi, - .add_event_callback = api_add_event_callback, - .remove_event_callback = api_remove_event_callback + .event_subscribe = api_event_subscribe, + .event_unsubscribe = api_event_unsubscribe }; // ---- Driver lifecycle ---- -// Starting/stopping the child (which brings the ESP-IDF WiFi stack up/down) -// also happens on the pinned thread, since esp_wifi_init/start/stop/deinit -// are subject to the same core-affinity requirement as the state-changing -// WifiApi calls above. +// Starting/stopping the child (allocating/freeing its bookkeeping - see esp32_wifi.cpp's own +// start_device()/stop_device() comment) happens on the pinned thread for the same core-affinity +// reason as the state-changing WifiApi calls above; enable()/disable() (which actually bring the +// ESP-IDF WiFi stack up/down) are marshalled the same way. error_t start_device(Device* device) { auto* ctx = new(std::nothrow) Esp32WifiPinnedCtx(); diff --git a/Platforms/platform-posix/source/drivers/mock_wifi.cpp b/Platforms/platform-posix/source/drivers/mock_wifi.cpp index 35855a8d6..321bc7ac3 100644 --- a/Platforms/platform-posix/source/drivers/mock_wifi.cpp +++ b/Platforms/platform-posix/source/drivers/mock_wifi.cpp @@ -19,8 +19,6 @@ namespace { -constexpr size_t WIFI_MAX_CALLBACKS = 4; - struct MockApRecord { const char* ssid; int8_t rssi; @@ -40,11 +38,6 @@ constexpr size_t MOCK_SCAN_RESULT_COUNT = sizeof(MOCK_SCAN_RESULTS) / sizeof(MOC constexpr int8_t MOCK_CONNECTED_RSSI = -30; constexpr const char* MOCK_IPV4_ADDRESS = "192.168.1.2"; -struct WifiCallbackEntry { - WifiEventCallback fn = nullptr; - void* ctx = nullptr; -}; - struct PosixWifiCtx { Device* device = nullptr; @@ -54,24 +47,25 @@ struct PosixWifiCtx { bool scanning = false; char targetSsid[33] = {}; - Mutex callbackMutex {}; - WifiCallbackEntry callbacks[WIFI_MAX_CALLBACKS] = {}; - size_t callbackCount = 0; + Mutex subscriptionsMutex {}; + WifiEventSubscription* subscriptions = nullptr; }; #define GET_CTX(device) (static_cast(device_get_driver_data(device))) void fireEvent(PosixWifiCtx* ctx, WifiEvent event) { - WifiCallbackEntry local[WIFI_MAX_CALLBACKS]; - size_t count; - mutex_lock(&ctx->callbackMutex); - count = ctx->callbackCount; - memcpy(local, ctx->callbacks, count * sizeof(WifiCallbackEntry)); - mutex_unlock(&ctx->callbackMutex); - - for (size_t i = 0; i < count; i++) { - local[i].fn(ctx->device, local[i].ctx, event); + mutex_lock(&ctx->subscriptionsMutex); + for (WifiEventSubscription* sub = ctx->subscriptions; sub != nullptr; sub = sub->next) { + mutex_lock(&sub->ring_mutex); + if (sub->count < WIFI_EVENT_QUEUE_CAPACITY) { + uint8_t tail = (sub->head + sub->count) % WIFI_EVENT_QUEUE_CAPACITY; + sub->queue[tail] = event; + sub->count++; + } + mutex_unlock(&sub->ring_mutex); + task_event_group_signal(sub->event_group, sub->bit); } + mutex_unlock(&ctx->subscriptionsMutex); } // ---- WifiApi ---- @@ -247,41 +241,89 @@ error_t apiStationGetRssi(Device* device, int32_t* rssi) { return ERROR_NONE; } -error_t apiAddEventCallback(Device* device, void* callback_context, WifiEventCallback callback) { +error_t apiSetRadioOn(Device* device) { + auto* ctx = GET_CTX(device); + if (ctx == nullptr) return ERROR_INVALID_STATE; + + mutex_lock(&ctx->mutex); + bool already_on = ctx->radioState == WIFI_RADIO_STATE_ON; + ctx->radioState = WIFI_RADIO_STATE_ON; + mutex_unlock(&ctx->mutex); + if (already_on) return ERROR_NONE; + + WifiEvent radio_event = {}; + radio_event.type = WIFI_EVENT_TYPE_RADIO_STATE_CHANGED; + radio_event.radio_state = WIFI_RADIO_STATE_ON; + fireEvent(ctx, radio_event); + + LOG_I(TAG, "WiFi radio on (mock)"); + return ERROR_NONE; +} + +error_t apiSetRadioOff(Device* device) { + auto* ctx = GET_CTX(device); + if (ctx == nullptr) return ERROR_INVALID_STATE; + + mutex_lock(&ctx->mutex); + bool already_off = ctx->radioState == WIFI_RADIO_STATE_OFF; + ctx->radioState = WIFI_RADIO_STATE_OFF; + ctx->stationState = WIFI_STATION_STATE_DISCONNECTED; + ctx->scanning = false; + mutex_unlock(&ctx->mutex); + if (already_off) return ERROR_NONE; + + WifiEvent radio_event = {}; + radio_event.type = WIFI_EVENT_TYPE_RADIO_STATE_CHANGED; + radio_event.radio_state = WIFI_RADIO_STATE_OFF; + fireEvent(ctx, radio_event); + + LOG_I(TAG, "WiFi radio off (mock)"); + return ERROR_NONE; +} + +error_t apiEventSubscribe(Device* device, WifiEventSubscription* sub, TaskEventGroup* event_group) { auto* ctx = GET_CTX(device); - if (ctx == nullptr || callback == nullptr) return ERROR_INVALID_ARGUMENT; + if (ctx == nullptr || sub == nullptr || event_group == nullptr) return ERROR_INVALID_ARGUMENT; - mutex_lock(&ctx->callbackMutex); - if (ctx->callbackCount >= WIFI_MAX_CALLBACKS) { - mutex_unlock(&ctx->callbackMutex); - return ERROR_OUT_OF_MEMORY; + uint32_t bit; + error_t claim_result = task_event_group_claim_bit(event_group, &bit); + if (claim_result != ERROR_NONE) { + return claim_result; } - ctx->callbacks[ctx->callbackCount] = { .fn = callback, .ctx = callback_context }; - ctx->callbackCount++; - mutex_unlock(&ctx->callbackMutex); + sub->event_group = event_group; + sub->bit = bit; + + mutex_lock(&ctx->subscriptionsMutex); + sub->next = ctx->subscriptions; + ctx->subscriptions = sub; + mutex_unlock(&ctx->subscriptionsMutex); return ERROR_NONE; } -error_t apiRemoveEventCallback(Device* device, WifiEventCallback callback) { +error_t apiEventUnsubscribe(Device* device, WifiEventSubscription* sub) { auto* ctx = GET_CTX(device); - if (ctx == nullptr || callback == nullptr) return ERROR_INVALID_ARGUMENT; - - mutex_lock(&ctx->callbackMutex); - for (size_t i = 0; i < ctx->callbackCount; i++) { - if (ctx->callbacks[i].fn == callback) { - for (size_t j = i; j + 1 < ctx->callbackCount; j++) { - ctx->callbacks[j] = ctx->callbacks[j + 1]; - } - ctx->callbackCount--; - mutex_unlock(&ctx->callbackMutex); - return ERROR_NONE; + if (ctx == nullptr || sub == nullptr) return ERROR_INVALID_ARGUMENT; + + error_t result = ERROR_NOT_FOUND; + mutex_lock(&ctx->subscriptionsMutex); + for (WifiEventSubscription** link = &ctx->subscriptions; *link != nullptr; link = &(*link)->next) { + if (*link == sub) { + *link = sub->next; + result = ERROR_NONE; + break; } } - mutex_unlock(&ctx->callbackMutex); - return ERROR_NOT_FOUND; + mutex_unlock(&ctx->subscriptionsMutex); + + if (result == ERROR_NONE) { + task_event_group_release_bit(sub->event_group, sub->bit); + } + return result; } const WifiApi posix_wifi_api = { + .set_radio_on = apiSetRadioOn, + .set_radio_off = apiSetRadioOff, .get_radio_state = apiGetRadioState, .get_station_state = apiGetStationState, .get_access_point_state = apiGetAccessPointState, @@ -293,13 +335,14 @@ const WifiApi posix_wifi_api = { .station_connect = apiStationConnect, .station_disconnect = apiStationDisconnect, .station_get_rssi = apiStationGetRssi, - .add_event_callback = apiAddEventCallback, - .remove_event_callback = apiRemoveEventCallback + .event_subscribe = apiEventSubscribe, + .event_unsubscribe = apiEventUnsubscribe }; // ---- Driver lifecycle ---- -// Unlike the real esp32 driver, there's no hardware to bring up: the radio -// simply reports itself as ON as soon as the device is started. +// startDevice()/stopDevice() only allocate/free this driver's bookkeeping (subscriber list +// included), so event subscribers can stay subscribed across radio on/off toggles - the radio +// itself is only touched by apiSetRadioOn()/apiSetRadioOff(). error_t startDevice(Device* device) { auto* ctx = new(std::nothrow) PosixWifiCtx(); @@ -307,12 +350,9 @@ error_t startDevice(Device* device) { ctx->device = device; mutex_construct(&ctx->mutex); - mutex_construct(&ctx->callbackMutex); - ctx->radioState = WIFI_RADIO_STATE_ON; + mutex_construct(&ctx->subscriptionsMutex); device_set_driver_data(device, ctx); - - LOG_I(TAG, "WiFi radio on (mock)"); return ERROR_NONE; } @@ -320,12 +360,15 @@ error_t stopDevice(Device* device) { auto* ctx = GET_CTX(device); if (ctx == nullptr) return ERROR_NONE; + if (ctx->radioState == WIFI_RADIO_STATE_ON) { + apiSetRadioOff(device); + } + device_set_driver_data(device, nullptr); - mutex_destruct(&ctx->callbackMutex); + mutex_destruct(&ctx->subscriptionsMutex); mutex_destruct(&ctx->mutex); delete ctx; - LOG_I(TAG, "WiFi radio off (mock)"); return ERROR_NONE; } diff --git a/Tactility/Include/Tactility/service/wifi/Wifi.h b/Tactility/Include/Tactility/service/wifi/Wifi.h index a51e4f72f..5a23a8af5 100644 --- a/Tactility/Include/Tactility/service/wifi/Wifi.h +++ b/Tactility/Include/Tactility/service/wifi/Wifi.h @@ -26,12 +26,6 @@ enum class RadioState { Off, }; -/** - * @brief Get wifi pubsub that broadcasts Event objects - * @return PubSub - */ -std::shared_ptr> getPubsub(); - /** @return Get the current radio state */ RadioState getRadioState(); diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 7d8041ac0..3fcb52a0f 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -519,7 +519,6 @@ void run(Module* const dtsModules[], const DtsDevice dtsDevices[]) { .on_start = onLvglStarted, .on_stop = onLvglStopped, .task_priority = THREAD_PRIORITY_HIGHER, - // TODO: Remove Wi-Fi driver callback mechanism and use subscribe/await from wifi app to be able to reduce callstack .task_stack_size = 9120, #ifdef ESP_PLATFORM .task_affinity = getCpuAffinityConfiguration().graphics diff --git a/Tactility/Source/app/addgps/AddGps.cpp b/Tactility/Source/app/addgps/AddGps.cpp index 1f1aa5462..152c9c5c0 100644 --- a/Tactility/Source/app/addgps/AddGps.cpp +++ b/Tactility/Source/app/addgps/AddGps.cpp @@ -203,32 +203,38 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - app_manager_stop(event.result.launch_id); - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/alertdialog/AlertDialog.cpp b/Tactility/Source/app/alertdialog/AlertDialog.cpp index ce9b8a3a3..b9722b8d1 100644 --- a/Tactility/Source/app/alertdialog/AlertDialog.cpp +++ b/Tactility/Source/app/alertdialog/AlertDialog.cpp @@ -102,24 +102,32 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.argc = argc; ctx.argv = argv; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/appdetails/AppDetails.cpp b/Tactility/Source/app/appdetails/AppDetails.cpp index 562ae8433..bf78088b1 100644 --- a/Tactility/Source/app/appdetails/AppDetails.cpp +++ b/Tactility/Source/app/appdetails/AppDetails.cpp @@ -114,38 +114,44 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { return 0; } + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - if (event.result.launch_id == ctx.pendingUninstallDialogId) { - if (event.result.result == 0) { // 0 = Yes - app_uninstall(ctx.targetManifest.id); - shouldClose = true; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + if (event.result.launch_id == ctx.pendingUninstallDialogId) { + if (event.result.result == 0) { // 0 = Yes + app_uninstall(ctx.targetManifest.id); + shouldClose = true; + } + app_manager_stop(event.result.launch_id); } - app_manager_stop(event.result.launch_id); - } - break; - default: - break; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/apphub/AppHubApp.cpp b/Tactility/Source/app/apphub/AppHubApp.cpp index d3cf6c6e1..23e3e4095 100644 --- a/Tactility/Source/app/apphub/AppHubApp.cpp +++ b/Tactility/Source/app/apphub/AppHubApp.cpp @@ -49,7 +49,7 @@ void refresh(Context* ctx); void onBackPressed(lv_event_t* event) { auto* ctx = static_cast(lv_event_get_user_data(event)); - AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; + AppEvent closeEvent {.type = APP_EVENT_CLOSE, .timestamp = 0, .result = {}}; app_event_emit(ctx->appInstanceId, &closeEvent); } @@ -201,29 +201,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } @@ -234,7 +240,7 @@ extern const ::AppManifest manifest = { .id = "tactility.apphub", .name = "App Hub", .category = APP_CATEGORY_SYSTEM, - .location = { APP_LOCATION_MEMORY, reinterpret_cast(appMain) } + .location = {APP_LOCATION_MEMORY, reinterpret_cast(appMain)} }; -} // namespace +} // namespace tt::app::apphub diff --git a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp index 4cb0bf823..d959421aa 100644 --- a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp +++ b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp @@ -230,41 +230,47 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } } + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: { - bool confirmed = event.result.result == CONFIRMATION_BUTTON_INDEX; - if (event.result.launch_id == ctx.installDialogId && confirmed) { - installApp(&ctx); - } else if (event.result.launch_id == ctx.uninstallDialogId && confirmed) { - uninstallApp(&ctx); - } else if (event.result.launch_id == ctx.updateDialogId && confirmed) { - updateApp(&ctx); + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: { + bool confirmed = event.result.result == CONFIRMATION_BUTTON_INDEX; + if (event.result.launch_id == ctx.installDialogId && confirmed) { + installApp(&ctx); + } else if (event.result.launch_id == ctx.uninstallDialogId && confirmed) { + uninstallApp(&ctx); + } else if (event.result.launch_id == ctx.updateDialogId && confirmed) { + updateApp(&ctx); + } + app_manager_stop(event.result.launch_id); + break; } - app_manager_stop(event.result.launch_id); - break; + default: + break; } - default: - break; + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/applist/AppList.cpp b/Tactility/Source/app/applist/AppList.cpp index 119992651..36d07c76e 100644 --- a/Tactility/Source/app/applist/AppList.cpp +++ b/Tactility/Source/app/applist/AppList.cpp @@ -81,24 +81,32 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { appListInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/appsettings/AppSettings.cpp b/Tactility/Source/app/appsettings/AppSettings.cpp index 6a57b3cbc..d2395d336 100644 --- a/Tactility/Source/app/appsettings/AppSettings.cpp +++ b/Tactility/Source/app/appsettings/AppSettings.cpp @@ -90,29 +90,35 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { appSettingsInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/apwebserver/ApWebServer.cpp b/Tactility/Source/app/apwebserver/ApWebServer.cpp index d50c11c5d..9ece43edc 100644 --- a/Tactility/Source/app/apwebserver/ApWebServer.cpp +++ b/Tactility/Source/app/apwebserver/ApWebServer.cpp @@ -126,24 +126,29 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.appInstanceId = appInstanceId; ctx.wsSettings = settings::webserver::loadOrGetDefault(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } @@ -168,6 +173,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/audiosettings/AudioSettings.cpp b/Tactility/Source/app/audiosettings/AudioSettings.cpp index 4bab6d63c..dd6219bfb 100644 --- a/Tactility/Source/app/audiosettings/AudioSettings.cpp +++ b/Tactility/Source/app/audiosettings/AudioSettings.cpp @@ -215,24 +215,29 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } @@ -243,6 +248,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index 4b5631494..eb7c8dc53 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -292,9 +292,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { isUsbBootSplash = hal::usb::isUsbBootMode(); sdCardMissing = false; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); bootWindowId = window_manager_create(appInstanceId, createSplashWidgets, nullptr); @@ -304,19 +307,24 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { // startNextApp() above is what triggers that, via app-module's "save the previously active // app" policy, unless sdCardMissing halted before it. while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } if (bootWindowId != 0) { window_manager_remove(bootWindowId); } app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/btmanage/BtManage.cpp b/Tactility/Source/app/btmanage/BtManage.cpp index f561038ea..4b7d7b298 100644 --- a/Tactility/Source/app/btmanage/BtManage.cpp +++ b/Tactility/Source/app/btmanage/BtManage.cpp @@ -220,9 +220,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.state.updateScanResults(); ctx.state.updatePairedPeers(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -243,16 +246,18 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } @@ -271,6 +276,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp b/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp index 24a47449c..934440f68 100644 --- a/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp +++ b/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp @@ -199,38 +199,43 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { device_put(btDevice); } + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - if (event.result.result == 0) { // 0 = Yes - if (isCurrentlyConnected(&ctx)) { - if (ctx.profileId == BT_PROFILE_HID_HOST) { - bluetooth::hidHostDisconnect(); - } else { - bluetooth::disconnect(ctx.addr, ctx.profileId); + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + if (event.result.result == 0) { // 0 = Yes + if (isCurrentlyConnected(&ctx)) { + if (ctx.profileId == BT_PROFILE_HID_HOST) { + bluetooth::hidHostDisconnect(); + } else { + bluetooth::disconnect(ctx.addr, ctx.profileId); + } } + bluetooth::unpair(ctx.addr); + shouldClose = true; } - bluetooth::unpair(ctx.addr); - shouldClose = true; - } - app_manager_stop(event.result.launch_id); - break; - default: - break; + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (shouldClose) break; } } @@ -241,6 +246,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/chat/ChatApp.cpp b/Tactility/Source/app/chat/ChatApp.cpp index 9c1800654..170b53f5b 100644 --- a/Tactility/Source/app/chat/ChatApp.cpp +++ b/Tactility/Source/app/chat/ChatApp.cpp @@ -185,29 +185,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); service::espnow::unsubscribeReceiver(ctx.receiveSubscription); disableEspNow(&ctx); diff --git a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp index 0e681b4a8..5ef050dbc 100644 --- a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp +++ b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp @@ -153,31 +153,37 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); if (!ctx.hasFatalError) { bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); bool continuePressed = ctx.continuePressed; diff --git a/Tactility/Source/app/development/Development.cpp b/Tactility/Source/app/development/Development.cpp index 892988eec..86076fd3d 100644 --- a/Tactility/Source/app/development/Development.cpp +++ b/Tactility/Source/app/development/Development.cpp @@ -174,9 +174,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { return 0; } + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -197,16 +200,18 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } @@ -217,6 +222,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/files/FilesApp.cpp b/Tactility/Source/app/files/FilesApp.cpp index defc2cf61..90cb022f7 100644 --- a/Tactility/Source/app/files/FilesApp.cpp +++ b/Tactility/Source/app/files/FilesApp.cpp @@ -30,34 +30,40 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { View view(state); CreateContext createContext { &view, appInstanceId }; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &createContext); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - view.onResult(event.result.launch_id, event.result.result); - app_manager_stop(event.result.launch_id); - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + view.onResult(event.result.launch_id, event.result.result); + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (shouldClose) break; } } view.deinit(); window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/fileselection/FileSelection.cpp b/Tactility/Source/app/fileselection/FileSelection.cpp index 7c1e5796b..3baba0c46 100644 --- a/Tactility/Source/app/fileselection/FileSelection.cpp +++ b/Tactility/Source/app/fileselection/FileSelection.cpp @@ -62,24 +62,32 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { app_event_emit(appInstanceId, &closeEvent); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/gpssettings/GpsSettings.cpp b/Tactility/Source/app/gpssettings/GpsSettings.cpp index 863346fda..47f0bae51 100644 --- a/Tactility/Source/app/gpssettings/GpsSettings.cpp +++ b/Tactility/Source/app/gpssettings/GpsSettings.cpp @@ -267,51 +267,57 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { updateDeviceStates(&ctx); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.timer->start(); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - if (ctx.hasPendingDelete) { - ctx.hasPendingDelete = false; - if (event.result.result == 0) { // 0 = Yes - lvgl_lock(); - std::erase_if(ctx.deviceRows, [&ctx](const DeviceRow& row) { - return row.device == ctx.pendingDeleteDevice; - }); - lvgl_unlock(); - - gps_settings_remove_configuration_at(ctx.pendingDeleteIndex); - ctx.pendingDeleteDevice = nullptr; - - lvgl_lock(); - rebuildDeviceList(&ctx); - lvgl_unlock(); + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + if (ctx.hasPendingDelete) { + ctx.hasPendingDelete = false; + if (event.result.result == 0) { // 0 = Yes + lvgl_lock(); + std::erase_if(ctx.deviceRows, [&ctx](const DeviceRow& row) { + return row.device == ctx.pendingDeleteDevice; + }); + lvgl_unlock(); + + gps_settings_remove_configuration_at(ctx.pendingDeleteIndex); + ctx.pendingDeleteDevice = nullptr; + + lvgl_lock(); + rebuildDeviceList(&ctx); + lvgl_unlock(); + } } - } - app_manager_stop(event.result.launch_id); - break; - default: - break; + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (shouldClose) break; } } ctx.timer->stop(); window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/grovesettings/GroveSettings.cpp b/Tactility/Source/app/grovesettings/GroveSettings.cpp index 5d04a7593..25e54f227 100644 --- a/Tactility/Source/app/grovesettings/GroveSettings.cpp +++ b/Tactility/Source/app/grovesettings/GroveSettings.cpp @@ -93,29 +93,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index af2116e6d..c4a18e93f 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -388,30 +388,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - stopScanningIfRunning(&ctx); - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + stopScanningIfRunning(&ctx); + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/imageviewer/ImageViewer.cpp b/Tactility/Source/app/imageviewer/ImageViewer.cpp index e69a48be7..175fca478 100644 --- a/Tactility/Source/app/imageviewer/ImageViewer.cpp +++ b/Tactility/Source/app/imageviewer/ImageViewer.cpp @@ -89,29 +89,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.appInstanceId = appInstanceId; ctx.filePath = argv[0]; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/inputdialog/InputDialog.cpp b/Tactility/Source/app/inputdialog/InputDialog.cpp index ae5a9ba90..877f44027 100644 --- a/Tactility/Source/app/inputdialog/InputDialog.cpp +++ b/Tactility/Source/app/inputdialog/InputDialog.cpp @@ -111,24 +111,32 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.argc = argc; ctx.argv = argv; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/kerneldisplay/KernelDisplay.cpp b/Tactility/Source/app/kerneldisplay/KernelDisplay.cpp index e3e8f3662..24a56c701 100644 --- a/Tactility/Source/app/kerneldisplay/KernelDisplay.cpp +++ b/Tactility/Source/app/kerneldisplay/KernelDisplay.cpp @@ -308,30 +308,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - persistIfUpdated(ctx); - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + persistIfUpdated(ctx); + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/keyboard/KeyboardSettings.cpp b/Tactility/Source/app/keyboard/KeyboardSettings.cpp index 5f57f5c18..08ce740cd 100644 --- a/Tactility/Source/app/keyboard/KeyboardSettings.cpp +++ b/Tactility/Source/app/keyboard/KeyboardSettings.cpp @@ -209,30 +209,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - persistIfUpdated(ctx); - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + persistIfUpdated(ctx); + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/launcher/Launcher.cpp b/Tactility/Source/app/launcher/Launcher.cpp index 4e4a9f3c4..cb82386c6 100644 --- a/Tactility/Source/app/launcher/Launcher.cpp +++ b/Tactility/Source/app/launcher/Launcher.cpp @@ -234,26 +234,34 @@ void runAutoStart() { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { runAutoStart(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); // The launcher is meant to stay resident (it's the home screen) - it only gives up its // thread when app-module's scheduler asks it to (e.g. another new-model app is started). while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/localesettings/LocaleSettings.cpp b/Tactility/Source/app/localesettings/LocaleSettings.cpp index 013e0d38c..5c7d0c611 100644 --- a/Tactility/Source/app/localesettings/LocaleSettings.cpp +++ b/Tactility/Source/app/localesettings/LocaleSettings.cpp @@ -145,29 +145,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/notes/Notes.cpp b/Tactility/Source/app/notes/Notes.cpp index 8e71019b0..923e01156 100644 --- a/Tactility/Source/app/notes/Notes.cpp +++ b/Tactility/Source/app/notes/Notes.cpp @@ -190,51 +190,57 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.filePath = argv[0]; } + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - LOG_I(TAG, "Result for launch id %u", event.result.launch_id); - if (event.result.launch_id == ctx.loadFileLaunchId) { - ctx.loadFileLaunchId = 0; - if (event.result.result == 0 /* Ok */) { - auto path = fileselection::getLastPath(); - if (!path.empty()) { - openFile(&ctx, path); + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + LOG_I(TAG, "Result for launch id %u", event.result.launch_id); + if (event.result.launch_id == ctx.loadFileLaunchId) { + ctx.loadFileLaunchId = 0; + if (event.result.result == 0 /* Ok */) { + auto path = fileselection::getLastPath(); + if (!path.empty()) { + openFile(&ctx, path); + } } - } - } else if (event.result.launch_id == ctx.saveFileLaunchId) { - ctx.saveFileLaunchId = 0; - if (event.result.result == 0 /* Ok */) { - auto path = fileselection::getLastPath(); - // Must re-open file, because the UI was cleared after opening the dialog. - if (!path.empty() && saveFile(&ctx, path)) { - openFile(&ctx, path); + } else if (event.result.launch_id == ctx.saveFileLaunchId) { + ctx.saveFileLaunchId = 0; + if (event.result.result == 0 /* Ok */) { + auto path = fileselection::getLastPath(); + // Must re-open file, because the UI was cleared after opening the dialog. + if (!path.empty() && saveFile(&ctx, path)) { + openFile(&ctx, path); + } } } - } - app_manager_stop(event.result.launch_id); - break; - default: - break; + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index 93f378ddf..34130857e 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -240,31 +240,37 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { updateUi(&ctx); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.timer->start(); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } ctx.timer->stop(); window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/poweroff/PowerOff.cpp b/Tactility/Source/app/poweroff/PowerOff.cpp index edc638af3..5804f7104 100644 --- a/Tactility/Source/app/poweroff/PowerOff.cpp +++ b/Tactility/Source/app/poweroff/PowerOff.cpp @@ -136,18 +136,21 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } + while (app_event_poll(&sub, &event) == ERROR_NONE) { switch (event.type) { case APP_EVENT_CLOSE: shouldClose = true; @@ -155,10 +158,13 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { default: break; } + if (shouldClose) break; + } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index 86ecccc70..6fbe17783 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -250,24 +250,29 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } @@ -277,6 +282,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp index 801783c37..dff75bba1 100644 --- a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp +++ b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp @@ -104,24 +104,32 @@ int32_t appMain(AppInstanceId appInstanceId, int argc, char* argv[]) { ctx.argc = argc; ctx.argv = argv; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index f2623a52d..61239aae3 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -79,24 +79,32 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { settingsInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/setup/Setup.cpp b/Tactility/Source/app/setup/Setup.cpp index 66551182e..2b01b375d 100644 --- a/Tactility/Source/app/setup/Setup.cpp +++ b/Tactility/Source/app/setup/Setup.cpp @@ -159,7 +159,7 @@ void onContinueClicked(lv_event_t* event) { markCompleted(); // Async, non-blocking - must NOT call app_manager_stop() directly here: this // callback runs ON the LVGL task, and app-lifecycle transitions must happen on this - // app's own thread (woken via app_event_await()), which closes by returning. + // app's own thread (woken via app_event_poll()), which closes by returning. AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; app_event_emit(ctx->appInstanceId, &closeEvent); break; @@ -229,36 +229,42 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } }; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - if (event.result.launch_id == ctx.pendingStepDialogId) { - ctx.pendingStepDialogId = 0; - advanceTo(&ctx, ctx.stepIndex + 1); - } - app_manager_stop(event.result.launch_id); - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + if (event.result.launch_id == ctx.pendingStepDialogId) { + ctx.pendingStepDialogId = 0; + advanceTo(&ctx, ctx.stepIndex + 1); + } + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index 24c8ca021..db35843ce 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -417,9 +417,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { lvgl_unlock(); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.memoryTimer->start(); // Memory: every 10s @@ -427,16 +430,18 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } @@ -444,6 +449,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.tasksTimer->stop(); window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp b/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp index 9d22f7a27..81faed8cf 100644 --- a/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp +++ b/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp @@ -157,44 +157,50 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - if (event.result.launch_id == ctx.pendingTimeZoneDialogId) { - ctx.pendingTimeZoneDialogId = 0; - if (event.result.result == 0 /* Ok */) { - const auto name = timezone::getLastName(); - LOG_I(TAG, "Result name=%s code=%s", name.c_str(), timezone::getLastCode().c_str()); - if (!name.empty()) { - lvgl_lock(); - lv_label_set_text(ctx.timeZoneLabel, name.c_str()); - lvgl_unlock(); + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + if (event.result.launch_id == ctx.pendingTimeZoneDialogId) { + ctx.pendingTimeZoneDialogId = 0; + if (event.result.result == 0 /* Ok */) { + const auto name = timezone::getLastName(); + LOG_I(TAG, "Result name=%s code=%s", name.c_str(), timezone::getLastCode().c_str()); + if (!name.empty()) { + lvgl_lock(); + lv_label_set_text(ctx.timeZoneLabel, name.c_str()); + lvgl_unlock(); + } } } - } - app_manager_stop(event.result.launch_id); - break; - default: - break; + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/timezone/TimeZone.cpp b/Tactility/Source/app/timezone/TimeZone.cpp index c745d1482..277d7499d 100644 --- a/Tactility/Source/app/timezone/TimeZone.cpp +++ b/Tactility/Source/app/timezone/TimeZone.cpp @@ -240,26 +240,34 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { updateList(&ctx); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.updateTimer->start(); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } } + if (shouldClose) break; } ctx.updateTimer->stop(); window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp index e799d3297..56d4f77f4 100644 --- a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp +++ b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp @@ -174,7 +174,7 @@ void onPress(lv_event_t* event) { // Async, non-blocking - must NOT call app_manager_stop() directly here: this callback runs // ON the LVGL task, and app-lifecycle transitions must happen on this app's own thread - // (woken up via app_event_await() below), which closes by returning. The result (Ok/Error) + // (woken up via app_event_poll() below), which closes by returning. The result (Ok/Error) // is reported by appMain() itself when it returns, based on ctx.calibrationApplied. AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; app_event_emit(ctx->appInstanceId, &closeEvent); @@ -233,29 +233,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } lvgl_unlock(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); // finishCalibration() already applied a new calibration on success. On cancel/failure, // restore whatever calibration was on disk before the block above cleared it. diff --git a/Tactility/Source/app/trackball/TrackballSettings.cpp b/Tactility/Source/app/trackball/TrackballSettings.cpp index bd2d9e326..4040acb18 100644 --- a/Tactility/Source/app/trackball/TrackballSettings.cpp +++ b/Tactility/Source/app/trackball/TrackballSettings.cpp @@ -272,30 +272,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - persistIfUpdated(ctx); - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + persistIfUpdated(ctx); + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/usbsettings/UsbSettings.cpp b/Tactility/Source/app/usbsettings/UsbSettings.cpp index 865fecc58..e867489c1 100644 --- a/Tactility/Source/app/usbsettings/UsbSettings.cpp +++ b/Tactility/Source/app/usbsettings/UsbSettings.cpp @@ -85,29 +85,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/webserversettings/WebServerSettings.cpp b/Tactility/Source/app/webserversettings/WebServerSettings.cpp index b14afad6e..563cb76bd 100644 --- a/Tactility/Source/app/webserversettings/WebServerSettings.cpp +++ b/Tactility/Source/app/webserversettings/WebServerSettings.cpp @@ -362,24 +362,29 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.wsSettings.webServerEnabled = service::webserver::isWebServerEnabled(); ctx.originalSettings = ctx.wsSettings; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } } @@ -423,6 +428,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp index 6b8b02831..cfb198e4a 100644 --- a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp +++ b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp @@ -12,6 +12,7 @@ #include #include +#include #include namespace tt::app::wifiapsettings { @@ -29,9 +30,14 @@ struct Context { lv_obj_t* busySpinner = nullptr; lv_obj_t* connectButton = nullptr; lv_obj_t* disconnectButton = nullptr; + lv_obj_t* autoConnectSwitch = nullptr; + lv_obj_t* forgetButton = nullptr; + lv_obj_t* autoConnectWrapper = nullptr; uint32_t forgetDialogId = 0; - PubSub::SubscriptionHandle wifiSubscription = nullptr; + // Set once in appMain() before subscribing, left null if this device has no WiFi driver + Device* wifiDevice = nullptr; + WifiEventSubscription wifiEventSub {}; }; @@ -41,8 +47,7 @@ void onBackPressed(lv_event_t* event) { auto* ctx = static_cast(lv_event_get_user_data(event)); // Async, non-blocking - must NOT call app_manager_stop() directly here: that bound-waits // (thread_join) for this app's own thread to finish, which needs the LVGL lock - // (window_manager_remove()) - but this callback runs ON the LVGL task, which would - // deadlock against itself. + // but this callback runs ON the LVGL task, which would deadlock against itself. AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; app_event_emit(ctx->appInstanceId, &closeEvent); } @@ -104,13 +109,34 @@ void updateBusySpinner(Context* ctx) { } } +// Touches the filesystem (service::wifi::settings::load()) - callers must not run this on the +// LVGL task (see updateViews()'s callers). +void updateAutoConnectSection(Context* ctx) { + service::wifi::settings::WifiApSettings settings; + if (service::wifi::settings::load(ctx->ssid.c_str(), settings)) { + if (settings.autoConnect) { + lv_obj_add_state(ctx->autoConnectSwitch, LV_STATE_CHECKED); + } else { + lv_obj_remove_state(ctx->autoConnectSwitch, LV_STATE_CHECKED); + } + lv_obj_remove_flag(ctx->forgetButton, LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(ctx->autoConnectWrapper, LV_OBJ_FLAG_HIDDEN); + } else { + LOG_W(TAG, "No settings found"); + lv_obj_add_flag(ctx->forgetButton, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(ctx->autoConnectWrapper, LV_OBJ_FLAG_HIDDEN); + } +} + +// Runs on whichever thread calls it, not necessarily the LVGL task void updateViews(Context* ctx) { if (ctx->connectButton == nullptr) { - // Buried (e.g. the forget confirmation dialog opened on top) - see destroyWidgets(). + // Buried (e.g. the forget confirmation dialog opened on top) return; } updateConnectButton(ctx); updateBusySpinner(ctx); + updateAutoConnectSection(ctx); } void requestViewUpdate(Context* ctx) { @@ -119,13 +145,14 @@ void requestViewUpdate(Context* ctx) { lvgl_unlock(); } -// Runs with the LVGL lock already held, possibly on another app's thread - see -// WindowDestroyWidgetsFn's warnings. Must stay lock-free: only nulls pointers. void destroyWidgets(void* userData) { auto* ctx = static_cast(userData); ctx->busySpinner = nullptr; ctx->connectButton = nullptr; ctx->disconnectButton = nullptr; + ctx->autoConnectSwitch = nullptr; + ctx->forgetButton = nullptr; + ctx->autoConnectWrapper = nullptr; } void createWidgets(lv_obj_t* parent, void* userData) { @@ -162,43 +189,30 @@ void createWidgets(lv_obj_t* parent, void* userData) { // Forget - auto* forget_button = lv_button_create(wrapper); - lv_obj_set_width(forget_button, LV_PCT(100)); - lv_obj_add_event_cb(forget_button, onPressForget, LV_EVENT_SHORT_CLICKED, ctx); - auto* forget_button_label = lv_label_create(forget_button); + ctx->forgetButton = lv_button_create(wrapper); + lv_obj_set_width(ctx->forgetButton, LV_PCT(100)); + lv_obj_add_event_cb(ctx->forgetButton, onPressForget, LV_EVENT_SHORT_CLICKED, ctx); + auto* forget_button_label = lv_label_create(ctx->forgetButton); lv_obj_align(forget_button_label, LV_ALIGN_CENTER, 0, 0); lv_label_set_text(forget_button_label, "Forget"); + lv_obj_add_flag(ctx->forgetButton, LV_OBJ_FLAG_HIDDEN); // shown by updateAutoConnectSection() // Auto-connect - auto* auto_connect_wrapper = lv_obj_create(wrapper); - lv_obj_set_size(auto_connect_wrapper, LV_PCT(100), LV_SIZE_CONTENT); - lvgl::obj_set_style_bg_invisible(auto_connect_wrapper); - lv_obj_set_style_pad_all(auto_connect_wrapper, 0, LV_STATE_DEFAULT); - lv_obj_set_style_border_width(auto_connect_wrapper, 0, LV_STATE_DEFAULT); + ctx->autoConnectWrapper = lv_obj_create(wrapper); + lv_obj_set_size(ctx->autoConnectWrapper, LV_PCT(100), LV_SIZE_CONTENT); + lvgl::obj_set_style_bg_invisible(ctx->autoConnectWrapper); + lv_obj_set_style_pad_all(ctx->autoConnectWrapper, 0, LV_STATE_DEFAULT); + lv_obj_set_style_border_width(ctx->autoConnectWrapper, 0, LV_STATE_DEFAULT); + lv_obj_add_flag(ctx->autoConnectWrapper, LV_OBJ_FLAG_HIDDEN); // shown by updateAutoConnectSection() - auto* auto_connect_label = lv_label_create(auto_connect_wrapper); + auto* auto_connect_label = lv_label_create(ctx->autoConnectWrapper); lv_label_set_text(auto_connect_label, "Auto-connect"); lv_obj_align(auto_connect_label, LV_ALIGN_LEFT_MID, 0, 0); - auto* auto_connect_switch = lv_switch_create(auto_connect_wrapper); - lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, ctx); - lv_obj_align(auto_connect_switch, LV_ALIGN_RIGHT_MID, 0, 0); - - service::wifi::settings::WifiApSettings settings; - if (service::wifi::settings::load(ctx->ssid.c_str(), settings)) { - if (settings.autoConnect) { - lv_obj_add_state(auto_connect_switch, LV_STATE_CHECKED); - } else { - lv_obj_remove_state(auto_connect_switch, LV_STATE_CHECKED); - } - } else { - LOG_W(TAG, "No settings found"); - lv_obj_add_flag(forget_button, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(auto_connect_wrapper, LV_OBJ_FLAG_HIDDEN); - } - - updateViews(ctx); + ctx->autoConnectSwitch = lv_switch_create(ctx->autoConnectWrapper); + lv_obj_add_event_cb(ctx->autoConnectSwitch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, ctx); + lv_obj_align(ctx->autoConnectSwitch, LV_ALIGN_RIGHT_MID, 0, 0); } int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { @@ -207,56 +221,95 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.appInstanceId = appInstanceId; ctx.ssid = (argc > 0) ? argv[0] : std::string(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); - // Subscribed once here, not in createWidgets(): that callback re-runs on every - // burial/resurface rebuild, and re-subscribing there would leak the previous subscription - // (and its captured ctx pointer) every time, only the last of which shutdown ever cleans up. - ctx.wifiSubscription = service::wifi::getPubsub()->subscribe([&ctx](auto) { - requestViewUpdate(&ctx); - }); + Device* wifi_device = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &wifi_device) == ERROR_NONE) { + if (wifi_event_subscribe(wifi_device, &ctx.wifiEventSub, &event_group) == ERROR_NONE) { + ctx.wifiDevice = wifi_device; + } else { + LOG_W(TAG, "Failed to subscribe to WiFi events"); + device_put(wifi_device); + } + } else { + LOG_W(TAG, "No WiFi device found"); + } WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); + // The file-I/O-touching part of the view (updateAutoConnectSection()'s settings::load()) + // runs here, on this app's own task, not the LVGL task createWidgets() + requestViewUpdate(&ctx); + bool shouldClose = false; while (!shouldClose) { - AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; + TickType_t wait_timeout = (ctx.wifiDevice == nullptr) ? pdMS_TO_TICKS(500) : portMAX_DELAY; + task_event_group_wait_any(&event_group, nullptr, wait_timeout); + + if (ctx.wifiDevice == nullptr) { + Device* retry_device = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &retry_device) == ERROR_NONE) { + if (wifi_event_subscribe(retry_device, &ctx.wifiEventSub, &event_group) == ERROR_NONE) { + ctx.wifiDevice = retry_device; + } else { + device_put(retry_device); + } + } } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - case APP_EVENT_RESULT: - if (event.result.launch_id == ctx.forgetDialogId && event.result.result == 0) { // 0 = Yes - if (!service::wifi::settings::remove(ctx.ssid.c_str())) { - LOG_E(TAG, "Failed to remove SSID"); - } else { - LOG_I(TAG, "Removed SSID"); - if ( - service::wifi::getRadioState() == service::wifi::RadioState::ConnectionActive && - service::wifi::getConnectionTarget() == ctx.ssid - ) { - service::wifi::disconnect(); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + case APP_EVENT_RESULT: + if (event.result.launch_id == ctx.forgetDialogId && event.result.result == 0) { // 0 = Yes + if (!service::wifi::settings::remove(ctx.ssid.c_str())) { + LOG_E(TAG, "Failed to remove SSID"); + } else { + LOG_I(TAG, "Removed SSID"); + if ( + service::wifi::getRadioState() == service::wifi::RadioState::ConnectionActive && + service::wifi::getConnectionTarget() == ctx.ssid + ) { + service::wifi::disconnect(); + } + shouldClose = true; } - shouldClose = true; } - } - app_manager_stop(event.result.launch_id); - break; - default: - break; + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (shouldClose) break; + } + + if (ctx.wifiDevice != nullptr) { + WifiEvent wifi_event {}; + bool wifi_event_received = false; + while (wifi_event_poll(&ctx.wifiEventSub, &wifi_event) == ERROR_NONE) { + wifi_event_received = true; + } + if (wifi_event_received) { + requestViewUpdate(&ctx); + } } } - if (ctx.wifiSubscription != nullptr) { - service::wifi::getPubsub()->unsubscribe(ctx.wifiSubscription); + if (ctx.wifiDevice != nullptr) { + wifi_event_unsubscribe(ctx.wifiDevice, &ctx.wifiEventSub); + device_put(ctx.wifiDevice); } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/wificonnect/WifiConnect.cpp b/Tactility/Source/app/wificonnect/WifiConnect.cpp index 5701cc751..db3f70e11 100644 --- a/Tactility/Source/app/wificonnect/WifiConnect.cpp +++ b/Tactility/Source/app/wificonnect/WifiConnect.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -33,10 +34,6 @@ struct Context { std::string initialSsid; std::string initialPassword; - // Touched only from the LVGL task: directly by onConnectPressed() (an LVGL event callback, - // which already runs with the LVGL lock held), and by onWifiEvent() (a wifi-pubsub - // callback running on some other thread) which explicitly wraps its touches in - // lvgl_lock()/lvgl_unlock() - see WifiApSettings.cpp for the same convention. bool connecting = false; bool connectionError = false; @@ -49,7 +46,9 @@ struct Context { lv_obj_t* connecting_spinner = nullptr; lv_obj_t* connection_error = nullptr; - PubSub::SubscriptionHandle wifiSubscription = nullptr; + // Set once in appMain() before subscribing, left null if this device has no WiFi driver + Device* wifiDevice = nullptr; + WifiEventSubscription wifiEventSub {}; }; @@ -61,14 +60,12 @@ void onBackPressed(lv_event_t* event) { auto* ctx = static_cast(lv_event_get_user_data(event)); // Async, non-blocking - must NOT call app_manager_stop() directly here: that bound-waits // (thread_join) for this app's own thread to finish, which needs the LVGL lock - // (window_manager_remove()) - but this callback runs ON the LVGL task, which would - // deadlock against itself. + // but this callback runs ON the LVGL task, which would deadlock against itself. AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; app_event_emit(ctx->appInstanceId, &closeEvent); } -// Runs on the wifi service's pubsub thread, not the LVGL task or this app's own thread. -void onWifiEvent(Context* ctx, service::wifi::WifiEvent event) { +void onWifiEvent(Context* ctx, WifiEvent event) { bool shouldClose = false; lvgl_lock(); @@ -90,8 +87,6 @@ void onWifiEvent(Context* ctx, service::wifi::WifiEvent event) { lvgl_unlock(); if (shouldClose) { - // Async, non-blocking - same reasoning as onBackPressed() (must not call - // app_manager_stop() on ourselves); safe to call from any thread. AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; app_event_emit(ctx->appInstanceId, &closeEvent); } @@ -121,8 +116,7 @@ void setLoading(Context* ctx, bool loading) { void updateView(Context* ctx) { if (ctx->connect_button == nullptr) { - // Buried (e.g. this window's own connecting state closed it, or a future dialog opens - // on top) - see destroyWidgets(). + // Buried (e.g. this window's own connecting state closed it, or a future dialog opens on top) return; } if (ctx->connectionError) { @@ -313,39 +307,70 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.initialSsid = (argc > 0) ? argv[0] : std::string(); ctx.initialPassword = (argc > 1) ? argv[1] : std::string(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); - // Subscribed once here, not in createWidgets(): that callback re-runs on every - // burial/resurface rebuild, and re-subscribing there would leak the previous subscription - // (and its captured ctx pointer) every time, only the last of which shutdown ever cleans up. - ctx.wifiSubscription = service::wifi::getPubsub()->subscribe([&ctx](auto event) { - onWifiEvent(&ctx, event); - }); + Device* wifi_device = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &wifi_device) == ERROR_NONE) { + if (wifi_event_subscribe(wifi_device, &ctx.wifiEventSub, &event_group) == ERROR_NONE) { + ctx.wifiDevice = wifi_device; + } else { + LOG_W(TAG, "Failed to subscribe to WiFi events"); + device_put(wifi_device); + } + } else { + LOG_W(TAG, "No WiFi device found"); + } WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { + TickType_t wait_timeout = (ctx.wifiDevice == nullptr) ? pdMS_TO_TICKS(500) : portMAX_DELAY; + task_event_group_wait_any(&event_group, nullptr, wait_timeout); + + if (ctx.wifiDevice == nullptr) { + Device* retry_device = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &retry_device) == ERROR_NONE) { + if (wifi_event_subscribe(retry_device, &ctx.wifiEventSub, &event_group) == ERROR_NONE) { + ctx.wifiDevice = retry_device; + } else { + device_put(retry_device); + } + } + } + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + + if (ctx.wifiDevice != nullptr) { + WifiEvent wifi_event {}; + while (wifi_event_poll(&ctx.wifiEventSub, &wifi_event) == ERROR_NONE) { + onWifiEvent(&ctx, wifi_event); + } } } - if (ctx.wifiSubscription != nullptr) { - service::wifi::getPubsub()->unsubscribe(ctx.wifiSubscription); + if (ctx.wifiDevice != nullptr) { + wifi_event_unsubscribe(ctx.wifiDevice, &ctx.wifiEventSub); + device_put(ctx.wifiDevice); } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index a138cc23d..cbb5502cc 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -10,10 +10,13 @@ #include +#include #include #include +#include + namespace tt::app::wifimanage { constexpr auto* TAG = "WifiManage"; @@ -24,12 +27,18 @@ namespace { struct Context { uint32_t appInstanceId; - PubSub::SubscriptionHandle wifiSubscription = nullptr; + // Set once in appMain() before subscribing, left null if this device has no WiFi driver + Device* wifiDevice = nullptr; + WifiEventSubscription wifiEventSub {}; Mutex mutex; Bindings bindings {}; State state; View view = View(&bindings, &state); + TaskEventGroup* eventGroup = nullptr; + uint32_t refreshBit = 0; + std::atomic needsRefresh {false}; + void lock() { mutex.lock(); } void unlock() { mutex.unlock(); } }; @@ -62,17 +71,15 @@ static void onConnectToHidden() { wificonnect::start(); } -void requestViewUpdate(Context* ctx) { +void updateView(Context* ctx) { ctx->lock(); lvgl_lock(); - // Safe even while buried (e.g. WifiApSettings/WifiConnect opened on top): destroyWidgets() - // nulls the view's widget pointers before they're deleted, and update() no-ops on that. ctx->view.update(); lvgl_unlock(); ctx->unlock(); } -void onWifiEvent(Context* ctx, service::wifi::WifiEvent event) { +void onWifiEvent(Context* ctx, WifiEvent event) { auto radio_state = service::wifi::getRadioState(); LOG_I(TAG, "Update with state %s", service::wifi::radioStateToString(radio_state)); ctx->state.setRadioState(radio_state); @@ -93,7 +100,7 @@ void onWifiEvent(Context* ctx, service::wifi::WifiEvent event) { break; } - requestViewUpdate(ctx); + updateView(ctx); } void createWidgets(lv_obj_t* parent, void* userData) { @@ -101,12 +108,11 @@ void createWidgets(lv_obj_t* parent, void* userData) { ctx->lock(); ctx->state.setConnectSsid("Connected"); // TODO update with proper SSID ctx->view.init(ctx->appInstanceId, parent); - ctx->view.update(); ctx->unlock(); + ctx->needsRefresh = true; + task_event_group_signal(ctx->eventGroup, ctx->refreshBit); } -// Runs with the LVGL lock already held, possibly on another app's thread - see -// WindowDestroyWidgetsFn's warnings. Must stay lock-free: View::reset() only nulls pointers. void destroyWidgets(void* userData) { auto* ctx = static_cast(userData); ctx->view.reset(); @@ -123,18 +129,33 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { .onConnectToHidden = onConnectToHidden }; - ctx.wifiSubscription = service::wifi::getPubsub()->subscribe([&ctx](auto event) { - onWifiEvent(&ctx, event); - }); - // State update (it has its own locking) ctx.state.setRadioState(service::wifi::getRadioState()); ctx.state.setScanning(service::wifi::isScanning()); ctx.state.updateApRecords(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + ctx.eventGroup = &event_group; + if (task_event_group_claim_bit(&event_group, &ctx.refreshBit) != ERROR_NONE) { + LOG_W(TAG, "Failed to claim a refresh bit; resurfacing after burial won't repopulate the view"); + } + AppEventSubscription sub {}; sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); + + Device* wifi_device = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &wifi_device) == ERROR_NONE) { + if (wifi_event_subscribe(wifi_device, &ctx.wifiEventSub, &event_group) == ERROR_NONE) { + ctx.wifiDevice = wifi_device; + } else { + LOG_W(TAG, "Failed to subscribe to WiFi events"); + device_put(wifi_device); + } + } else { + LOG_W(TAG, "No WiFi device found"); + } WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); @@ -154,26 +175,58 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { bool shouldClose = false; while (!shouldClose) { + // If the wifi device wasn't started yet when this app opened (boot-order race), + // ctx.wifiDevice is still null and there's no wifi-driven wake source to learn + // "it's ready now" from, so poll for it on a bounded timeout instead of blocking + // indefinitely. Once subscribed, this reverts to + // portMAX_DELAY - task_event_group_wait_any() still returns immediately for app_event + // and (once live) wifi_event, this timeout only matters while neither has fired yet. + TickType_t wait_timeout = (ctx.wifiDevice == nullptr) ? pdMS_TO_TICKS(500) : portMAX_DELAY; + task_event_group_wait_any(&event_group, nullptr, wait_timeout); + + if (ctx.wifiDevice == nullptr) { + Device* retry_device = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &retry_device) == ERROR_NONE) { + if (wifi_event_subscribe(retry_device, &ctx.wifiEventSub, &event_group) == ERROR_NONE) { + ctx.wifiDevice = retry_device; + } else { + device_put(retry_device); + } + } + } + AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; } - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; + + if (ctx.wifiDevice != nullptr) { + WifiEvent wifi_event {}; + while (wifi_event_poll(&ctx.wifiEventSub, &wifi_event) == ERROR_NONE) { + onWifiEvent(&ctx, wifi_event); + } + } + + if (ctx.needsRefresh.exchange(false)) { + updateView(&ctx); } } - ctx.lock(); - service::wifi::getPubsub()->unsubscribe(ctx.wifiSubscription); - ctx.wifiSubscription = nullptr; - ctx.unlock(); + if (ctx.wifiDevice != nullptr) { + wifi_event_unsubscribe(ctx.wifiDevice, &ctx.wifiEventSub); + device_put(ctx.wifiDevice); + } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/service/espnow/EspNowHostedTransport.cpp b/Tactility/Source/service/espnow/EspNowHostedTransport.cpp index 73bb4c9d7..751ffe11e 100644 --- a/Tactility/Source/service/espnow/EspNowHostedTransport.cpp +++ b/Tactility/Source/service/espnow/EspNowHostedTransport.cpp @@ -6,6 +6,7 @@ #include +#include #include namespace tt::service::espnow::backend { @@ -16,16 +17,19 @@ namespace tt::service::espnow::backend { // truth shared with any other caller (e.g. an external OTA app), instead of being duplicated // here. bool waitForHostedTransport(uint32_t timeoutMs) { - Device* device = wifi_find_first_registered_device(); - if (device == nullptr) { + Device* device = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &device) != ERROR_NONE) { return false; } const FirmwareOps* ops = nullptr; void* ctx = nullptr; if (wifi_get_firmware_ops(device, &ops, &ctx) != ERROR_NONE) { + device_put(device); return false; } - return ops->wait_ready(ctx, timeoutMs); + bool ready = ops->wait_ready(ctx, timeoutMs); + device_put(device); + return ready; } } diff --git a/Tactility/Source/service/wifi/Wifi.cpp b/Tactility/Source/service/wifi/Wifi.cpp index 394f92c71..3ca2e6f95 100644 --- a/Tactility/Source/service/wifi/Wifi.cpp +++ b/Tactility/Source/service/wifi/Wifi.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,6 @@ namespace { /** State lives for the entire process; only ever (re)initialized by onStart(). */ struct WifiServiceState { Device* device = nullptr; - std::shared_ptr> pubsub = std::make_shared>(); RecursiveMutex mutex; bool secureConnection = false; // Internal: set by connect()/disconnect() while a manual attempt is in flight, cleared on @@ -79,24 +79,32 @@ struct WifiServiceState { TickType_t lastScanTime = MAX_TICKS; std::unique_ptr autoConnectTimer; bool bootEventSubscribed = false; + + // Dedicated consumer for WifiEvents, alive for the service's whole lifetime (started in + // onStart(), stopped in onStop() - see dispatchSetEnabled()'s comment on why this outlives + // radio on/off toggles): runs onWifiDeviceEvent() on its own stack instead of the ESP-IDF + // esp_event task's, by blocking in task_event_group_wait_any() rather than being called back + // directly from fire_event(). + TaskEventGroup wifiEventGroup {}; + WifiEventSubscription wifiEventSub {}; + Thread* wifiEventThread = nullptr; + std::atomic wifiEventThreadRunning {false}; }; WifiServiceState state; bool started = false; -void onWifiDeviceEvent(Device* device, void* context, ::WifiEvent event); +void onWifiDeviceEvent(Device* device, ::WifiEvent event); // ---- Helpers ---- -void publish(WifiEvent event) { - state.pubsub->publish(event); -} - -void publishRadioState(WifiRadioState radio_state) { - WifiEvent event = {}; - event.type = WIFI_EVENT_TYPE_RADIO_STATE_CHANGED; - event.radio_state = radio_state; - publish(event); +// state.device is started (bookkeeping allocated) for the service's entire lifetime now - see +// dispatchSetEnabled()'s comment - so device_is_ready() no longer tracks radio-on state; query +// the driver directly instead. +bool isRadioOn() { + if (state.device == nullptr) return false; + WifiRadioState radio = WIFI_RADIO_STATE_OFF; + return wifi_get_radio_state(state.device, &radio) == ERROR_NONE && radio == WIFI_RADIO_STATE_ON; } RadioState combineRadioState(WifiRadioState radio, WifiStationState station) { @@ -114,56 +122,88 @@ RadioState combineRadioState(WifiRadioState radio, WifiStationState station) { return RadioState::Off; } +// ---- WifiEvent consumer thread ---- +// Runs onWifiDeviceEvent() on its own stack (see WifiServiceState::wifiEventGroup's comment). + +constexpr configSTACK_DEPTH_TYPE WIFI_EVENT_THREAD_STACK_SIZE = 4096; + +int32_t wifiEventThreadMain() { + // The 250ms timeout only bounds how promptly a stop request (wifiEventThreadRunning going + // false) is noticed; a real event still wakes this immediately regardless, since + // task_event_group_wait_any() returns as soon as the bit is signalled, whichever comes first. + while (state.wifiEventThreadRunning.load()) { + task_event_group_wait_any(&state.wifiEventGroup, nullptr, pdMS_TO_TICKS(250)); + + WifiEvent event {}; + while (wifi_event_poll(&state.wifiEventSub, &event) == ERROR_NONE) { + onWifiDeviceEvent(state.device, event); + } + } + return 0; +} + +bool startWifiEventThread() { + task_event_group_construct(&state.wifiEventGroup); + if (wifi_event_subscribe(state.device, &state.wifiEventSub, &state.wifiEventGroup) != ERROR_NONE) { + task_event_group_destruct(&state.wifiEventGroup); + return false; + } + + state.wifiEventThreadRunning = true; + state.wifiEventThread = new Thread("wifi-events", WIFI_EVENT_THREAD_STACK_SIZE, [] { return wifiEventThreadMain(); }); + state.wifiEventThread->start(); + return true; +} + +void stopWifiEventThread() { + if (state.wifiEventThread == nullptr) return; + + state.wifiEventThreadRunning = false; + state.wifiEventThread->join(); + delete state.wifiEventThread; + state.wifiEventThread = nullptr; + + wifi_event_unsubscribe(state.device, &state.wifiEventSub); + task_event_group_destruct(&state.wifiEventGroup); +} + // ---- Dispatched work (runs on the main task) ---- +// state.device is started (device_start()) once, in onStart(), and never stopped until onStop() - +// this only toggles the radio itself, so the wifi-events thread (and any app subscribed directly +// to the driver) stays subscribed across on/off toggles instead of having to resubscribe. void dispatchSetEnabled(bool enabled) { LOG_I(TAG, "dispatchSetEnabled(%d)", (int)enabled); if (!started || state.device == nullptr) return; - bool ready = device_is_ready(state.device); - if (enabled == ready) { + if (enabled == isRadioOn()) { LOG_W(TAG, "Can't enable/disable from current state"); return; } if (enabled) { - publishRadioState(WIFI_RADIO_STATE_ON_PENDING); - if (device_start(state.device) != ERROR_NONE) { - LOG_E(TAG, "Failed to start WiFi device"); - publishRadioState(WIFI_RADIO_STATE_OFF); - return; - } - - if (wifi_add_event_callback(state.device, nullptr, onWifiDeviceEvent) != ERROR_NONE) { - LOG_E(TAG, "Failed to register WiFi event callback"); - device_stop(state.device); - publishRadioState(WIFI_RADIO_STATE_OFF); + if (wifi_set_radio_on(state.device) != ERROR_NONE) { + LOG_E(TAG, "Failed to enable WiFi radio"); return; } state.pauseAutoConnect = false; state.lastScanTime = 0; - publishRadioState(WIFI_RADIO_STATE_ON); } else { - publishRadioState(WIFI_RADIO_STATE_OFF_PENDING); - if (device_stop(state.device) != ERROR_NONE) { - LOG_E(TAG, "Failed to stop WiFi device"); - publishRadioState(WIFI_RADIO_STATE_ON); + if (wifi_set_radio_off(state.device) != ERROR_NONE) { + LOG_E(TAG, "Failed to disable WiFi radio"); return; } - wifi_remove_event_callback(state.device, onWifiDeviceEvent); - state.secureConnection = false; - publishRadioState(WIFI_RADIO_STATE_OFF); } } void dispatchScan() { LOG_I(TAG, "dispatchScan()"); - if (!started || state.device == nullptr || !device_is_ready(state.device)) return; + if (!started || state.device == nullptr || !isRadioOn()) return; state.lastScanTime = get_ticks(); @@ -278,7 +318,7 @@ void onAutoConnectTimer() { // ---- Kernel driver event bridge ---- -void onWifiDeviceEvent(Device* device, void* /*context*/, ::WifiEvent event) { +void onWifiDeviceEvent(Device* device, ::WifiEvent event) { switch (event.type) { case WIFI_EVENT_TYPE_SCAN_FINISHED: getMainDispatcher().dispatch([] { dispatchAutoConnect(); }); @@ -348,12 +388,8 @@ void autoScanSetPaused(bool paused) { // region Public functions -std::shared_ptr> getPubsub() { - return state.pubsub; -} - RadioState getRadioState() { - if (!started || state.device == nullptr || !device_is_ready(state.device)) { + if (!started || state.device == nullptr) { return RadioState::Off; } @@ -403,7 +439,7 @@ void connect(const settings::WifiApSettings& ap, bool remember) { state.pauseAutoConnect = true; state.connectionTarget = ap; state.connectionTargetRemember = remember; - radio_off = !device_is_ready(state.device); + radio_off = !isRadioOn(); } getMainDispatcher().dispatch([radio_off] { @@ -505,9 +541,17 @@ class WifiService final : public Service { wifi_auto_scan_set_paused_function(autoScanSetPaused); - state.device = wifi_find_first_registered_device(); - if (state.device == nullptr) { + Device* wifi_device = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &wifi_device) != ERROR_NONE) { LOG_W(TAG, "No WiFi device found"); + } else if (device_start(wifi_device) != ERROR_NONE) { + LOG_E(TAG, "Failed to start WiFi device"); + device_put(wifi_device); + } else { + state.device = wifi_device; + if (!startWifiEventThread()) { + LOG_E(TAG, "Failed to subscribe to WiFi events"); + } } if (system_event_callback_add(KERNEL_EVENT_BOOT_COMPLETED, onBootCompleted, nullptr) == ERROR_NONE) { @@ -535,9 +579,13 @@ class WifiService final : public Service { state.bootEventSubscribed = false; } - if (state.device != nullptr && device_is_ready(state.device)) { - wifi_remove_event_callback(state.device, onWifiDeviceEvent); + if (state.device != nullptr) { + if (isRadioOn()) { + wifi_set_radio_off(state.device); + } + stopWifiEventThread(); device_stop(state.device); + device_put(state.device); } state.secureConnection = false; diff --git a/TactilityKernel/include/tactility/concurrent/task_event_group.h b/TactilityKernel/include/tactility/concurrent/task_event_group.h new file mode 100644 index 000000000..e0fd2ff6f --- /dev/null +++ b/TactilityKernel/include/tactility/concurrent/task_event_group.h @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Usable bits per FreeRTOS event group when configUSE_16_BIT_TICKS is 0 + * (This project's setting on every target. See Devices/simulator/Source/FreeRTOSConfig.h). */ +#define TASK_EVENT_GROUP_MAX_BITS 24 + +/** + * Lets a task block on several independent event sources (e.g. app_event and system_event) with + * a single wait call, instead of each source claiming the task's own FreeRTOS notification value + * or its own semaphore - primitives that can't be combined into one blocking call. + * + * Caller-owned storage, constructed/destructed like struct Mutex/EventGroupHandle_t - no heap + * allocation. Each event source claims its own bit at subscribe time via + * task_event_group_claim_bit(), so sources at any layer (kernel, firmware, app) can share one + * group without knowing about each other's bit assignments. + * + * @warning `bit_mutex`/`claimed_bits` are implementation-only; do not read or write directly. + */ +struct TaskEventGroup { + uint32_t claimed_bits; + struct { + EventGroupHandle_t handle; + struct Mutex bit_mutex; + } internal; +}; + +/** @warning Does not work in ISR context. */ +void task_event_group_construct(struct TaskEventGroup* group); + +/** @warning Does not work in ISR context. */ +void task_event_group_destruct(struct TaskEventGroup* group); + +/** + * Claim an unused bit in @a group for exclusive use by one event source. Always starts clear + * (unsignalled), whether this bit has ever been claimed before or not. + * @param[out] out_bit set to the claimed bit (a single-bit mask, not a bit index) on success + * @retval ERROR_NONE on success + * @retval ERROR_RESOURCE all TASK_EVENT_GROUP_MAX_BITS bits are already claimed + */ +error_t task_event_group_claim_bit(struct TaskEventGroup* group, uint32_t* out_bit); + +/** + * Release a bit previously returned by task_event_group_claim_bit(), making it available for + * reuse. Also clears it, so a signal nobody drained before releasing can't be handed to the next + * claimant as a false "already fired" wake. + */ +void task_event_group_release_bit(struct TaskEventGroup* group, uint32_t bit); + +/** Signal @a bit, waking any task blocked in task_event_group_wait() for it. ISR-safe. */ +error_t task_event_group_signal(struct TaskEventGroup* group, uint32_t bit); + +/** + * Block until one or more bits in @a bits_mask are signalled, or @a timeout elapses. Matched + * bits are cleared on exit. + * @param[in] await_all if true, wait for every bit in @a bits_mask; otherwise wait for any of them + * @param[out] out_flags if non-NULL, set to the matched bits on ERROR_NONE + */ +error_t task_event_group_wait( + struct TaskEventGroup* group, + uint32_t bits_mask, + bool await_all, + uint32_t* out_flags, + TickType_t timeout +); + +/** + * Like task_event_group_wait(), but waits on every bit currently claimed in @a group (snapshotted + * at call time) instead of a caller-supplied mask - so the caller doesn't need to track/OR + * together each subscription's bit by hand. A bit claimed by a new subscription *during* the wait + * isn't included until the next call. + * @warning Meant for the common case where every subscription sharing @a group is set up before + * the wait loop starts (matching app_event/system_event/wifi_event's own usage patterns) - not + * for a group a subscriber can join mid-wait and expect to wake immediately. + * @retval ERROR_TIMEOUT returned immediately, without blocking, if @a group currently has no + * claimed bits (FreeRTOS asserts on a zero-bit wait mask, so this is handled before it gets there) + */ +error_t task_event_group_wait_any(struct TaskEventGroup* group, uint32_t* out_flags, TickType_t timeout); + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/include/tactility/drivers/wifi.h b/TactilityKernel/include/tactility/drivers/wifi.h index 0a5b10308..0caad93d9 100644 --- a/TactilityKernel/include/tactility/drivers/wifi.h +++ b/TactilityKernel/include/tactility/drivers/wifi.h @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -90,9 +92,59 @@ struct WifiEvent { }; }; -typedef void (*WifiEventCallback)(struct Device* device, void* callback_context, struct WifiEvent event); +/** Number of events a WifiEventSubscription can hold before wifi_event_emit() starts dropping + * the newest event for it (still delivered to any other matching subscription). Generous: a + * device fires these serially, one at a time, not in genuinely concurrent bursts. */ +#define WIFI_EVENT_QUEUE_CAPACITY 4 + +/** + * Caller-owned subscription node, registered with wifi_event_subscribe() and polled with + * wifi_event_poll(). Like app_event, this queues events by value (FIFO) rather than + * coalescing to the latest one: a burst of distinct WifiEventTypes (e.g. + * WIFI_EVENT_TYPE_STATION_STATE_CHANGED immediately followed by + * WIFI_EVENT_TYPE_STATION_CONNECTION_RESULT) must each be delivered, not just "something + * changed." + * @warning Fields other than `bit` are for internal use only; do not read or write them + * directly. + */ +struct WifiEventSubscription { + /** Set by wifi_event_subscribe(). Read-only for the caller: OR it into a + * task_event_group_wait() mask (alongside other subscriptions sharing the same + * `event_group`) to block on this subscription and other event sources with one call. */ + uint32_t bit; + + struct { + /** Caller-owned, borrowed; set by wifi_event_subscribe(). */ + struct TaskEventGroup* event_group; + /** Guards `queue`/`head`/`count` between wifi_event_emit() (driver thread) and + * wifi_event_poll() (caller's thread) - the two live in different translation units with + * no other shared lock. */ + struct Mutex ring_mutex; + struct WifiEvent queue[WIFI_EVENT_QUEUE_CAPACITY]; + uint8_t head; + uint8_t count; + + struct WifiEventSubscription* next; + } internal; +}; struct WifiApi { + /** + * Turn the radio on. Unlike start_device()/stop_device() (which only allocate/free the + * driver's bookkeeping, so event subscribers can stay subscribed across radio toggles), + * this is what actually brings the hardware up. + * @param[in] device the wifi device + * @return ERROR_NONE on success, or if the radio is already on + */ + error_t (*set_radio_on)(struct Device* device); + + /** + * Turn the radio off. See set_radio_on(). + * @param[in] device the wifi device + * @return ERROR_NONE on success, or if the radio is already off + */ + error_t (*set_radio_off)(struct Device* device); + /** * Get the radio state of the device. * @param[in] device the wifi device @@ -182,21 +234,29 @@ struct WifiApi { error_t (*station_get_rssi)(struct Device* device, int32_t* rssi); /** - * Add a WifiEvent callback. + * Register a subscription for this device's WifiEvents. + * @warning Does not work in ISR context. * @param[in] device the wifi device - * @param[in] callback_context the context to pass to the callback - * @param[in] callback the callback function - * @return ERROR_NONE on success + * @param[in,out] sub subscription to register; owns the storage, must stay alive (and + * stationary) until unsubscribed + * @param[in] event_group caller-owned group to wait on; must outlive @a sub (i.e. be + * destructed only after event_unsubscribe()). To block for an event, call + * task_event_group_wait()/task_event_group_wait_any() on this group (OR sub->bit into the + * mask, or use _wait_any() to include every subscription sharing it), then drain with + * wifi_event_poll(). + * @retval ERROR_NONE on success + * @retval ERROR_RESOURCE @a event_group has no free bits left to claim */ - error_t (*add_event_callback)(struct Device* device, void* callback_context, WifiEventCallback callback); + error_t (*event_subscribe)(struct Device* device, struct WifiEventSubscription* sub, struct TaskEventGroup* event_group); /** - * Remove a WifiEvent callback. + * Remove a previously registered subscription. + * @warning Does not work in ISR context. * @param[in] device the wifi device - * @param[in] callback the callback function - * @return ERROR_NONE on success + * @param[in] sub subscription to remove, as passed to event_subscribe() + * @return ERROR_NONE on success, ERROR_NOT_FOUND if no matching subscription exists */ - error_t (*remove_event_callback)(struct Device* device, WifiEventCallback callback); + error_t (*event_unsubscribe)(struct Device* device, struct WifiEventSubscription* sub); /** * Get this device's co-processor firmware update interface, if it has one. @@ -210,8 +270,10 @@ struct WifiApi { extern const struct DeviceType WIFI_TYPE; -/** @return the first registered WiFi device, regardless of started state, or NULL if none exists */ -struct Device* wifi_find_first_registered_device(void); +/** Turn the radio on. See WifiApi::set_radio_on(). Requires the device to be started (device_start()). */ +error_t wifi_set_radio_on(struct Device* device); +/** Turn the radio off. See WifiApi::set_radio_off(). Requires the device to be started (device_start()). */ +error_t wifi_set_radio_off(struct Device* device); error_t wifi_get_radio_state(struct Device* device, enum WifiRadioState* state); error_t wifi_get_station_state(struct Device* device, enum WifiStationState* state); @@ -224,8 +286,39 @@ error_t wifi_station_get_target_ssid(struct Device* device, char* ssid); error_t wifi_station_connect(struct Device* device, const char* ssid, const char* password, int32_t channel); error_t wifi_station_disconnect(struct Device* device); error_t wifi_station_get_rssi(struct Device* device, int32_t* rssi); -error_t wifi_add_event_callback(struct Device* device, void* callback_context, WifiEventCallback callback); -error_t wifi_remove_event_callback(struct Device* device, WifiEventCallback callback); + +/** + * Register a subscription for @a device's WifiEvents. + * @warning Does not work in ISR context. + * @param[in] device the wifi device + * @param[in,out] sub subscription to register; owns the storage, must stay alive (and + * stationary) until unsubscribed + * @param[in] event_group caller-owned group to wait on; must outlive @a sub. To block for an + * event, call task_event_group_wait()/task_event_group_wait_any() on this group (OR sub->bit + * into the mask, or use _wait_any() to include every subscription sharing it), then drain with + * wifi_event_poll(). + * @retval ERROR_NONE on success + * @retval ERROR_RESOURCE @a event_group has no free bits left to claim + */ +error_t wifi_event_subscribe(struct Device* device, struct WifiEventSubscription* sub, struct TaskEventGroup* event_group); + +/** + * Remove a previously registered subscription. + * @warning Does not work in ISR context. + * @return ERROR_NONE on success, ERROR_NOT_FOUND if no matching subscription exists + */ +error_t wifi_event_unsubscribe(struct Device* device, struct WifiEventSubscription* sub); + +/** + * Non-blocking: pop the next event for @a sub if one is already queued. + * @warning Never blocks. To wait for an event, block in task_event_group_wait()/ + * task_event_group_wait_any() on @a sub's event group first (see wifi_event_subscribe()), then + * drain with this in a loop. + * @retval ERROR_NONE @a out_event was filled + * @retval ERROR_TIMEOUT nothing queued right now + */ +error_t wifi_event_poll(struct WifiEventSubscription* sub, struct WifiEvent* out_event); + error_t wifi_get_firmware_ops(struct Device* device, const struct FirmwareOps** ops, void** ctx); #ifdef __cplusplus diff --git a/TactilityKernel/include/tactility/system_event.h b/TactilityKernel/include/tactility/system_event.h index 71a97dc68..7ebd6a176 100644 --- a/TactilityKernel/include/tactility/system_event.h +++ b/TactilityKernel/include/tactility/system_event.h @@ -5,10 +5,8 @@ #include #include +#include #include -#include -#include -#include #ifdef __cplusplus extern "C" { @@ -141,17 +139,9 @@ error_t system_event_emit( /** * Poll subscription: caller-owned node, registered with system_event_subscribe() - * and polled with system_event_await(). Unlike system_event_callback_t, `event` is a by-value + * and polled with system_event_poll(). Unlike system_event_callback_t, `event` is a by-value * copy that remains valid for the subscription's lifetime (until the next matching event * overwrites it), not just for the duration of a callback. - * @warning Must be zero-initialized before the first system_event_subscribe() call (e.g. - * `SystemEventSubscription sub = {};` in C++, `SystemEventSubscription sub = {0};` in C, or - * static/global storage) - system_event_subscribe() reads `internal.unsubscribe_in_progress` - * before it writes it, to detect reuse of a node still being torn down by a concurrent - * system_event_unsubscribe() call; on indeterminate (non-zeroed) storage that read is undefined - * behavior. Not required again for a later system_event_subscribe() reusing the same node after - * system_event_unsubscribe() - the fields it depends on are fully owned/maintained by this API - * from the first successful registration onward. */ struct SystemEventSubscription { /** `event.type` is the event type to subscribe to; set by the caller before @@ -159,36 +149,26 @@ struct SystemEventSubscription { * each matching system_event_emit() - see the @warning above. */ struct SystemEvent event; + /** Set by system_event_subscribe(). Read-only for the caller: OR it into a + * task_event_group_wait() mask (alongside other subscriptions sharing the same + * `internal.event_group`) to block on this subscription and other event sources with one + * call. */ + uint32_t bit; + /** Implementation-only bookkeeping; do not read or write directly. */ struct { - /** Own wakeup signal, not the subscribing task's shared default notification value - a - * task with more than one poll subscription would otherwise have events for one - * subscription wake (and consume the notification meant for) system_event_await() - * calls on another. */ - SemaphoreHandle_t semaphore; + /** Caller-owned, borrowed; set by system_event_subscribe(). A task with more than one + * poll subscription shares one group across them - each subscription claims its own + * bit, so an event for one can't wake (and consume the signal meant for) another. */ + struct TaskEventGroup* event_group; uint32_t sequence; uint32_t consumed_sequence; - /** Number of tasks currently blocked in system_event_await() on `semaphore` - - * system_event_unsubscribe() waits for this to reach 0 before deleting it, since - * FreeRTOS requires no task be blocked on a semaphore when it's deleted. */ - int waiter_count; - /** Set by system_event_unsubscribe() before it gives `semaphore` and waits, so a task - * already blocked in system_event_await() bails out (ERROR_INVALID_STATE) instead of - * waiting out its full timeout. Reset once system_event_unsubscribe() finishes - * draining old awaiters (see unsubscribe_in_progress) - not simply "on the next - * system_event_subscribe()", so a fresh registration can never observe a stale `true` - * left over from an unsubscribe that hasn't returned yet. */ + /** Set by system_event_unsubscribe(). Diagnostic only: lets a subsequent + * system_event_poll() call report ERROR_INVALID_STATE instead of ERROR_TIMEOUT. Not a + * safety mechanism - see system_event_unsubscribe()'s @warning. */ bool cancelled; - /** True from the moment system_event_unsubscribe() unlinks `sub` until it has finished - * draining old awaiters and deleted the old semaphore. system_event_subscribe() spins - * until this clears before reusing `sub` - otherwise a new registration could reset - * waiter_count/cancelled (both shared with the old registration, there being only one - * `sub`) out from under the old system_event_unsubscribe() call still relying on them, - * or hand out a new semaphore for that same call to then promptly delete instead of the - * old one, while an old awaiter is still blocked on the real old semaphore. */ - bool unsubscribe_in_progress; struct SystemEventSubscription* next; } internal; @@ -197,52 +177,50 @@ struct SystemEventSubscription { /** * Register a poll subscription for events of @a sub->type. * @warning Does not work in ISR context. - * @warning On its very first call for a given @a sub, @a sub must have been zero-initialized - - * see SystemEventSubscription's @warning. - * @warning If @a sub was just passed to system_event_unsubscribe() (e.g. reusing a node for a - * new registration) and that call hasn't returned yet on another task, this call blocks - * (briefly - not for the full duration of anyone's timeout) until it does, before registering - - * see SystemEventSubscription::internal.unsubscribe_in_progress. * @param[in,out] sub subscription to register; caller sets @a sub->type beforehand, owns the * storage, and must keep it alive (and stationary) until unsubscribed + * @param[in] event_group caller-owned group to wait on; must outlive @a sub (i.e. be + * destructed only after system_event_unsubscribe()). To block for an event, call + * task_event_group_wait()/task_event_group_wait_any() on this group (OR sub->bit into the mask, + * or use _wait_any() to include every subscription sharing it), then poll with + * system_event_poll(). * @retval ERROR_NONE on success - * @retval ERROR_OUT_OF_MEMORY failed to allocate the subscription's wakeup semaphore; @a sub - * was not registered + * @retval ERROR_RESOURCE @a event_group has no free bits left to claim; @a sub was not registered * @retval ERROR_INVALID_STATE @a sub is already registered */ -error_t system_event_subscribe(struct SystemEventSubscription* sub); +error_t system_event_subscribe(struct SystemEventSubscription* sub, struct TaskEventGroup* event_group); /** * Remove a previously registered poll subscription. * @warning Does not work in ISR context. - * @warning Blocks (briefly - not for the full duration of anyone's timeout) until any task - * currently blocked in system_event_await() on @a sub has woken up and left, so it's safe to - * delete the subscription's semaphore before this call returns. A blocked awaiter is woken - * (with ERROR_INVALID_STATE) as part of this call rather than left to time out on its own. - * A concurrent system_event_subscribe() reusing the same @a sub waits out this same window - * (see system_event_subscribe()'s @warning) rather than racing it. + * @warning Does not wait for a task concurrently blocked in task_event_group_wait()/ + * task_event_group_wait_any() on @a sub's bit to leave before releasing that bit - the caller + * must ensure no other task is still waiting on @a sub before unsubscribing it. (A blocked task + * is still nudged awake as a best-effort courtesy - its subsequent system_event_poll() call will + * report ERROR_INVALID_STATE - but this is diagnostic, not a guarantee.) * @param[in] sub subscription to remove, as passed to system_event_subscribe() * @return ERROR_NONE on success, ERROR_NOT_FOUND if no matching subscription exists */ error_t system_event_unsubscribe(struct SystemEventSubscription* sub); /** - * Blocks the calling task until a new event arrives for @a sub, or timeout elapses. + * Non-blocking: check whether a new event has arrived for @a sub since the last call. + * @warning Never blocks. To wait, block in task_event_group_wait()/task_event_group_wait_any() + * on @a sub's bit first (see system_event_subscribe()), then call this. * @warning Poll subscriptions coalesce to the latest event, they are not a queue: if * system_event_emit() is called more than once for @a sub->event.type between two - * system_event_await() calls, only the most recent event's data/timestamp is visible via + * system_event_poll() calls, only the most recent event's data/timestamp is visible via * system_event_get_data()/system_event_get_timestamp() afterward - intermediate events are * silently overwritten, never delivered. Use system_event_callback_add() instead if every * individual event matters. - * @warning Cannot be called concurrently from different tasks. Each tasks must have its own subscription. - * @param[in,out] sub subscription to wait on, as passed to system_event_subscribe() - * @param[in] timeout max ticks to wait - * @retval ERROR_NONE an event arrived - * @retval ERROR_TIMEOUT @a timeout elapsed first - * @retval ERROR_INVALID_STATE another task called system_event_unsubscribe() on @a sub while - * this call was blocked + * @warning Cannot be called concurrently from different tasks. Each task must have its own subscription. + * @param[in,out] sub subscription to poll, as passed to system_event_subscribe() + * @retval ERROR_NONE a new event arrived - read it via @a sub->event or system_event_get_data() + * @retval ERROR_TIMEOUT no new event since the last call + * @retval ERROR_INVALID_STATE @a sub was unsubscribed (best-effort diagnostic, not guaranteed - + * see system_event_unsubscribe()'s @warning) */ -error_t system_event_await(struct SystemEventSubscription* sub, TickType_t timeout); +error_t system_event_poll(struct SystemEventSubscription* sub); /** * Copies @a sub's current event payload (the data from the most recent system_event_emit() diff --git a/TactilityKernel/source/concurrent/task_event_group.cpp b/TactilityKernel/source/concurrent/task_event_group.cpp new file mode 100644 index 000000000..383ef904f --- /dev/null +++ b/TactilityKernel/source/concurrent/task_event_group.cpp @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +#include + +extern "C" { + +void task_event_group_construct(TaskEventGroup* group) { + event_group_construct(&group->internal.handle); + mutex_construct(&group->internal.bit_mutex); + group->claimed_bits = 0; +} + +void task_event_group_destruct(TaskEventGroup* group) { + event_group_destruct(&group->internal.handle); + mutex_destruct(&group->internal.bit_mutex); + group->claimed_bits = 0; +} + +error_t task_event_group_claim_bit(TaskEventGroup* group, uint32_t* out_bit) { + mutex_lock(&group->internal.bit_mutex); + + error_t result = ERROR_RESOURCE; + for (uint32_t i = 0; i < TASK_EVENT_GROUP_MAX_BITS; i++) { + uint32_t bit = 1u << i; + if ((group->claimed_bits & bit) == 0) { + group->claimed_bits |= bit; + *out_bit = bit; + result = ERROR_NONE; + break; + } + } + + mutex_unlock(&group->internal.bit_mutex); + return result; +} + +void task_event_group_release_bit(TaskEventGroup* group, uint32_t bit) { + // Clear the real flag, not just the claim bookkeeping: a signal nobody drained before + // releasing (e.g. a courtesy nudge on unsubscribe with no one currently waiting) would + // otherwise sit set in the underlying event group and hand the next claimant of this same + // bit a false "already fired" wake. + event_group_clear(group->internal.handle, bit); + + mutex_lock(&group->internal.bit_mutex); + group->claimed_bits &= ~bit; + mutex_unlock(&group->internal.bit_mutex); +} + +error_t task_event_group_signal(TaskEventGroup* group, uint32_t bit) { + return event_group_set(group->internal.handle, bit); +} + +error_t task_event_group_wait( + TaskEventGroup* group, + uint32_t bits_mask, + bool await_all, + uint32_t* out_flags, + TickType_t timeout +) { + return event_group_wait(group->internal.handle, bits_mask, await_all, true, out_flags, timeout); +} + +error_t task_event_group_wait_any(TaskEventGroup* group, uint32_t* out_flags, TickType_t timeout) { + mutex_lock(&group->internal.bit_mutex); + uint32_t mask = group->claimed_bits; + mutex_unlock(&group->internal.bit_mutex); + + if (mask == 0) { + // xEventGroupWaitBits() asserts on a zero mask - nothing claimed means nothing to wait + // for, so this is the correct outcome anyway. + return ERROR_TIMEOUT; + } + + return task_event_group_wait(group, mask, false, out_flags, timeout); +} + +} // extern "C" diff --git a/TactilityKernel/source/drivers/wifi.cpp b/TactilityKernel/source/drivers/wifi.cpp index 8ac182d11..5c6c917ec 100644 --- a/TactilityKernel/source/drivers/wifi.cpp +++ b/TactilityKernel/source/drivers/wifi.cpp @@ -7,13 +7,12 @@ extern "C" { -struct Device* wifi_find_first_registered_device() { - struct Device* found = nullptr; - device_for_each_of_type(&WIFI_TYPE, &found, [](struct Device* dev, void* ctx) -> bool { - *static_cast(ctx) = dev; - return false; - }); - return found; +error_t wifi_set_radio_on(struct Device* device) { + return WIFI_API(device)->set_radio_on(device); +} + +error_t wifi_set_radio_off(struct Device* device) { + return WIFI_API(device)->set_radio_off(device); } error_t wifi_get_radio_state(struct Device* device, enum WifiRadioState* state) { @@ -60,12 +59,36 @@ error_t wifi_station_get_rssi(struct Device* device, int32_t* rssi) { return WIFI_API(device)->station_get_rssi(device, rssi); } -error_t wifi_add_event_callback(struct Device* device, void* callback_context, WifiEventCallback callback) { - return WIFI_API(device)->add_event_callback(device, callback_context, callback); +error_t wifi_event_subscribe(struct Device* device, struct WifiEventSubscription* sub, struct TaskEventGroup* event_group) { + mutex_construct(&sub->internal.ring_mutex); + sub->internal.head = 0; + sub->internal.count = 0; + + error_t result = WIFI_API(device)->event_subscribe(device, sub, event_group); + if (result != ERROR_NONE) { + mutex_destruct(&sub->internal.ring_mutex); + } + return result; +} + +error_t wifi_event_unsubscribe(struct Device* device, struct WifiEventSubscription* sub) { + error_t result = WIFI_API(device)->event_unsubscribe(device, sub); + if (result == ERROR_NONE) { + mutex_destruct(&sub->internal.ring_mutex); + } + return result; } -error_t wifi_remove_event_callback(struct Device* device, WifiEventCallback callback) { - return WIFI_API(device)->remove_event_callback(device, callback); +error_t wifi_event_poll(struct WifiEventSubscription* sub, struct WifiEvent* out_event) { + mutex_lock(&sub->internal.ring_mutex); + bool has_event = sub->internal.count > 0; + if (has_event) { + *out_event = sub->internal.queue[sub->internal.head]; + sub->internal.head = (sub->internal.head + 1) % WIFI_EVENT_QUEUE_CAPACITY; + sub->internal.count--; + } + mutex_unlock(&sub->internal.ring_mutex); + return has_event ? ERROR_NONE : ERROR_TIMEOUT; } error_t wifi_get_firmware_ops(struct Device* device, const struct FirmwareOps** ops, void** ctx) { diff --git a/TactilityKernel/source/symbols.c b/TactilityKernel/source/symbols.c index d55076948..62ab04bee 100644 --- a/TactilityKernel/source/symbols.c +++ b/TactilityKernel/source/symbols.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -379,7 +380,8 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = { DEFINE_MODULE_SYMBOL(camera_capture_jpeg), DEFINE_MODULE_SYMBOL(CAMERA_TYPE), // drivers/wifi - DEFINE_MODULE_SYMBOL(wifi_find_first_registered_device), + DEFINE_MODULE_SYMBOL(wifi_set_radio_on), + DEFINE_MODULE_SYMBOL(wifi_set_radio_off), DEFINE_MODULE_SYMBOL(wifi_get_radio_state), DEFINE_MODULE_SYMBOL(wifi_get_station_state), DEFINE_MODULE_SYMBOL(wifi_get_access_point_state), @@ -391,8 +393,9 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = { DEFINE_MODULE_SYMBOL(wifi_station_connect), DEFINE_MODULE_SYMBOL(wifi_station_disconnect), DEFINE_MODULE_SYMBOL(wifi_station_get_rssi), - DEFINE_MODULE_SYMBOL(wifi_add_event_callback), - DEFINE_MODULE_SYMBOL(wifi_remove_event_callback), + DEFINE_MODULE_SYMBOL(wifi_event_subscribe), + DEFINE_MODULE_SYMBOL(wifi_event_unsubscribe), + DEFINE_MODULE_SYMBOL(wifi_event_poll), DEFINE_MODULE_SYMBOL(wifi_get_firmware_ops), DEFINE_MODULE_SYMBOL(WIFI_TYPE), // wifi_auto_scan @@ -471,6 +474,14 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = { DEFINE_MODULE_SYMBOL(event_group_clear), DEFINE_MODULE_SYMBOL(event_group_get), DEFINE_MODULE_SYMBOL(event_group_wait), + // concurrent/task_event_group + DEFINE_MODULE_SYMBOL(task_event_group_construct), + DEFINE_MODULE_SYMBOL(task_event_group_destruct), + DEFINE_MODULE_SYMBOL(task_event_group_claim_bit), + DEFINE_MODULE_SYMBOL(task_event_group_release_bit), + DEFINE_MODULE_SYMBOL(task_event_group_signal), + DEFINE_MODULE_SYMBOL(task_event_group_wait), + DEFINE_MODULE_SYMBOL(task_event_group_wait_any), // concurrent/thread DEFINE_MODULE_SYMBOL(thread_alloc), DEFINE_MODULE_SYMBOL(thread_alloc_full), diff --git a/TactilityKernel/source/system_event.cpp b/TactilityKernel/source/system_event.cpp index 73c07ffac..66beffbe0 100644 --- a/TactilityKernel/source/system_event.cpp +++ b/TactilityKernel/source/system_event.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include @@ -30,9 +29,9 @@ struct KernelEventMutex { static KernelEventMutex subscriptions_mutex; // Intrusive singly-linked list of poll subscriptions (system_event_subscribe()/_unsubscribe()/ -// _await()), separate from the callback-based `subscriptions` vector above. Guarded by its own -// mutex since notifying a poll subscriber never invokes caller code (just a memcpy and an -// xTaskNotifyGive), so there is no reentrancy concern requiring a snapshot-then-unlock dance. +// _poll()), separate from the callback-based `subscriptions` vector above. Guarded by its own +// mutex since notifying a poll subscriber never invokes caller code (just a memcpy and a +// task_event_group_signal), so there is no reentrancy concern requiring a snapshot-then-unlock dance. static SystemEventSubscription* poll_subscriptions = nullptr; static KernelEventMutex poll_subscriptions_mutex; @@ -68,9 +67,9 @@ error_t system_event_callback_remove( return result; } -// Copies `data` into every current poll subscriber of `type` and signals its wakeup semaphore. +// Copies `data` into every current poll subscriber of `type` and signals its wakeup bit. // Held entirely under the lock: unlike the callback path, this never invokes caller code -// (just a memcpy and a semaphore give), so there is nothing that could reenter and deadlock. +// (just a memcpy and a signal), so there is nothing that could reenter and deadlock. static void notify_poll_subscribers( SystemEventType type, uint64_t timestamp, @@ -88,7 +87,7 @@ static void notify_poll_subscribers( } sub->event.data_len = copied_len; sub->internal.sequence++; - xSemaphoreGive(sub->internal.semaphore); + task_event_group_signal(sub->internal.event_group, sub->bit); } } @@ -165,27 +164,11 @@ error_t system_event_emit( return ERROR_NONE; } -error_t system_event_subscribe(SystemEventSubscription* sub) { - // Wait out any system_event_unsubscribe() call still draining old awaiters for this same - // `sub` on another task (see internal.unsubscribe_in_progress). waiter_count/cancelled - // belong to `sub` itself, not to a given registration - reusing `sub` before that call - // finishes would reset them out from under it, and could hand out a fresh semaphore for it - // to then promptly delete instead of the old one, while an old awaiter is still blocked on - // the real old semaphore. - while (true) { - mutex_lock(&poll_subscriptions_mutex.handle); - bool busy = sub->internal.unsubscribe_in_progress; - mutex_unlock(&poll_subscriptions_mutex.handle); - - if (!busy) { - break; - } - delay_ticks(pdMS_TO_TICKS(10)); - } - - SemaphoreHandle_t semaphore = xSemaphoreCreateBinary(); - if (semaphore == nullptr) { - return ERROR_OUT_OF_MEMORY; +error_t system_event_subscribe(SystemEventSubscription* sub, TaskEventGroup* event_group) { + uint32_t bit; + error_t claim_result = task_event_group_claim_bit(event_group, &bit); + if (claim_result != ERROR_NONE) { + return claim_result; } mutex_lock(&poll_subscriptions_mutex.handle); @@ -196,15 +179,15 @@ error_t system_event_subscribe(SystemEventSubscription* sub) { for (SystemEventSubscription* existing = poll_subscriptions; existing != nullptr; existing = existing->internal.next) { if (existing == sub) { mutex_unlock(&poll_subscriptions_mutex.handle); - vSemaphoreDelete(semaphore); + task_event_group_release_bit(event_group, bit); return ERROR_INVALID_STATE; } } - sub->internal.semaphore = semaphore; + sub->internal.event_group = event_group; + sub->bit = bit; sub->internal.sequence = 0; sub->internal.consumed_sequence = 0; - sub->internal.waiter_count = 0; sub->internal.cancelled = false; sub->event.data_len = 0; sub->internal.next = poll_subscriptions; @@ -217,7 +200,6 @@ error_t system_event_subscribe(SystemEventSubscription* sub) { error_t system_event_unsubscribe(SystemEventSubscription* sub) { error_t result = ERROR_NOT_FOUND; - SemaphoreHandle_t semaphore_to_delete = nullptr; mutex_lock(&poll_subscriptions_mutex.handle); for (SystemEventSubscription** link = &poll_subscriptions; *link != nullptr; link = &(*link)->internal.next) { @@ -228,99 +210,34 @@ error_t system_event_unsubscribe(SystemEventSubscription* sub) { } } if (result == ERROR_NONE) { - // Unlinked first, so notify_poll_subscribers() can no longer reach this subscription. - // Mark it cancelled (checked by system_event_await()'s loop) and capture the semaphore - // handle into a local variable rather than deleting it via sub->internal.semaphore - // directly - a concurrent system_event_subscribe() re-registering this same `sub` after - // this point would overwrite that field with a freshly created semaphore, and we must - // not delete the wrong (newly active) one. + // Diagnostic only now (see @warning) - not relied on for safety. Best-effort nudge for a + // task that might still be blocked in task_event_group_wait() on this bit; unlike before, + // this does not wait for it to leave before the bit is released. sub->internal.cancelled = true; - // Blocks a concurrent system_event_subscribe() from reusing `sub` until this whole - // call returns - see internal.unsubscribe_in_progress and system_event_subscribe(). - sub->internal.unsubscribe_in_progress = true; - semaphore_to_delete = sub->internal.semaphore; - sub->internal.semaphore = nullptr; - } - mutex_unlock(&poll_subscriptions_mutex.handle); - - if (result != ERROR_NONE) { - return result; - } - - // Nudge any task already blocked in system_event_await() (it captured its own local copy - // of this same semaphore handle before this point, so it's unaffected by the field having - // just been cleared above) so it re-checks `cancelled` and bails out now instead of waiting - // out its full timeout, then wait for it to actually leave the semaphore before deleting it - // - FreeRTOS requires no task be blocked on a semaphore when it's deleted. - xSemaphoreGive(semaphore_to_delete); - while (true) { - mutex_lock(&poll_subscriptions_mutex.handle); - bool still_waiting = sub->internal.waiter_count > 0; - mutex_unlock(&poll_subscriptions_mutex.handle); - - if (!still_waiting) { - break; - } - delay_ticks(pdMS_TO_TICKS(10)); + task_event_group_signal(sub->internal.event_group, sub->bit); + task_event_group_release_bit(sub->internal.event_group, sub->bit); } - - vSemaphoreDelete(semaphore_to_delete); - - // Reset under the lock, together, as the last step - only past this point is `sub` safe - // for system_event_subscribe() to reuse (see internal.unsubscribe_in_progress and the - // busy-wait at the top of system_event_subscribe()). - mutex_lock(&poll_subscriptions_mutex.handle); - sub->internal.cancelled = false; - sub->internal.unsubscribe_in_progress = false; mutex_unlock(&poll_subscriptions_mutex.handle); - return ERROR_NONE; + return result; } -error_t system_event_await(SystemEventSubscription* sub, TickType_t timeout) { +error_t system_event_poll(SystemEventSubscription* sub) { mutex_lock(&poll_subscriptions_mutex.handle); - SemaphoreHandle_t semaphore = sub->internal.semaphore; - sub->internal.waiter_count++; - mutex_unlock(&poll_subscriptions_mutex.handle); - - error_t result = ERROR_NONE; - - // sequence/consumed_sequence are written by notify_poll_subscribers() under - // poll_subscriptions_mutex - read (and, on a match, updated) under the same lock each - // iteration, rather than compared lock-free, so a concurrent emit can't land between an - // unlocked read and this loop acting on it. - // - // Compare against consumed_sequence, not a sequence snapshot taken now - an emit that - // landed between system_event_subscribe() and this call already incremented sequence and - // gave the semaphore, so that event is pending but unconsumed. Snapshotting "now" would - // make the loop wait for yet another event instead of returning this already-pending one. - while (true) { - mutex_lock(&poll_subscriptions_mutex.handle); - bool pending = sub->internal.sequence != sub->internal.consumed_sequence; - bool cancelled = sub->internal.cancelled; - if (pending) { - sub->internal.consumed_sequence = sub->internal.sequence; - } - mutex_unlock(&poll_subscriptions_mutex.handle); - - if (pending) { - break; - } - if (cancelled) { - result = ERROR_INVALID_STATE; - break; - } - if (xSemaphoreTake(semaphore, timeout) == pdFALSE) { - result = ERROR_TIMEOUT; - break; - } + bool pending = sub->internal.sequence != sub->internal.consumed_sequence; + bool cancelled = sub->internal.cancelled; + if (pending) { + sub->internal.consumed_sequence = sub->internal.sequence; } - - mutex_lock(&poll_subscriptions_mutex.handle); - sub->internal.waiter_count--; mutex_unlock(&poll_subscriptions_mutex.handle); - return result; + if (pending) { + return ERROR_NONE; + } + if (cancelled) { + return ERROR_INVALID_STATE; + } + return ERROR_TIMEOUT; } error_t system_event_get_data(SystemEventSubscription* sub, uint8_t* data, size_t data_len) { diff --git a/TactilityKernel/tests/source/system_event_test.cpp b/TactilityKernel/tests/source/system_event_test.cpp index 698a7d855..4a46dca79 100644 --- a/TactilityKernel/tests/source/system_event_test.cpp +++ b/TactilityKernel/tests/source/system_event_test.cpp @@ -216,17 +216,20 @@ TEST_CASE("system_event_emit is safe when a callback subscribes, unsubscribes an system_event_callback_remove(KERNEL_EVENT_TIME_CHANGED, listener_b); } -// gps.h-style poll subscription: system_event_subscribe()/_await()/_unsubscribe(). +// gps.h-style poll subscription: system_event_subscribe()/_poll()/_unsubscribe(). // -// system_event_await() only detects sequence increments that happen *after* it starts -// waiting (same as gps_api_event_await()), so the emit must be started from another task -// while this one is already blocked in await() - emitting first and awaiting after would -// race the notification the same way it would with any FreeRTOS task-notify consumer. +// system_event_poll() only detects sequence increments that happened before it's called (same +// as gps_api_event_await()), so tests that need to observe an emit from another task block via +// task_event_group_wait() first - emitting before that wait started would race the notification +// the same way it would with any FreeRTOS task-notify consumer. + +TEST_CASE("system_event_subscribe/_poll deliver the event payload by value") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); -TEST_CASE("system_event_subscribe/_await deliver the event payload by value") { SystemEventSubscription sub {}; sub.event.type = KERNEL_EVENT_NETWORK_CONNECTED; - CHECK_EQ(system_event_subscribe(&sub), ERROR_NONE); + CHECK_EQ(system_event_subscribe(&sub, &event_group), ERROR_NONE); NetworkConnectedEvent connected { .device = nullptr, .ipv4_addr = 0x0A000001, .gateway = 0x0A0000FE }; auto* thread = thread_alloc_full( @@ -243,7 +246,8 @@ TEST_CASE("system_event_subscribe/_await deliver the event payload by value") { ); CHECK_EQ(thread_start(thread), ERROR_NONE); - CHECK_EQ(system_event_await(&sub, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(task_event_group_wait(&event_group, sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(system_event_poll(&sub), ERROR_NONE); NetworkConnectedEvent received {}; CHECK_EQ(system_event_get_data(&sub, reinterpret_cast(&received), sizeof(received)), ERROR_NONE); @@ -255,62 +259,78 @@ TEST_CASE("system_event_subscribe/_await deliver the event payload by value") { CHECK_EQ(system_event_unsubscribe(&sub), ERROR_NONE); CHECK_EQ(system_event_unsubscribe(&sub), ERROR_NOT_FOUND); + + task_event_group_destruct(&event_group); } -TEST_CASE("system_event_await returns a matching event that arrived before it started waiting") { +TEST_CASE("system_event_poll returns a matching event that arrived before it was called") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + SystemEventSubscription sub {}; sub.event.type = KERNEL_EVENT_NETWORK_CONNECTED; - CHECK_EQ(system_event_subscribe(&sub), ERROR_NONE); + CHECK_EQ(system_event_subscribe(&sub, &event_group), ERROR_NONE); - // Same-thread emit, no background thread needed: unlike the "detects a change after it - // starts waiting" tests above, this is exactly the case system_event_await() must handle - - // sequence already moved ahead of consumed_sequence before await() is even called. + // Same-thread emit, no background thread or wait needed: unlike the "detects a change after + // it starts waiting" tests above, this is exactly the case system_event_poll() must handle - + // sequence already moved ahead of consumed_sequence before poll() is even called. NetworkConnectedEvent connected { .device = nullptr, .ipv4_addr = 0x0A000001, .gateway = 0x0A0000FE }; CHECK_EQ(system_event_emit(KERNEL_EVENT_NETWORK_CONNECTED, &connected, sizeof(connected)), ERROR_NONE); - CHECK_EQ(system_event_await(&sub, 0), ERROR_NONE); + CHECK_EQ(system_event_poll(&sub), ERROR_NONE); NetworkConnectedEvent received {}; CHECK_EQ(system_event_get_data(&sub, reinterpret_cast(&received), sizeof(received)), ERROR_NONE); CHECK_EQ(received.ipv4_addr, connected.ipv4_addr); CHECK_EQ(received.gateway, connected.gateway); - // The pending event was consumed by the call above - a second await() with no further - // emit must time out rather than returning the same event again. - CHECK_EQ(system_event_await(&sub, 0), ERROR_TIMEOUT); + // The pending event was consumed by the call above - a second poll() with no further emit + // must time out rather than returning the same event again. + CHECK_EQ(system_event_poll(&sub), ERROR_TIMEOUT); system_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } -TEST_CASE("system_event_await times out when no matching event has arrived") { +TEST_CASE("system_event_poll times out when no matching event has arrived") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + SystemEventSubscription sub {}; sub.event.type = KERNEL_EVENT_TIME_CHANGED; - system_event_subscribe(&sub); + system_event_subscribe(&sub, &event_group); - CHECK_EQ(system_event_await(&sub, 0), ERROR_TIMEOUT); + CHECK_EQ(system_event_poll(&sub), ERROR_TIMEOUT); system_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } TEST_CASE("system_event_emit does not notify a poll subscriber of a different type") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + SystemEventSubscription sub {}; sub.event.type = KERNEL_EVENT_BOOT_COMPLETED; - system_event_subscribe(&sub); + system_event_subscribe(&sub, &event_group); system_event_emit(KERNEL_EVENT_TIME_CHANGED, nullptr, 0); - CHECK_EQ(system_event_await(&sub, 0), ERROR_TIMEOUT); + CHECK_EQ(system_event_poll(&sub), ERROR_TIMEOUT); system_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } TEST_CASE("system_event_get_data reports ERROR_BUFFER_OVERFLOW and leaves the buffer untouched") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + SystemEventSubscription sub {}; sub.event.type = KERNEL_EVENT_NETWORK_DISCONNECTED; - CHECK_EQ(system_event_subscribe(&sub), ERROR_NONE); + CHECK_EQ(system_event_subscribe(&sub, &event_group), ERROR_NONE); - // system_event_await() only detects sequence increments that happen *after* it starts - // waiting (see the comment above), so the emit must come from another task while this one - // is already blocked in await() - same pattern as the payload-delivery test above. + // Background emit + wait, same pattern as the payload-delivery test above - see the comment + // near the top of the file. NetworkDisconnectedEvent disconnected { .device = nullptr }; auto* thread = thread_alloc_full( "system-event-emitter", @@ -325,7 +345,8 @@ TEST_CASE("system_event_get_data reports ERROR_BUFFER_OVERFLOW and leaves the bu -1 ); CHECK_EQ(thread_start(thread), ERROR_NONE); - CHECK_EQ(system_event_await(&sub, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(task_event_group_wait(&event_group, sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(system_event_poll(&sub), ERROR_NONE); CHECK_EQ(thread_join(thread, pdMS_TO_TICKS(2000), pdMS_TO_TICKS(1)), ERROR_NONE); thread_free(thread); @@ -337,12 +358,16 @@ TEST_CASE("system_event_get_data reports ERROR_BUFFER_OVERFLOW and leaves the bu CHECK_EQ(system_event_get_data(&sub, exact, sizeof(exact)), ERROR_NONE); system_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } TEST_CASE("system_event_get_data on a subscription with no payload copies nothing and succeeds") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + SystemEventSubscription sub {}; sub.event.type = KERNEL_EVENT_BOOT_COMPLETED; - CHECK_EQ(system_event_subscribe(&sub), ERROR_NONE); + CHECK_EQ(system_event_subscribe(&sub, &event_group), ERROR_NONE); auto* thread = thread_alloc_full( "system-event-emitter", @@ -356,7 +381,8 @@ TEST_CASE("system_event_get_data on a subscription with no payload copies nothin -1 ); CHECK_EQ(thread_start(thread), ERROR_NONE); - CHECK_EQ(system_event_await(&sub, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(task_event_group_wait(&event_group, sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(system_event_poll(&sub), ERROR_NONE); CHECK_EQ(thread_join(thread, pdMS_TO_TICKS(2000), pdMS_TO_TICKS(1)), ERROR_NONE); thread_free(thread); @@ -365,59 +391,44 @@ TEST_CASE("system_event_get_data on a subscription with no payload copies nothin CHECK_EQ(buffer[0], 0x42); // untouched - nothing to copy system_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); } -// Regression coverage for system_event_unsubscribe() racing a task blocked in -// system_event_await() on the same subscription, and for reusing a subscription node after -// unsubscribing it - see the @warning on system_event_unsubscribe() in system_event.h. +// Regression coverage for system_event_unsubscribe()'s (now best-effort, not guaranteed - see +// its @warning in system_event.h) diagnostic signal, and for reusing a subscription node after +// unsubscribing it. + +TEST_CASE("system_event_poll returns ERROR_INVALID_STATE after unsubscribe (diagnostic, best-effort)") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); -TEST_CASE("system_event_unsubscribe wakes a task blocked in system_event_await with ERROR_INVALID_STATE") { SystemEventSubscription sub {}; sub.event.type = KERNEL_EVENT_SERVICE_STARTED; - CHECK_EQ(system_event_subscribe(&sub), ERROR_NONE); - - auto* thread = thread_alloc_full( - "system-event-awaiter", - 4096, - [](void* context) { - auto* awaited_sub = static_cast(context); - // Long timeout - the point is that unsubscribe() wakes this early, not that it - // eventually times out on its own. - return static_cast(system_event_await(awaited_sub, pdMS_TO_TICKS(5000))); - }, - &sub, - -1 - ); - CHECK_EQ(thread_start(thread), ERROR_NONE); - - // Give the awaiter task a moment to actually reach xSemaphoreTake() before unsubscribing - - // otherwise this test wouldn't exercise the "already blocked" race at all. - delay_millis(20); - - // Must return promptly (nudging the blocked awaiter awake), not by waiting out its timeout. - TickType_t before = get_ticks(); + CHECK_EQ(system_event_subscribe(&sub, &event_group), ERROR_NONE); CHECK_EQ(system_event_unsubscribe(&sub), ERROR_NONE); - CHECK_LT(get_ticks() - before, pdMS_TO_TICKS(1000)); - CHECK_EQ(thread_join(thread, pdMS_TO_TICKS(2000), pdMS_TO_TICKS(1)), ERROR_NONE); - CHECK_EQ(thread_get_return_code(thread), ERROR_INVALID_STATE); - thread_free(thread); + // `sub` is still caller-owned storage after unsubscribe - polling it directly (rather than + // still being blocked in task_event_group_wait() on it, which system_event_unsubscribe()'s + // @warning now says not to do) is the one remaining diagnostic case `cancelled` covers. + CHECK_EQ(system_event_poll(&sub), ERROR_INVALID_STATE); - // A second unsubscribe() has nothing left to do. - CHECK_EQ(system_event_unsubscribe(&sub), ERROR_NOT_FOUND); + task_event_group_destruct(&event_group); } TEST_CASE("a subscription node can be re-subscribed after system_event_unsubscribe") { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + SystemEventSubscription sub {}; sub.event.type = KERNEL_EVENT_SERVICE_STOPPED; - CHECK_EQ(system_event_subscribe(&sub), ERROR_NONE); + CHECK_EQ(system_event_subscribe(&sub, &event_group), ERROR_NONE); CHECK_EQ(system_event_unsubscribe(&sub), ERROR_NONE); // Re-registering the same node (same storage, not a fresh SystemEventSubscription) must - // work as if it were new - a fresh semaphore, and no leftover `cancelled` state from the + // work as if it were new - a fresh bit, and no leftover `cancelled` state from the // unsubscribe() above causing an immediate spurious ERROR_INVALID_STATE below. - CHECK_EQ(system_event_subscribe(&sub), ERROR_NONE); + CHECK_EQ(system_event_subscribe(&sub, &event_group), ERROR_NONE); auto* thread = thread_alloc_full( "system-event-emitter", @@ -431,9 +442,12 @@ TEST_CASE("a subscription node can be re-subscribed after system_event_unsubscri -1 ); CHECK_EQ(thread_start(thread), ERROR_NONE); - CHECK_EQ(system_event_await(&sub, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(task_event_group_wait(&event_group, sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE); + CHECK_EQ(system_event_poll(&sub), ERROR_NONE); CHECK_EQ(thread_join(thread, pdMS_TO_TICKS(2000), pdMS_TO_TICKS(1)), ERROR_NONE); thread_free(thread); CHECK_EQ(system_event_unsubscribe(&sub), ERROR_NONE); + + task_event_group_destruct(&event_group); } diff --git a/TactilityKernel/tests/source/task_event_group_test.cpp b/TactilityKernel/tests/source/task_event_group_test.cpp new file mode 100644 index 000000000..b26159bef --- /dev/null +++ b/TactilityKernel/tests/source/task_event_group_test.cpp @@ -0,0 +1,42 @@ +#include "doctest.h" + +#include + +TEST_CASE("task_event_group_wait_any wakes on any bit currently claimed in the group") { + TaskEventGroup group {}; + task_event_group_construct(&group); + + uint32_t bit_a, bit_b; + CHECK_EQ(task_event_group_claim_bit(&group, &bit_a), ERROR_NONE); + CHECK_EQ(task_event_group_claim_bit(&group, &bit_b), ERROR_NONE); + CHECK_NE(bit_a, bit_b); + + CHECK_EQ(task_event_group_signal(&group, bit_b), ERROR_NONE); + + uint32_t out_flags = 0; + CHECK_EQ(task_event_group_wait_any(&group, &out_flags, 0), ERROR_NONE); + CHECK_EQ(out_flags, bit_b); + + task_event_group_destruct(&group); +} + +TEST_CASE("task_event_group_wait_any times out immediately when the group has no claimed bits") { + TaskEventGroup group {}; + task_event_group_construct(&group); + + CHECK_EQ(task_event_group_wait_any(&group, nullptr, 0), ERROR_TIMEOUT); + + task_event_group_destruct(&group); +} + +TEST_CASE("task_event_group_wait_any times out when claimed bits exist but none are signalled") { + TaskEventGroup group {}; + task_event_group_construct(&group); + + uint32_t bit; + CHECK_EQ(task_event_group_claim_bit(&group, &bit), ERROR_NONE); + + CHECK_EQ(task_event_group_wait_any(&group, nullptr, 0), ERROR_TIMEOUT); + + task_event_group_destruct(&group); +} diff --git a/Tests/SdkIntegration/main/Source/main.c b/Tests/SdkIntegration/main/Source/main.c index 93c49bc75..a7fdb32c9 100644 --- a/Tests/SdkIntegration/main/Source/main.c +++ b/Tests/SdkIntegration/main/Source/main.c @@ -21,24 +21,30 @@ static void create_widgets(lv_obj_t* parent, void* userData) { int main(int argc, char* argv[]) { AppInstanceId app_instance_id = app_scheduler_current_app_id(); + struct TaskEventGroup event_group = {0}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub = { .app_instance_id = app_instance_id }; - app_event_subscribe(&sub); + app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(app_instance_id, create_widgets, NULL); bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, NULL, portMAX_DELAY); + struct AppEvent event; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_CLOSE) { - should_close = true; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + should_close = true; + break; + } } } window_manager_remove(window); app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } From c5ae4d15950a087e5f528e4334d9fe6011c48f88 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 24 Aug 2026 18:16:40 +0200 Subject: [PATCH 02/10] Fixes and improvements --- Modules/app-module/include/app/event.h | 14 ++++- Modules/app-module/source/event.cpp | 36 +++++++++++- Modules/app-module/source/symbols.cpp | 9 +-- .../source/drivers/esp32_wifi.cpp | 25 +++++++- .../source/drivers/mock_wifi.cpp | 57 ++++++++++++++----- Tactility/Source/Tactility.cpp | 3 +- Tactility/Source/app/apphub/AppHubApp.cpp | 26 +-------- Tactility/Source/app/applist/AppList.cpp | 23 +------- .../Source/app/appsettings/AppSettings.cpp | 26 +-------- .../Source/app/apwebserver/ApWebServer.cpp | 26 +-------- .../app/audiosettings/AudioSettings.cpp | 26 +-------- Tactility/Source/app/boot/Boot.cpp | 23 +------- Tactility/Source/app/btmanage/BtManage.cpp | 26 +-------- Tactility/Source/app/chat/ChatApp.cpp | 26 +-------- .../app/crashdiagnostics/CrashDiagnostics.cpp | 26 +-------- .../Source/app/development/Development.cpp | 26 +-------- .../KernelDisplay.cpp => display/Display.cpp} | 35 ++---------- .../app/fileselection/FileSelection.cpp | 23 +------- .../Source/app/i2cscanner/I2cScanner.cpp | 28 +-------- .../Source/app/imageviewer/ImageViewer.cpp | 26 +-------- .../Source/app/inputdialog/InputDialog.cpp | 23 +------- .../Source/app/keyboard/KeyboardSettings.cpp | 28 +-------- Tactility/Source/app/launcher/Launcher.cpp | 23 +------- .../app/localesettings/LocaleSettings.cpp | 26 +-------- Tactility/Source/app/power/Power.cpp | 26 +-------- Tactility/Source/app/poweroff/PowerOff.cpp | 26 +-------- .../Source/app/screenshot/Screenshot.cpp | 26 +-------- .../app/selectiondialog/SelectionDialog.cpp | 23 +------- Tactility/Source/app/settings/Settings.cpp | 23 +------- .../Source/app/systeminfo/SystemInfo.cpp | 26 +-------- Tactility/Source/app/timezone/TimeZone.cpp | 23 +------- .../app/touchcalibration/TouchCalibration.cpp | 26 +-------- .../app/trackball/TrackballSettings.cpp | 28 +-------- .../Source/app/usbsettings/UsbSettings.cpp | 26 +-------- .../webserversettings/WebServerSettings.cpp | 26 +-------- .../Source/app/wifimanage/WifiManage.cpp | 7 ++- Tactility/Source/service/wifi/Wifi.cpp | 17 +----- .../include/tactility/drivers/wifi.h | 6 +- TactilityKernel/source/system_event.cpp | 14 ++--- 39 files changed, 166 insertions(+), 767 deletions(-) rename Tactility/Source/app/{kerneldisplay/KernelDisplay.cpp => display/Display.cpp} (93%) diff --git a/Modules/app-module/include/app/event.h b/Modules/app-module/include/app/event.h index 2edd857e4..69316ce7e 100644 --- a/Modules/app-module/include/app/event.h +++ b/Modules/app-module/include/app/event.h @@ -84,7 +84,8 @@ struct AppEventSubscription { * task_event_group_wait()/task_event_group_wait_any() on this group (OR sub->bit into the mask, * or use _wait_any() to include every subscription sharing it), then drain with app_event_poll(). * @retval ERROR_NONE on success - * @retval ERROR_RESOURCE @a event_group has no free bits left to claim + * @retval ERROR_RESOURCE @a event_group has no free bits left to claim; @a sub was not registered + * @retval ERROR_INVALID_STATE @a sub is already registered */ error_t app_event_subscribe(struct AppEventSubscription* sub, struct TaskEventGroup* event_group); @@ -115,6 +116,17 @@ error_t app_event_emit(AppInstanceId app_instance_id, const struct AppEvent* eve */ error_t app_event_poll(struct AppEventSubscription* sub, struct AppEvent* out_event); +/** + * Blocks the calling app's task until it receives APP_EVENT_CLOSE, then returns. For apps that + * don't care about APP_EVENT_RESULT or any other app event - just "run until told to close". + * Owns its own subscription/event group for the call's duration (construct/subscribe on entry, + * unsubscribe/destruct before returning); does not interact with any subscription the caller + * manages itself. + * @warning Does not work in ISR context. Must be called from the app's own task (uses + * app_scheduler_current_app_id()). + */ +void app_event_loop_run(void); + #ifdef __cplusplus } #endif diff --git a/Modules/app-module/source/event.cpp b/Modules/app-module/source/event.cpp index e5c963c4f..053f5d963 100644 --- a/Modules/app-module/source/event.cpp +++ b/Modules/app-module/source/event.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include @@ -28,12 +29,19 @@ error_t app_event_subscribe(AppEventSubscription* sub, TaskEventGroup* event_gro return claim_result; } + mutex_lock(&subscriptions_mutex.handle); + + // Avoid cyclic subscription list that would loop forever + if (subscriptions == sub) { + mutex_unlock(&subscriptions_mutex.handle); + task_event_group_release_bit(event_group, bit); + return ERROR_INVALID_STATE; + } + sub->bit = bit; sub->internal.event_group = event_group; sub->internal.head = 0; sub->internal.count = 0; - - mutex_lock(&subscriptions_mutex.handle); sub->internal.next = subscriptions; subscriptions = sub; mutex_unlock(&subscriptions_mutex.handle); @@ -107,4 +115,28 @@ error_t app_event_poll(AppEventSubscription* sub, AppEvent* out_event) { return try_pop(sub, out_event) ? ERROR_NONE : ERROR_TIMEOUT; } +void app_event_loop_run(void) { + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = app_scheduler_current_app_id(); + app_event_subscribe(&sub, &event_group); + + bool should_close = false; + while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + should_close = true; + } + } + } + + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); +} + } // extern "C" diff --git a/Modules/app-module/source/symbols.cpp b/Modules/app-module/source/symbols.cpp index 9c79b5cb4..954ce3881 100644 --- a/Modules/app-module/source/symbols.cpp +++ b/Modules/app-module/source/symbols.cpp @@ -22,14 +22,7 @@ const ModuleSymbol app_module_symbols[] = { DEFINE_MODULE_SYMBOL(app_event_unsubscribe), DEFINE_MODULE_SYMBOL(app_event_emit), DEFINE_MODULE_SYMBOL(app_event_poll), - // tactility/concurrent/task_event_group - needed to construct the group app_event_subscribe() takes - DEFINE_MODULE_SYMBOL(task_event_group_construct), - DEFINE_MODULE_SYMBOL(task_event_group_destruct), - DEFINE_MODULE_SYMBOL(task_event_group_claim_bit), - DEFINE_MODULE_SYMBOL(task_event_group_release_bit), - DEFINE_MODULE_SYMBOL(task_event_group_signal), - DEFINE_MODULE_SYMBOL(task_event_group_wait), - DEFINE_MODULE_SYMBOL(task_event_group_wait_any), + DEFINE_MODULE_SYMBOL(app_event_loop_run), // app/install DEFINE_MODULE_SYMBOL(app_get_install_path), DEFINE_MODULE_SYMBOL(app_install), diff --git a/Platforms/platform-esp32/source/drivers/esp32_wifi.cpp b/Platforms/platform-esp32/source/drivers/esp32_wifi.cpp index 805cfff65..afae6d75a 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_wifi.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_wifi.cpp @@ -538,10 +538,18 @@ error_t api_event_subscribe(Device* device, WifiEventSubscription* sub, TaskEven if (claim_result != ERROR_NONE) { return claim_result; } - sub->internal.event_group = event_group; - sub->bit = bit; mutex_lock(&ctx->subscriptionsMutex); + + // Avoid cyclic subscription list that would loop forever + if (ctx->subscriptions == sub) { + mutex_unlock(&ctx->subscriptionsMutex); + task_event_group_release_bit(event_group, bit); + return ERROR_INVALID_STATE; + } + + sub->internal.event_group = event_group; + sub->bit = bit; sub->internal.next = ctx->subscriptions; ctx->subscriptions = sub; mutex_unlock(&ctx->subscriptionsMutex); @@ -629,6 +637,19 @@ error_t stop_device(Device* device) { bring_down_wifi(ctx); } + // Release any subscribers that never unsubscribed: device_stop() doesn't wait for apps still + // using this device, so a later wifi_event_unsubscribe() would find no ctx and skip releasing + // the bit and destructing sub->internal.ring_mutex. + mutex_lock(&ctx->subscriptionsMutex); + for (WifiEventSubscription* sub = ctx->subscriptions; sub != nullptr;) { + WifiEventSubscription* next = sub->internal.next; + task_event_group_release_bit(sub->internal.event_group, sub->bit); + mutex_destruct(&sub->internal.ring_mutex); + sub = next; + } + ctx->subscriptions = nullptr; + mutex_unlock(&ctx->subscriptionsMutex); + device_set_driver_data(device, nullptr); mutex_destruct(&ctx->subscriptionsMutex); mutex_destruct(&ctx->mutex); diff --git a/Platforms/platform-posix/source/drivers/mock_wifi.cpp b/Platforms/platform-posix/source/drivers/mock_wifi.cpp index 321bc7ac3..42c85e4dc 100644 --- a/Platforms/platform-posix/source/drivers/mock_wifi.cpp +++ b/Platforms/platform-posix/source/drivers/mock_wifi.cpp @@ -55,15 +55,15 @@ struct PosixWifiCtx { void fireEvent(PosixWifiCtx* ctx, WifiEvent event) { mutex_lock(&ctx->subscriptionsMutex); - for (WifiEventSubscription* sub = ctx->subscriptions; sub != nullptr; sub = sub->next) { - mutex_lock(&sub->ring_mutex); - if (sub->count < WIFI_EVENT_QUEUE_CAPACITY) { - uint8_t tail = (sub->head + sub->count) % WIFI_EVENT_QUEUE_CAPACITY; - sub->queue[tail] = event; - sub->count++; + for (WifiEventSubscription* sub = ctx->subscriptions; sub != nullptr; sub = sub->internal.next) { + mutex_lock(&sub->internal.ring_mutex); + if (sub->internal.count < WIFI_EVENT_QUEUE_CAPACITY) { + uint8_t tail = (sub->internal.head + sub->internal.count) % WIFI_EVENT_QUEUE_CAPACITY; + sub->internal.queue[tail] = event; + sub->internal.count++; } - mutex_unlock(&sub->ring_mutex); - task_event_group_signal(sub->event_group, sub->bit); + mutex_unlock(&sub->internal.ring_mutex); + task_event_group_signal(sub->internal.event_group, sub->bit); } mutex_unlock(&ctx->subscriptionsMutex); } @@ -266,12 +266,20 @@ error_t apiSetRadioOff(Device* device) { mutex_lock(&ctx->mutex); bool already_off = ctx->radioState == WIFI_RADIO_STATE_OFF; + bool was_connected = ctx->stationState != WIFI_STATION_STATE_DISCONNECTED; ctx->radioState = WIFI_RADIO_STATE_OFF; ctx->stationState = WIFI_STATION_STATE_DISCONNECTED; ctx->scanning = false; mutex_unlock(&ctx->mutex); if (already_off) return ERROR_NONE; + if (was_connected) { + WifiEvent station_event = {}; + station_event.type = WIFI_EVENT_TYPE_STATION_STATE_CHANGED; + station_event.station_state = WIFI_STATION_STATE_DISCONNECTED; + fireEvent(ctx, station_event); + } + WifiEvent radio_event = {}; radio_event.type = WIFI_EVENT_TYPE_RADIO_STATE_CHANGED; radio_event.radio_state = WIFI_RADIO_STATE_OFF; @@ -290,11 +298,19 @@ error_t apiEventSubscribe(Device* device, WifiEventSubscription* sub, TaskEventG if (claim_result != ERROR_NONE) { return claim_result; } - sub->event_group = event_group; - sub->bit = bit; mutex_lock(&ctx->subscriptionsMutex); - sub->next = ctx->subscriptions; + + // Avoid cyclic subscription list that would loop forever + if (ctx->subscriptions == sub) { + mutex_unlock(&ctx->subscriptionsMutex); + task_event_group_release_bit(event_group, bit); + return ERROR_INVALID_STATE; + } + + sub->internal.event_group = event_group; + sub->bit = bit; + sub->internal.next = ctx->subscriptions; ctx->subscriptions = sub; mutex_unlock(&ctx->subscriptionsMutex); return ERROR_NONE; @@ -306,9 +322,9 @@ error_t apiEventUnsubscribe(Device* device, WifiEventSubscription* sub) { error_t result = ERROR_NOT_FOUND; mutex_lock(&ctx->subscriptionsMutex); - for (WifiEventSubscription** link = &ctx->subscriptions; *link != nullptr; link = &(*link)->next) { + for (WifiEventSubscription** link = &ctx->subscriptions; *link != nullptr; link = &(*link)->internal.next) { if (*link == sub) { - *link = sub->next; + *link = sub->internal.next; result = ERROR_NONE; break; } @@ -316,7 +332,7 @@ error_t apiEventUnsubscribe(Device* device, WifiEventSubscription* sub) { mutex_unlock(&ctx->subscriptionsMutex); if (result == ERROR_NONE) { - task_event_group_release_bit(sub->event_group, sub->bit); + task_event_group_release_bit(sub->internal.event_group, sub->bit); } return result; } @@ -364,6 +380,19 @@ error_t stopDevice(Device* device) { apiSetRadioOff(device); } + // Release any subscribers that never unsubscribed: device_stop() doesn't wait for apps still + // using this device, so a later wifi_event_unsubscribe() would find no ctx and skip releasing + // the bit and destructing sub->internal.ring_mutex. + mutex_lock(&ctx->subscriptionsMutex); + for (WifiEventSubscription* sub = ctx->subscriptions; sub != nullptr;) { + WifiEventSubscription* next = sub->internal.next; + task_event_group_release_bit(sub->internal.event_group, sub->bit); + mutex_destruct(&sub->internal.ring_mutex); + sub = next; + } + ctx->subscriptions = nullptr; + mutex_unlock(&ctx->subscriptionsMutex); + device_set_driver_data(device, nullptr); mutex_destruct(&ctx->subscriptionsMutex); mutex_destruct(&ctx->mutex); diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 3fcb52a0f..74f151409 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -141,7 +141,6 @@ namespace app { namespace boot { extern const ::AppManifest manifest; } namespace development { extern const ::AppManifest manifest; } namespace display { extern const ::AppManifest manifest; } - namespace kerneldisplay { extern const ::AppManifest manifest; } namespace files { extern const ::AppManifest manifest; } namespace fileselection { extern const ::AppManifest manifest; } namespace gpssettings { extern const ::AppManifest manifest; } @@ -206,7 +205,7 @@ static void registerInternalApps() { app_manager_add(&app::audiosettings::manifest); } if (device_exists_of_type(&DISPLAY_TYPE)) { - app_manager_add(&app::kerneldisplay::manifest); + app_manager_add(&app::display::manifest); } app_manager_add(&app::files::manifest); app_manager_add(&app::fileselection::manifest); diff --git a/Tactility/Source/app/apphub/AppHubApp.cpp b/Tactility/Source/app/apphub/AppHubApp.cpp index 23e3e4095..17b4808c3 100644 --- a/Tactility/Source/app/apphub/AppHubApp.cpp +++ b/Tactility/Source/app/apphub/AppHubApp.cpp @@ -201,35 +201,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/applist/AppList.cpp b/Tactility/Source/app/applist/AppList.cpp index 36d07c76e..d6c2268e2 100644 --- a/Tactility/Source/app/applist/AppList.cpp +++ b/Tactility/Source/app/applist/AppList.cpp @@ -81,32 +81,11 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { appListInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); - while (true) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - bool shouldClose = false; - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - shouldClose = true; - break; - } - } - if (shouldClose) break; - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/appsettings/AppSettings.cpp b/Tactility/Source/app/appsettings/AppSettings.cpp index d2395d336..2f23a20f6 100644 --- a/Tactility/Source/app/appsettings/AppSettings.cpp +++ b/Tactility/Source/app/appsettings/AppSettings.cpp @@ -90,35 +90,11 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { appSettingsInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/apwebserver/ApWebServer.cpp b/Tactility/Source/app/apwebserver/ApWebServer.cpp index 9ece43edc..c352f8bcf 100644 --- a/Tactility/Source/app/apwebserver/ApWebServer.cpp +++ b/Tactility/Source/app/apwebserver/ApWebServer.cpp @@ -126,31 +126,9 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.appInstanceId = appInstanceId; ctx.wsSettings = settings::webserver::loadOrGetDefault(); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); // Equivalent of the old model's onHide(): persist the ORIGINAL settings (as loaded at // startup, not the temporary AP-mode config createWidgets() applied above) and revert the @@ -172,8 +150,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { }); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/audiosettings/AudioSettings.cpp b/Tactility/Source/app/audiosettings/AudioSettings.cpp index dd6219bfb..c5f54b125 100644 --- a/Tactility/Source/app/audiosettings/AudioSettings.cpp +++ b/Tactility/Source/app/audiosettings/AudioSettings.cpp @@ -215,31 +215,9 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); if (ctx.audioSubscription != nullptr) { service::audio::getPubsub()->unsubscribe(ctx.audioSubscription); @@ -247,8 +225,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index eb7c8dc53..3a2dd4c7b 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -292,13 +292,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { isUsbBootSplash = hal::usb::isUsbBootMode(); sdCardMissing = false; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - bootWindowId = window_manager_create(appInstanceId, createSplashWidgets, nullptr); runBootSequence(start_time); @@ -306,25 +299,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { // Waits until app_manager_start(launcher) (or a permanent stop) tells us to give up - // startNextApp() above is what triggers that, via app-module's "save the previously active // app" policy, unless sdCardMissing halted before it. - while (true) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - bool shouldClose = false; - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - shouldClose = true; - break; - } - } - if (shouldClose) break; - } + app_event_loop_run(); if (bootWindowId != 0) { window_manager_remove(bootWindowId); } - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/btmanage/BtManage.cpp b/Tactility/Source/app/btmanage/BtManage.cpp index 4b7d7b298..f97c0f8db 100644 --- a/Tactility/Source/app/btmanage/BtManage.cpp +++ b/Tactility/Source/app/btmanage/BtManage.cpp @@ -220,13 +220,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.state.updateScanResults(); ctx.state.updatePairedPeers(); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.btDevice = dev; @@ -244,22 +237,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { bluetooth_scan_start(dev); } - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); // Invalidate any BT event dispatched-but-not-yet-run for this instance before doing // anything else, so it can't race the teardown below (see onKernelBtEvent()). @@ -275,8 +253,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/chat/ChatApp.cpp b/Tactility/Source/app/chat/ChatApp.cpp index 170b53f5b..2dcc0d881 100644 --- a/Tactility/Source/app/chat/ChatApp.cpp +++ b/Tactility/Source/app/chat/ChatApp.cpp @@ -185,35 +185,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); service::espnow::unsubscribeReceiver(ctx.receiveSubscription); disableEspNow(&ctx); diff --git a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp index 5ef050dbc..30a3252f2 100644 --- a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp +++ b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp @@ -153,37 +153,13 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); if (!ctx.hasFatalError) { - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); } window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); bool continuePressed = ctx.continuePressed; diff --git a/Tactility/Source/app/development/Development.cpp b/Tactility/Source/app/development/Development.cpp index 86076fd3d..d1463531d 100644 --- a/Tactility/Source/app/development/Development.cpp +++ b/Tactility/Source/app/development/Development.cpp @@ -174,13 +174,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { return 0; } - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.timer = std::make_unique(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [&ctx, window] { @@ -198,22 +191,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { }); ctx.timer->start(); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); // Equivalent of the old model's onHide(): ensure the periodic update isn't already happening. lvgl_lock(); @@ -221,8 +199,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { lvgl_unlock(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/kerneldisplay/KernelDisplay.cpp b/Tactility/Source/app/display/Display.cpp similarity index 93% rename from Tactility/Source/app/kerneldisplay/KernelDisplay.cpp rename to Tactility/Source/app/display/Display.cpp index 24a56c701..2f177a7e3 100644 --- a/Tactility/Source/app/kerneldisplay/KernelDisplay.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -25,11 +24,11 @@ #include #endif -namespace tt::app::kerneldisplay { +namespace tt::app::display { extern const ::AppManifest manifest; -constexpr auto* TAG = "KernelDisplay"; +constexpr auto* TAG = "Display"; namespace { @@ -308,36 +307,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - persistIfUpdated(ctx); - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); + persistIfUpdated(ctx); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } @@ -351,4 +326,4 @@ extern const ::AppManifest manifest = { .location = { APP_LOCATION_MEMORY, reinterpret_cast(appMain) } }; -} // namespace tt::app::kerneldisplay +} // namespace tt::app::display diff --git a/Tactility/Source/app/fileselection/FileSelection.cpp b/Tactility/Source/app/fileselection/FileSelection.cpp index 3baba0c46..2071b211f 100644 --- a/Tactility/Source/app/fileselection/FileSelection.cpp +++ b/Tactility/Source/app/fileselection/FileSelection.cpp @@ -62,32 +62,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { app_event_emit(appInstanceId, &closeEvent); }); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - while (true) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - bool shouldClose = false; - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - shouldClose = true; - break; - } - } - if (shouldClose) break; - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index c4a18e93f..a5a521bb0 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -388,36 +388,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - stopScanningIfRunning(&ctx); - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); + stopScanningIfRunning(&ctx); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/imageviewer/ImageViewer.cpp b/Tactility/Source/app/imageviewer/ImageViewer.cpp index 175fca478..8f453f97c 100644 --- a/Tactility/Source/app/imageviewer/ImageViewer.cpp +++ b/Tactility/Source/app/imageviewer/ImageViewer.cpp @@ -89,35 +89,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.appInstanceId = appInstanceId; ctx.filePath = argv[0]; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/inputdialog/InputDialog.cpp b/Tactility/Source/app/inputdialog/InputDialog.cpp index 877f44027..a8af63987 100644 --- a/Tactility/Source/app/inputdialog/InputDialog.cpp +++ b/Tactility/Source/app/inputdialog/InputDialog.cpp @@ -111,32 +111,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.argc = argc; ctx.argv = argv; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - while (true) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - bool shouldClose = false; - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - shouldClose = true; - break; - } - } - if (shouldClose) break; - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/keyboard/KeyboardSettings.cpp b/Tactility/Source/app/keyboard/KeyboardSettings.cpp index 08ce740cd..6d60042ac 100644 --- a/Tactility/Source/app/keyboard/KeyboardSettings.cpp +++ b/Tactility/Source/app/keyboard/KeyboardSettings.cpp @@ -209,36 +209,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - persistIfUpdated(ctx); - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); + persistIfUpdated(ctx); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/launcher/Launcher.cpp b/Tactility/Source/app/launcher/Launcher.cpp index cb82386c6..a80fb61ba 100644 --- a/Tactility/Source/app/launcher/Launcher.cpp +++ b/Tactility/Source/app/launcher/Launcher.cpp @@ -234,34 +234,13 @@ void runAutoStart() { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { runAutoStart(); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); // The launcher is meant to stay resident (it's the home screen) - it only gives up its // thread when app-module's scheduler asks it to (e.g. another new-model app is started). - while (true) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - bool shouldClose = false; - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - shouldClose = true; - break; - } - } - if (shouldClose) break; - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/localesettings/LocaleSettings.cpp b/Tactility/Source/app/localesettings/LocaleSettings.cpp index 5c7d0c611..55b0ee59e 100644 --- a/Tactility/Source/app/localesettings/LocaleSettings.cpp +++ b/Tactility/Source/app/localesettings/LocaleSettings.cpp @@ -145,35 +145,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index 34130857e..80cc699e7 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -240,37 +240,13 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { updateUi(&ctx); }); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.timer->start(); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); ctx.timer->stop(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/poweroff/PowerOff.cpp b/Tactility/Source/app/poweroff/PowerOff.cpp index 5804f7104..8a00075af 100644 --- a/Tactility/Source/app/poweroff/PowerOff.cpp +++ b/Tactility/Source/app/poweroff/PowerOff.cpp @@ -136,35 +136,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index 6fbe17783..a39bce81b 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -250,39 +250,15 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } }); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); if (ctx.updateTimer->isRunning()) { ctx.updateTimer->stop(); } window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp index dff75bba1..107483404 100644 --- a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp +++ b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp @@ -104,32 +104,11 @@ int32_t appMain(AppInstanceId appInstanceId, int argc, char* argv[]) { ctx.argc = argc; ctx.argv = argv; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - while (true) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - bool shouldClose = false; - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - shouldClose = true; - break; - } - } - if (shouldClose) break; - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index 61239aae3..63e8d5f1a 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -79,32 +79,11 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { settingsInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); - while (true) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - bool shouldClose = false; - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - shouldClose = true; - break; - } - } - if (shouldClose) break; - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index db35843ce..cbb52b63e 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -417,39 +417,15 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { lvgl_unlock(); }); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.memoryTimer->start(); // Memory: every 10s ctx.tasksTimer->start(); // Tasks/CPU: every 15s - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); ctx.memoryTimer->stop(); ctx.tasksTimer->stop(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/timezone/TimeZone.cpp b/Tactility/Source/app/timezone/TimeZone.cpp index 277d7499d..cc52fe6c3 100644 --- a/Tactility/Source/app/timezone/TimeZone.cpp +++ b/Tactility/Source/app/timezone/TimeZone.cpp @@ -240,34 +240,13 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { updateList(&ctx); }); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.updateTimer->start(); - while (true) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - bool shouldClose = false; - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - shouldClose = true; - break; - } - } - if (shouldClose) break; - } + app_event_loop_run(); ctx.updateTimer->stop(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp index 56d4f77f4..988ce3cdf 100644 --- a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp +++ b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp @@ -233,35 +233,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } lvgl_unlock(); - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); // finishCalibration() already applied a new calibration on success. On cancel/failure, // restore whatever calibration was on disk before the block above cleared it. diff --git a/Tactility/Source/app/trackball/TrackballSettings.cpp b/Tactility/Source/app/trackball/TrackballSettings.cpp index 4040acb18..64e3a7529 100644 --- a/Tactility/Source/app/trackball/TrackballSettings.cpp +++ b/Tactility/Source/app/trackball/TrackballSettings.cpp @@ -272,36 +272,12 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - persistIfUpdated(ctx); - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); + persistIfUpdated(ctx); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/usbsettings/UsbSettings.cpp b/Tactility/Source/app/usbsettings/UsbSettings.cpp index e867489c1..044e0bb80 100644 --- a/Tactility/Source/app/usbsettings/UsbSettings.cpp +++ b/Tactility/Source/app/usbsettings/UsbSettings.cpp @@ -85,35 +85,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/webserversettings/WebServerSettings.cpp b/Tactility/Source/app/webserversettings/WebServerSettings.cpp index 563cb76bd..19b37eb2b 100644 --- a/Tactility/Source/app/webserversettings/WebServerSettings.cpp +++ b/Tactility/Source/app/webserversettings/WebServerSettings.cpp @@ -362,31 +362,9 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.wsSettings.webServerEnabled = service::webserver::isWebServerEnabled(); ctx.originalSettings = ctx.wsSettings; - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub, &event_group); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - bool shouldClose = false; - while (!shouldClose) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - switch (event.type) { - case APP_EVENT_CLOSE: - shouldClose = true; - break; - default: - break; - } - if (shouldClose) break; - } - } + app_event_loop_run(); // Equivalent of the old model's onHide(). if (ctx.updated) { @@ -427,8 +405,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index cbb5502cc..6cb12f184 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -72,11 +72,14 @@ static void onConnectToHidden() { } void updateView(Context* ctx) { - ctx->lock(); + // Same lock order as createWidgets() (called with the LVGL lock already held, per the + // window-manager's WindowCreateWidgetsFn contract, then acquiring ctx->mutex) - acquiring + // these in the opposite order here would deadlock against a concurrent createWidgets() call. lvgl_lock(); + ctx->lock(); ctx->view.update(); - lvgl_unlock(); ctx->unlock(); + lvgl_unlock(); } void onWifiEvent(Context* ctx, WifiEvent event) { diff --git a/Tactility/Source/service/wifi/Wifi.cpp b/Tactility/Source/service/wifi/Wifi.cpp index 3ca2e6f95..ac8989c26 100644 --- a/Tactility/Source/service/wifi/Wifi.cpp +++ b/Tactility/Source/service/wifi/Wifi.cpp @@ -229,18 +229,7 @@ void dispatchConnect() { LOG_I(TAG, "Connecting to %s", target.ssid.c_str()); - error_t result = wifi_station_connect(state.device, target.ssid.c_str(), target.password.c_str(), target.channel); - if (result != ERROR_NONE) { - LOG_E(TAG, "Failed to connect to %s (%s)", target.ssid.c_str(), error_to_string(result)); - WifiEvent event = {}; - event.type = WIFI_EVENT_TYPE_STATION_CONNECTION_RESULT; - // The driver couldn't even initiate the connection attempt; there's no - // more specific WifiStationConnectionError for that. - event.connection_error = WIFI_STATION_CONNECTION_ERROR_TIMEOUT; - publish(event); - } - // On success, WIFI_EVENT_TYPE_STATION_STATE_CHANGED / _CONNECTION_RESULT arrive - // asynchronously via onWifiDeviceEvent(). + wifi_station_connect(state.device, target.ssid.c_str(), target.password.c_str(), target.channel); } void dispatchDisconnect() { @@ -373,10 +362,6 @@ void onWifiDeviceEvent(Device* device, ::WifiEvent event) { default: break; } - - // Forward the event as-is: subscribers inspect event.type and the - // relevant union field directly, same as this function does. - publish(event); } void autoScanSetPaused(bool paused) { diff --git a/TactilityKernel/include/tactility/drivers/wifi.h b/TactilityKernel/include/tactility/drivers/wifi.h index 0caad93d9..78c7cff6a 100644 --- a/TactilityKernel/include/tactility/drivers/wifi.h +++ b/TactilityKernel/include/tactility/drivers/wifi.h @@ -245,7 +245,8 @@ struct WifiApi { * mask, or use _wait_any() to include every subscription sharing it), then drain with * wifi_event_poll(). * @retval ERROR_NONE on success - * @retval ERROR_RESOURCE @a event_group has no free bits left to claim + * @retval ERROR_RESOURCE @a event_group has no free bits left to claim; @a sub was not registered + * @retval ERROR_INVALID_STATE @a sub is already registered */ error_t (*event_subscribe)(struct Device* device, struct WifiEventSubscription* sub, struct TaskEventGroup* event_group); @@ -298,7 +299,8 @@ error_t wifi_station_get_rssi(struct Device* device, int32_t* rssi); * into the mask, or use _wait_any() to include every subscription sharing it), then drain with * wifi_event_poll(). * @retval ERROR_NONE on success - * @retval ERROR_RESOURCE @a event_group has no free bits left to claim + * @retval ERROR_RESOURCE @a event_group has no free bits left to claim; @a sub was not registered + * @retval ERROR_INVALID_STATE @a sub is already registered */ error_t wifi_event_subscribe(struct Device* device, struct WifiEventSubscription* sub, struct TaskEventGroup* event_group); diff --git a/TactilityKernel/source/system_event.cpp b/TactilityKernel/source/system_event.cpp index 66beffbe0..d77602a6f 100644 --- a/TactilityKernel/source/system_event.cpp +++ b/TactilityKernel/source/system_event.cpp @@ -173,15 +173,11 @@ error_t system_event_subscribe(SystemEventSubscription* sub, TaskEventGroup* eve mutex_lock(&poll_subscriptions_mutex.handle); - // Check-and-insert in one critical section: registering the same `sub` twice would link - // it into a list that already contains it, creating a cycle that notify_poll_subscribers() - // would then traverse forever while holding this same mutex. - for (SystemEventSubscription* existing = poll_subscriptions; existing != nullptr; existing = existing->internal.next) { - if (existing == sub) { - mutex_unlock(&poll_subscriptions_mutex.handle); - task_event_group_release_bit(event_group, bit); - return ERROR_INVALID_STATE; - } + // Avoid cyclic subscription list that would loop forever + if (poll_subscriptions == sub) { + mutex_unlock(&poll_subscriptions_mutex.handle); + task_event_group_release_bit(event_group, bit); + return ERROR_INVALID_STATE; } sub->internal.event_group = event_group; From 9fe4753993812d5bdf734b5ad764f4f1eeb67d82 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 24 Aug 2026 20:59:12 +0200 Subject: [PATCH 03/10] Revert app_event_loop_run() --- Documentation/ideas.md | 1 + Modules/app-module/include/app/event.h | 11 -------- Modules/app-module/source/event.cpp | 25 ----------------- Modules/app-module/source/symbols.cpp | 1 - Tactility/Source/app/apphub/AppHubApp.cpp | 26 ++++++++++++++++- Tactility/Source/app/applist/AppList.cpp | 23 ++++++++++++++- .../Source/app/appsettings/AppSettings.cpp | 26 ++++++++++++++++- .../Source/app/apwebserver/ApWebServer.cpp | 26 ++++++++++++++++- .../app/audiosettings/AudioSettings.cpp | 26 ++++++++++++++++- Tactility/Source/app/boot/Boot.cpp | 23 ++++++++++++++- Tactility/Source/app/btmanage/BtManage.cpp | 26 ++++++++++++++++- Tactility/Source/app/chat/ChatApp.cpp | 26 ++++++++++++++++- .../app/crashdiagnostics/CrashDiagnostics.cpp | 26 ++++++++++++++++- .../Source/app/development/Development.cpp | 26 ++++++++++++++++- Tactility/Source/app/display/Display.cpp | 28 +++++++++++++++++-- .../app/fileselection/FileSelection.cpp | 23 ++++++++++++++- .../Source/app/i2cscanner/I2cScanner.cpp | 28 +++++++++++++++++-- .../Source/app/imageviewer/ImageViewer.cpp | 26 ++++++++++++++++- .../Source/app/inputdialog/InputDialog.cpp | 23 ++++++++++++++- .../Source/app/keyboard/KeyboardSettings.cpp | 28 +++++++++++++++++-- Tactility/Source/app/launcher/Launcher.cpp | 23 ++++++++++++++- .../app/localesettings/LocaleSettings.cpp | 26 ++++++++++++++++- Tactility/Source/app/power/Power.cpp | 26 ++++++++++++++++- Tactility/Source/app/poweroff/PowerOff.cpp | 26 ++++++++++++++++- .../Source/app/screenshot/Screenshot.cpp | 26 ++++++++++++++++- .../app/selectiondialog/SelectionDialog.cpp | 23 ++++++++++++++- Tactility/Source/app/settings/Settings.cpp | 23 ++++++++++++++- .../Source/app/systeminfo/SystemInfo.cpp | 26 ++++++++++++++++- Tactility/Source/app/timezone/TimeZone.cpp | 23 ++++++++++++++- .../app/touchcalibration/TouchCalibration.cpp | 26 ++++++++++++++++- .../app/trackball/TrackballSettings.cpp | 28 +++++++++++++++++-- .../Source/app/usbsettings/UsbSettings.cpp | 26 ++++++++++++++++- .../webserversettings/WebServerSettings.cpp | 26 ++++++++++++++++- 33 files changed, 706 insertions(+), 70 deletions(-) diff --git a/Documentation/ideas.md b/Documentation/ideas.md index f88418540..99dd68e2d 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -11,6 +11,7 @@ ## Higher Priority +- stopAppFromToolbar() in Tactility.cpp stops the top-most app. Change it so the toolbar knows for which app id it is created, so it can rely on that. - Warn if file operations are done from prohibited tasks (e.g. lvgl task) - AppHubApp: Prevent download callbacks from accessing a destroyed view. - Move USB host task stacks to SPIRAM when available: esp32_usbhost*.cpp diff --git a/Modules/app-module/include/app/event.h b/Modules/app-module/include/app/event.h index 69316ce7e..bddc847f7 100644 --- a/Modules/app-module/include/app/event.h +++ b/Modules/app-module/include/app/event.h @@ -116,17 +116,6 @@ error_t app_event_emit(AppInstanceId app_instance_id, const struct AppEvent* eve */ error_t app_event_poll(struct AppEventSubscription* sub, struct AppEvent* out_event); -/** - * Blocks the calling app's task until it receives APP_EVENT_CLOSE, then returns. For apps that - * don't care about APP_EVENT_RESULT or any other app event - just "run until told to close". - * Owns its own subscription/event group for the call's duration (construct/subscribe on entry, - * unsubscribe/destruct before returning); does not interact with any subscription the caller - * manages itself. - * @warning Does not work in ISR context. Must be called from the app's own task (uses - * app_scheduler_current_app_id()). - */ -void app_event_loop_run(void); - #ifdef __cplusplus } #endif diff --git a/Modules/app-module/source/event.cpp b/Modules/app-module/source/event.cpp index 053f5d963..05606f1c8 100644 --- a/Modules/app-module/source/event.cpp +++ b/Modules/app-module/source/event.cpp @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include #include #include @@ -115,28 +114,4 @@ error_t app_event_poll(AppEventSubscription* sub, AppEvent* out_event) { return try_pop(sub, out_event) ? ERROR_NONE : ERROR_TIMEOUT; } -void app_event_loop_run(void) { - TaskEventGroup event_group {}; - task_event_group_construct(&event_group); - - AppEventSubscription sub {}; - sub.app_instance_id = app_scheduler_current_app_id(); - app_event_subscribe(&sub, &event_group); - - bool should_close = false; - while (!should_close) { - task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); - - AppEvent event {}; - while (app_event_poll(&sub, &event) == ERROR_NONE) { - if (event.type == APP_EVENT_CLOSE) { - should_close = true; - } - } - } - - app_event_unsubscribe(&sub); - task_event_group_destruct(&event_group); -} - } // extern "C" diff --git a/Modules/app-module/source/symbols.cpp b/Modules/app-module/source/symbols.cpp index 954ce3881..871f6b0fe 100644 --- a/Modules/app-module/source/symbols.cpp +++ b/Modules/app-module/source/symbols.cpp @@ -22,7 +22,6 @@ const ModuleSymbol app_module_symbols[] = { DEFINE_MODULE_SYMBOL(app_event_unsubscribe), DEFINE_MODULE_SYMBOL(app_event_emit), DEFINE_MODULE_SYMBOL(app_event_poll), - DEFINE_MODULE_SYMBOL(app_event_loop_run), // app/install DEFINE_MODULE_SYMBOL(app_get_install_path), DEFINE_MODULE_SYMBOL(app_install), diff --git a/Tactility/Source/app/apphub/AppHubApp.cpp b/Tactility/Source/app/apphub/AppHubApp.cpp index 17b4808c3..23e3e4095 100644 --- a/Tactility/Source/app/apphub/AppHubApp.cpp +++ b/Tactility/Source/app/apphub/AppHubApp.cpp @@ -201,11 +201,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/applist/AppList.cpp b/Tactility/Source/app/applist/AppList.cpp index d6c2268e2..36d07c76e 100644 --- a/Tactility/Source/app/applist/AppList.cpp +++ b/Tactility/Source/app/applist/AppList.cpp @@ -81,11 +81,32 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { appListInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); - app_event_loop_run(); + while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } + } + if (shouldClose) break; + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/appsettings/AppSettings.cpp b/Tactility/Source/app/appsettings/AppSettings.cpp index 2f23a20f6..d2395d336 100644 --- a/Tactility/Source/app/appsettings/AppSettings.cpp +++ b/Tactility/Source/app/appsettings/AppSettings.cpp @@ -90,11 +90,35 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { appSettingsInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/apwebserver/ApWebServer.cpp b/Tactility/Source/app/apwebserver/ApWebServer.cpp index c352f8bcf..9ece43edc 100644 --- a/Tactility/Source/app/apwebserver/ApWebServer.cpp +++ b/Tactility/Source/app/apwebserver/ApWebServer.cpp @@ -126,9 +126,31 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.appInstanceId = appInstanceId; ctx.wsSettings = settings::webserver::loadOrGetDefault(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } // Equivalent of the old model's onHide(): persist the ORIGINAL settings (as loaded at // startup, not the temporary AP-mode config createWidgets() applied above) and revert the @@ -150,6 +172,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { }); window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/audiosettings/AudioSettings.cpp b/Tactility/Source/app/audiosettings/AudioSettings.cpp index c5f54b125..dd6219bfb 100644 --- a/Tactility/Source/app/audiosettings/AudioSettings.cpp +++ b/Tactility/Source/app/audiosettings/AudioSettings.cpp @@ -215,9 +215,31 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } if (ctx.audioSubscription != nullptr) { service::audio::getPubsub()->unsubscribe(ctx.audioSubscription); @@ -225,6 +247,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index 3a2dd4c7b..eb7c8dc53 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -292,6 +292,13 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { isUsbBootSplash = hal::usb::isUsbBootMode(); sdCardMissing = false; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + bootWindowId = window_manager_create(appInstanceId, createSplashWidgets, nullptr); runBootSequence(start_time); @@ -299,11 +306,25 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { // Waits until app_manager_start(launcher) (or a permanent stop) tells us to give up - // startNextApp() above is what triggers that, via app-module's "save the previously active // app" policy, unless sdCardMissing halted before it. - app_event_loop_run(); + while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } + } + if (shouldClose) break; + } if (bootWindowId != 0) { window_manager_remove(bootWindowId); } + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/btmanage/BtManage.cpp b/Tactility/Source/app/btmanage/BtManage.cpp index f97c0f8db..4b7d7b298 100644 --- a/Tactility/Source/app/btmanage/BtManage.cpp +++ b/Tactility/Source/app/btmanage/BtManage.cpp @@ -220,6 +220,13 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.state.updateScanResults(); ctx.state.updatePairedPeers(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.btDevice = dev; @@ -237,7 +244,22 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { bluetooth_scan_start(dev); } - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } // Invalidate any BT event dispatched-but-not-yet-run for this instance before doing // anything else, so it can't race the teardown below (see onKernelBtEvent()). @@ -253,6 +275,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/chat/ChatApp.cpp b/Tactility/Source/app/chat/ChatApp.cpp index 2dcc0d881..170b53f5b 100644 --- a/Tactility/Source/app/chat/ChatApp.cpp +++ b/Tactility/Source/app/chat/ChatApp.cpp @@ -185,11 +185,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); service::espnow::unsubscribeReceiver(ctx.receiveSubscription); disableEspNow(&ctx); diff --git a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp index 30a3252f2..5ef050dbc 100644 --- a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp +++ b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp @@ -153,13 +153,37 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); if (!ctx.hasFatalError) { - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); bool continuePressed = ctx.continuePressed; diff --git a/Tactility/Source/app/development/Development.cpp b/Tactility/Source/app/development/Development.cpp index d1463531d..86076fd3d 100644 --- a/Tactility/Source/app/development/Development.cpp +++ b/Tactility/Source/app/development/Development.cpp @@ -174,6 +174,13 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { return 0; } + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.timer = std::make_unique(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [&ctx, window] { @@ -191,7 +198,22 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { }); ctx.timer->start(); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } // Equivalent of the old model's onHide(): ensure the periodic update isn't already happening. lvgl_lock(); @@ -199,6 +221,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { lvgl_unlock(); window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index 2f177a7e3..08c768ae0 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -307,12 +307,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); - persistIfUpdated(ctx); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + persistIfUpdated(ctx); + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/fileselection/FileSelection.cpp b/Tactility/Source/app/fileselection/FileSelection.cpp index 2071b211f..3baba0c46 100644 --- a/Tactility/Source/app/fileselection/FileSelection.cpp +++ b/Tactility/Source/app/fileselection/FileSelection.cpp @@ -62,11 +62,32 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { app_event_emit(appInstanceId, &closeEvent); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } + } + if (shouldClose) break; + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index a5a521bb0..c4a18e93f 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -388,12 +388,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); - stopScanningIfRunning(&ctx); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + stopScanningIfRunning(&ctx); + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/imageviewer/ImageViewer.cpp b/Tactility/Source/app/imageviewer/ImageViewer.cpp index 8f453f97c..175fca478 100644 --- a/Tactility/Source/app/imageviewer/ImageViewer.cpp +++ b/Tactility/Source/app/imageviewer/ImageViewer.cpp @@ -89,11 +89,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.appInstanceId = appInstanceId; ctx.filePath = argv[0]; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/inputdialog/InputDialog.cpp b/Tactility/Source/app/inputdialog/InputDialog.cpp index a8af63987..877f44027 100644 --- a/Tactility/Source/app/inputdialog/InputDialog.cpp +++ b/Tactility/Source/app/inputdialog/InputDialog.cpp @@ -111,11 +111,32 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.argc = argc; ctx.argv = argv; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } + } + if (shouldClose) break; + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/keyboard/KeyboardSettings.cpp b/Tactility/Source/app/keyboard/KeyboardSettings.cpp index 6d60042ac..08ce740cd 100644 --- a/Tactility/Source/app/keyboard/KeyboardSettings.cpp +++ b/Tactility/Source/app/keyboard/KeyboardSettings.cpp @@ -209,12 +209,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); - persistIfUpdated(ctx); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + persistIfUpdated(ctx); + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/launcher/Launcher.cpp b/Tactility/Source/app/launcher/Launcher.cpp index a80fb61ba..cb82386c6 100644 --- a/Tactility/Source/app/launcher/Launcher.cpp +++ b/Tactility/Source/app/launcher/Launcher.cpp @@ -234,13 +234,34 @@ void runAutoStart() { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { runAutoStart(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); // The launcher is meant to stay resident (it's the home screen) - it only gives up its // thread when app-module's scheduler asks it to (e.g. another new-model app is started). - app_event_loop_run(); + while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } + } + if (shouldClose) break; + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/localesettings/LocaleSettings.cpp b/Tactility/Source/app/localesettings/LocaleSettings.cpp index 55b0ee59e..5c7d0c611 100644 --- a/Tactility/Source/app/localesettings/LocaleSettings.cpp +++ b/Tactility/Source/app/localesettings/LocaleSettings.cpp @@ -145,11 +145,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index 80cc699e7..34130857e 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -240,13 +240,37 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { updateUi(&ctx); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.timer->start(); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } ctx.timer->stop(); window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/poweroff/PowerOff.cpp b/Tactility/Source/app/poweroff/PowerOff.cpp index 8a00075af..5804f7104 100644 --- a/Tactility/Source/app/poweroff/PowerOff.cpp +++ b/Tactility/Source/app/poweroff/PowerOff.cpp @@ -136,11 +136,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index a39bce81b..6fbe17783 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -250,15 +250,39 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } if (ctx.updateTimer->isRunning()) { ctx.updateTimer->stop(); } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp index 107483404..dff75bba1 100644 --- a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp +++ b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp @@ -104,11 +104,32 @@ int32_t appMain(AppInstanceId appInstanceId, int argc, char* argv[]) { ctx.argc = argc; ctx.argv = argv; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } + } + if (shouldClose) break; + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index 63e8d5f1a..61239aae3 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -79,11 +79,32 @@ void createWidgets(lv_obj_t* parent, void*) { int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { settingsInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); - app_event_loop_run(); + while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } + } + if (shouldClose) break; + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index cbb52b63e..db35843ce 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -417,15 +417,39 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { lvgl_unlock(); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.memoryTimer->start(); // Memory: every 10s ctx.tasksTimer->start(); // Tasks/CPU: every 15s - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } ctx.memoryTimer->stop(); ctx.tasksTimer->stop(); window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/timezone/TimeZone.cpp b/Tactility/Source/app/timezone/TimeZone.cpp index cc52fe6c3..277d7499d 100644 --- a/Tactility/Source/app/timezone/TimeZone.cpp +++ b/Tactility/Source/app/timezone/TimeZone.cpp @@ -240,13 +240,34 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { updateList(&ctx); }); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.updateTimer->start(); - app_event_loop_run(); + while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool shouldClose = false; + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + shouldClose = true; + break; + } + } + if (shouldClose) break; + } ctx.updateTimer->stop(); window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return ctx.result; } diff --git a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp index 988ce3cdf..56d4f77f4 100644 --- a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp +++ b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp @@ -233,11 +233,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } lvgl_unlock(); + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); // finishCalibration() already applied a new calibration on success. On cancel/failure, // restore whatever calibration was on disk before the block above cleared it. diff --git a/Tactility/Source/app/trackball/TrackballSettings.cpp b/Tactility/Source/app/trackball/TrackballSettings.cpp index 64e3a7529..4040acb18 100644 --- a/Tactility/Source/app/trackball/TrackballSettings.cpp +++ b/Tactility/Source/app/trackball/TrackballSettings.cpp @@ -272,12 +272,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); - persistIfUpdated(ctx); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + persistIfUpdated(ctx); + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/usbsettings/UsbSettings.cpp b/Tactility/Source/app/usbsettings/UsbSettings.cpp index 044e0bb80..e867489c1 100644 --- a/Tactility/Source/app/usbsettings/UsbSettings.cpp +++ b/Tactility/Source/app/usbsettings/UsbSettings.cpp @@ -85,11 +85,35 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = appInstanceId; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/webserversettings/WebServerSettings.cpp b/Tactility/Source/app/webserversettings/WebServerSettings.cpp index 19b37eb2b..563cb76bd 100644 --- a/Tactility/Source/app/webserversettings/WebServerSettings.cpp +++ b/Tactility/Source/app/webserversettings/WebServerSettings.cpp @@ -362,9 +362,31 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { ctx.wsSettings.webServerEnabled = service::webserver::isWebServerEnabled(); ctx.originalSettings = ctx.wsSettings; + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + sub.app_instance_id = appInstanceId; + app_event_subscribe(&sub, &event_group); + WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); - app_event_loop_run(); + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } // Equivalent of the old model's onHide(). if (ctx.updated) { @@ -405,6 +427,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } window_manager_remove(window); + app_event_unsubscribe(&sub); + task_event_group_destruct(&event_group); return 0; } From 5f41c7d6426a3f986f0dc6569394482b99d2b66c Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 24 Aug 2026 21:17:12 +0200 Subject: [PATCH 04/10] Fix for wifi toggle off not working and added device configs for wifi --- Devices/btt-panda-touch/bigtreetech,panda-touch.dts | 1 - Devices/cyd-2432s024c/cyd,2432s024c.dts | 1 - Devices/cyd-2432s024r/cyd,2432s024r.dts | 1 - Devices/cyd-2432s028r/cyd,2432s028r.dts | 1 - Devices/cyd-2432s028rv3/cyd,2432s028rv3.dts | 1 - Devices/cyd-2432s032c/cyd,2432s032c.dts | 1 - Devices/cyd-3248s035c/cyd,3248s035c.dts | 1 - Devices/cyd-4848s040c/cyd,4848s040c.dts | 1 - Devices/cyd-8048s043c/cyd,8048s043c.dts | 1 - Devices/cyd-e32r28t/cyd,e32r28t.dts | 1 - Devices/cyd-e32r32p/cyd,e32r32p.dts | 1 - .../elecrow,crowpanel-advance-28.dts | 1 - .../elecrow,crowpanel-advance-35.dts | 1 - .../elecrow,crowpanel-advance-50.dts | 1 - .../elecrow,crowpanel-basic-28.dts | 1 - .../elecrow,crowpanel-basic-35.dts | 1 - .../elecrow,crowpanel-basic-50.dts | 1 - Devices/es3c28p/es3c28p.dts | 1 - Devices/generic-esp32/generic,esp32.dts | 1 - Devices/generic-esp32c6/generic,esp32c6.dts | 1 - Devices/generic-esp32s3/generic,esp32s3.dts | 1 - Devices/guition-jc2432w328c/guition,jc2432w328c.dts | 1 - Devices/guition-jc8048w550c/guition,jc8048w550c.dts | 1 - Devices/heltec-wifi-lora-32-v3/heltec,wifi-lora-32-v3.dts | 1 - Devices/lilygo-tdeck-max/lilygo,tdeck-max.dts | 1 - Devices/lilygo-tdeck-plus/lilygo,tdeck-plus.dts | 1 - Devices/lilygo-tdeck-pro/lilygo,tdeck-pro.dts | 1 - Devices/lilygo-tdeck/lilygo,tdeck.dts | 1 - Devices/lilygo-tdisplay-s3/lilygo,tdisplay-s3.dts | 1 - Devices/lilygo-tdisplay/lilygo,tdisplay.dts | 1 - Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts | 1 - Devices/lilygo-thmi/lilygo,thmi.dts | 1 - Devices/lilygo-tlora-pager/lilygo,tlora-pager.dts | 1 - Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts | 1 - Devices/m5stack-cardputer/m5stack,cardputer.dts | 1 - Devices/m5stack-core2/m5stack,core2.dts | 1 - Devices/m5stack-cores3/m5stack,cores3.dts | 1 - Devices/m5stack-papers3/m5stack,papers3.dts | 1 - Devices/m5stack-stackchan/m5stack,stackchan.dts | 1 - Devices/m5stack-stickc-plus2/m5stack,stickc-plus2.dts | 1 - Devices/m5stack-sticks3/m5stack,sticks3.dts | 1 - Devices/tulip-4r11/tulip-4r11.dts | 1 - Devices/unphone/unphone.dts | 1 - Devices/waveshare-esp32-s3-geek/waveshare,esp32-s3-geek.dts | 1 - Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts | 1 - .../waveshare,s3-touch-lcd-128.dts | 1 - .../waveshare,s3-touch-lcd-147.dts | 1 - .../waveshare-s3-touch-lcd-43/waveshare,s3-touch-lcd-43.dts | 1 - .../wireless-tag,wt32-sc01-plus.dts | 1 - Tactility/Source/service/wifi/Wifi.cpp | 6 ++++-- 50 files changed, 4 insertions(+), 51 deletions(-) diff --git a/Devices/btt-panda-touch/bigtreetech,panda-touch.dts b/Devices/btt-panda-touch/bigtreetech,panda-touch.dts index d106f1ce4..e6995484d 100644 --- a/Devices/btt-panda-touch/bigtreetech,panda-touch.dts +++ b/Devices/btt-panda-touch/bigtreetech,panda-touch.dts @@ -18,7 +18,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/cyd-2432s024c/cyd,2432s024c.dts b/Devices/cyd-2432s024c/cyd,2432s024c.dts index dadaf0bc6..eafea7ac4 100644 --- a/Devices/cyd-2432s024c/cyd,2432s024c.dts +++ b/Devices/cyd-2432s024c/cyd,2432s024c.dts @@ -18,7 +18,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/cyd-2432s024r/cyd,2432s024r.dts b/Devices/cyd-2432s024r/cyd,2432s024r.dts index 06f7c9741..179628783 100644 --- a/Devices/cyd-2432s024r/cyd,2432s024r.dts +++ b/Devices/cyd-2432s024r/cyd,2432s024r.dts @@ -18,7 +18,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/cyd-2432s028r/cyd,2432s028r.dts b/Devices/cyd-2432s028r/cyd,2432s028r.dts index 9aa1dcefb..443a3f42e 100644 --- a/Devices/cyd-2432s028r/cyd,2432s028r.dts +++ b/Devices/cyd-2432s028r/cyd,2432s028r.dts @@ -19,7 +19,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/cyd-2432s028rv3/cyd,2432s028rv3.dts b/Devices/cyd-2432s028rv3/cyd,2432s028rv3.dts index 2b1efc462..93aaf1731 100644 --- a/Devices/cyd-2432s028rv3/cyd,2432s028rv3.dts +++ b/Devices/cyd-2432s028rv3/cyd,2432s028rv3.dts @@ -19,7 +19,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/cyd-2432s032c/cyd,2432s032c.dts b/Devices/cyd-2432s032c/cyd,2432s032c.dts index 940b9ac50..563e73726 100644 --- a/Devices/cyd-2432s032c/cyd,2432s032c.dts +++ b/Devices/cyd-2432s032c/cyd,2432s032c.dts @@ -18,7 +18,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/cyd-3248s035c/cyd,3248s035c.dts b/Devices/cyd-3248s035c/cyd,3248s035c.dts index dd8e8647c..8718e54b3 100644 --- a/Devices/cyd-3248s035c/cyd,3248s035c.dts +++ b/Devices/cyd-3248s035c/cyd,3248s035c.dts @@ -19,7 +19,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/cyd-4848s040c/cyd,4848s040c.dts b/Devices/cyd-4848s040c/cyd,4848s040c.dts index 6a5471b2f..b8198b5a2 100644 --- a/Devices/cyd-4848s040c/cyd,4848s040c.dts +++ b/Devices/cyd-4848s040c/cyd,4848s040c.dts @@ -18,7 +18,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/cyd-8048s043c/cyd,8048s043c.dts b/Devices/cyd-8048s043c/cyd,8048s043c.dts index 71b477695..eecd8949b 100644 --- a/Devices/cyd-8048s043c/cyd,8048s043c.dts +++ b/Devices/cyd-8048s043c/cyd,8048s043c.dts @@ -19,7 +19,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/cyd-e32r28t/cyd,e32r28t.dts b/Devices/cyd-e32r28t/cyd,e32r28t.dts index e5e111e41..53457ad16 100644 --- a/Devices/cyd-e32r28t/cyd,e32r28t.dts +++ b/Devices/cyd-e32r28t/cyd,e32r28t.dts @@ -17,7 +17,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/cyd-e32r32p/cyd,e32r32p.dts b/Devices/cyd-e32r32p/cyd,e32r32p.dts index 2953bf9d3..51a8fa564 100644 --- a/Devices/cyd-e32r32p/cyd,e32r32p.dts +++ b/Devices/cyd-e32r32p/cyd,e32r32p.dts @@ -20,7 +20,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/elecrow-crowpanel-advance-28/elecrow,crowpanel-advance-28.dts b/Devices/elecrow-crowpanel-advance-28/elecrow,crowpanel-advance-28.dts index 78db1398e..919c72db3 100644 --- a/Devices/elecrow-crowpanel-advance-28/elecrow,crowpanel-advance-28.dts +++ b/Devices/elecrow-crowpanel-advance-28/elecrow,crowpanel-advance-28.dts @@ -20,7 +20,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/elecrow-crowpanel-advance-35/elecrow,crowpanel-advance-35.dts b/Devices/elecrow-crowpanel-advance-35/elecrow,crowpanel-advance-35.dts index a687b01c6..810a5209d 100644 --- a/Devices/elecrow-crowpanel-advance-35/elecrow,crowpanel-advance-35.dts +++ b/Devices/elecrow-crowpanel-advance-35/elecrow,crowpanel-advance-35.dts @@ -20,7 +20,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/elecrow-crowpanel-advance-50/elecrow,crowpanel-advance-50.dts b/Devices/elecrow-crowpanel-advance-50/elecrow,crowpanel-advance-50.dts index 1a0f93ba8..a9e168832 100644 --- a/Devices/elecrow-crowpanel-advance-50/elecrow,crowpanel-advance-50.dts +++ b/Devices/elecrow-crowpanel-advance-50/elecrow,crowpanel-advance-50.dts @@ -20,7 +20,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/elecrow-crowpanel-basic-28/elecrow,crowpanel-basic-28.dts b/Devices/elecrow-crowpanel-basic-28/elecrow,crowpanel-basic-28.dts index 5c04fcab3..77e07953a 100644 --- a/Devices/elecrow-crowpanel-basic-28/elecrow,crowpanel-basic-28.dts +++ b/Devices/elecrow-crowpanel-basic-28/elecrow,crowpanel-basic-28.dts @@ -18,7 +18,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/elecrow-crowpanel-basic-35/elecrow,crowpanel-basic-35.dts b/Devices/elecrow-crowpanel-basic-35/elecrow,crowpanel-basic-35.dts index de27790aa..f5ab1aeca 100644 --- a/Devices/elecrow-crowpanel-basic-35/elecrow,crowpanel-basic-35.dts +++ b/Devices/elecrow-crowpanel-basic-35/elecrow,crowpanel-basic-35.dts @@ -18,7 +18,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/elecrow-crowpanel-basic-50/elecrow,crowpanel-basic-50.dts b/Devices/elecrow-crowpanel-basic-50/elecrow,crowpanel-basic-50.dts index ebb9e0b84..085d8ed6c 100644 --- a/Devices/elecrow-crowpanel-basic-50/elecrow,crowpanel-basic-50.dts +++ b/Devices/elecrow-crowpanel-basic-50/elecrow,crowpanel-basic-50.dts @@ -20,7 +20,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/es3c28p/es3c28p.dts b/Devices/es3c28p/es3c28p.dts index 75d5ac1dc..6aca63899 100644 --- a/Devices/es3c28p/es3c28p.dts +++ b/Devices/es3c28p/es3c28p.dts @@ -24,7 +24,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/generic-esp32/generic,esp32.dts b/Devices/generic-esp32/generic,esp32.dts index 3e49b6c50..84fc39d4f 100644 --- a/Devices/generic-esp32/generic,esp32.dts +++ b/Devices/generic-esp32/generic,esp32.dts @@ -11,7 +11,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/generic-esp32c6/generic,esp32c6.dts b/Devices/generic-esp32c6/generic,esp32c6.dts index fef95675b..7c5254acb 100644 --- a/Devices/generic-esp32c6/generic,esp32c6.dts +++ b/Devices/generic-esp32c6/generic,esp32c6.dts @@ -11,7 +11,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/generic-esp32s3/generic,esp32s3.dts b/Devices/generic-esp32s3/generic,esp32s3.dts index 8443fcf47..eba13b006 100644 --- a/Devices/generic-esp32s3/generic,esp32s3.dts +++ b/Devices/generic-esp32s3/generic,esp32s3.dts @@ -11,7 +11,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/guition-jc2432w328c/guition,jc2432w328c.dts b/Devices/guition-jc2432w328c/guition,jc2432w328c.dts index 138df637c..3fa69997c 100644 --- a/Devices/guition-jc2432w328c/guition,jc2432w328c.dts +++ b/Devices/guition-jc2432w328c/guition,jc2432w328c.dts @@ -19,7 +19,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/guition-jc8048w550c/guition,jc8048w550c.dts b/Devices/guition-jc8048w550c/guition,jc8048w550c.dts index 3be39b67a..df755b847 100644 --- a/Devices/guition-jc8048w550c/guition,jc8048w550c.dts +++ b/Devices/guition-jc8048w550c/guition,jc8048w550c.dts @@ -21,7 +21,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/heltec-wifi-lora-32-v3/heltec,wifi-lora-32-v3.dts b/Devices/heltec-wifi-lora-32-v3/heltec,wifi-lora-32-v3.dts index db8be2bf2..9d8a983e9 100644 --- a/Devices/heltec-wifi-lora-32-v3/heltec,wifi-lora-32-v3.dts +++ b/Devices/heltec-wifi-lora-32-v3/heltec,wifi-lora-32-v3.dts @@ -18,7 +18,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/lilygo-tdeck-max/lilygo,tdeck-max.dts b/Devices/lilygo-tdeck-max/lilygo,tdeck-max.dts index d8ca0f0ab..64bd0d874 100644 --- a/Devices/lilygo-tdeck-max/lilygo,tdeck-max.dts +++ b/Devices/lilygo-tdeck-max/lilygo,tdeck-max.dts @@ -26,7 +26,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/lilygo-tdeck-plus/lilygo,tdeck-plus.dts b/Devices/lilygo-tdeck-plus/lilygo,tdeck-plus.dts index 5a0476c92..52354d01a 100644 --- a/Devices/lilygo-tdeck-plus/lilygo,tdeck-plus.dts +++ b/Devices/lilygo-tdeck-plus/lilygo,tdeck-plus.dts @@ -48,7 +48,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/lilygo-tdeck-pro/lilygo,tdeck-pro.dts b/Devices/lilygo-tdeck-pro/lilygo,tdeck-pro.dts index a8db46f48..eed3ea5d8 100644 --- a/Devices/lilygo-tdeck-pro/lilygo,tdeck-pro.dts +++ b/Devices/lilygo-tdeck-pro/lilygo,tdeck-pro.dts @@ -23,7 +23,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/lilygo-tdeck/lilygo,tdeck.dts b/Devices/lilygo-tdeck/lilygo,tdeck.dts index 9c4bd34dd..be3d027a1 100644 --- a/Devices/lilygo-tdeck/lilygo,tdeck.dts +++ b/Devices/lilygo-tdeck/lilygo,tdeck.dts @@ -46,7 +46,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/lilygo-tdisplay-s3/lilygo,tdisplay-s3.dts b/Devices/lilygo-tdisplay-s3/lilygo,tdisplay-s3.dts index 2b742a3c2..9f436f89b 100644 --- a/Devices/lilygo-tdisplay-s3/lilygo,tdisplay-s3.dts +++ b/Devices/lilygo-tdisplay-s3/lilygo,tdisplay-s3.dts @@ -19,7 +19,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/lilygo-tdisplay/lilygo,tdisplay.dts b/Devices/lilygo-tdisplay/lilygo,tdisplay.dts index 877efc869..fc8248fd4 100644 --- a/Devices/lilygo-tdisplay/lilygo,tdisplay.dts +++ b/Devices/lilygo-tdisplay/lilygo,tdisplay.dts @@ -15,7 +15,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts b/Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts index 519bade15..2c7a7bcd6 100644 --- a/Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts +++ b/Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts @@ -19,7 +19,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/lilygo-thmi/lilygo,thmi.dts b/Devices/lilygo-thmi/lilygo,thmi.dts index 3811c0c6c..fe3d027ce 100644 --- a/Devices/lilygo-thmi/lilygo,thmi.dts +++ b/Devices/lilygo-thmi/lilygo,thmi.dts @@ -23,7 +23,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/lilygo-tlora-pager/lilygo,tlora-pager.dts b/Devices/lilygo-tlora-pager/lilygo,tlora-pager.dts index d42846f31..553213840 100644 --- a/Devices/lilygo-tlora-pager/lilygo,tlora-pager.dts +++ b/Devices/lilygo-tlora-pager/lilygo,tlora-pager.dts @@ -27,7 +27,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts b/Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts index bdfa69ea2..9306ff6f6 100644 --- a/Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts +++ b/Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts @@ -27,7 +27,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/m5stack-cardputer/m5stack,cardputer.dts b/Devices/m5stack-cardputer/m5stack,cardputer.dts index 8af8b42e4..bf97ffe67 100644 --- a/Devices/m5stack-cardputer/m5stack,cardputer.dts +++ b/Devices/m5stack-cardputer/m5stack,cardputer.dts @@ -28,7 +28,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/m5stack-core2/m5stack,core2.dts b/Devices/m5stack-core2/m5stack,core2.dts index 499df6d1f..87ceca1eb 100644 --- a/Devices/m5stack-core2/m5stack,core2.dts +++ b/Devices/m5stack-core2/m5stack,core2.dts @@ -24,7 +24,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/m5stack-cores3/m5stack,cores3.dts b/Devices/m5stack-cores3/m5stack,cores3.dts index 130a2c41a..0b6c098ef 100644 --- a/Devices/m5stack-cores3/m5stack,cores3.dts +++ b/Devices/m5stack-cores3/m5stack,cores3.dts @@ -31,7 +31,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/m5stack-papers3/m5stack,papers3.dts b/Devices/m5stack-papers3/m5stack,papers3.dts index b2bc3ccec..b51fa9d73 100644 --- a/Devices/m5stack-papers3/m5stack,papers3.dts +++ b/Devices/m5stack-papers3/m5stack,papers3.dts @@ -23,7 +23,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/m5stack-stackchan/m5stack,stackchan.dts b/Devices/m5stack-stackchan/m5stack,stackchan.dts index 9cbb0f4f6..078d491cc 100644 --- a/Devices/m5stack-stackchan/m5stack,stackchan.dts +++ b/Devices/m5stack-stackchan/m5stack,stackchan.dts @@ -31,7 +31,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/m5stack-stickc-plus2/m5stack,stickc-plus2.dts b/Devices/m5stack-stickc-plus2/m5stack,stickc-plus2.dts index 701040f98..dff6ff412 100644 --- a/Devices/m5stack-stickc-plus2/m5stack,stickc-plus2.dts +++ b/Devices/m5stack-stickc-plus2/m5stack,stickc-plus2.dts @@ -19,7 +19,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; gpio0 { diff --git a/Devices/m5stack-sticks3/m5stack,sticks3.dts b/Devices/m5stack-sticks3/m5stack,sticks3.dts index 0011b7e3e..139d12f9e 100644 --- a/Devices/m5stack-sticks3/m5stack,sticks3.dts +++ b/Devices/m5stack-sticks3/m5stack,sticks3.dts @@ -24,7 +24,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/tulip-4r11/tulip-4r11.dts b/Devices/tulip-4r11/tulip-4r11.dts index fd206859a..2781ce45c 100644 --- a/Devices/tulip-4r11/tulip-4r11.dts +++ b/Devices/tulip-4r11/tulip-4r11.dts @@ -24,7 +24,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/unphone/unphone.dts b/Devices/unphone/unphone.dts index f0c3c3999..f98fdd8b8 100644 --- a/Devices/unphone/unphone.dts +++ b/Devices/unphone/unphone.dts @@ -22,7 +22,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/waveshare-esp32-s3-geek/waveshare,esp32-s3-geek.dts b/Devices/waveshare-esp32-s3-geek/waveshare,esp32-s3-geek.dts index b159666c5..da0a0ba5b 100644 --- a/Devices/waveshare-esp32-s3-geek/waveshare,esp32-s3-geek.dts +++ b/Devices/waveshare-esp32-s3-geek/waveshare,esp32-s3-geek.dts @@ -21,7 +21,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts b/Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts index 388d0c20f..f59ba1bca 100644 --- a/Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts +++ b/Devices/waveshare-s3-lcd-13/waveshare,s3-lcd-13.dts @@ -20,7 +20,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/waveshare-s3-touch-lcd-128/waveshare,s3-touch-lcd-128.dts b/Devices/waveshare-s3-touch-lcd-128/waveshare,s3-touch-lcd-128.dts index 09e23375a..50dc811e6 100644 --- a/Devices/waveshare-s3-touch-lcd-128/waveshare,s3-touch-lcd-128.dts +++ b/Devices/waveshare-s3-touch-lcd-128/waveshare,s3-touch-lcd-128.dts @@ -21,7 +21,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/waveshare-s3-touch-lcd-147/waveshare,s3-touch-lcd-147.dts b/Devices/waveshare-s3-touch-lcd-147/waveshare,s3-touch-lcd-147.dts index 737a9e478..32ea31df8 100644 --- a/Devices/waveshare-s3-touch-lcd-147/waveshare,s3-touch-lcd-147.dts +++ b/Devices/waveshare-s3-touch-lcd-147/waveshare,s3-touch-lcd-147.dts @@ -21,7 +21,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/waveshare-s3-touch-lcd-43/waveshare,s3-touch-lcd-43.dts b/Devices/waveshare-s3-touch-lcd-43/waveshare,s3-touch-lcd-43.dts index 3bb7edb1b..44c66423a 100644 --- a/Devices/waveshare-s3-touch-lcd-43/waveshare,s3-touch-lcd-43.dts +++ b/Devices/waveshare-s3-touch-lcd-43/waveshare,s3-touch-lcd-43.dts @@ -20,7 +20,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Devices/wireless-tag-wt32-sc01-plus/wireless-tag,wt32-sc01-plus.dts b/Devices/wireless-tag-wt32-sc01-plus/wireless-tag,wt32-sc01-plus.dts index 385683d00..0fb9663a4 100644 --- a/Devices/wireless-tag-wt32-sc01-plus/wireless-tag,wt32-sc01-plus.dts +++ b/Devices/wireless-tag-wt32-sc01-plus/wireless-tag,wt32-sc01-plus.dts @@ -20,7 +20,6 @@ wifi0 { compatible = "espressif,esp32-wifi-pinned"; - status = "disabled"; }; ble0 { diff --git a/Tactility/Source/service/wifi/Wifi.cpp b/Tactility/Source/service/wifi/Wifi.cpp index ac8989c26..2f8cc1146 100644 --- a/Tactility/Source/service/wifi/Wifi.cpp +++ b/Tactility/Source/service/wifi/Wifi.cpp @@ -262,12 +262,14 @@ bool findAutoConnectAp(settings::WifiApSettings& out) { void dispatchAutoConnect() { LOG_I(TAG, "dispatchAutoConnect()"); - if (state.pauseAutoConnect || state.externalScanPause.load()) { + if (state.pauseAutoConnect || state.externalScanPause.load() || !isRadioOn()) { // A manual disconnect() or an in-progress manual connect() has paused // auto-connect, or a caller (e.g. AutoScanPauseGuard) has externally paused it. // This is called on every SCAN_FINISHED, not just the auto-connect timer's own // scans (e.g. WifiManage re-scans on show), so it must honor the pause instead of - // reconnecting unconditionally. + // reconnecting unconditionally. The radio-off check matters because a scan that was + // already in flight can finish after the user turns the radio off. Without it, + // connect() would call dispatchSetEnabled(true) and turn the radio back on. return; } RadioState radio_state = getRadioState(); From 13a8c5b01731ced793e587bd19d8c6f134a9131f Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 24 Aug 2026 21:49:39 +0200 Subject: [PATCH 05/10] Simplify app event subscription --- .claude/rules/app-framework.md | 4 +- Documentation/ideas.md | 3 ++ Modules/app-module/include/app/event.h | 42 +++++++++++++------ Modules/app-module/include/app/loader.h | 6 +-- .../app-module/source/app_internal_loader.cpp | 4 +- Modules/app-module/source/event.cpp | 10 ++++- Modules/app-module/source/symbols.cpp | 1 + .../tests/source/app_event_test.cpp | 21 ++++------ .../tests/source/app_manager_test.cpp | 14 +++---- .../tests/source/combined_event_test.cpp | 3 +- Tactility/Source/app/addgps/AddGps.cpp | 5 ++- .../Source/app/alertdialog/AlertDialog.cpp | 5 ++- .../Source/app/appdetails/AppDetails.cpp | 5 ++- Tactility/Source/app/apphub/AppHubApp.cpp | 5 ++- .../app/apphubdetails/AppHubDetailsApp.cpp | 5 ++- Tactility/Source/app/applist/AppList.cpp | 5 ++- .../Source/app/appsettings/AppSettings.cpp | 5 ++- .../Source/app/apwebserver/ApWebServer.cpp | 5 ++- .../app/audiosettings/AudioSettings.cpp | 5 ++- Tactility/Source/app/boot/Boot.cpp | 5 ++- Tactility/Source/app/btmanage/BtManage.cpp | 5 ++- .../app/btpeersettings/BtPeerSettings.cpp | 5 ++- Tactility/Source/app/chat/ChatApp.cpp | 5 ++- .../app/crashdiagnostics/CrashDiagnostics.cpp | 5 ++- .../Source/app/development/Development.cpp | 5 ++- Tactility/Source/app/display/Display.cpp | 5 ++- Tactility/Source/app/files/FilesApp.cpp | 5 ++- .../app/fileselection/FileSelection.cpp | 5 ++- .../Source/app/gpssettings/GpsSettings.cpp | 5 ++- .../app/grovesettings/GroveSettings.cpp | 5 ++- .../Source/app/i2cscanner/I2cScanner.cpp | 5 ++- .../Source/app/imageviewer/ImageViewer.cpp | 5 ++- .../Source/app/inputdialog/InputDialog.cpp | 5 ++- .../Source/app/keyboard/KeyboardSettings.cpp | 5 ++- Tactility/Source/app/launcher/Launcher.cpp | 5 ++- .../app/localesettings/LocaleSettings.cpp | 5 ++- Tactility/Source/app/notes/Notes.cpp | 5 ++- Tactility/Source/app/power/Power.cpp | 5 ++- Tactility/Source/app/poweroff/PowerOff.cpp | 5 ++- .../Source/app/screenshot/Screenshot.cpp | 5 ++- .../app/selectiondialog/SelectionDialog.cpp | 5 ++- Tactility/Source/app/settings/Settings.cpp | 5 ++- Tactility/Source/app/setup/Setup.cpp | 5 ++- .../Source/app/systeminfo/SystemInfo.cpp | 5 ++- .../app/timedatesettings/TimeDateSettings.cpp | 5 ++- Tactility/Source/app/timezone/TimeZone.cpp | 5 ++- .../app/touchcalibration/TouchCalibration.cpp | 5 ++- .../app/trackball/TrackballSettings.cpp | 5 ++- .../Source/app/usbsettings/UsbSettings.cpp | 5 ++- .../webserversettings/WebServerSettings.cpp | 5 ++- .../app/wifiapsettings/WifiApSettings.cpp | 5 ++- .../Source/app/wificonnect/WifiConnect.cpp | 5 ++- .../Source/app/wifimanage/WifiManage.cpp | 5 ++- 53 files changed, 192 insertions(+), 131 deletions(-) diff --git a/.claude/rules/app-framework.md b/.claude/rules/app-framework.md index 551fb9d16..d9b36568f 100644 --- a/.claude/rules/app-framework.md +++ b/.claude/rules/app-framework.md @@ -1,10 +1,10 @@ # Architecture: App Framework -Apps are event-driven, C API (`app-module`, ``), not a C++ class. Each app has an `AppManifest` (`id`, `name`, `category`, `location`, `flags`) and a `main(app_instance_id, argc, argv)` entry point (`AppMainFn`), modelled on a C program's `main()`. Every app instance gets its own dedicated task for its whole lifetime, and blocks in that task until it returns. +Apps are event-driven, C API (`app-module`, ``), not a C++ class. Each app has an `AppManifest` (`id`, `name`, `category`, `location`, `flags`) and a `main(argc, argv)` entry point (`AppMainFn`), modelled on a C program's `main()`. Every app instance gets its own dedicated task for its whole lifetime, and blocks in that task until it returns. Use `app_scheduler_current_app_id()` (`app/scheduler.h`) to identify the running instance - it's not passed as a parameter. Lifecycle and inter-app communication go through `app_manager_*()` (`app/manager.h`) and `app_event_*()` (`app/event.h`): - `app_manager_start()`/`app_manager_start_with_parameters()` launch a plain instance; `app_manager_start_for_result()` launches a modal child that reports back to a parent instance. -- An app subscribes with `app_event_subscribe()`, blocks via `task_event_group_wait()`/`task_event_group_wait_any()`, and drains with `app_event_poll()`, reacting to `APP_EVENT_CLOSE` (terminate now) and `APP_EVENT_RESULT` (a child it started reported back). +- An app subscribes with `app_event_subscribe()` (registers for its own instance's events, no id argument needed), blocks via `task_event_group_wait()`/`task_event_group_wait_any()`, and drains with `app_event_poll()`, reacting to `APP_EVENT_CLOSE` (terminate now) and `APP_EVENT_RESULT` (a child it started reported back). - An app closes itself by calling `app_manager_finish()` right before returning from `main()`; another instance is closed via `app_manager_stop()`. Apps are registered at startup via `app_manager_add()`. External apps can be loaded from SD card via `manifest.properties` files, or side-loaded as ELF binaries on ESP32 (see `app/loader.h`'s `AppLoaderApi`). diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 99dd68e2d..4402319f7 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -11,6 +11,9 @@ ## Higher Priority +- Apps should be able to specify stack size in their manifest +- Apps currently have a `Context` object with an `appInstanceId` in it, purely for being able to close the app. + Change it so that the app has its own termination signal that it waits for in the loop, it should subscribe to the event group. - stopAppFromToolbar() in Tactility.cpp stops the top-most app. Change it so the toolbar knows for which app id it is created, so it can rely on that. - Warn if file operations are done from prohibited tasks (e.g. lvgl task) - AppHubApp: Prevent download callbacks from accessing a destroyed view. diff --git a/Modules/app-module/include/app/event.h b/Modules/app-module/include/app/event.h index bddc847f7..f6b77f903 100644 --- a/Modules/app-module/include/app/event.h +++ b/Modules/app-module/include/app/event.h @@ -50,19 +50,21 @@ struct AppEvent { * Caller-owned subscription node. Unlike TactilityKernel's system_event poll subscription * (which coalesces to the latest value), this queues events by value (FIFO) since dropping an * APP_EVENT_RESULT would be unacceptable. - * @warning Fields other than `app_instance_id` and `bit` are for internal use only; do not read - * or write them directly. + * @warning Fields other than `bit` are for internal use only; do not read or write them + * directly. */ struct AppEventSubscription { - /** The app instance this subscription receives events for; set by the caller before app_event_subscribe(). */ - AppInstanceId app_instance_id; - - /** Set by app_event_subscribe(). Read-only for the caller: OR it into a - * task_event_group_wait() mask (alongside other subscriptions sharing the same - * `event_group`) to block on this subscription and other event sources with one call. */ + /** Set by app_event_subscribe()/app_event_subscribe_with_app_id(). Read-only for the + * caller: OR it into a task_event_group_wait() mask (alongside other subscriptions sharing + * the same `event_group`) to block on this subscription and other event sources with one + * call. */ uint32_t bit; struct { + /** The app instance this subscription receives events for; set by + * app_event_subscribe()/app_event_subscribe_with_app_id(). */ + AppInstanceId app_instance_id; + /** Caller-owned, borrowed; set by app_event_subscribe(). */ struct TaskEventGroup* event_group; @@ -75,10 +77,11 @@ struct AppEventSubscription { }; /** - * Register a subscription for events addressed to @a sub->app_instance_id. - * @warning Does not work in ISR context. - * @param[in,out] sub subscription to register; caller sets @a sub->app_instance_id beforehand, - * owns the storage, and must keep it alive (and stationary) until unsubscribed + * Register a subscription for events addressed to the calling app's own instance (identified via + * app_scheduler_current_app_id()). + * @warning Does not work in ISR context. Must be called from the app's own task. + * @param[in,out] sub subscription to register; owns the storage, must stay alive (and + * stationary) until unsubscribed * @param[in] event_group caller-owned group to wait on; must outlive @a sub (i.e. be * destructed only after app_event_unsubscribe()). To block for an event, call * task_event_group_wait()/task_event_group_wait_any() on this group (OR sub->bit into the mask, @@ -89,6 +92,21 @@ struct AppEventSubscription { */ error_t app_event_subscribe(struct AppEventSubscription* sub, struct TaskEventGroup* event_group); +/** + * Same as app_event_subscribe(), but for a caller that isn't running on @a app_instance_id's own + * task (e.g. test code simulating multiple distinct app instances from one thread). Production + * app code should use app_event_subscribe() instead. + * @warning Does not work in ISR context. + * @param[in,out] sub subscription to register; owns the storage, must stay alive (and + * stationary) until unsubscribed + * @param[in] event_group caller-owned group to wait on; same contract as app_event_subscribe() + * @param[in] app_instance_id the app instance this subscription receives events for + * @retval ERROR_NONE on success + * @retval ERROR_RESOURCE @a event_group has no free bits left to claim; @a sub was not registered + * @retval ERROR_INVALID_STATE @a sub is already registered + */ +error_t app_event_subscribe_with_app_id(struct AppEventSubscription* sub, struct TaskEventGroup* event_group, AppInstanceId app_instance_id); + /** * Remove a previously registered subscription. * @warning Does not work in ISR context. diff --git a/Modules/app-module/include/app/loader.h b/Modules/app-module/include/app/loader.h index 0d606184a..db2a12704 100644 --- a/Modules/app-module/include/app/loader.h +++ b/Modules/app-module/include/app/loader.h @@ -21,13 +21,13 @@ extern "C" { /** * Entry point signature for an APP_LOCATION_MEMORY app: a function linked directly into this * firmware binary. Called on the dedicated task app-module's scheduler spawns for this instance, - * blocking for the app's whole lifetime - same contract as an external app's main(), plus - * @a app_instance_id identifying this running instance (use it with + * blocking for the app's whole lifetime - same contract as an external app's main(). Use + * app_scheduler_current_app_id() to identify this running instance (e.g. with * app_event_subscribe()/window_manager_create()/etc.). The instance closes when this function * returns - no separate call is needed. * AppManifest::location.location holds this cast to void*. */ -typedef int32_t (*AppMainFn)(uint32_t app_instance_id, int argc, char* argv[]); +typedef int32_t (*AppMainFn)(int argc, char* argv[]); typedef void* AppRuntime; diff --git a/Modules/app-module/source/app_internal_loader.cpp b/Modules/app-module/source/app_internal_loader.cpp index 4c447d3fe..70d0e179d 100644 --- a/Modules/app-module/source/app_internal_loader.cpp +++ b/Modules/app-module/source/app_internal_loader.cpp @@ -16,9 +16,9 @@ error_t api_load(AppLocation location, AppRuntime* out_runtime) { return ERROR_NONE; } -int32_t api_run(AppRuntime runtime, uint32_t app_instance_id, int argc, char* argv[]) { +int32_t api_run(AppRuntime runtime, uint32_t /*app_instance_id*/, int argc, char* argv[]) { auto entry = reinterpret_cast(runtime); - return entry(app_instance_id, argc, argv); + return entry(argc, argv); } void api_unload(AppRuntime /*unused*/) { diff --git a/Modules/app-module/source/event.cpp b/Modules/app-module/source/event.cpp index 05606f1c8..957668758 100644 --- a/Modules/app-module/source/event.cpp +++ b/Modules/app-module/source/event.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include @@ -21,7 +22,7 @@ static AppEventMutex subscriptions_mutex; extern "C" { -error_t app_event_subscribe(AppEventSubscription* sub, TaskEventGroup* event_group) { +error_t app_event_subscribe_with_app_id(AppEventSubscription* sub, TaskEventGroup* event_group, AppInstanceId app_instance_id) { uint32_t bit; error_t claim_result = task_event_group_claim_bit(event_group, &bit); if (claim_result != ERROR_NONE) { @@ -38,6 +39,7 @@ error_t app_event_subscribe(AppEventSubscription* sub, TaskEventGroup* event_gro } sub->bit = bit; + sub->internal.app_instance_id = app_instance_id; sub->internal.event_group = event_group; sub->internal.head = 0; sub->internal.count = 0; @@ -48,6 +50,10 @@ error_t app_event_subscribe(AppEventSubscription* sub, TaskEventGroup* event_gro return ERROR_NONE; } +error_t app_event_subscribe(AppEventSubscription* sub, TaskEventGroup* event_group) { + return app_event_subscribe_with_app_id(sub, event_group, app_scheduler_current_app_id()); +} + error_t app_event_unsubscribe(AppEventSubscription* sub) { error_t result = ERROR_NOT_FOUND; @@ -76,7 +82,7 @@ error_t app_event_emit(AppInstanceId app_instance_id, const AppEvent* event) { mutex_lock(&subscriptions_mutex.handle); for (AppEventSubscription* sub = subscriptions; sub != nullptr; sub = sub->internal.next) { - if (sub->app_instance_id != app_instance_id) { + if (sub->internal.app_instance_id != app_instance_id) { continue; } diff --git a/Modules/app-module/source/symbols.cpp b/Modules/app-module/source/symbols.cpp index 871f6b0fe..28711df61 100644 --- a/Modules/app-module/source/symbols.cpp +++ b/Modules/app-module/source/symbols.cpp @@ -19,6 +19,7 @@ extern ServiceManifest app_internal_loader_service_manifest; const ModuleSymbol app_module_symbols[] = { // app/event DEFINE_MODULE_SYMBOL(app_event_subscribe), + DEFINE_MODULE_SYMBOL(app_event_subscribe_with_app_id), DEFINE_MODULE_SYMBOL(app_event_unsubscribe), DEFINE_MODULE_SYMBOL(app_event_emit), DEFINE_MODULE_SYMBOL(app_event_poll), diff --git a/Modules/app-module/tests/source/app_event_test.cpp b/Modules/app-module/tests/source/app_event_test.cpp index 4a6c06b08..43795aeff 100644 --- a/Modules/app-module/tests/source/app_event_test.cpp +++ b/Modules/app-module/tests/source/app_event_test.cpp @@ -11,8 +11,7 @@ TEST_CASE("app_event_subscribe/_poll deliver events in FIFO order") { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = 1; - CHECK_EQ(app_event_subscribe(&sub, &event_group), ERROR_NONE); + CHECK_EQ(app_event_subscribe_with_app_id(&sub, &event_group, 1), ERROR_NONE); for (uint32_t i = 0; i < 3; i++) { AppEvent event { .type = APP_EVENT_RESULT, .timestamp = 0, .result = { .launch_id = i, .result = 0 } }; @@ -35,8 +34,7 @@ TEST_CASE("app_event_emit only delivers to subscriptions for that app_instance_i task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = 10; - app_event_subscribe(&sub, &event_group); + app_event_subscribe_with_app_id(&sub, &event_group, 10); AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; CHECK_EQ(app_event_emit(11, &event), ERROR_NOT_FOUND); @@ -53,8 +51,7 @@ TEST_CASE("app_event_emit returns ERROR_RESOURCE and drops the newest event once task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = 20; - app_event_subscribe(&sub, &event_group); + app_event_subscribe_with_app_id(&sub, &event_group, 20); for (uint32_t i = 0; i < APP_EVENT_QUEUE_CAPACITY; i++) { AppEvent event { .type = APP_EVENT_RESULT, .timestamp = 0, .result = { .launch_id = i, .result = 0 } }; @@ -83,8 +80,7 @@ TEST_CASE("app_event_unsubscribe stops further delivery") { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = 30; - app_event_subscribe(&sub, &event_group); + app_event_subscribe_with_app_id(&sub, &event_group, 30); CHECK_EQ(app_event_unsubscribe(&sub), ERROR_NONE); CHECK_EQ(app_event_unsubscribe(&sub), ERROR_NOT_FOUND); @@ -100,8 +96,7 @@ TEST_CASE("app_event_poll times out when no event has arrived") { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = 40; - app_event_subscribe(&sub, &event_group); + app_event_subscribe_with_app_id(&sub, &event_group, 40); AppEvent out {}; CHECK_EQ(app_event_poll(&sub, &out), ERROR_TIMEOUT); @@ -115,8 +110,7 @@ TEST_CASE("app_event_emit stamps the event with the current boot-relative time") task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = 50; - app_event_subscribe(&sub, &event_group); + app_event_subscribe_with_app_id(&sub, &event_group, 50); auto before = static_cast(get_micros_since_boot()); AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; @@ -137,8 +131,7 @@ TEST_CASE("task_event_group_wait wakes when the event is emitted from another ta task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = 60; - CHECK_EQ(app_event_subscribe(&sub, &event_group), ERROR_NONE); + CHECK_EQ(app_event_subscribe_with_app_id(&sub, &event_group, 60), ERROR_NONE); auto* thread = thread_alloc_full( "app-event-emitter", diff --git a/Modules/app-module/tests/source/app_manager_test.cpp b/Modules/app-module/tests/source/app_manager_test.cpp index e67f9d299..85a894102 100644 --- a/Modules/app-module/tests/source/app_manager_test.cpp +++ b/Modules/app-module/tests/source/app_manager_test.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -55,7 +56,7 @@ void stash_received_arguments(int argc, char* argv[]) { // parameter (app_manager_start_for_result()), acts as a modal dialog instead: returns the // requested result (argv[0], parsed as an int) immediately (the app's own return value IS the // delivered APP_EVENT_RESULT.result - see app_scheduler.cpp's thread_main()). -int32_t fake_run(void*, uint32_t app_instance_id, int argc, char* argv[]) { +int32_t fake_run(void*, uint32_t /*app_instance_id*/, int argc, char* argv[]) { stash_received_arguments(argc, argv); if (argc == 1) { @@ -70,7 +71,6 @@ int32_t fake_run(void*, uint32_t app_instance_id, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; app_event_subscribe(&sub, &event_group); while (true) { @@ -139,8 +139,8 @@ void ensure_memory_loader_registered() { // Same subscribe-until-close contract as fake_run() above, but called directly as an AppMainFn - // this is what a real internal app's entry point looks like. -int32_t fake_app_main(uint32_t app_instance_id, int argc, char* argv[]) { - return fake_run(nullptr, app_instance_id, argc, argv); +int32_t fake_app_main(int argc, char* argv[]) { + return fake_run(nullptr, app_scheduler_current_app_id(), argc, argv); } // Wraps app_manager_get_topmost_instance_id() for terse assertions: 0 if no app is Active. @@ -343,8 +343,7 @@ TEST_CASE("app_manager_start_for_result delivers APP_EVENT_RESULT to the parent, task_event_group_construct(&parent_event_group); AppEventSubscription parent_sub {}; - parent_sub.app_instance_id = parent_id; - REQUIRE_EQ(app_event_subscribe(&parent_sub, &parent_event_group), ERROR_NONE); + REQUIRE_EQ(app_event_subscribe_with_app_id(&parent_sub, &parent_event_group, parent_id), ERROR_NONE); const char* argv[] = { "42" }; uint32_t child_id = 0; @@ -384,8 +383,7 @@ TEST_CASE("app_manager_start_for_result delivers the child's own return value as task_event_group_construct(&parent_event_group); AppEventSubscription parent_sub {}; - parent_sub.app_instance_id = parent_id; - REQUIRE_EQ(app_event_subscribe(&parent_sub, &parent_event_group), ERROR_NONE); + REQUIRE_EQ(app_event_subscribe_with_app_id(&parent_sub, &parent_event_group, parent_id), ERROR_NONE); uint32_t child_id = 0; // No parameters - fake_run falls through to its normal CLOSE loop instead of acting as a diff --git a/Modules/app-module/tests/source/combined_event_test.cpp b/Modules/app-module/tests/source/combined_event_test.cpp index 25031c5b4..88ead21b6 100644 --- a/Modules/app-module/tests/source/combined_event_test.cpp +++ b/Modules/app-module/tests/source/combined_event_test.cpp @@ -14,8 +14,7 @@ TEST_CASE("a task can wait on app_event and system_event together via one TaskEv task_event_group_construct(&event_group); AppEventSubscription app_sub {}; - app_sub.app_instance_id = 100; - CHECK_EQ(app_event_subscribe(&app_sub, &event_group), ERROR_NONE); + CHECK_EQ(app_event_subscribe_with_app_id(&app_sub, &event_group, 100), ERROR_NONE); SystemEventSubscription sys_sub {}; sys_sub.event.type = KERNEL_EVENT_BOOT_COMPLETED; diff --git a/Tactility/Source/app/addgps/AddGps.cpp b/Tactility/Source/app/addgps/AddGps.cpp index 152c9c5c0..0900a7421 100644 --- a/Tactility/Source/app/addgps/AddGps.cpp +++ b/Tactility/Source/app/addgps/AddGps.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -199,7 +200,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { // endregion } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -207,7 +209,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/alertdialog/AlertDialog.cpp b/Tactility/Source/app/alertdialog/AlertDialog.cpp index b9722b8d1..3ea99142f 100644 --- a/Tactility/Source/app/alertdialog/AlertDialog.cpp +++ b/Tactility/Source/app/alertdialog/AlertDialog.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -97,7 +98,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx { appInstanceId }; ctx.argc = argc; ctx.argv = argv; @@ -106,7 +108,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/appdetails/AppDetails.cpp b/Tactility/Source/app/appdetails/AppDetails.cpp index bf78088b1..d7adc3f98 100644 --- a/Tactility/Source/app/appdetails/AppDetails.cpp +++ b/Tactility/Source/app/appdetails/AppDetails.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -104,7 +105,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -118,7 +120,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/apphub/AppHubApp.cpp b/Tactility/Source/app/apphub/AppHubApp.cpp index 23e3e4095..8fd5a0a79 100644 --- a/Tactility/Source/app/apphub/AppHubApp.cpp +++ b/Tactility/Source/app/apphub/AppHubApp.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -197,7 +198,8 @@ void destroyWidgets(void* userData) { ctx->refreshButton = nullptr; } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx; ctx.appInstanceId = appInstanceId; @@ -205,7 +207,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); diff --git a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp index d959421aa..4c4f9d2d7 100644 --- a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp +++ b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -211,7 +212,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { updateViews(ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); // argv layout: [0]=appId, [1]=appVersionName, [2]=appVersionCode, [3]=appName, // [4]=appDescription, [5]=targetSdk, [6]=file, [7..argc)=targetPlatforms. @@ -234,7 +236,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/applist/AppList.cpp b/Tactility/Source/app/applist/AppList.cpp index 36d07c76e..7eba66616 100644 --- a/Tactility/Source/app/applist/AppList.cpp +++ b/Tactility/Source/app/applist/AppList.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -78,14 +79,14 @@ void createWidgets(lv_obj_t* parent, void*) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); appListInstanceId = appInstanceId; TaskEventGroup event_group {}; task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); diff --git a/Tactility/Source/app/appsettings/AppSettings.cpp b/Tactility/Source/app/appsettings/AppSettings.cpp index d2395d336..bf6ce27fc 100644 --- a/Tactility/Source/app/appsettings/AppSettings.cpp +++ b/Tactility/Source/app/appsettings/AppSettings.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -87,14 +88,14 @@ void createWidgets(lv_obj_t* parent, void*) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); appSettingsInstanceId = appInstanceId; TaskEventGroup event_group {}; task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); diff --git a/Tactility/Source/app/apwebserver/ApWebServer.cpp b/Tactility/Source/app/apwebserver/ApWebServer.cpp index 9ece43edc..3758668c7 100644 --- a/Tactility/Source/app/apwebserver/ApWebServer.cpp +++ b/Tactility/Source/app/apwebserver/ApWebServer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -121,7 +122,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { ctx->webServerEnabledChanged = true; } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; ctx.wsSettings = settings::webserver::loadOrGetDefault(); @@ -130,7 +132,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/audiosettings/AudioSettings.cpp b/Tactility/Source/app/audiosettings/AudioSettings.cpp index dd6219bfb..4b632060f 100644 --- a/Tactility/Source/app/audiosettings/AudioSettings.cpp +++ b/Tactility/Source/app/audiosettings/AudioSettings.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -211,7 +212,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { }); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -219,7 +221,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index eb7c8dc53..855adc751 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -284,7 +285,8 @@ void runBootSequence(TickType_t startTime) { system_event_emit(KERNEL_EVENT_BOOT_COMPLETED, nullptr, 0); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); bootAppInstanceId = appInstanceId; const auto start_time = get_ticks(); @@ -296,7 +298,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); bootWindowId = window_manager_create(appInstanceId, createSplashWidgets, nullptr); diff --git a/Tactility/Source/app/btmanage/BtManage.cpp b/Tactility/Source/app/btmanage/BtManage.cpp index 4b7d7b298..8e35f0744 100644 --- a/Tactility/Source/app/btmanage/BtManage.cpp +++ b/Tactility/Source/app/btmanage/BtManage.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -199,7 +200,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { ctx->unlock(); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx; ctx.appInstanceId = appInstanceId; ctx.bindings = (Bindings) { @@ -224,7 +226,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp b/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp index 934440f68..95eb36926 100644 --- a/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp +++ b/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -178,7 +179,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { updateViews(ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -203,7 +205,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/chat/ChatApp.cpp b/Tactility/Source/app/chat/ChatApp.cpp index 170b53f5b..5c3b26f8d 100644 --- a/Tactility/Source/app/chat/ChatApp.cpp +++ b/Tactility/Source/app/chat/ChatApp.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -167,7 +168,8 @@ void switchChannel(Context* ctx, const std::string& chatChannel) { namespace { -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; ctx.isFirstLaunch = !settingsFileExists(); @@ -189,7 +191,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp index 5ef050dbc..e88ecc2a9 100644 --- a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp +++ b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -149,7 +150,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -157,7 +159,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/development/Development.cpp b/Tactility/Source/app/development/Development.cpp index 86076fd3d..77f3fac06 100644 --- a/Tactility/Source/app/development/Development.cpp +++ b/Tactility/Source/app/development/Development.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -162,7 +163,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { updateViewState(ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; ctx.service = service::development::findService(); @@ -178,7 +180,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index 08c768ae0..0a3a423e4 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -13,6 +13,7 @@ #include #include +#include #include @@ -303,7 +304,8 @@ void persistIfUpdated(Context& ctx) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -311,7 +313,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/files/FilesApp.cpp b/Tactility/Source/app/files/FilesApp.cpp index 90cb022f7..048d763ad 100644 --- a/Tactility/Source/app/files/FilesApp.cpp +++ b/Tactility/Source/app/files/FilesApp.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -25,7 +26,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { ctx->view->init(ctx->appInstanceId, parent); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); auto state = std::make_shared(); View view(state); CreateContext createContext { &view, appInstanceId }; @@ -34,7 +36,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &createContext); diff --git a/Tactility/Source/app/fileselection/FileSelection.cpp b/Tactility/Source/app/fileselection/FileSelection.cpp index 3baba0c46..a89651db0 100644 --- a/Tactility/Source/app/fileselection/FileSelection.cpp +++ b/Tactility/Source/app/fileselection/FileSelection.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -43,7 +44,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { ctx->view->init(parent, ctx->mode); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); // argv layout: [0]="existing" or "existing_or_new". Context ctx {}; @@ -66,7 +68,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/gpssettings/GpsSettings.cpp b/Tactility/Source/app/gpssettings/GpsSettings.cpp index 47f0bae51..a8ee385b6 100644 --- a/Tactility/Source/app/gpssettings/GpsSettings.cpp +++ b/Tactility/Source/app/gpssettings/GpsSettings.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -257,7 +258,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { updateDeviceStates(ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -271,7 +273,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/grovesettings/GroveSettings.cpp b/Tactility/Source/app/grovesettings/GroveSettings.cpp index 25e54f227..5edd01470 100644 --- a/Tactility/Source/app/grovesettings/GroveSettings.cpp +++ b/Tactility/Source/app/grovesettings/GroveSettings.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -89,7 +90,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -97,7 +99,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index c4a18e93f..e3b56ae1c 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -384,7 +385,8 @@ void stopScanningIfRunning(Context* ctx) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx; ctx.appInstanceId = appInstanceId; @@ -392,7 +394,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/imageviewer/ImageViewer.cpp b/Tactility/Source/app/imageviewer/ImageViewer.cpp index 175fca478..c7fcc0b08 100644 --- a/Tactility/Source/app/imageviewer/ImageViewer.cpp +++ b/Tactility/Source/app/imageviewer/ImageViewer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -82,7 +83,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); check(argc > 0, "Parameters not set"); Context ctx {}; @@ -93,7 +95,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/inputdialog/InputDialog.cpp b/Tactility/Source/app/inputdialog/InputDialog.cpp index 877f44027..0fb743be4 100644 --- a/Tactility/Source/app/inputdialog/InputDialog.cpp +++ b/Tactility/Source/app/inputdialog/InputDialog.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -106,7 +107,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { createButton(ctx, button_wrapper, "Cancel", nullptr); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx { appInstanceId }; ctx.argc = argc; ctx.argv = argv; @@ -115,7 +117,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/keyboard/KeyboardSettings.cpp b/Tactility/Source/app/keyboard/KeyboardSettings.cpp index 08ce740cd..c43208a2e 100644 --- a/Tactility/Source/app/keyboard/KeyboardSettings.cpp +++ b/Tactility/Source/app/keyboard/KeyboardSettings.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -205,7 +206,8 @@ void persistIfUpdated(Context& ctx) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -213,7 +215,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/launcher/Launcher.cpp b/Tactility/Source/app/launcher/Launcher.cpp index cb82386c6..6bbd92321 100644 --- a/Tactility/Source/app/launcher/Launcher.cpp +++ b/Tactility/Source/app/launcher/Launcher.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -231,14 +232,14 @@ void runAutoStart() { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); runAutoStart(); TaskEventGroup event_group {}; task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); diff --git a/Tactility/Source/app/localesettings/LocaleSettings.cpp b/Tactility/Source/app/localesettings/LocaleSettings.cpp index 5c7d0c611..fef376dbe 100644 --- a/Tactility/Source/app/localesettings/LocaleSettings.cpp +++ b/Tactility/Source/app/localesettings/LocaleSettings.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -141,7 +142,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { lv_obj_add_event_cb(ctx->languageDropdown, onLanguageSet, LV_EVENT_VALUE_CHANGED, ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx; ctx.appInstanceId = appInstanceId; @@ -149,7 +151,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/notes/Notes.cpp b/Tactility/Source/app/notes/Notes.cpp index 923e01156..9975ecd73 100644 --- a/Tactility/Source/app/notes/Notes.cpp +++ b/Tactility/Source/app/notes/Notes.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -182,7 +183,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -194,7 +196,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index 34130857e..df377b639 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -229,7 +230,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -244,7 +246,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/poweroff/PowerOff.cpp b/Tactility/Source/app/poweroff/PowerOff.cpp index 5804f7104..f837dee40 100644 --- a/Tactility/Source/app/poweroff/PowerOff.cpp +++ b/Tactility/Source/app/poweroff/PowerOff.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -132,7 +133,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { lv_obj_add_event_cb(no_button, onNoPressed, LV_EVENT_SHORT_CLICKED, ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -140,7 +142,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index 6fbe17783..72c423e78 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -240,7 +241,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; ctx.updateTimer = std::make_unique(Timer::Type::Periodic, 500 / portTICK_PERIOD_MS, [&ctx] { @@ -254,7 +256,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp index dff75bba1..a31b2c864 100644 --- a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp +++ b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -99,7 +100,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(AppInstanceId appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + AppInstanceId appInstanceId = app_scheduler_current_app_id(); Context ctx { appInstanceId }; ctx.argc = argc; ctx.argv = argv; @@ -108,7 +110,6 @@ int32_t appMain(AppInstanceId appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index 61239aae3..823cc97ea 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -76,14 +77,14 @@ void createWidgets(lv_obj_t* parent, void*) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); settingsInstanceId = appInstanceId; TaskEventGroup event_group {}; task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); diff --git a/Tactility/Source/app/setup/Setup.cpp b/Tactility/Source/app/setup/Setup.cpp index 2b01b375d..356e5823d 100644 --- a/Tactility/Source/app/setup/Setup.cpp +++ b/Tactility/Source/app/setup/Setup.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -203,7 +204,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { renderCurrent(ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; ctx.steps = { @@ -233,7 +235,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index db35843ce..6917020b5 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -399,7 +400,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { updateTasks(ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -421,7 +423,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp b/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp index 81faed8cf..1ef225c0a 100644 --- a/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp +++ b/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -153,7 +154,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { lv_label_set_text(ctx->timeZoneLabel, timeZoneName.c_str()); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -161,7 +163,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/timezone/TimeZone.cpp b/Tactility/Source/app/timezone/TimeZone.cpp index 277d7499d..f2b3bd259 100644 --- a/Tactility/Source/app/timezone/TimeZone.cpp +++ b/Tactility/Source/app/timezone/TimeZone.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -229,7 +230,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { ctx->listWidget = list; } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); // argv layout: [0]="1"/"0" (saveTimeZone). Context ctx; @@ -244,7 +246,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp index 56d4f77f4..85db06d20 100644 --- a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp +++ b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -221,7 +222,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { updateUi(ctx); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -237,7 +239,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/trackball/TrackballSettings.cpp b/Tactility/Source/app/trackball/TrackballSettings.cpp index 4040acb18..1e7148434 100644 --- a/Tactility/Source/app/trackball/TrackballSettings.cpp +++ b/Tactility/Source/app/trackball/TrackballSettings.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -268,7 +269,8 @@ void persistIfUpdated(Context& ctx) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -276,7 +278,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/usbsettings/UsbSettings.cpp b/Tactility/Source/app/usbsettings/UsbSettings.cpp index e867489c1..a36f1e325 100644 --- a/Tactility/Source/app/usbsettings/UsbSettings.cpp +++ b/Tactility/Source/app/usbsettings/UsbSettings.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -81,7 +82,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -89,7 +91,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/webserversettings/WebServerSettings.cpp b/Tactility/Source/app/webserversettings/WebServerSettings.cpp index 563cb76bd..29b4caa26 100644 --- a/Tactility/Source/app/webserversettings/WebServerSettings.cpp +++ b/Tactility/Source/app/webserversettings/WebServerSettings.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -354,7 +355,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { "AP mode uses the password configured above."); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; ctx.wsSettings = settings::webserver::loadOrGetDefault(); @@ -366,7 +368,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); diff --git a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp index cfb198e4a..4e12f0633 100644 --- a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp +++ b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -215,7 +216,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { lv_obj_align(ctx->autoConnectSwitch, LV_ALIGN_RIGHT_MID, 0, 0); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -225,7 +227,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); Device* wifi_device = nullptr; diff --git a/Tactility/Source/app/wificonnect/WifiConnect.cpp b/Tactility/Source/app/wificonnect/WifiConnect.cpp index db3f70e11..bd1266059 100644 --- a/Tactility/Source/app/wificonnect/WifiConnect.cpp +++ b/Tactility/Source/app/wificonnect/WifiConnect.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -300,7 +301,8 @@ void createWidgets(lv_obj_t* parent, void* userData) { } } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx {}; ctx.appInstanceId = appInstanceId; @@ -311,7 +313,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); Device* wifi_device = nullptr; diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index 6cb12f184..bb9f79fa5 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -121,7 +122,8 @@ void destroyWidgets(void* userData) { ctx->view.reset(); } -int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); Context ctx; ctx.appInstanceId = appInstanceId; ctx.bindings = (Bindings) { @@ -145,7 +147,6 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { } AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; app_event_subscribe(&sub, &event_group); Device* wifi_device = nullptr; From bc0fd4859ec035bb1a862cc0d8f95a7259576f46 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 24 Aug 2026 22:19:22 +0200 Subject: [PATCH 06/10] Add checks --- Tactility/Source/app/addgps/AddGps.cpp | 5 +++-- Tactility/Source/app/alertdialog/AlertDialog.cpp | 5 +++-- Tactility/Source/app/appdetails/AppDetails.cpp | 5 +++-- Tactility/Source/app/apphub/AppHubApp.cpp | 5 +++-- Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp | 5 +++-- Tactility/Source/app/applist/AppList.cpp | 6 ++++-- Tactility/Source/app/appsettings/AppSettings.cpp | 6 ++++-- Tactility/Source/app/apwebserver/ApWebServer.cpp | 5 +++-- Tactility/Source/app/audiosettings/AudioSettings.cpp | 6 ++++-- Tactility/Source/app/boot/Boot.cpp | 5 +++-- Tactility/Source/app/btmanage/BtManage.cpp | 5 +++-- Tactility/Source/app/btpeersettings/BtPeerSettings.cpp | 5 +++-- Tactility/Source/app/chat/ChatApp.cpp | 5 +++-- Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp | 5 +++-- Tactility/Source/app/development/Development.cpp | 5 +++-- Tactility/Source/app/display/Display.cpp | 5 +++-- Tactility/Source/app/files/FilesApp.cpp | 6 ++++-- Tactility/Source/app/fileselection/FileSelection.cpp | 6 ++++-- Tactility/Source/app/gpssettings/GpsSettings.cpp | 5 +++-- Tactility/Source/app/grovesettings/GroveSettings.cpp | 5 +++-- Tactility/Source/app/i2cscanner/I2cScanner.cpp | 5 +++-- Tactility/Source/app/imageviewer/ImageViewer.cpp | 4 ++-- Tactility/Source/app/inputdialog/InputDialog.cpp | 5 +++-- Tactility/Source/app/keyboard/KeyboardSettings.cpp | 5 +++-- Tactility/Source/app/launcher/Launcher.cpp | 5 +++-- Tactility/Source/app/localesettings/LocaleSettings.cpp | 6 ++++-- Tactility/Source/app/notes/Notes.cpp | 5 +++-- Tactility/Source/app/power/Power.cpp | 5 +++-- Tactility/Source/app/poweroff/PowerOff.cpp | 5 +++-- Tactility/Source/app/screenshot/Screenshot.cpp | 5 +++-- Tactility/Source/app/selectiondialog/SelectionDialog.cpp | 5 +++-- Tactility/Source/app/settings/Settings.cpp | 4 ++-- Tactility/Source/app/setup/Setup.cpp | 5 +++-- Tactility/Source/app/systeminfo/SystemInfo.cpp | 5 +++-- Tactility/Source/app/timedatesettings/TimeDateSettings.cpp | 5 +++-- Tactility/Source/app/timezone/TimeZone.cpp | 5 +++-- Tactility/Source/app/touchcalibration/TouchCalibration.cpp | 5 +++-- Tactility/Source/app/trackball/TrackballSettings.cpp | 5 +++-- Tactility/Source/app/usbsettings/UsbSettings.cpp | 6 ++++-- .../Source/app/webserversettings/WebServerSettings.cpp | 5 +++-- Tactility/Source/app/wifiapsettings/WifiApSettings.cpp | 5 +++-- Tactility/Source/app/wificonnect/WifiConnect.cpp | 5 +++-- Tactility/Source/app/wifimanage/WifiManage.cpp | 5 +++-- 43 files changed, 134 insertions(+), 86 deletions(-) diff --git a/Tactility/Source/app/addgps/AddGps.cpp b/Tactility/Source/app/addgps/AddGps.cpp index 0900a7421..03448dd9f 100644 --- a/Tactility/Source/app/addgps/AddGps.cpp +++ b/Tactility/Source/app/addgps/AddGps.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -209,7 +210,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -234,7 +235,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/alertdialog/AlertDialog.cpp b/Tactility/Source/app/alertdialog/AlertDialog.cpp index 3ea99142f..88c31a4cd 100644 --- a/Tactility/Source/app/alertdialog/AlertDialog.cpp +++ b/Tactility/Source/app/alertdialog/AlertDialog.cpp @@ -7,6 +7,7 @@ #include +#include #include #include @@ -108,7 +109,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -127,7 +128,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return ctx.result; diff --git a/Tactility/Source/app/appdetails/AppDetails.cpp b/Tactility/Source/app/appdetails/AppDetails.cpp index d7adc3f98..c919d1305 100644 --- a/Tactility/Source/app/appdetails/AppDetails.cpp +++ b/Tactility/Source/app/appdetails/AppDetails.cpp @@ -16,6 +16,7 @@ #include #include +#include #include constexpr auto* TAG = "AppDetails"; @@ -120,7 +121,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -151,7 +152,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/apphub/AppHubApp.cpp b/Tactility/Source/app/apphub/AppHubApp.cpp index 8fd5a0a79..5f1910f43 100644 --- a/Tactility/Source/app/apphub/AppHubApp.cpp +++ b/Tactility/Source/app/apphub/AppHubApp.cpp @@ -14,6 +14,7 @@ #include +#include #include #include @@ -207,7 +208,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); @@ -229,7 +230,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp index 4c4f9d2d7..495281228 100644 --- a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp +++ b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -236,7 +237,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -270,7 +271,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/applist/AppList.cpp b/Tactility/Source/app/applist/AppList.cpp index 7eba66616..cdab48a42 100644 --- a/Tactility/Source/app/applist/AppList.cpp +++ b/Tactility/Source/app/applist/AppList.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include #include @@ -87,7 +89,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); @@ -106,7 +108,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/appsettings/AppSettings.cpp b/Tactility/Source/app/appsettings/AppSettings.cpp index bf6ce27fc..7bfc21553 100644 --- a/Tactility/Source/app/appsettings/AppSettings.cpp +++ b/Tactility/Source/app/appsettings/AppSettings.cpp @@ -10,6 +10,8 @@ #include +#include + #include #include #include @@ -96,7 +98,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); @@ -118,7 +120,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/apwebserver/ApWebServer.cpp b/Tactility/Source/app/apwebserver/ApWebServer.cpp index 3758668c7..8ede703dd 100644 --- a/Tactility/Source/app/apwebserver/ApWebServer.cpp +++ b/Tactility/Source/app/apwebserver/ApWebServer.cpp @@ -13,6 +13,7 @@ #include #include +#include #include namespace tt::app::apwebserver { @@ -132,7 +133,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -173,7 +174,7 @@ int32_t appMain(int argc, char* argv[]) { }); window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/audiosettings/AudioSettings.cpp b/Tactility/Source/app/audiosettings/AudioSettings.cpp index 4b632060f..0d37cd25e 100644 --- a/Tactility/Source/app/audiosettings/AudioSettings.cpp +++ b/Tactility/Source/app/audiosettings/AudioSettings.cpp @@ -9,6 +9,8 @@ #include +#include + #include #include #include @@ -221,7 +223,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -248,7 +250,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index 855adc751..33381c3a7 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -1,5 +1,6 @@ #include "tactility/system_event.h" +#include #include #include #include @@ -298,7 +299,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); bootWindowId = window_manager_create(appInstanceId, createSplashWidgets, nullptr); @@ -324,7 +325,7 @@ int32_t appMain(int argc, char* argv[]) { if (bootWindowId != 0) { window_manager_remove(bootWindowId); } - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/btmanage/BtManage.cpp b/Tactility/Source/app/btmanage/BtManage.cpp index 8e35f0744..8aae722db 100644 --- a/Tactility/Source/app/btmanage/BtManage.cpp +++ b/Tactility/Source/app/btmanage/BtManage.cpp @@ -12,6 +12,7 @@ #include +#include #include namespace tt::app::btmanage { @@ -226,7 +227,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -276,7 +277,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp b/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp index 95eb36926..75b8a3bb8 100644 --- a/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp +++ b/Tactility/Source/app/btpeersettings/BtPeerSettings.cpp @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -205,7 +206,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -246,7 +247,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/chat/ChatApp.cpp b/Tactility/Source/app/chat/ChatApp.cpp index 5c3b26f8d..d0b054381 100644 --- a/Tactility/Source/app/chat/ChatApp.cpp +++ b/Tactility/Source/app/chat/ChatApp.cpp @@ -14,6 +14,7 @@ #include +#include #include #include @@ -191,7 +192,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -213,7 +214,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); service::espnow::unsubscribeReceiver(ctx.receiveSubscription); diff --git a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp index e88ecc2a9..bd73cfc57 100644 --- a/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp +++ b/Tactility/Source/app/crashdiagnostics/CrashDiagnostics.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -159,7 +160,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -183,7 +184,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); bool continuePressed = ctx.continuePressed; diff --git a/Tactility/Source/app/development/Development.cpp b/Tactility/Source/app/development/Development.cpp index 77f3fac06..6f69dc677 100644 --- a/Tactility/Source/app/development/Development.cpp +++ b/Tactility/Source/app/development/Development.cpp @@ -14,6 +14,7 @@ #include +#include #include #include @@ -180,7 +181,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -222,7 +223,7 @@ int32_t appMain(int argc, char* argv[]) { lvgl_unlock(); window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index 0a3a423e4..51fb52bbb 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -313,7 +314,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -336,7 +337,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/files/FilesApp.cpp b/Tactility/Source/app/files/FilesApp.cpp index 048d763ad..c08d48423 100644 --- a/Tactility/Source/app/files/FilesApp.cpp +++ b/Tactility/Source/app/files/FilesApp.cpp @@ -8,6 +8,8 @@ #include +#include + #include namespace tt::app::files { @@ -36,7 +38,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &createContext); @@ -63,7 +65,7 @@ int32_t appMain(int argc, char* argv[]) { view.deinit(); window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/fileselection/FileSelection.cpp b/Tactility/Source/app/fileselection/FileSelection.cpp index a89651db0..96f4f31bf 100644 --- a/Tactility/Source/app/fileselection/FileSelection.cpp +++ b/Tactility/Source/app/fileselection/FileSelection.cpp @@ -9,6 +9,8 @@ #include +#include + #include #include @@ -68,7 +70,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -87,7 +89,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return ctx.result; diff --git a/Tactility/Source/app/gpssettings/GpsSettings.cpp b/Tactility/Source/app/gpssettings/GpsSettings.cpp index a8ee385b6..8bb66e2b1 100644 --- a/Tactility/Source/app/gpssettings/GpsSettings.cpp +++ b/Tactility/Source/app/gpssettings/GpsSettings.cpp @@ -13,6 +13,7 @@ #include +#include #include #include @@ -273,7 +274,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.timer->start(); @@ -317,7 +318,7 @@ int32_t appMain(int argc, char* argv[]) { ctx.timer->stop(); window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/grovesettings/GroveSettings.cpp b/Tactility/Source/app/grovesettings/GroveSettings.cpp index 5edd01470..846143571 100644 --- a/Tactility/Source/app/grovesettings/GroveSettings.cpp +++ b/Tactility/Source/app/grovesettings/GroveSettings.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -99,7 +100,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -121,7 +122,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index e3b56ae1c..7f7f76b17 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -394,7 +395,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -417,7 +418,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/imageviewer/ImageViewer.cpp b/Tactility/Source/app/imageviewer/ImageViewer.cpp index c7fcc0b08..bf47bda6d 100644 --- a/Tactility/Source/app/imageviewer/ImageViewer.cpp +++ b/Tactility/Source/app/imageviewer/ImageViewer.cpp @@ -95,7 +95,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -117,7 +117,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/inputdialog/InputDialog.cpp b/Tactility/Source/app/inputdialog/InputDialog.cpp index 0fb743be4..459dc0996 100644 --- a/Tactility/Source/app/inputdialog/InputDialog.cpp +++ b/Tactility/Source/app/inputdialog/InputDialog.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -117,7 +118,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -136,7 +137,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return ctx.result; diff --git a/Tactility/Source/app/keyboard/KeyboardSettings.cpp b/Tactility/Source/app/keyboard/KeyboardSettings.cpp index c43208a2e..8738bbbf6 100644 --- a/Tactility/Source/app/keyboard/KeyboardSettings.cpp +++ b/Tactility/Source/app/keyboard/KeyboardSettings.cpp @@ -11,6 +11,7 @@ #include +#include #include #include @@ -215,7 +216,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -238,7 +239,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/launcher/Launcher.cpp b/Tactility/Source/app/launcher/Launcher.cpp index 6bbd92321..995538033 100644 --- a/Tactility/Source/app/launcher/Launcher.cpp +++ b/Tactility/Source/app/launcher/Launcher.cpp @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -240,7 +241,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); @@ -261,7 +262,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/localesettings/LocaleSettings.cpp b/Tactility/Source/app/localesettings/LocaleSettings.cpp index fef376dbe..186eee5a0 100644 --- a/Tactility/Source/app/localesettings/LocaleSettings.cpp +++ b/Tactility/Source/app/localesettings/LocaleSettings.cpp @@ -13,6 +13,8 @@ #include +#include + #include #include @@ -151,7 +153,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -173,7 +175,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/notes/Notes.cpp b/Tactility/Source/app/notes/Notes.cpp index 9975ecd73..94d6b5d2c 100644 --- a/Tactility/Source/app/notes/Notes.cpp +++ b/Tactility/Source/app/notes/Notes.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace tt::app::notes { @@ -196,7 +197,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -240,7 +241,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index df377b639..6b1e28af5 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -246,7 +247,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.timer->start(); @@ -270,7 +271,7 @@ int32_t appMain(int argc, char* argv[]) { ctx.timer->stop(); window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/poweroff/PowerOff.cpp b/Tactility/Source/app/poweroff/PowerOff.cpp index f837dee40..81ec9451c 100644 --- a/Tactility/Source/app/poweroff/PowerOff.cpp +++ b/Tactility/Source/app/poweroff/PowerOff.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -142,7 +143,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -164,7 +165,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index 72c423e78..93fe098f0 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -16,6 +16,7 @@ #include +#include #include #include @@ -256,7 +257,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -282,7 +283,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp index a31b2c864..c0b5d7b50 100644 --- a/Tactility/Source/app/selectiondialog/SelectionDialog.cpp +++ b/Tactility/Source/app/selectiondialog/SelectionDialog.cpp @@ -7,6 +7,7 @@ #include +#include #include #include @@ -110,7 +111,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -129,7 +130,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return ctx.result; diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index 823cc97ea..22ea8513c 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -85,7 +85,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr); @@ -104,7 +104,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; } diff --git a/Tactility/Source/app/setup/Setup.cpp b/Tactility/Source/app/setup/Setup.cpp index 356e5823d..6382df639 100644 --- a/Tactility/Source/app/setup/Setup.cpp +++ b/Tactility/Source/app/setup/Setup.cpp @@ -15,6 +15,7 @@ #include +#include #include #include @@ -235,7 +236,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -264,7 +265,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index 6917020b5..912b261f6 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -1,3 +1,4 @@ +#include #include "tactility/time.h" #include @@ -423,7 +424,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.memoryTimer->start(); // Memory: every 10s @@ -449,7 +450,7 @@ int32_t appMain(int argc, char* argv[]) { ctx.memoryTimer->stop(); ctx.tasksTimer->stop(); window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp b/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp index 1ef225c0a..e706460b5 100644 --- a/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp +++ b/Tactility/Source/app/timedatesettings/TimeDateSettings.cpp @@ -10,6 +10,7 @@ #include +#include #include #include @@ -163,7 +164,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -200,7 +201,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/timezone/TimeZone.cpp b/Tactility/Source/app/timezone/TimeZone.cpp index f2b3bd259..77318c578 100644 --- a/Tactility/Source/app/timezone/TimeZone.cpp +++ b/Tactility/Source/app/timezone/TimeZone.cpp @@ -13,6 +13,7 @@ #include +#include #include #include @@ -246,7 +247,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); ctx.updateTimer->start(); @@ -267,7 +268,7 @@ int32_t appMain(int argc, char* argv[]) { ctx.updateTimer->stop(); window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return ctx.result; diff --git a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp index 85db06d20..dd0f0047d 100644 --- a/Tactility/Source/app/touchcalibration/TouchCalibration.cpp +++ b/Tactility/Source/app/touchcalibration/TouchCalibration.cpp @@ -12,6 +12,7 @@ #include +#include #include #include #include @@ -239,7 +240,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -261,7 +262,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); // finishCalibration() already applied a new calibration on success. On cancel/failure, diff --git a/Tactility/Source/app/trackball/TrackballSettings.cpp b/Tactility/Source/app/trackball/TrackballSettings.cpp index 1e7148434..90ef53bb9 100644 --- a/Tactility/Source/app/trackball/TrackballSettings.cpp +++ b/Tactility/Source/app/trackball/TrackballSettings.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -278,7 +279,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -301,7 +302,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/usbsettings/UsbSettings.cpp b/Tactility/Source/app/usbsettings/UsbSettings.cpp index a36f1e325..c517a314a 100644 --- a/Tactility/Source/app/usbsettings/UsbSettings.cpp +++ b/Tactility/Source/app/usbsettings/UsbSettings.cpp @@ -7,6 +7,8 @@ #include +#include + #include #include @@ -91,7 +93,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -113,7 +115,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/webserversettings/WebServerSettings.cpp b/Tactility/Source/app/webserversettings/WebServerSettings.cpp index 29b4caa26..c5efd2583 100644 --- a/Tactility/Source/app/webserversettings/WebServerSettings.cpp +++ b/Tactility/Source/app/webserversettings/WebServerSettings.cpp @@ -12,6 +12,7 @@ #include +#include #include #include @@ -368,7 +369,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); @@ -428,7 +429,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp index 4e12f0633..13cc95ad0 100644 --- a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp +++ b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -227,7 +228,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); Device* wifi_device = nullptr; if (device_get_first_by_type(&WIFI_TYPE, &wifi_device) == ERROR_NONE) { @@ -309,7 +310,7 @@ int32_t appMain(int argc, char* argv[]) { device_put(ctx.wifiDevice); } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/wificonnect/WifiConnect.cpp b/Tactility/Source/app/wificonnect/WifiConnect.cpp index bd1266059..70b7160c8 100644 --- a/Tactility/Source/app/wificonnect/WifiConnect.cpp +++ b/Tactility/Source/app/wificonnect/WifiConnect.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -313,7 +314,7 @@ int32_t appMain(int argc, char* argv[]) { task_event_group_construct(&event_group); AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); Device* wifi_device = nullptr; if (device_get_first_by_type(&WIFI_TYPE, &wifi_device) == ERROR_NONE) { @@ -370,7 +371,7 @@ int32_t appMain(int argc, char* argv[]) { device_put(ctx.wifiDevice); } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index bb9f79fa5..935f8b400 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -11,6 +11,7 @@ #include +#include #include #include @@ -147,7 +148,7 @@ int32_t appMain(int argc, char* argv[]) { } AppEventSubscription sub {}; - app_event_subscribe(&sub, &event_group); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); Device* wifi_device = nullptr; if (device_get_first_by_type(&WIFI_TYPE, &wifi_device) == ERROR_NONE) { @@ -229,7 +230,7 @@ int32_t appMain(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; From 890477c233d4d76f65ccd73d0b51312ded158721 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 24 Aug 2026 22:24:01 +0200 Subject: [PATCH 07/10] Add missing symbol for using check() --- TactilityKernel/source/symbols.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TactilityKernel/source/symbols.c b/TactilityKernel/source/symbols.c index 62ab04bee..accedeada 100644 --- a/TactilityKernel/source/symbols.c +++ b/TactilityKernel/source/symbols.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -512,6 +513,8 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = { DEFINE_MODULE_SYMBOL(timer_set_callback_priority), // error DEFINE_MODULE_SYMBOL(error_to_string), + // check + DEFINE_MODULE_SYMBOL(__crash), // log #ifndef ESP_PLATFORM DEFINE_MODULE_SYMBOL(log_generic), From 8250566549189a1be363f69dc1bec448376c0cb4 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 24 Aug 2026 22:59:14 +0200 Subject: [PATCH 08/10] Fix SdkIntegration test --- Tests/SdkIntegration/main/Source/main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Tests/SdkIntegration/main/Source/main.c b/Tests/SdkIntegration/main/Source/main.c index a7fdb32c9..54db9bc63 100644 --- a/Tests/SdkIntegration/main/Source/main.c +++ b/Tests/SdkIntegration/main/Source/main.c @@ -4,6 +4,8 @@ #include +#include + #include #include @@ -24,8 +26,8 @@ int main(int argc, char* argv[]) { struct TaskEventGroup event_group = {0}; task_event_group_construct(&event_group); - struct AppEventSubscription sub = { .app_instance_id = app_instance_id }; - app_event_subscribe(&sub, &event_group); + struct AppEventSubscription sub = {0}; + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, create_widgets, NULL); @@ -43,7 +45,7 @@ int main(int argc, char* argv[]) { } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); return 0; From 05a5131e19f0b83e9f786f42ce1daa8b0732e43d Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Tue, 25 Aug 2026 16:53:56 +0200 Subject: [PATCH 09/10] Fix for subscribe/unsubscribe vs lvgl race --- .../Tactility/app/chat/ChatAppPrivate.h | 1 - Tactility/Source/app/chat/ChatApp.cpp | 19 +++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Tactility/Private/Tactility/app/chat/ChatAppPrivate.h b/Tactility/Private/Tactility/app/chat/ChatAppPrivate.h index 7abd67cc3..d5eece322 100644 --- a/Tactility/Private/Tactility/app/chat/ChatAppPrivate.h +++ b/Tactility/Private/Tactility/app/chat/ChatAppPrivate.h @@ -24,7 +24,6 @@ struct Context { uint32_t appInstanceId; ChatState state; ChatView view = ChatView(this, &state); - service::espnow::ReceiverSubscription receiveSubscription = -1; ChatSettingsData settings; bool isFirstLaunch = false; }; diff --git a/Tactility/Source/app/chat/ChatApp.cpp b/Tactility/Source/app/chat/ChatApp.cpp index d0b054381..3556abc47 100644 --- a/Tactility/Source/app/chat/ChatApp.cpp +++ b/Tactility/Source/app/chat/ChatApp.cpp @@ -181,13 +181,6 @@ int32_t appMain(int argc, char* argv[]) { } enableEspNow(&ctx); - ctx.receiveSubscription = service::espnow::subscribeReceiver( - [&ctx](const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length) { - onReceive(&ctx, receiveInfo, data, length); - } - ); - - TaskEventGroup event_group {}; task_event_group_construct(&event_group); @@ -196,6 +189,12 @@ int32_t appMain(int argc, char* argv[]) { WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); + auto espnow_subscription = service::espnow::subscribeReceiver( + [&ctx](const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length) { + onReceive(&ctx, receiveInfo, data, length); + } + ); + bool shouldClose = false; while (!shouldClose) { task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); @@ -213,13 +212,13 @@ int32_t appMain(int argc, char* argv[]) { } } + service::espnow::unsubscribeReceiver(espnow_subscription); + disableEspNow(&ctx); + window_manager_remove(window); check(app_event_unsubscribe(&sub) == ERROR_NONE); task_event_group_destruct(&event_group); - service::espnow::unsubscribeReceiver(ctx.receiveSubscription); - disableEspNow(&ctx); - return 0; } From 94c81d3b32fb216b357d0017613f2df6ec81a7c0 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Tue, 25 Aug 2026 16:54:12 +0200 Subject: [PATCH 10/10] Update docs --- Documentation/ideas.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 4402319f7..e004d7911 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -11,6 +11,7 @@ ## Higher Priority +- lvgl file lock won't work with display vs sdcard when lvgl is stopped (external app bug risk) - Apps should be able to specify stack size in their manifest - Apps currently have a `Context` object with an `appInstanceId` in it, purely for being able to close the app. Change it so that the app has its own termination signal that it waits for in the loop, it should subscribe to the event group.