diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index 8959072..fe5e2a0 100644 --- a/src/CrossPointSettings.cpp +++ b/src/CrossPointSettings.cpp @@ -23,7 +23,7 @@ bool CrossPointSettings::saveToFile() const { std::ofstream outputFile(SETTINGS_FILE); serialization::writePod(outputFile, SETTINGS_FILE_VERSION); serialization::writePod(outputFile, SETTINGS_COUNT); - serialization::writePod(outputFile, whiteSleepScreen); + serialization::writePod(outputFile, sleepScreen); serialization::writePod(outputFile, extraParagraphSpacing); serialization::writePod(outputFile, shortPwrBtn); outputFile.close(); @@ -54,7 +54,7 @@ bool CrossPointSettings::loadFromFile() { // load settings that exist uint8_t settingsRead = 0; do { - serialization::readPod(inputFile, whiteSleepScreen); + serialization::readPod(inputFile, sleepScreen); if (++settingsRead >= fileSettingsCount) break; serialization::readPod(inputFile, extraParagraphSpacing); if (++settingsRead >= fileSettingsCount) break; diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index d6ad766..14c3332 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -15,8 +15,11 @@ class CrossPointSettings { CrossPointSettings(const CrossPointSettings&) = delete; CrossPointSettings& operator=(const CrossPointSettings&) = delete; + // Should match with SettingsActivity text + enum SLEEP_SCREEN_MODE { DARK = 0, LIGHT = 1, CUSTOM = 2, COVER = 3 }; + // Sleep screen settings - uint8_t whiteSleepScreen = 0; + uint8_t sleepScreen = DARK; // Text rendering settings uint8_t extraParagraphSpacing = 1; // Duration of the power button press diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 900a0db..4200c4e 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -1,16 +1,45 @@ #include "SleepActivity.h" +#include #include #include #include #include "CrossPointSettings.h" +#include "CrossPointState.h" #include "config.h" #include "images/CrossLarge.h" void SleepActivity::onEnter() { renderPopup("Entering Sleep..."); + + if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM) { + return renderCustomSleepScreen(); + } + + if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::COVER) { + return renderCoverSleepScreen(); + } + + renderDefaultSleepScreen(); +} + +void SleepActivity::renderPopup(const char* message) const { + const int textWidth = renderer.getTextWidth(READER_FONT_ID, message); + constexpr int margin = 20; + const int x = (renderer.getScreenWidth() - textWidth - margin * 2) / 2; + constexpr int y = 117; + const int w = textWidth + margin * 2; + const int h = renderer.getLineHeight(READER_FONT_ID) + margin * 2; + // renderer.clearScreen(); + renderer.fillRect(x + 5, y + 5, w - 10, h - 10, false); + renderer.drawText(READER_FONT_ID, x + margin, y + margin, message); + renderer.drawRect(x + 5, y + 5, w - 10, h - 10); + renderer.displayBuffer(); +} + +void SleepActivity::renderCustomSleepScreen() const { // Check if we have a /sleep directory auto dir = SD.open("/sleep"); if (dir && dir.isDirectory()) { @@ -28,31 +57,31 @@ void SleepActivity::onEnter() { } if (filename.substr(filename.length() - 4) != ".bmp") { - Serial.printf("[%lu] [Slp] Skipping non-.bmp file name: %s\n", millis(), file.name()); + Serial.printf("[%lu] [SLP] Skipping non-.bmp file name: %s\n", millis(), file.name()); file.close(); continue; } Bitmap bitmap(file); if (bitmap.parseHeaders() != BmpReaderError::Ok) { - Serial.printf("[%lu] [Slp] Skipping invalid BMP file: %s\n", millis(), file.name()); + Serial.printf("[%lu] [SLP] Skipping invalid BMP file: %s\n", millis(), file.name()); file.close(); continue; } files.emplace_back(filename); file.close(); } - int numFiles = files.size(); + const auto numFiles = files.size(); if (numFiles > 0) { // Generate a random number between 1 and numFiles - int randomFileIndex = random(numFiles); - auto filename = "/sleep/" + files[randomFileIndex]; + const auto randomFileIndex = random(numFiles); + const auto filename = "/sleep/" + files[randomFileIndex]; auto file = SD.open(filename.c_str()); if (file) { - Serial.printf("[%lu] [Slp] Randomly loading: /sleep/%s\n", millis(), files[randomFileIndex].c_str()); + Serial.printf("[%lu] [SLP] Randomly loading: /sleep/%s\n", millis(), files[randomFileIndex].c_str()); delay(100); Bitmap bitmap(file); if (bitmap.parseHeaders() == BmpReaderError::Ok) { - renderCustomSleepScreen(bitmap); + renderBitmapSleepScreen(bitmap); dir.close(); return; } @@ -67,8 +96,8 @@ void SleepActivity::onEnter() { if (file) { Bitmap bitmap(file); if (bitmap.parseHeaders() == BmpReaderError::Ok) { - Serial.printf("[%lu] [Slp] Loading: /sleep.bmp\n", millis()); - renderCustomSleepScreen(bitmap); + Serial.printf("[%lu] [SLP] Loading: /sleep.bmp\n", millis()); + renderBitmapSleepScreen(bitmap); return; } } @@ -76,41 +105,27 @@ void SleepActivity::onEnter() { renderDefaultSleepScreen(); } -void SleepActivity::renderPopup(const char* message) const { - const int textWidth = renderer.getTextWidth(READER_FONT_ID, message); - constexpr int margin = 20; - const int x = (GfxRenderer::getScreenWidth() - textWidth - margin * 2) / 2; - constexpr int y = 117; - const int w = textWidth + margin * 2; - const int h = renderer.getLineHeight(READER_FONT_ID) + margin * 2; - // renderer.clearScreen(); - renderer.fillRect(x + 5, y + 5, w - 10, h - 10, false); - renderer.drawText(READER_FONT_ID, x + margin, y + margin, message); - renderer.drawRect(x + 5, y + 5, w - 10, h - 10); - renderer.displayBuffer(); -} - void SleepActivity::renderDefaultSleepScreen() const { - const auto pageWidth = GfxRenderer::getScreenWidth(); - const auto pageHeight = GfxRenderer::getScreenHeight(); + const auto pageWidth = renderer.getScreenWidth(); + const auto pageHeight = renderer.getScreenHeight(); renderer.clearScreen(); renderer.drawImage(CrossLarge, (pageWidth - 128) / 2, (pageHeight - 128) / 2, 128, 128); renderer.drawCenteredText(UI_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, BOLD); renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "SLEEPING"); - // Apply white screen if enabled in settings - if (!SETTINGS.whiteSleepScreen) { + // Make sleep screen dark unless light is selected in settings + if (SETTINGS.sleepScreen != CrossPointSettings::SLEEP_SCREEN_MODE::LIGHT) { renderer.invertScreen(); } renderer.displayBuffer(EInkDisplay::HALF_REFRESH); } -void SleepActivity::renderCustomSleepScreen(const Bitmap& bitmap) const { +void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { int x, y; - const auto pageWidth = GfxRenderer::getScreenWidth(); - const auto pageHeight = GfxRenderer::getScreenHeight(); + const auto pageWidth = renderer.getScreenWidth(); + const auto pageHeight = renderer.getScreenHeight(); if (bitmap.getWidth() > pageWidth || bitmap.getHeight() > pageHeight) { // image will scale, make sure placement is right @@ -153,3 +168,26 @@ void SleepActivity::renderCustomSleepScreen(const Bitmap& bitmap) const { renderer.setRenderMode(GfxRenderer::BW); } } + +void SleepActivity::renderCoverSleepScreen() const { + Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint"); + if (!lastEpub.load()) { + Serial.println("[SLP] Failed to load last epub"); + return renderDefaultSleepScreen(); + } + if (!lastEpub.generateCoverBmp()) { + Serial.println("[SLP] Failed to generate cover bmp"); + return renderDefaultSleepScreen(); + } + + auto file = SD.open(lastEpub.getCoverBmpPath().c_str(), FILE_READ); + if (file) { + Bitmap bitmap(file); + if (bitmap.parseHeaders() == BmpReaderError::Ok) { + renderBitmapSleepScreen(bitmap); + return; + } + } + + renderDefaultSleepScreen(); +} diff --git a/src/activities/boot_sleep/SleepActivity.h b/src/activities/boot_sleep/SleepActivity.h index defc1d5..2112199 100644 --- a/src/activities/boot_sleep/SleepActivity.h +++ b/src/activities/boot_sleep/SleepActivity.h @@ -9,7 +9,9 @@ class SleepActivity final : public Activity { void onEnter() override; private: - void renderDefaultSleepScreen() const; - void renderCustomSleepScreen(const Bitmap& bitmap) const; void renderPopup(const char* message) const; + void renderDefaultSleepScreen() const; + void renderCustomSleepScreen() const; + void renderCoverSleepScreen() const; + void renderBitmapSleepScreen(const Bitmap& bitmap) const; }; diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index b3acf3f..29f6876 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -6,11 +6,14 @@ #include "config.h" // Define the static settings list - -const SettingInfo SettingsActivity::settingsList[settingsCount] = { - {"White Sleep Screen", SettingType::TOGGLE, &CrossPointSettings::whiteSleepScreen}, - {"Extra Paragraph Spacing", SettingType::TOGGLE, &CrossPointSettings::extraParagraphSpacing}, - {"Short Power Button Click", SettingType::TOGGLE, &CrossPointSettings::shortPwrBtn}}; +namespace { +constexpr int settingsCount = 3; +const SettingInfo settingsList[settingsCount] = { + // Should match with SLEEP_SCREEN_MODE + {"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}}, + {"Extra Paragraph Spacing", SettingType::TOGGLE, &CrossPointSettings::extraParagraphSpacing, {}}, + {"Short Power Button Click", SettingType::TOGGLE, &CrossPointSettings::shortPwrBtn, {}}}; +} // namespace void SettingsActivity::taskTrampoline(void* param) { auto* self = static_cast(param); @@ -81,15 +84,18 @@ void SettingsActivity::toggleCurrentSetting() { const auto& setting = settingsList[selectedSettingIndex]; - // Only toggle if it's a toggle type and has a value pointer - if (setting.type != SettingType::TOGGLE || setting.valuePtr == nullptr) { + if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) { + // Toggle the boolean value using the member pointer + const bool currentValue = SETTINGS.*(setting.valuePtr); + SETTINGS.*(setting.valuePtr) = !currentValue; + } else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) { + const uint8_t currentValue = SETTINGS.*(setting.valuePtr); + SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast(setting.enumValues.size()); + } else { + // Only toggle if it's a toggle type and has a value pointer return; } - // Toggle the boolean value using the member pointer - bool currentValue = SETTINGS.*(setting.valuePtr); - SETTINGS.*(setting.valuePtr) = !currentValue; - // Save settings when they change SETTINGS.saveToFile(); } @@ -129,8 +135,13 @@ void SettingsActivity::render() const { // Draw value based on setting type if (settingsList[i].type == SettingType::TOGGLE && settingsList[i].valuePtr != nullptr) { - bool value = SETTINGS.*(settingsList[i].valuePtr); + const bool value = SETTINGS.*(settingsList[i].valuePtr); renderer.drawText(UI_FONT_ID, pageWidth - 80, settingY, value ? "ON" : "OFF"); + } else if (settingsList[i].type == SettingType::ENUM && settingsList[i].valuePtr != nullptr) { + const uint8_t value = SETTINGS.*(settingsList[i].valuePtr); + auto valueText = settingsList[i].enumValues[value]; + const auto width = renderer.getTextWidth(UI_FONT_ID, valueText.c_str()); + renderer.drawText(UI_FONT_ID, pageWidth - 50 - width, settingY, valueText.c_str()); } } diff --git a/src/activities/settings/SettingsActivity.h b/src/activities/settings/SettingsActivity.h index 7843a5c..333f467 100644 --- a/src/activities/settings/SettingsActivity.h +++ b/src/activities/settings/SettingsActivity.h @@ -12,13 +12,14 @@ class CrossPointSettings; -enum class SettingType { TOGGLE }; +enum class SettingType { TOGGLE, ENUM }; // Structure to hold setting information struct SettingInfo { const char* name; // Display name of the setting SettingType type; // Type of setting - uint8_t CrossPointSettings::* valuePtr; // Pointer to member in CrossPointSettings (for TOGGLE) + uint8_t CrossPointSettings::* valuePtr; // Pointer to member in CrossPointSettings (for TOGGLE/ENUM) + std::vector enumValues; }; class SettingsActivity final : public Activity { @@ -28,10 +29,6 @@ class SettingsActivity final : public Activity { int selectedSettingIndex = 0; // Currently selected setting const std::function onGoHome; - // Static settings list - static constexpr int settingsCount = 3; // Number of settings - static const SettingInfo settingsList[settingsCount]; - static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); void render() const;