fix: prevent Serial.printf from blocking when USB disconnected
All checks were successful
CI / build (push) Successful in 2m23s
All checks were successful
CI / build (push) Successful in 2m23s
On ESP32-C3 with USB CDC, Serial.printf() blocks indefinitely when USB is not connected. This caused device freezes when booted without USB. Solution: Call Serial.setTxTimeoutMs(0) after Serial.begin() to make all Serial output non-blocking. Also added if (Serial) guards to high-traffic logging paths in EpubReaderActivity as belt-and-suspenders protection. Includes documentation of the debugging process and Serial call inventory. Also applies clang-format to fix pre-existing formatting issues.
This commit is contained in:
@@ -111,24 +111,25 @@ void EpubReaderActivity::onEnter() {
|
||||
FsFile f;
|
||||
if (SdMan.openFileForRead("ERS", epub->getCachePath() + "/progress.bin", f)) {
|
||||
const size_t fileSize = f.size();
|
||||
|
||||
|
||||
if (fileSize >= 9) {
|
||||
// New format: version (1) + spineIndex (2) + pageNumber (2) + contentOffset (4) = 9 bytes
|
||||
uint8_t version;
|
||||
serialization::readPod(f, version);
|
||||
|
||||
|
||||
if (version == EPUB_PROGRESS_VERSION) {
|
||||
uint16_t spineIndex, pageNumber;
|
||||
serialization::readPod(f, spineIndex);
|
||||
serialization::readPod(f, pageNumber);
|
||||
serialization::readPod(f, savedContentOffset);
|
||||
|
||||
|
||||
currentSpineIndex = spineIndex;
|
||||
nextPageNumber = pageNumber;
|
||||
hasContentOffset = true;
|
||||
|
||||
Serial.printf("[%lu] [ERS] Loaded progress v1: spine %d, page %d, offset %u\n",
|
||||
millis(), currentSpineIndex, nextPageNumber, savedContentOffset);
|
||||
|
||||
if (Serial)
|
||||
Serial.printf("[%lu] [ERS] Loaded progress v1: spine %d, page %d, offset %u\n", millis(), currentSpineIndex,
|
||||
nextPageNumber, savedContentOffset);
|
||||
} else {
|
||||
// Unknown version, try legacy format
|
||||
f.seek(0);
|
||||
@@ -137,8 +138,9 @@ void EpubReaderActivity::onEnter() {
|
||||
currentSpineIndex = data[0] + (data[1] << 8);
|
||||
nextPageNumber = data[2] + (data[3] << 8);
|
||||
hasContentOffset = false;
|
||||
Serial.printf("[%lu] [ERS] Loaded legacy progress (unknown version %d): spine %d, page %d\n",
|
||||
millis(), version, currentSpineIndex, nextPageNumber);
|
||||
if (Serial)
|
||||
Serial.printf("[%lu] [ERS] Loaded legacy progress (unknown version %d): spine %d, page %d\n", millis(),
|
||||
version, currentSpineIndex, nextPageNumber);
|
||||
}
|
||||
}
|
||||
} else if (fileSize >= 4) {
|
||||
@@ -148,20 +150,23 @@ void EpubReaderActivity::onEnter() {
|
||||
currentSpineIndex = data[0] + (data[1] << 8);
|
||||
nextPageNumber = data[2] + (data[3] << 8);
|
||||
hasContentOffset = false;
|
||||
Serial.printf("[%lu] [ERS] Loaded legacy progress: spine %d, page %d\n",
|
||||
millis(), currentSpineIndex, nextPageNumber);
|
||||
if (Serial)
|
||||
Serial.printf("[%lu] [ERS] Loaded legacy progress: spine %d, page %d\n", millis(), currentSpineIndex,
|
||||
nextPageNumber);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
// We may want a better condition to detect if we are opening for the first time.
|
||||
// This will trigger if the book is re-opened at Chapter 0.
|
||||
if (currentSpineIndex == 0) {
|
||||
int textSpineIndex = epub->getSpineIndexForTextReference();
|
||||
if (textSpineIndex != 0) {
|
||||
currentSpineIndex = textSpineIndex;
|
||||
Serial.printf("[%lu] [ERS] Opened for first time, navigating to text reference at index %d\n", millis(),
|
||||
textSpineIndex);
|
||||
if (Serial)
|
||||
Serial.printf("[%lu] [ERS] Opened for first time, navigating to text reference at index %d\n", millis(),
|
||||
textSpineIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,19 +305,20 @@ void EpubReaderActivity::loop() {
|
||||
Section* cachedSection = section.get();
|
||||
SemaphoreHandle_t cachedMutex = renderingMutex;
|
||||
EpubReaderActivity* self = this;
|
||||
|
||||
|
||||
// Handle dictionary mode selection - exitActivity deletes DictionaryMenuActivity
|
||||
exitActivity();
|
||||
|
||||
|
||||
if (mode == DictionaryMode::ENTER_WORD) {
|
||||
// Enter word mode - show keyboard and search
|
||||
self->enterNewActivity(new DictionarySearchActivity(cachedRenderer, cachedMappedInput,
|
||||
[self]() {
|
||||
// On back from dictionary
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
},
|
||||
"")); // Empty string = show keyboard
|
||||
self->enterNewActivity(new DictionarySearchActivity(
|
||||
cachedRenderer, cachedMappedInput,
|
||||
[self]() {
|
||||
// On back from dictionary
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
},
|
||||
"")); // Empty string = show keyboard
|
||||
} else {
|
||||
// Select from screen mode - show word selection on current page
|
||||
if (cachedSection) {
|
||||
@@ -322,7 +328,7 @@ void EpubReaderActivity::loop() {
|
||||
// Get margins for word selection positioning
|
||||
int orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft;
|
||||
cachedRenderer.getOrientedViewableTRBL(&orientedMarginTop, &orientedMarginRight, &orientedMarginBottom,
|
||||
&orientedMarginLeft);
|
||||
&orientedMarginLeft);
|
||||
orientedMarginTop += SETTINGS.screenMargin;
|
||||
orientedMarginLeft += SETTINGS.screenMargin;
|
||||
|
||||
@@ -335,12 +341,13 @@ void EpubReaderActivity::loop() {
|
||||
[self](const std::string& selectedWord) {
|
||||
// Word selected - look it up
|
||||
self->exitActivity();
|
||||
self->enterNewActivity(new DictionarySearchActivity(self->renderer, self->mappedInput,
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
},
|
||||
selectedWord));
|
||||
self->enterNewActivity(new DictionarySearchActivity(
|
||||
self->renderer, self->mappedInput,
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
},
|
||||
selectedWord));
|
||||
},
|
||||
[self]() {
|
||||
// Cancelled word selection
|
||||
@@ -372,14 +379,14 @@ void EpubReaderActivity::loop() {
|
||||
if (SETTINGS.shortPwrBtn == CrossPointSettings::SHORT_PWRBTN::QUICK_MENU &&
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Power)) {
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
|
||||
|
||||
// Check if current page is bookmarked
|
||||
bool isBookmarked = false;
|
||||
if (section) {
|
||||
const uint32_t contentOffset = section->getContentOffsetForPage(section->currentPage);
|
||||
isBookmarked = BookmarkStore::isPageBookmarked(epub->getPath(), currentSpineIndex, contentOffset);
|
||||
}
|
||||
|
||||
|
||||
exitActivity();
|
||||
enterNewActivity(new QuickMenuActivity(
|
||||
renderer, mappedInput,
|
||||
@@ -387,9 +394,9 @@ void EpubReaderActivity::loop() {
|
||||
// Cache values before exitActivity
|
||||
EpubReaderActivity* self = this;
|
||||
SemaphoreHandle_t cachedMutex = renderingMutex;
|
||||
|
||||
|
||||
exitActivity();
|
||||
|
||||
|
||||
if (action == QuickMenuAction::DICTIONARY) {
|
||||
// Open dictionary menu - cache renderer/input for this scope
|
||||
GfxRenderer& cachedRenderer = self->renderer;
|
||||
@@ -402,15 +409,17 @@ void EpubReaderActivity::loop() {
|
||||
MappedInputManager& m = self->mappedInput;
|
||||
Section* s = self->section.get();
|
||||
SemaphoreHandle_t mtx = self->renderingMutex;
|
||||
|
||||
|
||||
self->exitActivity();
|
||||
|
||||
|
||||
if (mode == DictionaryMode::ENTER_WORD) {
|
||||
self->enterNewActivity(new DictionarySearchActivity(r, m,
|
||||
self->enterNewActivity(new DictionarySearchActivity(
|
||||
r, m,
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
}, ""));
|
||||
},
|
||||
""));
|
||||
} else if (s) {
|
||||
xSemaphoreTake(mtx, portMAX_DELAY);
|
||||
auto page = s->loadPageFromSectionFile();
|
||||
@@ -420,7 +429,7 @@ void EpubReaderActivity::loop() {
|
||||
mt += SETTINGS.screenMargin;
|
||||
ml += SETTINGS.screenMargin;
|
||||
const int fontId = SETTINGS.getReaderFontId();
|
||||
|
||||
|
||||
self->enterNewActivity(new EpubWordSelectionActivity(
|
||||
r, m, std::move(page), fontId, ml, mt,
|
||||
[self](const std::string& word) {
|
||||
@@ -430,7 +439,8 @@ void EpubReaderActivity::loop() {
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
}, word));
|
||||
},
|
||||
word));
|
||||
},
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
@@ -455,7 +465,7 @@ void EpubReaderActivity::loop() {
|
||||
if (self->section) {
|
||||
const uint32_t contentOffset = self->section->getContentOffsetForPage(self->section->currentPage);
|
||||
const std::string& bookPath = self->epub->getPath();
|
||||
|
||||
|
||||
if (BookmarkStore::isPageBookmarked(bookPath, self->currentSpineIndex, contentOffset)) {
|
||||
// Remove bookmark
|
||||
BookmarkStore::removeBookmark(bookPath, self->currentSpineIndex, contentOffset);
|
||||
@@ -466,7 +476,7 @@ void EpubReaderActivity::loop() {
|
||||
bm.contentOffset = contentOffset;
|
||||
bm.pageNumber = self->section->currentPage;
|
||||
bm.timestamp = millis() / 1000; // Approximate timestamp
|
||||
|
||||
|
||||
// Generate name: "Chapter - Page X" or fallback
|
||||
std::string chapterTitle;
|
||||
const int tocIndex = self->epub->getTocIndexForSpineIndex(self->currentSpineIndex);
|
||||
@@ -478,7 +488,7 @@ void EpubReaderActivity::loop() {
|
||||
} else {
|
||||
bm.name = "Page " + std::to_string(self->section->currentPage + 1);
|
||||
}
|
||||
|
||||
|
||||
BookmarkStore::addBookmark(bookPath, bm);
|
||||
}
|
||||
}
|
||||
@@ -629,18 +639,19 @@ void EpubReaderActivity::renderScreen() {
|
||||
|
||||
if (!section) {
|
||||
const auto filepath = epub->getSpineItem(currentSpineIndex).href;
|
||||
Serial.printf("[%lu] [ERS] Loading file: %s, index: %d\n", millis(), filepath.c_str(), currentSpineIndex);
|
||||
if (Serial)
|
||||
Serial.printf("[%lu] [ERS] Loading file: %s, index: %d\n", millis(), filepath.c_str(), currentSpineIndex);
|
||||
section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer));
|
||||
|
||||
const uint16_t viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
|
||||
const uint16_t viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
|
||||
|
||||
bool sectionWasReIndexed = false;
|
||||
|
||||
|
||||
if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
|
||||
viewportHeight, SETTINGS.hyphenationEnabled)) {
|
||||
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
|
||||
if (Serial) Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
|
||||
sectionWasReIndexed = true;
|
||||
|
||||
// Progress bar dimensions
|
||||
@@ -683,15 +694,15 @@ void EpubReaderActivity::renderScreen() {
|
||||
renderer.displayBuffer(EInkDisplay::FAST_REFRESH);
|
||||
};
|
||||
|
||||
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
|
||||
viewportHeight, SETTINGS.hyphenationEnabled, progressSetup, progressCallback)) {
|
||||
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
|
||||
if (Serial) Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
|
||||
section.reset();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[%lu] [ERS] Cache found, skipping build...\n", millis());
|
||||
if (Serial) Serial.printf("[%lu] [ERS] Cache found, skipping build...\n", millis());
|
||||
}
|
||||
|
||||
// Determine the correct page to display
|
||||
@@ -703,8 +714,9 @@ void EpubReaderActivity::renderScreen() {
|
||||
// Use the offset to find the correct page
|
||||
const int restoredPage = section->findPageForContentOffset(savedContentOffset);
|
||||
section->currentPage = restoredPage;
|
||||
Serial.printf("[%lu] [ERS] Restored position via offset: %u -> page %d (was page %d)\n",
|
||||
millis(), savedContentOffset, restoredPage, nextPageNumber);
|
||||
if (Serial)
|
||||
Serial.printf("[%lu] [ERS] Restored position via offset: %u -> page %d (was page %d)\n", millis(),
|
||||
savedContentOffset, restoredPage, nextPageNumber);
|
||||
// Clear the offset flag since we've used it
|
||||
hasContentOffset = false;
|
||||
} else {
|
||||
@@ -716,7 +728,7 @@ void EpubReaderActivity::renderScreen() {
|
||||
renderer.clearScreen();
|
||||
|
||||
if (section->pageCount == 0) {
|
||||
Serial.printf("[%lu] [ERS] No pages to render\n", millis());
|
||||
if (Serial) Serial.printf("[%lu] [ERS] No pages to render\n", millis());
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Empty chapter", true, EpdFontFamily::BOLD);
|
||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||
renderer.displayBuffer();
|
||||
@@ -724,7 +736,9 @@ void EpubReaderActivity::renderScreen() {
|
||||
}
|
||||
|
||||
if (section->currentPage < 0 || section->currentPage >= section->pageCount) {
|
||||
Serial.printf("[%lu] [ERS] Page out of bounds: %d (max %d)\n", millis(), section->currentPage, section->pageCount);
|
||||
if (Serial)
|
||||
Serial.printf("[%lu] [ERS] Page out of bounds: %d (max %d)\n", millis(), section->currentPage,
|
||||
section->pageCount);
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, "Out of bounds", true, EpdFontFamily::BOLD);
|
||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||
renderer.displayBuffer();
|
||||
@@ -734,25 +748,25 @@ void EpubReaderActivity::renderScreen() {
|
||||
{
|
||||
auto p = section->loadPageFromSectionFile();
|
||||
if (!p) {
|
||||
Serial.printf("[%lu] [ERS] Failed to load page from SD - clearing section cache\n", millis());
|
||||
if (Serial) Serial.printf("[%lu] [ERS] Failed to load page from SD - clearing section cache\n", millis());
|
||||
section->clearCache();
|
||||
section.reset();
|
||||
return renderScreen();
|
||||
}
|
||||
|
||||
|
||||
// Handle empty pages (e.g., from malformed chapters that couldn't be parsed)
|
||||
if (p->elements.empty()) {
|
||||
Serial.printf("[%lu] [ERS] Page has no content (possibly malformed chapter)\n", millis());
|
||||
if (Serial) Serial.printf("[%lu] [ERS] Page has no content (possibly malformed chapter)\n", millis());
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, 280, "Chapter content unavailable", true, EpdFontFamily::BOLD);
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, 320, "(File may be malformed)");
|
||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||
renderer.displayBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const auto start = millis();
|
||||
renderContents(std::move(p), orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||
Serial.printf("[%lu] [ERS] Rendered page in %dms\n", millis(), millis() - start);
|
||||
if (Serial) Serial.printf("[%lu] [ERS] Rendered page in %dms\n", millis(), millis() - start);
|
||||
}
|
||||
|
||||
// Save progress with content offset for position restoration after re-indexing
|
||||
@@ -760,16 +774,17 @@ void EpubReaderActivity::renderScreen() {
|
||||
if (SdMan.openFileForWrite("ERS", epub->getCachePath() + "/progress.bin", f)) {
|
||||
// Get content offset for current page
|
||||
const uint32_t contentOffset = section->getContentOffsetForPage(section->currentPage);
|
||||
|
||||
|
||||
// New format: version (1) + spineIndex (2) + pageNumber (2) + contentOffset (4) = 9 bytes
|
||||
serialization::writePod(f, EPUB_PROGRESS_VERSION);
|
||||
serialization::writePod(f, static_cast<uint16_t>(currentSpineIndex));
|
||||
serialization::writePod(f, static_cast<uint16_t>(section->currentPage));
|
||||
serialization::writePod(f, contentOffset);
|
||||
|
||||
|
||||
f.close();
|
||||
Serial.printf("[%lu] [ERS] Saved progress: spine %d, page %d, offset %u\n",
|
||||
millis(), currentSpineIndex, section->currentPage, contentOffset);
|
||||
if (Serial)
|
||||
Serial.printf("[%lu] [ERS] Saved progress: spine %d, page %d, offset %u\n", millis(), currentSpineIndex,
|
||||
section->currentPage, contentOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,7 +792,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
const int orientedMarginRight, const int orientedMarginBottom,
|
||||
const int orientedMarginLeft) {
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
|
||||
|
||||
// Draw bookmark indicator (folded corner) if this page is bookmarked
|
||||
if (section) {
|
||||
const uint32_t contentOffset = section->getContentOffsetForPage(section->currentPage);
|
||||
@@ -787,14 +802,14 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
constexpr int cornerSize = 20;
|
||||
const int cornerX = screenWidth - orientedMarginRight - cornerSize;
|
||||
const int cornerY = orientedMarginTop;
|
||||
|
||||
|
||||
// Draw triangle (folded corner effect)
|
||||
const int xPoints[3] = {cornerX, cornerX + cornerSize, cornerX + cornerSize};
|
||||
const int yPoints[3] = {cornerY, cornerY, cornerY + cornerSize};
|
||||
renderer.fillPolygon(xPoints, yPoints, 3, true); // Black triangle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||
if (pagesUntilFullRefresh <= 1) {
|
||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||
|
||||
@@ -24,7 +24,7 @@ class EpubReaderActivity final : public ActivityWithSubactivity {
|
||||
// End-of-book prompt state
|
||||
bool showingEndOfBookPrompt = false;
|
||||
int endOfBookSelection = 2; // 0=Archive, 1=Delete, 2=Keep (default to safe option)
|
||||
|
||||
|
||||
// Content offset for position restoration after re-indexing
|
||||
uint32_t savedContentOffset = 0;
|
||||
bool hasContentOffset = false; // True if we have a valid content offset to use
|
||||
|
||||
@@ -160,12 +160,13 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
|
||||
const int bezelLeft = renderer.getBezelOffsetLeft();
|
||||
const int bezelRight = renderer.getBezelOffsetRight();
|
||||
|
||||
const std::string title =
|
||||
renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(), pageWidth - 40 - bezelLeft - bezelRight, EpdFontFamily::BOLD);
|
||||
const std::string title = renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(),
|
||||
pageWidth - 40 - bezelLeft - bezelRight, EpdFontFamily::BOLD);
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, 15 + bezelTop, title.c_str(), true, EpdFontFamily::BOLD);
|
||||
|
||||
const auto pageStartIndex = selectorIndex / pageItems * pageItems;
|
||||
renderer.fillRect(bezelLeft, 60 + bezelTop + (selectorIndex % pageItems) * 30 - 2, pageWidth - 1 - bezelLeft - bezelRight, 30);
|
||||
renderer.fillRect(bezelLeft, 60 + bezelTop + (selectorIndex % pageItems) * 30 - 2,
|
||||
pageWidth - 1 - bezelLeft - bezelRight, 30);
|
||||
|
||||
for (int itemIndex = pageStartIndex; itemIndex < totalItems && itemIndex < pageStartIndex + pageItems; itemIndex++) {
|
||||
const int displayY = 60 + bezelTop + (itemIndex % pageItems) * 30;
|
||||
@@ -175,8 +176,8 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
|
||||
const int tocIndex = tocIndexFromItemIndex(itemIndex);
|
||||
auto item = epub->getTocItem(tocIndex);
|
||||
const int indentSize = 20 + bezelLeft + (item.level - 1) * 15;
|
||||
const std::string chapterName =
|
||||
renderer.truncatedText(UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - bezelLeft - bezelRight - indentSize + 20 + bezelLeft);
|
||||
const std::string chapterName = renderer.truncatedText(
|
||||
UI_10_FONT_ID, item.title.c_str(), pageWidth - 40 - bezelLeft - bezelRight - indentSize + 20 + bezelLeft);
|
||||
renderer.drawText(UI_10_FONT_ID, indentSize, displayY, chapterName.c_str(), !isSelected);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,11 +62,8 @@ void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
|
||||
currentBookPath = epubPath;
|
||||
exitActivity();
|
||||
enterNewActivity(new EpubReaderActivity(
|
||||
renderer, mappedInput, std::move(epub),
|
||||
[this, epubPath] { goToLibrary(epubPath); },
|
||||
[this] { onGoBack(); },
|
||||
onGoToClearCache,
|
||||
onGoToSettings));
|
||||
renderer, mappedInput, std::move(epub), [this, epubPath] { goToLibrary(epubPath); }, [this] { onGoBack(); },
|
||||
onGoToClearCache, onGoToSettings));
|
||||
}
|
||||
|
||||
void ReaderActivity::onGoToTxtReader(std::unique_ptr<Txt> txt) {
|
||||
|
||||
@@ -658,15 +658,15 @@ void TxtReaderActivity::saveProgress() const {
|
||||
if (SdMan.openFileForWrite("TRS", txt->getCachePath() + "/progress.bin", f)) {
|
||||
// New format: version + byte offset + page number (for backwards compatibility debugging)
|
||||
serialization::writePod(f, PROGRESS_VERSION);
|
||||
|
||||
|
||||
// Store byte offset - this is stable across font/setting changes
|
||||
const size_t byteOffset = (currentPage >= 0 && currentPage < static_cast<int>(pageOffsets.size()))
|
||||
? pageOffsets[currentPage] : 0;
|
||||
const size_t byteOffset =
|
||||
(currentPage >= 0 && currentPage < static_cast<int>(pageOffsets.size())) ? pageOffsets[currentPage] : 0;
|
||||
serialization::writePod(f, static_cast<uint32_t>(byteOffset));
|
||||
|
||||
|
||||
// Also store page number for debugging/logging purposes
|
||||
serialization::writePod(f, static_cast<uint16_t>(currentPage));
|
||||
|
||||
|
||||
f.close();
|
||||
Serial.printf("[%lu] [TRS] Saved progress: page %d, offset %zu\n", millis(), currentPage, byteOffset);
|
||||
}
|
||||
@@ -677,24 +677,24 @@ void TxtReaderActivity::loadProgress() {
|
||||
if (SdMan.openFileForRead("TRS", txt->getCachePath() + "/progress.bin", f)) {
|
||||
// Check file size to determine format
|
||||
const size_t fileSize = f.size();
|
||||
|
||||
|
||||
if (fileSize >= 7) {
|
||||
// New format: version (1) + byte offset (4) + page number (2) = 7 bytes
|
||||
uint8_t version;
|
||||
serialization::readPod(f, version);
|
||||
|
||||
|
||||
if (version == PROGRESS_VERSION) {
|
||||
uint32_t savedOffset;
|
||||
serialization::readPod(f, savedOffset);
|
||||
|
||||
|
||||
uint16_t savedPage;
|
||||
serialization::readPod(f, savedPage);
|
||||
|
||||
|
||||
// Use byte offset to find the correct page (works even if re-indexed)
|
||||
currentPage = findPageForOffset(savedOffset);
|
||||
|
||||
Serial.printf("[%lu] [TRS] Loaded progress: offset %u -> page %d/%d (was page %d)\n",
|
||||
millis(), savedOffset, currentPage, totalPages, savedPage);
|
||||
|
||||
Serial.printf("[%lu] [TRS] Loaded progress: offset %u -> page %d/%d (was page %d)\n", millis(), savedOffset,
|
||||
currentPage, totalPages, savedPage);
|
||||
} else {
|
||||
// Unknown version, fall back to legacy behavior
|
||||
Serial.printf("[%lu] [TRS] Unknown progress version %d, ignoring\n", millis(), version);
|
||||
@@ -708,7 +708,7 @@ void TxtReaderActivity::loadProgress() {
|
||||
Serial.printf("[%lu] [TRS] Loaded legacy progress: page %d/%d\n", millis(), currentPage, totalPages);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Bounds check
|
||||
if (currentPage >= totalPages) {
|
||||
currentPage = totalPages - 1;
|
||||
@@ -716,7 +716,7 @@ void TxtReaderActivity::loadProgress() {
|
||||
if (currentPage < 0) {
|
||||
currentPage = 0;
|
||||
}
|
||||
|
||||
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
@@ -725,16 +725,16 @@ int TxtReaderActivity::findPageForOffset(size_t targetOffset) const {
|
||||
if (pageOffsets.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Binary search: find the largest offset that is <= targetOffset
|
||||
// This finds the page that contains or starts at the target offset
|
||||
auto it = std::upper_bound(pageOffsets.begin(), pageOffsets.end(), targetOffset);
|
||||
|
||||
|
||||
if (it == pageOffsets.begin()) {
|
||||
// Target is before the first page, return page 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// upper_bound returns iterator to first element > targetOffset
|
||||
// So we need the element before it (which is <= targetOffset)
|
||||
return static_cast<int>(std::distance(pageOffsets.begin(), it) - 1);
|
||||
|
||||
Reference in New Issue
Block a user