Compare commits

...

2 Commits

Author SHA1 Message Date
65ee33a141 desktop fix 2025-10-25 18:25:12 +02:00
0e69debf39 reset sdkconfig 2025-10-25 16:01:50 +02:00
5 changed files with 139 additions and 170 deletions

View File

@@ -11,6 +11,13 @@ struct InputState {
bool b = false;
bool select = false;
bool start = false;
bool operator==(const InputState& other) const {
return up == other.up && left == other.left && right == other.right && down == other.down && a == other.a &&
b == other.b && select == other.select && start == other.start;
}
bool operator!=(const InputState& other) const { return !(*this == other); }
};
} // namespace cardboy::sdk

View File

@@ -7,8 +7,8 @@
#include <SFML/Window/Keyboard.hpp>
#include <chrono>
#include <condition_variable>
#include <deque>
#include <cstdint>
#include <deque>
#include <filesystem>
#include <limits>
#include <memory>
@@ -106,38 +106,30 @@ private:
std::uint32_t revisionCounter = 0;
};
class DesktopEventBus final : public cardboy::sdk::IEventBus {
class DesktopLoopHooks final : public cardboy::sdk::ILoopHooks {
public:
explicit DesktopEventBus(DesktopRuntime& owner);
explicit DesktopLoopHooks(DesktopRuntime& owner);
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 onLoopIteration() override;
private:
DesktopRuntime& runtime;
std::mutex mutex;
std::condition_variable cv;
std::uint32_t pendingBits = 0;
};
class DesktopScopedEventBus final : public cardboy::sdk::IAppEventBus {
class DesktopEventBus final : public cardboy::sdk::IEventBus {
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;
std::optional<cardboy::sdk::AppEvent> pop(std::optional<std::uint32_t> timeout_ms = std::nullopt) override;
private:
cardboy::sdk::IEventBus& globalBus;
std::mutex mutex;
std::condition_variable cv;
std::deque<cardboy::sdk::AppEvent> queue;
};
class DesktopTimerService final : public cardboy::sdk::ITimerService {
public:
DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IAppEventBus& appBus);
DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& eventBus);
~DesktopTimerService() override;
cardboy::sdk::AppTimerHandle scheduleTimer(std::uint32_t delay_ms, bool repeat) override;
@@ -158,7 +150,7 @@ private:
void cleanupInactive();
DesktopRuntime& runtime;
cardboy::sdk::IAppEventBus& appEventBus;
cardboy::sdk::IEventBus& eventBus;
std::mutex mutex;
std::condition_variable cv;
std::vector<TimerRecord> timers;
@@ -171,11 +163,11 @@ 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;
[[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;
};
@@ -263,6 +255,7 @@ private:
DesktopEventBus eventBusService;
DesktopAppServiceProvider appServiceProvider;
DesktopNotificationCenter notificationService;
DesktopLoopHooks loopHooksService;
cardboy::sdk::Services services{};
};

View File

@@ -9,6 +9,7 @@
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <stdexcept>
#include <system_error>
#include <utility>
@@ -19,78 +20,31 @@ namespace {
constexpr std::size_t kDesktopEventQueueLimit = 64;
} // namespace
DesktopEventBus::DesktopEventBus(DesktopRuntime& owner) : runtime(owner) {}
void DesktopEventBus::signal(std::uint32_t bits) {
if (bits == 0)
return;
{
void DesktopEventBus::post(const cardboy::sdk::AppEvent& event) {
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);
}
}
}
DesktopScopedEventBus::DesktopScopedEventBus(cardboy::sdk::IEventBus& bus) : globalBus(bus) {}
void DesktopScopedEventBus::post(const cardboy::sdk::AppEvent& event) {
{
std::lock_guard<std::mutex> lock(mutex);
if (queue.size() >= kDesktopEventQueueLimit)
queue.pop_front();
queue.push_back(event);
}
globalBus.signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
cv.notify_one();
}
bool DesktopScopedEventBus::pop(cardboy::sdk::AppEvent& outEvent) {
std::lock_guard<std::mutex> lock(mutex);
if (queue.empty())
return false;
outEvent = queue.front();
std::optional<cardboy::sdk::AppEvent> DesktopEventBus::pop(std::optional<std::uint32_t> timeout_ms) {
std::unique_lock<std::mutex> lock(mutex);
if (queue.empty()) {
if (!timeout_ms) {
return std::nullopt;
}
auto timeout = std::chrono::milliseconds(*timeout_ms);
cv.wait_for(lock, timeout, [this] { return !queue.empty(); });
if (queue.empty()) {
return std::nullopt;
}
}
auto event = queue.front();
queue.pop_front();
return true;
return event;
}
void DesktopScopedEventBus::clear() {
std::lock_guard<std::mutex> lock(mutex);
queue.clear();
}
DesktopTimerService::DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IAppEventBus& appBus) :
runtime(owner), appEventBus(appBus) {
DesktopTimerService::DesktopTimerService(DesktopRuntime& owner, cardboy::sdk::IEventBus& eventBus) :
runtime(owner), eventBus(eventBus) {
worker = std::thread(&DesktopTimerService::workerLoop, this);
}
@@ -109,8 +63,8 @@ cardboy::sdk::AppTimerHandle DesktopTimerService::scheduleTimer(std::uint32_t de
const auto now = std::chrono::steady_clock::now();
const auto effectiveDelayMs = std::chrono::milliseconds(delay_ms);
const auto dueTime = delay_ms == 0 ? now : now + effectiveDelayMs;
const auto interval = std::chrono::milliseconds(std::max<std::uint32_t>(1, repeat ? std::max(delay_ms, 1u)
: 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{};
record.repeat = repeat;
@@ -163,9 +117,8 @@ void DesktopTimerService::workerLoop() {
continue;
}
auto nextIt = std::min_element(timers.begin(), timers.end(), [](const TimerRecord& a, const TimerRecord& b) {
return a.due < b.due;
});
auto nextIt = std::min_element(timers.begin(), timers.end(),
[](const TimerRecord& a, const TimerRecord& b) { return a.due < b.due; });
if (nextIt == timers.end())
continue;
@@ -189,7 +142,7 @@ void DesktopTimerService::workerLoop() {
cardboy::sdk::AppEvent event{};
event.timestamp_ms = runtime.clock.millis();
event.data = timerEvent;
appEventBus.post(event);
eventBus.post(event);
lock.lock();
continue;
@@ -209,12 +162,11 @@ void DesktopTimerService::cleanupInactive() {
DesktopAppServiceProvider::DesktopAppServiceProvider(DesktopRuntime& owner, cardboy::sdk::IEventBus& bus) :
runtime(owner), eventBus(bus) {}
std::unique_ptr<cardboy::sdk::AppScopedServices> DesktopAppServiceProvider::createScopedServices(std::uint64_t generation) {
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->ownedTimer = std::make_unique<DesktopTimerService>(runtime, eventBus);
scoped->timer = scoped->ownedTimer.get();
return scoped;
}
@@ -364,6 +316,13 @@ void DesktopNotificationCenter::removeByExternalId(std::uint64_t externalId) {
++revisionCounter;
}
DesktopLoopHooks::DesktopLoopHooks(DesktopRuntime& owner) : runtime(owner) {}
void DesktopLoopHooks::onLoopIteration() {
runtime.processEvents();
runtime.presentIfNeeded();
}
DesktopFramebuffer::DesktopFramebuffer(DesktopRuntime& runtime) : runtime(runtime) {}
int DesktopFramebuffer::width_impl() const { return cardboy::sdk::kDisplayWidth; }
@@ -391,26 +350,29 @@ DesktopInput::DesktopInput(DesktopRuntime& runtime) : runtime(runtime) {}
cardboy::sdk::InputState DesktopInput::readState_impl() { return state; }
void DesktopInput::handleKey(sf::Keyboard::Key key, bool pressed) {
const auto oldState = state;
bool handled = true;
switch (key) {
case sf::Keyboard::Key::Up:
case sf::Keyboard::Key::W:
state.up = pressed;
break;
case sf::Keyboard::Key::Down:
case sf::Keyboard::Key::S:
state.down = pressed;
break;
case sf::Keyboard::Key::Left:
case sf::Keyboard::Key::A:
state.left = pressed;
break;
case sf::Keyboard::Key::Right:
case sf::Keyboard::Key::D:
state.right = pressed;
break;
case sf::Keyboard::Key::Z:
case sf::Keyboard::Key::A:
state.a = pressed;
break;
case sf::Keyboard::Key::X:
case sf::Keyboard::Key::S:
state.b = pressed;
break;
case sf::Keyboard::Key::Space:
@@ -423,8 +385,11 @@ void DesktopInput::handleKey(sf::Keyboard::Key key, bool pressed) {
handled = false;
break;
}
if (handled)
runtime.eventBusService.signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Input));
if (handled && oldState != state) {
cardboy::sdk::AppButtonEvent btnEvent{oldState, state};
cardboy::sdk::AppEvent event{runtime.clock.millis(), btnEvent};
runtime.eventBusService.post(event);
}
}
DesktopClock::DesktopClock(DesktopRuntime& runtime) : runtime(runtime), start(std::chrono::steady_clock::now()) {}
@@ -442,14 +407,18 @@ DesktopRuntime::DesktopRuntime() :
"Cardboy Desktop"),
texture(), sprite(texture),
pixels(static_cast<std::size_t>(cardboy::sdk::kDisplayWidth * cardboy::sdk::kDisplayHeight) * 4, 0),
framebuffer(*this), input(*this), clock(*this), eventBusService(*this), appServiceProvider(*this, eventBusService) {
framebuffer(*this), input(*this), clock(*this), eventBusService(), appServiceProvider(*this, eventBusService),
loopHooksService(*this) {
window.setFramerateLimit(60);
if (!texture.resize(sf::Vector2u{cardboy::sdk::kDisplayWidth, cardboy::sdk::kDisplayHeight}))
throw std::runtime_error("Failed to allocate texture for desktop framebuffer");
sprite.setTexture(texture, true);
sprite.setScale(sf::Vector2f{static_cast<float>(kPixelScale), static_cast<float>(kPixelScale)});
clearPixels(true);
clearPixels(false);
presentIfNeeded();
window.requestFocus();
std::cout << "Desktop window initialized and presented." << std::endl;
services.buzzer = &buzzerService;
services.battery = &batteryService;
@@ -459,7 +428,7 @@ DesktopRuntime::DesktopRuntime() :
services.filesystem = &filesystemService;
services.eventBus = &eventBusService;
services.appServices = &appServiceProvider;
services.loopHooks = nullptr;
services.loopHooks = &loopHooksService;
services.notifications = &notificationService;
}

View File

@@ -567,9 +567,9 @@ CONFIG_SECURE_TEE_LOG_LEVEL=0
# Serial flasher config
#
# CONFIG_ESPTOOLPY_NO_STUB is not set
# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set
CONFIG_ESPTOOLPY_FLASHMODE_DIO=y
# CONFIG_ESPTOOLPY_FLASHMODE_DIO is not set
# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set
CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y
CONFIG_ESPTOOLPY_FLASHMODE="dio"
@@ -613,9 +613,9 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# Compiler options
#
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
CONFIG_COMPILER_OPTIMIZATION_PERF=y
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
@@ -1217,7 +1217,7 @@ CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
# ESP-Driver:USB Serial/JTAG Configuration
#
CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y
# CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION is not set
CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION=y
# end of ESP-Driver:USB Serial/JTAG Configuration
#
@@ -1714,7 +1714,6 @@ CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
#
# Port
#
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
@@ -1762,16 +1761,16 @@ CONFIG_HAL_WDT_USE_ROM_IMPL=y
#
# Heap memory debugging
#
# CONFIG_HEAP_POISONING_DISABLED is not set
CONFIG_HEAP_POISONING_DISABLED=y
# CONFIG_HEAP_POISONING_LIGHT is not set
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set
CONFIG_HEAP_TRACING_OFF=y
# CONFIG_HEAP_TRACING_STANDALONE is not set
# CONFIG_HEAP_TRACING_TOHOST is not set
# CONFIG_HEAP_USE_HOOKS is not set
# CONFIG_HEAP_TASK_TRACKING is not set
# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set
# CONFIG_HEAP_TLSF_USE_ROM_IMPL is not set
CONFIG_HEAP_TLSF_USE_ROM_IMPL=y
# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set
# end of Heap memory debugging
@@ -2459,14 +2458,14 @@ CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
# 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_QIO=y
# CONFIG_FLASHMODE_QOUT is not set
CONFIG_FLASHMODE_DIO=y
# CONFIG_FLASHMODE_DIO is not set
# CONFIG_FLASHMODE_DOUT is not set
CONFIG_MONITOR_BAUD=115200
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
# 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

View File

@@ -613,9 +613,9 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# Compiler options
#
# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
CONFIG_COMPILER_OPTIMIZATION_PERF=y
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
@@ -1714,6 +1714,7 @@ CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
#
# Port
#
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
@@ -1761,16 +1762,16 @@ CONFIG_HAL_WDT_USE_ROM_IMPL=y
#
# Heap memory debugging
#
CONFIG_HEAP_POISONING_DISABLED=y
# CONFIG_HEAP_POISONING_DISABLED is not set
# CONFIG_HEAP_POISONING_LIGHT is not set
# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_HEAP_TRACING_OFF=y
# CONFIG_HEAP_TRACING_STANDALONE is not set
# CONFIG_HEAP_TRACING_TOHOST is not set
# CONFIG_HEAP_USE_HOOKS is not set
# CONFIG_HEAP_TASK_TRACKING is not set
# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set
CONFIG_HEAP_TLSF_USE_ROM_IMPL=y
# CONFIG_HEAP_TLSF_USE_ROM_IMPL is not set
# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set
# end of Heap memory debugging
@@ -2463,9 +2464,9 @@ CONFIG_LOG_BOOTLOADER_LEVEL=3
CONFIG_FLASHMODE_DIO=y
# CONFIG_FLASHMODE_DOUT is not set
CONFIG_MONITOR_BAUD=115200
# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y