diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 22c6a941..c38b2ceb 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -150,6 +150,9 @@ The Settings screen allows you to configure the device's behavior. There are a f - **Reader Paragraph Alignment**: Set the alignment of paragraphs; options are "Justified" (default), "Left", "Center", or "Right". - **Time to Sleep**: Set the duration of inactivity before the device automatically goes to sleep. - **Refresh Frequency**: Set how often the screen does a full refresh while reading to reduce ghosting. +- **Sunlight Fading Fix**: Configure whether to enable a software-fix for the issue where white X4 models may fade when used in direct sunlight + - "OFF" (default) - Disable the fix + - "ON" - Enable the fix - **OPDS Browser**: Configure OPDS server settings for browsing and downloading books. Set the server URL (for Calibre Content Server, add `/opds` to the end), and optionally configure username and password for servers requiring authentication. Note: Only HTTP Basic authentication is supported. If using Calibre Content Server with authentication enabled, you must set it to use Basic authentication instead of the default Digest authentication. - **Check for updates**: Check for firmware updates over WiFi. diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 17ad99c6..98cc0ee9 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -624,7 +624,9 @@ void GfxRenderer::invertScreen() const { } } -void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const { display.displayBuffer(refreshMode); } +void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const { + display.displayBuffer(refreshMode, fadingFix); +} std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth, const EpdFontFamily::Style style) const { @@ -817,7 +819,7 @@ void GfxRenderer::copyGrayscaleLsbBuffers() const { display.copyGrayscaleLsbBuff void GfxRenderer::copyGrayscaleMsbBuffers() const { display.copyGrayscaleMsbBuffers(display.getFrameBuffer()); } -void GfxRenderer::displayGrayBuffer() const { display.displayGrayBuffer(); } +void GfxRenderer::displayGrayBuffer() const { display.displayGrayBuffer(fadingFix); } void GfxRenderer::freeBwBufferChunks() { for (auto& bwBufferChunk : bwBufferChunks) { diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index 46d825d5..b84e7993 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -32,6 +32,7 @@ class GfxRenderer { HalDisplay& display; RenderMode renderMode; Orientation orientation; + bool fadingFix; uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr}; std::map fontMap; void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState, @@ -42,7 +43,8 @@ class GfxRenderer { void fillArc(int maxRadius, int cx, int cy, int xDir, int yDir, Color color) const; public: - explicit GfxRenderer(HalDisplay& halDisplay) : display(halDisplay), renderMode(BW), orientation(Portrait) {} + explicit GfxRenderer(HalDisplay& halDisplay) + : display(halDisplay), renderMode(BW), orientation(Portrait), fadingFix(false) {} ~GfxRenderer() { freeBwBufferChunks(); } static constexpr int VIEWABLE_MARGIN_TOP = 9; @@ -57,6 +59,9 @@ class GfxRenderer { void setOrientation(const Orientation o) { orientation = o; } Orientation getOrientation() const { return orientation; } + // Fading fix control + void setFadingFix(const bool enabled) { fadingFix = enabled; } + // Screen ops int getScreenWidth() const; int getScreenHeight() const; diff --git a/lib/hal/HalDisplay.cpp b/lib/hal/HalDisplay.cpp index 6f69d7fc..0fafdbb5 100644 --- a/lib/hal/HalDisplay.cpp +++ b/lib/hal/HalDisplay.cpp @@ -28,7 +28,9 @@ EInkDisplay::RefreshMode convertRefreshMode(HalDisplay::RefreshMode mode) { } } -void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode) { einkDisplay.displayBuffer(convertRefreshMode(mode)); } +void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode, bool turnOffScreen) { + einkDisplay.displayBuffer(convertRefreshMode(mode), turnOffScreen); +} void HalDisplay::refreshDisplay(HalDisplay::RefreshMode mode, bool turnOffScreen) { einkDisplay.refreshDisplay(convertRefreshMode(mode), turnOffScreen); @@ -48,4 +50,4 @@ void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) { einkDisplay void HalDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) { einkDisplay.cleanupGrayscaleBuffers(bwBuffer); } -void HalDisplay::displayGrayBuffer() { einkDisplay.displayGrayBuffer(); } +void HalDisplay::displayGrayBuffer(bool turnOffScreen) { einkDisplay.displayGrayBuffer(turnOffScreen); } diff --git a/lib/hal/HalDisplay.h b/lib/hal/HalDisplay.h index 6eb7156b..238832b0 100644 --- a/lib/hal/HalDisplay.h +++ b/lib/hal/HalDisplay.h @@ -31,7 +31,7 @@ class HalDisplay { void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem = false) const; - void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH); + void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); void refreshDisplay(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); // Power management @@ -45,7 +45,7 @@ class HalDisplay { void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer); void cleanupGrayscaleBuffers(const uint8_t* bwBuffer); - void displayGrayBuffer(); + void displayGrayBuffer(bool turnOffScreen = false); private: EInkDisplay einkDisplay; diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index c31eaea2..da287046 100644 --- a/src/CrossPointSettings.cpp +++ b/src/CrossPointSettings.cpp @@ -22,7 +22,7 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) { namespace { constexpr uint8_t SETTINGS_FILE_VERSION = 1; // Increment this when adding new persisted settings fields -constexpr uint8_t SETTINGS_COUNT = 28; +constexpr uint8_t SETTINGS_COUNT = 29; constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin"; // Validate front button mapping to ensure each hardware button is unique. @@ -116,6 +116,7 @@ bool CrossPointSettings::saveToFile() const { serialization::writePod(outputFile, frontButtonConfirm); serialization::writePod(outputFile, frontButtonLeft); serialization::writePod(outputFile, frontButtonRight); + serialization::writePod(outputFile, fadingFix); // New fields added at end for backward compatibility outputFile.close(); @@ -216,6 +217,9 @@ bool CrossPointSettings::loadFromFile() { if (++settingsRead >= fileSettingsCount) break; readAndValidate(inputFile, frontButtonRight, FRONT_BUTTON_HARDWARE_COUNT); frontButtonMappingRead = true; + if (++settingsRead >= fileSettingsCount) break; + serialization::readPod(inputFile, fadingFix); + if (++settingsRead >= fileSettingsCount) break; // New fields added at end for backward compatibility } while (false); diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 1d85e8d8..8fa4c624 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -165,6 +165,8 @@ class CrossPointSettings { uint8_t longPressChapterSkip = 1; // UI Theme uint8_t uiTheme = LYRA; + // Sunlight fading compensation + uint8_t fadingFix = 0; ~CrossPointSettings() = default; diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 5bf04bfd..5bfaa2c2 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -17,7 +17,7 @@ const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader namespace { constexpr int changeTabsMs = 700; -constexpr int displaySettingsCount = 7; +constexpr int displaySettingsCount = 8; const SettingInfo displaySettings[displaySettingsCount] = { // Should match with SLEEP_SCREEN_MODE SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, @@ -31,6 +31,7 @@ const SettingInfo displaySettings[displaySettingsCount] = { SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency, {"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}), SettingInfo::Enum("UI Theme", &CrossPointSettings::uiTheme, {"Classic", "Lyra"}), + SettingInfo::Toggle("Sunlight Fading Fix", &CrossPointSettings::fadingFix), }; constexpr int readerSettingsCount = 9; diff --git a/src/main.cpp b/src/main.cpp index 03ed0b18..e1c74038 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -355,6 +355,8 @@ void loop() { gpio.update(); + renderer.setFadingFix(SETTINGS.fadingFix); + if (Serial && millis() - lastMemPrint >= 10000) { Serial.printf("[%lu] [MEM] Free: %d bytes, Total: %d bytes, Min Free: %d bytes\n", millis(), ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getMinFreeHeap());