Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@

### Fixed

- On the X4 Pro, the e-ink panel is powered off before every restart (after an OTA or SD card update, or a silent restart when leaving Wi-Fi) instead of staying powered until the reset.
- File Transfer choices no longer appear preselected when opened on a touch device.
- On the X4 Pro and Sticky, the Home screen no longer stalls waiting for the SD card while the Library indexes in the background.
- Small images that are scaled up, such as a short progressive JPEG, are now cached after the first draw instead of being decoded again on every screen refresh.
Expand Down
8 changes: 8 additions & 0 deletions src/activities/ActivityManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,10 @@ RenderLock::RenderLock(const Mode mode) {

RenderLock::RenderLock([[maybe_unused]] Activity&, const Mode mode) : RenderLock(mode) {}

RenderLock::RenderLock(const unsigned long timeoutMs) {
isLocked = xSemaphoreTake(activityManager.renderingMutex, pdMS_TO_TICKS(timeoutMs)) == pdTRUE;
}

RenderLock::~RenderLock() {
if (isLocked) {
xSemaphoreGive(activityManager.renderingMutex);
Expand All @@ -1373,3 +1377,7 @@ void RenderLock::unlock() {
* @note Must not be called from ISR context — xSemaphoreGetMutexHolder is not ISR-safe.
*/
bool RenderLock::peek() { return xSemaphoreGetMutexHolder(activityManager.renderingMutex) != nullptr; }

bool RenderLock::heldByCaller() {
return xSemaphoreGetMutexHolder(activityManager.renderingMutex) == xTaskGetCurrentTaskHandle();
}
2 changes: 2 additions & 0 deletions src/activities/RenderLock.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ class RenderLock {
enum class Mode { Blocking, Try };
explicit RenderLock(Mode mode = Mode::Blocking);
explicit RenderLock(Activity&, Mode mode = Mode::Blocking); // Compatibility overload used by activity call sites.
explicit RenderLock(unsigned long timeoutMs); // bounded take; check ownsLock()
RenderLock(const RenderLock&) = delete;
RenderLock& operator=(const RenderLock&) = delete;
~RenderLock();
void unlock();
bool ownsLock() const { return isLocked; }
static bool peek();
static bool heldByCaller();
};
27 changes: 27 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "util/BootReason.h"

#ifndef SIMULATOR
#include <esp_system.h>
#include <nvs.h>
#endif

Expand All @@ -49,6 +50,7 @@
#include "SilentRestart.h"
#include "activities/Activity.h"
#include "activities/ActivityManager.h"
#include "activities/RenderLock.h"
#include "activities/boot_sleep/ImageFolderIndex.h"
#include "activities/home/BookActions.h"
#include "activities/reader/KOReaderSyncActivity.h"
Expand Down Expand Up @@ -1101,6 +1103,28 @@ void enterDeepSleep(bool fromTimeout) {
powerManager.startDeepSleep(gpio);
}

#if !defined(SIMULATOR) && FREEINK_DEVICE_X4PRO
// Every esp_restart() (silent restart, OTA, SD update) runs this: the panel
// stays powered between refreshes, so power it off (POF + deep sleep) instead
// of leaving the booster on until the reset. begin() re-inits the controller
// and the first paint after boot is non-differential, so lost RAM is harmless.
// X4 Pro only: SDMMC keeps the SD card off the display's SPI bus, so the bus
// lock cannot block here behind an SD transfer.
static void powerOffPanelOnRestart() {
// ponytail: 3 s covers the longest refresh and stays under the 5 s task
// watchdog; a longer hold leaves the panel on.
constexpr unsigned long RENDER_LOCK_WAIT_MS = 3000;
const bool ownLock = RenderLock::heldByCaller();
RenderLock lock(ownLock ? 0UL : RENDER_LOCK_WAIT_MS);
if (!ownLock && !lock.ownsLock()) {
LOG_ERR("MAIN", "Render busy at restart; panel left powered");
return;
}
display.deepSleep();
LOG_INF("MAIN", "Panel powered off before restart");
}
#endif

void setupDisplayAndFonts(const bool seamless, const bool loadReaderResources, const bool useReaderRenderStack) {
#if !defined(SIMULATOR) && !FREEINK_MCU_C3
// C3 X3/X4 detection already runs in HalGPIO::begin() before SPI owns the
Expand All @@ -1120,6 +1144,9 @@ void setupDisplayAndFonts(const bool seamless, const bool loadReaderResources, c
display.begin();
#else
display.begin(seamless);
#if FREEINK_DEVICE_X4PRO
esp_register_shutdown_handler(powerOffPanelOnRestart);
#endif
#endif
renderer.begin();
display.setInverted(SETTINGS.screenInverted != 0);
Expand Down
Loading