#pragma once #include #include #include #include #include "BookmarkStore.h" #include "EpubReaderMenuActivity.h" #include "ReaderUtils.h" #include "activities/Activity.h" class EpubReaderActivity final : public Activity { // Reader can launch sync in several UX modes: // - COMPARE: legacy chooser (apply/upload) for power users. // - PULL_REMOTE / PUSH_LOCAL: direct one-step actions from menu entries. // - AUTO_PUSH: silent push on reader exit; skips when remote is already ahead. // Keeping this split in the caller avoids branching on menu semantics deep // inside generic reader state handling. enum class SyncLaunchMode { COMPARE, PULL_REMOTE, PUSH_LOCAL, AUTO_PUSH, }; std::shared_ptr epub; std::unique_ptr
section = nullptr; int currentSpineIndex = 0; int nextPageNumber = 0; // Set when navigating to a TOC entry in a different spine (chapter skip or chapter selector). // Cleared on the next render after the new section loads and resolves it to a page. std::optional pendingTocIndex; // Set when navigating to a footnote href with a fragment (e.g. #note1). // Cleared on the next render after the new section loads and resolves it to a page. std::string pendingAnchor; int pagesUntilFullRefresh = 0; int cachedSpineIndex = 0; int cachedChapterTotalPageCount = 0; unsigned long lastPageTurnTime = 0UL; unsigned long pageTurnDuration = 0UL; bool pendingHalfRefreshAfterImagePage = false; // Temporary AA suspension when BW snapshot allocation fails under memory pressure. // Automatically lifted once heap recovers above hysteresis thresholds. bool antiAliasingSuspendedLowMemory = false; bool showTruncatedSectionHintThisRender = false; uint8_t truncatedSectionHintRendersRemaining = 0; int lastWarnedTruncatedSpineIndex = -1; struct RenderPhaseStats { unsigned long prewarmMs = 0UL; unsigned long bwRenderMs = 0UL; unsigned long displayMs = 0UL; unsigned long bwStoreMs = 0UL; unsigned long grayLsbMs = 0UL; unsigned long grayMsbMs = 0UL; unsigned long grayDisplayMs = 0UL; unsigned long bwRestoreMs = 0UL; unsigned long totalMs = 0UL; }; struct LastRenderStats { bool valid = false; bool cacheRebuilt = false; bool usedGrayscale = false; bool hadImages = false; bool imagePageWithAA = false; bool forcedHalfRefresh = false; uint8_t orientation = 0; uint8_t imageRendering = 0; bool embeddedStyle = false; bool textAntiAliasing = false; int effectiveFontId = 0; int spineIndex = 0; int pageIndex = 0; int pageCount = 0; int footnoteCount = 0; int marginTop = 0; int marginRight = 0; int marginBottom = 0; int marginLeft = 0; uint16_t viewportWidth = 0; uint16_t viewportHeight = 0; unsigned long sectionLoadMs = 0UL; unsigned long pageLoadMs = 0UL; unsigned long requestRenderMs = 0UL; RenderPhaseStats phases; uint32_t freeHeapBefore = 0; uint32_t largestFreeBlockBefore = 0; uint32_t freeHeapAfter = 0; uint32_t largestFreeBlockAfter = 0; uint32_t fontCacheHits = 0; uint32_t fontCacheMisses = 0; uint32_t fontDecompressMs = 0; uint16_t fontUniqueGroups = 0; uint32_t fontPageBufferBytes = 0; uint32_t fontPageGlyphsBytes = 0; uint32_t fontPeakTempBytes = 0; uint32_t fontGetBitmapTimeUs = 0; uint32_t fontGetBitmapCalls = 0; }; struct BenchmarkAggregate { int renderCount = 0; int imagePageCount = 0; int cacheRebuildCount = 0; int maxFootnotes = 0; unsigned long totalRequestRenderMs = 0UL; unsigned long minRequestRenderMs = 0UL; unsigned long maxRequestRenderMs = 0UL; unsigned long totalRenderMs = 0UL; unsigned long minRenderMs = 0UL; unsigned long maxRenderMs = 0UL; unsigned long totalSectionLoadMs = 0UL; unsigned long totalPageLoadMs = 0UL; RenderPhaseStats totalPhases; uint32_t totalFontCacheHits = 0; uint32_t totalFontCacheMisses = 0; uint32_t totalFontDecompressMs = 0; uint32_t totalFontGetBitmapTimeUs = 0; uint32_t totalFontGetBitmapCalls = 0; uint32_t minFreeHeapAfter = 0; uint32_t maxFreeHeapAfter = 0; }; LastRenderStats lastRenderStats; // 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; // Pending paragraph index from KOReader sync (resolved to page via Section paragraph LUT) bool pendingParagraphLookup = false; uint16_t pendingParagraphIndex = 0; // Pending list-item index for KOReader-supplied XPaths whose deepest element is /li[N]. // Preferred over pendingParagraphLookup when set because
  • -anchored XPaths are not // representable in the body-child

    LUT. bool pendingListItemLookup = false; uint16_t pendingListItemIndex = 0; bool pendingScreenshot = false; bool skipNextButtonCheck = false; // Skip button processing for one frame after subactivity exit bool finishedBookActivityStarted_ = false; ReaderUtils::InputDrainGuard inputDrainGuard; bool automaticPageTurnActive = false; // Pages turned in the current reader session. Used to gate auto-push-on-close: a brief // inspection of a book should not trigger a network round-trip. Reset on every reader // entry; not persisted, since "session" means the lifetime of this activity instance. int sessionPagesAdvanced = 0; // -1 means use global SETTINGS value. int8_t bookEmbeddedStyleOverride = -1; int8_t bookImageRenderingOverride = -1; int8_t bookFontFamilyOverride = -1; std::string bookSdFontFamilyOverride; int8_t bookFontSizeOverride = -1; bool bookBionicReadingOverride = false; // Bookmarks (starred pages) BookmarkStore bookmarkStore; // Footnote support std::vector currentPageFootnotes; struct SavedPosition { int spineIndex; int pageNumber; }; static constexpr int MAX_FOOTNOTE_DEPTH = 3; SavedPosition savedPositions[MAX_FOOTNOTE_DEPTH] = {}; int footnoteDepth = 0; void renderContents(std::unique_ptr page, int orientedMarginTop, int orientedMarginRight, int orientedMarginBottom, int orientedMarginLeft); void renderStatusBar() const; void silentIndexNextChapterIfNeeded(uint16_t viewportWidth, uint16_t viewportHeight); void saveProgress(int spineIndex, int currentPage, int pageCount); // Jump to a percentage of the book (0-100), mapping it to spine and page. void jumpToPercent(int percent); void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action); void launchKOReaderSync(SyncLaunchMode mode = SyncLaunchMode::COMPARE); // Reader-close auto-push gate. Returns true if AUTO_PUSH was launched (the caller // must not perform its own exit — the sync activity will route to home on completion). // Returns false when any of the gates fails (setting off, no credentials, < 3 pages), // letting the caller take its normal exit path. bool tryAutoPushOnClose(); // Consume a persisted standalone KOReader sync session for this EPUB. Remote // apply writes the mapped reopen position into progress.bin before the normal // reader startup path reads it. Upload-complete leaves the existing local // progress.bin untouched and simply clears the pending session marker. void applyPendingSyncSession(); // Consume a persisted bookmark-jump request (from GlobalBookmarksActivity) for // this book. Rewrites progress.bin to the bookmarked position before the normal // reader startup path reads it. void applyPendingBookmarkJump(); void applyOrientation(uint8_t orientation); void applyTextDarkness(uint8_t textDarkness); void toggleAutoPageTurn(uint8_t selectedPageTurnOption); void stopAutomaticPageTurn(); void applyBookReaderOverrides(int8_t embeddedStyleOverride, int8_t imageRenderingOverride, int8_t fontFamilyOverride, const std::string& sdFontFamilyOverride, int8_t fontSizeOverride, bool bionicReadingOverride); void openReaderMenu(); bool getEffectiveEmbeddedStyle() const; uint8_t getEffectiveImageRendering() const; int getEffectiveReaderFontId() const; float getEffectiveReaderLineCompression() const; bool stepPageState(bool isForwardTurn); void pageTurn(bool isForwardTurn); void runRenderBenchmark(); std::string buildRenderBenchmarkReport(const LastRenderStats& startSnapshot, const BenchmarkAggregate& aggregate, int forwardTurns, unsigned long forwardMs, int backwardTurns, unsigned long backwardMs) const; // Footnote navigation void navigateToHref(const std::string& href, bool savePosition = false); void restoreSavedPosition(); public: explicit EpubReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::unique_ptr epub) : Activity("EpubReader", renderer, mappedInput), epub(std::move(epub)) {} void onEnter() override; void onExit() override; void loop() override; void render(RenderLock&& lock) override; bool isReaderActivity() const override { return true; } void onButtonAction(CrossPointSettings::BUTTON_ACTION action) override; // Renders the last saved page to the frame buffer without flushing to display. // Used by SleepActivity to prepare the background for the overlay sleep mode. // Returns false if the page cannot be loaded (missing cache / file error). static bool drawCurrentPageToBuffer(const std::string& filePath, GfxRenderer& renderer); };