diff --git a/src/activities/boot_sleep/BootActivity.cpp b/src/activities/boot_sleep/BootActivity.cpp index 9e59ed59..174ffb7d 100644 --- a/src/activities/boot_sleep/BootActivity.cpp +++ b/src/activities/boot_sleep/BootActivity.cpp @@ -8,6 +8,7 @@ void BootActivity::onEnter() { Activity::onEnter(); + RenderLock lock(*this); const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); diff --git a/src/activities/home/BookInfoActivity.cpp b/src/activities/home/BookInfoActivity.cpp index 1c82ec8b..7c883cc6 100644 --- a/src/activities/home/BookInfoActivity.cpp +++ b/src/activities/home/BookInfoActivity.cpp @@ -98,7 +98,10 @@ void BookInfoActivity::loadData() { void BookInfoActivity::onEnter() { Activity::onEnter(); - renderLoading(); + { + RenderLock lock(*this); + renderLoading(); + } loadData(); requestUpdate(true); } diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 59080969..3d3ac5e1 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -131,11 +131,9 @@ void HomeActivity::loadRecentBooks(int maxBooks) { void HomeActivity::loadRecentCovers(int coverHeight) { recentsLoading = true; - bool showingLoading = false; - Rect popupRect; - int progress = 0; - for (RecentBook& book : recentBooks) { + for (; nextRecentCoverIndex < recentBooks.size(); nextRecentCoverIndex++) { + RecentBook& book = recentBooks[nextRecentCoverIndex]; if (!book.coverBmpPath.empty()) { std::string coverPath = UITheme::getCoverThumbPath(book.coverBmpPath, coverHeight); if (!Storage.exists(coverPath.c_str())) { @@ -146,40 +144,35 @@ void HomeActivity::loadRecentCovers(int coverHeight) { epub.load(false, true); // Try to generate thumbnail image for Continue Reading card - if (!showingLoading) { - showingLoading = true; - popupRect = GUI.drawPopup(renderer, tr(STR_LOADING_POPUP)); - } - GUI.fillPopupProgress(renderer, popupRect, 10 + progress * (90 / recentBooks.size())); bool success = epub.generateThumbBmp(coverHeight); if (!success) { RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, ""); book.coverBmpPath = ""; } coverRendered = false; + nextRecentCoverIndex++; + recentsLoading = false; requestUpdate(); + return; } else if (FsHelpers::hasXtcExtension(book.path)) { // Handle XTC file Xtc xtc(book.path, "/.crosspoint"); if (xtc.load()) { // Try to generate thumbnail image for Continue Reading card - if (!showingLoading) { - showingLoading = true; - popupRect = GUI.drawPopup(renderer, tr(STR_LOADING_POPUP)); - } - GUI.fillPopupProgress(renderer, popupRect, 10 + progress * (90 / recentBooks.size())); bool success = xtc.generateThumbBmp(coverHeight); if (!success) { RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, ""); book.coverBmpPath = ""; } coverRendered = false; + nextRecentCoverIndex++; + recentsLoading = false; requestUpdate(); + return; } } } } - progress++; } recentsLoaded = true; @@ -193,9 +186,18 @@ void HomeActivity::onEnter() { hasOpdsUrl = strlen(SETTINGS.opdsServerUrl) > 0; selectorIndex = 0; + recentsLoading = false; + recentsLoaded = false; + firstRenderDone = false; + nextRecentCoverIndex = 0; + coverRendered = false; + freeCoverBuffer(); const auto& metrics = UITheme::getInstance().getMetrics(); loadRecentBooks(metrics.homeRecentBooksCount); + if (recentBooks.empty()) { + recentsLoaded = true; + } // Trigger first update requestUpdate(); @@ -251,6 +253,15 @@ void HomeActivity::freeCoverBuffer() { } void HomeActivity::loop() { + if (firstRenderDone && !recentsLoaded && !recentsLoading) { + const auto& metrics = UITheme::getInstance().getMetrics(); + const Rect contentRect = UITheme::getContentRect(renderer, true, false); + const int menuItemCount = hasOpdsUrl ? 6 : 5; + const HomeScreenLayout layout = computeHomeScreenLayout(metrics, contentRect.height, menuItemCount); + loadRecentCovers(getHomeCoverRenderHeight(layout)); + return; + } + const int menuCount = getMenuItemCount(); buttonNavigator.onNext([this, menuCount] { @@ -336,9 +347,6 @@ void HomeActivity::render(RenderLock&&) { if (!firstRenderDone) { firstRenderDone = true; requestUpdate(); - } else if (!recentsLoaded && !recentsLoading) { - recentsLoading = true; - loadRecentCovers(getHomeCoverRenderHeight(layout)); } } diff --git a/src/activities/home/HomeActivity.h b/src/activities/home/HomeActivity.h index 32d1c075..7d66caeb 100644 --- a/src/activities/home/HomeActivity.h +++ b/src/activities/home/HomeActivity.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -18,6 +19,7 @@ class HomeActivity final : public Activity { bool hasOpdsUrl = false; bool coverRendered = false; // Track if cover has been rendered once bool coverBufferStored = false; // Track if cover buffer is stored + size_t nextRecentCoverIndex = 0; uint8_t* coverBuffer = nullptr; // HomeActivity's own buffer for cover image std::vector recentBooks; void onSelectBook(const std::string& path); diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 97c68011..0826f3ab 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -58,7 +58,10 @@ void EpubReaderActivity::onEnter() { // Configure screen orientation based on settings // NOTE: This affects layout math and must be applied before any render calls. - ReaderUtils::applyOrientation(renderer, SETTINGS.orientation); + { + RenderLock lock(*this); + ReaderUtils::applyOrientation(renderer, SETTINGS.orientation); + } epub->setupCacheDir(); diff --git a/src/activities/reader/ReaderActivity.cpp b/src/activities/reader/ReaderActivity.cpp index 3a19ea39..3f702ab8 100644 --- a/src/activities/reader/ReaderActivity.cpp +++ b/src/activities/reader/ReaderActivity.cpp @@ -125,9 +125,12 @@ void ReaderActivity::onEnter() { if (isImageFile(initialBookPath)) { onGoToBmpViewer(initialBookPath); } else if (isXtcFile(initialBookPath)) { - renderer.clearScreen(); - renderer.drawCenteredText(UI_12_FONT_ID, 300, tr(STR_LOADING), true, EpdFontFamily::BOLD); - renderer.displayBuffer(); + { + RenderLock lock(*this); + renderer.clearScreen(); + renderer.drawCenteredText(UI_12_FONT_ID, 300, tr(STR_LOADING), true, EpdFontFamily::BOLD); + renderer.displayBuffer(); + } auto xtc = loadXtc(initialBookPath); if (!xtc) { diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index bef5ffaf..0df0fe1e 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -92,7 +92,10 @@ void TxtReaderActivity::onEnter() { return; } - ReaderUtils::applyOrientation(renderer, SETTINGS.orientation); + { + RenderLock lock(*this); + ReaderUtils::applyOrientation(renderer, SETTINGS.orientation); + } txt->setupCacheDir(); diff --git a/src/activities/util/BmpViewerActivity.cpp b/src/activities/util/BmpViewerActivity.cpp index 38ea7f9c..54c4d60d 100644 --- a/src/activities/util/BmpViewerActivity.cpp +++ b/src/activities/util/BmpViewerActivity.cpp @@ -127,6 +127,7 @@ void BmpViewerActivity::onExit() { } bool BmpViewerActivity::renderBmpImage(const bool showControls) { + RenderLock lock(*this); FsFile file; const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); @@ -192,6 +193,7 @@ bool BmpViewerActivity::renderBmpImage(const bool showControls) { } bool BmpViewerActivity::renderDecodedImage(const bool showControls) { + RenderLock lock(*this); const auto pageWidth = renderer.getScreenWidth(); const auto pageHeight = renderer.getScreenHeight(); Rect popupRect = GUI.drawPopup(renderer, tr(STR_LOADING_POPUP)); @@ -291,8 +293,11 @@ void BmpViewerActivity::toggleDisplayMode() { grayscaleDisplay = !grayscaleDisplay; // Switching between 1-bit BW and 4-level grayscale requires a full refresh to clear // ghosting from the previous mode — a half refresh leaves visible residue. - renderer.clearScreen(); - renderer.displayBuffer(HalDisplay::FULL_REFRESH); + { + RenderLock lock(*this); + renderer.clearScreen(); + renderer.displayBuffer(HalDisplay::FULL_REFRESH); + } if (!renderCurrentImage()) { renderError(tr(STR_COULD_NOT_RENDER_IMAGE)); } @@ -335,6 +340,7 @@ void BmpViewerActivity::saveDitherSettingsIfNeeded() { #endif void BmpViewerActivity::renderError(const char* message) { + RenderLock lock(*this); const auto pageHeight = renderer.getScreenHeight(); renderer.clearScreen(); renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, message); @@ -365,8 +371,11 @@ void BmpViewerActivity::setAsSleepScreen() { if (!success) { LOG_ERR("BMP", "Failed to set %s as sleep screen", filePath.c_str()); - GUI.drawPopup(renderer, tr(STR_FAILED_TO_SET_SLEEP_SCREEN)); - renderer.displayBuffer(HalDisplay::HALF_REFRESH); + { + RenderLock lock(*this); + GUI.drawPopup(renderer, tr(STR_FAILED_TO_SET_SLEEP_SCREEN)); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); + } return; } @@ -374,8 +383,11 @@ void BmpViewerActivity::setAsSleepScreen() { SETTINGS.saveToFile(); LOG_INF("BMP", "Set %s as sleep screen", filePath.c_str()); - GUI.drawPopup(renderer, tr(STR_SLEEP_SCREEN_SET)); - renderer.displayBuffer(HalDisplay::HALF_REFRESH); + { + RenderLock lock(*this); + GUI.drawPopup(renderer, tr(STR_SLEEP_SCREEN_SET)); + renderer.displayBuffer(HalDisplay::HALF_REFRESH); + } } void BmpViewerActivity::loop() { diff --git a/src/activities/util/FullScreenMessageActivity.cpp b/src/activities/util/FullScreenMessageActivity.cpp index 3b7428ab..144cd746 100644 --- a/src/activities/util/FullScreenMessageActivity.cpp +++ b/src/activities/util/FullScreenMessageActivity.cpp @@ -6,6 +6,7 @@ void FullScreenMessageActivity::onEnter() { Activity::onEnter(); + RenderLock lock(*this); const auto height = renderer.getLineHeight(UI_10_FONT_ID); const auto top = (renderer.getScreenHeight() - height) / 2; diff --git a/src/activities/weather/WeatherActivity.cpp b/src/activities/weather/WeatherActivity.cpp index dc382a83..eb0830e9 100644 --- a/src/activities/weather/WeatherActivity.cpp +++ b/src/activities/weather/WeatherActivity.cpp @@ -202,7 +202,10 @@ void WeatherActivity::onEnter() { Activity::onEnter(); // Force landscape orientation for weather display - renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + { + RenderLock lock(*this); + renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + } state = State::LOADING_CACHE; errorMessage.clear(); @@ -283,7 +286,10 @@ void WeatherActivity::launchWifiSelection() { void WeatherActivity::onWifiSelectionComplete(bool connected) { // Re-apply landscape after returning from WiFi selection (which uses portrait) - renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + { + RenderLock lock(*this); + renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + } LOG_DBG("WEA", "onWifiSelectionComplete connected=%d wifiStatus=%d", connected ? 1 : 0, (int)WiFi.status()); if (connected) { @@ -325,10 +331,16 @@ void WeatherActivity::fetchWeather() { } void WeatherActivity::openSettingsActivity() { - renderer.setOrientation(GfxRenderer::Orientation::Portrait); + { + RenderLock lock(*this); + renderer.setOrientation(GfxRenderer::Orientation::Portrait); + } startActivityForResult(std::make_unique(renderer, mappedInput), [this](const ActivityResult&) { - renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + { + RenderLock lock(*this); + renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + } forceRefresh = true; loadAndDisplay(); });