Skip to content

Fix for spurious multiple tap registration by preventing false releases from registering. Add edge-repeat for page turns.#1

Open
mikaryyn wants to merge 1 commit into
juicecultus:experimentalfrom
mikaryyn:experimental
Open

Fix for spurious multiple tap registration by preventing false releases from registering. Add edge-repeat for page turns.#1
mikaryyn wants to merge 1 commit into
juicecultus:experimentalfrom
mikaryyn:experimental

Conversation

@mikaryyn
Copy link
Copy Markdown

@mikaryyn mikaryyn commented Jan 4, 2026

GT911 sampling can report no new data when the 0x80 'ready' bit isn't set. Previously gt911_read_point() returned false in this case and touch_task() treated it as a finger-up transition, which could generate spurious release/press cycles and show up as accidental double taps on short presses.

Fix this by returning a richer Gt911ReadResult:

  • NO_UPDATE when no new sample is available (leave gesture state untouched)
  • NO_TOUCH when a new sample indicates 0 points (true release)
  • UPDATED when coordinates are read
  • ERROR on I2C/device failure

touch_task() now ignores NO_UPDATE for state transitions but still checks timers, so HOLD can be detected while the finger is down without creating fake releases. I2C errors back off briefly and retry.

Also add an optional edge-repeat behavior: touches starting in the left/right third of the screen emit SWIPE_RIGHT/SWIPE_LEFT after 500ms and repeat every 500ms (while remaining within the long-press move threshold), followed by a RELEASE when the finger lifts.

GT911 sampling can report no new data when the 0x80 'ready' bit isn't set. Previously gt911_read_point() returned false in this case and touch_task() treated it as a finger-up transition, which could generate spurious release/press cycles and show up as accidental double taps on short presses.

Fix this by returning a richer Gt911ReadResult:
- NO_UPDATE when no new sample is available (leave gesture state untouched)
- NO_TOUCH when a new sample indicates 0 points (true release)
- UPDATED when coordinates are read
- ERROR on I2C/device failure

touch_task() now ignores NO_UPDATE for state transitions but still checks timers, so HOLD can be detected while the finger is down without creating fake releases. I2C errors back off briefly and retry.

Also add an optional edge-repeat behavior: touches starting in the left/right third of the screen emit SWIPE_RIGHT/SWIPE_LEFT after 500ms and repeat every 500ms (while remaining within the long-press move threshold), followed by a RELEASE when the finger lifts.
@mikaryyn mikaryyn changed the title Fix for spurious multiple tap registration by prevent false releases from registering. Add edge-repeat for page turns. Fix for spurious multiple tap registration by preventing false releases from registering. Add edge-repeat for page turns. Jan 4, 2026
@ericgilalvarez
Copy link
Copy Markdown

Can confirm, I am no longer seeing double page flips with this PR. Thanks!

@jlaunay
Copy link
Copy Markdown

jlaunay commented Jan 5, 2026

I confirm too, thanks a lot.
I still have three minor issues with this firmware: the footer is duplicated (current page/ page total) (see attached image), the homepage restarts and crashes if I don't do anything for a while, and navigating to the next and previous books page is very slow.

But with this PR, the experience is already significantly better, thank you very much.

IMG_20260105_213533

@mikaryyn
Copy link
Copy Markdown
Author

mikaryyn commented Jan 8, 2026

Thanks for testing it! I've been also using this for a few days and the navigation works well. Would you consider merging this @ratedcounsel?

I still have three minor issues with this firmware: the footer is duplicated (current page/ page total) (see attached image), the homepage restarts and crashes if I don't do anything for a while, and navigating to the next and previous books page is very slow.

I had the same footer problem and was wondering if it is a hardware or driver problem. I sort of "fixed" it by moving the footer text a little farther from the screen's edge. It works but doesn't feel like a proper solution, so I didn't include it in the pull request. Page navigation is fast enough for me in the experimental branch.

@jlaunay
Copy link
Copy Markdown

jlaunay commented Jan 8, 2026

Thanks for testing it! I've been also using this for a few days and the navigation works well. Would you consider merging this @ratedcounsel?

I still have three minor issues with this firmware: the footer is duplicated (current page/ page total) (see attached image), the homepage restarts and crashes if I don't do anything for a while, and navigating to the next and previous books page is very slow.

I had the same footer problem and was wondering if it is a hardware or driver problem. I sort of "fixed" it by moving the footer text a little farther from the screen's edge. It works but doesn't feel like a proper solution, so I didn't include it in the pull request. Page navigation is fast enough for me in the experimental branch.

I don't think it's hardware because I don't have any problem using main branch without fastEPD.
Is your fix available on a branch on your GitHub? I'd like to test even if not perfect.

Page navigation in book is also fast for me, it's just on the library screen (home) to browse books and if I don't select a book for a while it crash and reload.

@mikaryyn
Copy link
Copy Markdown
Author

mikaryyn commented Jan 8, 2026

I don't think it's hardware because I don't have any problem using main branch without fastEDP.
Is your fix available on a branch on your GitHub? I'd like to test even if not perfect.

You're probably right. Maybe it's a bug in FastEPD.. My padding fix is now in this branch: https://github.com/mikaryyn/EPub-M5Stack-Paper-S3/tree/exp-landscape, if you want to give it a go. It also has support for landscape mode that I'm testing. It's very recent so probably still buggy.

murrain pushed a commit to murrain/EPub-M5Stack-Paper-S3 that referenced this pull request May 8, 2026
Code-quality review on 7ec5e93 returned REQUEST CHANGES with one
blocker (issue juicecultus#1, ordering bug that made the multi-page persist
dead code) plus two cheap defensive fixes (turgu1#2 page_count clamp +
turgu1#3 static_assert sizes). All addressed in this commit.

BLOCKER FIX:

AppController::going_to_deep_sleep called page_cache.stop() BEFORE
wake_snapshot.persist(). PageCache::stop sets active_=false and
frees the slab; enumerate_complete short-circuits on !active_ and
returns 0. So in v2-format land, every persist wrote page_count=1
and zero extra entries — the multi-page format the whole commit
implemented was unreachable in deployed code.

Fix: reorder going_to_deep_sleep so persist runs first, while the
cache is still alive and enumerate_complete can read live entries.
The ordering rationale that justified stop-before-persist (lock-
order discipline cache → page_locs → epub for teardown) still
holds for the stop ordering — page_cache.stop now runs after
persist but still before page_locs.stop_document.

DEFENSIVE FIXES:

- WakeSnapshot::Header and PageEntryMeta now have static_assert
  on their sizes (32 and 12 bytes respectively). The v2 file
  format is a contract with on-disk state on user devices; a
  future field reorder that re-introduces compiler-inserted
  padding would silently break that contract without tripping
  the build. The reserved fields' entire job is to keep this
  invariant; the assert formalizes it.

- hydrate_page_cache now clamps hdr.page_count against
  PageCache::SLOT_COUNT + 1 BEFORE allocating any buffers. A
  corrupted file claiming page_count=65535 would otherwise
  trigger a 768 KB malloc, multi-MB seek loop, and 64-bit
  overflow in offset arithmetic on 32-bit ESP-IDF where long
  is 32 bits. The clamp short-circuits at the header check
  before any of that.

- offset arithmetic in hydrate_page_cache uses off_t (64-bit on
  ESP-IDF) instead of long (32-bit on Xtensa). Removes the
  overflow class entirely, even within the legal page_count
  range.

POLISH:

- PageEntryMeta's pad field renamed from reserved0 to pad0 to
  avoid name shadow with Header::reserved0 (same TU, same name,
  different sizes — small footgun).
- Hydrate's PageEntryMeta array is now stack-allocated (96 bytes
  max post-clamp) instead of malloced. One fewer allocation,
  one fewer failure path.
- capture() no longer sets cached_header_.page_count = 1 — that
  field is now set only by persist() once the live cache count
  is known. A misuse like calling persist() twice without re-
  capture is now self-rejecting on read (page_count=0 fails
  the restore check) rather than silently writing stale data.
- persist() comment block tightened: now states the actual
  invariant (mainTask is the only mutator from this point) rather
  than apologizing about the API design.

Phase D (wake-time event routing for the post-wake-pre-app-
controller swipe window) intentionally deferred — see scope
discussion in the integration branch's status update. The
multi-page persist + hydrate flow is the substantive Stage 2
deliverable; Phase D is a polish tier worth waiting for hardware
testing feedback before investing.
murrain pushed a commit to murrain/EPub-M5Stack-Paper-S3 that referenced this pull request May 8, 2026
Tinos symptom fix juicecultus#1 of 4. User-reported: switching to a custom
Tinos font caused books to fail to load (had to manually edit
PARS to recover). Two coordinated bugs:

(1) Fonts::adjust_default_font ran a non-transactional chain
of 4 replace() calls; if any failed, font_cache ended up half-
swapped (mismatched metrics across styles → broken layout).
PARS was persisted before validation → unrecoverable.

(2) Stop-document / page-cache quiesce ran AFTER the font swap,
not before. Retriever / pre-paint threads hold raw Font* via
fonts.get(); a concurrent dereference during the cache mutation
UAFs. The pre-refactor flow happened to be safe by accident
(stop_document inside is_modified block ran before adjust);
preserving that ordering was load-bearing.

Fix:
  - Fonts::adjust_default_font is now transactional + boolean.
    Pre-loads all 4 styles into Font* temps, validates via
    is_ready(), commits atomically under the cache mutex on
    success / deletes temps + leaves cache untouched on
    failure. The standalone Fonts::replace() (only caller was
    the pre-transactional version) is removed.
  - BookParamController: hoists page_locs.stop_document() +
    page_cache.invalidate_all() to BEFORE adjust_default_font.
    Validates the load BEFORE persisting PARS; on failure
    shows msg_viewer alert and reverts local 'font' so the
    put writes the working previous value.
  - OptionController: same pattern for the global default
    font (Config::Ident::DEFAULT_FONT). page_cache.invalidate
    is correctly absent — option menu entry already closed
    the file so page_cache is empty.
  - EPub::open: now logs LOG_E on adjust_default_font failure
    but doesn't fail the open (rendering with current default
    beats refusing the book). Comment notes why no quiesce
    hoist is needed (no live retriever at open time).
  - Fonts::clear_glyph_caches now acquires the cache mutex
    (was previously racing with clear(true)).
  - Per-font cumulative size cap bumped from 300 KB to 2 MB
    on touch builds (INKPLATE_6PLUS || TOUCH_TRIAL). Comment
    clarifies this is a boot-time menu-eligibility gate, not
    a runtime memory ceiling.
  - Fonts::get() concurrency contract documented in the
    header (lock-free read; mutators must not race with
    renderers).

Reviews on the post-fixup state:
  - architecture-guardian: APPROVE
  - code-quality-reviewer: APPROVE
murrain pushed a commit to murrain/EPub-M5Stack-Paper-S3 that referenced this pull request May 8, 2026
sleep_screen_viewer.cpp and usb_msc_viewer.cpp each carried a
private copy of make_fmt / put_centered / draw_hrule plus a PAD
margin constant. The bodies were byte-identical except for one
field — line_height_factor (1.1 in sleep_screen, 1.15 in usb_msc).

Extract to a shared CenteredLayout namespace:
- include/viewers/centered_layout.hpp: declarations
- src/viewers/centered_layout.cpp: definitions

The line_height_factor difference is now a parameter to make_fmt;
each viewer keeps its tuned value as a private constant alongside
its other styling. PAD = 52 is exposed as CenteredLayout::PAD
(sleep_screen still uses it directly for the progress-bar
geometry at lines ~315-316).

Future full-screen text viewers (an "About" / firmware-version
screen, a fatal-error screen) can reuse these helpers instead of
copy-pasting a third time. Per audit 03-viewers.md finding juicecultus#1
(byte-identical helpers); 30-min mechanical refactor as estimated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
murrain pushed a commit to murrain/EPub-M5Stack-Paper-S3 that referenced this pull request May 8, 2026
Audit 05-code-quality.md finding juicecultus#1: three near-identical Page::
Format builders existed at:
- src/viewers/book_viewer.cpp:83 (BookViewer::build_page_at)
- src/models/page_locs.cpp:849 (PageLocs::build_page_locs)
- src/models/page_cache.cpp:706 (page_cache pre-paint render)

The page_cache.cpp comment explicitly acknowledged the duplication
("Kept in lockstep manually rather than factored out"). PR1 (today)
already had to fix a silent page_bottom formula drift that resulted
from this triple-coupling — so the duplication has actively hurt
us once.

Adds Page::make_body_format(font_idx, font_size, screen_top,
screen_bottom). The four variable inputs are exactly the per-call
parts; everything else (left/right margins 10 px, line_height_
factor 0.95, align LEFT, etc.) is fixed in the factory. All three
sites collapse from ~22 lines of struct literal to a one-line
factory call.

Comment in page_cache.cpp updated to point at the factory and
reference the audit finding instead of admitting the duplication.

No behavior change — values match exactly what each site built
before.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants