Refactored events (#621)
Event handling: - Added unified event handling for application, system, and Wi‑Fi events. - Updated all apps to reflect event handling changes Wi-Fi: - Refactored event handling from listener interface to task & event group. - Added direct Wi‑Fi radio controls and improved event subscriptions. - Wi‑Fi screens now refresh asynchronously and handle unavailable devices more gracefully. - Enabled Wi‑Fi by default on in dts files, but radio on/off is still done by code. The main reason was reliable event subscription and consistent devicetree states. - Improved Wi‑Fi shutdown cleanup and radio-state handling. Other: - Renamed the Kernel Display app to Display.
This commit is contained in:
committed by
GitHub
parent
db48dfe812
commit
ab75d2022d
@@ -1,10 +1,10 @@
|
|||||||
# Architecture: App Framework
|
# Architecture: App Framework
|
||||||
|
|
||||||
Apps are event-driven, C API (`app-module`, `<app/*.h>`), 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`, `<app/*.h>`), 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`):
|
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.
|
- `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()` (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()`.
|
- 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`).
|
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`).
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
wifi0 {
|
wifi0 {
|
||||||
compatible = "espressif,esp32-wifi-pinned";
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
status = "disabled";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
|
|||||||
@@ -11,11 +11,15 @@
|
|||||||
|
|
||||||
## Higher Priority
|
## 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.
|
- 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.
|
||||||
|
- 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.
|
- AppHubApp: Prevent download callbacks from accessing a destroyed view.
|
||||||
- Move USB host task stacks to SPIRAM when available: esp32_usbhost*.cpp
|
- 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.
|
- Get rid of WiFi service (Wifi.cpp/h) in Tactility.cpp
|
||||||
When that's changed reduce LVGL callstack size in Tactility.cpp run()
|
|
||||||
- Make it more clear to end-users that an SD card is required to run Tactility
|
- 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)
|
- 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
|
- Wrap file operations like fopen/fclose with file_mutex
|
||||||
|
|||||||
@@ -6,15 +6,14 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tactility/concurrent/task_event_group.h>
|
||||||
#include <tactility/error.h>
|
#include <tactility/error.h>
|
||||||
#include <tactility/freertos/freertos.h>
|
|
||||||
#include <tactility/freertos/task.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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 {
|
enum AppEventType {
|
||||||
APP_EVENT_RESULT, // struct AppResultEventData
|
APP_EVENT_RESULT, // struct AppResultEventData
|
||||||
APP_EVENT_CLOSE, // no data - terminate now, permanently
|
APP_EVENT_CLOSE, // no data - terminate now, permanently
|
||||||
@@ -51,30 +50,62 @@ struct AppEvent {
|
|||||||
* Caller-owned subscription node. Unlike TactilityKernel's system_event poll subscription
|
* 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
|
* (which coalesces to the latest value), this queues events by value (FIFO) since dropping an
|
||||||
* APP_EVENT_RESULT would be unacceptable.
|
* APP_EVENT_RESULT would be unacceptable.
|
||||||
* @warning Fields other than `app_instance_id` are for internal use only; do not read or write
|
* @warning Fields other than `bit` are for internal use only; do not read or write them
|
||||||
* them directly.
|
* directly.
|
||||||
*/
|
*/
|
||||||
struct AppEventSubscription {
|
struct AppEventSubscription {
|
||||||
/** The app instance this subscription receives events for; set by the caller before app_event_subscribe(). */
|
/** Set by app_event_subscribe()/app_event_subscribe_with_app_id(). Read-only for the
|
||||||
AppInstanceId app_instance_id;
|
* 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;
|
||||||
|
|
||||||
TaskHandle_t task;
|
struct {
|
||||||
|
/** The app instance this subscription receives events for; set by
|
||||||
|
* app_event_subscribe()/app_event_subscribe_with_app_id(). */
|
||||||
|
AppInstanceId app_instance_id;
|
||||||
|
|
||||||
struct AppEvent queue[APP_EVENT_QUEUE_CAPACITY];
|
/** Caller-owned, borrowed; set by app_event_subscribe(). */
|
||||||
uint8_t head;
|
struct TaskEventGroup* event_group;
|
||||||
uint8_t count;
|
|
||||||
|
|
||||||
struct AppEventSubscription* next;
|
struct AppEvent queue[APP_EVENT_QUEUE_CAPACITY];
|
||||||
|
uint8_t head;
|
||||||
|
uint8_t count;
|
||||||
|
|
||||||
|
struct AppEventSubscription* next;
|
||||||
|
} internal;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a subscription for events addressed to @a sub->app_instance_id.
|
* Register a subscription for events addressed to the calling app's own instance (identified via
|
||||||
* @warning Does not work in ISR context.
|
* app_scheduler_current_app_id()).
|
||||||
* @param[in,out] sub subscription to register; caller sets @a sub->app_instance_id beforehand,
|
* @warning Does not work in ISR context. Must be called from the app's own task.
|
||||||
* owns the storage, and must keep it alive (and stationary) until unsubscribed
|
* @param[in,out] sub subscription to register; owns the storage, must stay alive (and
|
||||||
* @return ERROR_NONE on success
|
* 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,
|
||||||
|
* 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; @a sub was not registered
|
||||||
|
* @retval ERROR_INVALID_STATE @a sub is already registered
|
||||||
*/
|
*/
|
||||||
error_t app_event_subscribe(struct AppEventSubscription* sub);
|
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.
|
* Remove a previously registered subscription.
|
||||||
@@ -94,11 +125,14 @@ error_t app_event_unsubscribe(struct AppEventSubscription* sub);
|
|||||||
error_t app_event_emit(AppInstanceId app_instance_id, const struct AppEvent* event);
|
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_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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ extern "C" {
|
|||||||
/**
|
/**
|
||||||
* Entry point signature for an APP_LOCATION_MEMORY app: a function linked directly into this
|
* 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,
|
* 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
|
* blocking for the app's whole lifetime - same contract as an external app's main(). Use
|
||||||
* @a app_instance_id identifying this running instance (use it with
|
* 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
|
* app_event_subscribe()/window_manager_create()/etc.). The instance closes when this function
|
||||||
* returns - no separate call is needed.
|
* returns - no separate call is needed.
|
||||||
* AppManifest::location.location holds this cast to void*.
|
* 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;
|
typedef void* AppRuntime;
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ error_t api_load(AppLocation location, AppRuntime* out_runtime) {
|
|||||||
return ERROR_NONE;
|
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<AppMainFn>(runtime);
|
auto entry = reinterpret_cast<AppMainFn>(runtime);
|
||||||
return entry(app_instance_id, argc, argv);
|
return entry(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void api_unload(AppRuntime /*unused*/) {
|
void api_unload(AppRuntime /*unused*/) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <tactility/concurrent/mutex.h>
|
#include <tactility/concurrent/mutex.h>
|
||||||
#include <tactility/time.h>
|
#include <tactility/time.h>
|
||||||
@@ -7,7 +8,7 @@
|
|||||||
/**
|
/**
|
||||||
* Intrusive singly-linked list of subscriptions, keyed by app_instance_id.
|
* 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
|
* 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;
|
static AppEventSubscription* subscriptions = nullptr;
|
||||||
|
|
||||||
@@ -21,32 +22,55 @@ static AppEventMutex subscriptions_mutex;
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
error_t app_event_subscribe(AppEventSubscription* sub) {
|
error_t app_event_subscribe_with_app_id(AppEventSubscription* sub, TaskEventGroup* event_group, AppInstanceId app_instance_id) {
|
||||||
sub->task = xTaskGetCurrentTaskHandle();
|
uint32_t bit;
|
||||||
sub->head = 0;
|
error_t claim_result = task_event_group_claim_bit(event_group, &bit);
|
||||||
sub->count = 0;
|
if (claim_result != ERROR_NONE) {
|
||||||
|
return claim_result;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&subscriptions_mutex.handle);
|
mutex_lock(&subscriptions_mutex.handle);
|
||||||
sub->next = subscriptions;
|
|
||||||
|
// 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.app_instance_id = app_instance_id;
|
||||||
|
sub->internal.event_group = event_group;
|
||||||
|
sub->internal.head = 0;
|
||||||
|
sub->internal.count = 0;
|
||||||
|
sub->internal.next = subscriptions;
|
||||||
subscriptions = sub;
|
subscriptions = sub;
|
||||||
mutex_unlock(&subscriptions_mutex.handle);
|
mutex_unlock(&subscriptions_mutex.handle);
|
||||||
|
|
||||||
return ERROR_NONE;
|
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 app_event_unsubscribe(AppEventSubscription* sub) {
|
||||||
error_t result = ERROR_NOT_FOUND;
|
error_t result = ERROR_NOT_FOUND;
|
||||||
|
|
||||||
mutex_lock(&subscriptions_mutex.handle);
|
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) {
|
if (*link == sub) {
|
||||||
*link = sub->next;
|
*link = sub->internal.next;
|
||||||
result = ERROR_NONE;
|
result = ERROR_NONE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&subscriptions_mutex.handle);
|
mutex_unlock(&subscriptions_mutex.handle);
|
||||||
|
|
||||||
|
if (result == ERROR_NONE) {
|
||||||
|
task_event_group_release_bit(sub->internal.event_group, sub->bit);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,23 +81,23 @@ error_t app_event_emit(AppInstanceId app_instance_id, const AppEvent* event) {
|
|||||||
error_t result = ERROR_NOT_FOUND;
|
error_t result = ERROR_NOT_FOUND;
|
||||||
|
|
||||||
mutex_lock(&subscriptions_mutex.handle);
|
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) {
|
if (sub->internal.app_instance_id != app_instance_id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sub->count >= APP_EVENT_QUEUE_CAPACITY) {
|
if (sub->internal.count >= APP_EVENT_QUEUE_CAPACITY) {
|
||||||
result = ERROR_RESOURCE;
|
result = ERROR_RESOURCE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t tail = (sub->head + sub->count) % APP_EVENT_QUEUE_CAPACITY;
|
uint8_t tail = (sub->internal.head + sub->internal.count) % APP_EVENT_QUEUE_CAPACITY;
|
||||||
sub->queue[tail] = stamped_event;
|
sub->internal.queue[tail] = stamped_event;
|
||||||
sub->count++;
|
sub->internal.count++;
|
||||||
if (result != ERROR_RESOURCE) {
|
if (result != ERROR_RESOURCE) {
|
||||||
result = ERROR_NONE;
|
result = ERROR_NONE;
|
||||||
}
|
}
|
||||||
xTaskNotifyGive(sub->task);
|
task_event_group_signal(sub->internal.event_group, sub->bit);
|
||||||
}
|
}
|
||||||
mutex_unlock(&subscriptions_mutex.handle);
|
mutex_unlock(&subscriptions_mutex.handle);
|
||||||
|
|
||||||
@@ -82,33 +106,17 @@ error_t app_event_emit(AppInstanceId app_instance_id, const AppEvent* event) {
|
|||||||
|
|
||||||
static bool try_pop(AppEventSubscription* sub, AppEvent* out_event) {
|
static bool try_pop(AppEventSubscription* sub, AppEvent* out_event) {
|
||||||
mutex_lock(&subscriptions_mutex.handle);
|
mutex_lock(&subscriptions_mutex.handle);
|
||||||
bool has_event = sub->count > 0;
|
bool has_event = sub->internal.count > 0;
|
||||||
if (has_event) {
|
if (has_event) {
|
||||||
*out_event = sub->queue[sub->head];
|
*out_event = sub->internal.queue[sub->internal.head];
|
||||||
sub->head = (sub->head + 1) % APP_EVENT_QUEUE_CAPACITY;
|
sub->internal.head = (sub->internal.head + 1) % APP_EVENT_QUEUE_CAPACITY;
|
||||||
sub->count--;
|
sub->internal.count--;
|
||||||
}
|
}
|
||||||
mutex_unlock(&subscriptions_mutex.handle);
|
mutex_unlock(&subscriptions_mutex.handle);
|
||||||
return has_event;
|
return has_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_t app_event_await(AppEventSubscription* sub, AppEvent* out_event, TickType_t timeout) {
|
error_t app_event_poll(AppEventSubscription* sub, AppEvent* out_event) {
|
||||||
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.
|
|
||||||
return try_pop(sub, out_event) ? ERROR_NONE : ERROR_TIMEOUT;
|
return try_pop(sub, out_event) ? ERROR_NONE : ERROR_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <service/manager.h>
|
#include <service/manager.h>
|
||||||
|
|
||||||
|
#include <tactility/concurrent/task_event_group.h>
|
||||||
#include <tactility/error.h>
|
#include <tactility/error.h>
|
||||||
#include <tactility/module.h>
|
#include <tactility/module.h>
|
||||||
|
|
||||||
@@ -18,9 +19,10 @@ extern ServiceManifest app_internal_loader_service_manifest;
|
|||||||
const ModuleSymbol app_module_symbols[] = {
|
const ModuleSymbol app_module_symbols[] = {
|
||||||
// app/event
|
// app/event
|
||||||
DEFINE_MODULE_SYMBOL(app_event_subscribe),
|
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_unsubscribe),
|
||||||
DEFINE_MODULE_SYMBOL(app_event_emit),
|
DEFINE_MODULE_SYMBOL(app_event_emit),
|
||||||
DEFINE_MODULE_SYMBOL(app_event_await),
|
DEFINE_MODULE_SYMBOL(app_event_poll),
|
||||||
// app/install
|
// app/install
|
||||||
DEFINE_MODULE_SYMBOL(app_get_install_path),
|
DEFINE_MODULE_SYMBOL(app_get_install_path),
|
||||||
DEFINE_MODULE_SYMBOL(app_install),
|
DEFINE_MODULE_SYMBOL(app_install),
|
||||||
|
|||||||
@@ -6,10 +6,12 @@
|
|||||||
#include <tactility/delay.h>
|
#include <tactility/delay.h>
|
||||||
#include <tactility/time.h>
|
#include <tactility/time.h>
|
||||||
|
|
||||||
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 {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = 1;
|
CHECK_EQ(app_event_subscribe_with_app_id(&sub, &event_group, 1), ERROR_NONE);
|
||||||
CHECK_EQ(app_event_subscribe(&sub), ERROR_NONE);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < 3; i++) {
|
for (uint32_t i = 0; i < 3; i++) {
|
||||||
AppEvent event { .type = APP_EVENT_RESULT, .timestamp = 0, .result = { .launch_id = i, .result = 0 } };
|
AppEvent event { .type = APP_EVENT_RESULT, .timestamp = 0, .result = { .launch_id = i, .result = 0 } };
|
||||||
@@ -18,32 +20,38 @@ TEST_CASE("app_event_subscribe/_await deliver events in FIFO order") {
|
|||||||
|
|
||||||
for (uint32_t i = 0; i < 3; i++) {
|
for (uint32_t i = 0; i < 3; i++) {
|
||||||
AppEvent out {};
|
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.type, APP_EVENT_RESULT);
|
||||||
CHECK_EQ(out.result.launch_id, i);
|
CHECK_EQ(out.result.launch_id, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
app_event_unsubscribe(&sub);
|
app_event_unsubscribe(&sub);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("app_event_emit only delivers to subscriptions for that app_instance_id") {
|
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 {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = 10;
|
app_event_subscribe_with_app_id(&sub, &event_group, 10);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
|
AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
|
||||||
CHECK_EQ(app_event_emit(11, &event), ERROR_NOT_FOUND);
|
CHECK_EQ(app_event_emit(11, &event), ERROR_NOT_FOUND);
|
||||||
|
|
||||||
AppEvent out {};
|
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);
|
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") {
|
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 {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = 20;
|
app_event_subscribe_with_app_id(&sub, &event_group, 20);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < APP_EVENT_QUEUE_CAPACITY; i++) {
|
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 } };
|
AppEvent event { .type = APP_EVENT_RESULT, .timestamp = 0, .result = { .launch_id = i, .result = 0 } };
|
||||||
@@ -57,42 +65,52 @@ 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.
|
// The already-queued events survive, in order, and the dropped one never arrives.
|
||||||
for (uint32_t i = 0; i < APP_EVENT_QUEUE_CAPACITY; i++) {
|
for (uint32_t i = 0; i < APP_EVENT_QUEUE_CAPACITY; i++) {
|
||||||
AppEvent out {};
|
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);
|
CHECK_EQ(out.result.launch_id, i);
|
||||||
}
|
}
|
||||||
AppEvent out {};
|
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);
|
app_event_unsubscribe(&sub);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("app_event_unsubscribe stops further delivery") {
|
TEST_CASE("app_event_unsubscribe stops further delivery") {
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = 30;
|
app_event_subscribe_with_app_id(&sub, &event_group, 30);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
CHECK_EQ(app_event_unsubscribe(&sub), ERROR_NONE);
|
CHECK_EQ(app_event_unsubscribe(&sub), ERROR_NONE);
|
||||||
CHECK_EQ(app_event_unsubscribe(&sub), ERROR_NOT_FOUND);
|
CHECK_EQ(app_event_unsubscribe(&sub), ERROR_NOT_FOUND);
|
||||||
|
|
||||||
AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
|
AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
|
||||||
CHECK_EQ(app_event_emit(30, &event), ERROR_NOT_FOUND);
|
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 {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = 40;
|
app_event_subscribe_with_app_id(&sub, &event_group, 40);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
AppEvent out {};
|
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);
|
app_event_unsubscribe(&sub);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("app_event_emit stamps the event with the current boot-relative time") {
|
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 {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = 50;
|
app_event_subscribe_with_app_id(&sub, &event_group, 50);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
auto before = static_cast<uint64_t>(get_micros_since_boot());
|
auto before = static_cast<uint64_t>(get_micros_since_boot());
|
||||||
AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
|
AppEvent event { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
|
||||||
@@ -100,17 +118,20 @@ TEST_CASE("app_event_emit stamps the event with the current boot-relative time")
|
|||||||
auto after = static_cast<uint64_t>(get_micros_since_boot());
|
auto after = static_cast<uint64_t>(get_micros_since_boot());
|
||||||
|
|
||||||
AppEvent out {};
|
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_GE(out.timestamp, before);
|
||||||
CHECK_LE(out.timestamp, after);
|
CHECK_LE(out.timestamp, after);
|
||||||
|
|
||||||
app_event_unsubscribe(&sub);
|
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 {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = 60;
|
CHECK_EQ(app_event_subscribe_with_app_id(&sub, &event_group, 60), ERROR_NONE);
|
||||||
CHECK_EQ(app_event_subscribe(&sub), ERROR_NONE);
|
|
||||||
|
|
||||||
auto* thread = thread_alloc_full(
|
auto* thread = thread_alloc_full(
|
||||||
"app-event-emitter",
|
"app-event-emitter",
|
||||||
@@ -126,12 +147,14 @@ TEST_CASE("app_event_await wakes when the event is emitted from another task") {
|
|||||||
);
|
);
|
||||||
CHECK_EQ(thread_start(thread), ERROR_NONE);
|
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 {};
|
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(out.type, APP_EVENT_CLOSE);
|
||||||
|
|
||||||
CHECK_EQ(thread_join(thread, pdMS_TO_TICKS(2000), 1), ERROR_NONE);
|
CHECK_EQ(thread_join(thread, pdMS_TO_TICKS(2000), 1), ERROR_NONE);
|
||||||
thread_free(thread);
|
thread_free(thread);
|
||||||
|
|
||||||
app_event_unsubscribe(&sub);
|
app_event_unsubscribe(&sub);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/loader.h>
|
#include <app/loader.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <service/manager.h>
|
#include <service/manager.h>
|
||||||
|
|
||||||
@@ -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
|
// 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
|
// 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()).
|
// 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);
|
stash_received_arguments(argc, argv);
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
@@ -66,21 +67,30 @@ int32_t fake_run(void*, uint32_t app_instance_id, int argc, char* argv[]) {
|
|||||||
return static_cast<int32_t>(strtol(argv[0], nullptr, 10));
|
return static_cast<int32_t>(strtol(argv[0], nullptr, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = app_instance_id;
|
app_event_subscribe(&sub, &event_group);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
AppEvent event {};
|
if (task_event_group_wait_any(&event_group, nullptr, pdMS_TO_TICKS(5000)) != ERROR_NONE) {
|
||||||
if (app_event_await(&sub, &event, pdMS_TO_TICKS(5000)) != ERROR_NONE) {
|
|
||||||
break; // safety net so a bug here can't hang the test suite
|
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);
|
app_event_unsubscribe(&sub);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +139,8 @@ void ensure_memory_loader_registered() {
|
|||||||
|
|
||||||
// Same subscribe-until-close contract as fake_run() above, but called directly as an AppMainFn -
|
// 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.
|
// 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[]) {
|
int32_t fake_app_main(int argc, char* argv[]) {
|
||||||
return fake_run(nullptr, app_instance_id, argc, 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.
|
// Wraps app_manager_get_topmost_instance_id() for terse assertions: 0 if no app is Active.
|
||||||
@@ -329,9 +339,11 @@ 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);
|
REQUIRE_EQ(app_manager_start("test.app.parent", &parent_id), ERROR_NONE);
|
||||||
CHECK(wait_for_state(parent_id, APP_INSTANCE_STATE_ACTIVE, 1000));
|
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 {};
|
AppEventSubscription parent_sub {};
|
||||||
parent_sub.app_instance_id = parent_id;
|
REQUIRE_EQ(app_event_subscribe_with_app_id(&parent_sub, &parent_event_group, parent_id), ERROR_NONE);
|
||||||
REQUIRE_EQ(app_event_subscribe(&parent_sub), ERROR_NONE);
|
|
||||||
|
|
||||||
const char* argv[] = { "42" };
|
const char* argv[] = { "42" };
|
||||||
uint32_t child_id = 0;
|
uint32_t child_id = 0;
|
||||||
@@ -340,13 +352,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.
|
// Launching a modal child never touches the parent's own task/state.
|
||||||
CHECK_EQ(app_manager_get_state(parent_id), APP_INSTANCE_STATE_ACTIVE);
|
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 {};
|
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.type, APP_EVENT_RESULT);
|
||||||
CHECK_EQ(event.result.launch_id, child_id);
|
CHECK_EQ(event.result.launch_id, child_id);
|
||||||
CHECK_EQ(event.result.result, 42);
|
CHECK_EQ(event.result.result, 42);
|
||||||
|
|
||||||
app_event_unsubscribe(&parent_sub);
|
app_event_unsubscribe(&parent_sub);
|
||||||
|
task_event_group_destruct(&parent_event_group);
|
||||||
app_manager_stop(child_id);
|
app_manager_stop(child_id);
|
||||||
app_manager_stop(parent_id);
|
app_manager_stop(parent_id);
|
||||||
app_manager_remove("test.app.parent");
|
app_manager_remove("test.app.parent");
|
||||||
@@ -365,9 +379,11 @@ 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);
|
REQUIRE_EQ(app_manager_start("test.app.parent2", &parent_id), ERROR_NONE);
|
||||||
CHECK(wait_for_state(parent_id, APP_INSTANCE_STATE_ACTIVE, 1000));
|
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 {};
|
AppEventSubscription parent_sub {};
|
||||||
parent_sub.app_instance_id = parent_id;
|
REQUIRE_EQ(app_event_subscribe_with_app_id(&parent_sub, &parent_event_group, parent_id), ERROR_NONE);
|
||||||
REQUIRE_EQ(app_event_subscribe(&parent_sub), ERROR_NONE);
|
|
||||||
|
|
||||||
uint32_t child_id = 0;
|
uint32_t child_id = 0;
|
||||||
// No parameters - fake_run falls through to its normal CLOSE loop instead of acting as a
|
// No parameters - fake_run falls through to its normal CLOSE loop instead of acting as a
|
||||||
@@ -377,13 +393,15 @@ TEST_CASE("app_manager_start_for_result delivers the child's own return value as
|
|||||||
|
|
||||||
app_manager_stop(child_id); // force-close
|
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 {};
|
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.type, APP_EVENT_RESULT);
|
||||||
CHECK_EQ(event.result.launch_id, child_id);
|
CHECK_EQ(event.result.launch_id, child_id);
|
||||||
CHECK_EQ(event.result.result, 0); // fake_run's CLOSE loop always returns 0
|
CHECK_EQ(event.result.result, 0); // fake_run's CLOSE loop always returns 0
|
||||||
|
|
||||||
app_event_unsubscribe(&parent_sub);
|
app_event_unsubscribe(&parent_sub);
|
||||||
|
task_event_group_destruct(&parent_event_group);
|
||||||
app_manager_stop(parent_id);
|
app_manager_stop(parent_id);
|
||||||
app_manager_remove("test.app.parent2");
|
app_manager_remove("test.app.parent2");
|
||||||
app_manager_remove("test.app.child2");
|
app_manager_remove("test.app.child2");
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#include "doctest.h"
|
||||||
|
|
||||||
|
#include <app/event.h>
|
||||||
|
|
||||||
|
#include <tactility/system_event.h>
|
||||||
|
|
||||||
|
// 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 {};
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ idf_component_register(
|
|||||||
SRCS ${SOURCES}
|
SRCS ${SOURCES}
|
||||||
INCLUDE_DIRS "include/"
|
INCLUDE_DIRS "include/"
|
||||||
PRIV_INCLUDE_DIRS "private/"
|
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}
|
PRIV_REQUIRES ${PRIV_REQUIRES_LIST}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
#include <tactility/time.h>
|
#include <tactility/time.h>
|
||||||
|
|
||||||
|
#include <TactilityCpp/Allocator.h>
|
||||||
|
|
||||||
#if defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED)
|
#if defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED)
|
||||||
#include <tactility/drivers/esp32_esp_hosted_ota.h>
|
#include <tactility/drivers/esp32_esp_hosted_ota.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -33,14 +35,8 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr size_t WIFI_MAX_CALLBACKS = 4;
|
|
||||||
constexpr uint16_t WIFI_SCAN_RECORD_LIMIT = 32;
|
constexpr uint16_t WIFI_SCAN_RECORD_LIMIT = 32;
|
||||||
|
|
||||||
struct WifiCallbackEntry {
|
|
||||||
WifiEventCallback fn = nullptr;
|
|
||||||
void* ctx = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Esp32WifiCtx {
|
struct Esp32WifiCtx {
|
||||||
Device* device = nullptr;
|
Device* device = nullptr;
|
||||||
|
|
||||||
@@ -66,9 +62,8 @@ struct Esp32WifiCtx {
|
|||||||
int32_t lastEventId = -1;
|
int32_t lastEventId = -1;
|
||||||
TickType_t lastEventTick = 0;
|
TickType_t lastEventTick = 0;
|
||||||
|
|
||||||
Mutex callbackMutex{};
|
Mutex subscriptionsMutex{};
|
||||||
WifiCallbackEntry callbacks[WIFI_MAX_CALLBACKS] = {};
|
WifiEventSubscription* subscriptions = nullptr;
|
||||||
size_t callbackCount = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GET_CTX(device) (static_cast<Esp32WifiCtx*>(device_get_driver_data(device)))
|
#define GET_CTX(device) (static_cast<Esp32WifiCtx*>(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) {
|
void fire_event(Esp32WifiCtx* ctx, WifiEvent event) {
|
||||||
WifiCallbackEntry local[WIFI_MAX_CALLBACKS];
|
mutex_lock(&ctx->subscriptionsMutex);
|
||||||
size_t count;
|
for (WifiEventSubscription* sub = ctx->subscriptions; sub != nullptr; sub = sub->internal.next) {
|
||||||
mutex_lock(&ctx->callbackMutex);
|
mutex_lock(&sub->internal.ring_mutex);
|
||||||
count = ctx->callbackCount;
|
if (sub->internal.count < WIFI_EVENT_QUEUE_CAPACITY) {
|
||||||
memcpy(local, ctx->callbacks, count * sizeof(WifiCallbackEntry));
|
uint8_t tail = (sub->internal.head + sub->internal.count) % WIFI_EVENT_QUEUE_CAPACITY;
|
||||||
mutex_unlock(&ctx->callbackMutex);
|
sub->internal.queue[tail] = event;
|
||||||
|
sub->internal.count++;
|
||||||
for (size_t i = 0; i < count; i++) {
|
}
|
||||||
local[i].fn(ctx->device, local[i].ctx, event);
|
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) ----
|
// ---- 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;
|
ctx->radioState = WIFI_RADIO_STATE_ON;
|
||||||
mutex_unlock(&ctx->mutex);
|
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");
|
LOG_I(TAG, "WiFi radio on");
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
@@ -306,6 +308,11 @@ void bring_down_wifi(Esp32WifiCtx* ctx) {
|
|||||||
ctx->radioState = WIFI_RADIO_STATE_OFF;
|
ctx->radioState = WIFI_RADIO_STATE_OFF;
|
||||||
mutex_unlock(&ctx->mutex);
|
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");
|
LOG_I(TAG, "WiFi radio off");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,38 +504,77 @@ error_t api_station_get_rssi(Device* device, int32_t* rssi) {
|
|||||||
return ERROR_NONE;
|
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);
|
auto* ctx = GET_CTX(device);
|
||||||
if (ctx == nullptr || callback == nullptr) return ERROR_INVALID_ARGUMENT;
|
if (ctx == nullptr) return ERROR_INVALID_STATE;
|
||||||
|
|
||||||
mutex_lock(&ctx->callbackMutex);
|
mutex_lock(&ctx->mutex);
|
||||||
if (ctx->callbackCount >= WIFI_MAX_CALLBACKS) {
|
bool already_on = ctx->radioState == WIFI_RADIO_STATE_ON;
|
||||||
mutex_unlock(&ctx->callbackMutex);
|
mutex_unlock(&ctx->mutex);
|
||||||
return ERROR_OUT_OF_MEMORY;
|
if (already_on) return ERROR_NONE;
|
||||||
}
|
|
||||||
ctx->callbacks[ctx->callbackCount] = { .fn = callback, .ctx = callback_context };
|
return bring_up_wifi(ctx);
|
||||||
ctx->callbackCount++;
|
}
|
||||||
mutex_unlock(&ctx->callbackMutex);
|
|
||||||
|
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;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_t api_remove_event_callback(Device* device, WifiEventCallback callback) {
|
error_t api_event_subscribe(Device* device, WifiEventSubscription* sub, TaskEventGroup* event_group) {
|
||||||
auto* ctx = GET_CTX(device);
|
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);
|
uint32_t bit;
|
||||||
for (size_t i = 0; i < ctx->callbackCount; i++) {
|
error_t claim_result = task_event_group_claim_bit(event_group, &bit);
|
||||||
if (ctx->callbacks[i].fn == callback) {
|
if (claim_result != ERROR_NONE) {
|
||||||
for (size_t j = i; j + 1 < ctx->callbackCount; j++) {
|
return claim_result;
|
||||||
ctx->callbacks[j] = ctx->callbacks[j + 1];
|
}
|
||||||
}
|
|
||||||
ctx->callbackCount--;
|
mutex_lock(&ctx->subscriptionsMutex);
|
||||||
mutex_unlock(&ctx->callbackMutex);
|
|
||||||
return ERROR_NONE;
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t api_event_unsubscribe(Device* device, WifiEventSubscription* sub) {
|
||||||
|
auto* ctx = GET_CTX(device);
|
||||||
|
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);
|
mutex_unlock(&ctx->subscriptionsMutex);
|
||||||
return ERROR_NOT_FOUND;
|
|
||||||
|
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) {
|
error_t api_get_firmware_ops(Device* /*device*/, const FirmwareOps** ops, void** ctx) {
|
||||||
@@ -550,6 +596,8 @@ error_t api_get_firmware_ops(Device* /*device*/, const FirmwareOps** ops, void**
|
|||||||
}
|
}
|
||||||
|
|
||||||
const WifiApi esp32_wifi_api = {
|
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_radio_state = api_get_radio_state,
|
||||||
.get_station_state = api_get_station_state,
|
.get_station_state = api_get_station_state,
|
||||||
.get_access_point_state = api_get_access_point_state,
|
.get_access_point_state = api_get_access_point_state,
|
||||||
@@ -561,34 +609,23 @@ const WifiApi esp32_wifi_api = {
|
|||||||
.station_connect = api_station_connect,
|
.station_connect = api_station_connect,
|
||||||
.station_disconnect = api_station_disconnect,
|
.station_disconnect = api_station_disconnect,
|
||||||
.station_get_rssi = api_station_get_rssi,
|
.station_get_rssi = api_station_get_rssi,
|
||||||
.add_event_callback = api_add_event_callback,
|
.event_subscribe = api_event_subscribe,
|
||||||
.remove_event_callback = api_remove_event_callback,
|
.event_unsubscribe = api_event_unsubscribe,
|
||||||
.get_firmware_ops = api_get_firmware_ops
|
.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) {
|
error_t start_device(Device* device) {
|
||||||
auto* ctx = new(std::nothrow) Esp32WifiCtx();
|
// Prefers PSRAM/SPIRAM, falling back to internal RAM when unavailable - aborts on true OOM
|
||||||
if (ctx == nullptr) return ERROR_OUT_OF_MEMORY;
|
// (see OptExternalAllocator's own doc), so no null check here.
|
||||||
|
tt::OptExternalAllocator<Esp32WifiCtx> allocator;
|
||||||
|
auto* ctx = allocator.allocate(1);
|
||||||
|
new (ctx) Esp32WifiCtx();
|
||||||
|
|
||||||
ctx->device = device;
|
ctx->device = device;
|
||||||
mutex_construct(&ctx->mutex);
|
mutex_construct(&ctx->mutex);
|
||||||
mutex_construct(&ctx->callbackMutex);
|
mutex_construct(&ctx->subscriptionsMutex);
|
||||||
device_set_driver_data(device, ctx);
|
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;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,12 +633,28 @@ error_t stop_device(Device* device) {
|
|||||||
auto* ctx = GET_CTX(device);
|
auto* ctx = GET_CTX(device);
|
||||||
if (ctx == nullptr) return ERROR_NONE;
|
if (ctx == nullptr) return ERROR_NONE;
|
||||||
|
|
||||||
bring_down_wifi(ctx);
|
if (ctx->radioState == WIFI_RADIO_STATE_ON) {
|
||||||
|
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);
|
device_set_driver_data(device, nullptr);
|
||||||
mutex_destruct(&ctx->callbackMutex);
|
mutex_destruct(&ctx->subscriptionsMutex);
|
||||||
mutex_destruct(&ctx->mutex);
|
mutex_destruct(&ctx->mutex);
|
||||||
delete ctx;
|
ctx->~Esp32WifiCtx();
|
||||||
|
tt::OptExternalAllocator<Esp32WifiCtx>().deallocate(ctx, 1);
|
||||||
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ struct Esp32WifiPinnedCtx {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define GET_CTX(device) (static_cast<Esp32WifiPinnedCtx*>(device_get_driver_data(device)))
|
#define GET_CTX(device) (static_cast<Esp32WifiPinnedCtx*>(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 {
|
struct MarshalledCall {
|
||||||
const std::function<void()>* work;
|
const std::function<void()>* work;
|
||||||
@@ -176,20 +182,44 @@ error_t api_station_get_rssi(Device* device, int32_t* rssi) {
|
|||||||
return wifi_station_get_rssi(ctx->child, 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);
|
auto* ctx = GET_CTX(device);
|
||||||
if (ctx == nullptr || ctx->child == nullptr) return ERROR_INVALID_STATE;
|
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);
|
auto* ctx = GET_CTX(device);
|
||||||
if (ctx == nullptr || ctx->child == nullptr) return ERROR_INVALID_STATE;
|
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 ----
|
// ---- 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) {
|
error_t api_scan(Device* device) {
|
||||||
auto* ctx = GET_CTX(device);
|
auto* ctx = GET_CTX(device);
|
||||||
if (ctx == nullptr || ctx->child == nullptr) return ERROR_INVALID_STATE;
|
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 = {
|
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_radio_state = api_get_radio_state,
|
||||||
.get_station_state = api_get_station_state,
|
.get_station_state = api_get_station_state,
|
||||||
.get_access_point_state = api_get_access_point_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_connect = api_station_connect,
|
||||||
.station_disconnect = api_station_disconnect,
|
.station_disconnect = api_station_disconnect,
|
||||||
.station_get_rssi = api_station_get_rssi,
|
.station_get_rssi = api_station_get_rssi,
|
||||||
.add_event_callback = api_add_event_callback,
|
.event_subscribe = api_event_subscribe,
|
||||||
.remove_event_callback = api_remove_event_callback
|
.event_unsubscribe = api_event_unsubscribe
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Driver lifecycle ----
|
// ---- Driver lifecycle ----
|
||||||
// Starting/stopping the child (which brings the ESP-IDF WiFi stack up/down)
|
// Starting/stopping the child (allocating/freeing its bookkeeping - see esp32_wifi.cpp's own
|
||||||
// also happens on the pinned thread, since esp_wifi_init/start/stop/deinit
|
// start_device()/stop_device() comment) happens on the pinned thread for the same core-affinity
|
||||||
// are subject to the same core-affinity requirement as the state-changing
|
// reason as the state-changing WifiApi calls above; enable()/disable() (which actually bring the
|
||||||
// WifiApi calls above.
|
// ESP-IDF WiFi stack up/down) are marshalled the same way.
|
||||||
|
|
||||||
error_t start_device(Device* device) {
|
error_t start_device(Device* device) {
|
||||||
auto* ctx = new(std::nothrow) Esp32WifiPinnedCtx();
|
auto* ctx = new(std::nothrow) Esp32WifiPinnedCtx();
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr size_t WIFI_MAX_CALLBACKS = 4;
|
|
||||||
|
|
||||||
struct MockApRecord {
|
struct MockApRecord {
|
||||||
const char* ssid;
|
const char* ssid;
|
||||||
int8_t rssi;
|
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 int8_t MOCK_CONNECTED_RSSI = -30;
|
||||||
constexpr const char* MOCK_IPV4_ADDRESS = "192.168.1.2";
|
constexpr const char* MOCK_IPV4_ADDRESS = "192.168.1.2";
|
||||||
|
|
||||||
struct WifiCallbackEntry {
|
|
||||||
WifiEventCallback fn = nullptr;
|
|
||||||
void* ctx = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PosixWifiCtx {
|
struct PosixWifiCtx {
|
||||||
Device* device = nullptr;
|
Device* device = nullptr;
|
||||||
|
|
||||||
@@ -54,24 +47,25 @@ struct PosixWifiCtx {
|
|||||||
bool scanning = false;
|
bool scanning = false;
|
||||||
char targetSsid[33] = {};
|
char targetSsid[33] = {};
|
||||||
|
|
||||||
Mutex callbackMutex {};
|
Mutex subscriptionsMutex {};
|
||||||
WifiCallbackEntry callbacks[WIFI_MAX_CALLBACKS] = {};
|
WifiEventSubscription* subscriptions = nullptr;
|
||||||
size_t callbackCount = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GET_CTX(device) (static_cast<PosixWifiCtx*>(device_get_driver_data(device)))
|
#define GET_CTX(device) (static_cast<PosixWifiCtx*>(device_get_driver_data(device)))
|
||||||
|
|
||||||
void fireEvent(PosixWifiCtx* ctx, WifiEvent event) {
|
void fireEvent(PosixWifiCtx* ctx, WifiEvent event) {
|
||||||
WifiCallbackEntry local[WIFI_MAX_CALLBACKS];
|
mutex_lock(&ctx->subscriptionsMutex);
|
||||||
size_t count;
|
for (WifiEventSubscription* sub = ctx->subscriptions; sub != nullptr; sub = sub->internal.next) {
|
||||||
mutex_lock(&ctx->callbackMutex);
|
mutex_lock(&sub->internal.ring_mutex);
|
||||||
count = ctx->callbackCount;
|
if (sub->internal.count < WIFI_EVENT_QUEUE_CAPACITY) {
|
||||||
memcpy(local, ctx->callbacks, count * sizeof(WifiCallbackEntry));
|
uint8_t tail = (sub->internal.head + sub->internal.count) % WIFI_EVENT_QUEUE_CAPACITY;
|
||||||
mutex_unlock(&ctx->callbackMutex);
|
sub->internal.queue[tail] = event;
|
||||||
|
sub->internal.count++;
|
||||||
for (size_t i = 0; i < count; i++) {
|
}
|
||||||
local[i].fn(ctx->device, local[i].ctx, event);
|
mutex_unlock(&sub->internal.ring_mutex);
|
||||||
|
task_event_group_signal(sub->internal.event_group, sub->bit);
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&ctx->subscriptionsMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- WifiApi ----
|
// ---- WifiApi ----
|
||||||
@@ -247,41 +241,105 @@ error_t apiStationGetRssi(Device* device, int32_t* rssi) {
|
|||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_t apiAddEventCallback(Device* device, void* callback_context, WifiEventCallback callback) {
|
error_t apiSetRadioOn(Device* device) {
|
||||||
auto* ctx = GET_CTX(device);
|
auto* ctx = GET_CTX(device);
|
||||||
if (ctx == nullptr || callback == nullptr) return ERROR_INVALID_ARGUMENT;
|
if (ctx == nullptr) return ERROR_INVALID_STATE;
|
||||||
|
|
||||||
mutex_lock(&ctx->callbackMutex);
|
mutex_lock(&ctx->mutex);
|
||||||
if (ctx->callbackCount >= WIFI_MAX_CALLBACKS) {
|
bool already_on = ctx->radioState == WIFI_RADIO_STATE_ON;
|
||||||
mutex_unlock(&ctx->callbackMutex);
|
ctx->radioState = WIFI_RADIO_STATE_ON;
|
||||||
return ERROR_OUT_OF_MEMORY;
|
mutex_unlock(&ctx->mutex);
|
||||||
}
|
if (already_on) return ERROR_NONE;
|
||||||
ctx->callbacks[ctx->callbackCount] = { .fn = callback, .ctx = callback_context };
|
|
||||||
ctx->callbackCount++;
|
WifiEvent radio_event = {};
|
||||||
mutex_unlock(&ctx->callbackMutex);
|
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;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_t apiRemoveEventCallback(Device* device, WifiEventCallback callback) {
|
error_t apiSetRadioOff(Device* device) {
|
||||||
auto* ctx = GET_CTX(device);
|
auto* ctx = GET_CTX(device);
|
||||||
if (ctx == nullptr || callback == nullptr) return ERROR_INVALID_ARGUMENT;
|
if (ctx == nullptr) return ERROR_INVALID_STATE;
|
||||||
|
|
||||||
mutex_lock(&ctx->callbackMutex);
|
mutex_lock(&ctx->mutex);
|
||||||
for (size_t i = 0; i < ctx->callbackCount; i++) {
|
bool already_off = ctx->radioState == WIFI_RADIO_STATE_OFF;
|
||||||
if (ctx->callbacks[i].fn == callback) {
|
bool was_connected = ctx->stationState != WIFI_STATION_STATE_DISCONNECTED;
|
||||||
for (size_t j = i; j + 1 < ctx->callbackCount; j++) {
|
ctx->radioState = WIFI_RADIO_STATE_OFF;
|
||||||
ctx->callbacks[j] = ctx->callbacks[j + 1];
|
ctx->stationState = WIFI_STATION_STATE_DISCONNECTED;
|
||||||
}
|
ctx->scanning = false;
|
||||||
ctx->callbackCount--;
|
mutex_unlock(&ctx->mutex);
|
||||||
mutex_unlock(&ctx->callbackMutex);
|
if (already_off) return ERROR_NONE;
|
||||||
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;
|
||||||
|
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 || sub == nullptr || event_group == nullptr) return ERROR_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
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(&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);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t apiEventUnsubscribe(Device* device, WifiEventSubscription* sub) {
|
||||||
|
auto* ctx = GET_CTX(device);
|
||||||
|
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);
|
mutex_unlock(&ctx->subscriptionsMutex);
|
||||||
return ERROR_NOT_FOUND;
|
|
||||||
|
if (result == ERROR_NONE) {
|
||||||
|
task_event_group_release_bit(sub->internal.event_group, sub->bit);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WifiApi posix_wifi_api = {
|
const WifiApi posix_wifi_api = {
|
||||||
|
.set_radio_on = apiSetRadioOn,
|
||||||
|
.set_radio_off = apiSetRadioOff,
|
||||||
.get_radio_state = apiGetRadioState,
|
.get_radio_state = apiGetRadioState,
|
||||||
.get_station_state = apiGetStationState,
|
.get_station_state = apiGetStationState,
|
||||||
.get_access_point_state = apiGetAccessPointState,
|
.get_access_point_state = apiGetAccessPointState,
|
||||||
@@ -293,13 +351,14 @@ const WifiApi posix_wifi_api = {
|
|||||||
.station_connect = apiStationConnect,
|
.station_connect = apiStationConnect,
|
||||||
.station_disconnect = apiStationDisconnect,
|
.station_disconnect = apiStationDisconnect,
|
||||||
.station_get_rssi = apiStationGetRssi,
|
.station_get_rssi = apiStationGetRssi,
|
||||||
.add_event_callback = apiAddEventCallback,
|
.event_subscribe = apiEventSubscribe,
|
||||||
.remove_event_callback = apiRemoveEventCallback
|
.event_unsubscribe = apiEventUnsubscribe
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Driver lifecycle ----
|
// ---- Driver lifecycle ----
|
||||||
// Unlike the real esp32 driver, there's no hardware to bring up: the radio
|
// startDevice()/stopDevice() only allocate/free this driver's bookkeeping (subscriber list
|
||||||
// simply reports itself as ON as soon as the device is started.
|
// 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) {
|
error_t startDevice(Device* device) {
|
||||||
auto* ctx = new(std::nothrow) PosixWifiCtx();
|
auto* ctx = new(std::nothrow) PosixWifiCtx();
|
||||||
@@ -307,12 +366,9 @@ error_t startDevice(Device* device) {
|
|||||||
|
|
||||||
ctx->device = device;
|
ctx->device = device;
|
||||||
mutex_construct(&ctx->mutex);
|
mutex_construct(&ctx->mutex);
|
||||||
mutex_construct(&ctx->callbackMutex);
|
mutex_construct(&ctx->subscriptionsMutex);
|
||||||
ctx->radioState = WIFI_RADIO_STATE_ON;
|
|
||||||
|
|
||||||
device_set_driver_data(device, ctx);
|
device_set_driver_data(device, ctx);
|
||||||
|
|
||||||
LOG_I(TAG, "WiFi radio on (mock)");
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,12 +376,28 @@ error_t stopDevice(Device* device) {
|
|||||||
auto* ctx = GET_CTX(device);
|
auto* ctx = GET_CTX(device);
|
||||||
if (ctx == nullptr) return ERROR_NONE;
|
if (ctx == nullptr) return ERROR_NONE;
|
||||||
|
|
||||||
|
if (ctx->radioState == WIFI_RADIO_STATE_ON) {
|
||||||
|
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);
|
device_set_driver_data(device, nullptr);
|
||||||
mutex_destruct(&ctx->callbackMutex);
|
mutex_destruct(&ctx->subscriptionsMutex);
|
||||||
mutex_destruct(&ctx->mutex);
|
mutex_destruct(&ctx->mutex);
|
||||||
delete ctx;
|
delete ctx;
|
||||||
|
|
||||||
LOG_I(TAG, "WiFi radio off (mock)");
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,6 @@ enum class RadioState {
|
|||||||
Off,
|
Off,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get wifi pubsub that broadcasts Event objects
|
|
||||||
* @return PubSub
|
|
||||||
*/
|
|
||||||
std::shared_ptr<PubSub<WifiEvent>> getPubsub();
|
|
||||||
|
|
||||||
/** @return Get the current radio state */
|
/** @return Get the current radio state */
|
||||||
RadioState getRadioState();
|
RadioState getRadioState();
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ struct Context {
|
|||||||
uint32_t appInstanceId;
|
uint32_t appInstanceId;
|
||||||
ChatState state;
|
ChatState state;
|
||||||
ChatView view = ChatView(this, &state);
|
ChatView view = ChatView(this, &state);
|
||||||
service::espnow::ReceiverSubscription receiveSubscription = -1;
|
|
||||||
ChatSettingsData settings;
|
ChatSettingsData settings;
|
||||||
bool isFirstLaunch = false;
|
bool isFirstLaunch = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -141,7 +141,6 @@ namespace app {
|
|||||||
namespace boot { extern const ::AppManifest manifest; }
|
namespace boot { extern const ::AppManifest manifest; }
|
||||||
namespace development { extern const ::AppManifest manifest; }
|
namespace development { extern const ::AppManifest manifest; }
|
||||||
namespace display { extern const ::AppManifest manifest; }
|
namespace display { extern const ::AppManifest manifest; }
|
||||||
namespace kerneldisplay { extern const ::AppManifest manifest; }
|
|
||||||
namespace files { extern const ::AppManifest manifest; }
|
namespace files { extern const ::AppManifest manifest; }
|
||||||
namespace fileselection { extern const ::AppManifest manifest; }
|
namespace fileselection { extern const ::AppManifest manifest; }
|
||||||
namespace gpssettings { extern const ::AppManifest manifest; }
|
namespace gpssettings { extern const ::AppManifest manifest; }
|
||||||
@@ -206,7 +205,7 @@ static void registerInternalApps() {
|
|||||||
app_manager_add(&app::audiosettings::manifest);
|
app_manager_add(&app::audiosettings::manifest);
|
||||||
}
|
}
|
||||||
if (device_exists_of_type(&DISPLAY_TYPE)) {
|
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::files::manifest);
|
||||||
app_manager_add(&app::fileselection::manifest);
|
app_manager_add(&app::fileselection::manifest);
|
||||||
@@ -519,7 +518,6 @@ void run(Module* const dtsModules[], const DtsDevice dtsDevices[]) {
|
|||||||
.on_start = onLvglStarted,
|
.on_start = onLvglStarted,
|
||||||
.on_stop = onLvglStopped,
|
.on_stop = onLvglStopped,
|
||||||
.task_priority = THREAD_PRIORITY_HIGHER,
|
.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,
|
.task_stack_size = 9120,
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
.task_affinity = getCpuAffinityConfiguration().graphics
|
.task_affinity = getCpuAffinityConfiguration().graphics
|
||||||
|
|||||||
@@ -5,11 +5,13 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
#include <lvgl/icons/shared.h>
|
#include <lvgl/icons/shared.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/drivers/uart_controller.h>
|
#include <tactility/drivers/uart_controller.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
@@ -199,36 +201,42 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
// endregion
|
// 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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
case APP_EVENT_RESULT:
|
||||||
break;
|
app_manager_stop(event.result.launch_id);
|
||||||
case APP_EVENT_RESULT:
|
break;
|
||||||
app_manager_stop(event.result.launch_id);
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
@@ -97,29 +99,37 @@ 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 };
|
Context ctx { appInstanceId };
|
||||||
ctx.argc = argc;
|
ctx.argc = argc;
|
||||||
ctx.argv = argv;
|
ctx.argv = argv;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
|
bool shouldClose = false;
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
if (event.type == APP_EVENT_CLOSE) {
|
||||||
}
|
shouldClose = true;
|
||||||
if (event.type == APP_EVENT_CLOSE) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return ctx.result;
|
return ctx.result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
#include <app/install.h>
|
#include <app/install.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <format>
|
#include <format>
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
#include <Tactility/file/File.h>
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/lvgl/Style.h>
|
#include <Tactility/lvgl/Style.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
constexpr auto* TAG = "AppDetails";
|
constexpr auto* TAG = "AppDetails";
|
||||||
@@ -104,7 +106,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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
@@ -114,38 +117,43 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
case APP_EVENT_RESULT:
|
||||||
break;
|
if (event.result.launch_id == ctx.pendingUninstallDialogId) {
|
||||||
case APP_EVENT_RESULT:
|
if (event.result.result == 0) { // 0 = Yes
|
||||||
if (event.result.launch_id == ctx.pendingUninstallDialogId) {
|
app_uninstall(ctx.targetManifest.id);
|
||||||
if (event.result.result == 0) { // 0 = Yes
|
shouldClose = true;
|
||||||
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);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
@@ -49,7 +51,7 @@ void refresh(Context* ctx);
|
|||||||
|
|
||||||
void onBackPressed(lv_event_t* event) {
|
void onBackPressed(lv_event_t* event) {
|
||||||
auto* ctx = static_cast<Context*>(lv_event_get_user_data(event));
|
auto* ctx = static_cast<Context*>(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);
|
app_event_emit(ctx->appInstanceId, &closeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,33 +199,39 @@ void destroyWidgets(void* userData) {
|
|||||||
ctx->refreshButton = nullptr;
|
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;
|
Context ctx;
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx);
|
WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -234,7 +242,7 @@ extern const ::AppManifest manifest = {
|
|||||||
.id = "tactility.apphub",
|
.id = "tactility.apphub",
|
||||||
.name = "App Hub",
|
.name = "App Hub",
|
||||||
.category = APP_CATEGORY_SYSTEM,
|
.category = APP_CATEGORY_SYSTEM,
|
||||||
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
|
.location = {APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain)}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace tt::app::apphub
|
||||||
|
|||||||
@@ -11,12 +11,14 @@
|
|||||||
#include <app/metadata.h>
|
#include <app/metadata.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -211,7 +213,8 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
updateViews(ctx);
|
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,
|
// argv layout: [0]=appId, [1]=appVersionName, [2]=appVersionCode, [3]=appName,
|
||||||
// [4]=appDescription, [5]=targetSdk, [6]=file, [7..argc)=targetPlatforms.
|
// [4]=appDescription, [5]=targetSdk, [6]=file, [7..argc)=targetPlatforms.
|
||||||
|
|
||||||
@@ -230,41 +233,46 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
case APP_EVENT_RESULT: {
|
||||||
break;
|
bool confirmed = event.result.result == CONFIRMATION_BUTTON_INDEX;
|
||||||
case APP_EVENT_RESULT: {
|
if (event.result.launch_id == ctx.installDialogId && confirmed) {
|
||||||
bool confirmed = event.result.result == CONFIRMATION_BUTTON_INDEX;
|
installApp(&ctx);
|
||||||
if (event.result.launch_id == ctx.installDialogId && confirmed) {
|
} else if (event.result.launch_id == ctx.uninstallDialogId && confirmed) {
|
||||||
installApp(&ctx);
|
uninstallApp(&ctx);
|
||||||
} else if (event.result.launch_id == ctx.uninstallDialogId && confirmed) {
|
} else if (event.result.launch_id == ctx.updateDialogId && confirmed) {
|
||||||
uninstallApp(&ctx);
|
updateApp(&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);
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
if (shouldClose) break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -78,27 +81,35 @@ 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;
|
appListInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
|
bool shouldClose = false;
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
if (event.type == APP_EVENT_CLOSE) {
|
||||||
}
|
shouldClose = true;
|
||||||
if (event.type == APP_EVENT_CLOSE) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,12 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
|
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -87,32 +90,38 @@ 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;
|
appSettingsInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,13 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
namespace tt::app::apwebserver {
|
namespace tt::app::apwebserver {
|
||||||
@@ -121,29 +123,34 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
ctx->webServerEnabledChanged = true;
|
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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
ctx.wsSettings = settings::webserver::loadOrGetDefault();
|
ctx.wsSettings = settings::webserver::loadOrGetDefault();
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +174,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,12 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <lvgl/widgets/sliderbox.h>
|
#include <lvgl/widgets/sliderbox.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
@@ -211,28 +214,33 @@ 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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +250,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "tactility/system_event.h"
|
#include "tactility/system_event.h"
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/delay.h>
|
#include <tactility/delay.h>
|
||||||
#include <tactility/drivers/backlight.h>
|
#include <tactility/drivers/backlight.h>
|
||||||
#include <tactility/drivers/display.h>
|
#include <tactility/drivers/display.h>
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
@@ -284,7 +286,8 @@ void runBootSequence(TickType_t startTime) {
|
|||||||
system_event_emit(KERNEL_EVENT_BOOT_COMPLETED, nullptr, 0);
|
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;
|
bootAppInstanceId = appInstanceId;
|
||||||
const auto start_time = get_ticks();
|
const auto start_time = get_ticks();
|
||||||
|
|
||||||
@@ -292,9 +295,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
isUsbBootSplash = hal::usb::isUsbBootMode();
|
isUsbBootSplash = hal::usb::isUsbBootMode();
|
||||||
sdCardMissing = false;
|
sdCardMissing = false;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
bootWindowId = window_manager_create(appInstanceId, createSplashWidgets, nullptr);
|
bootWindowId = window_manager_create(appInstanceId, createSplashWidgets, nullptr);
|
||||||
|
|
||||||
@@ -304,19 +309,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
|
// startNextApp() above is what triggers that, via app-module's "save the previously active
|
||||||
// app" policy, unless sdCardMissing halted before it.
|
// app" policy, unless sdCardMissing halted before it.
|
||||||
while (true) {
|
while (true) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
|
bool shouldClose = false;
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
if (event.type == APP_EVENT_CLOSE) {
|
||||||
}
|
shouldClose = true;
|
||||||
if (event.type == APP_EVENT_CLOSE) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bootWindowId != 0) {
|
if (bootWindowId != 0) {
|
||||||
window_manager_remove(bootWindowId);
|
window_manager_remove(bootWindowId);
|
||||||
}
|
}
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
namespace tt::app::btmanage {
|
namespace tt::app::btmanage {
|
||||||
@@ -199,7 +201,8 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
ctx->unlock();
|
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;
|
Context ctx;
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
ctx.bindings = (Bindings) {
|
ctx.bindings = (Bindings) {
|
||||||
@@ -220,9 +223,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
ctx.state.updateScanResults();
|
ctx.state.updateScanResults();
|
||||||
ctx.state.updatePairedPeers();
|
ctx.state.updatePairedPeers();
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
@@ -243,16 +248,18 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +277,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/drivers/bluetooth.h>
|
#include <tactility/drivers/bluetooth.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
@@ -178,7 +180,8 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
updateViews(ctx);
|
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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
@@ -199,38 +202,42 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
device_put(btDevice);
|
device_put(btDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
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;
|
shouldClose = true;
|
||||||
}
|
break;
|
||||||
app_manager_stop(event.result.launch_id);
|
case APP_EVENT_RESULT:
|
||||||
break;
|
if (event.result.result == 0) { // 0 = Yes
|
||||||
default:
|
if (isCurrentlyConnected(&ctx)) {
|
||||||
break;
|
if (ctx.profileId == BT_PROFILE_HID_HOST) {
|
||||||
|
bluetooth::hidHostDisconnect();
|
||||||
|
} else {
|
||||||
|
bluetooth::disconnect(ctx.addr, ctx.profileId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bluetooth::unpair(ctx.addr);
|
||||||
|
shouldClose = true;
|
||||||
|
}
|
||||||
|
app_manager_stop(event.result.launch_id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +247,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
@@ -167,7 +169,8 @@ void switchChannel(Context* ctx, const std::string& chatChannel) {
|
|||||||
|
|
||||||
namespace {
|
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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
ctx.isFirstLaunch = !settingsFileExists();
|
ctx.isFirstLaunch = !settingsFileExists();
|
||||||
@@ -178,40 +181,44 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
enableEspNow(&ctx);
|
enableEspNow(&ctx);
|
||||||
|
|
||||||
ctx.receiveSubscription = service::espnow::subscribeReceiver(
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
|
AppEventSubscription sub {};
|
||||||
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
|
|
||||||
|
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) {
|
[&ctx](const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length) {
|
||||||
onReceive(&ctx, receiveInfo, data, length);
|
onReceive(&ctx, receiveInfo, data, length);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
|
||||||
sub.app_instance_id = appInstanceId;
|
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
service::espnow::unsubscribeReceiver(espnow_subscription);
|
||||||
app_event_unsubscribe(&sub);
|
|
||||||
|
|
||||||
service::espnow::unsubscribeReceiver(ctx.receiveSubscription);
|
|
||||||
disableEspNow(&ctx);
|
disableEspNow(&ctx);
|
||||||
|
|
||||||
|
window_manager_remove(window);
|
||||||
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include <qrcode.h>
|
#include <qrcode.h>
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/drivers/pointer.h>
|
#include <tactility/drivers/pointer.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
@@ -149,35 +151,41 @@ 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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
if (!ctx.hasFatalError) {
|
if (!ctx.hasFatalError) {
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
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;
|
bool continuePressed = ctx.continuePressed;
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
@@ -162,7 +164,8 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
updateViewState(ctx);
|
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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
ctx.service = service::development::findService();
|
ctx.service = service::development::findService();
|
||||||
@@ -174,9 +177,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
@@ -197,16 +202,18 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +223,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-18
@@ -1,4 +1,5 @@
|
|||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/drivers/backlight.h>
|
#include <tactility/drivers/backlight.h>
|
||||||
#include <tactility/drivers/display.h>
|
#include <tactility/drivers/display.h>
|
||||||
@@ -12,8 +13,8 @@
|
|||||||
#include <Tactility/settings/DisplaySettings.h>
|
#include <Tactility/settings/DisplaySettings.h>
|
||||||
|
|
||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
@@ -25,11 +26,11 @@
|
|||||||
#include <sdkconfig.h>
|
#include <sdkconfig.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace tt::app::kerneldisplay {
|
namespace tt::app::display {
|
||||||
|
|
||||||
extern const ::AppManifest manifest;
|
extern const ::AppManifest manifest;
|
||||||
|
|
||||||
constexpr auto* TAG = "KernelDisplay";
|
constexpr auto* TAG = "Display";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -304,34 +305,40 @@ 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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
persistIfUpdated(ctx);
|
||||||
case APP_EVENT_CLOSE:
|
shouldClose = true;
|
||||||
persistIfUpdated(ctx);
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -345,4 +352,4 @@ extern const ::AppManifest manifest = {
|
|||||||
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
|
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tt::app::kerneldisplay
|
} // namespace tt::app::display
|
||||||
@@ -4,9 +4,12 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace tt::app::files {
|
namespace tt::app::files {
|
||||||
@@ -25,39 +28,45 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
ctx->view->init(ctx->appInstanceId, parent);
|
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<State>();
|
auto state = std::make_shared<State>();
|
||||||
View view(state);
|
View view(state);
|
||||||
CreateContext createContext { &view, appInstanceId };
|
CreateContext createContext { &view, appInstanceId };
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &createContext);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &createContext);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
case APP_EVENT_RESULT:
|
||||||
break;
|
view.onResult(event.result.launch_id, event.result.result);
|
||||||
case APP_EVENT_RESULT:
|
app_manager_stop(event.result.launch_id);
|
||||||
view.onResult(event.result.launch_id, event.result.result);
|
break;
|
||||||
app_manager_stop(event.result.launch_id);
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view.deinit();
|
view.deinit();
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,12 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -43,7 +46,8 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
ctx->view->init(parent, ctx->mode);
|
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".
|
// argv layout: [0]="existing" or "existing_or_new".
|
||||||
|
|
||||||
Context ctx {};
|
Context ctx {};
|
||||||
@@ -62,24 +66,31 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
app_event_emit(appInstanceId, &closeEvent);
|
app_event_emit(appInstanceId, &closeEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
|
bool shouldClose = false;
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
if (event.type == APP_EVENT_CLOSE) {
|
||||||
}
|
shouldClose = true;
|
||||||
if (event.type == APP_EVENT_CLOSE) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return ctx.result;
|
return ctx.result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/time.h>
|
#include <tactility/time.h>
|
||||||
|
|
||||||
@@ -257,7 +259,8 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
updateDeviceStates(ctx);
|
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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
@@ -267,51 +270,56 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
updateDeviceStates(&ctx);
|
updateDeviceStates(&ctx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
ctx.timer->start();
|
ctx.timer->start();
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
case APP_EVENT_RESULT:
|
||||||
break;
|
if (ctx.hasPendingDelete) {
|
||||||
case APP_EVENT_RESULT:
|
ctx.hasPendingDelete = false;
|
||||||
if (ctx.hasPendingDelete) {
|
if (event.result.result == 0) { // 0 = Yes
|
||||||
ctx.hasPendingDelete = false;
|
lvgl_lock();
|
||||||
if (event.result.result == 0) { // 0 = Yes
|
std::erase_if(ctx.deviceRows, [&ctx](const DeviceRow& row) {
|
||||||
lvgl_lock();
|
return row.device == ctx.pendingDeleteDevice;
|
||||||
std::erase_if(ctx.deviceRows, [&ctx](const DeviceRow& row) {
|
});
|
||||||
return row.device == ctx.pendingDeleteDevice;
|
lvgl_unlock();
|
||||||
});
|
|
||||||
lvgl_unlock();
|
|
||||||
|
|
||||||
gps_settings_remove_configuration_at(ctx.pendingDeleteIndex);
|
gps_settings_remove_configuration_at(ctx.pendingDeleteIndex);
|
||||||
ctx.pendingDeleteDevice = nullptr;
|
ctx.pendingDeleteDevice = nullptr;
|
||||||
|
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
rebuildDeviceList(&ctx);
|
rebuildDeviceList(&ctx);
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
app_manager_stop(event.result.launch_id);
|
||||||
app_manager_stop(event.result.launch_id);
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
break;
|
||||||
break;
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.timer->stop();
|
ctx.timer->stop();
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/drivers/grove.h>
|
#include <tactility/drivers/grove.h>
|
||||||
|
|
||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
@@ -89,33 +91,39 @@ 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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
#include <app/paths.h>
|
#include <app/paths.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/drivers/i2c_controller.h>
|
#include <tactility/drivers/i2c_controller.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
#include <tactility/preferences.h>
|
#include <tactility/preferences.h>
|
||||||
@@ -384,34 +386,40 @@ 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;
|
Context ctx;
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
stopScanningIfRunning(&ctx);
|
||||||
case APP_EVENT_CLOSE:
|
shouldClose = true;
|
||||||
stopScanningIfRunning(&ctx);
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
@@ -82,36 +83,42 @@ 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");
|
check(argc > 0, "Parameters not set");
|
||||||
|
|
||||||
Context ctx {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
ctx.filePath = argv[0];
|
ctx.filePath = argv[0];
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
@@ -106,29 +108,37 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
createButton(ctx, button_wrapper, "Cancel", nullptr);
|
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 };
|
Context ctx { appInstanceId };
|
||||||
ctx.argc = argc;
|
ctx.argc = argc;
|
||||||
ctx.argv = argv;
|
ctx.argv = argv;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
|
bool shouldClose = false;
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
if (event.type == APP_EVENT_CLOSE) {
|
||||||
}
|
shouldClose = true;
|
||||||
if (event.type == APP_EVENT_CLOSE) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return ctx.result;
|
return ctx.result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/drivers/backlight.h>
|
#include <tactility/drivers/backlight.h>
|
||||||
|
|
||||||
@@ -205,34 +207,40 @@ 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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
persistIfUpdated(ctx);
|
||||||
case APP_EVENT_CLOSE:
|
shouldClose = true;
|
||||||
persistIfUpdated(ctx);
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/drivers/power_supply.h>
|
#include <tactility/drivers/power_supply.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
@@ -231,29 +233,37 @@ 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();
|
runAutoStart();
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr);
|
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
|
// 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).
|
// thread when app-module's scheduler asks it to (e.g. another new-model app is started).
|
||||||
while (true) {
|
while (true) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
|
bool shouldClose = false;
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
if (event.type == APP_EVENT_CLOSE) {
|
||||||
}
|
shouldClose = true;
|
||||||
if (event.type == APP_EVENT_CLOSE) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,12 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
|
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
@@ -141,33 +144,39 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
lv_obj_add_event_cb(ctx->languageDropdown, onLanguageSet, LV_EVENT_VALUE_CHANGED, ctx);
|
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;
|
Context ctx;
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <lvgl/widgets/toolbar.h>
|
#include <lvgl/widgets/toolbar.h>
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
namespace tt::app::notes {
|
namespace tt::app::notes {
|
||||||
@@ -182,7 +184,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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
@@ -190,51 +193,56 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
ctx.filePath = argv[0];
|
ctx.filePath = argv[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
case APP_EVENT_RESULT:
|
||||||
break;
|
LOG_I(TAG, "Result for launch id %u", event.result.launch_id);
|
||||||
case APP_EVENT_RESULT:
|
if (event.result.launch_id == ctx.loadFileLaunchId) {
|
||||||
LOG_I(TAG, "Result for launch id %u", event.result.launch_id);
|
ctx.loadFileLaunchId = 0;
|
||||||
if (event.result.launch_id == ctx.loadFileLaunchId) {
|
if (event.result.result == 0 /* Ok */) {
|
||||||
ctx.loadFileLaunchId = 0;
|
auto path = fileselection::getLastPath();
|
||||||
if (event.result.result == 0 /* Ok */) {
|
if (!path.empty()) {
|
||||||
auto path = fileselection::getLastPath();
|
openFile(&ctx, path);
|
||||||
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) {
|
app_manager_stop(event.result.launch_id);
|
||||||
ctx.saveFileLaunchId = 0;
|
break;
|
||||||
if (event.result.result == 0 /* Ok */) {
|
default:
|
||||||
auto path = fileselection::getLastPath();
|
break;
|
||||||
// Must re-open file, because the UI was cleared after opening the dialog.
|
}
|
||||||
if (!path.empty() && saveFile(&ctx, path)) {
|
if (shouldClose) break;
|
||||||
openFile(&ctx, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
app_manager_stop(event.result.launch_id);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/drivers/power_supply.h>
|
#include <tactility/drivers/power_supply.h>
|
||||||
#include <tactility/time.h>
|
#include <tactility/time.h>
|
||||||
@@ -229,7 +231,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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
@@ -240,31 +243,36 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
updateUi(&ctx);
|
updateUi(&ctx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
ctx.timer->start();
|
ctx.timer->start();
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.timer->stop();
|
ctx.timer->stop();
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
#include <lvgl/fonts.h>
|
#include <lvgl/fonts.h>
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/drivers/power_supply.h>
|
#include <tactility/drivers/power_supply.h>
|
||||||
|
|
||||||
@@ -132,22 +134,25 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
lv_obj_add_event_cb(no_button, onNoPressed, LV_EVENT_SHORT_CLICKED, ctx);
|
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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case APP_EVENT_CLOSE:
|
case APP_EVENT_CLOSE:
|
||||||
shouldClose = true;
|
shouldClose = true;
|
||||||
@@ -155,10 +160,13 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
@@ -240,7 +242,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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
ctx.updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, 500 / portTICK_PERIOD_MS, [&ctx] {
|
ctx.updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, 500 / portTICK_PERIOD_MS, [&ctx] {
|
||||||
@@ -250,24 +253,28 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +283,8 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
@@ -99,29 +101,37 @@ 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 };
|
Context ctx { appInstanceId };
|
||||||
ctx.argc = argc;
|
ctx.argc = argc;
|
||||||
ctx.argv = argv;
|
ctx.argv = argv;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
|
bool shouldClose = false;
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
if (event.type == APP_EVENT_CLOSE) {
|
||||||
}
|
shouldClose = true;
|
||||||
if (event.type == APP_EVENT_CLOSE) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return ctx.result;
|
return ctx.result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
@@ -76,27 +77,35 @@ 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;
|
settingsInstanceId = appInstanceId;
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, nullptr);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
|
bool shouldClose = false;
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
if (event.type == APP_EVENT_CLOSE) {
|
||||||
}
|
shouldClose = true;
|
||||||
if (event.type == APP_EVENT_CLOSE) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,11 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <lvgl/fonts.h>
|
#include <lvgl/fonts.h>
|
||||||
@@ -159,7 +161,7 @@ void onContinueClicked(lv_event_t* event) {
|
|||||||
markCompleted();
|
markCompleted();
|
||||||
// Async, non-blocking - must NOT call app_manager_stop() directly here: this
|
// 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
|
// 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 = {} };
|
AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
|
||||||
app_event_emit(ctx->appInstanceId, &closeEvent);
|
app_event_emit(ctx->appInstanceId, &closeEvent);
|
||||||
break;
|
break;
|
||||||
@@ -203,7 +205,8 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
renderCurrent(ctx);
|
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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
ctx.steps = {
|
ctx.steps = {
|
||||||
@@ -229,36 +232,41 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
case APP_EVENT_RESULT:
|
||||||
break;
|
if (event.result.launch_id == ctx.pendingStepDialogId) {
|
||||||
case APP_EVENT_RESULT:
|
ctx.pendingStepDialogId = 0;
|
||||||
if (event.result.launch_id == ctx.pendingStepDialogId) {
|
advanceTo(&ctx, ctx.stepIndex + 1);
|
||||||
ctx.pendingStepDialogId = 0;
|
}
|
||||||
advanceTo(&ctx, ctx.stepIndex + 1);
|
app_manager_stop(event.result.launch_id);
|
||||||
}
|
break;
|
||||||
app_manager_stop(event.result.launch_id);
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <tactility/check.h>
|
||||||
#include "tactility/time.h"
|
#include "tactility/time.h"
|
||||||
|
|
||||||
#include <Tactility/DeprecatedPaths.h>
|
#include <Tactility/DeprecatedPaths.h>
|
||||||
@@ -8,6 +9,7 @@
|
|||||||
#include <app/event.h>
|
#include <app/event.h>
|
||||||
#include <app/manager.h>
|
#include <app/manager.h>
|
||||||
#include <app/manifest.h>
|
#include <app/manifest.h>
|
||||||
|
#include <app/scheduler.h>
|
||||||
|
|
||||||
#include <lvgl_window_manager/window_manager.h>
|
#include <lvgl_window_manager/window_manager.h>
|
||||||
|
|
||||||
@@ -399,7 +401,8 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
|||||||
updateTasks(ctx);
|
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 {};
|
Context ctx {};
|
||||||
ctx.appInstanceId = appInstanceId;
|
ctx.appInstanceId = appInstanceId;
|
||||||
|
|
||||||
@@ -417,9 +420,11 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
TaskEventGroup event_group {};
|
||||||
|
task_event_group_construct(&event_group);
|
||||||
|
|
||||||
AppEventSubscription sub {};
|
AppEventSubscription sub {};
|
||||||
sub.app_instance_id = appInstanceId;
|
check(app_event_subscribe(&sub, &event_group) == ERROR_NONE);
|
||||||
app_event_subscribe(&sub);
|
|
||||||
|
|
||||||
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
|
||||||
ctx.memoryTimer->start(); // Memory: every 10s
|
ctx.memoryTimer->start(); // Memory: every 10s
|
||||||
@@ -427,23 +432,26 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
|
|||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
while (!shouldClose) {
|
while (!shouldClose) {
|
||||||
|
task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY);
|
||||||
|
|
||||||
AppEvent event {};
|
AppEvent event {};
|
||||||
if (app_event_await(&sub, &event, portMAX_DELAY) != ERROR_NONE) {
|
while (app_event_poll(&sub, &event) == ERROR_NONE) {
|
||||||
break;
|
switch (event.type) {
|
||||||
}
|
case APP_EVENT_CLOSE:
|
||||||
switch (event.type) {
|
shouldClose = true;
|
||||||
case APP_EVENT_CLOSE:
|
break;
|
||||||
shouldClose = true;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
if (shouldClose) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.memoryTimer->stop();
|
ctx.memoryTimer->stop();
|
||||||
ctx.tasksTimer->stop();
|
ctx.tasksTimer->stop();
|
||||||
window_manager_remove(window);
|
window_manager_remove(window);
|
||||||
app_event_unsubscribe(&sub);
|
check(app_event_unsubscribe(&sub) == ERROR_NONE);
|
||||||
|
task_event_group_destruct(&event_group);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user