From 02f2474e3be00a1b0d38435aa90dbcbe82b66dff Mon Sep 17 00:00:00 2001 From: cottongin Date: Mon, 16 Feb 2026 13:22:40 -0500 Subject: [PATCH] mod: adapt mod activities to #774 render() pattern Migrate 5 mod Activity subclasses from old polling-based display task pattern to the upstream render() super-class pattern with freeRTOS notification: - EpubReaderBookmarkSelectionActivity - DictionaryWordSelectActivity - DictionarySuggestionsActivity - DictionaryDefinitionActivity - LookedUpWordsActivity Changes: remove own TaskHandle/SemaphoreHandle/updateRequired, use requestUpdate() + render(RenderLock&&) override, fix potential deadlocks around enterNewActivity() calls. Also fix stale conflict marker in EpubReaderMenuActivity.h. Co-authored-by: Cursor --- .../reader/DictionaryDefinitionActivity.cpp | 34 ++-------- .../reader/DictionaryDefinitionActivity.h | 11 +--- .../reader/DictionarySuggestionsActivity.cpp | 45 ++++--------- .../reader/DictionarySuggestionsActivity.h | 13 +--- .../reader/DictionaryWordSelectActivity.cpp | 66 +++++++------------ .../reader/DictionaryWordSelectActivity.h | 11 +--- .../EpubReaderBookmarkSelectionActivity.cpp | 61 +++-------------- .../EpubReaderBookmarkSelectionActivity.h | 17 +---- .../reader/EpubReaderMenuActivity.h | 1 - .../reader/LookedUpWordsActivity.cpp | 66 +++++++------------ src/activities/reader/LookedUpWordsActivity.h | 12 +--- 11 files changed, 77 insertions(+), 260 deletions(-) diff --git a/src/activities/reader/DictionaryDefinitionActivity.cpp b/src/activities/reader/DictionaryDefinitionActivity.cpp index fb3a55d5..6771b350 100644 --- a/src/activities/reader/DictionaryDefinitionActivity.cpp +++ b/src/activities/reader/DictionaryDefinitionActivity.cpp @@ -11,40 +11,14 @@ #include "components/UITheme.h" #include "fontIds.h" -void DictionaryDefinitionActivity::taskTrampoline(void* param) { - auto* self = static_cast(param); - self->displayTaskLoop(); -} - -void DictionaryDefinitionActivity::displayTaskLoop() { - while (true) { - if (updateRequired) { - updateRequired = false; - xSemaphoreTake(renderingMutex, portMAX_DELAY); - renderScreen(); - xSemaphoreGive(renderingMutex); - } - vTaskDelay(10 / portTICK_PERIOD_MS); - } -} - void DictionaryDefinitionActivity::onEnter() { Activity::onEnter(); - renderingMutex = xSemaphoreCreateMutex(); wrapText(); - updateRequired = true; - xTaskCreate(&DictionaryDefinitionActivity::taskTrampoline, "DictDefTask", 4096, this, 1, &displayTaskHandle); + requestUpdate(); } void DictionaryDefinitionActivity::onExit() { Activity::onExit(); - xSemaphoreTake(renderingMutex, portMAX_DELAY); - if (displayTaskHandle) { - vTaskDelete(displayTaskHandle); - displayTaskHandle = nullptr; - } - vSemaphoreDelete(renderingMutex); - renderingMutex = nullptr; } // --------------------------------------------------------------------------- @@ -442,12 +416,12 @@ void DictionaryDefinitionActivity::loop() { if (prevPage && currentPage > 0) { currentPage--; - updateRequired = true; + requestUpdate(); } if (nextPage && currentPage < totalPages - 1) { currentPage++; - updateRequired = true; + requestUpdate(); } if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { @@ -465,7 +439,7 @@ void DictionaryDefinitionActivity::loop() { } } -void DictionaryDefinitionActivity::renderScreen() { +void DictionaryDefinitionActivity::render(Activity::RenderLock&&) { renderer.clearScreen(); const bool landscape = orientation == CrossPointSettings::ORIENTATION::LANDSCAPE_CW || diff --git a/src/activities/reader/DictionaryDefinitionActivity.h b/src/activities/reader/DictionaryDefinitionActivity.h index c9ecbeec..c5e08d0d 100644 --- a/src/activities/reader/DictionaryDefinitionActivity.h +++ b/src/activities/reader/DictionaryDefinitionActivity.h @@ -1,8 +1,5 @@ #pragma once #include -#include -#include -#include #include #include @@ -27,6 +24,7 @@ class DictionaryDefinitionActivity final : public Activity { void onEnter() override; void onExit() override; void loop() override; + void render(Activity::RenderLock&&) override; private: // A positioned text segment within a wrapped line (pre-calculated x offset and style). @@ -61,17 +59,10 @@ class DictionaryDefinitionActivity final : public Activity { int currentPage = 0; int linesPerPage = 0; int totalPages = 0; - bool updateRequired = false; bool firstRender = true; - TaskHandle_t displayTaskHandle = nullptr; - SemaphoreHandle_t renderingMutex = nullptr; - std::vector parseHtml(const std::string& html); static std::string decodeEntity(const std::string& entity); static bool isRenderableCodepoint(uint32_t cp); void wrapText(); - void renderScreen(); - static void taskTrampoline(void* param); - [[noreturn]] void displayTaskLoop(); }; diff --git a/src/activities/reader/DictionarySuggestionsActivity.cpp b/src/activities/reader/DictionarySuggestionsActivity.cpp index 25d1dcd2..3f0d84be 100644 --- a/src/activities/reader/DictionarySuggestionsActivity.cpp +++ b/src/activities/reader/DictionarySuggestionsActivity.cpp @@ -8,39 +8,13 @@ #include "fontIds.h" #include "util/Dictionary.h" -void DictionarySuggestionsActivity::taskTrampoline(void* param) { - auto* self = static_cast(param); - self->displayTaskLoop(); -} - -void DictionarySuggestionsActivity::displayTaskLoop() { - while (true) { - if (updateRequired && !subActivity) { - updateRequired = false; - xSemaphoreTake(renderingMutex, portMAX_DELAY); - renderScreen(); - xSemaphoreGive(renderingMutex); - } - vTaskDelay(10 / portTICK_PERIOD_MS); - } -} - void DictionarySuggestionsActivity::onEnter() { ActivityWithSubactivity::onEnter(); - renderingMutex = xSemaphoreCreateMutex(); - updateRequired = true; - xTaskCreate(&DictionarySuggestionsActivity::taskTrampoline, "DictSugTask", 4096, this, 1, &displayTaskHandle); + requestUpdate(); } void DictionarySuggestionsActivity::onExit() { ActivityWithSubactivity::onExit(); - xSemaphoreTake(renderingMutex, portMAX_DELAY); - if (displayTaskHandle) { - vTaskDelete(displayTaskHandle); - displayTaskHandle = nullptr; - } - vSemaphoreDelete(renderingMutex); - renderingMutex = nullptr; } void DictionarySuggestionsActivity::loop() { @@ -49,7 +23,7 @@ void DictionarySuggestionsActivity::loop() { if (pendingBackFromDef) { pendingBackFromDef = false; exitActivity(); - updateRequired = true; + requestUpdate(); } if (pendingExitToReader) { pendingExitToReader = false; @@ -68,12 +42,12 @@ void DictionarySuggestionsActivity::loop() { buttonNavigator.onNext([this] { selectedIndex = ButtonNavigator::nextIndex(selectedIndex, static_cast(suggestions.size())); - updateRequired = true; + requestUpdate(); }); buttonNavigator.onPrevious([this] { selectedIndex = ButtonNavigator::previousIndex(selectedIndex, static_cast(suggestions.size())); - updateRequired = true; + requestUpdate(); }); if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { @@ -81,10 +55,13 @@ void DictionarySuggestionsActivity::loop() { std::string definition = Dictionary::lookup(selected); if (definition.empty()) { - GUI.drawPopup(renderer, "Not found"); - renderer.displayBuffer(HalDisplay::FAST_REFRESH); + { + Activity::RenderLock lock(*this); + GUI.drawPopup(renderer, "Not found"); + renderer.displayBuffer(HalDisplay::FAST_REFRESH); + } vTaskDelay(1000 / portTICK_PERIOD_MS); - updateRequired = true; + requestUpdate(); return; } @@ -100,7 +77,7 @@ void DictionarySuggestionsActivity::loop() { } } -void DictionarySuggestionsActivity::renderScreen() { +void DictionarySuggestionsActivity::render(Activity::RenderLock&&) { renderer.clearScreen(); const auto orient = renderer.getOrientation(); diff --git a/src/activities/reader/DictionarySuggestionsActivity.h b/src/activities/reader/DictionarySuggestionsActivity.h index 46390b56..2082cae5 100644 --- a/src/activities/reader/DictionarySuggestionsActivity.h +++ b/src/activities/reader/DictionarySuggestionsActivity.h @@ -1,8 +1,4 @@ #pragma once -#include -#include -#include - #include #include #include @@ -28,6 +24,7 @@ class DictionarySuggestionsActivity final : public ActivityWithSubactivity { void onEnter() override; void onExit() override; void loop() override; + void render(Activity::RenderLock&&) override; private: std::string originalWord; @@ -39,15 +36,7 @@ class DictionarySuggestionsActivity final : public ActivityWithSubactivity { const std::function onDone; int selectedIndex = 0; - bool updateRequired = false; bool pendingBackFromDef = false; bool pendingExitToReader = false; ButtonNavigator buttonNavigator; - - TaskHandle_t displayTaskHandle = nullptr; - SemaphoreHandle_t renderingMutex = nullptr; - - void renderScreen(); - static void taskTrampoline(void* param); - [[noreturn]] void displayTaskLoop(); }; diff --git a/src/activities/reader/DictionaryWordSelectActivity.cpp b/src/activities/reader/DictionaryWordSelectActivity.cpp index bb9e507a..ad8be929 100644 --- a/src/activities/reader/DictionaryWordSelectActivity.cpp +++ b/src/activities/reader/DictionaryWordSelectActivity.cpp @@ -14,45 +14,19 @@ #include "util/Dictionary.h" #include "util/LookupHistory.h" -void DictionaryWordSelectActivity::taskTrampoline(void* param) { - auto* self = static_cast(param); - self->displayTaskLoop(); -} - -void DictionaryWordSelectActivity::displayTaskLoop() { - while (true) { - if (updateRequired && !subActivity) { - updateRequired = false; - xSemaphoreTake(renderingMutex, portMAX_DELAY); - renderScreen(); - xSemaphoreGive(renderingMutex); - } - vTaskDelay(10 / portTICK_PERIOD_MS); - } -} - void DictionaryWordSelectActivity::onEnter() { ActivityWithSubactivity::onEnter(); - renderingMutex = xSemaphoreCreateMutex(); extractWords(); mergeHyphenatedWords(); if (!rows.empty()) { currentRow = static_cast(rows.size()) / 3; currentWordInRow = 0; } - updateRequired = true; - xTaskCreate(&DictionaryWordSelectActivity::taskTrampoline, "DictWordSelTask", 4096, this, 1, &displayTaskHandle); + requestUpdate(); } void DictionaryWordSelectActivity::onExit() { ActivityWithSubactivity::onExit(); - xSemaphoreTake(renderingMutex, portMAX_DELAY); - if (displayTaskHandle) { - vTaskDelete(displayTaskHandle); - displayTaskHandle = nullptr; - } - vSemaphoreDelete(renderingMutex); - renderingMutex = nullptr; } bool DictionaryWordSelectActivity::isLandscape() const { @@ -231,7 +205,7 @@ void DictionaryWordSelectActivity::loop() { if (pendingBackFromDef) { pendingBackFromDef = false; exitActivity(); - updateRequired = true; + requestUpdate(); } if (pendingExitToReader) { pendingExitToReader = false; @@ -353,25 +327,28 @@ void DictionaryWordSelectActivity::loop() { std::string cleaned = Dictionary::cleanWord(rawWord); if (cleaned.empty()) { - GUI.drawPopup(renderer, "No word"); - renderer.displayBuffer(HalDisplay::FAST_REFRESH); + { + Activity::RenderLock lock(*this); + GUI.drawPopup(renderer, "No word"); + renderer.displayBuffer(HalDisplay::FAST_REFRESH); + } vTaskDelay(1000 / portTICK_PERIOD_MS); - updateRequired = true; + requestUpdate(); return; } - // Show looking up popup, then release mutex so display task can run - xSemaphoreTake(renderingMutex, portMAX_DELAY); - Rect popupLayout = GUI.drawPopup(renderer, "Looking up..."); - xSemaphoreGive(renderingMutex); + Rect popupLayout; + { + Activity::RenderLock lock(*this); + popupLayout = GUI.drawPopup(renderer, "Looking up..."); + } bool cancelled = false; std::string definition = Dictionary::lookup( cleaned, [this, &popupLayout](int percent) { - xSemaphoreTake(renderingMutex, portMAX_DELAY); + Activity::RenderLock lock(*this); GUI.fillPopupProgress(renderer, popupLayout, percent); - xSemaphoreGive(renderingMutex); }, [this, &cancelled]() -> bool { mappedInput.update(); @@ -383,7 +360,7 @@ void DictionaryWordSelectActivity::loop() { }); if (cancelled) { - updateRequired = true; + requestUpdate(); return; } @@ -417,10 +394,13 @@ void DictionaryWordSelectActivity::loop() { return; } - GUI.drawPopup(renderer, "Not found"); - renderer.displayBuffer(HalDisplay::FAST_REFRESH); + { + Activity::RenderLock lock(*this); + GUI.drawPopup(renderer, "Not found"); + renderer.displayBuffer(HalDisplay::FAST_REFRESH); + } vTaskDelay(1500 / portTICK_PERIOD_MS); - updateRequired = true; + requestUpdate(); return; } @@ -430,11 +410,11 @@ void DictionaryWordSelectActivity::loop() { } if (changed) { - updateRequired = true; + requestUpdate(); } } -void DictionaryWordSelectActivity::renderScreen() { +void DictionaryWordSelectActivity::render(Activity::RenderLock&&) { renderer.clearScreen(); // Render the page content diff --git a/src/activities/reader/DictionaryWordSelectActivity.h b/src/activities/reader/DictionaryWordSelectActivity.h index 71b677e6..dbaec852 100644 --- a/src/activities/reader/DictionaryWordSelectActivity.h +++ b/src/activities/reader/DictionaryWordSelectActivity.h @@ -1,8 +1,5 @@ #pragma once #include -#include -#include -#include #include #include @@ -31,6 +28,7 @@ class DictionaryWordSelectActivity final : public ActivityWithSubactivity { void onEnter() override; void onExit() override; void loop() override; + void render(Activity::RenderLock&&) override; private: struct WordInfo { @@ -64,19 +62,12 @@ class DictionaryWordSelectActivity final : public ActivityWithSubactivity { std::vector rows; int currentRow = 0; int currentWordInRow = 0; - bool updateRequired = false; bool pendingBackFromDef = false; bool pendingExitToReader = false; - TaskHandle_t displayTaskHandle = nullptr; - SemaphoreHandle_t renderingMutex = nullptr; - bool isLandscape() const; bool isInverted() const; void extractWords(); void mergeHyphenatedWords(); - void renderScreen(); void drawHints(); - static void taskTrampoline(void* param); - [[noreturn]] void displayTaskLoop(); }; diff --git a/src/activities/reader/EpubReaderBookmarkSelectionActivity.cpp b/src/activities/reader/EpubReaderBookmarkSelectionActivity.cpp index 8ca8f6da..44351aa1 100644 --- a/src/activities/reader/EpubReaderBookmarkSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderBookmarkSelectionActivity.cpp @@ -42,36 +42,13 @@ std::string EpubReaderBookmarkSelectionActivity::getPageSuffix(const Bookmark& b return " - Page " + std::to_string(bookmark.pageNumber + 1); } -void EpubReaderBookmarkSelectionActivity::taskTrampoline(void* param) { - auto* self = static_cast(param); - self->displayTaskLoop(); -} - void EpubReaderBookmarkSelectionActivity::onEnter() { ActivityWithSubactivity::onEnter(); - - renderingMutex = xSemaphoreCreateMutex(); - - // Trigger first update - updateRequired = true; - xTaskCreate(&EpubReaderBookmarkSelectionActivity::taskTrampoline, "BookmarkSelTask", - 4096, // Stack size - this, // Parameters - 1, // Priority - &displayTaskHandle // Task handle - ); + requestUpdate(); } void EpubReaderBookmarkSelectionActivity::onExit() { ActivityWithSubactivity::onExit(); - - xSemaphoreTake(renderingMutex, portMAX_DELAY); - if (displayTaskHandle) { - vTaskDelete(displayTaskHandle); - displayTaskHandle = nullptr; - } - vSemaphoreDelete(renderingMutex); - renderingMutex = nullptr; } void EpubReaderBookmarkSelectionActivity::loop() { @@ -83,7 +60,6 @@ void EpubReaderBookmarkSelectionActivity::loop() { const int totalItems = getTotalItems(); if (totalItems == 0) { - // All bookmarks deleted, go back if (mappedInput.wasReleased(MappedInputManager::Button::Back) || mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { onGoBack(); @@ -91,14 +67,11 @@ void EpubReaderBookmarkSelectionActivity::loop() { return; } - // Delete confirmation mode: wait for confirm (delete) or back (cancel) if (deleteConfirmMode) { if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { if (ignoreNextConfirmRelease) { - // Ignore the release from the initial long press ignoreNextConfirmRelease = false; } else { - // Confirm delete BookmarkStore::removeBookmark(cachePath, bookmarks[pendingDeleteIndex].spineIndex, bookmarks[pendingDeleteIndex].pageNumber); bookmarks.erase(bookmarks.begin() + pendingDeleteIndex); @@ -106,25 +79,24 @@ void EpubReaderBookmarkSelectionActivity::loop() { selectorIndex = std::max(0, static_cast(bookmarks.size()) - 1); } deleteConfirmMode = false; - updateRequired = true; + requestUpdate(); } } if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { deleteConfirmMode = false; ignoreNextConfirmRelease = false; - updateRequired = true; + requestUpdate(); } return; } - // Detect long press on Confirm to trigger delete constexpr unsigned long DELETE_HOLD_MS = 700; if (mappedInput.isPressed(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() >= DELETE_HOLD_MS) { if (totalItems > 0 && selectorIndex >= 0 && selectorIndex < totalItems) { deleteConfirmMode = true; ignoreNextConfirmRelease = true; pendingDeleteIndex = selectorIndex; - updateRequired = true; + requestUpdate(); } return; } @@ -144,38 +116,26 @@ void EpubReaderBookmarkSelectionActivity::loop() { buttonNavigator.onNextRelease([this, totalItems] { selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems); - updateRequired = true; + requestUpdate(); }); buttonNavigator.onPreviousRelease([this, totalItems] { selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems); - updateRequired = true; + requestUpdate(); }); buttonNavigator.onNextContinuous([this, totalItems, pageItems] { selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems); - updateRequired = true; + requestUpdate(); }); buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] { selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems); - updateRequired = true; + requestUpdate(); }); } -void EpubReaderBookmarkSelectionActivity::displayTaskLoop() { - while (true) { - if (updateRequired && !subActivity) { - updateRequired = false; - xSemaphoreTake(renderingMutex, portMAX_DELAY); - renderScreen(); - xSemaphoreGive(renderingMutex); - } - vTaskDelay(10 / portTICK_PERIOD_MS); - } -} - -void EpubReaderBookmarkSelectionActivity::renderScreen() { +void EpubReaderBookmarkSelectionActivity::render(Activity::RenderLock&&) { renderer.clearScreen(); const auto pageWidth = renderer.getScreenWidth(); @@ -191,7 +151,6 @@ void EpubReaderBookmarkSelectionActivity::renderScreen() { const int pageItems = getPageItems(); const int totalItems = getTotalItems(); - // Title const int titleX = contentX + (contentWidth - renderer.getTextWidth(UI_12_FONT_ID, "Go to Bookmark", EpdFontFamily::BOLD)) / 2; renderer.drawText(UI_12_FONT_ID, titleX, 15 + contentY, "Go to Bookmark", true, EpdFontFamily::BOLD); @@ -213,7 +172,6 @@ void EpubReaderBookmarkSelectionActivity::renderScreen() { const std::string suffix = getPageSuffix(bookmarks[itemIndex]); const int suffixWidth = renderer.getTextWidth(UI_10_FONT_ID, suffix.c_str()); - // Truncate the prefix (chapter + snippet) to leave room for the page suffix const std::string prefix = getBookmarkPrefix(bookmarks[itemIndex]); const std::string truncatedPrefix = renderer.truncatedText(UI_10_FONT_ID, prefix.c_str(), maxLabelWidth - suffixWidth); @@ -225,7 +183,6 @@ void EpubReaderBookmarkSelectionActivity::renderScreen() { } if (deleteConfirmMode && pendingDeleteIndex < static_cast(bookmarks.size())) { - // Draw delete confirmation overlay const std::string suffix = getPageSuffix(bookmarks[pendingDeleteIndex]); std::string msg = "Delete bookmark" + suffix + "?"; diff --git a/src/activities/reader/EpubReaderBookmarkSelectionActivity.h b/src/activities/reader/EpubReaderBookmarkSelectionActivity.h index 4caf7c3f..48703a84 100644 --- a/src/activities/reader/EpubReaderBookmarkSelectionActivity.h +++ b/src/activities/reader/EpubReaderBookmarkSelectionActivity.h @@ -1,8 +1,5 @@ #pragma once #include -#include -#include -#include #include #include @@ -15,32 +12,19 @@ class EpubReaderBookmarkSelectionActivity final : public ActivityWithSubactivity std::shared_ptr epub; std::vector bookmarks; std::string cachePath; - TaskHandle_t displayTaskHandle = nullptr; - SemaphoreHandle_t renderingMutex = nullptr; ButtonNavigator buttonNavigator; int selectorIndex = 0; - bool updateRequired = false; bool deleteConfirmMode = false; bool ignoreNextConfirmRelease = false; int pendingDeleteIndex = 0; const std::function onGoBack; const std::function onSelectBookmark; - // Number of items that fit on a page, derived from logical screen height. int getPageItems() const; - int getTotalItems() const; - - // Build the prefix portion of a bookmark label (chapter + snippet, without page suffix) std::string getBookmarkPrefix(const Bookmark& bookmark) const; - - // Build the page suffix (e.g. " - Page 5") static std::string getPageSuffix(const Bookmark& bookmark); - static void taskTrampoline(void* param); - [[noreturn]] void displayTaskLoop(); - void renderScreen(); - public: explicit EpubReaderBookmarkSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::shared_ptr& epub, @@ -57,4 +41,5 @@ class EpubReaderBookmarkSelectionActivity final : public ActivityWithSubactivity void onEnter() override; void onExit() override; void loop() override; + void render(Activity::RenderLock&&) override; }; diff --git a/src/activities/reader/EpubReaderMenuActivity.h b/src/activities/reader/EpubReaderMenuActivity.h index 5eadd751..9b6eb559 100644 --- a/src/activities/reader/EpubReaderMenuActivity.h +++ b/src/activities/reader/EpubReaderMenuActivity.h @@ -82,7 +82,6 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity { const std::function onBack; const std::function onAction; -<<<<<<< HEAD // Map the internal override value to an index into letterboxFillLabels. int letterboxFillToIndex() const { diff --git a/src/activities/reader/LookedUpWordsActivity.cpp b/src/activities/reader/LookedUpWordsActivity.cpp index 3b605767..cfc8a329 100644 --- a/src/activities/reader/LookedUpWordsActivity.cpp +++ b/src/activities/reader/LookedUpWordsActivity.cpp @@ -12,41 +12,15 @@ #include "util/Dictionary.h" #include "util/LookupHistory.h" -void LookedUpWordsActivity::taskTrampoline(void* param) { - auto* self = static_cast(param); - self->displayTaskLoop(); -} - -void LookedUpWordsActivity::displayTaskLoop() { - while (true) { - if (updateRequired && !subActivity) { - updateRequired = false; - xSemaphoreTake(renderingMutex, portMAX_DELAY); - renderScreen(); - xSemaphoreGive(renderingMutex); - } - vTaskDelay(10 / portTICK_PERIOD_MS); - } -} - void LookedUpWordsActivity::onEnter() { ActivityWithSubactivity::onEnter(); - renderingMutex = xSemaphoreCreateMutex(); words = LookupHistory::load(cachePath); std::reverse(words.begin(), words.end()); - updateRequired = true; - xTaskCreate(&LookedUpWordsActivity::taskTrampoline, "LookedUpTask", 4096, this, 1, &displayTaskHandle); + requestUpdate(); } void LookedUpWordsActivity::onExit() { ActivityWithSubactivity::onExit(); - xSemaphoreTake(renderingMutex, portMAX_DELAY); - if (displayTaskHandle) { - vTaskDelete(displayTaskHandle); - displayTaskHandle = nullptr; - } - vSemaphoreDelete(renderingMutex); - renderingMutex = nullptr; } void LookedUpWordsActivity::loop() { @@ -55,7 +29,7 @@ void LookedUpWordsActivity::loop() { if (pendingBackFromDef) { pendingBackFromDef = false; exitActivity(); - updateRequired = true; + requestUpdate(); } if (pendingExitToReader) { pendingExitToReader = false; @@ -87,13 +61,13 @@ void LookedUpWordsActivity::loop() { selectedIndex = std::max(0, static_cast(words.size()) - 1); } deleteConfirmMode = false; - updateRequired = true; + requestUpdate(); } } if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { deleteConfirmMode = false; ignoreNextConfirmRelease = false; - updateRequired = true; + requestUpdate(); } return; } @@ -104,7 +78,7 @@ void LookedUpWordsActivity::loop() { deleteConfirmMode = true; ignoreNextConfirmRelease = true; pendingDeleteIndex = selectedIndex; - updateRequired = true; + requestUpdate(); return; } @@ -113,30 +87,37 @@ void LookedUpWordsActivity::loop() { buttonNavigator.onNextRelease([this, totalItems] { selectedIndex = ButtonNavigator::nextIndex(selectedIndex, totalItems); - updateRequired = true; + requestUpdate(); }); buttonNavigator.onPreviousRelease([this, totalItems] { selectedIndex = ButtonNavigator::previousIndex(selectedIndex, totalItems); - updateRequired = true; + requestUpdate(); }); buttonNavigator.onNextContinuous([this, totalItems, pageItems] { selectedIndex = ButtonNavigator::nextPageIndex(selectedIndex, totalItems, pageItems); - updateRequired = true; + requestUpdate(); }); buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] { selectedIndex = ButtonNavigator::previousPageIndex(selectedIndex, totalItems, pageItems); - updateRequired = true; + requestUpdate(); }); if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { const std::string& headword = words[selectedIndex]; - Rect popupLayout = GUI.drawPopup(renderer, "Looking up..."); + Rect popupLayout; + { + Activity::RenderLock lock(*this); + popupLayout = GUI.drawPopup(renderer, "Looking up..."); + } std::string definition = Dictionary::lookup( - headword, [this, &popupLayout](int percent) { GUI.fillPopupProgress(renderer, popupLayout, percent); }); + headword, [this, &popupLayout](int percent) { + Activity::RenderLock lock(*this); + GUI.fillPopupProgress(renderer, popupLayout, percent); + }); if (!definition.empty()) { enterNewActivity(new DictionaryDefinitionActivity( @@ -166,10 +147,13 @@ void LookedUpWordsActivity::loop() { return; } - GUI.drawPopup(renderer, "Not found"); - renderer.displayBuffer(HalDisplay::FAST_REFRESH); + { + Activity::RenderLock lock(*this); + GUI.drawPopup(renderer, "Not found"); + renderer.displayBuffer(HalDisplay::FAST_REFRESH); + } vTaskDelay(1500 / portTICK_PERIOD_MS); - updateRequired = true; + requestUpdate(); return; } @@ -190,7 +174,7 @@ int LookedUpWordsActivity::getPageItems() const { return std::max(1, contentHeight / metrics.listRowHeight); } -void LookedUpWordsActivity::renderScreen() { +void LookedUpWordsActivity::render(Activity::RenderLock&&) { renderer.clearScreen(); const auto orient = renderer.getOrientation(); diff --git a/src/activities/reader/LookedUpWordsActivity.h b/src/activities/reader/LookedUpWordsActivity.h index 6ad9e367..b786b56d 100644 --- a/src/activities/reader/LookedUpWordsActivity.h +++ b/src/activities/reader/LookedUpWordsActivity.h @@ -1,8 +1,4 @@ #pragma once -#include -#include -#include - #include #include #include @@ -25,6 +21,7 @@ class LookedUpWordsActivity final : public ActivityWithSubactivity { void onEnter() override; void onExit() override; void loop() override; + void render(Activity::RenderLock&&) override; private: std::string cachePath; @@ -35,7 +32,6 @@ class LookedUpWordsActivity final : public ActivityWithSubactivity { std::vector words; int selectedIndex = 0; - bool updateRequired = false; bool pendingBackFromDef = false; bool pendingExitToReader = false; ButtonNavigator buttonNavigator; @@ -45,11 +41,5 @@ class LookedUpWordsActivity final : public ActivityWithSubactivity { bool ignoreNextConfirmRelease = false; int pendingDeleteIndex = 0; - TaskHandle_t displayTaskHandle = nullptr; - SemaphoreHandle_t renderingMutex = nullptr; - int getPageItems() const; - void renderScreen(); - static void taskTrampoline(void* param); - [[noreturn]] void displayTaskLoop(); };