From 050a3bd1b673a206b1b6cb63bb25d94e6eccea5a Mon Sep 17 00:00:00 2001 From: Zach Nelson Date: Fri, 27 Feb 2026 15:48:24 -0600 Subject: [PATCH] fix: ActivityManager tweaks (#1220) ## Summary **What is the goal of this PR?** Small tweaks to #1016: - Only Activity and ActivityManager can access activityResultHandler and activityResult - `[[maybe_unused]]` in RenderLock constructor - Only ActivityManager and RenderLock can access renderingMutex - Missing renderUpdate after failed wifi selection - Standardize on activities calling finish instead of activityManager.popActivity - Hold RenderLock while mutating state in EpubReaderActivity result handlers --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**NO**_ --- src/activities/Activity.h | 4 +++- src/activities/ActivityManager.cpp | 2 +- src/activities/ActivityManager.h | 11 ++++++----- src/activities/browser/OpdsBookBrowserActivity.cpp | 1 + src/activities/network/CalibreConnectActivity.cpp | 4 ++-- src/activities/reader/EpubReaderActivity.cpp | 2 ++ src/activities/settings/CalibreSettingsActivity.cpp | 2 +- src/activities/settings/KOReaderSettingsActivity.cpp | 2 +- src/activities/settings/OtaUpdateActivity.cpp | 8 ++++---- 9 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/activities/Activity.h b/src/activities/Activity.h index 0d890d97..cc3fe443 100644 --- a/src/activities/Activity.h +++ b/src/activities/Activity.h @@ -13,15 +13,17 @@ #include "RenderLock.h" class Activity { + friend class ActivityManager; + protected: std::string name; GfxRenderer& renderer; MappedInputManager& mappedInput; - public: ActivityResultHandler resultHandler; ActivityResult result; + public: explicit Activity(std::string name, GfxRenderer& renderer, MappedInputManager& mappedInput) : name(std::move(name)), renderer(renderer), mappedInput(mappedInput) {} virtual ~Activity() = default; diff --git a/src/activities/ActivityManager.cpp b/src/activities/ActivityManager.cpp index 52c506d7..cf14a5c8 100644 --- a/src/activities/ActivityManager.cpp +++ b/src/activities/ActivityManager.cpp @@ -232,7 +232,7 @@ RenderLock::RenderLock() { isLocked = true; } -RenderLock::RenderLock(Activity& /* unused */) { +RenderLock::RenderLock([[maybe_unused]] Activity&) { xSemaphoreTake(activityManager.renderingMutex, portMAX_DELAY); isLocked = true; } diff --git a/src/activities/ActivityManager.h b/src/activities/ActivityManager.h index 698b9a7a..bd206730 100644 --- a/src/activities/ActivityManager.h +++ b/src/activities/ActivityManager.h @@ -11,7 +11,6 @@ #include "GfxRenderer.h" #include "MappedInputManager.h" -#include "RenderLock.h" class Activity; // forward declaration class RenderLock; // forward declaration @@ -31,6 +30,8 @@ class RenderLock; // forward declaration * - onActivityResult is implemented via a callback instead of a separate method, for simplicity */ class ActivityManager { + friend class RenderLock; + protected: GfxRenderer& renderer; MappedInputManager& mappedInput; @@ -49,6 +50,10 @@ class ActivityManager { static void renderTaskTrampoline(void* param); [[noreturn]] virtual void renderTaskLoop(); + // Mutex to protect rendering operations from race conditions + // Must only be used via RenderLock + SemaphoreHandle_t renderingMutex = nullptr; + // Whether to trigger a render after the current loop() // This variable must only be set by the main loop, to avoid race conditions bool requestedUpdate = false; @@ -61,10 +66,6 @@ class ActivityManager { } ~ActivityManager() { assert(false); /* should never be called */ }; - // Mutex to protect rendering operations from race conditions - // Must only be used via RenderLock - SemaphoreHandle_t renderingMutex = nullptr; - void begin(); void loop(); diff --git a/src/activities/browser/OpdsBookBrowserActivity.cpp b/src/activities/browser/OpdsBookBrowserActivity.cpp index 62aeab38..5b43da4c 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.cpp +++ b/src/activities/browser/OpdsBookBrowserActivity.cpp @@ -383,5 +383,6 @@ void OpdsBookBrowserActivity::onWifiSelectionComplete(const bool connected) { WiFi.mode(WIFI_OFF); state = BrowserState::ERROR; errorMessage = tr(STR_WIFI_CONN_FAILED); + requestUpdate(); } } diff --git a/src/activities/network/CalibreConnectActivity.cpp b/src/activities/network/CalibreConnectActivity.cpp index 4933178a..a42ab16e 100644 --- a/src/activities/network/CalibreConnectActivity.cpp +++ b/src/activities/network/CalibreConnectActivity.cpp @@ -63,7 +63,7 @@ void CalibreConnectActivity::onExit() { void CalibreConnectActivity::onWifiSelectionComplete(const bool connected) { if (!connected) { - activityManager.popActivity(); + finish(); return; } @@ -162,7 +162,7 @@ void CalibreConnectActivity::loop() { } if (exitRequested) { - activityManager.popActivity(); + finish(); return; } } diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index cc0a100b..b99cd07e 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -320,6 +320,7 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction std::make_unique(renderer, mappedInput, epub, path, spineIdx), [this](const ActivityResult& result) { if (!result.isCancelled && currentSpineIndex != std::get(result.data).spineIndex) { + RenderLock lock(*this); currentSpineIndex = std::get(result.data).spineIndex; nextPageNumber = 0; section.reset(); @@ -421,6 +422,7 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction if (!result.isCancelled) { const auto& sync = std::get(result.data); if (currentSpineIndex != sync.spineIndex || (section && section->currentPage != sync.page)) { + RenderLock lock(*this); currentSpineIndex = sync.spineIndex; nextPageNumber = sync.page; section.reset(); diff --git a/src/activities/settings/CalibreSettingsActivity.cpp b/src/activities/settings/CalibreSettingsActivity.cpp index 7cd6ac1b..8fcf9aba 100644 --- a/src/activities/settings/CalibreSettingsActivity.cpp +++ b/src/activities/settings/CalibreSettingsActivity.cpp @@ -27,7 +27,7 @@ void CalibreSettingsActivity::onExit() { Activity::onExit(); } void CalibreSettingsActivity::loop() { if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { - activityManager.popActivity(); + finish(); return; } diff --git a/src/activities/settings/KOReaderSettingsActivity.cpp b/src/activities/settings/KOReaderSettingsActivity.cpp index b84076f2..7291c8c1 100644 --- a/src/activities/settings/KOReaderSettingsActivity.cpp +++ b/src/activities/settings/KOReaderSettingsActivity.cpp @@ -29,7 +29,7 @@ void KOReaderSettingsActivity::onExit() { Activity::onExit(); } void KOReaderSettingsActivity::loop() { if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { - activityManager.popActivity(); + finish(); return; } diff --git a/src/activities/settings/OtaUpdateActivity.cpp b/src/activities/settings/OtaUpdateActivity.cpp index 8009adb3..c661a618 100644 --- a/src/activities/settings/OtaUpdateActivity.cpp +++ b/src/activities/settings/OtaUpdateActivity.cpp @@ -13,7 +13,7 @@ void OtaUpdateActivity::onWifiSelectionComplete(const bool success) { if (!success) { LOG_ERR("OTA", "WiFi connection failed, exiting"); - activityManager.popActivity(); + finish(); return; } @@ -172,7 +172,7 @@ void OtaUpdateActivity::loop() { } if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { - activityManager.popActivity(); + finish(); } return; @@ -180,14 +180,14 @@ void OtaUpdateActivity::loop() { if (state == FAILED) { if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { - activityManager.popActivity(); + finish(); } return; } if (state == NO_UPDATE) { if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { - activityManager.popActivity(); + finish(); } return; }