From 1b6e9a0f781b0069e4a253be2572a75bc951cc81 Mon Sep 17 00:00:00 2001 From: Stepan Usatiuk Date: Sun, 12 Oct 2025 16:57:40 +0200 Subject: [PATCH] settings app --- Firmware/main/src/app_main.cpp | 2 + Firmware/sdk/apps/CMakeLists.txt | 1 + Firmware/sdk/apps/settings/CMakeLists.txt | 9 ++ .../include/cardboy/apps/settings_app.hpp | 13 ++ .../sdk/apps/settings/src/settings_app.cpp | 130 ++++++++++++++++++ Firmware/sdk/launchers/desktop/src/main.cpp | 2 + 6 files changed, 157 insertions(+) create mode 100644 Firmware/sdk/apps/settings/CMakeLists.txt create mode 100644 Firmware/sdk/apps/settings/include/cardboy/apps/settings_app.hpp create mode 100644 Firmware/sdk/apps/settings/src/settings_app.cpp diff --git a/Firmware/main/src/app_main.cpp b/Firmware/main/src/app_main.cpp index eab8620..e4a7bcf 100644 --- a/Firmware/main/src/app_main.cpp +++ b/Firmware/main/src/app_main.cpp @@ -3,6 +3,7 @@ #include "cardboy/apps/clock_app.hpp" #include "cardboy/apps/gameboy_app.hpp" #include "cardboy/apps/menu_app.hpp" +#include "cardboy/apps/settings_app.hpp" #include "cardboy/apps/tetris_app.hpp" #include "cardboy/backend/esp_backend.hpp" #include "cardboy/sdk/app_system.hpp" @@ -222,6 +223,7 @@ extern "C" void app_main() { context.system = &system; system.registerApp(apps::createMenuAppFactory()); + system.registerApp(apps::createSettingsAppFactory()); system.registerApp(apps::createClockAppFactory()); system.registerApp(apps::createTetrisAppFactory()); system.registerApp(apps::createGameboyAppFactory()); diff --git a/Firmware/sdk/apps/CMakeLists.txt b/Firmware/sdk/apps/CMakeLists.txt index 07c31a4..b7de8c7 100644 --- a/Firmware/sdk/apps/CMakeLists.txt +++ b/Firmware/sdk/apps/CMakeLists.txt @@ -14,5 +14,6 @@ target_compile_features(cardboy_apps PUBLIC cxx_std_20) add_subdirectory(menu) add_subdirectory(clock) +add_subdirectory(settings) add_subdirectory(gameboy) add_subdirectory(tetris) diff --git a/Firmware/sdk/apps/settings/CMakeLists.txt b/Firmware/sdk/apps/settings/CMakeLists.txt new file mode 100644 index 0000000..91f7cd6 --- /dev/null +++ b/Firmware/sdk/apps/settings/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(cardboy_apps + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/settings_app.cpp +) + +target_include_directories(cardboy_apps + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) diff --git a/Firmware/sdk/apps/settings/include/cardboy/apps/settings_app.hpp b/Firmware/sdk/apps/settings/include/cardboy/apps/settings_app.hpp new file mode 100644 index 0000000..65e6b9c --- /dev/null +++ b/Firmware/sdk/apps/settings/include/cardboy/apps/settings_app.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "cardboy/sdk/app_framework.hpp" + +#include + +namespace apps { + +inline constexpr char kSettingsAppName[] = "Settings"; + +std::unique_ptr createSettingsAppFactory(); + +} // namespace apps diff --git a/Firmware/sdk/apps/settings/src/settings_app.cpp b/Firmware/sdk/apps/settings/src/settings_app.cpp new file mode 100644 index 0000000..205cef1 --- /dev/null +++ b/Firmware/sdk/apps/settings/src/settings_app.cpp @@ -0,0 +1,130 @@ +#include "cardboy/apps/settings_app.hpp" + +#include "cardboy/apps/menu_app.hpp" +#include "cardboy/gfx/font16x8.hpp" +#include "cardboy/sdk/app_framework.hpp" + +#include +#include +#include + +namespace apps { + +namespace { + +using cardboy::sdk::AppContext; +using Framebuffer = typename AppContext::Framebuffer; + +class SettingsApp final : public cardboy::sdk::IApp { +public: + explicit SettingsApp(AppContext& ctx) : context(ctx), framebuffer(ctx.framebuffer) {} + + void onStart() override { + refreshState(); + dirty = true; + renderIfNeeded(); + } + + void handleEvent(const cardboy::sdk::AppEvent& event) override { + if (event.type != cardboy::sdk::AppEventType::Button) + return; + + const auto& current = event.button.current; + const auto& previous = event.button.previous; + + const bool prevAvailable = buzzerAvailable; + const bool prevMuted = muted; + refreshState(); + if (prevAvailable != buzzerAvailable || prevMuted != muted) + dirty = true; + + if (current.b && !previous.b) { + context.requestAppSwitchByName(kMenuAppName); + return; + } + + const bool togglePressed = (current.a && !previous.a) || (current.select && !previous.select) || + (current.start && !previous.start); + if (togglePressed) + toggleMute(); + + renderIfNeeded(); + } + +private: + AppContext& context; + Framebuffer& framebuffer; + + bool buzzerAvailable = false; + bool muted = false; + bool dirty = false; + + void refreshState() { + if (auto* buzzer = context.buzzer()) { + buzzerAvailable = true; + muted = buzzer->isMuted(); + } else { + buzzerAvailable = false; + muted = false; + } + } + + void toggleMute() { + auto* buzzer = context.buzzer(); + if (!buzzer) + return; + const bool targetMuted = !muted; + buzzer->setMuted(targetMuted); + muted = buzzer->isMuted(); + if (!muted) + buzzer->beepMove(); + dirty = true; + } + + static void drawCenteredText(Framebuffer& fb, int y, std::string_view text, int scale, int letterSpacing = 1) { + const int width = font16x8::measureText(text, scale, letterSpacing); + const int x = (fb.width() - width) / 2; + font16x8::drawText(fb, x, y, text, scale, true, letterSpacing); + } + + void renderIfNeeded() { + if (!dirty) + return; + dirty = false; + + framebuffer.frameReady(); + framebuffer.clear(false); + + drawCenteredText(framebuffer, 24, "SETTINGS", 1, 1); + + const int centerY = framebuffer.height() / 2; + if (!buzzerAvailable) { + drawCenteredText(framebuffer, centerY - 12, "BUZZER SERVICE", 1, 1); + drawCenteredText(framebuffer, centerY + 8, "UNAVAILABLE", 1, 1); + } else { + const char* stateText = muted ? "ON" : "OFF"; + std::string line = std::string("MUTE: ") + stateText; + drawCenteredText(framebuffer, centerY - 20, line, 2, 0); + drawCenteredText(framebuffer, centerY + 26, "A TOGGLE", 1, 1); + drawCenteredText(framebuffer, centerY + 44, "START/SELECT ALSO", 1, 1); + } + + drawCenteredText(framebuffer, framebuffer.height() - 28, "B BACK", 1, 1); + + framebuffer.sendFrame(); + } +}; + +class SettingsAppFactory final : public cardboy::sdk::IAppFactory { +public: + const char* name() const override { return kSettingsAppName; } + std::unique_ptr create(cardboy::sdk::AppContext& context) override { + return std::make_unique(context); + } +}; + +} // namespace + +std::unique_ptr createSettingsAppFactory() { return std::make_unique(); } + +} // namespace apps diff --git a/Firmware/sdk/launchers/desktop/src/main.cpp b/Firmware/sdk/launchers/desktop/src/main.cpp index 3c8e0c8..82d3832 100644 --- a/Firmware/sdk/launchers/desktop/src/main.cpp +++ b/Firmware/sdk/launchers/desktop/src/main.cpp @@ -1,6 +1,7 @@ #include "cardboy/apps/clock_app.hpp" #include "cardboy/apps/gameboy_app.hpp" #include "cardboy/apps/menu_app.hpp" +#include "cardboy/apps/settings_app.hpp" #include "cardboy/apps/tetris_app.hpp" #include "cardboy/backend/desktop_backend.hpp" #include "cardboy/sdk/app_system.hpp" @@ -19,6 +20,7 @@ int main() { cardboy::sdk::AppSystem system(context); system.registerApp(apps::createMenuAppFactory()); + system.registerApp(apps::createSettingsAppFactory()); system.registerApp(apps::createClockAppFactory()); system.registerApp(apps::createGameboyAppFactory()); system.registerApp(apps::createTetrisAppFactory());