From 03820657f4a7ba033ebc8f673c5e1702b2c0c764 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sun, 1 Mar 2026 10:50:24 +0100 Subject: [PATCH 1/3] Add sleep screen information overlay --- lib/I18n/translations/english.yaml | 1 + src/CrossPointSettings.h | 2 + src/SettingsList.h | 2 + src/activities/boot_sleep/SleepActivity.cpp | 159 ++++++++++++++++++++ src/activities/boot_sleep/SleepActivity.h | 1 + 5 files changed, 165 insertions(+) diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index f5b80b8e..9d92e919 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -337,3 +337,4 @@ STR_LINK: "[link]" STR_SCREENSHOT_BUTTON: "Take screenshot" STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: " STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)" +STR_SLEEP_COVER_OVERLAY: "Sleep Screen Info Overlay" diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 5ba7bde2..03713b4b 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -140,6 +140,8 @@ class CrossPointSettings { uint8_t sleepScreenCoverMode = FIT; // Sleep screen cover filter uint8_t sleepScreenCoverFilter = NO_FILTER; + // Apply information overlay with reading progress on sleep cover + uint8_t sleepCoverOverlay = 1; // Status bar settings (statusBar retained for migration only) uint8_t statusBar = FULL; uint8_t statusBarChapterPageCount = 1; diff --git a/src/SettingsList.h b/src/SettingsList.h index c10d58c6..9d3852a6 100644 --- a/src/SettingsList.h +++ b/src/SettingsList.h @@ -23,6 +23,8 @@ inline const std::vector& getSettingsList() { SettingInfo::Enum(StrId::STR_SLEEP_COVER_FILTER, &CrossPointSettings::sleepScreenCoverFilter, {StrId::STR_NONE_OPT, StrId::STR_FILTER_CONTRAST, StrId::STR_INVERTED}, "sleepScreenCoverFilter", StrId::STR_CAT_DISPLAY), + SettingInfo::Toggle(StrId::STR_SLEEP_COVER_OVERLAY, &CrossPointSettings::sleepCoverOverlay, "sleepCoverOverlay", + StrId::STR_CAT_DISPLAY), SettingInfo::Enum(StrId::STR_HIDE_BATTERY, &CrossPointSettings::hideBatteryPercentage, {StrId::STR_NEVER, StrId::STR_IN_READER, StrId::STR_ALWAYS}, "hideBatteryPercentage", StrId::STR_CAT_DISPLAY), diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index cd6de5fc..79a1ec3d 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -126,6 +126,133 @@ void SleepActivity::renderDefaultSleepScreen() const { renderer.displayBuffer(HalDisplay::HALF_REFRESH); } +std::string SleepActivity::getBookOverlayText(const std::string& bookPath) const { + std::string title; + std::string author; + std::string progressLine = "Reading..."; // Default progress text + + // Extract metadata based on file type + if (StringUtils::checkFileExtension(bookPath, ".xtc") || StringUtils::checkFileExtension(bookPath, ".xtch")) { + // Handle XTC file + Xtc xtc(bookPath, "/.crosspoint"); + if (xtc.load()) { + title = xtc.getTitle(); + author = xtc.getAuthor(); + + // Load progress - XTC stores current page + FsFile f; + if (Storage.openFileForRead("SLP", xtc.getCachePath() + "/progress.bin", f)) { + uint8_t data[4]; + if (f.read(data, 4) == 4) { + uint32_t currentPage = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); + uint32_t totalPages = xtc.getPageCount(); + float progress = xtc.calculateProgress(currentPage) * 100.0f; + char progressStr[32]; + snprintf(progressStr, sizeof(progressStr), "%lu/%u %.0f%%", (unsigned long)currentPage + 1, totalPages, + progress); + progressLine = progressStr; + } + f.close(); + } + } + } else if (StringUtils::checkFileExtension(bookPath, ".txt")) { + // Handle TXT file + Txt txt(bookPath, "/.crosspoint"); + if (txt.load()) { + title = txt.getTitle(); + // TXT files don't have author metadata + + // Load progress - TXT stores current page + FsFile f; + if (Storage.openFileForRead("SLP", txt.getCachePath() + "/progress.bin", f)) { + uint8_t data[4]; + if (f.read(data, 4) == 4) { + uint32_t currentPage = data[0] + (data[1] << 8); + + // Load total pages from page index cache + uint32_t totalPages = 0; + FsFile indexFile; + if (Storage.openFileForRead("SLP", txt.getCachePath() + "/index.bin", indexFile)) { + // Read numPages directly (it's at a fixed offset after headers) + // Skip: magic(4) + version(4) + fileSize(4) + cachedWidth(4) + cachedLines(4) + fontId(4) + margin(4) + + // alignment(4) = 32 bytes + indexFile.seek(32); + if (indexFile.read(reinterpret_cast(&totalPages), sizeof(totalPages)) == sizeof(totalPages)) { + // Successfully read totalPages + } + indexFile.close(); + } + + if (totalPages > 0) { + float progress = (currentPage + 1) * 100.0f / totalPages; + char progressStr[32]; + snprintf(progressStr, sizeof(progressStr), "%lu/%u %.0f%%", (unsigned long)currentPage + 1, totalPages, + progress); + progressLine = progressStr; + } else { + char progressStr[16]; + snprintf(progressStr, sizeof(progressStr), "Page %lu", (unsigned long)currentPage + 1); + progressLine = progressStr; + } + } + f.close(); + } + } + } else if (StringUtils::checkFileExtension(bookPath, ".epub")) { + // Handle EPUB file + Epub epub(bookPath, "/.crosspoint"); + if (epub.load(true, true)) { // Skip CSS loading for metadata only + title = epub.getTitle(); + author = epub.getAuthor(); + + // Load progress and get chapter information + FsFile f; + if (Storage.openFileForRead("SLP", epub.getCachePath() + "/progress.bin", f)) { + uint8_t data[6]; + if (f.read(data, 6) == 6) { + int currentSpineIndex = data[0] + (data[1] << 8); + int currentPage = data[2] + (data[3] << 8); + int pageCount = data[4] + (data[5] << 8); + if (pageCount > 0) { + float chapterProgress = static_cast(currentPage) / static_cast(pageCount); + float bookProgress = epub.calculateProgress(currentSpineIndex, chapterProgress) * 100.0f; + + // Get chapter name from TOC + const int tocIndex = epub.getTocIndexForSpineIndex(currentSpineIndex); + std::string chapterName; + if (tocIndex == -1) { + chapterName = "Unnamed Chapter"; + } else { + const auto tocItem = epub.getTocItem(tocIndex); + chapterName = tocItem.title; + } + + // Create chapter info line with chapter name and progress + char chapterProgressStr[128]; + snprintf(chapterProgressStr, sizeof(chapterProgressStr), "%s: %d/%d %.0f%%", chapterName.c_str(), + currentPage + 1, pageCount, bookProgress); + progressLine = chapterProgressStr; + } + } + f.close(); + } + } + } + + // Format overlay text (two lines) + std::string overlayText; + if (!title.empty()) { + overlayText = title; + if (!author.empty()) { + overlayText += " - " + author; + } + overlayText += "\n"; // New line + overlayText += progressLine; + } + + return overlayText; +} + void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { int x, y; const auto pageWidth = renderer.getScreenWidth(); @@ -178,6 +305,38 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { renderer.invertScreen(); } + // Draw overlay text if provided + const std::string overlayText = SETTINGS.sleepCoverOverlay ? getBookOverlayText(APP_STATE.openEpubPath) : ""; + if (!overlayText.empty()) { + // Split text into lines + size_t newlinePos = overlayText.find('\n'); + std::string line1 = (newlinePos != std::string::npos) ? overlayText.substr(0, newlinePos) : overlayText; + std::string line2 = (newlinePos != std::string::npos && newlinePos + 1 < overlayText.length()) + ? overlayText.substr(newlinePos + 1) + : ""; + + // Truncate lines if too long for the screen + line1 = renderer.truncatedText(UI_10_FONT_ID, line1.c_str(), pageWidth - 20); + line2 = renderer.truncatedText(UI_10_FONT_ID, line2.c_str(), pageWidth - 20); + + // Draw semi-transparent background rectangle at the bottom (taller for two lines) + const int overlayHeight = 50; + const int overlayY = pageHeight - overlayHeight - 20; // Position 20 pixels higher from bottom + renderer.fillRect(0, overlayY, pageWidth, overlayHeight, false); // White background + + // First line - positioned higher in the overlay + const int line1Y = overlayY + 18; // Position higher up + const int textX1 = (pageWidth - renderer.getTextWidth(UI_10_FONT_ID, line1.c_str())) / 2; + renderer.drawText(UI_10_FONT_ID, textX1, line1Y, line1.c_str(), true); // Black text + + // Second line - positioned lower in the overlay + if (!line2.empty()) { + const int line2Y = overlayY + 38; // Position lower down with more space + const int textX2 = (pageWidth - renderer.getTextWidth(UI_10_FONT_ID, line2.c_str())) / 2; + renderer.drawText(UI_10_FONT_ID, textX2, line2Y, line2.c_str(), true); // Black text + } + } + renderer.displayBuffer(HalDisplay::HALF_REFRESH); if (hasGreyscale) { diff --git a/src/activities/boot_sleep/SleepActivity.h b/src/activities/boot_sleep/SleepActivity.h index 87df8ba1..904740fb 100644 --- a/src/activities/boot_sleep/SleepActivity.h +++ b/src/activities/boot_sleep/SleepActivity.h @@ -15,4 +15,5 @@ class SleepActivity final : public Activity { void renderCoverSleepScreen() const; void renderBitmapSleepScreen(const Bitmap& bitmap) const; void renderBlankSleepScreen() const; + std::string getBookOverlayText(const std::string& bookPath) const; }; From 496e244cf0957f1df07a3efe5f5b21c0c2a68555 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sun, 1 Mar 2026 13:32:19 +0100 Subject: [PATCH 2/3] Review changes --- src/activities/boot_sleep/SleepActivity.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 79a1ec3d..6138bdb2 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -307,7 +307,11 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { // Draw overlay text if provided const std::string overlayText = SETTINGS.sleepCoverOverlay ? getBookOverlayText(APP_STATE.openEpubPath) : ""; - if (!overlayText.empty()) { + const auto drawOverlay = [&]() { + if (overlayText.empty()) { + return; + } + // Split text into lines size_t newlinePos = overlayText.find('\n'); std::string line1 = (newlinePos != std::string::npos) ? overlayText.substr(0, newlinePos) : overlayText; @@ -319,7 +323,7 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { line1 = renderer.truncatedText(UI_10_FONT_ID, line1.c_str(), pageWidth - 20); line2 = renderer.truncatedText(UI_10_FONT_ID, line2.c_str(), pageWidth - 20); - // Draw semi-transparent background rectangle at the bottom (taller for two lines) + // Draw white background rectangle at the bottom (taller for two lines) const int overlayHeight = 50; const int overlayY = pageHeight - overlayHeight - 20; // Position 20 pixels higher from bottom renderer.fillRect(0, overlayY, pageWidth, overlayHeight, false); // White background @@ -335,7 +339,9 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { const int textX2 = (pageWidth - renderer.getTextWidth(UI_10_FONT_ID, line2.c_str())) / 2; renderer.drawText(UI_10_FONT_ID, textX2, line2Y, line2.c_str(), true); // Black text } - } + }; + + drawOverlay(); renderer.displayBuffer(HalDisplay::HALF_REFRESH); @@ -344,12 +350,14 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const { renderer.clearScreen(0x00); renderer.setRenderMode(GfxRenderer::GRAYSCALE_LSB); renderer.drawBitmap(bitmap, x, y, pageWidth, pageHeight, cropX, cropY); + drawOverlay(); renderer.copyGrayscaleLsbBuffers(); bitmap.rewindToData(); renderer.clearScreen(0x00); renderer.setRenderMode(GfxRenderer::GRAYSCALE_MSB); renderer.drawBitmap(bitmap, x, y, pageWidth, pageHeight, cropX, cropY); + drawOverlay(); renderer.copyGrayscaleMsbBuffers(); renderer.displayGrayBuffer(); From 60fd8781620660e88c8c53548d0bdba5fb1dc24e Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sun, 1 Mar 2026 21:30:06 +0100 Subject: [PATCH 3/3] Amend xtc logic --- src/activities/boot_sleep/SleepActivity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 6138bdb2..c1667bad 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -144,7 +144,7 @@ std::string SleepActivity::getBookOverlayText(const std::string& bookPath) const if (Storage.openFileForRead("SLP", xtc.getCachePath() + "/progress.bin", f)) { uint8_t data[4]; if (f.read(data, 4) == 4) { - uint32_t currentPage = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); + uint32_t currentPage = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); uint32_t totalPages = xtc.getPageCount(); float progress = xtc.calculateProgress(currentPage) * 100.0f; char progressStr[32];