From 7eb38dc7a55a60bd0a455565c316d1515cf3b72f Mon Sep 17 00:00:00 2001 From: Dave Allie Date: Wed, 17 Dec 2025 20:44:55 +1100 Subject: [PATCH] Add home screen --- src/main.cpp | 16 +++-- src/screens/EpubReaderScreen.cpp | 2 +- src/screens/EpubReaderScreen.h | 6 +- src/screens/FileSelectionScreen.cpp | 6 +- src/screens/FileSelectionScreen.h | 6 +- src/screens/HomeScreen.cpp | 99 +++++++++++++++++++++++++++++ src/screens/HomeScreen.h | 31 +++++++++ 7 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 src/screens/HomeScreen.cpp create mode 100644 src/screens/HomeScreen.h diff --git a/src/main.cpp b/src/main.cpp index eb3bc0b..00a4b45 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ #include "screens/EpubReaderScreen.h" #include "screens/FileSelectionScreen.h" #include "screens/FullScreenMessageScreen.h" +#include "screens/HomeScreen.h" #include "screens/SettingsScreen.h" #include "screens/SleepScreen.h" @@ -150,6 +151,7 @@ void enterDeepSleep() { } void onGoHome(); +void onGoToFileSelection(); void onSelectEpubFile(const std::string& path) { exitScreen(); enterNewScreen(new FullScreenMessageScreen(renderer, inputManager, "Loading...")); @@ -159,16 +161,21 @@ void onSelectEpubFile(const std::string& path) { appState.openEpubPath = path; appState.saveToFile(); exitScreen(); - enterNewScreen(new EpubReaderScreen(renderer, inputManager, std::move(epub), onGoHome)); + enterNewScreen(new EpubReaderScreen(renderer, inputManager, std::move(epub), onGoToFileSelection)); } else { exitScreen(); enterNewScreen( new FullScreenMessageScreen(renderer, inputManager, "Failed to load epub", REGULAR, EInkDisplay::HALF_REFRESH)); delay(2000); - onGoHome(); + onGoToFileSelection(); } } +void onGoToFileSelection() { + exitScreen(); + enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile, onGoHome)); +} + void onGoToSettings() { exitScreen(); enterNewScreen(new SettingsScreen(renderer, inputManager, onGoHome)); @@ -176,7 +183,7 @@ void onGoToSettings() { void onGoHome() { exitScreen(); - enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile, onGoToSettings)); + enterNewScreen(new HomeScreen(renderer, inputManager, onGoToFileSelection, onGoToSettings)); } void setup() { @@ -221,8 +228,7 @@ void setup() { } } - exitScreen(); - enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile, onGoToSettings)); + onGoHome(); // Ensure we're not still holding the power button before leaving setup waitForPowerRelease(); diff --git a/src/screens/EpubReaderScreen.cpp b/src/screens/EpubReaderScreen.cpp index 4e13f3d..84f9c26 100644 --- a/src/screens/EpubReaderScreen.cpp +++ b/src/screens/EpubReaderScreen.cpp @@ -98,7 +98,7 @@ void EpubReaderScreen::handleInput() { } if (inputManager.wasPressed(InputManager::BTN_BACK)) { - onGoHome(); + onGoBack(); return; } diff --git a/src/screens/EpubReaderScreen.h b/src/screens/EpubReaderScreen.h index 4ef8bef..0042c29 100644 --- a/src/screens/EpubReaderScreen.h +++ b/src/screens/EpubReaderScreen.h @@ -17,7 +17,7 @@ class EpubReaderScreen final : public Screen { int nextPageNumber = 0; int pagesUntilFullRefresh = 0; bool updateRequired = false; - const std::function onGoHome; + const std::function onGoBack; static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); @@ -27,8 +27,8 @@ class EpubReaderScreen final : public Screen { public: explicit EpubReaderScreen(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr epub, - const std::function& onGoHome) - : Screen(renderer, inputManager), epub(std::move(epub)), onGoHome(onGoHome) {} + const std::function& onGoBack) + : Screen(renderer, inputManager), epub(std::move(epub)), onGoBack(onGoBack) {} void onEnter() override; void onExit() override; void handleInput() override; diff --git a/src/screens/FileSelectionScreen.cpp b/src/screens/FileSelectionScreen.cpp index 09f2efc..ce4d000 100644 --- a/src/screens/FileSelectionScreen.cpp +++ b/src/screens/FileSelectionScreen.cpp @@ -98,8 +98,8 @@ void FileSelectionScreen::handleInput() { loadFiles(); updateRequired = true; } else { - // At root level, go to settings - onSettingsOpen(); + // At root level, go back home + onGoHome(); } } else if (prevPressed) { selectorIndex = (selectorIndex + files.size() - 1) % files.size(); @@ -129,7 +129,7 @@ void FileSelectionScreen::render() const { renderer.drawCenteredText(READER_FONT_ID, 10, "CrossPoint Reader", true, BOLD); // Help text - renderer.drawText(SMALL_FONT_ID, 20, GfxRenderer::getScreenHeight() - 30, "Press BACK for Settings"); + renderer.drawText(SMALL_FONT_ID, 20, GfxRenderer::getScreenHeight() - 30, "Press BACK for Home"); if (files.empty()) { renderer.drawText(UI_FONT_ID, 20, 60, "No EPUBs found"); diff --git a/src/screens/FileSelectionScreen.h b/src/screens/FileSelectionScreen.h index 00947a5..aaa076d 100644 --- a/src/screens/FileSelectionScreen.h +++ b/src/screens/FileSelectionScreen.h @@ -17,7 +17,7 @@ class FileSelectionScreen final : public Screen { int selectorIndex = 0; bool updateRequired = false; const std::function onSelect; - const std::function onSettingsOpen; + const std::function onGoHome; static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); @@ -27,8 +27,8 @@ class FileSelectionScreen final : public Screen { public: explicit FileSelectionScreen(GfxRenderer& renderer, InputManager& inputManager, const std::function& onSelect, - const std::function& onSettingsOpen) - : Screen(renderer, inputManager), onSelect(onSelect), onSettingsOpen(onSettingsOpen) {} + const std::function& onGoHome) + : Screen(renderer, inputManager), onSelect(onSelect), onGoHome(onGoHome) {} void onEnter() override; void onExit() override; void handleInput() override; diff --git a/src/screens/HomeScreen.cpp b/src/screens/HomeScreen.cpp new file mode 100644 index 0000000..168e0fc --- /dev/null +++ b/src/screens/HomeScreen.cpp @@ -0,0 +1,99 @@ +#include "HomeScreen.h" + +#include +#include + +#include "config.h" + +void HomeScreen::taskTrampoline(void* param) { + auto* self = static_cast(param); + self->displayTaskLoop(); +} + +void HomeScreen::onEnter() { + renderingMutex = xSemaphoreCreateMutex(); + + selectorIndex = 0; + + // Trigger first update + updateRequired = true; + + xTaskCreate(&HomeScreen::taskTrampoline, "HomeScreenTask", + 2048, // Stack size + this, // Parameters + 1, // Priority + &displayTaskHandle // Task handle + ); +} + +void HomeScreen::onExit() { + // Wait until not rendering to delete task to avoid killing mid-instruction to EPD + xSemaphoreTake(renderingMutex, portMAX_DELAY); + if (displayTaskHandle) { + vTaskDelete(displayTaskHandle); + displayTaskHandle = nullptr; + } + vSemaphoreDelete(renderingMutex); + renderingMutex = nullptr; +} + +void HomeScreen::handleInput() { + const bool prevPressed = + inputManager.wasPressed(InputManager::BTN_UP) || inputManager.wasPressed(InputManager::BTN_LEFT); + const bool nextPressed = + inputManager.wasPressed(InputManager::BTN_DOWN) || inputManager.wasPressed(InputManager::BTN_RIGHT); + + if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) { + if (selectorIndex == 0) { + onFileSelectionOpen(); + } else if (selectorIndex == 1) { + onSettingsOpen(); + } + } else if (prevPressed) { + selectorIndex = (selectorIndex + menuItemCount - 1) % menuItemCount; + updateRequired = true; + } else if (nextPressed) { + selectorIndex = (selectorIndex + 1) % menuItemCount; + updateRequired = true; + } +} + +void HomeScreen::displayTaskLoop() { + while (true) { + if (updateRequired) { + updateRequired = false; + xSemaphoreTake(renderingMutex, portMAX_DELAY); + render(); + xSemaphoreGive(renderingMutex); + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } +} + +void HomeScreen::render() const { + renderer.clearScreen(); + + const auto pageWidth = GfxRenderer::getScreenWidth(); + const auto pageHeight = GfxRenderer::getScreenHeight(); + renderer.drawCenteredText(READER_FONT_ID, 10, "CrossPoint Reader", true, BOLD); + + // Draw selection + renderer.fillRect(0, 60 + selectorIndex * 30 + 2, pageWidth - 1, 30); + renderer.drawText(UI_FONT_ID, 20, 60, "Read", selectorIndex != 0); + renderer.drawText(UI_FONT_ID, 20, 90, "Settings", selectorIndex != 1); + + renderer.drawRect(25, pageHeight - 40, 106, 40); + renderer.drawText(UI_FONT_ID, 25 + (105 - renderer.getTextWidth(UI_FONT_ID, "Back")) / 2, pageHeight - 35, "Back"); + + renderer.drawRect(130, pageHeight - 40, 106, 40); + renderer.drawText(UI_FONT_ID, 130 + (105 - renderer.getTextWidth(UI_FONT_ID, "Confirm")) / 2, pageHeight - 35, + "Confirm"); + + renderer.drawRect(245, pageHeight - 40, 106, 40); + renderer.drawText(UI_FONT_ID, 245 + (105 - renderer.getTextWidth(UI_FONT_ID, "Left")) / 2, pageHeight - 35, "Left"); + + renderer.drawRect(350, pageHeight - 40, 106, 40); + renderer.drawText(UI_FONT_ID, 350 + (105 - renderer.getTextWidth(UI_FONT_ID, "Right")) / 2, pageHeight - 35, "Right"); + + renderer.displayBuffer(); +} diff --git a/src/screens/HomeScreen.h b/src/screens/HomeScreen.h new file mode 100644 index 0000000..dbdd21b --- /dev/null +++ b/src/screens/HomeScreen.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include + +#include + +#include "Screen.h" + +class HomeScreen final : public Screen { + TaskHandle_t displayTaskHandle = nullptr; + SemaphoreHandle_t renderingMutex = nullptr; + int selectorIndex = 0; + bool updateRequired = false; + const std::function onFileSelectionOpen; + const std::function onSettingsOpen; + + static constexpr int menuItemCount = 2; + + static void taskTrampoline(void* param); + [[noreturn]] void displayTaskLoop(); + void render() const; + + public: + explicit HomeScreen(GfxRenderer& renderer, InputManager& inputManager, + const std::function& onFileSelectionOpen, const std::function& onSettingsOpen) + : Screen(renderer, inputManager), onFileSelectionOpen(onFileSelectionOpen), onSettingsOpen(onSettingsOpen) {} + void onEnter() override; + void onExit() override; + void handleInput() override; +};