Compare commits

...

4 Commits

Author SHA1 Message Date
danoob
7a50becd8f
Merge aa448fe470 into 78d6e5931c 2026-02-04 00:34:17 +03:00
Jake Kenneally
78d6e5931c
fix: Correct debugging_monitor.py script instructions (#676)
Some checks are pending
CI / build (push) Waiting to run
## Summary

**What is the goal of this PR?**
- Minor correction to the `debugging_monitor.py` script instructions

**What changes are included?**
- `pyserial` should be installed, NOT `serial`, which is a [different
lib](https://pypi.org/project/serial/)
- Added macOS serial port

## Additional Context

- Just a minor docs update. I can confirm the debugging script is
working great on macOS

---

### 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 >**_
2026-02-04 00:33:20 +03:00
Luke Stein
dac11c3fdd
fix: Correct instruction text to match actual button text (#672)
## Summary

* Instruction text says "Press OK to scan again" but button label is
actually "Connect" (not OK)
* Corrects instruction text

---

### AI Usage

Did you use AI tools to help write this code? **No**
2026-02-04 00:32:52 +03:00
danoooob
aa448fe470 move sync feature to menu 2026-02-03 19:30:06 +07:00
7 changed files with 92 additions and 85 deletions

View File

@ -102,13 +102,18 @@ After flashing the new features, its recommended to capture detailed logs fro
First, make sure all required Python packages are installed: First, make sure all required Python packages are installed:
```python ```python
python3 -m pip install serial colorama matplotlib python3 -m pip install pyserial colorama matplotlib
``` ```
after that run the script: after that run the script:
```sh ```sh
# For Linux
# This was tested on Debian and should work on most Linux systems.
python3 scripts/debugging_monitor.py python3 scripts/debugging_monitor.py
# For macOS
python3 scripts/debugging_monitor.py /dev/cu.usbmodem2101
``` ```
This was tested on Debian and should work on most Linux systems. Minor adjustments may be required for Windows or macOS. Minor adjustments may be required for Windows.
## Internals ## Internals

View File

@ -520,7 +520,7 @@ void WifiSelectionActivity::renderNetworkList() const {
const auto height = renderer.getLineHeight(UI_10_FONT_ID); const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height) / 2; const auto top = (pageHeight - height) / 2;
renderer.drawCenteredText(UI_10_FONT_ID, top, "No networks found"); renderer.drawCenteredText(UI_10_FONT_ID, top, "No networks found");
renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Press OK to scan again"); renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Press Connect to scan again");
} else { } else {
// Calculate how many networks we can display // Calculate how many networks we can display
constexpr int startY = 60; constexpr int startY = 60;

View File

@ -8,6 +8,8 @@
#include "CrossPointSettings.h" #include "CrossPointSettings.h"
#include "CrossPointState.h" #include "CrossPointState.h"
#include "EpubReaderChapterSelectionActivity.h" #include "EpubReaderChapterSelectionActivity.h"
#include "KOReaderCredentialStore.h"
#include "KOReaderSyncActivity.h"
#include "MappedInputManager.h" #include "MappedInputManager.h"
#include "RecentBooksStore.h" #include "RecentBooksStore.h"
#include "ScreenComponents.h" #include "ScreenComponents.h"
@ -120,10 +122,49 @@ void EpubReaderActivity::loop() {
// Pass input responsibility to sub activity if exists // Pass input responsibility to sub activity if exists
if (subActivity) { if (subActivity) {
subActivity->loop(); subActivity->loop();
// Deferred exit: process after subActivity->loop() returns to avoid use-after-free
if (pendingSubactivityExit) {
pendingSubactivityExit = false;
exitActivity();
updateRequired = true;
skipNextButtonCheck = true; // Skip button processing to ignore stale events
}
// Deferred go home: process after subActivity->loop() returns to avoid race condition
if (pendingGoHome) {
pendingGoHome = false;
exitActivity();
if (onGoHome) {
onGoHome();
}
return; // Don't access 'this' after callback
}
return; return;
} }
// Enter chapter selection activity // Handle pending go home when no subactivity (e.g., from long press back)
if (pendingGoHome) {
pendingGoHome = false;
if (onGoHome) {
onGoHome();
}
return; // Don't access 'this' after callback
}
// Skip button processing after returning from subactivity
// This prevents stale button release events from triggering actions
// We wait until: (1) all relevant buttons are released, AND (2) wasReleased events have been cleared
if (skipNextButtonCheck) {
const bool confirmCleared = !mappedInput.isPressed(MappedInputManager::Button::Confirm) &&
!mappedInput.wasReleased(MappedInputManager::Button::Confirm);
const bool backCleared = !mappedInput.isPressed(MappedInputManager::Button::Back) &&
!mappedInput.wasReleased(MappedInputManager::Button::Back);
if (confirmCleared && backCleared) {
skipNextButtonCheck = false;
}
return;
}
// Confirm button opens menu
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
// Don't start activity transition while rendering // Don't start activity transition while rendering
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
@ -269,11 +310,8 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
break; break;
} }
case EpubReaderMenuActivity::MenuAction::GO_HOME: { case EpubReaderMenuActivity::MenuAction::GO_HOME: {
// 2. Trigger the reader's "Go Home" callback // Defer go home to avoid race condition with display task
if (onGoHome) { pendingGoHome = true;
onGoHome();
}
break; break;
} }
case EpubReaderMenuActivity::MenuAction::DELETE_CACHE: { case EpubReaderMenuActivity::MenuAction::DELETE_CACHE: {
@ -294,10 +332,34 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
saveProgress(backupSpine, backupPage, backupPageCount); saveProgress(backupSpine, backupPage, backupPageCount);
} }
exitActivity();
updateRequired = true;
xSemaphoreGive(renderingMutex); xSemaphoreGive(renderingMutex);
if (onGoHome) onGoHome(); // Defer go home to avoid race condition with display task
pendingGoHome = true;
break;
}
case EpubReaderMenuActivity::MenuAction::SYNC: {
if (KOREADER_STORE.hasCredentials()) {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
const int currentPage = section ? section->currentPage : 0;
const int totalPages = section ? section->pageCount : 0;
exitActivity();
enterNewActivity(new KOReaderSyncActivity(
renderer, mappedInput, epub, epub->getPath(), currentSpineIndex, currentPage, totalPages,
[this]() {
// On cancel - defer exit to avoid use-after-free
pendingSubactivityExit = true;
},
[this](int newSpineIndex, int newPage) {
// On sync complete - update position and defer exit
if (currentSpineIndex != newSpineIndex || (section && section->currentPage != newPage)) {
currentSpineIndex = newSpineIndex;
nextPageNumber = newPage;
section.reset();
}
pendingSubactivityExit = true;
}));
xSemaphoreGive(renderingMutex);
}
break; break;
} }
} }

View File

@ -19,6 +19,9 @@ class EpubReaderActivity final : public ActivityWithSubactivity {
int cachedSpineIndex = 0; int cachedSpineIndex = 0;
int cachedChapterTotalPageCount = 0; int cachedChapterTotalPageCount = 0;
bool updateRequired = false; bool updateRequired = false;
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
const std::function<void()> onGoBack; const std::function<void()> onGoBack;
const std::function<void()> onGoHome; const std::function<void()> onGoHome;

View File

@ -2,8 +2,6 @@
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include "KOReaderCredentialStore.h"
#include "KOReaderSyncActivity.h"
#include "MappedInputManager.h" #include "MappedInputManager.h"
#include "fontIds.h" #include "fontIds.h"
@ -12,25 +10,7 @@ namespace {
constexpr int SKIP_PAGE_MS = 700; constexpr int SKIP_PAGE_MS = 700;
} // namespace } // namespace
bool EpubReaderChapterSelectionActivity::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); } int EpubReaderChapterSelectionActivity::getTotalItems() const { return epub->getTocItemsCount(); }
int EpubReaderChapterSelectionActivity::getTotalItems() const {
// Add 2 for sync options (top and bottom) if credentials are configured
const int syncCount = hasSyncOption() ? 2 : 0;
return epub->getTocItemsCount() + syncCount;
}
bool EpubReaderChapterSelectionActivity::isSyncItem(int index) const {
if (!hasSyncOption()) return false;
// First item and last item are sync options
return index == 0 || index == getTotalItems() - 1;
}
int EpubReaderChapterSelectionActivity::tocIndexFromItemIndex(int itemIndex) const {
// Account for the sync option at the top
const int offset = hasSyncOption() ? 1 : 0;
return itemIndex - offset;
}
int EpubReaderChapterSelectionActivity::getPageItems() const { int EpubReaderChapterSelectionActivity::getPageItems() const {
// Layout constants used in renderScreen // Layout constants used in renderScreen
@ -64,13 +44,10 @@ void EpubReaderChapterSelectionActivity::onEnter() {
renderingMutex = xSemaphoreCreateMutex(); renderingMutex = xSemaphoreCreateMutex();
// Account for sync option offset when finding current TOC index
const int syncOffset = hasSyncOption() ? 1 : 0;
selectorIndex = epub->getTocIndexForSpineIndex(currentSpineIndex); selectorIndex = epub->getTocIndexForSpineIndex(currentSpineIndex);
if (selectorIndex == -1) { if (selectorIndex == -1) {
selectorIndex = 0; selectorIndex = 0;
} }
selectorIndex += syncOffset; // Offset for top sync option
// Trigger first update // Trigger first update
updateRequired = true; updateRequired = true;
@ -95,24 +72,6 @@ void EpubReaderChapterSelectionActivity::onExit() {
renderingMutex = nullptr; renderingMutex = nullptr;
} }
void EpubReaderChapterSelectionActivity::launchSyncActivity() {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new KOReaderSyncActivity(
renderer, mappedInput, epub, epubPath, currentSpineIndex, currentPage, totalPagesInSpine,
[this]() {
// On cancel
exitActivity();
updateRequired = true;
},
[this](int newSpineIndex, int newPage) {
// On sync complete
exitActivity();
onSyncPosition(newSpineIndex, newPage);
}));
xSemaphoreGive(renderingMutex);
}
void EpubReaderChapterSelectionActivity::loop() { void EpubReaderChapterSelectionActivity::loop() {
if (subActivity) { if (subActivity) {
subActivity->loop(); subActivity->loop();
@ -129,15 +88,7 @@ void EpubReaderChapterSelectionActivity::loop() {
const int totalItems = getTotalItems(); const int totalItems = getTotalItems();
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
// Check if sync option is selected (first or last item) const auto newSpineIndex = epub->getSpineIndexForTocIndex(selectorIndex);
if (isSyncItem(selectorIndex)) {
launchSyncActivity();
return;
}
// Get TOC index (account for top sync offset)
const int tocIndex = tocIndexFromItemIndex(selectorIndex);
const auto newSpineIndex = epub->getSpineIndexForTocIndex(tocIndex);
if (newSpineIndex == -1) { if (newSpineIndex == -1) {
onGoBack(); onGoBack();
} else { } else {
@ -192,11 +143,7 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
const int displayY = 60 + i * 30; const int displayY = 60 + i * 30;
const bool isSelected = (itemIndex == selectorIndex); const bool isSelected = (itemIndex == selectorIndex);
if (isSyncItem(itemIndex)) { auto item = epub->getTocItem(itemIndex);
renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected);
} else {
const int tocIndex = tocIndexFromItemIndex(itemIndex);
auto item = epub->getTocItem(tocIndex);
const int indentSize = 20 + (item.level - 1) * 15; const int indentSize = 20 + (item.level - 1) * 15;
const std::string chapterName = const std::string chapterName =
@ -204,7 +151,6 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
renderer.drawText(UI_10_FONT_ID, indentSize, displayY, chapterName.c_str(), !isSelected); renderer.drawText(UI_10_FONT_ID, indentSize, displayY, chapterName.c_str(), !isSelected);
} }
}
// Skip button hints in landscape CW mode (they overlap content) // Skip button hints in landscape CW mode (they overlap content)
if (renderer.getOrientation() != GfxRenderer::LandscapeClockwise) { if (renderer.getOrientation() != GfxRenderer::LandscapeClockwise) {

View File

@ -26,22 +26,12 @@ class EpubReaderChapterSelectionActivity final : public ActivityWithSubactivity
// This adapts automatically when switching between portrait and landscape. // This adapts automatically when switching between portrait and landscape.
int getPageItems() const; int getPageItems() const;
// Total items including sync options (top and bottom) // Total TOC items count
int getTotalItems() const; int getTotalItems() const;
// Check if sync option is available (credentials configured)
bool hasSyncOption() const;
// Check if given item index is a sync option (first or last)
bool isSyncItem(int index) const;
// Convert item index to TOC index (accounting for top sync option offset)
int tocIndexFromItemIndex(int itemIndex) const;
static void taskTrampoline(void* param); static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop(); [[noreturn]] void displayTaskLoop();
void renderScreen(); void renderScreen();
void launchSyncActivity();
public: public:
explicit EpubReaderChapterSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, explicit EpubReaderChapterSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,

View File

@ -13,7 +13,7 @@
class EpubReaderMenuActivity final : public ActivityWithSubactivity { class EpubReaderMenuActivity final : public ActivityWithSubactivity {
public: public:
enum class MenuAction { SELECT_CHAPTER, GO_HOME, DELETE_CACHE }; enum class MenuAction { SELECT_CHAPTER, GO_HOME, SYNC, DELETE_CACHE };
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title, explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
const std::function<void()>& onBack, const std::function<void(MenuAction)>& onAction) const std::function<void()>& onBack, const std::function<void(MenuAction)>& onAction)
@ -34,6 +34,7 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
const std::vector<MenuItem> menuItems = {{MenuAction::SELECT_CHAPTER, "Go to Chapter"}, const std::vector<MenuItem> menuItems = {{MenuAction::SELECT_CHAPTER, "Go to Chapter"},
{MenuAction::GO_HOME, "Go Home"}, {MenuAction::GO_HOME, "Go Home"},
{MenuAction::SYNC, "Sync Progress"},
{MenuAction::DELETE_CACHE, "Delete Book Cache"}}; {MenuAction::DELETE_CACHE, "Delete Book Cache"}};
int selectedIndex = 0; int selectedIndex = 0;