diff --git a/USER_GUIDE.md b/USER_GUIDE.md index e4d72b0c..1c523d2a 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -148,6 +148,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 b5aa7710..5b691589 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -411,7 +411,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 { @@ -669,7 +671,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 86ddc8fc..a02a3f8d 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -28,6 +28,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, @@ -36,7 +37,8 @@ class GfxRenderer { void rotateCoordinates(int x, int y, int* rotatedX, int* rotatedY) 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; @@ -51,6 +53,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/open-x4-sdk b/open-x4-sdk index bd4e6707..c8ce3949 160000 --- a/open-x4-sdk +++ b/open-x4-sdk @@ -1 +1 @@ -Subproject commit bd4e6707503ab9c97d13ee0d8f8c69e9ff03cd12 +Subproject commit c8ce3949b3368329c290ca1858e65dc3416fc591 diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index 232c7c57..f74b0c78 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 = 23; +constexpr uint8_t SETTINGS_COUNT = 24; constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin"; } // namespace @@ -61,6 +61,7 @@ bool CrossPointSettings::saveToFile() const { serialization::writeString(outputFile, std::string(opdsPassword)); serialization::writePod(outputFile, sleepScreenCoverFilter); // New fields added at end for backward compatibility + serialization::writePod(outputFile, fadingFix); outputFile.close(); Serial.printf("[%lu] [CPS] Settings saved to file\n", millis()); @@ -148,6 +149,8 @@ bool CrossPointSettings::loadFromFile() { if (++settingsRead >= fileSettingsCount) break; readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT); 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 c450d348..6ccde287 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -137,6 +137,8 @@ class CrossPointSettings { uint8_t hideBatteryPercentage = HIDE_NEVER; // Long-press chapter skip on side buttons uint8_t longPressChapterSkip = 1; + // Sunlight fading compensation + uint8_t fadingFix = 0; ~CrossPointSettings() = default; diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 7316db05..a1ca135d 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -11,7 +11,7 @@ const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"}; namespace { -constexpr int displaySettingsCount = 6; +constexpr int displaySettingsCount = 7; const SettingInfo displaySettings[displaySettingsCount] = { // Should match with SLEEP_SCREEN_MODE SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}), @@ -22,7 +22,9 @@ const SettingInfo displaySettings[displaySettingsCount] = { {"None", "No Progress", "Full w/ Percentage", "Full w/ Progress Bar", "Progress Bar"}), SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}), SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency, - {"1 page", "5 pages", "10 pages", "15 pages", "30 pages"})}; + {"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}), + SettingInfo::Enum("Sunlight Fading Fix", &CrossPointSettings::fadingFix, {"OFF", "ON"}), +}; constexpr int readerSettingsCount = 9; const SettingInfo readerSettings[readerSettingsCount] = { diff --git a/src/main.cpp b/src/main.cpp index 89c4e13c..ea7a6e6f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -344,6 +344,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());