2025-12-03 22:00:29 +11:00
|
|
|
#pragma once
|
|
|
|
|
#include <Epub.h>
|
|
|
|
|
#include <Epub/Section.h>
|
|
|
|
|
#include <freertos/FreeRTOS.h>
|
|
|
|
|
#include <freertos/semphr.h>
|
|
|
|
|
#include <freertos/task.h>
|
|
|
|
|
|
2026-02-01 08:34:30 +01:00
|
|
|
#include "EpubReaderMenuActivity.h"
|
2025-12-21 21:17:00 +11:00
|
|
|
#include "activities/ActivityWithSubactivity.h"
|
2025-12-03 22:00:29 +11:00
|
|
|
|
2025-12-21 21:17:00 +11:00
|
|
|
class EpubReaderActivity final : public ActivityWithSubactivity {
|
2025-12-12 22:13:34 +11:00
|
|
|
std::shared_ptr<Epub> epub;
|
|
|
|
|
std::unique_ptr<Section> section = nullptr;
|
2025-12-03 22:00:29 +11:00
|
|
|
TaskHandle_t displayTaskHandle = nullptr;
|
2025-12-06 03:02:52 +11:00
|
|
|
SemaphoreHandle_t renderingMutex = nullptr;
|
2025-12-03 22:00:29 +11:00
|
|
|
int currentSpineIndex = 0;
|
|
|
|
|
int nextPageNumber = 0;
|
2025-12-05 22:19:44 +11:00
|
|
|
int pagesUntilFullRefresh = 0;
|
2026-01-27 19:11:11 +08:00
|
|
|
int cachedSpineIndex = 0;
|
|
|
|
|
int cachedChapterTotalPageCount = 0;
|
2026-02-05 15:17:51 +03:00
|
|
|
// Signals that the next render should reposition within the newly loaded section
|
|
|
|
|
// based on a cross-book percentage jump.
|
|
|
|
|
bool pendingPercentJump = false;
|
|
|
|
|
// Normalized 0.0-1.0 progress within the target spine item, computed from book percentage.
|
|
|
|
|
float pendingSpineProgress = 0.0f;
|
2025-12-03 22:00:29 +11:00
|
|
|
bool updateRequired = false;
|
feat: Move Sync feature to menu (#680)
## Summary
* **What is the goal of this PR?**
Move the "Sync Progress" option from TOC (Chapter Selection) screen to
the Reader Menu, and fix use-after-free crashes related to callback
handling in activity lifecycle.
* **What changes are included?**
- Added "Sync Progress" as a menu item in `EpubReaderMenuActivity` (now
4 items: Go to Chapter, Sync Progress, Go Home, Delete Book Cache)
- Removed sync-related logic from `EpubReaderChapterSelectionActivity` -
TOC now only displays chapters
- Implemented `pendingGoHome` and `pendingSubactivityExit` flags in
`EpubReaderActivity` to safely handle activity destruction
- Fixed GO_HOME, DELETE_CACHE, and SYNC menu actions to use deferred
callbacks avoiding use-after-free
## Additional Context
* Root cause of crashes: callbacks like `onGoHome()` or `onCancel()`
invoked from activity handlers could destroy the current activity while
code was still executing, causing use-after-free and race conditions
with FreeRTOS display task.
* Solution: Deferred execution pattern - set flags and process them in
`loop()` after all nested activity loops have safely returned.
* Files changed: `EpubReaderMenuActivity.h`,
`EpubReaderActivity.h/.cpp`, `EpubReaderChapterSelectionActivity.h/.cpp`
---
### 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? _**YES**_
Co-authored-by: danoooob <danoooob@example.com>
Co-authored-by: Dave Allie <dave@daveallie.com>
2026-02-05 22:04:38 +07:00
|
|
|
bool pendingSubactivityExit = false; // Defer subactivity exit to avoid use-after-free
|
|
|
|
|
bool pendingGoHome = false; // Defer go home to avoid race condition with display task
|
|
|
|
|
bool skipNextButtonCheck = false; // Skip button processing for one frame after subactivity exit
|
2025-12-17 20:47:43 +11:00
|
|
|
const std::function<void()> onGoBack;
|
2025-12-26 09:55:23 +09:00
|
|
|
const std::function<void()> onGoHome;
|
2025-12-03 22:00:29 +11:00
|
|
|
|
|
|
|
|
static void taskTrampoline(void* param);
|
|
|
|
|
[[noreturn]] void displayTaskLoop();
|
2025-12-08 19:48:49 +11:00
|
|
|
void renderScreen();
|
Rotation Support (#77)
• What is the goal of this PR?
Implement a horizontal EPUB reading mode so books can be read in
landscape orientation (both 90° and 270°), while keeping the rest of the
UI in portrait.
• What changes are included?
◦ Rendering / Display
▪ Added an orientation model to GfxRenderer (Portrait, LandscapeNormal,
LandscapeFlipped) and made:
▪ drawPixel, drawImage, displayWindow map logical coordinates
differently depending on orientation.
▪ getScreenWidth() / getScreenHeight() return orientation‑aware logical
dimensions (480×800 in portrait, 800×480 in landscape).
◦ Settings / Configuration
▪ Extended CrossPointSettings with:
▪ landscapeReading (toggle for portrait vs. landscape EPUB reading).
▪ landscapeFlipped (toggle to flip landscape 180° so both horizontal
holding directions are supported).
▪ Updated settings serialization/deserialization to persist these fields
while remaining backward‑compatible with existing settings files.
▪ Updated SettingsActivity to expose two new toggles:
▪ “Landscape Reading”
▪ “Flip Landscape (swap top/bottom)”
◦ EPUB Reader
▪ In EpubReaderActivity:
▪ On onEnter, set GfxRenderer orientation based on the new settings
(Portrait, LandscapeNormal, or LandscapeFlipped).
▪ On onExit, reset orientation back to Portrait so Home, WiFi, Settings,
etc. continue to render as before.
▪ Adjusted renderStatusBar to position the status bar and battery
indicator relative to GfxRenderer::getScreenHeight() instead of
hard‑coded Y coordinates, so it stays correctly at the bottom in both
portrait and landscape.
◦ EPUB Caching / Layout
▪ Extended Section cache metadata (section.bin) to include the logical
screenWidth and screenHeight used when pages were generated; bumped
SECTION_FILE_VERSION.
▪ Updated loadCacheMetadata to compare:
▪ font/margins/line compression/extraParagraphSpacing and screen
dimensions; mismatches now invalidate and clear the cache.
▪ Updated persistPageDataToSD and all call sites in EpubReaderActivity
to pass the current GfxRenderer::getScreenWidth() / getScreenHeight() so
portrait and landscape caches are kept separate and correctly sized.
Additional Context
• Cache behavior / migration
◦ Existing section.bin files (old SECTION_FILE_VERSION) will be detected
as incompatible and their caches cleared and rebuilt once per chapter
when first opened after this change.
◦ Within a given orientation, caches will be reused as before. Switching
orientation (portrait ↔ landscape) will cause a one‑time re‑index of
each chapter in the new orientation.
• Scope and risks
◦ Orientation changes are scoped to the EPUB reader; the Home screen,
Settings, WiFi selection, sleep screens, and web server UI continue to
assume portrait orientation.
◦ The renderer’s orientation is a static/global setting; if future code
uses GfxRenderer outside the reader while a reader instance is active,
it should be aware that orientation is no longer implicitly fixed.
◦ All drawing primitives now go through orientation‑aware coordinate
transforms; any code that previously relied on edge‑case behavior or
out‑of‑bounds writes might surface as logged “Outside range” warnings
instead.
• Testing suggestions / areas to focus on
◦ Verify in hardware:
▪ Portrait mode still renders correctly (boot, home, settings, WiFi,
reader).
▪ Landscape reading in both directions:
▪ Landscape Reading = ON, Flip Landscape = OFF.
▪ Landscape Reading = ON, Flip Landscape = ON.
▪ Status bar (page X/Y, % progress, battery icon) is fully visible and
aligned at the bottom in all three combinations.
◦ Open the same book:
▪ In portrait first, then switch to landscape and reopen it.
▪ Confirm that:
▪ Old portrait caches are rebuilt once for landscape (you should see the
“Indexing…” page).
▪ Progress save/restore still works (resume opens to the correct page in
the current orientation).
◦ Ensure grayscale rendering (the secondary pass in
EpubReaderActivity::renderContents) still looks correct in both
orientations.
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-28 05:33:20 -05:00
|
|
|
void renderContents(std::unique_ptr<Page> page, int orientedMarginTop, int orientedMarginRight,
|
|
|
|
|
int orientedMarginBottom, int orientedMarginLeft);
|
|
|
|
|
void renderStatusBar(int orientedMarginRight, int orientedMarginBottom, int orientedMarginLeft) const;
|
2026-02-01 08:34:30 +01:00
|
|
|
void saveProgress(int spineIndex, int currentPage, int pageCount);
|
2026-02-05 15:17:51 +03:00
|
|
|
// Jump to a percentage of the book (0-100), mapping it to spine and page.
|
|
|
|
|
void jumpToPercent(int percent);
|
2026-02-05 14:53:35 +03:00
|
|
|
void onReaderMenuBack(uint8_t orientation);
|
2026-02-01 08:34:30 +01:00
|
|
|
void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action);
|
2026-02-05 14:53:35 +03:00
|
|
|
void applyOrientation(uint8_t orientation);
|
2025-12-03 22:00:29 +11:00
|
|
|
|
|
|
|
|
public:
|
2025-12-28 21:59:14 -06:00
|
|
|
explicit EpubReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr<Epub> epub,
|
2025-12-26 09:55:23 +09:00
|
|
|
const std::function<void()>& onGoBack, const std::function<void()>& onGoHome)
|
2025-12-28 21:59:14 -06:00
|
|
|
: ActivityWithSubactivity("EpubReader", renderer, mappedInput),
|
2025-12-26 09:55:23 +09:00
|
|
|
epub(std::move(epub)),
|
|
|
|
|
onGoBack(onGoBack),
|
|
|
|
|
onGoHome(onGoHome) {}
|
2025-12-03 22:00:29 +11:00
|
|
|
void onEnter() override;
|
|
|
|
|
void onExit() override;
|
2025-12-17 23:32:18 +11:00
|
|
|
void loop() override;
|
2025-12-03 22:00:29 +11:00
|
|
|
};
|