mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
Compare commits
4 Commits
1ee132898b
...
9b5521fc28
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b5521fc28 | |||
| 278e822600 | |||
| 844cf86d8d | |||
| f8735d4bce |
@@ -4,7 +4,6 @@ idf_component_register(
|
|||||||
"src/buttons.cpp"
|
"src/buttons.cpp"
|
||||||
"src/buzzer.cpp"
|
"src/buzzer.cpp"
|
||||||
"src/esp_backend.cpp"
|
"src/esp_backend.cpp"
|
||||||
"src/event_bus.cpp"
|
|
||||||
"src/display.cpp"
|
"src/display.cpp"
|
||||||
"src/fs_helper.cpp"
|
"src/fs_helper.cpp"
|
||||||
"src/i2c_global.cpp"
|
"src/i2c_global.cpp"
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
#ifndef BUTTONS_HPP
|
#ifndef BUTTONS_HPP
|
||||||
#define BUTTONS_HPP
|
#define BUTTONS_HPP
|
||||||
|
|
||||||
#include "cardboy/sdk/event_bus.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "cardboy/sdk/input_state.hpp"
|
||||||
|
#include "cardboy/sdk/services.hpp"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
@@ -24,20 +25,19 @@ typedef enum {
|
|||||||
|
|
||||||
class Buttons {
|
class Buttons {
|
||||||
public:
|
public:
|
||||||
static Buttons& get();
|
static Buttons& get();
|
||||||
void pooler(); // FIXME:
|
void pooler(); // FIXME:
|
||||||
uint8_t get_pressed();
|
uint8_t get_pressed();
|
||||||
void install_isr();
|
cardboy::sdk::InputState get_state();
|
||||||
void register_listener(TaskHandle_t task);
|
void install_isr();
|
||||||
void setEventBus(cardboy::sdk::IEventBus* bus);
|
void setEventBus(cardboy::sdk::IEventBus* bus);
|
||||||
|
|
||||||
TaskHandle_t _pooler_task;
|
TaskHandle_t _pooler_task;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Buttons();
|
Buttons();
|
||||||
|
uint8_t _previous;
|
||||||
volatile uint8_t _current;
|
volatile uint8_t _current;
|
||||||
volatile TaskHandle_t _listener = nullptr;
|
|
||||||
cardboy::sdk::IEventBus* _eventBus = nullptr;
|
cardboy::sdk::IEventBus* _eventBus = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cardboy/sdk/event_bus.hpp>
|
|
||||||
|
|
||||||
#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
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <cardboy/sdk/display_spec.hpp>
|
#include <cardboy/sdk/display_spec.hpp>
|
||||||
#include "cardboy/backend/esp/display.hpp"
|
#include "cardboy/backend/esp/display.hpp"
|
||||||
#include "cardboy/backend/esp/event_bus.hpp"
|
|
||||||
#include "cardboy/sdk/platform.hpp"
|
#include "cardboy/sdk/platform.hpp"
|
||||||
#include "cardboy/sdk/services.hpp"
|
#include "cardboy/sdk/services.hpp"
|
||||||
|
|
||||||
@@ -63,19 +62,22 @@ private:
|
|||||||
class LoopHooksService;
|
class LoopHooksService;
|
||||||
class NotificationService;
|
class NotificationService;
|
||||||
class TimerService;
|
class TimerService;
|
||||||
|
class EventBus;
|
||||||
|
class AppScopedServices;
|
||||||
|
class AppServiceProvider;
|
||||||
|
|
||||||
std::unique_ptr<BuzzerService> buzzerService;
|
std::unique_ptr<BuzzerService> _buzzerService;
|
||||||
std::unique_ptr<BatteryService> batteryService;
|
std::unique_ptr<BatteryService> _batteryService;
|
||||||
std::unique_ptr<StorageService> storageService;
|
std::unique_ptr<StorageService> _storageService;
|
||||||
std::unique_ptr<RandomService> randomService;
|
std::unique_ptr<RandomService> _randomService;
|
||||||
std::unique_ptr<HighResClockService> highResClockService;
|
std::unique_ptr<HighResClockService> _highResClockService;
|
||||||
std::unique_ptr<FilesystemService> filesystemService;
|
std::unique_ptr<FilesystemService> _filesystemService;
|
||||||
std::unique_ptr<EventBus> eventBus;
|
std::unique_ptr<EventBus> _eventBus;
|
||||||
std::unique_ptr<TimerService> timerService;
|
std::unique_ptr<LoopHooksService> _loopHooksService;
|
||||||
std::unique_ptr<LoopHooksService> loopHooksService;
|
std::unique_ptr<NotificationService> _notificationService;
|
||||||
std::unique_ptr<NotificationService> notificationService;
|
std::unique_ptr<AppServiceProvider> _appServiceProvider;
|
||||||
|
|
||||||
cardboy::sdk::Services services{};
|
cardboy::sdk::Services _services{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Backend {
|
struct Backend {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
#include "cardboy/backend/esp/config.hpp"
|
#include "cardboy/backend/esp/config.hpp"
|
||||||
#include "cardboy/backend/esp/i2c_global.hpp"
|
#include "cardboy/backend/esp/i2c_global.hpp"
|
||||||
#include "cardboy/sdk/event_bus.hpp"
|
|
||||||
|
|
||||||
static i2c_master_dev_handle_t dev_handle;
|
static i2c_master_dev_handle_t dev_handle;
|
||||||
static inline i2c_device_config_t dev_cfg = {
|
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() {
|
void Buttons::pooler() {
|
||||||
while (true) {
|
while (true) {
|
||||||
BaseType_t xResult = xTaskNotifyWait(pdFALSE, ULONG_MAX, nullptr, portMAX_DELAY);
|
BaseType_t xResult = xTaskNotifyWait(pdFALSE, ULONG_MAX, nullptr, portMAX_DELAY);
|
||||||
@@ -88,15 +109,27 @@ void Buttons::pooler() {
|
|||||||
reg = 1;
|
reg = 1;
|
||||||
ESP_ERROR_CHECK(
|
ESP_ERROR_CHECK(
|
||||||
i2c_master_transmit_receive(dev_handle, ®, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 1, -1));
|
i2c_master_transmit_receive(dev_handle, ®, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 1, -1));
|
||||||
if (_listener)
|
|
||||||
xTaskNotifyGive(_listener);
|
|
||||||
if (_eventBus)
|
if (_eventBus) {
|
||||||
_eventBus->signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Input));
|
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<std::uint32_t>((static_cast<std::uint64_t>(ticks) * 1000ULL) / configTICK_RATE_HZ);
|
||||||
|
|
||||||
|
evt.timestamp_ms = now;
|
||||||
|
evt.data = button;
|
||||||
|
_eventBus->post(evt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint8_t Buttons::get_pressed() { return _current; }
|
uint8_t Buttons::get_pressed() { return _current; }
|
||||||
void Buttons::install_isr() { gpio_isr_handler_add(EXP_INT, wakeup, nullptr); }
|
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::register_listener(TaskHandle_t task) { _listener = task; }
|
void Buttons::setEventBus(cardboy::sdk::IEventBus* bus) { _eventBus = bus; }
|
||||||
|
|
||||||
void Buttons::setEventBus(cardboy::sdk::IEventBus* bus) { _eventBus = bus; }
|
|
||||||
|
|||||||
@@ -18,15 +18,17 @@
|
|||||||
#include "esp_random.h"
|
#include "esp_random.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
#include "freertos/timers.h"
|
#include "freertos/timers.h"
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <deque>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -134,61 +136,99 @@ public:
|
|||||||
void onLoopIteration() override { vTaskDelay(1); }
|
void onLoopIteration() override { vTaskDelay(1); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class EspRuntime::TimerService final : public cardboy::sdk::ITimerService {
|
class EspRuntime::EventBus final : public cardboy::sdk::IEventBus {
|
||||||
public:
|
public:
|
||||||
explicit TimerService(cardboy::sdk::IEventBus& bus);
|
explicit EventBus() {
|
||||||
~TimerService() override;
|
_queueHandle =
|
||||||
|
xQueueCreateStatic(_kMaxQueueSize, sizeof(cardboy::sdk::AppEvent), _queueStorage.data(), &_queue);
|
||||||
|
}
|
||||||
|
|
||||||
cardboy::sdk::TimerClientId acquireClient() override;
|
~EventBus() override { vQueueDelete(_queueHandle); }
|
||||||
void releaseClient(cardboy::sdk::TimerClientId clientId) override;
|
void post(const sdk::AppEvent& event) override { xQueueSendToBack(_queueHandle, &event, portMAX_DELAY); }
|
||||||
cardboy::sdk::AppTimerHandle scheduleTimer(cardboy::sdk::TimerClientId clientId, std::uint32_t delay_ms,
|
std::optional<sdk::AppEvent> pop(std::optional<std::uint32_t> timeout_ms = std::nullopt) override {
|
||||||
bool repeat) override;
|
sdk::AppEvent out;
|
||||||
void cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) override;
|
TickType_t ticks = timeout_ms ? pdMS_TO_TICKS(*timeout_ms) : portMAX_DELAY;
|
||||||
void cancelAllTimers(cardboy::sdk::TimerClientId clientId) override;
|
if (xQueueReceive(_queueHandle, &out, ticks) == pdTRUE) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct TimerRecord {
|
static constexpr std::uint32_t _kMaxQueueSize = 32;
|
||||||
TimerService* owner = nullptr;
|
StaticQueue_t _queue;
|
||||||
TimerHandle_t timer = nullptr;
|
std::array<std::uint8_t, 32 * sizeof(cardboy::sdk::AppEvent)> _queueStorage{};
|
||||||
cardboy::sdk::TimerClientId clientId = cardboy::sdk::kInvalidTimerClient;
|
QueueHandle_t _queueHandle;
|
||||||
cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer;
|
};
|
||||||
bool repeat = false;
|
|
||||||
bool active = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RecursiveLock {
|
|
||||||
public:
|
|
||||||
explicit RecursiveLock(SemaphoreHandle_t m) : mutex(m) { lock(); }
|
|
||||||
~RecursiveLock() { unlock(); }
|
|
||||||
|
|
||||||
void lock() {
|
class EspRuntime::TimerService final : public cardboy::sdk::ITimerService {
|
||||||
if (mutex && !locked) {
|
public:
|
||||||
if (xSemaphoreTakeRecursive(mutex, portMAX_DELAY) == pdTRUE)
|
explicit TimerService(cardboy::sdk::IEventBus& appBus);
|
||||||
locked = true;
|
~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() {
|
SemaphoreHandle_t operator*() { return _handle; }
|
||||||
if (mutex && locked) {
|
SemaphoreHandle_t operator->() { return _handle; }
|
||||||
xSemaphoreGiveRecursive(mutex);
|
|
||||||
locked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SemaphoreHandle_t mutex;
|
SemaphoreHandle_t _handle;
|
||||||
bool locked = false;
|
};
|
||||||
|
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);
|
static void timerCallback(TimerHandle_t timer);
|
||||||
void handleTimer(TimerRecord* record);
|
void handleTimer(sdk::AppTimerHandle record);
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
cardboy::sdk::IEventBus& eventBus;
|
cardboy::sdk::IEventBus& _appEventBus;
|
||||||
SemaphoreHandle_t mutex = nullptr;
|
SemaphoreHandle_t _mutex;
|
||||||
std::list<TimerRecord> timers;
|
StaticSemaphore_t _mutexStatic;
|
||||||
cardboy::sdk::AppTimerHandle nextTimerHandle = 1;
|
std::list<TimerRecord> _timers;
|
||||||
cardboy::sdk::TimerClientId nextClientId = 1;
|
std::atomic<sdk::AppTimerHandle> _nextTimerHandle = 1;
|
||||||
|
static_assert(std::atomic<sdk::AppTimerHandle>::is_always_lock_free);
|
||||||
|
};
|
||||||
|
|
||||||
|
class EspRuntime::AppScopedServices final : public cardboy::sdk::AppScopedServices {
|
||||||
|
public:
|
||||||
|
AppScopedServices(std::unique_ptr<TimerService> timer) : _ownedTimer(std::move(timer)) {
|
||||||
|
this->timer = _ownedTimer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<TimerService> _ownedTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EspRuntime::AppServiceProvider final : public cardboy::sdk::IAppServiceProvider {
|
||||||
|
public:
|
||||||
|
explicit AppServiceProvider(cardboy::sdk::IEventBus& bus) : eventBus(bus) {}
|
||||||
|
|
||||||
|
[[nodiscard]] std::unique_ptr<cardboy::sdk::AppScopedServices>
|
||||||
|
createScopedServices(std::uint64_t generation) override {
|
||||||
|
(void) generation;
|
||||||
|
auto timer = std::make_unique<TimerService>(eventBus);
|
||||||
|
return std::make_unique<AppScopedServices>(std::move(timer));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
cardboy::sdk::IEventBus& eventBus;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EspRuntime::NotificationService final : public cardboy::sdk::INotificationCenter {
|
class EspRuntime::NotificationService final : public cardboy::sdk::INotificationCenter {
|
||||||
@@ -262,8 +302,8 @@ public:
|
|||||||
bool removed = false;
|
bool removed = false;
|
||||||
for (auto it = entries.begin(); it != entries.end();) {
|
for (auto it = entries.begin(); it != entries.end();) {
|
||||||
if (it->id == id) {
|
if (it->id == id) {
|
||||||
it = entries.erase(it);
|
it = entries.erase(it);
|
||||||
removed = true;
|
removed = true;
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
@@ -279,8 +319,8 @@ public:
|
|||||||
bool removed = false;
|
bool removed = false;
|
||||||
for (auto it = entries.begin(); it != entries.end();) {
|
for (auto it = entries.begin(); it != entries.end();) {
|
||||||
if (it->externalId == externalId) {
|
if (it->externalId == externalId) {
|
||||||
it = entries.erase(it);
|
it = entries.erase(it);
|
||||||
removed = true;
|
removed = true;
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
@@ -307,99 +347,70 @@ private:
|
|||||||
std::uint32_t revisionCounter = 0;
|
std::uint32_t revisionCounter = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
EspRuntime::TimerService::TimerService(cardboy::sdk::IEventBus& bus) : eventBus(bus) {
|
EspRuntime::TimerService::TimerService(cardboy::sdk::IEventBus& appBus) : _appEventBus(appBus) {
|
||||||
mutex = xSemaphoreCreateRecursiveMutex();
|
xSemaphoreTake(*_currentSemaphore, portMAX_DELAY);
|
||||||
|
assert(_current == nullptr);
|
||||||
|
_mutex = xSemaphoreCreateBinaryStatic(&_mutexStatic);
|
||||||
|
assert(_mutex);
|
||||||
|
xSemaphoreGive(_mutex);
|
||||||
|
_current = this;
|
||||||
|
xSemaphoreGive(*_currentSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
EspRuntime::TimerService::~TimerService() {
|
EspRuntime::TimerService::~TimerService() {
|
||||||
shutdown();
|
xSemaphoreTake(*_currentSemaphore, portMAX_DELAY);
|
||||||
if (mutex)
|
assert(_current == this);
|
||||||
vSemaphoreDelete(mutex);
|
_current = nullptr;
|
||||||
|
cancelAllTimers();
|
||||||
|
vSemaphoreDelete(_mutex);
|
||||||
|
xSemaphoreGive(*_currentSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
cardboy::sdk::TimerClientId EspRuntime::TimerService::acquireClient() {
|
cardboy::sdk::AppTimerHandle EspRuntime::TimerService::scheduleTimer(std::uint32_t delay_ms, bool repeat) {
|
||||||
if (!mutex)
|
TimerRecord record{};
|
||||||
return cardboy::sdk::kInvalidTimerClient;
|
record.owner = this;
|
||||||
RecursiveLock lock(mutex);
|
record.repeat = repeat;
|
||||||
cardboy::sdk::TimerClientId id = cardboy::sdk::kInvalidTimerClient;
|
|
||||||
|
cardboy::sdk::AppTimerHandle newHandle = cardboy::sdk::kInvalidAppTimer;
|
||||||
do {
|
do {
|
||||||
id = nextClientId++;
|
newHandle = _nextTimerHandle++;
|
||||||
} while (id == cardboy::sdk::kInvalidTimerClient);
|
} while (newHandle == cardboy::sdk::kInvalidAppTimer);
|
||||||
if (nextClientId == cardboy::sdk::kInvalidTimerClient)
|
if (_nextTimerHandle == cardboy::sdk::kInvalidAppTimer)
|
||||||
++nextClientId;
|
++_nextTimerHandle;
|
||||||
return id;
|
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<TickType_t>(pdMS_TO_TICKS(delay_ms), 1);
|
||||||
|
|
||||||
cardboy::sdk::AppTimerHandle EspRuntime::TimerService::scheduleTimer(cardboy::sdk::TimerClientId clientId,
|
static_assert(sizeof(void*) >= sizeof(cardboy::sdk::AppTimerHandle));
|
||||||
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<std::uint32_t>(1, delay_ms);
|
|
||||||
const TickType_t ticks = std::max<TickType_t>(pdMS_TO_TICKS(effectiveDelay), 1);
|
|
||||||
|
|
||||||
TimerHandle_t timerHandle =
|
TimerHandle_t timerHandle =
|
||||||
xTimerCreate("AppSvcTimer", ticks, repeat ? pdTRUE : pdFALSE, storedRecord, &TimerService::timerCallback);
|
xTimerCreate("AppSvcTimer", ticks, repeat ? pdTRUE : pdFALSE, reinterpret_cast<void*>(storedRecord->handle),
|
||||||
if (!timerHandle) {
|
&TimerService::timerCallback);
|
||||||
RecursiveLock lock(mutex);
|
storedRecord->timer = timerHandle;
|
||||||
for (auto it = timers.begin(); it != timers.end(); ++it) {
|
|
||||||
if (&(*it) == storedRecord) {
|
if (xTimerStart(timerHandle, portMAX_DELAY) != pdPASS) {
|
||||||
timers.erase(it);
|
assert(false);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cardboy::sdk::kInvalidAppTimer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
return newHandle;
|
||||||
RecursiveLock lock(mutex);
|
|
||||||
storedRecord->timer = timerHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xTimerStart(timerHandle, 0) != pdPASS) {
|
|
||||||
cancelTimer(clientId, storedRecord->handle);
|
|
||||||
return cardboy::sdk::kInvalidAppTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return storedRecord->handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EspRuntime::TimerService::cancelTimer(cardboy::sdk::TimerClientId clientId,
|
void EspRuntime::TimerService::cancelTimer(cardboy::sdk::AppTimerHandle handle) {
|
||||||
cardboy::sdk::AppTimerHandle handle) {
|
assert(handle != sdk::kInvalidAppTimer);
|
||||||
if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient || handle == cardboy::sdk::kInvalidAppTimer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TimerHandle_t timerHandle = nullptr;
|
TimerHandle_t timerHandle = nullptr;
|
||||||
{
|
{
|
||||||
RecursiveLock lock(mutex);
|
xSemaphoreTake(_mutex, portMAX_DELAY);
|
||||||
for (auto& record: timers) {
|
for (auto it = _timers.begin(); it != _timers.end(); ++it) {
|
||||||
if (record.clientId == clientId && record.handle == handle) {
|
if (it->handle == handle) {
|
||||||
record.active = false;
|
timerHandle = it->timer;
|
||||||
timerHandle = record.timer;
|
it = _timers.erase(it);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
xSemaphoreGive(_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!timerHandle)
|
if (!timerHandle)
|
||||||
@@ -407,143 +418,106 @@ void EspRuntime::TimerService::cancelTimer(cardboy::sdk::TimerClientId clientId,
|
|||||||
|
|
||||||
xTimerStop(timerHandle, portMAX_DELAY);
|
xTimerStop(timerHandle, portMAX_DELAY);
|
||||||
xTimerDelete(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) {
|
void EspRuntime::TimerService::cancelAllTimers() {
|
||||||
if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient)
|
if (!_mutex)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<TimerHandle_t> handles;
|
std::vector<TimerHandle_t> handles;
|
||||||
|
handles.resize(_timers.size());
|
||||||
{
|
{
|
||||||
RecursiveLock lock(mutex);
|
xSemaphoreTake(_mutex, portMAX_DELAY);
|
||||||
for (auto& record: timers) {
|
size_t i = 0;
|
||||||
if (record.clientId == clientId) {
|
for (auto& record: _timers) {
|
||||||
record.active = false;
|
if (record.timer) {
|
||||||
if (record.timer)
|
assert(record.timer);
|
||||||
handles.push_back(record.timer);
|
handles[i] = record.timer;
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
_timers.clear();
|
||||||
|
xSemaphoreGive(_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto timerHandle: handles) {
|
for (auto timerHandle: handles) {
|
||||||
xTimerStop(timerHandle, portMAX_DELAY);
|
xTimerStop(timerHandle, portMAX_DELAY);
|
||||||
xTimerDelete(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) {
|
void EspRuntime::TimerService::timerCallback(TimerHandle_t timer) {
|
||||||
auto* record = static_cast<TimerRecord*>(pvTimerGetTimerID(timer));
|
auto handle = reinterpret_cast<sdk::AppTimerHandle>(pvTimerGetTimerID(timer));
|
||||||
if (!record || !record->owner)
|
xSemaphoreTake(*_currentSemaphore, portMAX_DELAY);
|
||||||
|
if (!_current)
|
||||||
return;
|
return;
|
||||||
record->owner->handleTimer(record);
|
_current->handleTimer(handle);
|
||||||
|
xSemaphoreGive(*_currentSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EspRuntime::TimerService::handleTimer(TimerRecord* record) {
|
void EspRuntime::TimerService::handleTimer(sdk::AppTimerHandle handle) {
|
||||||
if (!record || !record->active)
|
TimerHandle_t timerHandle = nullptr;
|
||||||
return;
|
bool repeat = false;
|
||||||
|
|
||||||
const cardboy::sdk::TimerClientId clientId = record->clientId;
|
{
|
||||||
const cardboy::sdk::AppTimerHandle handle = record->handle;
|
xSemaphoreTake(_mutex, portMAX_DELAY);
|
||||||
const bool isRepeating = record->repeat;
|
for (auto it = _timers.begin(); it != _timers.end(); ++it) {
|
||||||
TimerHandle_t timerHandle = record->timer;
|
if (it->handle == handle) {
|
||||||
|
timerHandle = it->timer;
|
||||||
if (!isRepeating) {
|
if (!it->repeat) {
|
||||||
record->active = false;
|
_timers.erase(it);
|
||||||
{
|
} else {
|
||||||
RecursiveLock lock(mutex);
|
repeat = true;
|
||||||
for (auto it = timers.begin(); it != timers.end();) {
|
}
|
||||||
if (&(*it) == record)
|
break;
|
||||||
it = timers.erase(it);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (timerHandle)
|
xSemaphoreGive(_mutex);
|
||||||
xTimerDelete(timerHandle, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!timerHandle) {
|
||||||
|
printf("Couldn't find handle for timer %lu\n", handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!repeat && timerHandle)
|
||||||
|
xTimerDelete(timerHandle, portMAX_DELAY);
|
||||||
|
|
||||||
cardboy::sdk::AppTimerEvent timerEvent{};
|
cardboy::sdk::AppTimerEvent timerEvent{};
|
||||||
timerEvent.clientId = clientId;
|
timerEvent.handle = handle;
|
||||||
timerEvent.handle = handle;
|
|
||||||
|
|
||||||
cardboy::sdk::AppEvent event{};
|
cardboy::sdk::AppEvent event{};
|
||||||
event.timestamp_ms = static_cast<std::uint32_t>(esp_timer_get_time() / 1000ULL);
|
event.timestamp_ms = static_cast<std::uint32_t>(esp_timer_get_time() / 1000ULL);
|
||||||
event.data = timerEvent;
|
event.data = timerEvent;
|
||||||
eventBus.postEvent(event);
|
_appEventBus.post(event);
|
||||||
}
|
|
||||||
|
|
||||||
void EspRuntime::TimerService::shutdown() {
|
|
||||||
if (!mutex)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<TimerHandle_t> 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EspRuntime::EspRuntime() : framebuffer(), input(), clock() {
|
EspRuntime::EspRuntime() : framebuffer(), input(), clock() {
|
||||||
initializeHardware();
|
initializeHardware();
|
||||||
|
|
||||||
buzzerService = std::make_unique<BuzzerService>();
|
_buzzerService = std::make_unique<BuzzerService>();
|
||||||
batteryService = std::make_unique<BatteryService>();
|
_batteryService = std::make_unique<BatteryService>();
|
||||||
storageService = std::make_unique<StorageService>();
|
_storageService = std::make_unique<StorageService>();
|
||||||
randomService = std::make_unique<RandomService>();
|
_randomService = std::make_unique<RandomService>();
|
||||||
highResClockService = std::make_unique<HighResClockService>();
|
_highResClockService = std::make_unique<HighResClockService>();
|
||||||
filesystemService = std::make_unique<FilesystemService>();
|
_filesystemService = std::make_unique<FilesystemService>();
|
||||||
eventBus = std::make_unique<EventBus>();
|
_eventBus = std::make_unique<EventBus>();
|
||||||
timerService = std::make_unique<TimerService>(*eventBus);
|
_appServiceProvider = std::make_unique<AppServiceProvider>(*_eventBus);
|
||||||
loopHooksService = std::make_unique<LoopHooksService>();
|
_loopHooksService = std::make_unique<LoopHooksService>();
|
||||||
notificationService = std::make_unique<NotificationService>();
|
_notificationService = std::make_unique<NotificationService>();
|
||||||
|
|
||||||
services.buzzer = buzzerService.get();
|
_services.buzzer = _buzzerService.get();
|
||||||
services.battery = batteryService.get();
|
_services.battery = _batteryService.get();
|
||||||
services.storage = storageService.get();
|
_services.storage = _storageService.get();
|
||||||
services.random = randomService.get();
|
_services.random = _randomService.get();
|
||||||
services.highResClock = highResClockService.get();
|
_services.highResClock = _highResClockService.get();
|
||||||
services.filesystem = filesystemService.get();
|
_services.filesystem = _filesystemService.get();
|
||||||
services.eventBus = eventBus.get();
|
_services.eventBus = _eventBus.get();
|
||||||
services.timer = timerService.get();
|
_services.appServices = _appServiceProvider.get();
|
||||||
services.loopHooks = loopHooksService.get();
|
_services.loopHooks = _loopHooksService.get();
|
||||||
services.notifications = notificationService.get();
|
_services.notifications = _notificationService.get();
|
||||||
|
|
||||||
Buttons::get().setEventBus(eventBus.get());
|
Buttons::get().setEventBus(_eventBus.get());
|
||||||
set_notification_center(notificationService.get());
|
set_notification_center(_notificationService.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
EspRuntime::~EspRuntime() {
|
EspRuntime::~EspRuntime() {
|
||||||
@@ -551,7 +525,7 @@ EspRuntime::~EspRuntime() {
|
|||||||
shutdown_time_sync_service();
|
shutdown_time_sync_service();
|
||||||
}
|
}
|
||||||
|
|
||||||
cardboy::sdk::Services& EspRuntime::serviceRegistry() { return services; }
|
cardboy::sdk::Services& EspRuntime::serviceRegistry() { return _services; }
|
||||||
|
|
||||||
void EspRuntime::initializeHardware() {
|
void EspRuntime::initializeHardware() {
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
@@ -595,27 +569,7 @@ void EspFramebuffer::sendFrame_impl(bool clearAfterSend) { SMD::send_frame(clear
|
|||||||
|
|
||||||
bool EspFramebuffer::frameInFlight_impl() const { return SMD::frame_transfer_in_flight(); }
|
bool EspFramebuffer::frameInFlight_impl() const { return SMD::frame_transfer_in_flight(); }
|
||||||
|
|
||||||
cardboy::sdk::InputState EspInput::readState_impl() {
|
cardboy::sdk::InputState EspInput::readState_impl() { return Buttons::get().get_state(); }
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t EspClock::millis_impl() {
|
std::uint32_t EspClock::millis_impl() {
|
||||||
TickType_t ticks = xTaskGetTickCount();
|
TickType_t ticks = xTaskGetTickCount();
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
#include "cardboy/backend/esp/event_bus.hpp"
|
|
||||||
|
|
||||||
#include "cardboy/sdk/event_bus.hpp"
|
|
||||||
|
|
||||||
#include "freertos/portmacro.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
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<std::uint32_t>(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
|
|
||||||
@@ -4,6 +4,11 @@ project(cardboy_sdk LANGUAGES CXX)
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||||
set(CMAKE_CXX_EXTENSIONS NO)
|
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)
|
add_subdirectory(utils)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace apps {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using cardboy::sdk::AppButtonEvent;
|
using cardboy::sdk::AppButtonEvent;
|
||||||
|
using cardboy::sdk::AppTimeoutEvent;
|
||||||
using cardboy::sdk::AppContext;
|
using cardboy::sdk::AppContext;
|
||||||
using cardboy::sdk::AppTimerEvent;
|
using cardboy::sdk::AppTimerEvent;
|
||||||
|
|
||||||
@@ -49,18 +50,20 @@ public:
|
|||||||
const auto snap = captureTime();
|
const auto snap = captureTime();
|
||||||
renderIfNeeded(snap);
|
renderIfNeeded(snap);
|
||||||
lastSnapshot = snap;
|
lastSnapshot = snap;
|
||||||
refreshTimer = context.scheduleRepeatingTimer(200);
|
if (auto* timer = context.timer())
|
||||||
|
refreshTimer = timer->scheduleTimer(200, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStop() override { cancelRefreshTimer(); }
|
void onStop() override { cancelRefreshTimer(); }
|
||||||
|
|
||||||
void handleEvent(const cardboy::sdk::AppEvent& event) override {
|
std::optional<std::uint32_t> handleEvent(const cardboy::sdk::AppEvent& event) override {
|
||||||
event.visit(cardboy::sdk::overload(
|
event.visit(cardboy::sdk::overload([this](const AppButtonEvent& button) { handleButtonEvent(button); },
|
||||||
[this](const AppButtonEvent& button) { handleButtonEvent(button); },
|
[this](const AppTimerEvent& timer) {
|
||||||
[this](const AppTimerEvent& timer) {
|
if (timer.handle == refreshTimer)
|
||||||
if (timer.handle == refreshTimer)
|
updateDisplay();
|
||||||
updateDisplay();
|
},
|
||||||
}));
|
[](const AppTimeoutEvent&) { /* ignore */ }));
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -75,10 +78,11 @@ private:
|
|||||||
TimeSnapshot lastSnapshot{};
|
TimeSnapshot lastSnapshot{};
|
||||||
|
|
||||||
void cancelRefreshTimer() {
|
void cancelRefreshTimer() {
|
||||||
if (refreshTimer != cardboy::sdk::kInvalidAppTimer) {
|
if (refreshTimer == cardboy::sdk::kInvalidAppTimer)
|
||||||
context.cancelTimer(refreshTimer);
|
return;
|
||||||
refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
if (auto* timer = context.timer())
|
||||||
}
|
timer->cancelTimer(refreshTimer);
|
||||||
|
refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleButtonEvent(const cardboy::sdk::AppButtonEvent& button) {
|
void handleButtonEvent(const cardboy::sdk::AppButtonEvent& button) {
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ class GameboyApp;
|
|||||||
using cardboy::sdk::AppButtonEvent;
|
using cardboy::sdk::AppButtonEvent;
|
||||||
using cardboy::sdk::AppContext;
|
using cardboy::sdk::AppContext;
|
||||||
using cardboy::sdk::AppEvent;
|
using cardboy::sdk::AppEvent;
|
||||||
|
using cardboy::sdk::AppTimeoutEvent;
|
||||||
using cardboy::sdk::AppTimerEvent;
|
using cardboy::sdk::AppTimerEvent;
|
||||||
using cardboy::sdk::AppTimerHandle;
|
using cardboy::sdk::AppTimerHandle;
|
||||||
using cardboy::sdk::InputState;
|
using cardboy::sdk::InputState;
|
||||||
@@ -224,7 +225,6 @@ public:
|
|||||||
::gAudioWriteThunk = &GameboyApp::audioWriteThunk;
|
::gAudioWriteThunk = &GameboyApp::audioWriteThunk;
|
||||||
apu.attach(this);
|
apu.attach(this);
|
||||||
apu.reset();
|
apu.reset();
|
||||||
cancelTick();
|
|
||||||
frameDelayCarryUs = 0;
|
frameDelayCarryUs = 0;
|
||||||
GB_PERF_ONLY(perf.resetAll();)
|
GB_PERF_ONLY(perf.resetAll();)
|
||||||
prevInput = context.input.readState();
|
prevInput = context.input.readState();
|
||||||
@@ -233,13 +233,12 @@ public:
|
|||||||
scaleMode = ScaleMode::FullHeightWide;
|
scaleMode = ScaleMode::FullHeightWide;
|
||||||
ensureFilesystemReady();
|
ensureFilesystemReady();
|
||||||
refreshRomList();
|
refreshRomList();
|
||||||
mode = Mode::Browse;
|
mode = Mode::Browse;
|
||||||
browserDirty = true;
|
browserDirty = true;
|
||||||
scheduleNextTick(0);
|
nextTimeoutMs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStop() override {
|
void onStop() override {
|
||||||
cancelTick();
|
|
||||||
frameDelayCarryUs = 0;
|
frameDelayCarryUs = 0;
|
||||||
GB_PERF_ONLY(perf.maybePrintAggregate(true);)
|
GB_PERF_ONLY(perf.maybePrintAggregate(true);)
|
||||||
unloadRom();
|
unloadRom();
|
||||||
@@ -252,14 +251,9 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleEvent(const AppEvent& event) override {
|
std::optional<std::uint32_t> handleEvent(const AppEvent& event) override {
|
||||||
bool handled = false;
|
|
||||||
event.visit(cardboy::sdk::overload(
|
event.visit(cardboy::sdk::overload(
|
||||||
[this, &handled](const AppTimerEvent& timer) {
|
[this](const AppTimeoutEvent&) {
|
||||||
if (timer.handle != tickTimer)
|
|
||||||
return;
|
|
||||||
handled = true;
|
|
||||||
tickTimer = kInvalidAppTimer;
|
|
||||||
const uint64_t frameStartUs = nowMicros();
|
const uint64_t frameStartUs = nowMicros();
|
||||||
performStep();
|
performStep();
|
||||||
const uint64_t frameEndUs = nowMicros();
|
const uint64_t frameEndUs = nowMicros();
|
||||||
@@ -269,10 +263,10 @@ public:
|
|||||||
},
|
},
|
||||||
[this](const AppButtonEvent&) {
|
[this](const AppButtonEvent&) {
|
||||||
frameDelayCarryUs = 0;
|
frameDelayCarryUs = 0;
|
||||||
scheduleNextTick(0);
|
nextTimeoutMs = 0;
|
||||||
}));
|
},
|
||||||
if (handled)
|
[](const AppTimerEvent&) { /* ignore */ }));
|
||||||
return;
|
return nextTimeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void performStep() {
|
void performStep() {
|
||||||
@@ -1119,9 +1113,9 @@ public:
|
|||||||
cardboy::sdk::IFilesystem* filesystem = nullptr;
|
cardboy::sdk::IFilesystem* filesystem = nullptr;
|
||||||
cardboy::sdk::IHighResClock* highResClock = nullptr;
|
cardboy::sdk::IHighResClock* highResClock = nullptr;
|
||||||
PerfTracker perf{};
|
PerfTracker perf{};
|
||||||
AppTimerHandle tickTimer = kInvalidAppTimer;
|
|
||||||
int64_t frameDelayCarryUs = 0;
|
int64_t frameDelayCarryUs = 0;
|
||||||
static constexpr uint32_t kTargetFrameUs = 1000000 / 60; // ~16.6 ms
|
std::optional<std::uint32_t> nextTimeoutMs;
|
||||||
|
static constexpr uint32_t kTargetFrameUs = 1000000 / 60; // ~16.6 ms
|
||||||
|
|
||||||
Mode mode = Mode::Browse;
|
Mode mode = Mode::Browse;
|
||||||
ScaleMode scaleMode = ScaleMode::FullHeightWide;
|
ScaleMode scaleMode = ScaleMode::FullHeightWide;
|
||||||
@@ -1155,18 +1149,6 @@ public:
|
|||||||
uint8_t lastLoud = 0;
|
uint8_t lastLoud = 0;
|
||||||
uint32_t stableFrames = 0;
|
uint32_t stableFrames = 0;
|
||||||
|
|
||||||
void cancelTick() {
|
|
||||||
if (tickTimer != kInvalidAppTimer) {
|
|
||||||
context.cancelTimer(tickTimer);
|
|
||||||
tickTimer = kInvalidAppTimer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduleNextTick(uint32_t delayMs) {
|
|
||||||
cancelTick();
|
|
||||||
tickTimer = context.scheduleTimer(delayMs, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t idleDelayMs() const { return browserDirty ? 50 : 140; }
|
uint32_t idleDelayMs() const { return browserDirty ? 50 : 140; }
|
||||||
|
|
||||||
void scheduleAfterFrame(uint64_t elapsedUs) {
|
void scheduleAfterFrame(uint64_t elapsedUs) {
|
||||||
@@ -1175,17 +1157,17 @@ public:
|
|||||||
desiredUs += frameDelayCarryUs;
|
desiredUs += frameDelayCarryUs;
|
||||||
if (desiredUs <= 0) {
|
if (desiredUs <= 0) {
|
||||||
frameDelayCarryUs = desiredUs;
|
frameDelayCarryUs = desiredUs;
|
||||||
scheduleNextTick(0);
|
nextTimeoutMs = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
frameDelayCarryUs = desiredUs % 1000;
|
frameDelayCarryUs = desiredUs % 1000;
|
||||||
desiredUs -= frameDelayCarryUs;
|
desiredUs -= frameDelayCarryUs;
|
||||||
uint32_t delayMs = static_cast<uint32_t>(desiredUs / 1000);
|
uint32_t delayMs = static_cast<uint32_t>(desiredUs / 1000);
|
||||||
scheduleNextTick(delayMs);
|
nextTimeoutMs = delayMs;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
frameDelayCarryUs = 0;
|
frameDelayCarryUs = 0;
|
||||||
scheduleNextTick(idleDelayMs());
|
nextTimeoutMs = idleDelayMs();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ensureFilesystemReady() {
|
bool ensureFilesystemReady() {
|
||||||
@@ -1836,7 +1818,7 @@ public:
|
|||||||
promptDirty = true;
|
promptDirty = true;
|
||||||
mode = Mode::Prompt;
|
mode = Mode::Prompt;
|
||||||
gb.direct.joypad = 0xFF;
|
gb.direct.joypad = 0xFF;
|
||||||
scheduleNextTick(0);
|
// scheduleNextTick(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void exitPrompt(Mode nextMode) {
|
void exitPrompt(Mode nextMode) {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
#include "cardboy/sdk/app_framework.hpp"
|
#include "cardboy/sdk/app_framework.hpp"
|
||||||
#include "cardboy/sdk/app_system.hpp"
|
#include "cardboy/sdk/app_system.hpp"
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -19,27 +19,24 @@ namespace {
|
|||||||
|
|
||||||
using cardboy::sdk::AppButtonEvent;
|
using cardboy::sdk::AppButtonEvent;
|
||||||
using cardboy::sdk::AppContext;
|
using cardboy::sdk::AppContext;
|
||||||
|
using cardboy::sdk::AppTimeoutEvent;
|
||||||
using cardboy::sdk::AppTimerEvent;
|
using cardboy::sdk::AppTimerEvent;
|
||||||
|
|
||||||
constexpr std::uint32_t kRefreshIntervalMs = 100;
|
constexpr std::uint32_t kRefreshIntervalMs = 100;
|
||||||
|
constexpr std::uint32_t kSlowRefreshMs = 1000;
|
||||||
|
constexpr std::uint32_t kFastRefreshMs = 20;
|
||||||
constexpr std::uint32_t kUnlockHoldMs = 1500;
|
constexpr std::uint32_t kUnlockHoldMs = 1500;
|
||||||
|
|
||||||
using Framebuffer = typename AppContext::Framebuffer;
|
using Framebuffer = typename AppContext::Framebuffer;
|
||||||
using Clock = typename AppContext::Clock;
|
using Clock = typename AppContext::Clock;
|
||||||
|
|
||||||
constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowUpGlyph{0b00011000, 0b00111100, 0b01111110,
|
constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowUpGlyph{
|
||||||
0b11111111, 0b00011000, 0b00011000,
|
0b00011000, 0b00111100, 0b01111110, 0b11111111, 0b00011000, 0b00011000, 0b00011000, 0b00011000,
|
||||||
0b00011000, 0b00011000, 0b00011000,
|
0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00000000, 0b00000000};
|
||||||
0b00011000, 0b00011000, 0b00011000,
|
|
||||||
0b00011000, 0b00011000, 0b00000000,
|
|
||||||
0b00000000};
|
|
||||||
|
|
||||||
constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowDownGlyph{0b00000000, 0b00000000, 0b00011000,
|
constexpr std::array<std::uint8_t, font16x8::kGlyphHeight> kArrowDownGlyph{
|
||||||
0b00011000, 0b00011000, 0b00011000,
|
0b00000000, 0b00000000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00011000,
|
||||||
0b00011000, 0b00011000, 0b00011000,
|
0b00011000, 0b00011000, 0b00011000, 0b11111111, 0b01111110, 0b00111100, 0b00011000, 0b00000000};
|
||||||
0b00011000, 0b00011000, 0b11111111,
|
|
||||||
0b01111110, 0b00111100, 0b00011000,
|
|
||||||
0b00000000};
|
|
||||||
|
|
||||||
struct TimeSnapshot {
|
struct TimeSnapshot {
|
||||||
bool hasWallTime = false;
|
bool hasWallTime = false;
|
||||||
@@ -60,54 +57,67 @@ public:
|
|||||||
|
|
||||||
void onStart() override {
|
void onStart() override {
|
||||||
cancelRefreshTimer();
|
cancelRefreshTimer();
|
||||||
lastSnapshot = {};
|
lastSnapshot = {};
|
||||||
holdActive = false;
|
holdActive = false;
|
||||||
holdProgressMs = 0;
|
holdProgressMs = 0;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
lastNotificationInteractionMs = clock.millis();
|
lastNotificationInteractionMs = clock.millis();
|
||||||
|
lastRefreshMs = clock.millis();
|
||||||
refreshNotifications();
|
refreshNotifications();
|
||||||
const auto snap = captureTime();
|
const auto snap = captureTime();
|
||||||
renderIfNeeded(snap);
|
renderIfNeeded(snap);
|
||||||
lastSnapshot = snap;
|
lastSnapshot = snap;
|
||||||
refreshTimer = context.scheduleRepeatingTimer(kRefreshIntervalMs);
|
rescheduleRefreshTimer(kSlowRefreshMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStop() override { cancelRefreshTimer(); }
|
void onStop() override { cancelRefreshTimer(); }
|
||||||
|
|
||||||
void handleEvent(const cardboy::sdk::AppEvent& event) override {
|
std::optional<std::uint32_t> handleEvent(const cardboy::sdk::AppEvent& event) override {
|
||||||
event.visit(cardboy::sdk::overload(
|
event.visit(cardboy::sdk::overload([this](const AppButtonEvent& button) { handleButtonEvent(button); },
|
||||||
[this](const AppButtonEvent& button) { handleButtonEvent(button); },
|
[this](const AppTimerEvent& timer) {
|
||||||
[this](const AppTimerEvent& timer) {
|
if (timer.handle == refreshTimer) {
|
||||||
if (timer.handle == refreshTimer) {
|
const std::uint32_t now = clock.millis();
|
||||||
advanceHoldProgress();
|
const std::uint32_t elapsed = now - lastRefreshMs;
|
||||||
updateDisplay();
|
lastRefreshMs = now;
|
||||||
}
|
advanceHoldProgress(elapsed);
|
||||||
}));
|
updateDisplay();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[](const AppTimeoutEvent&) { /* ignore */ }));
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr std::size_t kMaxDisplayedNotifications = 5;
|
static constexpr std::size_t kMaxDisplayedNotifications = 5;
|
||||||
static constexpr std::uint32_t kNotificationHideMs = 8000;
|
static constexpr std::uint32_t kNotificationHideMs = 8000;
|
||||||
AppContext& context;
|
AppContext& context;
|
||||||
Framebuffer& framebuffer;
|
Framebuffer& framebuffer;
|
||||||
Clock& clock;
|
Clock& clock;
|
||||||
cardboy::sdk::INotificationCenter* notificationCenter = nullptr;
|
cardboy::sdk::INotificationCenter* notificationCenter = nullptr;
|
||||||
std::uint32_t lastNotificationRevision = 0;
|
std::uint32_t lastNotificationRevision = 0;
|
||||||
std::vector<cardboy::sdk::INotificationCenter::Notification> notifications;
|
std::vector<cardboy::sdk::INotificationCenter::Notification> notifications;
|
||||||
std::size_t selectedNotification = 0;
|
std::size_t selectedNotification = 0;
|
||||||
|
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
cardboy::sdk::AppTimerHandle refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
cardboy::sdk::AppTimerHandle refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
TimeSnapshot lastSnapshot{};
|
TimeSnapshot lastSnapshot{};
|
||||||
bool holdActive = false;
|
bool holdActive = false;
|
||||||
std::uint32_t holdProgressMs = 0;
|
std::uint32_t holdProgressMs = 0;
|
||||||
std::uint32_t lastNotificationInteractionMs = 0;
|
std::uint32_t lastNotificationInteractionMs = 0;
|
||||||
|
std::uint32_t lastRefreshMs = 0;
|
||||||
|
|
||||||
void cancelRefreshTimer() {
|
void cancelRefreshTimer() {
|
||||||
if (refreshTimer != cardboy::sdk::kInvalidAppTimer) {
|
if (refreshTimer == cardboy::sdk::kInvalidAppTimer)
|
||||||
context.cancelTimer(refreshTimer);
|
return;
|
||||||
refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
if (auto* timer = context.timer())
|
||||||
}
|
timer->cancelTimer(refreshTimer);
|
||||||
|
refreshTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rescheduleRefreshTimer(std::uint32_t intervalMs) {
|
||||||
|
cancelRefreshTimer();
|
||||||
|
if (auto* timer = context.timer())
|
||||||
|
refreshTimer = timer->scheduleTimer(intervalMs, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool comboPressed(const cardboy::sdk::InputState& state) { return state.a && state.select; }
|
static bool comboPressed(const cardboy::sdk::InputState& state) { return state.a && state.select; }
|
||||||
@@ -118,7 +128,7 @@ private:
|
|||||||
bool navPressed = false;
|
bool navPressed = false;
|
||||||
|
|
||||||
if (!notifications.empty() && (upPressed || downPressed)) {
|
if (!notifications.empty() && (upPressed || downPressed)) {
|
||||||
const std::size_t count = notifications.size();
|
const std::size_t count = notifications.size();
|
||||||
lastNotificationInteractionMs = clock.millis();
|
lastNotificationInteractionMs = clock.millis();
|
||||||
navPressed = true;
|
navPressed = true;
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
@@ -131,8 +141,8 @@ private:
|
|||||||
|
|
||||||
const bool deletePressed = button.current.b && !button.previous.b;
|
const bool deletePressed = button.current.b && !button.previous.b;
|
||||||
if (deletePressed && notificationCenter && !notifications.empty()) {
|
if (deletePressed && notificationCenter && !notifications.empty()) {
|
||||||
const std::size_t index = std::min<std::size_t>(selectedNotification, notifications.size() - 1);
|
const std::size_t index = std::min<std::size_t>(selectedNotification, notifications.size() - 1);
|
||||||
const auto& note = notifications[index];
|
const auto& note = notifications[index];
|
||||||
std::size_t preferredIndex = index;
|
std::size_t preferredIndex = index;
|
||||||
if (index + 1 < notifications.size())
|
if (index + 1 < notifications.size())
|
||||||
preferredIndex = index + 1;
|
preferredIndex = index + 1;
|
||||||
@@ -142,9 +152,9 @@ private:
|
|||||||
notificationCenter->removeByExternalId(note.externalId);
|
notificationCenter->removeByExternalId(note.externalId);
|
||||||
else
|
else
|
||||||
notificationCenter->removeById(note.id);
|
notificationCenter->removeById(note.id);
|
||||||
selectedNotification = preferredIndex;
|
selectedNotification = preferredIndex;
|
||||||
lastNotificationInteractionMs = clock.millis();
|
lastNotificationInteractionMs = clock.millis();
|
||||||
dirty = true;
|
dirty = true;
|
||||||
refreshNotifications();
|
refreshNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,9 +169,9 @@ private:
|
|||||||
if (!notificationCenter) {
|
if (!notificationCenter) {
|
||||||
if (!notifications.empty() || lastNotificationRevision != 0) {
|
if (!notifications.empty() || lastNotificationRevision != 0) {
|
||||||
notifications.clear();
|
notifications.clear();
|
||||||
selectedNotification = 0;
|
selectedNotification = 0;
|
||||||
lastNotificationRevision = 0;
|
lastNotificationRevision = 0;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -173,7 +183,7 @@ private:
|
|||||||
const std::uint64_t previousId =
|
const std::uint64_t previousId =
|
||||||
(selectedNotification < notifications.size()) ? notifications[selectedNotification].id : 0;
|
(selectedNotification < notifications.size()) ? notifications[selectedNotification].id : 0;
|
||||||
|
|
||||||
auto latest = notificationCenter->recent(kMaxDisplayedNotifications);
|
auto latest = notificationCenter->recent(kMaxDisplayedNotifications);
|
||||||
notifications = std::move(latest);
|
notifications = std::move(latest);
|
||||||
|
|
||||||
if (notifications.empty()) {
|
if (notifications.empty()) {
|
||||||
@@ -191,14 +201,17 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
lastNotificationInteractionMs = clock.millis();
|
lastNotificationInteractionMs = clock.millis();
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateHoldState(bool comboNow) {
|
void updateHoldState(bool comboNow) {
|
||||||
|
const bool wasActive = holdActive;
|
||||||
if (comboNow) {
|
if (comboNow) {
|
||||||
if (!holdActive) {
|
if (!holdActive) {
|
||||||
holdActive = true;
|
holdActive = true;
|
||||||
dirty = true;
|
holdProgressMs = 0;
|
||||||
|
lastRefreshMs = clock.millis();
|
||||||
|
dirty = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (holdActive || holdProgressMs != 0) {
|
if (holdActive || holdProgressMs != 0) {
|
||||||
@@ -207,12 +220,14 @@ private:
|
|||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (wasActive != holdActive) {
|
||||||
|
rescheduleRefreshTimer(holdActive ? kFastRefreshMs : kSlowRefreshMs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void advanceHoldProgress() {
|
void advanceHoldProgress(std::uint32_t elapsedMs) {
|
||||||
if (holdActive) {
|
if (holdActive) {
|
||||||
const std::uint32_t next =
|
const std::uint32_t next = std::min<std::uint32_t>(holdProgressMs + elapsedMs, kUnlockHoldMs);
|
||||||
std::min<std::uint32_t>(holdProgressMs + kRefreshIntervalMs, kUnlockHoldMs);
|
|
||||||
if (next != holdProgressMs) {
|
if (next != holdProgressMs) {
|
||||||
holdProgressMs = next;
|
holdProgressMs = next;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
@@ -237,8 +252,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool sameSnapshot(const TimeSnapshot& a, const TimeSnapshot& b) {
|
static bool sameSnapshot(const TimeSnapshot& a, const TimeSnapshot& b) {
|
||||||
return a.hasWallTime == b.hasWallTime && a.hour24 == b.hour24 && a.minute == b.minute &&
|
return a.hasWallTime == b.hasWallTime && a.hour24 == b.hour24 && a.minute == b.minute && a.second == b.second &&
|
||||||
a.second == b.second && a.day == b.day && a.month == b.month && a.year == b.year;
|
a.day == b.day && a.month == b.month && a.year == b.year;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeSnapshot captureTime() const {
|
TimeSnapshot captureTime() const {
|
||||||
@@ -325,7 +340,7 @@ private:
|
|||||||
if (font16x8::measureText(text, scale, letterSpacing) <= maxWidth)
|
if (font16x8::measureText(text, scale, letterSpacing) <= maxWidth)
|
||||||
return std::string(text);
|
return std::string(text);
|
||||||
|
|
||||||
std::string result(text.begin(), text.end());
|
std::string result(text.begin(), text.end());
|
||||||
const std::string ellipsis = "...";
|
const std::string ellipsis = "...";
|
||||||
while (!result.empty()) {
|
while (!result.empty()) {
|
||||||
result.pop_back();
|
result.pop_back();
|
||||||
@@ -367,8 +382,7 @@ private:
|
|||||||
|
|
||||||
if (!word.empty()) {
|
if (!word.empty()) {
|
||||||
std::string candidate = current.empty() ? word : current + " " + word;
|
std::string candidate = current.empty() ? word : current + " " + word;
|
||||||
if (!current.empty() &&
|
if (!current.empty() && font16x8::measureText(candidate, scale, letterSpacing) > maxWidth) {
|
||||||
font16x8::measureText(candidate, scale, letterSpacing) > maxWidth) {
|
|
||||||
flushCurrent();
|
flushCurrent();
|
||||||
if (lines.size() >= static_cast<std::size_t>(maxLines)) {
|
if (lines.size() >= static_cast<std::size_t>(maxLines)) {
|
||||||
truncated = true;
|
truncated = true;
|
||||||
@@ -430,20 +444,20 @@ private:
|
|||||||
|
|
||||||
framebuffer.frameReady();
|
framebuffer.frameReady();
|
||||||
|
|
||||||
const int scaleTime = 4;
|
const int scaleTime = 4;
|
||||||
const int scaleSeconds = 2;
|
const int scaleSeconds = 2;
|
||||||
const int scaleSmall = 1;
|
const int scaleSmall = 1;
|
||||||
const int textLineHeight = font16x8::kGlyphHeight * scaleSmall;
|
const int textLineHeight = font16x8::kGlyphHeight * scaleSmall;
|
||||||
|
|
||||||
const int cardMarginTop = 4;
|
const int cardMarginTop = 4;
|
||||||
const int cardMarginSide = 8;
|
const int cardMarginSide = 8;
|
||||||
const int cardPadding = 6;
|
const int cardPadding = 6;
|
||||||
const int cardLineSpacing = 4;
|
const int cardLineSpacing = 4;
|
||||||
int cardHeight = 0;
|
int cardHeight = 0;
|
||||||
const int cardWidth = framebuffer.width() - cardMarginSide * 2;
|
const int cardWidth = framebuffer.width() - cardMarginSide * 2;
|
||||||
|
|
||||||
const std::uint32_t nowMs = clock.millis();
|
const std::uint32_t nowMs = clock.millis();
|
||||||
const bool hasNotifications = !notifications.empty();
|
const bool hasNotifications = !notifications.empty();
|
||||||
const bool showNotificationDetails =
|
const bool showNotificationDetails =
|
||||||
hasNotifications && (nowMs - lastNotificationInteractionMs <= kNotificationHideMs);
|
hasNotifications && (nowMs - lastNotificationInteractionMs <= kNotificationHideMs);
|
||||||
|
|
||||||
@@ -479,7 +493,8 @@ private:
|
|||||||
std::snprintf(counter, sizeof(counter), "%zu/%zu", selectedNotification + 1, notifications.size());
|
std::snprintf(counter, sizeof(counter), "%zu/%zu", selectedNotification + 1, notifications.size());
|
||||||
const int counterWidth = font16x8::measureText(counter, scaleSmall, 1);
|
const int counterWidth = font16x8::measureText(counter, scaleSmall, 1);
|
||||||
const int counterX = cardMarginSide + cardWidth - cardPadding - counterWidth;
|
const int counterX = cardMarginSide + cardWidth - cardPadding - counterWidth;
|
||||||
font16x8::drawText(framebuffer, counterX, cardMarginTop + cardPadding, counter, scaleSmall, true, 1);
|
font16x8::drawText(framebuffer, counterX, cardMarginTop + cardPadding, counter, scaleSmall, true,
|
||||||
|
1);
|
||||||
const int arrowWidth = font16x8::kGlyphWidth * scaleSmall;
|
const int arrowWidth = font16x8::kGlyphWidth * scaleSmall;
|
||||||
const int arrowSpacing = std::max(1, scaleSmall);
|
const int arrowSpacing = std::max(1, scaleSmall);
|
||||||
const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing;
|
const int arrowsTotalWide = arrowWidth * 2 + arrowSpacing;
|
||||||
@@ -493,7 +508,7 @@ private:
|
|||||||
|
|
||||||
if (!bodyLines.empty()) {
|
if (!bodyLines.empty()) {
|
||||||
int bodyY = cardMarginTop + cardPadding + textLineHeight + cardLineSpacing;
|
int bodyY = cardMarginTop + cardPadding + textLineHeight + cardLineSpacing;
|
||||||
for (const auto& line : bodyLines) {
|
for (const auto& line: bodyLines) {
|
||||||
font16x8::drawText(framebuffer, cardMarginSide + cardPadding, bodyY, line, scaleSmall, true, 1);
|
font16x8::drawText(framebuffer, cardMarginSide + cardPadding, bodyY, line, scaleSmall, true, 1);
|
||||||
bodyY += textLineHeight + cardLineSpacing;
|
bodyY += textLineHeight + cardLineSpacing;
|
||||||
}
|
}
|
||||||
@@ -529,9 +544,8 @@ private:
|
|||||||
if (cardHeight > 0)
|
if (cardHeight > 0)
|
||||||
timeY = cardMarginTop + cardHeight + 16;
|
timeY = cardMarginTop + cardHeight + 16;
|
||||||
const int minTimeY = (cardHeight > 0) ? (cardMarginTop + cardHeight + 12) : 16;
|
const int minTimeY = (cardHeight > 0) ? (cardMarginTop + cardHeight + 12) : 16;
|
||||||
const int maxTimeY =
|
const int maxTimeY = std::max(minTimeY, framebuffer.height() - font16x8::kGlyphHeight * scaleTime - 48);
|
||||||
std::max(minTimeY, framebuffer.height() - font16x8::kGlyphHeight * scaleTime - 48);
|
timeY = std::clamp(timeY, minTimeY, maxTimeY);
|
||||||
timeY = std::clamp(timeY, minTimeY, maxTimeY);
|
|
||||||
|
|
||||||
char hoursMinutes[6];
|
char hoursMinutes[6];
|
||||||
std::snprintf(hoursMinutes, sizeof(hoursMinutes), "%02d:%02d", snap.hour24, snap.minute);
|
std::snprintf(hoursMinutes, sizeof(hoursMinutes), "%02d:%02d", snap.hour24, snap.minute);
|
||||||
@@ -539,7 +553,7 @@ private:
|
|||||||
const int timeX = (framebuffer.width() - mainW) / 2;
|
const int timeX = (framebuffer.width() - mainW) / 2;
|
||||||
const int secX = timeX + mainW + 12;
|
const int secX = timeX + mainW + 12;
|
||||||
const int secY = timeY + font16x8::kGlyphHeight * scaleTime - font16x8::kGlyphHeight * scaleSeconds;
|
const int secY = timeY + font16x8::kGlyphHeight * scaleTime - font16x8::kGlyphHeight * scaleSeconds;
|
||||||
char secs[3];
|
char secs[3];
|
||||||
std::snprintf(secs, sizeof(secs), "%02d", snap.second);
|
std::snprintf(secs, sizeof(secs), "%02d", snap.second);
|
||||||
|
|
||||||
font16x8::drawText(framebuffer, timeX, timeY, hoursMinutes, scaleTime, true, 0);
|
font16x8::drawText(framebuffer, timeX, timeY, hoursMinutes, scaleTime, true, 0);
|
||||||
@@ -548,7 +562,7 @@ private:
|
|||||||
const std::string dateLine = formatDate(snap);
|
const std::string dateLine = formatDate(snap);
|
||||||
drawCenteredText(framebuffer, timeY + font16x8::kGlyphHeight * scaleTime + 16, dateLine, scaleSmall, 1);
|
drawCenteredText(framebuffer, timeY + font16x8::kGlyphHeight * scaleTime + 16, dateLine, scaleSmall, 1);
|
||||||
|
|
||||||
const std::string instruction = holdActive ? "KEEP HOLDING A+SELECT" : "HOLD A+SELECT";
|
const std::string instruction = holdActive ? "KEEP HOLDING A+SELECT" : "HOLD A+SELECT";
|
||||||
const int instructionWidth = font16x8::measureText(instruction, scaleSmall, 1);
|
const int instructionWidth = font16x8::measureText(instruction, scaleSmall, 1);
|
||||||
const int barHeight = 14;
|
const int barHeight = 14;
|
||||||
const int barY = framebuffer.height() - 24;
|
const int barY = framebuffer.height() - 24;
|
||||||
@@ -566,11 +580,10 @@ private:
|
|||||||
drawRectOutline(framebuffer, barX, barY, barWidth, barHeight);
|
drawRectOutline(framebuffer, barX, barY, barWidth, barHeight);
|
||||||
|
|
||||||
if (holdActive || holdProgressMs > 0) {
|
if (holdActive || holdProgressMs > 0) {
|
||||||
const int innerWidth = barWidth - 2;
|
const int innerWidth = barWidth - 2;
|
||||||
const int innerHeight = barHeight - 2;
|
const int innerHeight = barHeight - 2;
|
||||||
const float ratio =
|
const float ratio = std::clamp(holdProgressMs / static_cast<float>(kUnlockHoldMs), 0.0f, 1.0f);
|
||||||
std::clamp(holdProgressMs / static_cast<float>(kUnlockHoldMs), 0.0f, 1.0f);
|
const int fillWidth = static_cast<int>(ratio * innerWidth + 0.5f);
|
||||||
const int fillWidth = static_cast<int>(ratio * innerWidth + 0.5f);
|
|
||||||
if (fillWidth > 0)
|
if (fillWidth > 0)
|
||||||
fillRect(framebuffer, barX + 1, barY + 1, fillWidth, innerHeight);
|
fillRect(framebuffer, barX + 1, barY + 1, fillWidth, innerHeight);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace apps {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using cardboy::sdk::AppButtonEvent;
|
using cardboy::sdk::AppButtonEvent;
|
||||||
|
using cardboy::sdk::AppTimeoutEvent;
|
||||||
using cardboy::sdk::AppContext;
|
using cardboy::sdk::AppContext;
|
||||||
using cardboy::sdk::AppEvent;
|
using cardboy::sdk::AppEvent;
|
||||||
using cardboy::sdk::AppTimerEvent;
|
using cardboy::sdk::AppTimerEvent;
|
||||||
@@ -44,15 +45,16 @@ public:
|
|||||||
|
|
||||||
void onStop() override { cancelInactivityTimer(); }
|
void onStop() override { cancelInactivityTimer(); }
|
||||||
|
|
||||||
void handleEvent(const cardboy::sdk::AppEvent& event) override {
|
std::optional<std::uint32_t> handleEvent(const cardboy::sdk::AppEvent& event) override {
|
||||||
event.visit(cardboy::sdk::overload(
|
event.visit(cardboy::sdk::overload([this](const AppButtonEvent& button) { handleButtonEvent(button); },
|
||||||
[this](const AppButtonEvent& button) { handleButtonEvent(button); },
|
[this](const AppTimerEvent& timer) {
|
||||||
[this](const AppTimerEvent& timer) {
|
if (timer.handle == inactivityTimer) {
|
||||||
if (timer.handle == inactivityTimer) {
|
cancelInactivityTimer();
|
||||||
cancelInactivityTimer();
|
context.requestAppSwitchByName(kLockscreenAppName);
|
||||||
context.requestAppSwitchByName(kLockscreenAppName);
|
}
|
||||||
}
|
},
|
||||||
}));
|
[](const AppTimeoutEvent&) { /* ignore */ }));
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -189,15 +191,17 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cancelInactivityTimer() {
|
void cancelInactivityTimer() {
|
||||||
if (inactivityTimer != cardboy::sdk::kInvalidAppTimer) {
|
if (inactivityTimer == cardboy::sdk::kInvalidAppTimer)
|
||||||
context.cancelTimer(inactivityTimer);
|
return;
|
||||||
inactivityTimer = cardboy::sdk::kInvalidAppTimer;
|
if (auto* timer = context.timer())
|
||||||
}
|
timer->cancelTimer(inactivityTimer);
|
||||||
|
inactivityTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetInactivityTimer() {
|
void resetInactivityTimer() {
|
||||||
cancelInactivityTimer();
|
cancelInactivityTimer();
|
||||||
inactivityTimer = context.scheduleTimer(kIdleTimeoutMs);
|
if (auto* timer = context.timer())
|
||||||
|
inactivityTimer = timer->scheduleTimer(kIdleTimeoutMs, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,42 +38,44 @@ public:
|
|||||||
renderIfNeeded();
|
renderIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleEvent(const cardboy::sdk::AppEvent& event) override {
|
std::optional<std::uint32_t> handleEvent(const cardboy::sdk::AppEvent& event) override {
|
||||||
const auto* buttonEvent = event.button();
|
event.visit(cardboy::sdk::overload(
|
||||||
if (!buttonEvent)
|
[this](const cardboy::sdk::AppButtonEvent& button) {
|
||||||
return;
|
const auto& current = button.current;
|
||||||
|
const auto& previous = button.previous;
|
||||||
|
|
||||||
const auto& current = buttonEvent->current;
|
const bool previousAvailable = buzzerAvailable;
|
||||||
const auto& previous = buttonEvent->previous;
|
syncBuzzerState();
|
||||||
|
if (previousAvailable != buzzerAvailable)
|
||||||
|
dirty = true;
|
||||||
|
|
||||||
const bool previousAvailable = buzzerAvailable;
|
if (current.b && !previous.b) {
|
||||||
syncBuzzerState();
|
context.requestAppSwitchByName(kMenuAppName);
|
||||||
if (previousAvailable != buzzerAvailable)
|
return;
|
||||||
dirty = true;
|
}
|
||||||
|
|
||||||
if (current.b && !previous.b) {
|
bool moved = false;
|
||||||
context.requestAppSwitchByName(kMenuAppName);
|
if (current.down && !previous.down) {
|
||||||
return;
|
moveSelection(+1);
|
||||||
}
|
moved = true;
|
||||||
|
} else if (current.up && !previous.up) {
|
||||||
|
moveSelection(-1);
|
||||||
|
moved = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool moved = false;
|
const bool togglePressed = (current.a && !previous.a) || (current.start && !previous.start) ||
|
||||||
if (current.down && !previous.down) {
|
(current.select && !previous.select);
|
||||||
moveSelection(+1);
|
if (togglePressed)
|
||||||
moved = true;
|
handleToggle();
|
||||||
} else if (current.up && !previous.up) {
|
|
||||||
moveSelection(-1);
|
|
||||||
moved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool togglePressed = (current.a && !previous.a) || (current.start && !previous.start) ||
|
if (moved)
|
||||||
(current.select && !previous.select);
|
dirty = true;
|
||||||
if (togglePressed)
|
|
||||||
handleToggle();
|
|
||||||
|
|
||||||
if (moved)
|
renderIfNeeded();
|
||||||
dirty = true;
|
},
|
||||||
|
[](const cardboy::sdk::AppTimerEvent&) { /* ignore */ },
|
||||||
renderIfNeeded();
|
[](const cardboy::sdk::AppTimeoutEvent&) { /* ignore */ }));
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace apps {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using cardboy::sdk::AppButtonEvent;
|
using cardboy::sdk::AppButtonEvent;
|
||||||
|
using cardboy::sdk::AppTimeoutEvent;
|
||||||
using cardboy::sdk::AppContext;
|
using cardboy::sdk::AppContext;
|
||||||
using cardboy::sdk::AppEvent;
|
using cardboy::sdk::AppEvent;
|
||||||
using cardboy::sdk::AppTimerEvent;
|
using cardboy::sdk::AppTimerEvent;
|
||||||
@@ -28,13 +29,13 @@ using cardboy::sdk::InputState;
|
|||||||
|
|
||||||
constexpr char kSnakeAppName[] = "Snake";
|
constexpr char kSnakeAppName[] = "Snake";
|
||||||
|
|
||||||
constexpr int kBoardWidth = 32;
|
constexpr int kBoardWidth = 32;
|
||||||
constexpr int kBoardHeight = 20;
|
constexpr int kBoardHeight = 20;
|
||||||
constexpr int kCellSize = 10;
|
constexpr int kCellSize = 10;
|
||||||
constexpr int kInitialSnakeLength = 5;
|
constexpr int kInitialSnakeLength = 5;
|
||||||
constexpr int kScorePerFood = 10;
|
constexpr int kScorePerFood = 10;
|
||||||
constexpr int kMinMoveIntervalMs = 80;
|
constexpr int kMinMoveIntervalMs = 80;
|
||||||
constexpr int kBaseMoveIntervalMs = 220;
|
constexpr int kBaseMoveIntervalMs = 220;
|
||||||
constexpr int kIntervalSpeedupPerSegment = 4;
|
constexpr int kIntervalSpeedupPerSegment = 4;
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
@@ -69,11 +70,12 @@ public:
|
|||||||
|
|
||||||
void onStop() { cancelMoveTimer(); }
|
void onStop() { cancelMoveTimer(); }
|
||||||
|
|
||||||
void handleEvent(const AppEvent& event) {
|
std::optional<std::uint32_t> handleEvent(const AppEvent& event) {
|
||||||
event.visit(cardboy::sdk::overload(
|
event.visit(cardboy::sdk::overload([this](const AppButtonEvent& button) { handleButtons(button); },
|
||||||
[this](const AppButtonEvent& button) { handleButtons(button); },
|
[this](const AppTimerEvent& timer) { handleTimer(timer.handle); },
|
||||||
[this](const AppTimerEvent& timer) { handleTimer(timer.handle); }));
|
[](const AppTimeoutEvent&) { /* ignore */ }));
|
||||||
renderIfNeeded();
|
renderIfNeeded();
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -89,8 +91,8 @@ private:
|
|||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
int score = 0;
|
int score = 0;
|
||||||
int highScore = 0;
|
int highScore = 0;
|
||||||
AppTimerHandle moveTimer = cardboy::sdk::kInvalidAppTimer;
|
AppTimerHandle moveTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
std::mt19937 rng;
|
std::mt19937 rng;
|
||||||
|
|
||||||
void handleButtons(const AppButtonEvent& evt) {
|
void handleButtons(const AppButtonEvent& evt) {
|
||||||
const auto& cur = evt.current;
|
const auto& cur = evt.current;
|
||||||
@@ -158,7 +160,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void advance() {
|
void advance() {
|
||||||
direction = queuedDirection;
|
direction = queuedDirection;
|
||||||
Point nextHead = snake.front();
|
Point nextHead = snake.front();
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case Direction::Up:
|
case Direction::Up:
|
||||||
@@ -248,14 +250,16 @@ private:
|
|||||||
void scheduleMoveTimer() {
|
void scheduleMoveTimer() {
|
||||||
cancelMoveTimer();
|
cancelMoveTimer();
|
||||||
const std::uint32_t interval = currentInterval();
|
const std::uint32_t interval = currentInterval();
|
||||||
moveTimer = context.scheduleRepeatingTimer(interval);
|
if (auto* timer = context.timer())
|
||||||
|
moveTimer = timer->scheduleTimer(interval, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancelMoveTimer() {
|
void cancelMoveTimer() {
|
||||||
if (moveTimer != cardboy::sdk::kInvalidAppTimer) {
|
if (moveTimer == cardboy::sdk::kInvalidAppTimer)
|
||||||
context.cancelTimer(moveTimer);
|
return;
|
||||||
moveTimer = cardboy::sdk::kInvalidAppTimer;
|
if (auto* timer = context.timer())
|
||||||
}
|
timer->cancelTimer(moveTimer);
|
||||||
|
moveTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::uint32_t currentInterval() const {
|
[[nodiscard]] std::uint32_t currentInterval() const {
|
||||||
@@ -297,9 +301,7 @@ private:
|
|||||||
framebuffer.sendFrame();
|
framebuffer.sendFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int boardOriginX() const {
|
[[nodiscard]] int boardOriginX() const { return (cardboy::sdk::kDisplayWidth - kBoardWidth * kCellSize) / 2; }
|
||||||
return (cardboy::sdk::kDisplayWidth - kBoardWidth * kCellSize) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int boardOriginY() const {
|
[[nodiscard]] int boardOriginY() const {
|
||||||
const int centered = (cardboy::sdk::kDisplayHeight - kBoardHeight * kCellSize) / 2;
|
const int centered = (cardboy::sdk::kDisplayHeight - kBoardHeight * kCellSize) / 2;
|
||||||
@@ -404,9 +406,9 @@ class SnakeApp final : public cardboy::sdk::IApp {
|
|||||||
public:
|
public:
|
||||||
explicit SnakeApp(AppContext& ctx) : game(ctx) {}
|
explicit SnakeApp(AppContext& ctx) : game(ctx) {}
|
||||||
|
|
||||||
void onStart() override { game.onStart(); }
|
void onStart() override { game.onStart(); }
|
||||||
void onStop() override { game.onStop(); }
|
void onStop() override { game.onStop(); }
|
||||||
void handleEvent(const AppEvent& event) override { game.handleEvent(event); }
|
std::optional<std::uint32_t> handleEvent(const AppEvent& event) override { return game.handleEvent(event); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SnakeGame game;
|
SnakeGame game;
|
||||||
|
|||||||
@@ -148,11 +148,12 @@ public:
|
|||||||
|
|
||||||
void onStop() { cancelTimers(); }
|
void onStop() { cancelTimers(); }
|
||||||
|
|
||||||
void handleEvent(const AppEvent& event) {
|
std::optional<std::uint32_t> handleEvent(const AppEvent& event) {
|
||||||
event.visit(cardboy::sdk::overload(
|
event.visit(cardboy::sdk::overload([this](const AppButtonEvent& button) { handleButtons(button); },
|
||||||
[this](const AppButtonEvent& button) { handleButtons(button); },
|
[this](const AppTimerEvent& timer) { handleTimer(timer.handle); },
|
||||||
[this](const AppTimerEvent& timer) { handleTimer(timer.handle); }));
|
[](const cardboy::sdk::AppTimeoutEvent&) { /* ignore */ }));
|
||||||
renderIfNeeded();
|
renderIfNeeded();
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -235,35 +236,40 @@ private:
|
|||||||
|
|
||||||
void cancelTimers() {
|
void cancelTimers() {
|
||||||
if (dropTimer != cardboy::sdk::kInvalidAppTimer) {
|
if (dropTimer != cardboy::sdk::kInvalidAppTimer) {
|
||||||
context.cancelTimer(dropTimer);
|
if (auto* timer = context.timer())
|
||||||
|
timer->cancelTimer(dropTimer);
|
||||||
dropTimer = cardboy::sdk::kInvalidAppTimer;
|
dropTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
}
|
}
|
||||||
cancelSoftDropTimer();
|
cancelSoftDropTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancelSoftDropTimer() {
|
void cancelSoftDropTimer() {
|
||||||
if (softTimer != cardboy::sdk::kInvalidAppTimer) {
|
if (softTimer == cardboy::sdk::kInvalidAppTimer)
|
||||||
context.cancelTimer(softTimer);
|
return;
|
||||||
softTimer = cardboy::sdk::kInvalidAppTimer;
|
if (auto* timer = context.timer())
|
||||||
}
|
timer->cancelTimer(softTimer);
|
||||||
|
softTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduleDropTimer() {
|
void scheduleDropTimer() {
|
||||||
cancelDropTimer();
|
cancelDropTimer();
|
||||||
const std::uint32_t interval = dropIntervalMs();
|
const std::uint32_t interval = dropIntervalMs();
|
||||||
dropTimer = context.scheduleRepeatingTimer(interval);
|
if (auto* timer = context.timer())
|
||||||
|
dropTimer = timer->scheduleTimer(interval, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancelDropTimer() {
|
void cancelDropTimer() {
|
||||||
if (dropTimer != cardboy::sdk::kInvalidAppTimer) {
|
if (dropTimer == cardboy::sdk::kInvalidAppTimer)
|
||||||
context.cancelTimer(dropTimer);
|
return;
|
||||||
dropTimer = cardboy::sdk::kInvalidAppTimer;
|
if (auto* timer = context.timer())
|
||||||
}
|
timer->cancelTimer(dropTimer);
|
||||||
|
dropTimer = cardboy::sdk::kInvalidAppTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduleSoftDropTimer() {
|
void scheduleSoftDropTimer() {
|
||||||
cancelSoftDropTimer();
|
cancelSoftDropTimer();
|
||||||
softTimer = context.scheduleRepeatingTimer(60);
|
if (auto* timer = context.timer())
|
||||||
|
softTimer = timer->scheduleTimer(60, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::uint32_t dropIntervalMs() const {
|
[[nodiscard]] std::uint32_t dropIntervalMs() const {
|
||||||
@@ -629,9 +635,9 @@ class TetrisApp final : public cardboy::sdk::IApp {
|
|||||||
public:
|
public:
|
||||||
explicit TetrisApp(AppContext& ctx) : game(ctx) {}
|
explicit TetrisApp(AppContext& ctx) : game(ctx) {}
|
||||||
|
|
||||||
void onStart() override { game.onStart(); }
|
void onStart() override { game.onStart(); }
|
||||||
void onStop() override { game.onStop(); }
|
void onStop() override { game.onStop(); }
|
||||||
void handleEvent(const AppEvent& event) override { game.handleEvent(event); }
|
std::optional<std::uint32_t> handleEvent(const AppEvent& event) override { return game.handleEvent(event); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TetrisGame game;
|
TetrisGame game;
|
||||||
|
|||||||
@@ -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/backend/backend_interface.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/backend.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/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/loop_hooks.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/input_state.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/input_state.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/platform.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/platform.hpp
|
||||||
|
|||||||
@@ -12,27 +12,26 @@ namespace cardboy::sdk {
|
|||||||
using AppTimerHandle = std::uint32_t;
|
using AppTimerHandle = std::uint32_t;
|
||||||
constexpr AppTimerHandle kInvalidAppTimer = 0;
|
constexpr AppTimerHandle kInvalidAppTimer = 0;
|
||||||
|
|
||||||
using TimerClientId = std::uint32_t;
|
|
||||||
constexpr TimerClientId kInvalidTimerClient = 0;
|
|
||||||
|
|
||||||
struct AppButtonEvent {
|
struct AppButtonEvent {
|
||||||
InputState current{};
|
InputState current{};
|
||||||
InputState previous{};
|
InputState previous{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AppTimerEvent {
|
struct AppTimerEvent {
|
||||||
TimerClientId clientId = kInvalidTimerClient;
|
AppTimerHandle handle = kInvalidAppTimer;
|
||||||
AppTimerHandle handle = kInvalidAppTimer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AppTimeoutEvent {};
|
||||||
|
|
||||||
struct AppEvent {
|
struct AppEvent {
|
||||||
using Data = std::variant<AppButtonEvent, AppTimerEvent>;
|
using Data = std::variant<AppButtonEvent, AppTimerEvent, AppTimeoutEvent>;
|
||||||
|
|
||||||
std::uint32_t timestamp_ms = 0;
|
std::uint32_t timestamp_ms = 0;
|
||||||
Data data{AppButtonEvent{}};
|
Data data{AppButtonEvent{}};
|
||||||
|
|
||||||
[[nodiscard]] bool isButton() const { return std::holds_alternative<AppButtonEvent>(data); }
|
[[nodiscard]] bool isButton() const { return std::holds_alternative<AppButtonEvent>(data); }
|
||||||
[[nodiscard]] bool isTimer() const { return std::holds_alternative<AppTimerEvent>(data); }
|
[[nodiscard]] bool isTimer() const { return std::holds_alternative<AppTimerEvent>(data); }
|
||||||
|
[[nodiscard]] bool isTimeout() const { return std::holds_alternative<AppTimeoutEvent>(data); }
|
||||||
|
|
||||||
[[nodiscard]] const AppButtonEvent* button() const { return std::get_if<AppButtonEvent>(&data); }
|
[[nodiscard]] const AppButtonEvent* button() const { return std::get_if<AppButtonEvent>(&data); }
|
||||||
[[nodiscard]] AppButtonEvent* button() { return std::get_if<AppButtonEvent>(&data); }
|
[[nodiscard]] AppButtonEvent* button() { return std::get_if<AppButtonEvent>(&data); }
|
||||||
@@ -40,6 +39,9 @@ struct AppEvent {
|
|||||||
[[nodiscard]] const AppTimerEvent* timer() const { return std::get_if<AppTimerEvent>(&data); }
|
[[nodiscard]] const AppTimerEvent* timer() const { return std::get_if<AppTimerEvent>(&data); }
|
||||||
[[nodiscard]] AppTimerEvent* timer() { return std::get_if<AppTimerEvent>(&data); }
|
[[nodiscard]] AppTimerEvent* timer() { return std::get_if<AppTimerEvent>(&data); }
|
||||||
|
|
||||||
|
[[nodiscard]] const AppTimeoutEvent* timeout() const { return std::get_if<AppTimeoutEvent>(&data); }
|
||||||
|
[[nodiscard]] AppTimeoutEvent* timeout() { return std::get_if<AppTimeoutEvent>(&data); }
|
||||||
|
|
||||||
template<typename Visitor>
|
template<typename Visitor>
|
||||||
decltype(auto) visit(Visitor&& visitor) {
|
decltype(auto) visit(Visitor&& visitor) {
|
||||||
return std::visit(std::forward<Visitor>(visitor), data);
|
return std::visit(std::forward<Visitor>(visitor), data);
|
||||||
@@ -51,6 +53,8 @@ struct AppEvent {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(std::is_trivially_copyable_v<AppEvent>);
|
||||||
|
|
||||||
template<typename... Ts>
|
template<typename... Ts>
|
||||||
struct Overload : Ts... {
|
struct Overload : Ts... {
|
||||||
using Ts::operator()...;
|
using Ts::operator()...;
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "cardboy/sdk/app_events.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
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<EventBusSignal>(static_cast<std::uint32_t>(lhs) | static_cast<std::uint32_t>(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline EventBusSignal& operator|=(EventBusSignal& lhs, EventBusSignal rhs) {
|
|
||||||
lhs = lhs | rhs;
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline EventBusSignal operator&(EventBusSignal lhs, EventBusSignal rhs) {
|
|
||||||
return static_cast<EventBusSignal>(static_cast<std::uint32_t>(lhs) & static_cast<std::uint32_t>(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::uint32_t to_event_bits(EventBusSignal signal) { return static_cast<std::uint32_t>(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
|
|
||||||
@@ -11,8 +11,8 @@ public:
|
|||||||
static void invokePreSend(void* framebuffer);
|
static void invokePreSend(void* framebuffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static PreSendHook hook_;
|
static PreSendHook _hook;
|
||||||
static void* userData_;
|
static void* _userData;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cardboy/sdk/event_bus.hpp"
|
|
||||||
#include "cardboy/sdk/loop_hooks.hpp"
|
#include "cardboy/sdk/loop_hooks.hpp"
|
||||||
#include "cardboy/sdk/timer_service.hpp"
|
#include "cardboy/sdk/timer_service.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -74,36 +75,56 @@ public:
|
|||||||
class INotificationCenter {
|
class INotificationCenter {
|
||||||
public:
|
public:
|
||||||
struct Notification {
|
struct Notification {
|
||||||
std::uint64_t id = 0;
|
std::uint64_t id = 0;
|
||||||
std::uint64_t timestamp = 0;
|
std::uint64_t timestamp = 0;
|
||||||
std::uint64_t externalId = 0;
|
std::uint64_t externalId = 0;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string body;
|
std::string body;
|
||||||
bool unread = true;
|
bool unread = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~INotificationCenter() = default;
|
virtual ~INotificationCenter() = default;
|
||||||
|
|
||||||
virtual void pushNotification(Notification notification) = 0;
|
virtual void pushNotification(Notification notification) = 0;
|
||||||
[[nodiscard]] virtual std::uint32_t revision() const = 0;
|
[[nodiscard]] virtual std::uint32_t revision() const = 0;
|
||||||
[[nodiscard]] virtual std::vector<Notification> recent(std::size_t limit) const = 0;
|
[[nodiscard]] virtual std::vector<Notification> recent(std::size_t limit) const = 0;
|
||||||
virtual void markAllRead() = 0;
|
virtual void markAllRead() = 0;
|
||||||
virtual void clear() = 0;
|
virtual void clear() = 0;
|
||||||
virtual void removeById(std::uint64_t id) = 0;
|
virtual void removeById(std::uint64_t id) = 0;
|
||||||
virtual void removeByExternalId(std::uint64_t externalId) = 0;
|
virtual void removeByExternalId(std::uint64_t externalId) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IEventBus {
|
||||||
|
public:
|
||||||
|
virtual ~IEventBus() = default;
|
||||||
|
|
||||||
|
virtual void post(const AppEvent& event) = 0;
|
||||||
|
virtual std::optional<AppEvent> pop(std::optional<std::uint32_t> timeout_ms = std::nullopt) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AppScopedServices {
|
||||||
|
ITimerService* timer = nullptr;
|
||||||
|
virtual ~AppScopedServices() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IAppServiceProvider {
|
||||||
|
public:
|
||||||
|
virtual ~IAppServiceProvider() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual std::unique_ptr<AppScopedServices> createScopedServices(std::uint64_t generation) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Services {
|
struct Services {
|
||||||
IBuzzer* buzzer = nullptr;
|
IBuzzer* buzzer = nullptr;
|
||||||
IBatteryMonitor* battery = nullptr;
|
IBatteryMonitor* battery = nullptr;
|
||||||
IStorage* storage = nullptr;
|
IStorage* storage = nullptr;
|
||||||
IRandom* random = nullptr;
|
IRandom* random = nullptr;
|
||||||
IHighResClock* highResClock = nullptr;
|
IHighResClock* highResClock = nullptr;
|
||||||
IFilesystem* filesystem = nullptr;
|
IFilesystem* filesystem = nullptr;
|
||||||
IEventBus* eventBus = nullptr;
|
IEventBus* eventBus = nullptr;
|
||||||
ITimerService* timer = nullptr;
|
ILoopHooks* loopHooks = nullptr;
|
||||||
ILoopHooks* loopHooks = nullptr;
|
|
||||||
INotificationCenter* notifications = nullptr;
|
INotificationCenter* notifications = nullptr;
|
||||||
|
IAppServiceProvider* appServices = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
@@ -10,12 +10,9 @@ class ITimerService {
|
|||||||
public:
|
public:
|
||||||
virtual ~ITimerService() = default;
|
virtual ~ITimerService() = default;
|
||||||
|
|
||||||
virtual TimerClientId acquireClient() = 0;
|
virtual AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) = 0;
|
||||||
virtual void releaseClient(TimerClientId clientId) = 0;
|
virtual void cancelTimer(AppTimerHandle handle) = 0;
|
||||||
virtual AppTimerHandle scheduleTimer(TimerClientId clientId, std::uint32_t delay_ms, bool repeat) = 0;
|
virtual void cancelAllTimers() = 0;
|
||||||
virtual void cancelTimer(TimerClientId clientId, AppTimerHandle handle) = 0;
|
|
||||||
virtual void cancelAllTimers(TimerClientId clientId) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cardboy/sdk/event_bus.hpp"
|
|
||||||
#include "cardboy/sdk/platform.hpp"
|
#include "cardboy/sdk/platform.hpp"
|
||||||
#include "cardboy/sdk/services.hpp"
|
#include "cardboy/sdk/services.hpp"
|
||||||
|
|
||||||
@@ -12,6 +11,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -113,33 +113,39 @@ public:
|
|||||||
void signal(std::uint32_t bits) override;
|
void signal(std::uint32_t bits) override;
|
||||||
void signalFromISR(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;
|
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:
|
private:
|
||||||
DesktopRuntime& runtime;
|
DesktopRuntime& runtime;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::uint32_t pendingBits = 0;
|
std::uint32_t pendingBits = 0;
|
||||||
std::mutex eventMutex;
|
};
|
||||||
std::deque<cardboy::sdk::AppEvent> 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<cardboy::sdk::AppEvent> queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DesktopTimerService final : public cardboy::sdk::ITimerService {
|
class DesktopTimerService final : public cardboy::sdk::ITimerService {
|
||||||
public:
|
public:
|
||||||
DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus);
|
DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IAppEventBus& appBus);
|
||||||
~DesktopTimerService() override;
|
~DesktopTimerService() override;
|
||||||
|
|
||||||
cardboy::sdk::TimerClientId acquireClient() override;
|
cardboy::sdk::AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) override;
|
||||||
void releaseClient(cardboy::sdk::TimerClientId clientId) override;
|
void cancelTimer(cardboy::sdk::AppTimerHandle handle) override;
|
||||||
cardboy::sdk::AppTimerHandle scheduleTimer(cardboy::sdk::TimerClientId clientId, std::uint32_t delay_ms,
|
void cancelAllTimers() override;
|
||||||
bool repeat) override;
|
|
||||||
void cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) override;
|
|
||||||
void cancelAllTimers(cardboy::sdk::TimerClientId clientId) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct TimerRecord {
|
struct TimerRecord {
|
||||||
cardboy::sdk::TimerClientId clientId = cardboy::sdk::kInvalidTimerClient;
|
|
||||||
cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer;
|
cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer;
|
||||||
std::chrono::steady_clock::time_point due;
|
std::chrono::steady_clock::time_point due;
|
||||||
std::chrono::milliseconds interval{0};
|
std::chrono::milliseconds interval{0};
|
||||||
@@ -151,15 +157,30 @@ private:
|
|||||||
void wakeWorker();
|
void wakeWorker();
|
||||||
void cleanupInactive();
|
void cleanupInactive();
|
||||||
|
|
||||||
DesktopRuntime& runtime;
|
DesktopRuntime& runtime;
|
||||||
cardboy::sdk::IEventBus& eventBus;
|
cardboy::sdk::IAppEventBus& appEventBus;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::vector<TimerRecord> timers;
|
std::vector<TimerRecord> timers;
|
||||||
bool stopWorker = false;
|
bool stopWorker = false;
|
||||||
std::thread worker;
|
std::thread worker;
|
||||||
cardboy::sdk::AppTimerHandle nextHandle = 1;
|
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<cardboy::sdk::AppScopedServices> createScopedServices(std::uint64_t generation) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ScopedServices final : cardboy::sdk::AppScopedServices {
|
||||||
|
std::unique_ptr<DesktopScopedEventBus> ownedEventBus;
|
||||||
|
std::unique_ptr<DesktopTimerService> ownedTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
DesktopRuntime& runtime;
|
||||||
|
cardboy::sdk::IEventBus& eventBus;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DesktopFramebuffer final : public cardboy::sdk::FramebufferFacade<DesktopFramebuffer> {
|
class DesktopFramebuffer final : public cardboy::sdk::FramebufferFacade<DesktopFramebuffer> {
|
||||||
@@ -240,7 +261,7 @@ private:
|
|||||||
DesktopHighResClock highResService;
|
DesktopHighResClock highResService;
|
||||||
DesktopFilesystem filesystemService;
|
DesktopFilesystem filesystemService;
|
||||||
DesktopEventBus eventBusService;
|
DesktopEventBus eventBusService;
|
||||||
DesktopTimerService timerService;
|
DesktopAppServiceProvider appServiceProvider;
|
||||||
DesktopNotificationCenter notificationService;
|
DesktopNotificationCenter notificationService;
|
||||||
cardboy::sdk::Services services{};
|
cardboy::sdk::Services services{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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<std::mutex> lock(eventMutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
if (eventQueue.size() >= kDesktopEventQueueLimit)
|
if (queue.size() >= kDesktopEventQueueLimit)
|
||||||
eventQueue.pop_front();
|
queue.pop_front();
|
||||||
eventQueue.push_back(event);
|
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) {
|
bool DesktopScopedEventBus::pop(cardboy::sdk::AppEvent& outEvent) {
|
||||||
std::lock_guard<std::mutex> lock(eventMutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
if (eventQueue.empty())
|
if (queue.empty())
|
||||||
return false;
|
return false;
|
||||||
outEvent = eventQueue.front();
|
outEvent = queue.front();
|
||||||
eventQueue.pop_front();
|
queue.pop_front();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DesktopTimerService::DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus) : runtime(owner), eventBus(bus) {
|
void DesktopScopedEventBus::clear() {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopTimerService::DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IAppEventBus& appBus) :
|
||||||
|
runtime(owner), appEventBus(appBus) {
|
||||||
worker = std::thread(&DesktopTimerService::workerLoop, this);
|
worker = std::thread(&DesktopTimerService::workerLoop, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,33 +102,17 @@ DesktopTimerService::~DesktopTimerService() {
|
|||||||
cv.notify_all();
|
cv.notify_all();
|
||||||
if (worker.joinable())
|
if (worker.joinable())
|
||||||
worker.join();
|
worker.join();
|
||||||
|
cancelAllTimers();
|
||||||
}
|
}
|
||||||
|
|
||||||
cardboy::sdk::TimerClientId DesktopTimerService::acquireClient() {
|
cardboy::sdk::AppTimerHandle DesktopTimerService::scheduleTimer(std::uint32_t delay_ms, bool repeat) {
|
||||||
std::lock_guard<std::mutex> 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;
|
|
||||||
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
const auto effectiveDelayMs = std::chrono::milliseconds(delay_ms);
|
const auto effectiveDelayMs = std::chrono::milliseconds(delay_ms);
|
||||||
const auto dueTime = delay_ms == 0 ? now : now + effectiveDelayMs;
|
const auto dueTime = delay_ms == 0 ? now : now + effectiveDelayMs;
|
||||||
const auto interval = std::chrono::milliseconds(std::max<std::uint32_t>(1, repeat ? delay_ms : std::max(delay_ms, 1u)));
|
const auto interval = std::chrono::milliseconds(std::max<std::uint32_t>(1, repeat ? std::max(delay_ms, 1u)
|
||||||
|
: std::max(delay_ms, 1u)));
|
||||||
|
|
||||||
TimerRecord record{};
|
TimerRecord record{};
|
||||||
record.clientId = clientId;
|
|
||||||
record.repeat = repeat;
|
record.repeat = repeat;
|
||||||
record.interval = interval;
|
record.interval = interval;
|
||||||
record.due = dueTime;
|
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) {
|
void DesktopTimerService::cancelTimer(cardboy::sdk::AppTimerHandle handle) {
|
||||||
if (clientId == cardboy::sdk::kInvalidTimerClient || handle == cardboy::sdk::kInvalidAppTimer)
|
if (handle == cardboy::sdk::kInvalidAppTimer)
|
||||||
return;
|
return;
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
for (auto& record: timers) {
|
for (auto& record: timers) {
|
||||||
if (record.clientId == clientId && record.handle == handle)
|
if (record.handle == handle)
|
||||||
record.active = false;
|
record.active = false;
|
||||||
}
|
}
|
||||||
cleanupInactive();
|
cleanupInactive();
|
||||||
wakeWorker();
|
wakeWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DesktopTimerService::cancelAllTimers(cardboy::sdk::TimerClientId clientId) {
|
void DesktopTimerService::cancelAllTimers() {
|
||||||
if (clientId == cardboy::sdk::kInvalidTimerClient)
|
|
||||||
return;
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
for (auto& record: timers) {
|
for (auto& record: timers) {
|
||||||
if (record.clientId == clientId)
|
record.active = false;
|
||||||
record.active = false;
|
|
||||||
}
|
}
|
||||||
cleanupInactive();
|
cleanupInactive();
|
||||||
wakeWorker();
|
wakeWorker();
|
||||||
@@ -195,13 +184,12 @@ void DesktopTimerService::workerLoop() {
|
|||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
cardboy::sdk::AppTimerEvent timerEvent{};
|
cardboy::sdk::AppTimerEvent timerEvent{};
|
||||||
timerEvent.clientId = record.clientId;
|
timerEvent.handle = record.handle;
|
||||||
timerEvent.handle = record.handle;
|
|
||||||
|
|
||||||
cardboy::sdk::AppEvent event{};
|
cardboy::sdk::AppEvent event{};
|
||||||
event.timestamp_ms = runtime.clock.millis();
|
event.timestamp_ms = runtime.clock.millis();
|
||||||
event.data = timerEvent;
|
event.data = timerEvent;
|
||||||
eventBus.postEvent(event);
|
appEventBus.post(event);
|
||||||
|
|
||||||
lock.lock();
|
lock.lock();
|
||||||
continue;
|
continue;
|
||||||
@@ -218,6 +206,19 @@ void DesktopTimerService::cleanupInactive() {
|
|||||||
timers.end());
|
timers.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DesktopAppServiceProvider::DesktopAppServiceProvider(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus) :
|
||||||
|
runtime(owner), eventBus(bus) {}
|
||||||
|
|
||||||
|
std::unique_ptr<cardboy::sdk::AppScopedServices> DesktopAppServiceProvider::createScopedServices(std::uint64_t generation) {
|
||||||
|
(void)generation;
|
||||||
|
auto scoped = std::make_unique<ScopedServices>();
|
||||||
|
scoped->ownedEventBus = std::make_unique<DesktopScopedEventBus>(eventBus);
|
||||||
|
scoped->events = scoped->ownedEventBus.get();
|
||||||
|
scoped->ownedTimer = std::make_unique<DesktopTimerService>(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) {
|
bool DesktopStorage::readUint32(std::string_view ns, std::string_view key, std::uint32_t& out) {
|
||||||
auto it = data.find(composeKey(ns, key));
|
auto it = data.find(composeKey(ns, key));
|
||||||
if (it == data.end())
|
if (it == data.end())
|
||||||
@@ -441,7 +442,7 @@ DesktopRuntime::DesktopRuntime() :
|
|||||||
"Cardboy Desktop"),
|
"Cardboy Desktop"),
|
||||||
texture(), sprite(texture),
|
texture(), sprite(texture),
|
||||||
pixels(static_cast<std::size_t>(cardboy::sdk::kDisplayWidth * cardboy::sdk::kDisplayHeight) * 4, 0),
|
pixels(static_cast<std::size_t>(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);
|
window.setFramerateLimit(60);
|
||||||
if (!texture.resize(sf::Vector2u{cardboy::sdk::kDisplayWidth, cardboy::sdk::kDisplayHeight}))
|
if (!texture.resize(sf::Vector2u{cardboy::sdk::kDisplayWidth, cardboy::sdk::kDisplayHeight}))
|
||||||
throw std::runtime_error("Failed to allocate texture for desktop framebuffer");
|
throw std::runtime_error("Failed to allocate texture for desktop framebuffer");
|
||||||
@@ -457,7 +458,7 @@ DesktopRuntime::DesktopRuntime() :
|
|||||||
services.highResClock = &highResService;
|
services.highResClock = &highResService;
|
||||||
services.filesystem = &filesystemService;
|
services.filesystem = &filesystemService;
|
||||||
services.eventBus = &eventBusService;
|
services.eventBus = &eventBusService;
|
||||||
services.timer = &timerService;
|
services.appServices = &appServiceProvider;
|
||||||
services.loopHooks = nullptr;
|
services.loopHooks = nullptr;
|
||||||
services.notifications = ¬ificationService;
|
services.notifications = ¬ificationService;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -33,74 +34,52 @@ struct AppContext {
|
|||||||
|
|
||||||
[[nodiscard]] Services* getServices() const { return services; }
|
[[nodiscard]] Services* getServices() const { return services; }
|
||||||
|
|
||||||
[[nodiscard]] IBuzzer* buzzer() const { return services ? services->buzzer : nullptr; }
|
[[nodiscard]] IBuzzer* buzzer() const { return services ? services->buzzer : nullptr; }
|
||||||
[[nodiscard]] IBatteryMonitor* battery() const { return services ? services->battery : nullptr; }
|
[[nodiscard]] IBatteryMonitor* battery() const { return services ? services->battery : nullptr; }
|
||||||
[[nodiscard]] IStorage* storage() const { return services ? services->storage : nullptr; }
|
[[nodiscard]] IStorage* storage() const { return services ? services->storage : nullptr; }
|
||||||
[[nodiscard]] IRandom* random() const { return services ? services->random : nullptr; }
|
[[nodiscard]] IRandom* random() const { return services ? services->random : nullptr; }
|
||||||
[[nodiscard]] IHighResClock* highResClock() const { return services ? services->highResClock : nullptr; }
|
[[nodiscard]] IHighResClock* highResClock() const { return services ? services->highResClock : nullptr; }
|
||||||
[[nodiscard]] IFilesystem* filesystem() const { return services ? services->filesystem : nullptr; }
|
[[nodiscard]] IFilesystem* filesystem() const { return services ? services->filesystem : nullptr; }
|
||||||
[[nodiscard]] IEventBus* eventBus() const { return services ? services->eventBus : nullptr; }
|
[[nodiscard]] AppScopedServices* appServices() const { return _scopedServices; }
|
||||||
[[nodiscard]] ITimerService* timerService() const { return services ? services->timer : nullptr; }
|
[[nodiscard]] IEventBus* eventBus() const { return services ? services->eventBus : nullptr; }
|
||||||
[[nodiscard]] ILoopHooks* loopHooks() const { return services ? services->loopHooks : nullptr; }
|
[[nodiscard]] ITimerService* timer() const { return _scopedServices ? _scopedServices->timer : nullptr; }
|
||||||
|
[[nodiscard]] ILoopHooks* loopHooks() const { return services ? services->loopHooks : nullptr; }
|
||||||
[[nodiscard]] INotificationCenter* notificationCenter() const {
|
[[nodiscard]] INotificationCenter* notificationCenter() const {
|
||||||
return services ? services->notifications : nullptr;
|
return services ? services->notifications : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestAppSwitchByIndex(std::size_t index) {
|
void requestAppSwitchByIndex(std::size_t index) {
|
||||||
pendingAppIndex = index;
|
_pendingAppIndex = index;
|
||||||
pendingAppName.clear();
|
_pendingAppName.clear();
|
||||||
pendingSwitchByName = false;
|
_pendingSwitchByName = false;
|
||||||
pendingSwitch = true;
|
_pendingSwitch = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestAppSwitchByName(std::string_view name) {
|
void requestAppSwitchByName(std::string_view name) {
|
||||||
pendingAppName.assign(name.begin(), name.end());
|
_pendingAppName.assign(name.begin(), name.end());
|
||||||
pendingSwitchByName = true;
|
_pendingSwitchByName = true;
|
||||||
pendingSwitch = true;
|
_pendingSwitch = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool hasPendingAppSwitch() const { return pendingSwitch; }
|
[[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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class AppSystem;
|
friend class AppSystem;
|
||||||
bool pendingSwitch = false;
|
bool _pendingSwitch = false;
|
||||||
bool pendingSwitchByName = false;
|
bool _pendingSwitchByName = false;
|
||||||
std::size_t pendingAppIndex = 0;
|
std::size_t _pendingAppIndex = 0;
|
||||||
std::string pendingAppName;
|
std::string _pendingAppName;
|
||||||
TimerClientId timerClientId = kInvalidTimerClient;
|
AppScopedServices* _scopedServices = nullptr;
|
||||||
|
|
||||||
|
void setScopedServices(AppScopedServices* services) { _scopedServices = services; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class IApp {
|
class IApp {
|
||||||
public:
|
public:
|
||||||
virtual ~IApp() = default;
|
virtual ~IApp() = default;
|
||||||
virtual void onStart() {}
|
virtual void onStart() {}
|
||||||
virtual void onStop() {}
|
virtual void onStop() {}
|
||||||
virtual void handleEvent(const AppEvent& event) = 0;
|
virtual std::optional<std::uint32_t> handleEvent(const AppEvent& event) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IAppFactory {
|
class IAppFactory {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cardboy/sdk/event_bus.hpp>
|
|
||||||
#include "app_framework.hpp"
|
#include "app_framework.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -16,36 +15,31 @@ public:
|
|||||||
~AppSystem();
|
~AppSystem();
|
||||||
|
|
||||||
void registerApp(std::unique_ptr<IAppFactory> factory);
|
void registerApp(std::unique_ptr<IAppFactory> factory);
|
||||||
bool startApp(const std::string& name);
|
void startApp(const std::string& name);
|
||||||
bool startAppByIndex(std::size_t index);
|
void startAppByIndex(std::size_t index);
|
||||||
|
|
||||||
void run();
|
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]] const IAppFactory* factoryAt(std::size_t index) const;
|
||||||
[[nodiscard]] std::size_t indexOfFactory(const IAppFactory* factory) 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 IApp* currentApp() const { return _current.get(); }
|
||||||
[[nodiscard]] const IAppFactory* currentFactory() const { return activeFactory; }
|
[[nodiscard]] const IAppFactory* currentFactory() const { return _activeFactory; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct AppContext;
|
void handlePendingSwitchRequest();
|
||||||
|
std::unique_ptr<AppScopedServices> createAppScopedServices(std::uint64_t generation);
|
||||||
|
|
||||||
void dispatchEvent(const AppEvent& event);
|
AppContext _context;
|
||||||
bool handlePendingSwitchRequest();
|
std::vector<std::unique_ptr<IAppFactory>> _factories;
|
||||||
void attachTimerClient();
|
std::unique_ptr<IApp> _current;
|
||||||
void detachTimerClient();
|
IAppFactory* _activeFactory = nullptr;
|
||||||
void processEventBusEvents();
|
std::size_t _activeIndex = static_cast<std::size_t>(-1);
|
||||||
|
std::unique_ptr<AppScopedServices> _scopedServices;
|
||||||
AppContext context;
|
std::uint64_t _nextScopedGeneration = 1;
|
||||||
std::vector<std::unique_ptr<IAppFactory>> factories;
|
std::optional<std::uint32_t> _currentTimeout;
|
||||||
std::unique_ptr<IApp> current;
|
|
||||||
IAppFactory* activeFactory = nullptr;
|
|
||||||
std::size_t activeIndex = static_cast<std::size_t>(-1);
|
|
||||||
TimerClientId timerClientId = kInvalidTimerClient;
|
|
||||||
InputState lastInputState{};
|
|
||||||
bool suppressInputs = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ public:
|
|||||||
static void invokePreSend(void* framebuffer);
|
static void invokePreSend(void* framebuffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static PreSendHook hook_;
|
static PreSendHook _hook;
|
||||||
static void* userData_;
|
static void* _userData;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ class StatusBar {
|
|||||||
public:
|
public:
|
||||||
static StatusBar& instance();
|
static StatusBar& instance();
|
||||||
|
|
||||||
void setServices(Services* services) { services_ = services; }
|
void setServices(Services* services) { _services = services; }
|
||||||
|
|
||||||
void setEnabled(bool value);
|
void setEnabled(bool value);
|
||||||
void toggle();
|
void toggle();
|
||||||
[[nodiscard]] bool isEnabled() const { return enabled_; }
|
[[nodiscard]] bool isEnabled() const { return _enabled; }
|
||||||
|
|
||||||
void setCurrentAppName(std::string_view name);
|
void setCurrentAppName(std::string_view name);
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public:
|
|||||||
|
|
||||||
template<typename Framebuffer>
|
template<typename Framebuffer>
|
||||||
void renderIfEnabled(Framebuffer& fb) {
|
void renderIfEnabled(Framebuffer& fb) {
|
||||||
if (!enabled_)
|
if (!_enabled)
|
||||||
return;
|
return;
|
||||||
renderBar(fb);
|
renderBar(fb);
|
||||||
}
|
}
|
||||||
@@ -84,9 +84,9 @@ private:
|
|||||||
[[nodiscard]] std::string prepareLeftText(int displayWidth) const;
|
[[nodiscard]] std::string prepareLeftText(int displayWidth) const;
|
||||||
[[nodiscard]] std::string prepareRightText() const;
|
[[nodiscard]] std::string prepareRightText() const;
|
||||||
|
|
||||||
bool enabled_ = false;
|
bool _enabled = false;
|
||||||
Services* services_ = nullptr;
|
Services* _services = nullptr;
|
||||||
std::string appName_{};
|
std::string _appName{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
@@ -3,18 +3,11 @@
|
|||||||
#include "cardboy/sdk/status_bar.hpp"
|
#include "cardboy/sdk/status_bar.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace cardboy::sdk {
|
namespace cardboy::sdk {
|
||||||
namespace {
|
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<typename Framebuffer>
|
template<typename Framebuffer>
|
||||||
void statusBarPreSendHook(void* framebuffer, void* userData) {
|
void statusBarPreSendHook(void* framebuffer, void* userData) {
|
||||||
@@ -25,191 +18,128 @@ void statusBarPreSendHook(void* framebuffer, void* userData) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AppSystem::AppSystem(AppContext ctx) : context(std::move(ctx)) {
|
AppSystem::AppSystem(AppContext ctx) : _context(std::move(ctx)) {
|
||||||
context.system = this;
|
_context.system = this;
|
||||||
auto& statusBar = StatusBar::instance();
|
auto& statusBar = StatusBar::instance();
|
||||||
statusBar.setServices(context.services);
|
statusBar.setServices(_context.services);
|
||||||
using FBType = typename AppContext::Framebuffer;
|
FramebufferHooks::setPreSendHook(&statusBarPreSendHook<AppContext::Framebuffer>, &statusBar);
|
||||||
FramebufferHooks::setPreSendHook(&statusBarPreSendHook<FBType>, &statusBar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AppSystem::~AppSystem() {
|
AppSystem::~AppSystem() { FramebufferHooks::clearPreSendHook(); }
|
||||||
detachTimerClient();
|
|
||||||
FramebufferHooks::clearPreSendHook();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppSystem::registerApp(std::unique_ptr<IAppFactory> factory) {
|
void AppSystem::registerApp(std::unique_ptr<IAppFactory> factory) {
|
||||||
if (!factory)
|
assert(factory);
|
||||||
return;
|
_factories.emplace_back(std::move(factory));
|
||||||
factories.emplace_back(std::move(factory));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppSystem::startApp(const std::string& name) {
|
void AppSystem::startApp(const std::string& name) {
|
||||||
for (std::size_t i = 0; i < factories.size(); ++i) {
|
for (std::size_t i = 0; i < _factories.size(); ++i) {
|
||||||
if (factories[i]->name() == name)
|
if (_factories[i]->name() == name)
|
||||||
return startAppByIndex(i);
|
startAppByIndex(i);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppSystem::startAppByIndex(std::size_t index) {
|
void AppSystem::startAppByIndex(std::size_t index) {
|
||||||
if (index >= factories.size())
|
assert(index < _factories.size());
|
||||||
return false;
|
|
||||||
|
|
||||||
context.system = this;
|
if (_current) {
|
||||||
auto& factory = factories[index];
|
_current->onStop();
|
||||||
auto app = factory->create(context);
|
_current.reset();
|
||||||
if (!app)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (current) {
|
|
||||||
current->onStop();
|
|
||||||
current.reset();
|
|
||||||
detachTimerClient();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activeFactory = factory.get();
|
_context.system = this;
|
||||||
activeIndex = index;
|
|
||||||
context.pendingSwitch = false;
|
auto& factory = _factories[index];
|
||||||
context.pendingSwitchByName = false;
|
|
||||||
context.pendingAppName.clear();
|
const std::uint64_t newGeneration = _nextScopedGeneration++;
|
||||||
current = std::move(app);
|
|
||||||
attachTimerClient();
|
auto app = factory->create(_context);
|
||||||
lastInputState = context.input.readState();
|
assert(app);
|
||||||
suppressInputs = true;
|
|
||||||
StatusBar::instance().setServices(context.services);
|
_scopedServices.reset();
|
||||||
StatusBar::instance().setCurrentAppName(activeFactory ? activeFactory->name() : "");
|
auto scoped = createAppScopedServices(newGeneration);
|
||||||
current->onStart();
|
_scopedServices = std::move(scoped);
|
||||||
return true;
|
_context.setScopedServices(_scopedServices.get());
|
||||||
|
|
||||||
|
_activeFactory = factory.get();
|
||||||
|
_activeIndex = index;
|
||||||
|
_context._pendingSwitch = false;
|
||||||
|
_context._pendingSwitchByName = false;
|
||||||
|
_context._pendingAppName.clear();
|
||||||
|
_current = std::move(app);
|
||||||
|
_currentTimeout = std::nullopt;
|
||||||
|
StatusBar::instance().setServices(_context.services);
|
||||||
|
StatusBar::instance().setCurrentAppName(_activeFactory ? _activeFactory->name() : "");
|
||||||
|
_current->onStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSystem::run() {
|
void AppSystem::run() {
|
||||||
if (!current) {
|
if (!_current) {
|
||||||
if (factories.empty() || !startAppByIndex(0))
|
assert(!_factories.empty());
|
||||||
return;
|
startAppByIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
auto* eventBus = context.eventBus();
|
if (auto* hooks = _context.loopHooks())
|
||||||
if (!eventBus)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (auto* hooks = context.loopHooks())
|
|
||||||
hooks->onLoopIteration();
|
hooks->onLoopIteration();
|
||||||
|
|
||||||
processEventBusEvents();
|
AppEvent event;
|
||||||
if (handlePendingSwitchRequest())
|
auto event_opt = _context.eventBus()->pop(_currentTimeout);
|
||||||
continue;
|
if (!event_opt) {
|
||||||
|
event = AppEvent{_context.clock.millis(), AppTimeoutEvent{}};
|
||||||
const std::uint32_t now = context.clock.millis();
|
} else {
|
||||||
const InputState inputNow = context.input.readState();
|
event = *event_opt;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handlePendingSwitchRequest())
|
if (const auto* btn = event.button()) {
|
||||||
|
const bool consumedByStatusToggle = StatusBar::instance().handleToggleInput(btn->current, btn->previous);
|
||||||
|
if (consumedByStatusToggle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_currentTimeout = _current->handleEvent(event);
|
||||||
|
if (_context._pendingSwitch) {
|
||||||
|
handlePendingSwitchRequest();
|
||||||
continue;
|
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 {
|
const IAppFactory* AppSystem::factoryAt(std::size_t index) const {
|
||||||
if (index >= factories.size())
|
if (index >= _factories.size())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return factories[index].get();
|
return _factories[index].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t AppSystem::indexOfFactory(const IAppFactory* factory) const {
|
std::size_t AppSystem::indexOfFactory(const IAppFactory* factory) const {
|
||||||
if (!factory)
|
if (!factory)
|
||||||
return static_cast<std::size_t>(-1);
|
return static_cast<std::size_t>(-1);
|
||||||
for (std::size_t i = 0; i < factories.size(); ++i) {
|
for (std::size_t i = 0; i < _factories.size(); ++i) {
|
||||||
if (factories[i].get() == factory)
|
if (_factories[i].get() == factory)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
return static_cast<std::size_t>(-1);
|
return static_cast<std::size_t>(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSystem::dispatchEvent(const AppEvent& event) {
|
void AppSystem::handlePendingSwitchRequest() {
|
||||||
if (!current)
|
assert(_context._pendingSwitch);
|
||||||
return;
|
const bool byName = _context._pendingSwitchByName;
|
||||||
|
const std::size_t reqIndex = _context._pendingAppIndex;
|
||||||
if (const auto* timer = event.timer()) {
|
const std::string reqName = _context._pendingAppName;
|
||||||
if (timer->clientId != timerClientId)
|
_context._pendingSwitch = false;
|
||||||
return;
|
_context._pendingSwitchByName = false;
|
||||||
}
|
_context._pendingAppName.clear();
|
||||||
|
|
||||||
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();
|
|
||||||
bool switched = false;
|
bool switched = false;
|
||||||
if (byName)
|
if (byName)
|
||||||
switched = startApp(reqName);
|
startApp(reqName);
|
||||||
else
|
else
|
||||||
switched = startAppByIndex(reqIndex);
|
startAppByIndex(reqIndex);
|
||||||
return switched;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSystem::attachTimerClient() {
|
std::unique_ptr<AppScopedServices> AppSystem::createAppScopedServices(std::uint64_t generation) {
|
||||||
auto* timer = context.timerService();
|
if (!_context.services || !_context.services->appServices)
|
||||||
if (!timer) {
|
return nullptr;
|
||||||
timerClientId = kInvalidTimerClient;
|
return _context.services->appServices->createScopedServices(generation);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
@@ -2,22 +2,22 @@
|
|||||||
|
|
||||||
namespace cardboy::sdk {
|
namespace cardboy::sdk {
|
||||||
|
|
||||||
FramebufferHooks::PreSendHook FramebufferHooks::hook_ = nullptr;
|
FramebufferHooks::PreSendHook FramebufferHooks::_hook = nullptr;
|
||||||
void* FramebufferHooks::userData_ = nullptr;
|
void* FramebufferHooks::_userData = nullptr;
|
||||||
|
|
||||||
void FramebufferHooks::setPreSendHook(PreSendHook hook, void* userData) {
|
void FramebufferHooks::setPreSendHook(PreSendHook hook, void* userData) {
|
||||||
hook_ = hook;
|
_hook = hook;
|
||||||
userData_ = userData;
|
_userData = userData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramebufferHooks::clearPreSendHook() {
|
void FramebufferHooks::clearPreSendHook() {
|
||||||
hook_ = nullptr;
|
_hook = nullptr;
|
||||||
userData_ = nullptr;
|
_userData = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramebufferHooks::invokePreSend(void* framebuffer) {
|
void FramebufferHooks::invokePreSend(void* framebuffer) {
|
||||||
if (hook_)
|
if (_hook)
|
||||||
hook_(framebuffer, userData_);
|
_hook(framebuffer, _userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ StatusBar& StatusBar::instance() {
|
|||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBar::setEnabled(bool value) { enabled_ = value; }
|
void StatusBar::setEnabled(bool value) { _enabled = value; }
|
||||||
|
|
||||||
void StatusBar::toggle() {
|
void StatusBar::toggle() {
|
||||||
enabled_ = !enabled_;
|
_enabled = !_enabled;
|
||||||
if (services_ && services_->buzzer)
|
if (_services && _services->buzzer)
|
||||||
services_->buzzer->beepMove();
|
_services->buzzer->beepMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBar::setCurrentAppName(std::string_view name) {
|
void StatusBar::setCurrentAppName(std::string_view name) {
|
||||||
appName_.assign(name.begin(), name.end());
|
_appName.assign(name.begin(), name.end());
|
||||||
std::transform(appName_.begin(), appName_.end(), appName_.begin(),
|
std::transform(_appName.begin(), _appName.end(), _appName.begin(),
|
||||||
[](unsigned char ch) { return static_cast<char>(std::toupper(ch)); });
|
[](unsigned char ch) { return static_cast<char>(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 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);
|
int maxWidth = std::max(0, displayWidth - 32);
|
||||||
while (!text.empty() && font16x8::measureText(text, 1, 1) > maxWidth)
|
while (!text.empty() && font16x8::measureText(text, 1, 1) > maxWidth)
|
||||||
text.pop_back();
|
text.pop_back();
|
||||||
@@ -45,14 +45,14 @@ std::string StatusBar::prepareLeftText(int displayWidth) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string StatusBar::prepareRightText() const {
|
std::string StatusBar::prepareRightText() const {
|
||||||
if (!services_)
|
if (!_services)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::string right;
|
std::string right;
|
||||||
if (services_->battery && services_->battery->hasData()) {
|
if (_services->battery && _services->battery->hasData()) {
|
||||||
const float current = services_->battery->current();
|
const float current = _services->battery->current();
|
||||||
const float chargeMah = services_->battery->charge();
|
const float chargeMah = _services->battery->charge();
|
||||||
const float fallbackV = services_->battery->voltage();
|
const float fallbackV = _services->battery->voltage();
|
||||||
char buf[64];
|
char buf[64];
|
||||||
if (std::isfinite(current) && std::isfinite(chargeMah)) {
|
if (std::isfinite(current) && std::isfinite(chargeMah)) {
|
||||||
std::snprintf(buf, sizeof(buf), "cur %.2fmA chr %.2fmAh", static_cast<double>(current),
|
std::snprintf(buf, sizeof(buf), "cur %.2fmA chr %.2fmAh", static_cast<double>(current),
|
||||||
@@ -63,7 +63,7 @@ std::string StatusBar::prepareRightText() const {
|
|||||||
right.assign(buf);
|
right.assign(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (services_->buzzer && services_->buzzer->isMuted()) {
|
if (_services->buzzer && _services->buzzer->isMuted()) {
|
||||||
if (!right.empty())
|
if (!right.empty())
|
||||||
right.append(" ");
|
right.append(" ");
|
||||||
right.append("MUTE");
|
right.append("MUTE");
|
||||||
|
|||||||
@@ -613,9 +613,9 @@ CONFIG_PARTITION_TABLE_MD5=y
|
|||||||
#
|
#
|
||||||
# Compiler options
|
# Compiler options
|
||||||
#
|
#
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set
|
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
|
# 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_NONE is not set
|
||||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
|
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
|
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
|
||||||
@@ -1714,6 +1714,7 @@ CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
|
|||||||
#
|
#
|
||||||
# Port
|
# Port
|
||||||
#
|
#
|
||||||
|
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
|
||||||
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
|
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
|
||||||
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
|
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
|
||||||
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
|
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
|
||||||
@@ -1761,16 +1762,16 @@ CONFIG_HAL_WDT_USE_ROM_IMPL=y
|
|||||||
#
|
#
|
||||||
# Heap memory debugging
|
# 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_LIGHT is not set
|
||||||
# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set
|
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||||
CONFIG_HEAP_TRACING_OFF=y
|
CONFIG_HEAP_TRACING_OFF=y
|
||||||
# CONFIG_HEAP_TRACING_STANDALONE is not set
|
# CONFIG_HEAP_TRACING_STANDALONE is not set
|
||||||
# CONFIG_HEAP_TRACING_TOHOST is not set
|
# CONFIG_HEAP_TRACING_TOHOST is not set
|
||||||
# CONFIG_HEAP_USE_HOOKS is not set
|
# CONFIG_HEAP_USE_HOOKS is not set
|
||||||
# CONFIG_HEAP_TASK_TRACKING is not set
|
# CONFIG_HEAP_TASK_TRACKING is not set
|
||||||
# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS 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
|
# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set
|
||||||
# end of Heap memory debugging
|
# end of Heap memory debugging
|
||||||
|
|
||||||
@@ -2463,9 +2464,9 @@ CONFIG_LOG_BOOTLOADER_LEVEL=3
|
|||||||
CONFIG_FLASHMODE_DIO=y
|
CONFIG_FLASHMODE_DIO=y
|
||||||
# CONFIG_FLASHMODE_DOUT is not set
|
# CONFIG_FLASHMODE_DOUT is not set
|
||||||
CONFIG_MONITOR_BAUD=115200
|
CONFIG_MONITOR_BAUD=115200
|
||||||
# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
|
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
|
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
|
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
|
||||||
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
|
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
|
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
|
||||||
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
|
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
|
||||||
|
|||||||
@@ -599,13 +599,13 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
|
|||||||
#
|
#
|
||||||
# Partition Table
|
# 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_SINGLE_APP_LARGE is not set
|
||||||
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
|
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
|
||||||
# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE 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_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_OFFSET=0x8000
|
||||||
CONFIG_PARTITION_TABLE_MD5=y
|
CONFIG_PARTITION_TABLE_MD5=y
|
||||||
# end of Partition Table
|
# end of Partition Table
|
||||||
@@ -613,9 +613,9 @@ CONFIG_PARTITION_TABLE_MD5=y
|
|||||||
#
|
#
|
||||||
# Compiler options
|
# Compiler options
|
||||||
#
|
#
|
||||||
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
|
# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_SIZE 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_NONE is not set
|
||||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
|
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
|
# 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_RT_LIB_NAME="gcc"
|
||||||
CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y
|
CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y
|
||||||
# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set
|
# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set
|
||||||
CONFIG_COMPILER_STATIC_ANALYZER=y
|
# CONFIG_COMPILER_STATIC_ANALYZER is not set
|
||||||
# end of Compiler options
|
# end of Compiler options
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -695,7 +695,7 @@ CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y
|
|||||||
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
|
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
|
||||||
CONFIG_BT_NIMBLE_GATT_CLIENT=y
|
CONFIG_BT_NIMBLE_GATT_CLIENT=y
|
||||||
CONFIG_BT_NIMBLE_GATT_SERVER=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_SMP_ID_RESET is not set
|
||||||
CONFIG_BT_NIMBLE_SECURITY_ENABLE=y
|
CONFIG_BT_NIMBLE_SECURITY_ENABLE=y
|
||||||
CONFIG_BT_NIMBLE_SM_LEGACY=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_TIMER_QUEUE_LENGTH=10
|
||||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||||
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1
|
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_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_USE_TICKLESS_IDLE=y
|
||||||
CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
|
CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
|
||||||
# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
|
# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
|
||||||
@@ -1710,7 +1714,6 @@ CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
|
|||||||
#
|
#
|
||||||
# Port
|
# Port
|
||||||
#
|
#
|
||||||
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
|
|
||||||
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
|
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
|
||||||
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
|
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
|
||||||
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
|
# 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_LVL1=y
|
||||||
# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set
|
# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set
|
||||||
CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y
|
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_PLACE_FUNCTIONS_INTO_FLASH is not set
|
||||||
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
|
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
|
||||||
# end of Port
|
# end of Port
|
||||||
@@ -2410,6 +2414,211 @@ CONFIG_WL_SECTOR_SIZE=4096
|
|||||||
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
|
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
|
||||||
CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
|
CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
|
||||||
CONFIG_WIFI_PROV_BLE_SEC_CONN=y
|
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
|
# end of Component config
|
||||||
|
|
||||||
# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set
|
# 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
|
||||||
|
|||||||
Reference in New Issue
Block a user