diff --git a/Apps/Brainfuck/main/Source/main.cpp b/Apps/Brainfuck/main/Source/main.cpp index 4260b27..eb972ab 100644 --- a/Apps/Brainfuck/main/Source/main.cpp +++ b/Apps/Brainfuck/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + #include extern "C" { @@ -19,25 +21,30 @@ int main(int argc, char* argv[]) { auto ctx = std::make_unique(); ctx->appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, brainfuckCreateWidgets, ctx.get()); bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); brainfuckTeardown(ctx.get()); return 0; diff --git a/Apps/Brainfuck/manifest.properties b/Apps/Brainfuck/manifest.properties index 63e3367..50d4fe3 100644 --- a/Apps/Brainfuck/manifest.properties +++ b/Apps/Brainfuck/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.brainfuck -app.version.name=0.9.0 -app.version.code=9 +app.version.name=0.10.0 +app.version.code=10 app.name=Brainfuck interpreter app.description=Brainfuck esoteric language interpreter diff --git a/Apps/Breakout/main/Source/main.cpp b/Apps/Breakout/main/Source/main.cpp index cf82898..bc2e302 100644 --- a/Apps/Breakout/main/Source/main.cpp +++ b/Apps/Breakout/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + #include extern "C" { @@ -19,25 +21,30 @@ int main(int argc, char* argv[]) { auto ctx = std::make_unique(); ctx->appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, breakoutCreateWidgets, ctx.get()); bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); breakoutTeardown(ctx.get()); return 0; diff --git a/Apps/Breakout/manifest.properties b/Apps/Breakout/manifest.properties index 717a50d..0504db7 100644 --- a/Apps/Breakout/manifest.properties +++ b/Apps/Breakout/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.breakout -app.version.name=0.10.0 -app.version.code=10 +app.version.name=0.13.0 +app.version.code=13 app.name=Breakout app.description=Classic brick-breaking arcade game diff --git a/Apps/Calculator/main/Source/main.cpp b/Apps/Calculator/main/Source/main.cpp index 9c236d8..7c817f2 100644 --- a/Apps/Calculator/main/Source/main.cpp +++ b/Apps/Calculator/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + extern "C" { int main(int argc, char* argv[]) { @@ -14,25 +16,30 @@ int main(int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, calculatorCreateWidgets, &ctx); bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } 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/Apps/Calculator/manifest.properties b/Apps/Calculator/manifest.properties index 557736c..fd99629 100644 --- a/Apps/Calculator/manifest.properties +++ b/Apps/Calculator/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.calculator -app.version.name=0.10.0 -app.version.code=10 +app.version.name=0.11.0 +app.version.code=11 app.name=Calculator diff --git a/Apps/Diceware/main/Source/main.cpp b/Apps/Diceware/main/Source/main.cpp index ba5b145..2f29279 100644 --- a/Apps/Diceware/main/Source/main.cpp +++ b/Apps/Diceware/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + extern "C" { int main(int argc, char* argv[]) { @@ -14,35 +16,40 @@ int main(int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, dicewareCreateWidgets, &ctx); bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + struct AppEvent event; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - should_close = true; - break; - case APP_EVENT_RESULT: - if (event.result.launch_id == ctx.pendingHelpDialogId) { - ctx.pendingHelpDialogId = 0; - } - 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: + should_close = true; + break; + case APP_EVENT_RESULT: + if (event.result.launch_id == ctx.pendingHelpDialogId) { + ctx.pendingHelpDialogId = 0; + } + app_manager_stop(event.result.launch_id); + break; + default: + break; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); dicewareTeardown(&ctx); return 0; diff --git a/Apps/Diceware/manifest.properties b/Apps/Diceware/manifest.properties index 4212b53..28aba26 100644 --- a/Apps/Diceware/manifest.properties +++ b/Apps/Diceware/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.diceware -app.version.name=0.11.0 -app.version.code=11 +app.version.name=0.12.0 +app.version.code=12 app.name=Diceware diff --git a/Apps/EpubReader/main/Source/main.cpp b/Apps/EpubReader/main/Source/main.cpp index 6274990..457ec58 100644 --- a/Apps/EpubReader/main/Source/main.cpp +++ b/Apps/EpubReader/main/Source/main.cpp @@ -7,6 +7,8 @@ #include #include +#include + extern "C" { int main(int argc, char* argv[]) { @@ -18,60 +20,65 @@ int main(int argc, char* argv[]) { ctx.launchFilePath_ = argv[0]; } + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, epubReaderCreateWidgets, &ctx); bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + struct AppEvent event; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - should_close = true; - break; - - case APP_EVENT_RESULT: { - uint32_t launch_id = event.result.launch_id; - - if (launch_id == ctx.psramAlertId_) { - // PSRAM alert closed - this app cannot run without PSRAM, close self. - ctx.psramAlertId_ = 0; - app_manager_stop(launch_id); + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: should_close = true; - } else if (launch_id == ctx.tocDialogId_) { - ctx.tocDialogId_ = 0; - int32_t selection = event.result.result; - if (selection >= 0 && ctx.epub_) { - const auto& toc = ctx.epub_->getToc(); - const auto& spine = ctx.epub_->getSpine(); - if (selection < (int32_t)toc.size()) { - for (size_t i = 0; i < spine.size(); ++i) { - if (spine[i].href == toc[(size_t)selection].href) { - lvgl_lock(); - loadChapter(&ctx, (int)i, 0); - lvgl_unlock(); - break; + break; + + case APP_EVENT_RESULT: { + uint32_t launch_id = event.result.launch_id; + + if (launch_id == ctx.psramAlertId_) { + // PSRAM alert closed - this app cannot run without PSRAM, close self. + ctx.psramAlertId_ = 0; + app_manager_stop(launch_id); + should_close = true; + } else if (launch_id == ctx.tocDialogId_) { + ctx.tocDialogId_ = 0; + int32_t selection = event.result.result; + if (selection >= 0 && ctx.epub_) { + const auto& toc = ctx.epub_->getToc(); + const auto& spine = ctx.epub_->getSpine(); + if (selection < (int32_t)toc.size()) { + for (size_t i = 0; i < spine.size(); ++i) { + if (spine[i].href == toc[(size_t)selection].href) { + lvgl_lock(); + loadChapter(&ctx, (int)i, 0); + lvgl_unlock(); + break; + } } } } + app_manager_stop(launch_id); } - app_manager_stop(launch_id); + break; } - break; - } - default: - break; + default: + break; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); epubReaderTeardown(&ctx); return 0; diff --git a/Apps/EpubReader/manifest.properties b/Apps/EpubReader/manifest.properties index 3b4fada..57aeae8 100644 --- a/Apps/EpubReader/manifest.properties +++ b/Apps/EpubReader/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32s3,esp32p4 app.id=tactility.epubreader -app.version.name=0.8.0 -app.version.code=8 +app.version.name=0.9.0 +app.version.code=9 app.name=Epub Reader app.description=Epub and text file reader. Requires PSRAM! diff --git a/Apps/EspNowBridge/main/Source/EspNowBridge.cpp b/Apps/EspNowBridge/main/Source/EspNowBridge.cpp index e54c9e4..76eca69 100644 --- a/Apps/EspNowBridge/main/Source/EspNowBridge.cpp +++ b/Apps/EspNowBridge/main/Source/EspNowBridge.cpp @@ -592,8 +592,7 @@ static void waitForTransportTaskEntry(void* arg) { vTaskDelete(nullptr); } -static void onWifiEvent(Device* /*device*/, void* callbackContext, WifiEvent /*event*/) { - auto* ctx = static_cast(callbackContext); +static void handleWifiEvent(Context* ctx, const WifiEvent& /*event*/) { if (liveInstance.load() != ctx) { return; } @@ -603,26 +602,34 @@ static void onWifiEvent(Device* /*device*/, void* callbackContext, WifiEvent /*e }, nullptr, nullptr); } +void espNowBridgeProcessWifiEvents(Context* ctx) { + if (ctx->wifiDevice == nullptr) { + return; + } + WifiEvent event {}; + while (wifi_event_poll(&ctx->wifiEventSub, &event) == ERROR_NONE) { + handleWifiEvent(ctx, event); + } +} + static void onEnableWifiButtonClicked(lv_event_t* /*event*/) { auto* ctx = liveInstance.load(); if (ctx == nullptr || ctx->wifiDevice == nullptr) { return; } - device_start(ctx->wifiDevice); - // start_device() allocates a fresh driver context (Platforms/platform-esp32's - // esp32_wifi.cpp), which wipes any event callback registered before the device was started - - // re-register now that it's actually running. Also refresh once directly rather than relying - // solely on the next WifiEvent, so the "WiFi on" prompt updates immediately even though the + // The subscription set up in espNowBridgeInit() stays live across radio on/off - only the + // radio itself needs toggling here. Also refresh once directly rather than relying solely on + // the next WifiEvent, so the "WiFi on" prompt updates immediately even though the // co-processor firmware version below isn't available yet. - wifi_add_event_callback(ctx->wifiDevice, ctx, onWifiEvent); + wifi_set_radio_on(ctx->wifiDevice); refreshWifiPrompt(ctx); refreshCurrentVersion(ctx); - // The co-processor RPC transport isn't up the instant device_start() returns - it comes up - // asynchronously (~1-2s later) - so firmwareOps->get_info() above reliably fails right after - // enabling WiFi. Nothing else reliably re-triggers a version refresh once the transport - // actually comes up (the WiFi event callback only covers radio/station state, not transport - // readiness), so wait for it explicitly on a background task and refresh once it's ready. + // The co-processor RPC transport isn't up the instant wifi_set_radio_on() returns - it comes + // up asynchronously (~1-2s later) - so firmwareOps->get_info() above reliably fails right + // after enabling WiFi. Nothing else reliably re-triggers a version refresh once the transport + // actually comes up (WiFi events only cover radio/station state, not transport readiness), so + // wait for it explicitly on a background task and refresh once it's ready. if (ctx->firmwareOps != nullptr) { ctx->outstandingTasks.fetch_add(1); if (xTaskCreate(waitForTransportTaskEntry, "espnow_bridge_wait", 4096 / sizeof(StackType_t), ctx, tskIDLE_PRIORITY + 1, nullptr) != pdPASS) { @@ -631,23 +638,24 @@ static void onEnableWifiButtonClicked(lv_event_t* /*event*/) { } } -void espNowBridgeInit(Context* ctx) { +void espNowBridgeInit(Context* ctx, TaskEventGroup* eventGroup) { ctx->taskDoneSemaphore = xSemaphoreCreateBinary(); liveInstance = ctx; + + Device* wifiDevice = nullptr; + if (device_get_first_by_type(&WIFI_TYPE, &wifiDevice) == ERROR_NONE) { + if (wifi_event_subscribe(wifiDevice, &ctx->wifiEventSub, eventGroup) == ERROR_NONE) { + ctx->wifiDevice = wifiDevice; + } else { + device_put(wifiDevice); + } + } } void espNowBridgeCreateWidgets(lv_obj_t* parent, void* userData) { auto* ctx = static_cast(userData); - // Tear down whatever the previous build wired up. The first call has nothing to tear down - // (wifiDevice is null); a rebuild - after e.g. the file picker (a modal child) closes and - // this window resurfaces - does, since there's no separate "window buried" callback in this - // app framework to have done it already (unlike the old one's onHide()). ctx->isShown = false; - if (ctx->wifiDevice != nullptr) { - wifi_remove_event_callback(ctx->wifiDevice, onWifiEvent); - ctx->wifiDevice = nullptr; - } lv_obj_remove_flag(parent, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); @@ -691,13 +699,12 @@ void espNowBridgeCreateWidgets(lv_obj_t* parent, void* userData) { ctx->statusLabel = lv_label_create(wrapper); lv_label_set_text(ctx->statusLabel, "Ready"); - ctx->wifiDevice = wifi_find_first_registered_device(); - if (ctx->wifiDevice != nullptr) { - wifi_add_event_callback(ctx->wifiDevice, ctx, onWifiEvent); - if (wifi_get_firmware_ops(ctx->wifiDevice, &ctx->firmwareOps, &ctx->firmwareCtx) != ERROR_NONE) { - ctx->firmwareOps = nullptr; - ctx->firmwareCtx = nullptr; - } + // wifiDevice is resolved once in espNowBridgeInit() and stays subscribed for the whole app + // instance lifetime - only firmwareOps needs refreshing here, since the co-processor RPC + // transport can come and go independently of the WiFi radio. + if (ctx->wifiDevice != nullptr && wifi_get_firmware_ops(ctx->wifiDevice, &ctx->firmwareOps, &ctx->firmwareCtx) != ERROR_NONE) { + ctx->firmwareOps = nullptr; + ctx->firmwareCtx = nullptr; } refreshCurrentVersion(ctx); @@ -723,7 +730,8 @@ void espNowBridgeApplyPendingUpdate(Context* ctx) { void espNowBridgeTeardown(Context* ctx) { ctx->isShown = false; if (ctx->wifiDevice != nullptr) { - wifi_remove_event_callback(ctx->wifiDevice, onWifiEvent); + wifi_event_unsubscribe(ctx->wifiDevice, &ctx->wifiEventSub); + device_put(ctx->wifiDevice); ctx->wifiDevice = nullptr; } diff --git a/Apps/EspNowBridge/main/Source/EspNowBridge.h b/Apps/EspNowBridge/main/Source/EspNowBridge.h index f3fecb3..f4eee05 100644 --- a/Apps/EspNowBridge/main/Source/EspNowBridge.h +++ b/Apps/EspNowBridge/main/Source/EspNowBridge.h @@ -28,6 +28,7 @@ struct Context { uint32_t pickFileLaunchId = 0; std::string pendingUpdateFilePath; Device* wifiDevice = nullptr; + WifiEventSubscription wifiEventSub {}; // Resolved once per createWidgets() call via wifi_get_firmware_ops() - null on a WiFi // device with no updatable co-processor (e.g. a native, non-hosted chip). All OTA/ @@ -38,7 +39,7 @@ struct Context { // Set once widgets exist (end of espNowBridgeCreateWidgets()), false again the moment // they don't (start of a rebuild, or final teardown) - checked (via dispatchToUi(), below) - // before touching any lv_obj_t*, since the OTA worker task and the WiFi-event callback can + // before touching any lv_obj_t*, since the OTA worker task and WiFi event processing can // both outlive the window being buried by a modal child (e.g. the file picker) or the app // closing entirely. std::atomic isShown{false}; @@ -65,8 +66,14 @@ struct Context { }; /** Sets up state that must exist for the whole app instance lifetime, regardless of how many - * times the window is (re)built. Call once, right after constructing the Context. */ -void espNowBridgeInit(Context* ctx); + * times the window is (re)built. Call once, right after constructing the Context. + * @param eventGroup subscribes ctx's WiFi event subscription into this group; must outlive ctx + * (destructed only after espNowBridgeTeardown()). */ +void espNowBridgeInit(Context* ctx, TaskEventGroup* eventGroup); + +/** Drains any WiFi events queued for ctx and reacts to them (radio/station state changes). + * Call from the app's main loop after task_event_group_wait_any() returns. */ +void espNowBridgeProcessWifiEvents(Context* ctx); /** window_manager_create()'s WindowCreateWidgetsFn - @a userData is the Context* for this instance. */ void espNowBridgeCreateWidgets(lv_obj_t* parent, void* userData); diff --git a/Apps/EspNowBridge/main/Source/main.cpp b/Apps/EspNowBridge/main/Source/main.cpp index a5e8a93..248632b 100644 --- a/Apps/EspNowBridge/main/Source/main.cpp +++ b/Apps/EspNowBridge/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + #include #include @@ -20,46 +22,54 @@ int main(int argc, char* argv[]) { // stack frame here. auto ctx = std::make_unique(); ctx->appInstanceId = app_instance_id; - espNowBridgeInit(ctx.get()); + + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + espNowBridgeInit(ctx.get(), &event_group); struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, espNowBridgeCreateWidgets, ctx.get()); bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + struct AppEvent event; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - should_close = true; - break; - - case APP_EVENT_RESULT: - if (event.result.launch_id == ctx->pickFileLaunchId) { - ctx->pickFileLaunchId = 0; - if (event.result.result == 0) { // 0 = Ok (see FileSelection.h) - char pathBuf[256] = {}; - if (tt_app_fileselection_get_result_path(pathBuf, sizeof(pathBuf))) { - ctx->pendingUpdateFilePath = pathBuf; - espNowBridgeApplyPendingUpdate(ctx.get()); + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + should_close = true; + break; + + case APP_EVENT_RESULT: + if (event.result.launch_id == ctx->pickFileLaunchId) { + ctx->pickFileLaunchId = 0; + if (event.result.result == 0) { // 0 = Ok (see FileSelection.h) + char pathBuf[256] = {}; + if (tt_app_fileselection_get_result_path(pathBuf, sizeof(pathBuf))) { + ctx->pendingUpdateFilePath = pathBuf; + espNowBridgeApplyPendingUpdate(ctx.get()); + } } } - } - app_manager_stop(event.result.launch_id); - break; + app_manager_stop(event.result.launch_id); + break; - default: - break; + default: + break; + } + if (should_close) break; } + + espNowBridgeProcessWifiEvents(ctx.get()); } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); espNowBridgeTeardown(ctx.get()); return 0; diff --git a/Apps/EspNowBridge/manifest.properties b/Apps/EspNowBridge/manifest.properties index 7b5fffa..417d0b7 100644 --- a/Apps/EspNowBridge/manifest.properties +++ b/Apps/EspNowBridge/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32p4 app.id=tactility.espnowbridge -app.version.name=0.6.0 -app.version.code=6 +app.version.name=0.7.0 +app.version.code=7 app.name=ESP-NOW Bridge app.description=Companion app for updating P4 device C6 co-processor firmware to enable ESP-NOW bridge support. diff --git a/Apps/GPIO/main/Source/main.cpp b/Apps/GPIO/main/Source/main.cpp index 4454f21..3c79b28 100644 --- a/Apps/GPIO/main/Source/main.cpp +++ b/Apps/GPIO/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + extern "C" { int main(int argc, char* argv[]) { @@ -15,9 +17,11 @@ int main(int argc, char* argv[]) { ctx.appInstanceId = app_instance_id; gpioInit(&ctx); + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, gpioCreateWidgets, &ctx); ctx.window = window; @@ -25,17 +29,20 @@ int main(int argc, char* argv[]) { bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); gpioTeardown(&ctx); return 0; diff --git a/Apps/GPIO/manifest.properties b/Apps/GPIO/manifest.properties index 753fac2..74b62d8 100644 --- a/Apps/GPIO/manifest.properties +++ b/Apps/GPIO/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.gpio -app.version.name=0.12.0 -app.version.code=12 +app.version.name=0.13.0 +app.version.code=13 app.name=GPIO diff --git a/Apps/GraphicsDemo/main/Source/Main.cpp b/Apps/GraphicsDemo/main/Source/Main.cpp index 10efefa..1b788b5 100644 --- a/Apps/GraphicsDemo/main/Source/Main.cpp +++ b/Apps/GraphicsDemo/main/Source/Main.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -28,22 +29,33 @@ static void showErrorAndWait(AppInstanceId appInstanceId, const char* message) { return; } + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool done = false; struct AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_RESULT && event.result.launch_id == dialogInstanceId) { - app_manager_stop(dialogInstanceId); - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + done = true; + break; + } + if (event.type == APP_EVENT_RESULT && event.result.launch_id == dialogInstanceId) { + app_manager_stop(dialogInstanceId); + done = true; + break; + } } + if (done) break; } - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); } extern "C" { diff --git a/Apps/GraphicsDemo/manifest.properties b/Apps/GraphicsDemo/manifest.properties index 51f34a2..713ad6f 100644 --- a/Apps/GraphicsDemo/manifest.properties +++ b/Apps/GraphicsDemo/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.graphicsdemo -app.version.name=0.10.0 -app.version.code=10 +app.version.name=0.11.0 +app.version.code=11 app.name=Graphics Demo diff --git a/Apps/HelloWorld/main/Source/main.c b/Apps/HelloWorld/main/Source/main.c index d8a511d..12f4787 100644 --- a/Apps/HelloWorld/main/Source/main.c +++ b/Apps/HelloWorld/main/Source/main.c @@ -4,6 +4,8 @@ #include +#include + #include #include @@ -21,24 +23,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 AppEventSubscription sub = { .app_instance_id = app_instance_id }; - app_event_subscribe(&sub); + struct TaskEventGroup event_group = {0}; + task_event_group_construct(&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); 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; + } + if (should_close) break; } } 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/Apps/HelloWorld/manifest.properties b/Apps/HelloWorld/manifest.properties index 3f46bdf..71f0653 100644 --- a/Apps/HelloWorld/manifest.properties +++ b/Apps/HelloWorld/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.helloworld -app.version.name=0.10.0 -app.version.code=10 +app.version.name=0.11.0 +app.version.code=11 app.name=Hello World diff --git a/Apps/M5UnitTest/main/Source/main.cpp b/Apps/M5UnitTest/main/Source/main.cpp index bf64e84..525b9f6 100644 --- a/Apps/M5UnitTest/main/Source/main.cpp +++ b/Apps/M5UnitTest/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + extern "C" { int main(int argc, char* argv[]) { @@ -14,26 +16,31 @@ int main(int argc, char* argv[]) { Context ctx; ctx.appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, m5UnitTestCreateWidgets, &ctx); ctx.window = window; bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); m5UnitTestTeardown(&ctx); return 0; diff --git a/Apps/M5UnitTest/manifest.properties b/Apps/M5UnitTest/manifest.properties index 2c842b7..b774d6b 100644 --- a/Apps/M5UnitTest/manifest.properties +++ b/Apps/M5UnitTest/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32s3,esp32p4 app.id=tactility.m5unittest -app.version.name=0.8.0 -app.version.code=8 +app.version.name=0.9.0 +app.version.code=9 app.name=M5 Unit Test diff --git a/Apps/Magic8Ball/main/Source/main.cpp b/Apps/Magic8Ball/main/Source/main.cpp index cfdb0a1..8017348 100644 --- a/Apps/Magic8Ball/main/Source/main.cpp +++ b/Apps/Magic8Ball/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + extern "C" { int main(int argc, char* argv[]) { @@ -14,26 +16,31 @@ int main(int argc, char* argv[]) { Context ctx; ctx.appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, magic8BallCreateWidgets, &ctx); ctx.window = window; bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); magic8BallTeardown(&ctx); return 0; diff --git a/Apps/Magic8Ball/manifest.properties b/Apps/Magic8Ball/manifest.properties index 229b59d..f18cef4 100644 --- a/Apps/Magic8Ball/manifest.properties +++ b/Apps/Magic8Ball/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.magic8ball -app.version.name=0.9.0 -app.version.code=9 +app.version.name=0.10.0 +app.version.code=10 app.name=Magic 8-Ball diff --git a/Apps/MediaKeys/main/Source/main.cpp b/Apps/MediaKeys/main/Source/main.cpp index 911dfbf..34284ae 100644 --- a/Apps/MediaKeys/main/Source/main.cpp +++ b/Apps/MediaKeys/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + #include extern "C" { @@ -19,26 +21,31 @@ int main(int argc, char* argv[]) { auto ctx = std::make_unique(); ctx->appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, mediaKeysCreateWidgets, ctx.get()); ctx->window = window; bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); mediaKeysTeardown(ctx.get()); return 0; diff --git a/Apps/MediaKeys/manifest.properties b/Apps/MediaKeys/manifest.properties index dbd84fa..88d5c9a 100644 --- a/Apps/MediaKeys/manifest.properties +++ b/Apps/MediaKeys/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32s3,esp32p4 app.id=tactility.mediakeys -app.version.name=0.9.0 -app.version.code=9 +app.version.name=0.10.0 +app.version.code=10 app.name=Media Keys app.description=Bluetooth media keys. Touch or Physical Keyboard control\nB - previous, P - play/pause, N - next, M - mute, D - volume down, U - volume up.\nQ or ESC to exit focus. diff --git a/Apps/MystifyDemo/main/Source/Main.cpp b/Apps/MystifyDemo/main/Source/Main.cpp index 10efefa..1b788b5 100644 --- a/Apps/MystifyDemo/main/Source/Main.cpp +++ b/Apps/MystifyDemo/main/Source/Main.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -28,22 +29,33 @@ static void showErrorAndWait(AppInstanceId appInstanceId, const char* message) { return; } + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = appInstanceId; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); while (true) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + bool done = false; struct AppEvent event {}; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - if (event.type == APP_EVENT_RESULT && event.result.launch_id == dialogInstanceId) { - app_manager_stop(dialogInstanceId); - break; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + if (event.type == APP_EVENT_CLOSE) { + done = true; + break; + } + if (event.type == APP_EVENT_RESULT && event.result.launch_id == dialogInstanceId) { + app_manager_stop(dialogInstanceId); + done = true; + break; + } } + if (done) break; } - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); } extern "C" { diff --git a/Apps/MystifyDemo/manifest.properties b/Apps/MystifyDemo/manifest.properties index 9867d2c..b4a2373 100644 --- a/Apps/MystifyDemo/manifest.properties +++ b/Apps/MystifyDemo/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.mystifydemo -app.version.name=0.11.0 -app.version.code=11 +app.version.name=0.12.0 +app.version.code=12 app.name=Mystify Demo diff --git a/Apps/SerialConsole/main/Source/main.cpp b/Apps/SerialConsole/main/Source/main.cpp index de1fc97..ea01f63 100644 --- a/Apps/SerialConsole/main/Source/main.cpp +++ b/Apps/SerialConsole/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + #include extern "C" { @@ -21,26 +23,31 @@ int main(int argc, char* argv[]) { auto ctx = std::make_unique(); ctx->appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, serialConsoleCreateWidgets, ctx.get()); ctx->window = window; bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); serialConsoleTeardown(ctx.get()); return 0; diff --git a/Apps/SerialConsole/manifest.properties b/Apps/SerialConsole/manifest.properties index 08d09cb..44b6195 100644 --- a/Apps/SerialConsole/manifest.properties +++ b/Apps/SerialConsole/manifest.properties @@ -2,6 +2,6 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.serialconsole -app.version.name=0.12.0 -app.version.code=12 +app.version.name=0.13.0 +app.version.code=13 app.name=Serial Console diff --git a/Apps/Snake/main/Source/main.cpp b/Apps/Snake/main/Source/main.cpp index f194f69..36b1dfc 100644 --- a/Apps/Snake/main/Source/main.cpp +++ b/Apps/Snake/main/Source/main.cpp @@ -7,6 +7,8 @@ #include #include +#include + extern "C" { int main(int argc, char* argv[]) { @@ -17,9 +19,11 @@ int main(int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, snakeCreateWidgets, &ctx); ctx.window = window; @@ -31,61 +35,64 @@ int main(int argc, char* argv[]) { bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + struct AppEvent event; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - should_close = true; - break; - - case APP_EVENT_RESULT: { - uint32_t launch_id = event.result.launch_id; - - if (launch_id == ctx.selectionDialogId && ctx.selectionDialogId != 0) { - ctx.selectionDialogId = 0; - int32_t selection = event.result.result; - app_manager_stop(launch_id); - - if (selection == SNAKE_SELECTION_HOW_TO_PLAY) { - snakeShowHelpDialog(&ctx); - } else if (selection >= SNAKE_SELECTION_EASY && selection <= SNAKE_SELECTION_HELL) { + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + should_close = true; + break; + + case APP_EVENT_RESULT: { + uint32_t launch_id = event.result.launch_id; + + if (launch_id == ctx.selectionDialogId && ctx.selectionDialogId != 0) { + ctx.selectionDialogId = 0; + int32_t selection = event.result.result; + app_manager_stop(launch_id); + + if (selection == SNAKE_SELECTION_HOW_TO_PLAY) { + snakeShowHelpDialog(&ctx); + } else if (selection >= SNAKE_SELECTION_EASY && selection <= SNAKE_SELECTION_HELL) { + if (lvgl_try_lock(LVGL_LOCK_TIMEOUT)) { + snakeStartGame(&ctx, selection); + lvgl_unlock(); + } + } else { + // Closed without selecting - close self. + should_close = true; + } + + } else if (launch_id == ctx.helpDialogId && ctx.helpDialogId != 0) { + ctx.helpDialogId = 0; + app_manager_stop(launch_id); + // Return to selection dialog + snakeShowSelectionDialog(&ctx); + + } else if (launch_id == ctx.gameOverDialogId && ctx.gameOverDialogId != 0) { + ctx.gameOverDialogId = 0; + app_manager_stop(launch_id); + // Game has genuinely ended - clear it and return to the selection dialog. if (lvgl_try_lock(LVGL_LOCK_TIMEOUT)) { - snakeStartGame(&ctx, selection); + snakeClearGame(&ctx); lvgl_unlock(); } - } else { - // Closed without selecting - close self. - should_close = true; - } - - } else if (launch_id == ctx.helpDialogId && ctx.helpDialogId != 0) { - ctx.helpDialogId = 0; - app_manager_stop(launch_id); - // Return to selection dialog - snakeShowSelectionDialog(&ctx); - - } else if (launch_id == ctx.gameOverDialogId && ctx.gameOverDialogId != 0) { - ctx.gameOverDialogId = 0; - app_manager_stop(launch_id); - // Game has genuinely ended - clear it and return to the selection dialog. - if (lvgl_try_lock(LVGL_LOCK_TIMEOUT)) { - snakeClearGame(&ctx); - lvgl_unlock(); + snakeShowSelectionDialog(&ctx); } - snakeShowSelectionDialog(&ctx); + break; } - break; - } - default: - break; + default: + break; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); snakeTeardown(&ctx); return 0; diff --git a/Apps/Snake/manifest.properties b/Apps/Snake/manifest.properties index 678fd45..394169d 100644 --- a/Apps/Snake/manifest.properties +++ b/Apps/Snake/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.snake -app.version.name=0.13.0 -app.version.code=13 +app.version.name=0.14.0 +app.version.code=14 app.name=Snake app.description=Classic Snake game diff --git a/Apps/TamaTac/main/Source/main.cpp b/Apps/TamaTac/main/Source/main.cpp index 3151c9e..bb398ec 100644 --- a/Apps/TamaTac/main/Source/main.cpp +++ b/Apps/TamaTac/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + #include extern "C" { @@ -22,53 +24,58 @@ int main(int argc, char* argv[]) { tamaTacInit(ctx.get()); + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, tamaTacCreateWidgets, ctx.get()); ctx->window = window; bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + struct AppEvent event; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - should_close = true; - break; - - case APP_EVENT_RESULT: { - uint32_t launch_id = event.result.launch_id; - if (launch_id == ctx->resetDialogId && ctx->resetDialogId != 0) { - ctx->resetDialogId = 0; - int32_t buttonIndex = event.result.result; - - if (buttonIndex == 0) { - // User picked "Reset" (first button) - if (ctx->timerMutex) xSemaphoreTake(ctx->timerMutex, portMAX_DELAY); - - ctx->petLogic.reset(); - ctx->petLogic.saveState(); - ctx->lastKnownStage = LifeStage::Egg; - ctx->pendingResetUI = true; // Defer UI update to MainView's anim timer - - if (ctx->timerMutex) xSemaphoreGive(ctx->timerMutex); + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + should_close = true; + break; + + case APP_EVENT_RESULT: { + uint32_t launch_id = event.result.launch_id; + if (launch_id == ctx->resetDialogId && ctx->resetDialogId != 0) { + ctx->resetDialogId = 0; + int32_t buttonIndex = event.result.result; + + if (buttonIndex == 0) { + // User picked "Reset" (first button) + if (ctx->timerMutex) xSemaphoreTake(ctx->timerMutex, portMAX_DELAY); + + ctx->petLogic.reset(); + ctx->petLogic.saveState(); + ctx->lastKnownStage = LifeStage::Egg; + ctx->pendingResetUI = true; // Defer UI update to MainView's anim timer + + if (ctx->timerMutex) xSemaphoreGive(ctx->timerMutex); + } + app_manager_stop(launch_id); } - app_manager_stop(launch_id); + break; } - break; - } - default: - break; + default: + break; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); tamaTacTeardown(ctx.get()); return 0; diff --git a/Apps/TamaTac/manifest.properties b/Apps/TamaTac/manifest.properties index b11549a..b0290ca 100644 --- a/Apps/TamaTac/manifest.properties +++ b/Apps/TamaTac/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.tamatac -app.version.name=0.8.0 -app.version.code=8 +app.version.name=0.9.0 +app.version.code=9 app.name=TamaTac app.description=Virtual pet inspired by Tamagotchi. Only runs on devices with PSRAM. diff --git a/Apps/TodoList/main/Source/main.cpp b/Apps/TodoList/main/Source/main.cpp index 69b9682..513f062 100644 --- a/Apps/TodoList/main/Source/main.cpp +++ b/Apps/TodoList/main/Source/main.cpp @@ -6,6 +6,8 @@ #include +#include + #include extern "C" { @@ -18,26 +20,31 @@ int main(int argc, char* argv[]) { auto ctx = std::make_unique(); ctx->appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, todoListCreateWidgets, ctx.get()); ctx->window = window; bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, 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; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); todoListTeardown(ctx.get()); return 0; diff --git a/Apps/TodoList/manifest.properties b/Apps/TodoList/manifest.properties index 800451f..fe93745 100644 --- a/Apps/TodoList/manifest.properties +++ b/Apps/TodoList/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.todolist -app.version.name=0.10.0 -app.version.code=10 +app.version.name=0.11.0 +app.version.code=11 app.name=Todo List app.description=Simple task list manager diff --git a/Apps/TwoEleven/main/Source/main.cpp b/Apps/TwoEleven/main/Source/main.cpp index c0743ee..a45c7e9 100644 --- a/Apps/TwoEleven/main/Source/main.cpp +++ b/Apps/TwoEleven/main/Source/main.cpp @@ -7,6 +7,8 @@ #include #include +#include + extern "C" { int main(int argc, char* argv[]) { @@ -17,9 +19,11 @@ int main(int argc, char* argv[]) { Context ctx {}; ctx.appInstanceId = app_instance_id; + struct TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + struct AppEventSubscription sub {}; - sub.app_instance_id = app_instance_id; - app_event_subscribe(&sub); + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); WindowId window = window_manager_create(app_instance_id, twoElevenCreateWidgets, &ctx); ctx.window = window; @@ -31,61 +35,64 @@ int main(int argc, char* argv[]) { bool should_close = false; while (!should_close) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + struct AppEvent event; - if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) { - break; - } - switch (event.type) { - case APP_EVENT_CLOSE: - should_close = true; - break; - - case APP_EVENT_RESULT: { - uint32_t launch_id = event.result.launch_id; - - if (launch_id == ctx.selectionDialogId && ctx.selectionDialogId != 0) { - ctx.selectionDialogId = 0; - int32_t selection = event.result.result; - app_manager_stop(launch_id); - - if (selection == TWOELEVEN_SELECTION_HOW_TO_PLAY) { - twoElevenShowHelpDialog(&ctx); - } else if (selection >= TWOELEVEN_SELECTION_3X3 && selection <= TWOELEVEN_SELECTION_6X6) { + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + should_close = true; + break; + + case APP_EVENT_RESULT: { + uint32_t launch_id = event.result.launch_id; + + if (launch_id == ctx.selectionDialogId && ctx.selectionDialogId != 0) { + ctx.selectionDialogId = 0; + int32_t selection = event.result.result; + app_manager_stop(launch_id); + + if (selection == TWOELEVEN_SELECTION_HOW_TO_PLAY) { + twoElevenShowHelpDialog(&ctx); + } else if (selection >= TWOELEVEN_SELECTION_3X3 && selection <= TWOELEVEN_SELECTION_6X6) { + if (lvgl_try_lock(LVGL_LOCK_TIMEOUT)) { + twoElevenStartGame(&ctx, selection); + lvgl_unlock(); + } + } else { + // Closed without selecting - close self. + should_close = true; + } + + } else if (launch_id == ctx.helpDialogId && ctx.helpDialogId != 0) { + ctx.helpDialogId = 0; + app_manager_stop(launch_id); + // Return to selection dialog + twoElevenShowSelectionDialog(&ctx); + + } else if (launch_id == ctx.gameOverDialogId && ctx.gameOverDialogId != 0) { + ctx.gameOverDialogId = 0; + app_manager_stop(launch_id); + // Game has genuinely ended - clear it and return to the selection dialog. if (lvgl_try_lock(LVGL_LOCK_TIMEOUT)) { - twoElevenStartGame(&ctx, selection); + twoElevenClearGame(&ctx); lvgl_unlock(); } - } else { - // Closed without selecting - close self. - should_close = true; - } - - } else if (launch_id == ctx.helpDialogId && ctx.helpDialogId != 0) { - ctx.helpDialogId = 0; - app_manager_stop(launch_id); - // Return to selection dialog - twoElevenShowSelectionDialog(&ctx); - - } else if (launch_id == ctx.gameOverDialogId && ctx.gameOverDialogId != 0) { - ctx.gameOverDialogId = 0; - app_manager_stop(launch_id); - // Game has genuinely ended - clear it and return to the selection dialog. - if (lvgl_try_lock(LVGL_LOCK_TIMEOUT)) { - twoElevenClearGame(&ctx); - lvgl_unlock(); + twoElevenShowSelectionDialog(&ctx); } - twoElevenShowSelectionDialog(&ctx); + break; } - break; - } - default: - break; + default: + break; + } + if (should_close) break; } } window_manager_remove(window); - app_event_unsubscribe(&sub); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); twoElevenTeardown(&ctx); return 0; diff --git a/Apps/TwoEleven/manifest.properties b/Apps/TwoEleven/manifest.properties index e2a2583..dd0e55a 100644 --- a/Apps/TwoEleven/manifest.properties +++ b/Apps/TwoEleven/manifest.properties @@ -2,7 +2,7 @@ manifest.version=0.2 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 app.id=tactility.twoeleven -app.version.name=0.12.0 -app.version.code=12 +app.version.name=0.13.0 +app.version.code=13 app.name=2048 app.description=A fun, customizable 2048 sliding tile game for tactility!\nSlide tiles to combine numbers and reach 2048.\nChoose grid sizes: 3x3 (easy), 4x4 (classic), 5x5, or 6x6 (expert). diff --git a/Tools/bump-versions.py b/Tools/bump-versions.py new file mode 100755 index 0000000..52c2905 --- /dev/null +++ b/Tools/bump-versions.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""Bump app.version.name and app.version.code in all app manifests. + +Usage: + python Tools/bump-versions.py major # 0.10.0 -> 1.0.0 + python Tools/bump-versions.py minor # 0.10.0 -> 0.11.0 + python Tools/bump-versions.py patch # 0.10.0 -> 0.10.1 + +Regardless of the argument, app.version.code is incremented by 1. +""" + +import sys +from pathlib import Path + +APPS_DIR = Path(__file__).resolve().parent.parent / "Apps" +MANIFEST = "manifest.properties" +VERSION_NAME_KEY = "app.version.name" +VERSION_CODE_KEY = "app.version.code" + + +def parse_version(name: str) -> list[int]: + parts = name.strip().split(".") + return [int(p) for p in parts] + + +def bump_version(name: str, component: str) -> str: + parts = parse_version(name) + while len(parts) < 3: + parts.append(0) + + if component == "major": + parts[0] += 1 + parts[1] = 0 + parts[2] = 0 + elif component == "minor": + parts[1] += 1 + parts[2] = 0 + elif component == "patch": + parts[2] += 1 + + return ".".join(str(p) for p in parts) + + +def process_manifest(path: Path, component: str) -> None: + lines = path.read_text().splitlines(keepends=True) + changed = False + new_lines = [] + + for line in lines: + stripped = line.strip() + if stripped.startswith(VERSION_NAME_KEY + "="): + old_value = stripped.split("=", 1)[1].strip() + new_value = bump_version(old_value, component) + new_lines.append(line.replace(old_value, new_value)) + changed = True + elif stripped.startswith(VERSION_CODE_KEY + "="): + old_code = int(stripped.split("=", 1)[1].strip()) + new_code = old_code + 1 + new_lines.append(line.replace(str(old_code), str(new_code), 1)) + changed = True + else: + new_lines.append(line) + + if changed: + path.write_text("".join(new_lines)) + print(f" {path.parent.name}: bumped") + + +def main() -> int: + if len(sys.argv) != 2 or sys.argv[1] not in ("major", "minor", "patch"): + print(f"Usage: {sys.argv[0]} major|minor|patch", file=sys.stderr) + return 1 + + component = sys.argv[1] + + manifests = sorted(APPS_DIR.glob(f"*/{MANIFEST}")) + if not manifests: + print(f"No manifests found in {APPS_DIR}", file=sys.stderr) + return 1 + + print(f"Bumping {component} (+ version.code) in {len(manifests)} apps:\n") + for manifest in manifests: + process_manifest(manifest, component) + + print("\nDone.") + return 0 + + +if __name__ == "__main__": + sys.exit(main())