From db88e16aaa186184bbdc57fe7b4593e84d36d05f Mon Sep 17 00:00:00 2001 From: Stepan Usatiuk Date: Sun, 12 Oct 2025 17:29:15 +0200 Subject: [PATCH] better controls --- Firmware/sdk/apps/clock/src/clock_app.cpp | 28 +++++++++---------- Firmware/sdk/apps/gameboy/src/gameboy_app.cpp | 9 +++++- Firmware/sdk/apps/menu/src/menu_app.cpp | 6 ++-- .../sdk/apps/settings/src/settings_app.cpp | 4 +-- .../core/include/cardboy/sdk/app_system.hpp | 1 + Firmware/sdk/core/src/app_system.cpp | 11 +++++++- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/Firmware/sdk/apps/clock/src/clock_app.cpp b/Firmware/sdk/apps/clock/src/clock_app.cpp index 90e81de..0b2d74f 100644 --- a/Firmware/sdk/apps/clock/src/clock_app.cpp +++ b/Firmware/sdk/apps/clock/src/clock_app.cpp @@ -25,14 +25,14 @@ using Framebuffer = typename AppContext::Framebuffer; using Clock = typename AppContext::Clock; struct TimeSnapshot { - bool hasWallTime = false; - int hour24 = 0; - int minute = 0; - int second = 0; - int year = 0; - int month = 0; - int day = 0; - int weekday = 0; + bool hasWallTime = false; + int hour24 = 0; + int minute = 0; + int second = 0; + int year = 0; + int month = 0; + int day = 0; + int weekday = 0; std::uint64_t uptimeSeconds = 0; }; @@ -42,8 +42,8 @@ public: void onStart() override { cancelRefreshTimer(); - lastSnapshot = {}; - dirty = true; + lastSnapshot = {}; + dirty = true; const auto snap = captureTime(); renderIfNeeded(snap); lastSnapshot = snap; @@ -69,8 +69,8 @@ private: Framebuffer& framebuffer; Clock& clock; - bool use24Hour = true; - bool dirty = false; + bool use24Hour = true; + bool dirty = false; cardboy::sdk::AppTimerHandle refreshTimer = cardboy::sdk::kInvalidAppTimer; TimeSnapshot lastSnapshot{}; @@ -199,7 +199,7 @@ private: drawCenteredText(framebuffer, timeY + font16x8::kGlyphHeight * scaleLarge + 28, dateLine, scaleSmall, 1); if (!snap.hasWallTime) { - char uptimeLine[32]; + char uptimeLine[32]; const std::uint64_t days = snap.uptimeSeconds / 86400ULL; const std::uint64_t hrs = (snap.uptimeSeconds / 3600ULL) % 24ULL; const std::uint64_t mins = (snap.uptimeSeconds / 60ULL) % 60ULL; @@ -225,7 +225,7 @@ private: class ClockAppFactory final : public cardboy::sdk::IAppFactory { public: - const char* name() const override { return kClockAppName; } + const char* name() const override { return kClockAppName; } std::unique_ptr create(cardboy::sdk::AppContext& context) override { return std::make_unique(context); } diff --git a/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp b/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp index e235b7a..2f42c0a 100644 --- a/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp +++ b/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp @@ -1,6 +1,8 @@ #include "cardboy/apps/gameboy_app.hpp" #include "cardboy/apps/peanut_gb.h" +#include "cardboy/apps/menu_app.hpp" + #include "cardboy/gfx/font16x8.hpp" #include "cardboy/sdk/app_framework.hpp" #include "cardboy/sdk/app_system.hpp" @@ -760,6 +762,11 @@ private: if (roms.empty()) return; + + if (input.b && !prevInput.b) { + context.requestAppSwitchByName(apps::kMenuAppName); + return; + } const auto wrapDecrement = [&]() { if (selectedIndex == 0) selectedIndex = roms.size() - 1; @@ -822,7 +829,7 @@ private: } font16x8::drawText(framebuffer, 16, framebuffer.height() - 52, "A/START PLAY", 1, true, 1); - font16x8::drawText(framebuffer, 16, framebuffer.height() - 32, "SELECT RESCAN", 1, true, 1); + font16x8::drawText(framebuffer, 16, framebuffer.height() - 32, "B BACK SELECT RESCAN", 1, true, 1); } if (!statusMessage.empty()) { diff --git a/Firmware/sdk/apps/menu/src/menu_app.cpp b/Firmware/sdk/apps/menu/src/menu_app.cpp index 1cbe999..b68151e 100644 --- a/Firmware/sdk/apps/menu/src/menu_app.cpp +++ b/Firmware/sdk/apps/menu/src/menu_app.cpp @@ -47,7 +47,7 @@ public: moveSelection(+1); } - const bool launch = (current.a && !previous.a) || (current.select && !previous.select); + const bool launch = (current.a && !previous.a) || (current.start && !previous.start); if (launch) launchSelected(); @@ -153,7 +153,7 @@ private: font16x8::drawText(framebuffer, topRightX, 20, indexLabel, 1, true, 0); drawPagerDots(); - drawCenteredText(framebuffer, framebuffer.height() - 48, "A/SELECT START", 1, 1); + drawCenteredText(framebuffer, framebuffer.height() - 48, "A START APP", 1, 1); drawCenteredText(framebuffer, framebuffer.height() - 28, "L/R CHOOSE", 1, 1); } @@ -163,7 +163,7 @@ private: class MenuAppFactory final : public cardboy::sdk::IAppFactory { public: - const char* name() const override { return kMenuAppName; } + const char* name() const override { return kMenuAppName; } std::unique_ptr create(cardboy::sdk::AppContext& context) override { return std::make_unique(context); } diff --git a/Firmware/sdk/apps/settings/src/settings_app.cpp b/Firmware/sdk/apps/settings/src/settings_app.cpp index 16d50ca..badcce2 100644 --- a/Firmware/sdk/apps/settings/src/settings_app.cpp +++ b/Firmware/sdk/apps/settings/src/settings_app.cpp @@ -64,8 +64,8 @@ public: moved = true; } - const bool togglePressed = (current.a && !previous.a) || (current.select && !previous.select) || - (current.start && !previous.start); + const bool togglePressed = (current.a && !previous.a) || (current.start && !previous.start) || + (current.select && !previous.select); if (togglePressed) handleToggle(); diff --git a/Firmware/sdk/core/include/cardboy/sdk/app_system.hpp b/Firmware/sdk/core/include/cardboy/sdk/app_system.hpp index 13a271e..7654488 100644 --- a/Firmware/sdk/core/include/cardboy/sdk/app_system.hpp +++ b/Firmware/sdk/core/include/cardboy/sdk/app_system.hpp @@ -61,6 +61,7 @@ private: AppTimerHandle nextTimerId = 1; std::uint32_t currentGeneration = 0; InputState lastInputState{}; + bool suppressInputs = false; }; inline AppTimerHandle AppContext::scheduleTimerInternal(std::uint32_t delay_ms, bool repeat) { diff --git a/Firmware/sdk/core/src/app_system.cpp b/Firmware/sdk/core/src/app_system.cpp index d023626..5a737b7 100644 --- a/Firmware/sdk/core/src/app_system.cpp +++ b/Firmware/sdk/core/src/app_system.cpp @@ -13,6 +13,10 @@ namespace { 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; +} + constexpr std::uint32_t kIdlePollMs = 16; template @@ -71,6 +75,7 @@ bool AppSystem::startAppByIndex(std::size_t index) { clearTimersForCurrentApp(); current = std::move(app); lastInputState = context.input.readState(); + suppressInputs = true; StatusBar::instance().setServices(context.services); StatusBar::instance().setCurrentAppName(activeFactory ? activeFactory->name() : ""); current->onStart(); @@ -94,7 +99,11 @@ void AppSystem::run() { const InputState inputNow = context.input.readState(); const bool consumedByStatusToggle = StatusBar::instance().handleToggleInput(inputNow, lastInputState); - if (!consumedByStatusToggle && inputsDiffer(inputNow, lastInputState)) { + if (suppressInputs) { + lastInputState = inputNow; + if (!anyButtonPressed(inputNow)) + suppressInputs = false; + } else if (!consumedByStatusToggle && inputsDiffer(inputNow, lastInputState)) { AppEvent evt{}; evt.type = AppEventType::Button; evt.timestamp_ms = now;