From f8735d4bce6d7b034f293ad87c9df4e8ecd13d7c Mon Sep 17 00:00:00 2001 From: Stepan Usatiuk Date: Sat, 25 Oct 2025 12:34:53 +0200 Subject: [PATCH] some refactoring 2 --- .../components/backend-esp/CMakeLists.txt | 1 - .../include/cardboy/backend/esp/buttons.hpp | 20 +- .../include/cardboy/backend/esp/event_bus.hpp | 27 -- .../include/cardboy/backend/esp_backend.hpp | 26 +- .../components/backend-esp/src/buttons.cpp | 55 ++- .../backend-esp/src/esp_backend.cpp | 432 ++++++++---------- .../components/backend-esp/src/event_bus.cpp | 73 --- Firmware/main/src/app_main.cpp | 2 +- Firmware/sdk/CMakeLists.txt | 5 + Firmware/sdk/apps/clock/src/clock_app.cpp | 12 +- Firmware/sdk/apps/gameboy/src/gameboy_app.cpp | 12 +- .../apps/lockscreen/src/lockscreen_app.cpp | 12 +- Firmware/sdk/apps/menu/src/menu_app.cpp | 12 +- Firmware/sdk/apps/snake/src/snake_app.cpp | 12 +- Firmware/sdk/apps/tetris/src/tetris_app.cpp | 27 +- Firmware/sdk/backend_interface/CMakeLists.txt | 1 - .../include/cardboy/sdk/app_events.hpp | 8 +- .../include/cardboy/sdk/event_bus.hpp | 44 -- .../include/cardboy/sdk/framebuffer_hooks.hpp | 4 +- .../include/cardboy/sdk/services.hpp | 60 ++- .../include/cardboy/sdk/timer_service.hpp | 9 +- .../cardboy/backend/desktop_backend.hpp | 55 ++- .../backends/desktop/src/desktop_backend.cpp | 91 ++-- .../include/cardboy/sdk/app_framework.hpp | 72 +-- .../core/include/cardboy/sdk/app_system.hpp | 37 +- .../include/cardboy/sdk/framebuffer_hooks.hpp | 4 +- .../core/include/cardboy/sdk/status_bar.hpp | 12 +- Firmware/sdk/core/src/app_system.cpp | 221 +++------ Firmware/sdk/core/src/framebuffer_hooks.cpp | 16 +- Firmware/sdk/core/src/status_bar.cpp | 26 +- Firmware/sdkconfig | 17 +- Firmware/sdkconfig.old | 229 +++++++++- 32 files changed, 816 insertions(+), 818 deletions(-) delete mode 100644 Firmware/components/backend-esp/include/cardboy/backend/esp/event_bus.hpp delete mode 100644 Firmware/components/backend-esp/src/event_bus.cpp delete mode 100644 Firmware/sdk/backend_interface/include/cardboy/sdk/event_bus.hpp diff --git a/Firmware/components/backend-esp/CMakeLists.txt b/Firmware/components/backend-esp/CMakeLists.txt index 316c858..d1cd762 100644 --- a/Firmware/components/backend-esp/CMakeLists.txt +++ b/Firmware/components/backend-esp/CMakeLists.txt @@ -4,7 +4,6 @@ idf_component_register( "src/buttons.cpp" "src/buzzer.cpp" "src/esp_backend.cpp" - "src/event_bus.cpp" "src/display.cpp" "src/fs_helper.cpp" "src/i2c_global.cpp" diff --git a/Firmware/components/backend-esp/include/cardboy/backend/esp/buttons.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/buttons.hpp index 625a9b7..e4971e6 100644 --- a/Firmware/components/backend-esp/include/cardboy/backend/esp/buttons.hpp +++ b/Firmware/components/backend-esp/include/cardboy/backend/esp/buttons.hpp @@ -5,9 +5,10 @@ #ifndef BUTTONS_HPP #define BUTTONS_HPP -#include "cardboy/sdk/event_bus.hpp" - #include + +#include "cardboy/sdk/input_state.hpp" +#include "cardboy/sdk/services.hpp" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -24,20 +25,19 @@ typedef enum { class Buttons { public: - static Buttons& get(); - void pooler(); // FIXME: - uint8_t get_pressed(); - void install_isr(); - void register_listener(TaskHandle_t task); - void setEventBus(cardboy::sdk::IEventBus* bus); + static Buttons& get(); + void pooler(); // FIXME: + uint8_t get_pressed(); + cardboy::sdk::InputState get_state(); + void install_isr(); + void setEventBus(cardboy::sdk::IEventBus* bus); TaskHandle_t _pooler_task; private: Buttons(); - + uint8_t _previous; volatile uint8_t _current; - volatile TaskHandle_t _listener = nullptr; cardboy::sdk::IEventBus* _eventBus = nullptr; }; diff --git a/Firmware/components/backend-esp/include/cardboy/backend/esp/event_bus.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp/event_bus.hpp deleted file mode 100644 index abb5551..0000000 --- a/Firmware/components/backend-esp/include/cardboy/backend/esp/event_bus.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -#include "freertos/queue.h" - -namespace cardboy::backend::esp { - -class EventBus final : public cardboy::sdk::IEventBus { -public: - EventBus(); - ~EventBus() override; - - void signal(std::uint32_t bits) override; - void signalFromISR(std::uint32_t bits) override; - std::uint32_t wait(std::uint32_t mask, std::uint32_t timeout_ms) override; - void postEvent(const cardboy::sdk::AppEvent& event) override; - bool popEvent(cardboy::sdk::AppEvent& outEvent) override; - -private: - EventGroupHandle_t group; - QueueHandle_t eventQueue; -}; - -} // namespace cardboy::backend::esp diff --git a/Firmware/components/backend-esp/include/cardboy/backend/esp_backend.hpp b/Firmware/components/backend-esp/include/cardboy/backend/esp_backend.hpp index b726194..bc5fa3b 100644 --- a/Firmware/components/backend-esp/include/cardboy/backend/esp_backend.hpp +++ b/Firmware/components/backend-esp/include/cardboy/backend/esp_backend.hpp @@ -2,7 +2,6 @@ #include #include "cardboy/backend/esp/display.hpp" -#include "cardboy/backend/esp/event_bus.hpp" #include "cardboy/sdk/platform.hpp" #include "cardboy/sdk/services.hpp" @@ -63,19 +62,22 @@ private: class LoopHooksService; class NotificationService; class TimerService; + class EventBus; + class AppScopedServices; + class AppServiceProvider; - std::unique_ptr buzzerService; - std::unique_ptr batteryService; - std::unique_ptr storageService; - std::unique_ptr randomService; - std::unique_ptr highResClockService; - std::unique_ptr filesystemService; - std::unique_ptr eventBus; - std::unique_ptr timerService; - std::unique_ptr loopHooksService; - std::unique_ptr notificationService; + std::unique_ptr _buzzerService; + std::unique_ptr _batteryService; + std::unique_ptr _storageService; + std::unique_ptr _randomService; + std::unique_ptr _highResClockService; + std::unique_ptr _filesystemService; + std::unique_ptr _eventBus; + std::unique_ptr _loopHooksService; + std::unique_ptr _notificationService; + std::unique_ptr _appServiceProvider; - cardboy::sdk::Services services{}; + cardboy::sdk::Services _services{}; }; struct Backend { diff --git a/Firmware/components/backend-esp/src/buttons.cpp b/Firmware/components/backend-esp/src/buttons.cpp index be956a7..7e83a9c 100644 --- a/Firmware/components/backend-esp/src/buttons.cpp +++ b/Firmware/components/backend-esp/src/buttons.cpp @@ -13,7 +13,6 @@ #include "cardboy/backend/esp/config.hpp" #include "cardboy/backend/esp/i2c_global.hpp" -#include "cardboy/sdk/event_bus.hpp" static i2c_master_dev_handle_t dev_handle; static inline i2c_device_config_t dev_cfg = { @@ -76,6 +75,28 @@ static void delay(unsigned long long loop) { } } +static cardboy::sdk::InputState buttons_to_input_state(uint8_t pressed) { + cardboy::sdk::InputState state{}; + if (pressed & BTN_UP) + state.up = true; + if (pressed & BTN_LEFT) + state.left = true; + if (pressed & BTN_RIGHT) + state.right = true; + if (pressed & BTN_DOWN) + state.down = true; + if (pressed & BTN_A) + state.a = true; + if (pressed & BTN_B) + state.b = true; + if (pressed & BTN_SELECT) + state.select = true; + if (pressed & BTN_START) + state.start = true; + return state; +} + + void Buttons::pooler() { while (true) { BaseType_t xResult = xTaskNotifyWait(pdFALSE, ULONG_MAX, nullptr, portMAX_DELAY); @@ -88,15 +109,27 @@ void Buttons::pooler() { reg = 1; ESP_ERROR_CHECK( i2c_master_transmit_receive(dev_handle, ®, sizeof(reg), reinterpret_cast(&buffer), 1, -1)); - if (_listener) - xTaskNotifyGive(_listener); - if (_eventBus) - _eventBus->signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Input)); + + + if (_eventBus) { + cardboy::sdk::AppButtonEvent button{}; + button.current = buttons_to_input_state(_current); + button.previous = buttons_to_input_state(_previous); + _previous = _current; + + cardboy::sdk::AppEvent evt{}; + + // TODO: dedup? + TickType_t ticks = xTaskGetTickCount(); + auto now = static_cast((static_cast(ticks) * 1000ULL) / configTICK_RATE_HZ); + + evt.timestamp_ms = now; + evt.data = button; + _eventBus->post(evt); + } } } -uint8_t Buttons::get_pressed() { return _current; } -void Buttons::install_isr() { gpio_isr_handler_add(EXP_INT, wakeup, nullptr); } - -void Buttons::register_listener(TaskHandle_t task) { _listener = task; } - -void Buttons::setEventBus(cardboy::sdk::IEventBus* bus) { _eventBus = bus; } +uint8_t Buttons::get_pressed() { return _current; } +void Buttons::install_isr() { gpio_isr_handler_add(EXP_INT, wakeup, nullptr); } +cardboy::sdk::InputState Buttons::get_state() { return buttons_to_input_state(get_pressed()); } +void Buttons::setEventBus(cardboy::sdk::IEventBus* bus) { _eventBus = bus; } diff --git a/Firmware/components/backend-esp/src/esp_backend.cpp b/Firmware/components/backend-esp/src/esp_backend.cpp index 4d0e547..9b4a3ea 100644 --- a/Firmware/components/backend-esp/src/esp_backend.cpp +++ b/Firmware/components/backend-esp/src/esp_backend.cpp @@ -18,15 +18,17 @@ #include "esp_random.h" #include "esp_timer.h" #include "freertos/FreeRTOS.h" -#include "freertos/task.h" #include "freertos/semphr.h" +#include "freertos/task.h" #include "freertos/timers.h" #include "nvs.h" #include "nvs_flash.h" #include +#include #include #include +#include #include #include #include @@ -134,61 +136,96 @@ public: void onLoopIteration() override { vTaskDelay(1); } }; -class EspRuntime::TimerService final : public cardboy::sdk::ITimerService { +class EspRuntime::EventBus final : public cardboy::sdk::IEventBus { public: - explicit TimerService(cardboy::sdk::IEventBus& bus); - ~TimerService() override; + explicit EventBus() { + _queueHandle = + xQueueCreateStatic(_kMaxQueueSize, sizeof(cardboy::sdk::AppEvent), _queueStorage.data(), &_queue); + } - cardboy::sdk::TimerClientId acquireClient() override; - void releaseClient(cardboy::sdk::TimerClientId clientId) override; - cardboy::sdk::AppTimerHandle scheduleTimer(cardboy::sdk::TimerClientId clientId, std::uint32_t delay_ms, - bool repeat) override; - void cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) override; - void cancelAllTimers(cardboy::sdk::TimerClientId clientId) override; + ~EventBus() override { vQueueDelete(_queueHandle); } + void post(const sdk::AppEvent& event) override { xQueueSendToBack(_queueHandle, &event, portMAX_DELAY); } + sdk::AppEvent pop() override { + sdk::AppEvent out; + xQueueReceive(_queueHandle, &out, portMAX_DELAY); + return out; + } private: - struct TimerRecord { - TimerService* owner = nullptr; - TimerHandle_t timer = nullptr; - cardboy::sdk::TimerClientId clientId = cardboy::sdk::kInvalidTimerClient; - cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer; - bool repeat = false; - bool active = true; - }; + static constexpr std::uint32_t _kMaxQueueSize = 32; + StaticQueue_t _queue; + std::array _queueStorage{}; + QueueHandle_t _queueHandle; +}; - class RecursiveLock { - public: - explicit RecursiveLock(SemaphoreHandle_t m) : mutex(m) { lock(); } - ~RecursiveLock() { unlock(); } - void lock() { - if (mutex && !locked) { - if (xSemaphoreTakeRecursive(mutex, portMAX_DELAY) == pdTRUE) - locked = true; - } +class EspRuntime::TimerService final : public cardboy::sdk::ITimerService { +public: + explicit TimerService(cardboy::sdk::IEventBus& appBus); + ~TimerService() override; + + cardboy::sdk::AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) override; + void cancelTimer(cardboy::sdk::AppTimerHandle handle) override; + void cancelAllTimers() override; + +private: + static inline EspRuntime::TimerService* _current = nullptr; + struct InitializedSemaphore { + InitializedSemaphore() { + _handle = xSemaphoreCreateBinary(); + xSemaphoreGive(_handle); } + ~InitializedSemaphore() { vSemaphoreDelete(_handle); } - void unlock() { - if (mutex && locked) { - xSemaphoreGiveRecursive(mutex); - locked = false; - } - } + SemaphoreHandle_t operator*() { return _handle; } + SemaphoreHandle_t operator->() { return _handle; } private: - SemaphoreHandle_t mutex; - bool locked = false; + SemaphoreHandle_t _handle; + }; + static inline InitializedSemaphore _currentSemaphore; + + struct TimerRecord { + TimerService* owner = nullptr; + TimerHandle_t timer = nullptr; + cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer; + bool repeat = false; }; static void timerCallback(TimerHandle_t timer); - void handleTimer(TimerRecord* record); - void shutdown(); + void handleTimer(sdk::AppTimerHandle record); - cardboy::sdk::IEventBus& eventBus; - SemaphoreHandle_t mutex = nullptr; - std::list timers; - cardboy::sdk::AppTimerHandle nextTimerHandle = 1; - cardboy::sdk::TimerClientId nextClientId = 1; + cardboy::sdk::IEventBus& _appEventBus; + SemaphoreHandle_t _mutex; + StaticSemaphore_t _mutexStatic; + std::list _timers; + std::atomic _nextTimerHandle = 1; + static_assert(std::atomic::is_always_lock_free); +}; + +class EspRuntime::AppScopedServices final : public cardboy::sdk::AppScopedServices { +public: + AppScopedServices(std::unique_ptr timer) : _ownedTimer(std::move(timer)) { + this->timer = _ownedTimer.get(); + } + +private: + std::unique_ptr _ownedTimer; +}; + +class EspRuntime::AppServiceProvider final : public cardboy::sdk::IAppServiceProvider { +public: + explicit AppServiceProvider(cardboy::sdk::IEventBus& bus) : eventBus(bus) {} + + [[nodiscard]] std::unique_ptr + createScopedServices(std::uint64_t generation) override { + (void) generation; + auto timer = std::make_unique(eventBus); + return std::make_unique(std::move(timer)); + } + +private: + cardboy::sdk::IEventBus& eventBus; }; class EspRuntime::NotificationService final : public cardboy::sdk::INotificationCenter { @@ -262,8 +299,8 @@ public: bool removed = false; for (auto it = entries.begin(); it != entries.end();) { if (it->id == id) { - it = entries.erase(it); - removed = true; + it = entries.erase(it); + removed = true; } else { ++it; } @@ -279,8 +316,8 @@ public: bool removed = false; for (auto it = entries.begin(); it != entries.end();) { if (it->externalId == externalId) { - it = entries.erase(it); - removed = true; + it = entries.erase(it); + removed = true; } else { ++it; } @@ -307,99 +344,69 @@ private: std::uint32_t revisionCounter = 0; }; -EspRuntime::TimerService::TimerService(cardboy::sdk::IEventBus& bus) : eventBus(bus) { - mutex = xSemaphoreCreateRecursiveMutex(); +EspRuntime::TimerService::TimerService(cardboy::sdk::IEventBus& appBus) : _appEventBus(appBus) { + xSemaphoreTake(*_currentSemaphore, portMAX_DELAY); + assert(_current == nullptr); + _mutex = xSemaphoreCreateBinaryStatic(&_mutexStatic); + assert(_mutex); + xSemaphoreGive(_mutex); + _current = this; + xSemaphoreGive(*_currentSemaphore); } EspRuntime::TimerService::~TimerService() { - shutdown(); - if (mutex) - vSemaphoreDelete(mutex); + xSemaphoreTake(*_currentSemaphore, portMAX_DELAY); + assert(_current == this); + _current = nullptr; + cancelAllTimers(); + vSemaphoreDelete(_mutex); + xSemaphoreGive(*_currentSemaphore); } -cardboy::sdk::TimerClientId EspRuntime::TimerService::acquireClient() { - if (!mutex) - return cardboy::sdk::kInvalidTimerClient; - RecursiveLock lock(mutex); - cardboy::sdk::TimerClientId id = cardboy::sdk::kInvalidTimerClient; +cardboy::sdk::AppTimerHandle EspRuntime::TimerService::scheduleTimer(std::uint32_t delay_ms, bool repeat) { + TimerRecord record{}; + record.owner = this; + record.repeat = repeat; + + cardboy::sdk::AppTimerHandle newHandle = cardboy::sdk::kInvalidAppTimer; do { - id = nextClientId++; - } while (id == cardboy::sdk::kInvalidTimerClient); - if (nextClientId == cardboy::sdk::kInvalidTimerClient) - ++nextClientId; - return id; -} + newHandle = _nextTimerHandle++; + } while (newHandle == cardboy::sdk::kInvalidAppTimer); + if (_nextTimerHandle == cardboy::sdk::kInvalidAppTimer) + ++_nextTimerHandle; + record.handle = newHandle; + xSemaphoreTake(_mutex, portMAX_DELAY); + TimerRecord* storedRecord = &_timers.emplace_back(record); + xSemaphoreGive(_mutex); -void EspRuntime::TimerService::releaseClient(cardboy::sdk::TimerClientId clientId) { cancelAllTimers(clientId); } + const TickType_t ticks = std::max(pdMS_TO_TICKS(delay_ms), 1); -cardboy::sdk::AppTimerHandle EspRuntime::TimerService::scheduleTimer(cardboy::sdk::TimerClientId clientId, - std::uint32_t delay_ms, bool repeat) { - if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient) - return cardboy::sdk::kInvalidAppTimer; - - TimerRecord* storedRecord = nullptr; - { - RecursiveLock lock(mutex); - TimerRecord record{}; - record.owner = this; - record.clientId = clientId; - record.repeat = repeat; - record.active = true; - cardboy::sdk::AppTimerHandle newHandle = cardboy::sdk::kInvalidAppTimer; - do { - newHandle = nextTimerHandle++; - } while (newHandle == cardboy::sdk::kInvalidAppTimer); - if (nextTimerHandle == cardboy::sdk::kInvalidAppTimer) - ++nextTimerHandle; - record.handle = newHandle; - timers.emplace_back(record); - storedRecord = &timers.back(); - } - - const std::uint32_t effectiveDelay = std::max(1, delay_ms); - const TickType_t ticks = std::max(pdMS_TO_TICKS(effectiveDelay), 1); + static_assert(sizeof(void*) >= sizeof(cardboy::sdk::AppTimerHandle)); TimerHandle_t timerHandle = - xTimerCreate("AppSvcTimer", ticks, repeat ? pdTRUE : pdFALSE, storedRecord, &TimerService::timerCallback); - if (!timerHandle) { - RecursiveLock lock(mutex); - for (auto it = timers.begin(); it != timers.end(); ++it) { - if (&(*it) == storedRecord) { - timers.erase(it); - break; - } - } - return cardboy::sdk::kInvalidAppTimer; - } + xTimerCreate("AppSvcTimer", ticks, repeat ? pdTRUE : pdFALSE, reinterpret_cast(storedRecord->handle), + &TimerService::timerCallback); + storedRecord->timer = timerHandle; - { - RecursiveLock lock(mutex); - storedRecord->timer = timerHandle; + if (xTimerStart(timerHandle, portMAX_DELAY) != pdPASS) { + assert(false); } - - if (xTimerStart(timerHandle, 0) != pdPASS) { - cancelTimer(clientId, storedRecord->handle); - return cardboy::sdk::kInvalidAppTimer; - } - return storedRecord->handle; } -void EspRuntime::TimerService::cancelTimer(cardboy::sdk::TimerClientId clientId, - cardboy::sdk::AppTimerHandle handle) { - if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient || handle == cardboy::sdk::kInvalidAppTimer) - return; +void EspRuntime::TimerService::cancelTimer(cardboy::sdk::AppTimerHandle handle) { + assert(handle != sdk::kInvalidAppTimer); TimerHandle_t timerHandle = nullptr; { - RecursiveLock lock(mutex); - for (auto& record: timers) { - if (record.clientId == clientId && record.handle == handle) { - record.active = false; - timerHandle = record.timer; - break; + xSemaphoreTake(_mutex, portMAX_DELAY); + for (auto it = _timers.begin(); it != _timers.end(); ++it) { + if (it->handle == handle) { + timerHandle = it->timer; + it = _timers.erase(it); } } + xSemaphoreGive(_mutex); } if (!timerHandle) @@ -407,143 +414,106 @@ void EspRuntime::TimerService::cancelTimer(cardboy::sdk::TimerClientId clientId, xTimerStop(timerHandle, portMAX_DELAY); xTimerDelete(timerHandle, portMAX_DELAY); - - { - RecursiveLock lock(mutex); - for (auto it = timers.begin(); it != timers.end();) { - if (it->clientId == clientId && it->handle == handle) - it = timers.erase(it); - else - ++it; - } - } } -void EspRuntime::TimerService::cancelAllTimers(cardboy::sdk::TimerClientId clientId) { - if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient) +void EspRuntime::TimerService::cancelAllTimers() { + if (!_mutex) return; std::vector handles; + handles.resize(_timers.size()); { - RecursiveLock lock(mutex); - for (auto& record: timers) { - if (record.clientId == clientId) { - record.active = false; - if (record.timer) - handles.push_back(record.timer); + xSemaphoreTake(_mutex, portMAX_DELAY); + size_t i = 0; + for (auto& record: _timers) { + if (record.timer) { + assert(record.timer); + handles[i] = record.timer; } + i++; } + _timers.clear(); + xSemaphoreGive(_mutex); } for (auto timerHandle: handles) { xTimerStop(timerHandle, portMAX_DELAY); xTimerDelete(timerHandle, portMAX_DELAY); } - - { - RecursiveLock lock(mutex); - for (auto it = timers.begin(); it != timers.end();) { - if (it->clientId == clientId) - it = timers.erase(it); - else - ++it; - } - } } void EspRuntime::TimerService::timerCallback(TimerHandle_t timer) { - auto* record = static_cast(pvTimerGetTimerID(timer)); - if (!record || !record->owner) + auto handle = reinterpret_cast(pvTimerGetTimerID(timer)); + xSemaphoreTake(*_currentSemaphore, portMAX_DELAY); + if (!_current) return; - record->owner->handleTimer(record); + _current->handleTimer(handle); + xSemaphoreGive(*_currentSemaphore); } -void EspRuntime::TimerService::handleTimer(TimerRecord* record) { - if (!record || !record->active) - return; +void EspRuntime::TimerService::handleTimer(sdk::AppTimerHandle handle) { + TimerRecord* record = nullptr; + TimerHandle_t timerHandle = nullptr; + bool repeat = false; - const cardboy::sdk::TimerClientId clientId = record->clientId; - const cardboy::sdk::AppTimerHandle handle = record->handle; - const bool isRepeating = record->repeat; - TimerHandle_t timerHandle = record->timer; - - if (!isRepeating) { - record->active = false; - { - RecursiveLock lock(mutex); - for (auto it = timers.begin(); it != timers.end();) { - if (&(*it) == record) - it = timers.erase(it); - else - ++it; + { + xSemaphoreTake(_mutex, portMAX_DELAY); + for (auto it = _timers.begin(); it != _timers.end(); ++it) { + if (it->handle == handle) { + record = &(*it); + timerHandle = it->timer; + if (!it->repeat) { + _timers.erase(it); + } else { + repeat = true; + } + break; } } - if (timerHandle) - xTimerDelete(timerHandle, 0); + xSemaphoreGive(_mutex); } + if (!record) + return; + if (!repeat && timerHandle) + xTimerDelete(timerHandle, portMAX_DELAY); + cardboy::sdk::AppTimerEvent timerEvent{}; - timerEvent.clientId = clientId; - timerEvent.handle = handle; + timerEvent.handle = handle; cardboy::sdk::AppEvent event{}; event.timestamp_ms = static_cast(esp_timer_get_time() / 1000ULL); event.data = timerEvent; - eventBus.postEvent(event); -} - -void EspRuntime::TimerService::shutdown() { - if (!mutex) - return; - - std::vector handles; - { - RecursiveLock lock(mutex); - for (auto& record: timers) { - record.active = false; - if (record.timer) - handles.push_back(record.timer); - } - } - - for (auto timerHandle: handles) { - xTimerStop(timerHandle, portMAX_DELAY); - xTimerDelete(timerHandle, portMAX_DELAY); - } - - { - RecursiveLock lock(mutex); - timers.clear(); - } + _appEventBus.post(event); } EspRuntime::EspRuntime() : framebuffer(), input(), clock() { initializeHardware(); - buzzerService = std::make_unique(); - batteryService = std::make_unique(); - storageService = std::make_unique(); - randomService = std::make_unique(); - highResClockService = std::make_unique(); - filesystemService = std::make_unique(); - eventBus = std::make_unique(); - timerService = std::make_unique(*eventBus); - loopHooksService = std::make_unique(); - notificationService = std::make_unique(); + _buzzerService = std::make_unique(); + _batteryService = std::make_unique(); + _storageService = std::make_unique(); + _randomService = std::make_unique(); + _highResClockService = std::make_unique(); + _filesystemService = std::make_unique(); + _eventBus = std::make_unique(); + _appServiceProvider = std::make_unique(*_eventBus); + _loopHooksService = std::make_unique(); + _notificationService = std::make_unique(); - services.buzzer = buzzerService.get(); - services.battery = batteryService.get(); - services.storage = storageService.get(); - services.random = randomService.get(); - services.highResClock = highResClockService.get(); - services.filesystem = filesystemService.get(); - services.eventBus = eventBus.get(); - services.timer = timerService.get(); - services.loopHooks = loopHooksService.get(); - services.notifications = notificationService.get(); + _services.buzzer = _buzzerService.get(); + _services.battery = _batteryService.get(); + _services.storage = _storageService.get(); + _services.random = _randomService.get(); + _services.highResClock = _highResClockService.get(); + _services.filesystem = _filesystemService.get(); + _services.eventBus = _eventBus.get(); + _services.appServices = _appServiceProvider.get(); + _services.loopHooks = _loopHooksService.get(); + _services.notifications = _notificationService.get(); - Buttons::get().setEventBus(eventBus.get()); - set_notification_center(notificationService.get()); + Buttons::get().setEventBus(_eventBus.get()); + set_notification_center(_notificationService.get()); } EspRuntime::~EspRuntime() { @@ -551,7 +521,7 @@ EspRuntime::~EspRuntime() { shutdown_time_sync_service(); } -cardboy::sdk::Services& EspRuntime::serviceRegistry() { return services; } +cardboy::sdk::Services& EspRuntime::serviceRegistry() { return _services; } void EspRuntime::initializeHardware() { static bool initialized = false; @@ -595,27 +565,7 @@ void EspFramebuffer::sendFrame_impl(bool clearAfterSend) { SMD::send_frame(clear bool EspFramebuffer::frameInFlight_impl() const { return SMD::frame_transfer_in_flight(); } -cardboy::sdk::InputState EspInput::readState_impl() { - cardboy::sdk::InputState state{}; - const uint8_t pressed = Buttons::get().get_pressed(); - if (pressed & BTN_UP) - state.up = true; - if (pressed & BTN_LEFT) - state.left = true; - if (pressed & BTN_RIGHT) - state.right = true; - if (pressed & BTN_DOWN) - state.down = true; - if (pressed & BTN_A) - state.a = true; - if (pressed & BTN_B) - state.b = true; - if (pressed & BTN_SELECT) - state.select = true; - if (pressed & BTN_START) - state.start = true; - return state; -} +cardboy::sdk::InputState EspInput::readState_impl() { return Buttons::get().get_state(); } std::uint32_t EspClock::millis_impl() { TickType_t ticks = xTaskGetTickCount(); diff --git a/Firmware/components/backend-esp/src/event_bus.cpp b/Firmware/components/backend-esp/src/event_bus.cpp deleted file mode 100644 index b434be3..0000000 --- a/Firmware/components/backend-esp/src/event_bus.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "cardboy/backend/esp/event_bus.hpp" - -#include "cardboy/sdk/event_bus.hpp" - -#include "freertos/portmacro.h" - -#include - -namespace cardboy::backend::esp { - -namespace { -[[nodiscard]] TickType_t toTicks(std::uint32_t timeout_ms) { - if (timeout_ms == cardboy::sdk::IEventBus::kWaitForever) - return portMAX_DELAY; - return pdMS_TO_TICKS(timeout_ms); -} - -constexpr UBaseType_t kEventQueueLength = 16; -} // namespace - -EventBus::EventBus() : - group(xEventGroupCreate()), - eventQueue(xQueueCreate(kEventQueueLength, sizeof(cardboy::sdk::AppEvent))) {} - -EventBus::~EventBus() { - if (group) - vEventGroupDelete(group); - if (eventQueue) - vQueueDelete(eventQueue); -} - -void EventBus::signal(std::uint32_t bits) { - if (!group || bits == 0) - return; - xEventGroupSetBits(group, bits); -} - -void EventBus::signalFromISR(std::uint32_t bits) { - if (!group || bits == 0) - return; - BaseType_t higherPriorityTaskWoken = pdFALSE; - xEventGroupSetBitsFromISR(group, bits, &higherPriorityTaskWoken); - if (higherPriorityTaskWoken == pdTRUE) - portYIELD_FROM_ISR(higherPriorityTaskWoken); -} - -std::uint32_t EventBus::wait(std::uint32_t mask, std::uint32_t timeout_ms) { - if (!group || mask == 0) - return 0; - const EventBits_t bits = xEventGroupWaitBits(group, mask, pdTRUE, pdFALSE, toTicks(timeout_ms)); - return static_cast(bits & mask); -} - -void EventBus::postEvent(const cardboy::sdk::AppEvent& event) { - if (!eventQueue) - return; - - if (xQueueSend(eventQueue, &event, 0) == errQUEUE_FULL) { - cardboy::sdk::AppEvent discarded{}; - if (xQueueReceive(eventQueue, &discarded, 0) == pdTRUE) - xQueueSend(eventQueue, &event, 0); - } - - signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer)); -} - -bool EventBus::popEvent(cardboy::sdk::AppEvent& outEvent) { - if (!eventQueue) - return false; - return xQueueReceive(eventQueue, &outEvent, 0) == pdTRUE; -} - -} // namespace cardboy::backend::esp diff --git a/Firmware/main/src/app_main.cpp b/Firmware/main/src/app_main.cpp index bb27541..33c88b7 100644 --- a/Firmware/main/src/app_main.cpp +++ b/Firmware/main/src/app_main.cpp @@ -241,7 +241,7 @@ extern "C" void app_main() { system.registerApp(apps::createTetrisAppFactory()); system.registerApp(apps::createGameboyAppFactory()); - // start_task_usage_monitor(); + start_task_usage_monitor(); system.run(); } diff --git a/Firmware/sdk/CMakeLists.txt b/Firmware/sdk/CMakeLists.txt index 2392881..eee6ae5 100644 --- a/Firmware/sdk/CMakeLists.txt +++ b/Firmware/sdk/CMakeLists.txt @@ -4,6 +4,11 @@ project(cardboy_sdk LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED YES) set(CMAKE_CXX_EXTENSIONS NO) +# add_compile_options(-Werror -O0 -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-unused-variable +# -Wno-error=unused-function +# -Wshadow -Wformat=2 -Wfloat-equal -D_GLIBCXX_DEBUG -Wconversion) +#add_compile_options(-fsanitize=address -fno-sanitize-recover -D_GLIBCXX_DEBUG) +#add_link_options(-fsanitize=address -fno-sanitize-recover -D_GLIBCXX_DEBUG) add_subdirectory(utils) diff --git a/Firmware/sdk/apps/clock/src/clock_app.cpp b/Firmware/sdk/apps/clock/src/clock_app.cpp index c7b6530..ed05876 100644 --- a/Firmware/sdk/apps/clock/src/clock_app.cpp +++ b/Firmware/sdk/apps/clock/src/clock_app.cpp @@ -49,7 +49,8 @@ public: const auto snap = captureTime(); renderIfNeeded(snap); lastSnapshot = snap; - refreshTimer = context.scheduleRepeatingTimer(200); + if (auto* timer = context.timer()) + refreshTimer = timer->scheduleTimer(200, true); } void onStop() override { cancelRefreshTimer(); } @@ -75,10 +76,11 @@ private: TimeSnapshot lastSnapshot{}; void cancelRefreshTimer() { - if (refreshTimer != cardboy::sdk::kInvalidAppTimer) { - context.cancelTimer(refreshTimer); - refreshTimer = cardboy::sdk::kInvalidAppTimer; - } + if (refreshTimer == cardboy::sdk::kInvalidAppTimer) + return; + if (auto* timer = context.timer()) + timer->cancelTimer(refreshTimer); + refreshTimer = cardboy::sdk::kInvalidAppTimer; } void handleButtonEvent(const cardboy::sdk::AppButtonEvent& button) { diff --git a/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp b/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp index 8965fde..ff93de9 100644 --- a/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp +++ b/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp @@ -1156,15 +1156,17 @@ public: uint32_t stableFrames = 0; void cancelTick() { - if (tickTimer != kInvalidAppTimer) { - context.cancelTimer(tickTimer); - tickTimer = kInvalidAppTimer; - } + if (tickTimer == kInvalidAppTimer) + return; + if (auto* timer = context.timer()) + timer->cancelTimer(tickTimer); + tickTimer = kInvalidAppTimer; } void scheduleNextTick(uint32_t delayMs) { cancelTick(); - tickTimer = context.scheduleTimer(delayMs, false); + if (auto* timer = context.timer()) + tickTimer = timer->scheduleTimer(delayMs, false); } uint32_t idleDelayMs() const { return browserDirty ? 50 : 140; } diff --git a/Firmware/sdk/apps/lockscreen/src/lockscreen_app.cpp b/Firmware/sdk/apps/lockscreen/src/lockscreen_app.cpp index 18a039a..e844e43 100644 --- a/Firmware/sdk/apps/lockscreen/src/lockscreen_app.cpp +++ b/Firmware/sdk/apps/lockscreen/src/lockscreen_app.cpp @@ -69,7 +69,8 @@ public: const auto snap = captureTime(); renderIfNeeded(snap); lastSnapshot = snap; - refreshTimer = context.scheduleRepeatingTimer(kRefreshIntervalMs); + if (auto* timer = context.timer()) + refreshTimer = timer->scheduleTimer(kRefreshIntervalMs, true); } void onStop() override { cancelRefreshTimer(); } @@ -104,10 +105,11 @@ private: std::uint32_t lastNotificationInteractionMs = 0; void cancelRefreshTimer() { - if (refreshTimer != cardboy::sdk::kInvalidAppTimer) { - context.cancelTimer(refreshTimer); - refreshTimer = cardboy::sdk::kInvalidAppTimer; - } + if (refreshTimer == cardboy::sdk::kInvalidAppTimer) + return; + if (auto* timer = context.timer()) + timer->cancelTimer(refreshTimer); + refreshTimer = cardboy::sdk::kInvalidAppTimer; } static bool comboPressed(const cardboy::sdk::InputState& state) { return state.a && state.select; } diff --git a/Firmware/sdk/apps/menu/src/menu_app.cpp b/Firmware/sdk/apps/menu/src/menu_app.cpp index 0cba807..f7e3f5f 100644 --- a/Firmware/sdk/apps/menu/src/menu_app.cpp +++ b/Firmware/sdk/apps/menu/src/menu_app.cpp @@ -189,15 +189,17 @@ private: } void cancelInactivityTimer() { - if (inactivityTimer != cardboy::sdk::kInvalidAppTimer) { - context.cancelTimer(inactivityTimer); - inactivityTimer = cardboy::sdk::kInvalidAppTimer; - } + if (inactivityTimer == cardboy::sdk::kInvalidAppTimer) + return; + if (auto* timer = context.timer()) + timer->cancelTimer(inactivityTimer); + inactivityTimer = cardboy::sdk::kInvalidAppTimer; } void resetInactivityTimer() { cancelInactivityTimer(); - inactivityTimer = context.scheduleTimer(kIdleTimeoutMs); + if (auto* timer = context.timer()) + inactivityTimer = timer->scheduleTimer(kIdleTimeoutMs, false); } }; diff --git a/Firmware/sdk/apps/snake/src/snake_app.cpp b/Firmware/sdk/apps/snake/src/snake_app.cpp index 7131063..12f3e8b 100644 --- a/Firmware/sdk/apps/snake/src/snake_app.cpp +++ b/Firmware/sdk/apps/snake/src/snake_app.cpp @@ -248,14 +248,16 @@ private: void scheduleMoveTimer() { cancelMoveTimer(); const std::uint32_t interval = currentInterval(); - moveTimer = context.scheduleRepeatingTimer(interval); + if (auto* timer = context.timer()) + moveTimer = timer->scheduleTimer(interval, true); } void cancelMoveTimer() { - if (moveTimer != cardboy::sdk::kInvalidAppTimer) { - context.cancelTimer(moveTimer); - moveTimer = cardboy::sdk::kInvalidAppTimer; - } + if (moveTimer == cardboy::sdk::kInvalidAppTimer) + return; + if (auto* timer = context.timer()) + timer->cancelTimer(moveTimer); + moveTimer = cardboy::sdk::kInvalidAppTimer; } [[nodiscard]] std::uint32_t currentInterval() const { diff --git a/Firmware/sdk/apps/tetris/src/tetris_app.cpp b/Firmware/sdk/apps/tetris/src/tetris_app.cpp index 15352f8..1db5b2f 100644 --- a/Firmware/sdk/apps/tetris/src/tetris_app.cpp +++ b/Firmware/sdk/apps/tetris/src/tetris_app.cpp @@ -235,35 +235,40 @@ private: void cancelTimers() { if (dropTimer != cardboy::sdk::kInvalidAppTimer) { - context.cancelTimer(dropTimer); + if (auto* timer = context.timer()) + timer->cancelTimer(dropTimer); dropTimer = cardboy::sdk::kInvalidAppTimer; } cancelSoftDropTimer(); } void cancelSoftDropTimer() { - if (softTimer != cardboy::sdk::kInvalidAppTimer) { - context.cancelTimer(softTimer); - softTimer = cardboy::sdk::kInvalidAppTimer; - } + if (softTimer == cardboy::sdk::kInvalidAppTimer) + return; + if (auto* timer = context.timer()) + timer->cancelTimer(softTimer); + softTimer = cardboy::sdk::kInvalidAppTimer; } void scheduleDropTimer() { cancelDropTimer(); const std::uint32_t interval = dropIntervalMs(); - dropTimer = context.scheduleRepeatingTimer(interval); + if (auto* timer = context.timer()) + dropTimer = timer->scheduleTimer(interval, true); } void cancelDropTimer() { - if (dropTimer != cardboy::sdk::kInvalidAppTimer) { - context.cancelTimer(dropTimer); - dropTimer = cardboy::sdk::kInvalidAppTimer; - } + if (dropTimer == cardboy::sdk::kInvalidAppTimer) + return; + if (auto* timer = context.timer()) + timer->cancelTimer(dropTimer); + dropTimer = cardboy::sdk::kInvalidAppTimer; } void scheduleSoftDropTimer() { cancelSoftDropTimer(); - softTimer = context.scheduleRepeatingTimer(60); + if (auto* timer = context.timer()) + softTimer = timer->scheduleTimer(60, true); } [[nodiscard]] std::uint32_t dropIntervalMs() const { diff --git a/Firmware/sdk/backend_interface/CMakeLists.txt b/Firmware/sdk/backend_interface/CMakeLists.txt index 56a5260..683f27c 100644 --- a/Firmware/sdk/backend_interface/CMakeLists.txt +++ b/Firmware/sdk/backend_interface/CMakeLists.txt @@ -16,7 +16,6 @@ target_sources(cardboy_backend_interface ${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/backend/backend_interface.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/backend.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/display_spec.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/event_bus.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/loop_hooks.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/input_state.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/platform.hpp diff --git a/Firmware/sdk/backend_interface/include/cardboy/sdk/app_events.hpp b/Firmware/sdk/backend_interface/include/cardboy/sdk/app_events.hpp index 42f7351..776ad71 100644 --- a/Firmware/sdk/backend_interface/include/cardboy/sdk/app_events.hpp +++ b/Firmware/sdk/backend_interface/include/cardboy/sdk/app_events.hpp @@ -12,17 +12,13 @@ namespace cardboy::sdk { using AppTimerHandle = std::uint32_t; constexpr AppTimerHandle kInvalidAppTimer = 0; -using TimerClientId = std::uint32_t; -constexpr TimerClientId kInvalidTimerClient = 0; - struct AppButtonEvent { InputState current{}; InputState previous{}; }; struct AppTimerEvent { - TimerClientId clientId = kInvalidTimerClient; - AppTimerHandle handle = kInvalidAppTimer; + AppTimerHandle handle = kInvalidAppTimer; }; struct AppEvent { @@ -51,6 +47,8 @@ struct AppEvent { } }; +static_assert(std::is_trivially_copyable_v); + template struct Overload : Ts... { using Ts::operator()...; diff --git a/Firmware/sdk/backend_interface/include/cardboy/sdk/event_bus.hpp b/Firmware/sdk/backend_interface/include/cardboy/sdk/event_bus.hpp deleted file mode 100644 index 97b5302..0000000 --- a/Firmware/sdk/backend_interface/include/cardboy/sdk/event_bus.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "cardboy/sdk/app_events.hpp" - -#include - -namespace cardboy::sdk { - -enum class EventBusSignal : std::uint32_t { - None = 0, - Input = 1u << 0, - Timer = 1u << 1, - External = 1u << 2, -}; - -inline EventBusSignal operator|(EventBusSignal lhs, EventBusSignal rhs) { - return static_cast(static_cast(lhs) | static_cast(rhs)); -} - -inline EventBusSignal& operator|=(EventBusSignal& lhs, EventBusSignal rhs) { - lhs = lhs | rhs; - return lhs; -} - -inline EventBusSignal operator&(EventBusSignal lhs, EventBusSignal rhs) { - return static_cast(static_cast(lhs) & static_cast(rhs)); -} - -inline std::uint32_t to_event_bits(EventBusSignal signal) { return static_cast(signal); } - -class IEventBus { -public: - static constexpr std::uint32_t kWaitForever = 0xFFFFFFFFu; - - virtual ~IEventBus() = default; - - virtual void signal(std::uint32_t bits) = 0; - virtual void signalFromISR(std::uint32_t bits) = 0; - virtual std::uint32_t wait(std::uint32_t mask, std::uint32_t timeout_ms) = 0; - virtual void postEvent(const AppEvent& event) = 0; - virtual bool popEvent(AppEvent& outEvent) = 0; -}; - -} // namespace cardboy::sdk diff --git a/Firmware/sdk/backend_interface/include/cardboy/sdk/framebuffer_hooks.hpp b/Firmware/sdk/backend_interface/include/cardboy/sdk/framebuffer_hooks.hpp index 6bbec6d..9ca3421 100644 --- a/Firmware/sdk/backend_interface/include/cardboy/sdk/framebuffer_hooks.hpp +++ b/Firmware/sdk/backend_interface/include/cardboy/sdk/framebuffer_hooks.hpp @@ -11,8 +11,8 @@ public: static void invokePreSend(void* framebuffer); private: - static PreSendHook hook_; - static void* userData_; + static PreSendHook _hook; + static void* _userData; }; } // namespace cardboy::sdk diff --git a/Firmware/sdk/backend_interface/include/cardboy/sdk/services.hpp b/Firmware/sdk/backend_interface/include/cardboy/sdk/services.hpp index 5315012..7ced296 100644 --- a/Firmware/sdk/backend_interface/include/cardboy/sdk/services.hpp +++ b/Firmware/sdk/backend_interface/include/cardboy/sdk/services.hpp @@ -1,10 +1,10 @@ #pragma once -#include "cardboy/sdk/event_bus.hpp" #include "cardboy/sdk/loop_hooks.hpp" #include "cardboy/sdk/timer_service.hpp" #include +#include #include #include #include @@ -74,36 +74,56 @@ public: class INotificationCenter { public: struct Notification { - std::uint64_t id = 0; - std::uint64_t timestamp = 0; + std::uint64_t id = 0; + std::uint64_t timestamp = 0; std::uint64_t externalId = 0; std::string title; std::string body; - bool unread = true; + bool unread = true; }; virtual ~INotificationCenter() = default; - virtual void pushNotification(Notification notification) = 0; - [[nodiscard]] virtual std::uint32_t revision() const = 0; - [[nodiscard]] virtual std::vector recent(std::size_t limit) const = 0; - virtual void markAllRead() = 0; - virtual void clear() = 0; - virtual void removeById(std::uint64_t id) = 0; - virtual void removeByExternalId(std::uint64_t externalId) = 0; + virtual void pushNotification(Notification notification) = 0; + [[nodiscard]] virtual std::uint32_t revision() const = 0; + [[nodiscard]] virtual std::vector recent(std::size_t limit) const = 0; + virtual void markAllRead() = 0; + virtual void clear() = 0; + virtual void removeById(std::uint64_t id) = 0; + virtual void removeByExternalId(std::uint64_t externalId) = 0; +}; + +class IEventBus { +public: + virtual ~IEventBus() = default; + + virtual void post(const AppEvent& event) = 0; + virtual AppEvent pop() = 0; +}; + +struct AppScopedServices { + ITimerService* timer = nullptr; + virtual ~AppScopedServices() = default; +}; + +class IAppServiceProvider { +public: + virtual ~IAppServiceProvider() = default; + + [[nodiscard]] virtual std::unique_ptr createScopedServices(std::uint64_t generation) = 0; }; struct Services { - IBuzzer* buzzer = nullptr; - IBatteryMonitor* battery = nullptr; - IStorage* storage = nullptr; - IRandom* random = nullptr; - IHighResClock* highResClock = nullptr; - IFilesystem* filesystem = nullptr; - IEventBus* eventBus = nullptr; - ITimerService* timer = nullptr; - ILoopHooks* loopHooks = nullptr; + IBuzzer* buzzer = nullptr; + IBatteryMonitor* battery = nullptr; + IStorage* storage = nullptr; + IRandom* random = nullptr; + IHighResClock* highResClock = nullptr; + IFilesystem* filesystem = nullptr; + IEventBus* eventBus = nullptr; + ILoopHooks* loopHooks = nullptr; INotificationCenter* notifications = nullptr; + IAppServiceProvider* appServices = nullptr; }; } // namespace cardboy::sdk diff --git a/Firmware/sdk/backend_interface/include/cardboy/sdk/timer_service.hpp b/Firmware/sdk/backend_interface/include/cardboy/sdk/timer_service.hpp index 90cb6b6..c3e1abf 100644 --- a/Firmware/sdk/backend_interface/include/cardboy/sdk/timer_service.hpp +++ b/Firmware/sdk/backend_interface/include/cardboy/sdk/timer_service.hpp @@ -10,12 +10,9 @@ class ITimerService { public: virtual ~ITimerService() = default; - virtual TimerClientId acquireClient() = 0; - virtual void releaseClient(TimerClientId clientId) = 0; - virtual AppTimerHandle scheduleTimer(TimerClientId clientId, std::uint32_t delay_ms, bool repeat) = 0; - virtual void cancelTimer(TimerClientId clientId, AppTimerHandle handle) = 0; - virtual void cancelAllTimers(TimerClientId clientId) = 0; + virtual AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) = 0; + virtual void cancelTimer(AppTimerHandle handle) = 0; + virtual void cancelAllTimers() = 0; }; } // namespace cardboy::sdk - diff --git a/Firmware/sdk/backends/desktop/include/cardboy/backend/desktop_backend.hpp b/Firmware/sdk/backends/desktop/include/cardboy/backend/desktop_backend.hpp index 5ec2f2e..f131e53 100644 --- a/Firmware/sdk/backends/desktop/include/cardboy/backend/desktop_backend.hpp +++ b/Firmware/sdk/backends/desktop/include/cardboy/backend/desktop_backend.hpp @@ -1,6 +1,5 @@ #pragma once -#include "cardboy/sdk/event_bus.hpp" #include "cardboy/sdk/platform.hpp" #include "cardboy/sdk/services.hpp" @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -113,33 +113,39 @@ public: void signal(std::uint32_t bits) override; void signalFromISR(std::uint32_t bits) override; std::uint32_t wait(std::uint32_t mask, std::uint32_t timeout_ms) override; - void postEvent(const cardboy::sdk::AppEvent& event) override; - bool popEvent(cardboy::sdk::AppEvent& outEvent) override; private: DesktopRuntime& runtime; std::mutex mutex; std::condition_variable cv; std::uint32_t pendingBits = 0; - std::mutex eventMutex; - std::deque eventQueue; +}; + +class DesktopScopedEventBus final : public cardboy::sdk::IAppEventBus { +public: + explicit DesktopScopedEventBus(cardboy::sdk::IEventBus& bus); + + void post(const cardboy::sdk::AppEvent& event) override; + bool pop(cardboy::sdk::AppEvent& outEvent) override; + void clear() override; + +private: + cardboy::sdk::IEventBus& globalBus; + std::mutex mutex; + std::deque queue; }; class DesktopTimerService final : public cardboy::sdk::ITimerService { public: - DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus); + DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IAppEventBus& appBus); ~DesktopTimerService() override; - cardboy::sdk::TimerClientId acquireClient() override; - void releaseClient(cardboy::sdk::TimerClientId clientId) override; - cardboy::sdk::AppTimerHandle scheduleTimer(cardboy::sdk::TimerClientId clientId, std::uint32_t delay_ms, - bool repeat) override; - void cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) override; - void cancelAllTimers(cardboy::sdk::TimerClientId clientId) override; + cardboy::sdk::AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) override; + void cancelTimer(cardboy::sdk::AppTimerHandle handle) override; + void cancelAllTimers() override; private: struct TimerRecord { - cardboy::sdk::TimerClientId clientId = cardboy::sdk::kInvalidTimerClient; cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer; std::chrono::steady_clock::time_point due; std::chrono::milliseconds interval{0}; @@ -151,15 +157,30 @@ private: void wakeWorker(); void cleanupInactive(); - DesktopRuntime& runtime; - cardboy::sdk::IEventBus& eventBus; + DesktopRuntime& runtime; + cardboy::sdk::IAppEventBus& appEventBus; std::mutex mutex; std::condition_variable cv; std::vector timers; bool stopWorker = false; std::thread worker; cardboy::sdk::AppTimerHandle nextHandle = 1; - cardboy::sdk::TimerClientId nextClient = 1; +}; + +class DesktopAppServiceProvider final : public cardboy::sdk::IAppServiceProvider { +public: + DesktopAppServiceProvider(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus); + + [[nodiscard]] std::unique_ptr createScopedServices(std::uint64_t generation) override; + +private: + struct ScopedServices final : cardboy::sdk::AppScopedServices { + std::unique_ptr ownedEventBus; + std::unique_ptr ownedTimer; + }; + + DesktopRuntime& runtime; + cardboy::sdk::IEventBus& eventBus; }; class DesktopFramebuffer final : public cardboy::sdk::FramebufferFacade { @@ -240,7 +261,7 @@ private: DesktopHighResClock highResService; DesktopFilesystem filesystemService; DesktopEventBus eventBusService; - DesktopTimerService timerService; + DesktopAppServiceProvider appServiceProvider; DesktopNotificationCenter notificationService; cardboy::sdk::Services services{}; }; diff --git a/Firmware/sdk/backends/desktop/src/desktop_backend.cpp b/Firmware/sdk/backends/desktop/src/desktop_backend.cpp index cc3a35e..a2abb36 100644 --- a/Firmware/sdk/backends/desktop/src/desktop_backend.cpp +++ b/Firmware/sdk/backends/desktop/src/desktop_backend.cpp @@ -63,26 +63,34 @@ std::uint32_t DesktopEventBus::wait(std::uint32_t mask, std::uint32_t timeout_ms } } -void DesktopEventBus::postEvent(const cardboy::sdk::AppEvent& event) { +DesktopScopedEventBus::DesktopScopedEventBus(cardboy::sdk::IEventBus& bus) : globalBus(bus) {} + +void DesktopScopedEventBus::post(const cardboy::sdk::AppEvent& event) { { - std::lock_guard lock(eventMutex); - if (eventQueue.size() >= kDesktopEventQueueLimit) - eventQueue.pop_front(); - eventQueue.push_back(event); + std::lock_guard lock(mutex); + if (queue.size() >= kDesktopEventQueueLimit) + queue.pop_front(); + queue.push_back(event); } - signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer)); + globalBus.signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer)); } -bool DesktopEventBus::popEvent(cardboy::sdk::AppEvent& outEvent) { - std::lock_guard lock(eventMutex); - if (eventQueue.empty()) +bool DesktopScopedEventBus::pop(cardboy::sdk::AppEvent& outEvent) { + std::lock_guard lock(mutex); + if (queue.empty()) return false; - outEvent = eventQueue.front(); - eventQueue.pop_front(); + outEvent = queue.front(); + queue.pop_front(); return true; } -DesktopTimerService::DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus) : runtime(owner), eventBus(bus) { +void DesktopScopedEventBus::clear() { + std::lock_guard lock(mutex); + queue.clear(); +} + +DesktopTimerService::DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IAppEventBus& appBus) : + runtime(owner), appEventBus(appBus) { worker = std::thread(&DesktopTimerService::workerLoop, this); } @@ -94,33 +102,17 @@ DesktopTimerService::~DesktopTimerService() { cv.notify_all(); if (worker.joinable()) worker.join(); + cancelAllTimers(); } -cardboy::sdk::TimerClientId DesktopTimerService::acquireClient() { - std::lock_guard lock(mutex); - cardboy::sdk::TimerClientId id = cardboy::sdk::kInvalidTimerClient; - do { - id = nextClient++; - } while (id == cardboy::sdk::kInvalidTimerClient); - if (nextClient == cardboy::sdk::kInvalidTimerClient) - ++nextClient; - return id; -} - -void DesktopTimerService::releaseClient(cardboy::sdk::TimerClientId clientId) { cancelAllTimers(clientId); } - -cardboy::sdk::AppTimerHandle DesktopTimerService::scheduleTimer(cardboy::sdk::TimerClientId clientId, - std::uint32_t delay_ms, bool repeat) { - if (clientId == cardboy::sdk::kInvalidTimerClient) - return cardboy::sdk::kInvalidAppTimer; - +cardboy::sdk::AppTimerHandle DesktopTimerService::scheduleTimer(std::uint32_t delay_ms, bool repeat) { const auto now = std::chrono::steady_clock::now(); const auto effectiveDelayMs = std::chrono::milliseconds(delay_ms); const auto dueTime = delay_ms == 0 ? now : now + effectiveDelayMs; - const auto interval = std::chrono::milliseconds(std::max(1, repeat ? delay_ms : std::max(delay_ms, 1u))); + const auto interval = std::chrono::milliseconds(std::max(1, repeat ? std::max(delay_ms, 1u) + : std::max(delay_ms, 1u))); TimerRecord record{}; - record.clientId = clientId; record.repeat = repeat; record.interval = interval; record.due = dueTime; @@ -141,25 +133,22 @@ cardboy::sdk::AppTimerHandle DesktopTimerService::scheduleTimer(cardboy::sdk::Ti } } -void DesktopTimerService::cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) { - if (clientId == cardboy::sdk::kInvalidTimerClient || handle == cardboy::sdk::kInvalidAppTimer) +void DesktopTimerService::cancelTimer(cardboy::sdk::AppTimerHandle handle) { + if (handle == cardboy::sdk::kInvalidAppTimer) return; std::lock_guard lock(mutex); for (auto& record: timers) { - if (record.clientId == clientId && record.handle == handle) + if (record.handle == handle) record.active = false; } cleanupInactive(); wakeWorker(); } -void DesktopTimerService::cancelAllTimers(cardboy::sdk::TimerClientId clientId) { - if (clientId == cardboy::sdk::kInvalidTimerClient) - return; +void DesktopTimerService::cancelAllTimers() { std::lock_guard lock(mutex); for (auto& record: timers) { - if (record.clientId == clientId) - record.active = false; + record.active = false; } cleanupInactive(); wakeWorker(); @@ -195,13 +184,12 @@ void DesktopTimerService::workerLoop() { lock.unlock(); cardboy::sdk::AppTimerEvent timerEvent{}; - timerEvent.clientId = record.clientId; - timerEvent.handle = record.handle; + timerEvent.handle = record.handle; cardboy::sdk::AppEvent event{}; event.timestamp_ms = runtime.clock.millis(); event.data = timerEvent; - eventBus.postEvent(event); + appEventBus.post(event); lock.lock(); continue; @@ -218,6 +206,19 @@ void DesktopTimerService::cleanupInactive() { timers.end()); } +DesktopAppServiceProvider::DesktopAppServiceProvider(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus) : + runtime(owner), eventBus(bus) {} + +std::unique_ptr DesktopAppServiceProvider::createScopedServices(std::uint64_t generation) { + (void)generation; + auto scoped = std::make_unique(); + scoped->ownedEventBus = std::make_unique(eventBus); + scoped->events = scoped->ownedEventBus.get(); + scoped->ownedTimer = std::make_unique(runtime, *scoped->ownedEventBus); + scoped->timer = scoped->ownedTimer.get(); + return scoped; +} + bool DesktopStorage::readUint32(std::string_view ns, std::string_view key, std::uint32_t& out) { auto it = data.find(composeKey(ns, key)); if (it == data.end()) @@ -441,7 +442,7 @@ DesktopRuntime::DesktopRuntime() : "Cardboy Desktop"), texture(), sprite(texture), pixels(static_cast(cardboy::sdk::kDisplayWidth * cardboy::sdk::kDisplayHeight) * 4, 0), - framebuffer(*this), input(*this), clock(*this), eventBusService(*this), timerService(*this, eventBusService) { + framebuffer(*this), input(*this), clock(*this), eventBusService(*this), appServiceProvider(*this, eventBusService) { window.setFramerateLimit(60); if (!texture.resize(sf::Vector2u{cardboy::sdk::kDisplayWidth, cardboy::sdk::kDisplayHeight})) throw std::runtime_error("Failed to allocate texture for desktop framebuffer"); @@ -457,7 +458,7 @@ DesktopRuntime::DesktopRuntime() : services.highResClock = &highResService; services.filesystem = &filesystemService; services.eventBus = &eventBusService; - services.timer = &timerService; + services.appServices = &appServiceProvider; services.loopHooks = nullptr; services.notifications = ¬ificationService; } diff --git a/Firmware/sdk/core/include/cardboy/sdk/app_framework.hpp b/Firmware/sdk/core/include/cardboy/sdk/app_framework.hpp index f311b9f..6c02885 100644 --- a/Firmware/sdk/core/include/cardboy/sdk/app_framework.hpp +++ b/Firmware/sdk/core/include/cardboy/sdk/app_framework.hpp @@ -33,66 +33,44 @@ struct AppContext { [[nodiscard]] Services* getServices() const { return services; } - [[nodiscard]] IBuzzer* buzzer() const { return services ? services->buzzer : nullptr; } - [[nodiscard]] IBatteryMonitor* battery() const { return services ? services->battery : nullptr; } - [[nodiscard]] IStorage* storage() const { return services ? services->storage : nullptr; } - [[nodiscard]] IRandom* random() const { return services ? services->random : nullptr; } - [[nodiscard]] IHighResClock* highResClock() const { return services ? services->highResClock : nullptr; } - [[nodiscard]] IFilesystem* filesystem() const { return services ? services->filesystem : nullptr; } - [[nodiscard]] IEventBus* eventBus() const { return services ? services->eventBus : nullptr; } - [[nodiscard]] ITimerService* timerService() const { return services ? services->timer : nullptr; } - [[nodiscard]] ILoopHooks* loopHooks() const { return services ? services->loopHooks : nullptr; } + [[nodiscard]] IBuzzer* buzzer() const { return services ? services->buzzer : nullptr; } + [[nodiscard]] IBatteryMonitor* battery() const { return services ? services->battery : nullptr; } + [[nodiscard]] IStorage* storage() const { return services ? services->storage : nullptr; } + [[nodiscard]] IRandom* random() const { return services ? services->random : nullptr; } + [[nodiscard]] IHighResClock* highResClock() const { return services ? services->highResClock : nullptr; } + [[nodiscard]] IFilesystem* filesystem() const { return services ? services->filesystem : nullptr; } + [[nodiscard]] AppScopedServices* appServices() const { return _scopedServices; } + [[nodiscard]] IEventBus* eventBus() const { return services ? services->eventBus : nullptr; } + [[nodiscard]] ITimerService* timer() const { return _scopedServices ? _scopedServices->timer : nullptr; } + [[nodiscard]] ILoopHooks* loopHooks() const { return services ? services->loopHooks : nullptr; } [[nodiscard]] INotificationCenter* notificationCenter() const { return services ? services->notifications : nullptr; } void requestAppSwitchByIndex(std::size_t index) { - pendingAppIndex = index; - pendingAppName.clear(); - pendingSwitchByName = false; - pendingSwitch = true; + _pendingAppIndex = index; + _pendingAppName.clear(); + _pendingSwitchByName = false; + _pendingSwitch = true; } void requestAppSwitchByName(std::string_view name) { - pendingAppName.assign(name.begin(), name.end()); - pendingSwitchByName = true; - pendingSwitch = true; + _pendingAppName.assign(name.begin(), name.end()); + _pendingSwitchByName = true; + _pendingSwitch = true; } - [[nodiscard]] bool hasPendingAppSwitch() const { return pendingSwitch; } - - AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat = false) { - auto* timer = timerService(); - if (!timer || timerClientId == kInvalidTimerClient) - return kInvalidAppTimer; - return timer->scheduleTimer(timerClientId, delay_ms, repeat); - } - - AppTimerHandle scheduleRepeatingTimer(std::uint32_t interval_ms) { - return scheduleTimer(interval_ms, true); - } - - void cancelTimer(AppTimerHandle handle) { - auto* timer = timerService(); - if (!timer || timerClientId == kInvalidTimerClient) - return; - timer->cancelTimer(timerClientId, handle); - } - - void cancelAllTimers() { - auto* timer = timerService(); - if (!timer || timerClientId == kInvalidTimerClient) - return; - timer->cancelAllTimers(timerClientId); - } + [[nodiscard]] bool hasPendingAppSwitch() const { return _pendingSwitch; } private: friend class AppSystem; - bool pendingSwitch = false; - bool pendingSwitchByName = false; - std::size_t pendingAppIndex = 0; - std::string pendingAppName; - TimerClientId timerClientId = kInvalidTimerClient; + bool _pendingSwitch = false; + bool _pendingSwitchByName = false; + std::size_t _pendingAppIndex = 0; + std::string _pendingAppName; + AppScopedServices* _scopedServices = nullptr; + + void setScopedServices(AppScopedServices* services) { _scopedServices = services; } }; class IApp { diff --git a/Firmware/sdk/core/include/cardboy/sdk/app_system.hpp b/Firmware/sdk/core/include/cardboy/sdk/app_system.hpp index 791337c..7c85e9b 100644 --- a/Firmware/sdk/core/include/cardboy/sdk/app_system.hpp +++ b/Firmware/sdk/core/include/cardboy/sdk/app_system.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include "app_framework.hpp" #include @@ -16,36 +15,30 @@ public: ~AppSystem(); void registerApp(std::unique_ptr factory); - bool startApp(const std::string& name); - bool startAppByIndex(std::size_t index); + void startApp(const std::string& name); + void startAppByIndex(std::size_t index); void run(); - [[nodiscard]] std::size_t appCount() const { return factories.size(); } + [[nodiscard]] std::size_t appCount() const { return _factories.size(); } [[nodiscard]] const IAppFactory* factoryAt(std::size_t index) const; [[nodiscard]] std::size_t indexOfFactory(const IAppFactory* factory) const; - [[nodiscard]] std::size_t currentFactoryIndex() const { return activeIndex; } + [[nodiscard]] std::size_t currentFactoryIndex() const { return _activeIndex; } - [[nodiscard]] const IApp* currentApp() const { return current.get(); } - [[nodiscard]] const IAppFactory* currentFactory() const { return activeFactory; } + [[nodiscard]] const IApp* currentApp() const { return _current.get(); } + [[nodiscard]] const IAppFactory* currentFactory() const { return _activeFactory; } private: - friend struct AppContext; + void handlePendingSwitchRequest(); + std::unique_ptr createAppScopedServices(std::uint64_t generation); - void dispatchEvent(const AppEvent& event); - bool handlePendingSwitchRequest(); - void attachTimerClient(); - void detachTimerClient(); - void processEventBusEvents(); - - AppContext context; - std::vector> factories; - std::unique_ptr current; - IAppFactory* activeFactory = nullptr; - std::size_t activeIndex = static_cast(-1); - TimerClientId timerClientId = kInvalidTimerClient; - InputState lastInputState{}; - bool suppressInputs = false; + AppContext _context; + std::vector> _factories; + std::unique_ptr _current; + IAppFactory* _activeFactory = nullptr; + std::size_t _activeIndex = static_cast(-1); + std::unique_ptr _scopedServices; + std::uint64_t _nextScopedGeneration = 1; }; } // namespace cardboy::sdk diff --git a/Firmware/sdk/core/include/cardboy/sdk/framebuffer_hooks.hpp b/Firmware/sdk/core/include/cardboy/sdk/framebuffer_hooks.hpp index 23301ea..32e8efe 100644 --- a/Firmware/sdk/core/include/cardboy/sdk/framebuffer_hooks.hpp +++ b/Firmware/sdk/core/include/cardboy/sdk/framebuffer_hooks.hpp @@ -13,8 +13,8 @@ public: static void invokePreSend(void* framebuffer); private: - static PreSendHook hook_; - static void* userData_; + static PreSendHook _hook; + static void* _userData; }; } // namespace cardboy::sdk diff --git a/Firmware/sdk/core/include/cardboy/sdk/status_bar.hpp b/Firmware/sdk/core/include/cardboy/sdk/status_bar.hpp index 8d78e7f..0606c48 100644 --- a/Firmware/sdk/core/include/cardboy/sdk/status_bar.hpp +++ b/Firmware/sdk/core/include/cardboy/sdk/status_bar.hpp @@ -17,11 +17,11 @@ class StatusBar { public: static StatusBar& instance(); - void setServices(Services* services) { services_ = services; } + void setServices(Services* services) { _services = services; } void setEnabled(bool value); void toggle(); - [[nodiscard]] bool isEnabled() const { return enabled_; } + [[nodiscard]] bool isEnabled() const { return _enabled; } void setCurrentAppName(std::string_view name); @@ -29,7 +29,7 @@ public: template void renderIfEnabled(Framebuffer& fb) { - if (!enabled_) + if (!_enabled) return; renderBar(fb); } @@ -84,9 +84,9 @@ private: [[nodiscard]] std::string prepareLeftText(int displayWidth) const; [[nodiscard]] std::string prepareRightText() const; - bool enabled_ = false; - Services* services_ = nullptr; - std::string appName_{}; + bool _enabled = false; + Services* _services = nullptr; + std::string _appName{}; }; } // namespace cardboy::sdk diff --git a/Firmware/sdk/core/src/app_system.cpp b/Firmware/sdk/core/src/app_system.cpp index 9fe0bd2..30bd238 100644 --- a/Firmware/sdk/core/src/app_system.cpp +++ b/Firmware/sdk/core/src/app_system.cpp @@ -7,14 +7,6 @@ namespace cardboy::sdk { namespace { -[[nodiscard]] bool inputsDiffer(const InputState& a, const InputState& b) { - return a.up != b.up || a.down != b.down || a.left != b.left || a.right != b.right || a.a != b.a || a.b != b.b || - a.select != b.select || a.start != b.start; -} - -[[nodiscard]] bool anyButtonPressed(const InputState& state) { - return state.up || state.down || state.left || state.right || state.a || state.b || state.select || state.start; -} template void statusBarPreSendHook(void* framebuffer, void* userData) { @@ -25,191 +17,120 @@ void statusBarPreSendHook(void* framebuffer, void* userData) { } } // namespace -AppSystem::AppSystem(AppContext ctx) : context(std::move(ctx)) { - context.system = this; +AppSystem::AppSystem(AppContext ctx) : _context(std::move(ctx)) { + _context.system = this; auto& statusBar = StatusBar::instance(); - statusBar.setServices(context.services); - using FBType = typename AppContext::Framebuffer; - FramebufferHooks::setPreSendHook(&statusBarPreSendHook, &statusBar); + statusBar.setServices(_context.services); + FramebufferHooks::setPreSendHook(&statusBarPreSendHook, &statusBar); } -AppSystem::~AppSystem() { - detachTimerClient(); - FramebufferHooks::clearPreSendHook(); -} +AppSystem::~AppSystem() { FramebufferHooks::clearPreSendHook(); } void AppSystem::registerApp(std::unique_ptr factory) { - if (!factory) - return; - factories.emplace_back(std::move(factory)); + assert(factory); + _factories.emplace_back(std::move(factory)); } -bool AppSystem::startApp(const std::string& name) { - for (std::size_t i = 0; i < factories.size(); ++i) { - if (factories[i]->name() == name) - return startAppByIndex(i); +void AppSystem::startApp(const std::string& name) { + for (std::size_t i = 0; i < _factories.size(); ++i) { + if (_factories[i]->name() == name) + startAppByIndex(i); } - return false; } -bool AppSystem::startAppByIndex(std::size_t index) { - if (index >= factories.size()) - return false; +void AppSystem::startAppByIndex(std::size_t index) { + assert(index < _factories.size()); - context.system = this; - auto& factory = factories[index]; - auto app = factory->create(context); - if (!app) - return false; - - if (current) { - current->onStop(); - current.reset(); - detachTimerClient(); + if (_current) { + _current->onStop(); + _current.reset(); } - activeFactory = factory.get(); - activeIndex = index; - context.pendingSwitch = false; - context.pendingSwitchByName = false; - context.pendingAppName.clear(); - current = std::move(app); - attachTimerClient(); - lastInputState = context.input.readState(); - suppressInputs = true; - StatusBar::instance().setServices(context.services); - StatusBar::instance().setCurrentAppName(activeFactory ? activeFactory->name() : ""); - current->onStart(); - return true; + _context.system = this; + + auto& factory = _factories[index]; + + const std::uint64_t newGeneration = _nextScopedGeneration++; + + auto app = factory->create(_context); + assert(app); + + _scopedServices.reset(); + auto scoped = createAppScopedServices(newGeneration); + _scopedServices = std::move(scoped); + _context.setScopedServices(_scopedServices.get()); + + _activeFactory = factory.get(); + _activeIndex = index; + _context._pendingSwitch = false; + _context._pendingSwitchByName = false; + _context._pendingAppName.clear(); + _current = std::move(app); + StatusBar::instance().setServices(_context.services); + StatusBar::instance().setCurrentAppName(_activeFactory ? _activeFactory->name() : ""); + _current->onStart(); } void AppSystem::run() { - if (!current) { - if (factories.empty() || !startAppByIndex(0)) - return; + if (!_current) { + assert(!_factories.empty()); + startAppByIndex(0); } while (true) { - auto* eventBus = context.eventBus(); - if (!eventBus) - return; - - if (auto* hooks = context.loopHooks()) + if (auto* hooks = _context.loopHooks()) hooks->onLoopIteration(); - processEventBusEvents(); - if (handlePendingSwitchRequest()) - continue; - - const std::uint32_t now = context.clock.millis(); - const InputState inputNow = context.input.readState(); - const bool consumedByStatusToggle = StatusBar::instance().handleToggleInput(inputNow, lastInputState); - - if (suppressInputs) { - lastInputState = inputNow; - if (!anyButtonPressed(inputNow)) - suppressInputs = false; - } else if (!consumedByStatusToggle && inputsDiffer(inputNow, lastInputState)) { - AppButtonEvent button{}; - button.current = inputNow; - button.previous = lastInputState; - - AppEvent evt{}; - evt.timestamp_ms = now; - evt.data = button; - - lastInputState = inputNow; - dispatchEvent(evt); - } else if (consumedByStatusToggle) { - lastInputState = inputNow; + auto event = _context.eventBus()->pop(); + if (const auto* btn = event.button()) { + const bool consumedByStatusToggle = StatusBar::instance().handleToggleInput(btn->current, btn->previous); + if (consumedByStatusToggle) { + continue; + } } - - if (handlePendingSwitchRequest()) + _current->handleEvent(event); + if (_context._pendingSwitch) { + handlePendingSwitchRequest(); continue; - - const std::uint32_t mask = to_event_bits(EventBusSignal::Input) | to_event_bits(EventBusSignal::Timer); - eventBus->wait(mask, IEventBus::kWaitForever); + } } } const IAppFactory* AppSystem::factoryAt(std::size_t index) const { - if (index >= factories.size()) + if (index >= _factories.size()) return nullptr; - return factories[index].get(); + return _factories[index].get(); } std::size_t AppSystem::indexOfFactory(const IAppFactory* factory) const { if (!factory) return static_cast(-1); - for (std::size_t i = 0; i < factories.size(); ++i) { - if (factories[i].get() == factory) + for (std::size_t i = 0; i < _factories.size(); ++i) { + if (_factories[i].get() == factory) return i; } return static_cast(-1); } -void AppSystem::dispatchEvent(const AppEvent& event) { - if (!current) - return; - - if (const auto* timer = event.timer()) { - if (timer->clientId != timerClientId) - return; - } - - current->handleEvent(event); -} - -bool AppSystem::handlePendingSwitchRequest() { - if (!context.pendingSwitch) - return false; - const bool byName = context.pendingSwitchByName; - const std::size_t reqIndex = context.pendingAppIndex; - const std::string reqName = context.pendingAppName; - context.pendingSwitch = false; - context.pendingSwitchByName = false; - context.pendingAppName.clear(); +void AppSystem::handlePendingSwitchRequest() { + assert(_context._pendingSwitch); + const bool byName = _context._pendingSwitchByName; + const std::size_t reqIndex = _context._pendingAppIndex; + const std::string reqName = _context._pendingAppName; + _context._pendingSwitch = false; + _context._pendingSwitchByName = false; + _context._pendingAppName.clear(); bool switched = false; if (byName) - switched = startApp(reqName); + startApp(reqName); else - switched = startAppByIndex(reqIndex); - return switched; + startAppByIndex(reqIndex); } -void AppSystem::attachTimerClient() { - auto* timer = context.timerService(); - if (!timer) { - timerClientId = kInvalidTimerClient; - context.timerClientId = kInvalidTimerClient; - return; - } - - timerClientId = timer->acquireClient(); - context.timerClientId = timerClientId; -} - -void AppSystem::detachTimerClient() { - auto* timer = context.timerService(); - if (!timer || timerClientId == kInvalidTimerClient) - return; - - timer->releaseClient(timerClientId); - timerClientId = kInvalidTimerClient; - context.timerClientId = kInvalidTimerClient; -} - -void AppSystem::processEventBusEvents() { - auto* eventBus = context.eventBus(); - if (!eventBus) - return; - - AppEvent event{}; - while (eventBus->popEvent(event)) { - dispatchEvent(event); - if (context.pendingSwitch) - break; - } +std::unique_ptr AppSystem::createAppScopedServices(std::uint64_t generation) { + if (!_context.services || !_context.services->appServices) + return nullptr; + return _context.services->appServices->createScopedServices(generation); } } // namespace cardboy::sdk diff --git a/Firmware/sdk/core/src/framebuffer_hooks.cpp b/Firmware/sdk/core/src/framebuffer_hooks.cpp index a4c9764..4ea9ce1 100644 --- a/Firmware/sdk/core/src/framebuffer_hooks.cpp +++ b/Firmware/sdk/core/src/framebuffer_hooks.cpp @@ -2,22 +2,22 @@ namespace cardboy::sdk { -FramebufferHooks::PreSendHook FramebufferHooks::hook_ = nullptr; -void* FramebufferHooks::userData_ = nullptr; +FramebufferHooks::PreSendHook FramebufferHooks::_hook = nullptr; +void* FramebufferHooks::_userData = nullptr; void FramebufferHooks::setPreSendHook(PreSendHook hook, void* userData) { - hook_ = hook; - userData_ = userData; + _hook = hook; + _userData = userData; } void FramebufferHooks::clearPreSendHook() { - hook_ = nullptr; - userData_ = nullptr; + _hook = nullptr; + _userData = nullptr; } void FramebufferHooks::invokePreSend(void* framebuffer) { - if (hook_) - hook_(framebuffer, userData_); + if (_hook) + _hook(framebuffer, _userData); } } // namespace cardboy::sdk diff --git a/Firmware/sdk/core/src/status_bar.cpp b/Firmware/sdk/core/src/status_bar.cpp index 07b07aa..97dcc22 100644 --- a/Firmware/sdk/core/src/status_bar.cpp +++ b/Firmware/sdk/core/src/status_bar.cpp @@ -12,17 +12,17 @@ StatusBar& StatusBar::instance() { return bar; } -void StatusBar::setEnabled(bool value) { enabled_ = value; } +void StatusBar::setEnabled(bool value) { _enabled = value; } void StatusBar::toggle() { - enabled_ = !enabled_; - if (services_ && services_->buzzer) - services_->buzzer->beepMove(); + _enabled = !_enabled; + if (_services && _services->buzzer) + _services->buzzer->beepMove(); } void StatusBar::setCurrentAppName(std::string_view name) { - appName_.assign(name.begin(), name.end()); - std::transform(appName_.begin(), appName_.end(), appName_.begin(), + _appName.assign(name.begin(), name.end()); + std::transform(_appName.begin(), _appName.end(), _appName.begin(), [](unsigned char ch) { return static_cast(std::toupper(ch)); }); } @@ -37,7 +37,7 @@ bool StatusBar::handleToggleInput(const InputState& current, const InputState& p } std::string StatusBar::prepareLeftText(int displayWidth) const { - std::string text = appName_.empty() ? std::string("CARDBOY") : appName_; + std::string text = _appName.empty() ? std::string("CARDBOY") : _appName; int maxWidth = std::max(0, displayWidth - 32); while (!text.empty() && font16x8::measureText(text, 1, 1) > maxWidth) text.pop_back(); @@ -45,14 +45,14 @@ std::string StatusBar::prepareLeftText(int displayWidth) const { } std::string StatusBar::prepareRightText() const { - if (!services_) + if (!_services) return {}; std::string right; - if (services_->battery && services_->battery->hasData()) { - const float current = services_->battery->current(); - const float chargeMah = services_->battery->charge(); - const float fallbackV = services_->battery->voltage(); + if (_services->battery && _services->battery->hasData()) { + const float current = _services->battery->current(); + const float chargeMah = _services->battery->charge(); + const float fallbackV = _services->battery->voltage(); char buf[64]; if (std::isfinite(current) && std::isfinite(chargeMah)) { std::snprintf(buf, sizeof(buf), "cur %.2fmA chr %.2fmAh", static_cast(current), @@ -63,7 +63,7 @@ std::string StatusBar::prepareRightText() const { right.assign(buf); } - if (services_->buzzer && services_->buzzer->isMuted()) { + if (_services->buzzer && _services->buzzer->isMuted()) { if (!right.empty()) right.append(" "); right.append("MUTE"); diff --git a/Firmware/sdkconfig b/Firmware/sdkconfig index 8f7159a..d9b0c3c 100644 --- a/Firmware/sdkconfig +++ b/Firmware/sdkconfig @@ -613,9 +613,9 @@ CONFIG_PARTITION_TABLE_MD5=y # # Compiler options # -# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set +CONFIG_COMPILER_OPTIMIZATION_DEBUG=y # CONFIG_COMPILER_OPTIMIZATION_SIZE is not set -CONFIG_COMPILER_OPTIMIZATION_PERF=y +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set # CONFIG_COMPILER_OPTIMIZATION_NONE is not set CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set @@ -1714,6 +1714,7 @@ CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 # # Port # +CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y # CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y # CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set @@ -1761,16 +1762,16 @@ CONFIG_HAL_WDT_USE_ROM_IMPL=y # # Heap memory debugging # -CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_DISABLED is not set # CONFIG_HEAP_POISONING_LIGHT is not set -# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_POISONING_COMPREHENSIVE=y CONFIG_HEAP_TRACING_OFF=y # CONFIG_HEAP_TRACING_STANDALONE is not set # CONFIG_HEAP_TRACING_TOHOST is not set # CONFIG_HEAP_USE_HOOKS is not set # CONFIG_HEAP_TASK_TRACKING is not set # CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set -CONFIG_HEAP_TLSF_USE_ROM_IMPL=y +# CONFIG_HEAP_TLSF_USE_ROM_IMPL is not set # CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set # end of Heap memory debugging @@ -2463,9 +2464,9 @@ CONFIG_LOG_BOOTLOADER_LEVEL=3 CONFIG_FLASHMODE_DIO=y # CONFIG_FLASHMODE_DOUT is not set CONFIG_MONITOR_BAUD=115200 -# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set -# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set -# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set +CONFIG_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y # CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set # CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y diff --git a/Firmware/sdkconfig.old b/Firmware/sdkconfig.old index 9249b88..8f7159a 100644 --- a/Firmware/sdkconfig.old +++ b/Firmware/sdkconfig.old @@ -599,13 +599,13 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 # # Partition Table # -CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set # CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set # CONFIG_PARTITION_TABLE_TWO_OTA is not set # CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set -# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" -CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y # end of Partition Table @@ -613,9 +613,9 @@ CONFIG_PARTITION_TABLE_MD5=y # # Compiler options # -CONFIG_COMPILER_OPTIMIZATION_DEBUG=y +# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set # CONFIG_COMPILER_OPTIMIZATION_SIZE is not set -# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +CONFIG_COMPILER_OPTIMIZATION_PERF=y # CONFIG_COMPILER_OPTIMIZATION_NONE is not set CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set @@ -644,7 +644,7 @@ CONFIG_COMPILER_RT_LIB_GCCLIB=y CONFIG_COMPILER_RT_LIB_NAME="gcc" CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y # CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set -CONFIG_COMPILER_STATIC_ANALYZER=y +# CONFIG_COMPILER_STATIC_ANALYZER is not set # end of Compiler options # @@ -695,7 +695,7 @@ CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y CONFIG_BT_NIMBLE_ROLE_OBSERVER=y CONFIG_BT_NIMBLE_GATT_CLIENT=y CONFIG_BT_NIMBLE_GATT_SERVER=y -# CONFIG_BT_NIMBLE_NVS_PERSIST is not set +CONFIG_BT_NIMBLE_NVS_PERSIST=y # CONFIG_BT_NIMBLE_SMP_ID_RESET is not set CONFIG_BT_NIMBLE_SECURITY_ENABLE=y CONFIG_BT_NIMBLE_SM_LEGACY=y @@ -1699,9 +1699,13 @@ CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 -# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y # CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set -# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID is not set +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U32=y +# CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64 is not set CONFIG_FREERTOS_USE_TICKLESS_IDLE=y CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 # CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set @@ -1710,7 +1714,6 @@ CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 # # Port # -CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y # CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y # CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set @@ -1722,6 +1725,7 @@ CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y # CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y +CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y # CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set # CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set # end of Port @@ -2410,6 +2414,211 @@ CONFIG_WL_SECTOR_SIZE=4096 CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 CONFIG_WIFI_PROV_BLE_SEC_CONN=y + +# +# LittleFS +# +# CONFIG_LITTLEFS_SDMMC_SUPPORT is not set +CONFIG_LITTLEFS_MAX_PARTITIONS=3 +CONFIG_LITTLEFS_PAGE_SIZE=256 +CONFIG_LITTLEFS_OBJ_NAME_LEN=64 +CONFIG_LITTLEFS_READ_SIZE=128 +CONFIG_LITTLEFS_WRITE_SIZE=128 +CONFIG_LITTLEFS_LOOKAHEAD_SIZE=128 +CONFIG_LITTLEFS_CACHE_SIZE=512 +CONFIG_LITTLEFS_BLOCK_CYCLES=512 +CONFIG_LITTLEFS_USE_MTIME=y +# CONFIG_LITTLEFS_USE_ONLY_HASH is not set +# CONFIG_LITTLEFS_HUMAN_READABLE is not set +CONFIG_LITTLEFS_MTIME_USE_SECONDS=y +# CONFIG_LITTLEFS_MTIME_USE_NONCE is not set +# CONFIG_LITTLEFS_SPIFFS_COMPAT is not set +# CONFIG_LITTLEFS_FLUSH_FILE_EVERY_WRITE is not set +# CONFIG_LITTLEFS_FCNTL_GET_PATH is not set +# CONFIG_LITTLEFS_MULTIVERSION is not set +# CONFIG_LITTLEFS_MALLOC_STRATEGY_DISABLE is not set +CONFIG_LITTLEFS_MALLOC_STRATEGY_DEFAULT=y +# CONFIG_LITTLEFS_MALLOC_STRATEGY_INTERNAL is not set +CONFIG_LITTLEFS_ASSERTS=y +# CONFIG_LITTLEFS_MMAP_PARTITION is not set +# end of LittleFS # end of Component config # CONFIG_IDF_EXPERIMENTAL_FEATURES is not set + +# Deprecated options for backward compatibility +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +CONFIG_MONITOR_BAUD=115200 +# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set +# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set +# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_CXX_EXCEPTIONS is not set +# CONFIG_STACK_CHECK_NONE is not set +# CONFIG_STACK_CHECK_NORM is not set +CONFIG_STACK_CHECK_STRONG=y +# CONFIG_STACK_CHECK_ALL is not set +CONFIG_STACK_CHECK=y +CONFIG_WARN_WRITE_STRINGS=y +# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y +# CONFIG_BLUEDROID_ENABLED is not set +CONFIG_NIMBLE_ENABLED=y +CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y +# CONFIG_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set +CONFIG_NIMBLE_MAX_CONNECTIONS=3 +CONFIG_NIMBLE_MAX_BONDS=3 +CONFIG_NIMBLE_MAX_CCCDS=8 +CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0 +CONFIG_NIMBLE_PINNED_TO_CORE=0 +CONFIG_NIMBLE_TASK_STACK_SIZE=4096 +CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096 +CONFIG_NIMBLE_ROLE_CENTRAL=y +CONFIG_NIMBLE_ROLE_PERIPHERAL=y +CONFIG_NIMBLE_ROLE_BROADCASTER=y +CONFIG_NIMBLE_ROLE_OBSERVER=y +CONFIG_NIMBLE_NVS_PERSIST=y +CONFIG_NIMBLE_SM_LEGACY=y +CONFIG_NIMBLE_SM_SC=y +# CONFIG_NIMBLE_SM_SC_DEBUG_KEYS is not set +CONFIG_BT_NIMBLE_SM_SC_LVL=0 +# CONFIG_NIMBLE_DEBUG is not set +CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" +CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 +CONFIG_NIMBLE_ATT_PREFERRED_MTU=256 +CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0 +CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=24 +CONFIG_BT_NIMBLE_ACL_BUF_COUNT=24 +CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255 +CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 +CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30 +CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8 +CONFIG_NIMBLE_RPA_TIMEOUT=900 +# CONFIG_NIMBLE_MESH is not set +CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y +# CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN is not set +CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y +CONFIG_SW_COEXIST_ENABLE=y +CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y +CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y +# CONFIG_ANA_CMPR_ISR_IRAM_SAFE is not set +# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set +# CONFIG_MCPWM_ISR_IRAM_SAFE is not set +# CONFIG_EVENT_LOOP_PROFILING is not set +CONFIG_POST_EVENTS_FROM_ISR=y +CONFIG_POST_EVENTS_FROM_IRAM_ISR=y +CONFIG_GDBSTUB_SUPPORT_TASKS=y +CONFIG_GDBSTUB_MAX_TASKS=32 +# CONFIG_OTA_ALLOW_HTTP is not set +CONFIG_ESP_SYSTEM_PD_FLASH=y +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_BROWNOUT_DET=y +# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +CONFIG_BROWNOUT_DET_LVL_SEL_3=y +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_0 is not set +CONFIG_BROWNOUT_DET_LVL=3 +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP32_PHY_MAX_TX_POWER=20 +# CONFIG_REDUCE_PHY_TX_POWER is not set +# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set +CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y +CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES=0 +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_CONSOLE_UART_NONE is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_TASK_WDT=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set +# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_L2_TO_L3_COPY is not set +CONFIG_ESP_GRATUITOUS_ARP=y +CONFIG_GARP_TMR_INTERVAL=60 +CONFIG_TCPIP_RECVMBOX_SIZE=32 +CONFIG_TCP_MAXRTX=12 +CONFIG_TCP_SYNMAXRTX=12 +CONFIG_TCP_MSS=1440 +CONFIG_TCP_MSL=60000 +CONFIG_TCP_SND_BUF_DEFAULT=5760 +CONFIG_TCP_WND_DEFAULT=5760 +CONFIG_TCP_RECVMBOX_SIZE=6 +CONFIG_TCP_QUEUE_OOSEQ=y +CONFIG_TCP_OVERSIZE_MSS=y +# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_TCP_OVERSIZE_DISABLE is not set +CONFIG_UDP_RECVMBOX_SIZE=6 +CONFIG_TCPIP_TASK_STACK_SIZE=3072 +CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y +# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set +CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF +# CONFIG_PPP_SUPPORT is not set +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set +CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +# CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y +CONFIG_SUPPORT_TERMIOS=y +CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +# End of deprecated options