mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 15:17:48 +01:00
better timers
This commit is contained in:
@@ -4,6 +4,7 @@ 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,11 @@
|
|||||||
#ifndef BUTTONS_HPP
|
#ifndef BUTTONS_HPP
|
||||||
#define BUTTONS_HPP
|
#define BUTTONS_HPP
|
||||||
|
|
||||||
|
#include "cardboy/sdk/event_bus.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BTN_START = 1 << 1,
|
BTN_START = 1 << 1,
|
||||||
@@ -25,16 +27,18 @@ public:
|
|||||||
static Buttons& get();
|
static Buttons& get();
|
||||||
void pooler(); // FIXME:
|
void pooler(); // FIXME:
|
||||||
uint8_t get_pressed();
|
uint8_t get_pressed();
|
||||||
void install_isr();
|
void install_isr();
|
||||||
void register_listener(TaskHandle_t task);
|
void register_listener(TaskHandle_t task);
|
||||||
|
void setEventBus(cardboy::sdk::IEventBus* bus);
|
||||||
|
|
||||||
TaskHandle_t _pooler_task;
|
TaskHandle_t _pooler_task;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Buttons();
|
Buttons();
|
||||||
|
|
||||||
volatile uint8_t _current;
|
volatile uint8_t _current;
|
||||||
volatile TaskHandle_t _listener = nullptr;
|
volatile TaskHandle_t _listener = nullptr;
|
||||||
|
cardboy::sdk::IEventBus* _eventBus = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cardboy/sdk/event_bus.hpp>
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
#include "freertos/timers.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 scheduleTimerSignal(std::uint32_t delay_ms) override;
|
||||||
|
void cancelTimerSignal() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
EventGroupHandle_t group;
|
||||||
|
TimerHandle_t timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cardboy::backend::esp
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#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"
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ private:
|
|||||||
std::unique_ptr<HighResClockService> highResClockService;
|
std::unique_ptr<HighResClockService> highResClockService;
|
||||||
std::unique_ptr<PowerService> powerService;
|
std::unique_ptr<PowerService> powerService;
|
||||||
std::unique_ptr<FilesystemService> filesystemService;
|
std::unique_ptr<FilesystemService> filesystemService;
|
||||||
|
std::unique_ptr<EventBus> eventBus;
|
||||||
|
|
||||||
cardboy::sdk::Services services{};
|
cardboy::sdk::Services services{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,14 +6,15 @@
|
|||||||
|
|
||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
#include <esp_err.h>
|
#include <esp_err.h>
|
||||||
#include "cardboy/backend/esp/power_helper.hpp"
|
|
||||||
#include <rom/ets_sys.h>
|
#include <rom/ets_sys.h>
|
||||||
|
#include "cardboy/backend/esp/power_helper.hpp"
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
#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 = {
|
||||||
@@ -92,9 +93,13 @@ void Buttons::pooler() {
|
|||||||
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)
|
if (_listener)
|
||||||
xTaskNotifyGive(_listener);
|
xTaskNotifyGive(_listener);
|
||||||
|
if (_eventBus)
|
||||||
|
_eventBus->signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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); }
|
||||||
|
|
||||||
void Buttons::register_listener(TaskHandle_t task) { _listener = task; }
|
void Buttons::register_listener(TaskHandle_t task) { _listener = task; }
|
||||||
|
|
||||||
|
void Buttons::setEventBus(cardboy::sdk::IEventBus* bus) { _eventBus = bus; }
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ EspRuntime::EspRuntime() : framebuffer(), input(), clock() {
|
|||||||
highResClockService = std::make_unique<HighResClockService>();
|
highResClockService = std::make_unique<HighResClockService>();
|
||||||
powerService = std::make_unique<PowerService>();
|
powerService = std::make_unique<PowerService>();
|
||||||
filesystemService = std::make_unique<FilesystemService>();
|
filesystemService = std::make_unique<FilesystemService>();
|
||||||
|
eventBus = std::make_unique<EventBus>();
|
||||||
|
|
||||||
services.buzzer = buzzerService.get();
|
services.buzzer = buzzerService.get();
|
||||||
services.battery = batteryService.get();
|
services.battery = batteryService.get();
|
||||||
@@ -146,6 +147,9 @@ EspRuntime::EspRuntime() : framebuffer(), input(), clock() {
|
|||||||
services.highResClock = highResClockService.get();
|
services.highResClock = highResClockService.get();
|
||||||
services.powerManager = powerService.get();
|
services.powerManager = powerService.get();
|
||||||
services.filesystem = filesystemService.get();
|
services.filesystem = filesystemService.get();
|
||||||
|
services.eventBus = eventBus.get();
|
||||||
|
|
||||||
|
Buttons::get().setEventBus(eventBus.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
EspRuntime::~EspRuntime() = default;
|
EspRuntime::~EspRuntime() = default;
|
||||||
|
|||||||
81
Firmware/components/backend-esp/src/event_bus.cpp
Normal file
81
Firmware/components/backend-esp/src/event_bus.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static void timerCallback(TimerHandle_t handle) {
|
||||||
|
auto* bus = static_cast<EventBus*>(pvTimerGetTimerID(handle));
|
||||||
|
if (bus)
|
||||||
|
bus->signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||||
|
}
|
||||||
|
|
||||||
|
EventBus::EventBus() :
|
||||||
|
group(xEventGroupCreate()), timer(xTimerCreate("EventBusTimer", pdMS_TO_TICKS(1), pdFALSE, this, timerCallback)) {}
|
||||||
|
|
||||||
|
EventBus::~EventBus() {
|
||||||
|
if (timer)
|
||||||
|
xTimerDelete(timer, portMAX_DELAY);
|
||||||
|
if (group)
|
||||||
|
vEventGroupDelete(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
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::scheduleTimerSignal(std::uint32_t delay_ms) {
|
||||||
|
if (!timer)
|
||||||
|
return;
|
||||||
|
xTimerStop(timer, 0);
|
||||||
|
|
||||||
|
if (delay_ms == cardboy::sdk::IEventBus::kWaitForever)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (delay_ms == 0) {
|
||||||
|
signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TickType_t ticks = std::max<TickType_t>(pdMS_TO_TICKS(delay_ms), 1);
|
||||||
|
if (xTimerChangePeriod(timer, ticks, 0) == pdPASS)
|
||||||
|
xTimerStart(timer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventBus::cancelTimerSignal() {
|
||||||
|
if (!timer)
|
||||||
|
return;
|
||||||
|
xTimerStop(timer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cardboy::backend::esp
|
||||||
@@ -16,6 +16,7 @@ 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/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
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/services.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/include/cardboy/sdk/services.hpp
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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 scheduleTimerSignal(std::uint32_t delay_ms) = 0;
|
||||||
|
virtual void cancelTimerSignal() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cardboy::sdk
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "cardboy/sdk/event_bus.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -82,6 +84,7 @@ struct Services {
|
|||||||
IHighResClock* highResClock = nullptr;
|
IHighResClock* highResClock = nullptr;
|
||||||
IPowerManager* powerManager = nullptr;
|
IPowerManager* powerManager = nullptr;
|
||||||
IFilesystem* filesystem = nullptr;
|
IFilesystem* filesystem = nullptr;
|
||||||
|
IEventBus* eventBus = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
#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"
|
||||||
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <SFML/Window/Keyboard.hpp>
|
#include <SFML/Window/Keyboard.hpp>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <condition_variable>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <mutex>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -92,6 +95,29 @@ private:
|
|||||||
bool mounted = false;
|
bool mounted = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DesktopEventBus final : public cardboy::sdk::IEventBus {
|
||||||
|
public:
|
||||||
|
explicit DesktopEventBus(DesktopRuntime& owner);
|
||||||
|
~DesktopEventBus() 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 scheduleTimerSignal(std::uint32_t delay_ms) override;
|
||||||
|
void cancelTimerSignal() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DesktopRuntime& runtime;
|
||||||
|
std::mutex mutex;
|
||||||
|
std::condition_variable cv;
|
||||||
|
std::uint32_t pendingBits = 0;
|
||||||
|
|
||||||
|
std::mutex timerMutex;
|
||||||
|
std::condition_variable timerCv;
|
||||||
|
std::thread timerThread;
|
||||||
|
bool timerCancel = false;
|
||||||
|
};
|
||||||
|
|
||||||
class DesktopFramebuffer final : public cardboy::sdk::FramebufferFacade<DesktopFramebuffer> {
|
class DesktopFramebuffer final : public cardboy::sdk::FramebufferFacade<DesktopFramebuffer> {
|
||||||
public:
|
public:
|
||||||
explicit DesktopFramebuffer(DesktopRuntime& runtime);
|
explicit DesktopFramebuffer(DesktopRuntime& runtime);
|
||||||
@@ -170,6 +196,7 @@ private:
|
|||||||
DesktopHighResClock highResService;
|
DesktopHighResClock highResService;
|
||||||
DesktopPowerManager powerService;
|
DesktopPowerManager powerService;
|
||||||
DesktopFilesystem filesystemService;
|
DesktopFilesystem filesystemService;
|
||||||
|
DesktopEventBus eventBusService;
|
||||||
cardboy::sdk::Services services{};
|
cardboy::sdk::Services services{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,92 @@
|
|||||||
|
|
||||||
namespace cardboy::backend::desktop {
|
namespace cardboy::backend::desktop {
|
||||||
|
|
||||||
|
DesktopEventBus::DesktopEventBus(DesktopRuntime& owner) : runtime(owner) {}
|
||||||
|
|
||||||
|
DesktopEventBus::~DesktopEventBus() { cancelTimerSignal(); }
|
||||||
|
|
||||||
|
void DesktopEventBus::signal(std::uint32_t bits) {
|
||||||
|
if (bits == 0)
|
||||||
|
return;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
pendingBits |= bits;
|
||||||
|
}
|
||||||
|
cv.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopEventBus::signalFromISR(std::uint32_t bits) { signal(bits); }
|
||||||
|
|
||||||
|
std::uint32_t DesktopEventBus::wait(std::uint32_t mask, std::uint32_t timeout_ms) {
|
||||||
|
if (mask == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const auto start = std::chrono::steady_clock::now();
|
||||||
|
const bool infinite = timeout_ms == cardboy::sdk::IEventBus::kWaitForever;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
const std::uint32_t ready = pendingBits & mask;
|
||||||
|
if (ready != 0) {
|
||||||
|
pendingBits &= ~mask;
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!infinite) {
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
const auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
|
||||||
|
if (elapsedMs >= static_cast<std::int64_t>(timeout_ms))
|
||||||
|
return 0;
|
||||||
|
const auto remaining = timeout_ms - static_cast<std::uint32_t>(elapsedMs);
|
||||||
|
runtime.sleepFor(std::min<std::uint32_t>(remaining, 8));
|
||||||
|
} else {
|
||||||
|
runtime.sleepFor(8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopEventBus::scheduleTimerSignal(std::uint32_t delay_ms) {
|
||||||
|
cancelTimerSignal();
|
||||||
|
|
||||||
|
if (delay_ms == cardboy::sdk::IEventBus::kWaitForever)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (delay_ms == 0) {
|
||||||
|
signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(timerMutex);
|
||||||
|
timerCancel = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
timerThread = std::thread([this, delay_ms]() {
|
||||||
|
std::unique_lock<std::mutex> lock(timerMutex);
|
||||||
|
const bool cancelled =
|
||||||
|
timerCv.wait_for(lock, std::chrono::milliseconds(delay_ms), [this] { return timerCancel; });
|
||||||
|
lock.unlock();
|
||||||
|
if (!cancelled)
|
||||||
|
signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopEventBus::cancelTimerSignal() {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(timerMutex);
|
||||||
|
timerCancel = true;
|
||||||
|
}
|
||||||
|
timerCv.notify_all();
|
||||||
|
if (timerThread.joinable())
|
||||||
|
timerThread.join();
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(timerMutex);
|
||||||
|
timerCancel = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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())
|
||||||
@@ -90,6 +176,7 @@ DesktopInput::DesktopInput(DesktopRuntime& runtime) : runtime(runtime) {}
|
|||||||
cardboy::sdk::InputState DesktopInput::readState_impl() { return state; }
|
cardboy::sdk::InputState DesktopInput::readState_impl() { return state; }
|
||||||
|
|
||||||
void DesktopInput::handleKey(sf::Keyboard::Key key, bool pressed) {
|
void DesktopInput::handleKey(sf::Keyboard::Key key, bool pressed) {
|
||||||
|
bool handled = true;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case sf::Keyboard::Key::Up:
|
case sf::Keyboard::Key::Up:
|
||||||
state.up = pressed;
|
state.up = pressed;
|
||||||
@@ -118,8 +205,11 @@ void DesktopInput::handleKey(sf::Keyboard::Key key, bool pressed) {
|
|||||||
state.start = pressed;
|
state.start = pressed;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
handled = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (handled)
|
||||||
|
runtime.eventBusService.signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Input));
|
||||||
}
|
}
|
||||||
|
|
||||||
DesktopClock::DesktopClock(DesktopRuntime& runtime) : runtime(runtime), start(std::chrono::steady_clock::now()) {}
|
DesktopClock::DesktopClock(DesktopRuntime& runtime) : runtime(runtime), start(std::chrono::steady_clock::now()) {}
|
||||||
@@ -137,7 +227,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) {
|
framebuffer(*this), input(*this), clock(*this), eventBusService(*this) {
|
||||||
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");
|
||||||
@@ -153,6 +243,7 @@ DesktopRuntime::DesktopRuntime() :
|
|||||||
services.highResClock = &highResService;
|
services.highResClock = &highResService;
|
||||||
services.powerManager = &powerService;
|
services.powerManager = &powerService;
|
||||||
services.filesystem = &filesystemService;
|
services.filesystem = &filesystemService;
|
||||||
|
services.eventBus = &eventBusService;
|
||||||
}
|
}
|
||||||
|
|
||||||
cardboy::sdk::Services& DesktopRuntime::serviceRegistry() { return services; }
|
cardboy::sdk::Services& DesktopRuntime::serviceRegistry() { return services; }
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ struct AppContext {
|
|||||||
[[nodiscard]] IHighResClock* highResClock() const { return services ? services->highResClock : nullptr; }
|
[[nodiscard]] IHighResClock* highResClock() const { return services ? services->highResClock : nullptr; }
|
||||||
[[nodiscard]] IPowerManager* powerManager() const { return services ? services->powerManager : nullptr; }
|
[[nodiscard]] IPowerManager* powerManager() const { return services ? services->powerManager : 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; }
|
||||||
|
|
||||||
void requestAppSwitchByIndex(std::size_t index) {
|
void requestAppSwitchByIndex(std::size_t index) {
|
||||||
pendingAppIndex = index;
|
pendingAppIndex = index;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cardboy/sdk/event_bus.hpp>
|
||||||
#include "app_framework.hpp"
|
#include "app_framework.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -51,6 +52,7 @@ private:
|
|||||||
void clearTimersForCurrentApp();
|
void clearTimersForCurrentApp();
|
||||||
TimerRecord* findTimer(AppTimerHandle handle);
|
TimerRecord* findTimer(AppTimerHandle handle);
|
||||||
bool handlePendingSwitchRequest();
|
bool handlePendingSwitchRequest();
|
||||||
|
void notifyEventBus(EventBusSignal signal);
|
||||||
|
|
||||||
AppContext context;
|
AppContext context;
|
||||||
std::vector<std::unique_ptr<IAppFactory>> factories;
|
std::vector<std::unique_ptr<IAppFactory>> factories;
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ namespace {
|
|||||||
return state.up || state.down || state.left || state.right || state.a || state.b || state.select || state.start;
|
return state.up || state.down || state.left || state.right || state.a || state.b || state.select || state.start;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::uint32_t kIdlePollMs = 16;
|
|
||||||
|
|
||||||
template<typename Framebuffer>
|
template<typename Framebuffer>
|
||||||
void statusBarPreSendHook(void* framebuffer, void* userData) {
|
void statusBarPreSendHook(void* framebuffer, void* userData) {
|
||||||
auto* fb = static_cast<Framebuffer*>(framebuffer);
|
auto* fb = static_cast<Framebuffer*>(framebuffer);
|
||||||
@@ -127,13 +125,19 @@ void AppSystem::run() {
|
|||||||
if (waitMs == 0)
|
if (waitMs == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (waitMs == std::numeric_limits<std::uint32_t>::max())
|
auto* eventBus = context.eventBus();
|
||||||
waitMs = kIdlePollMs;
|
if (!eventBus)
|
||||||
else
|
return;
|
||||||
waitMs = std::min(waitMs, kIdlePollMs);
|
|
||||||
|
|
||||||
if (waitMs > 0)
|
const std::uint32_t mask = to_event_bits(EventBusSignal::Input) | to_event_bits(EventBusSignal::Timer);
|
||||||
context.clock.sleep_ms(waitMs);
|
|
||||||
|
if (waitMs == std::numeric_limits<std::uint32_t>::max()) {
|
||||||
|
eventBus->cancelTimerSignal();
|
||||||
|
eventBus->wait(mask, IEventBus::kWaitForever);
|
||||||
|
} else {
|
||||||
|
eventBus->scheduleTimerSignal(waitMs);
|
||||||
|
eventBus->wait(mask, IEventBus::kWaitForever);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,24 +171,32 @@ AppTimerHandle AppSystem::scheduleTimer(std::uint32_t delay_ms, bool repeat) {
|
|||||||
record.repeat = repeat;
|
record.repeat = repeat;
|
||||||
record.active = true;
|
record.active = true;
|
||||||
timers.push_back(record);
|
timers.push_back(record);
|
||||||
|
notifyEventBus(EventBusSignal::Timer);
|
||||||
return record.id;
|
return record.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSystem::cancelTimer(AppTimerHandle handle) {
|
void AppSystem::cancelTimer(AppTimerHandle handle) {
|
||||||
auto* timer = findTimer(handle);
|
auto* timer = findTimer(handle);
|
||||||
if (timer)
|
if (!timer)
|
||||||
timer->active = false;
|
return;
|
||||||
|
timer->active = false;
|
||||||
timers.erase(std::remove_if(timers.begin(), timers.end(), [](const TimerRecord& rec) { return !rec.active; }),
|
timers.erase(std::remove_if(timers.begin(), timers.end(), [](const TimerRecord& rec) { return !rec.active; }),
|
||||||
timers.end());
|
timers.end());
|
||||||
|
notifyEventBus(EventBusSignal::Timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSystem::cancelAllTimers() {
|
void AppSystem::cancelAllTimers() {
|
||||||
|
bool changed = false;
|
||||||
for (auto& timer: timers) {
|
for (auto& timer: timers) {
|
||||||
if (timer.generation == currentGeneration)
|
if (timer.generation == currentGeneration && timer.active) {
|
||||||
timer.active = false;
|
timer.active = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
timers.erase(std::remove_if(timers.begin(), timers.end(), [](const TimerRecord& rec) { return !rec.active; }),
|
timers.erase(std::remove_if(timers.begin(), timers.end(), [](const TimerRecord& rec) { return !rec.active; }),
|
||||||
timers.end());
|
timers.end());
|
||||||
|
if (changed)
|
||||||
|
notifyEventBus(EventBusSignal::Timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSystem::dispatchEvent(const AppEvent& event) {
|
void AppSystem::dispatchEvent(const AppEvent& event) {
|
||||||
@@ -229,8 +241,11 @@ std::uint32_t AppSystem::nextTimerDueMs(std::uint32_t now) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AppSystem::clearTimersForCurrentApp() {
|
void AppSystem::clearTimersForCurrentApp() {
|
||||||
|
const bool hadTimers = !timers.empty();
|
||||||
++currentGeneration;
|
++currentGeneration;
|
||||||
timers.clear();
|
timers.clear();
|
||||||
|
if (hadTimers)
|
||||||
|
notifyEventBus(EventBusSignal::Timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppSystem::TimerRecord* AppSystem::findTimer(AppTimerHandle handle) {
|
AppSystem::TimerRecord* AppSystem::findTimer(AppTimerHandle handle) {
|
||||||
@@ -260,4 +275,11 @@ bool AppSystem::handlePendingSwitchRequest() {
|
|||||||
return switched;
|
return switched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppSystem::notifyEventBus(EventBusSignal signal) {
|
||||||
|
if (signal == EventBusSignal::None)
|
||||||
|
return;
|
||||||
|
if (auto* bus = context.eventBus())
|
||||||
|
bus->signal(to_event_bits(signal));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace cardboy::sdk
|
} // namespace cardboy::sdk
|
||||||
|
|||||||
Reference in New Issue
Block a user