Skip to content

Support the reMarkable Paper Pro Move by detecting geometry at runtime#23

Open
z3knayr0 wants to merge 3 commits into
MaximeRivest:mainfrom
z3knayr0:add-paper-pro-move-support
Open

Support the reMarkable Paper Pro Move by detecting geometry at runtime#23
z3knayr0 wants to merge 3 commits into
MaximeRivest:mainfrom
z3knayr0:add-paper-pro-move-support

Conversation

@z3knayr0

@z3knayr0 z3knayr0 commented Jul 11, 2026

Copy link
Copy Markdown

Summary

Riddle hardcoded the Paper Pro's ("ferrari") panel resolution and pen digitizer range as compile-time constants throughout the codebase. The Paper Pro Move ("chiappa") has a different panel and digitizer range, so the app misrendered/mismapped pen input on that device.

Rather than adding a second hardcoded constant set (which just duplicates the problem for any future device), both values are now detected at runtime:

  • fb.rs: SCREEN_W/SCREEN_Hscreen_w()/screen_h() backed by a OnceLock, set once via fb::init_screen() right after the display backend reports its real surface dimensions.
  • pen.rs: DIGI_MAX_X/DIGI_MAX_Y are now read per-device via EVIOCGABS on the marker input fd at PenDevice::open() time, with the Paper Pro's known values kept only as a fallback if the ioctl ever fails.
  • display.rs: added a comment noting the qtfb (windowed AppLoad) path is Ferrari-only (its wire format is documented as Paper-Pro-native) and isn't exercised by the shipped app anyway ("qtfb": false in the manifest).

Also included: reply pagination

Testing long replies on the Move's shorter 1696px page surfaced a pre-existing bug unrelated to device support: replies started a third of the way down the page (centered on the first streamed chunk's height, not the full reply's) and anything past the page bottom was silently dropped ("reply reached the page bottom; trailing text dropped") — still recorded in memory, but never shown, with no way to scroll back to it.

Fixed with proper pagination:

  • Replies now start at a small fixed top margin instead of that centering heuristic.
  • plan_reply only lays out as many lines as fit before the page bottom and hands back the rest as leftover text.
  • When a page finishes inking with leftover text pending, the diary parks (PageFull) rather than auto-advancing — fading and writing the rest before it's been read would be pointless. Touching the pen to the page (the same gesture that already early-dismisses Lingering) dissolves it and continues writing from the top (TurningPage), while still absorbing further streamed text in the background so it's ready the moment the page turns.
  • A freshly-turned page is itself capped to what fits, so backlog re-paginates correctly no matter how much piled up while parked.

Review feedback addressed

Codex flagged that screen_w()/screen_h() panic until fb::init_screen() runs (only called from run()), which broke 4 existing tests that reach screen geometry without going through run(). Fixed by adding fb::test_init_screen() and calling it at the top of each affected test. cargo test now passes: 26 passed, 0 failed.

Device info (Paper Pro Move / "chiappa")

  • Codename: chiappa, SoC: i.MX93, hostname imx93-chiappa
  • OS: Codex Linux 5.7.121, image version 3.27.1.0
  • Panel: 954x1696 marketed resolution (264 PPI); actual aux framebuffer is 960x1696 (32px-aligned)
  • Digitizer ("Elan marker input"): ABS_X max 6760, ABS_Y max 11960
  • SDK used: remarkable-production-image-5.7.119-chiappa-public-aarch64-toolchain.sh (v3.27.0.97)

Testing

Built with the official chiappa SDK against libqsgepaper.so pulled directly from the device, deployed, and ran a live takeover session on real hardware. Confirmed via device logs:

quill: aux framebuffer 960x1696 format=4 bpl=3840
riddle: display quill/takeover (960x1696 stride 3840)
riddle: pen digitizer range 6760x11960
riddle: oracle ready

Also confirmed end-to-end with real oracle round-trips (handwritten input -> OpenRouter -> handwritten reply) in normal use via AppLoad, including a long reply that exercised the new pagination (page filled, parked, touch-turned, continued writing).

Existing Paper Pro behavior is unchanged -- screen_w()/screen_h() and the digitizer read resolve to the same 1620x2160 / 11180x15340 values as before, just discovered at runtime instead of compiled in.

Riddle hardcoded the Paper Pro's ("ferrari") panel resolution and pen
digitizer range as compile-time constants throughout the codebase.
The Paper Pro Move ("chiappa") has a different panel (960x1696 aux
framebuffer vs. 1620x2160) and a different digitizer range, so the
app misrendered/mismapped pen input on that device.

Instead of adding a second hardcoded set of constants (which would
require detecting the device model and duplicate the problem for any
future panel size), this makes both values runtime-detected:

- fb.rs: SCREEN_W/SCREEN_H become screen_w()/screen_h() backed by a
  OnceLock, set once via fb::init_screen() right after the display
  backend reports its real surface dimensions (works for both the
  takeover/quill and windowed/qtfb paths).
- pen.rs: DIGI_MAX_X/DIGI_MAX_Y are now read per-device via
  EVIOCGABS on the marker input fd at PenDevice::open() time, with
  the Paper Pro's known values kept only as a fallback if the ioctl
  ever fails.

The qtfb (windowed AppLoad) path is left as Ferrari-only — its wire
format (FBFMT_RMPP_RGB565) is documented as Paper-Pro-native, and
riddle ships with "qtfb": false in its manifest, so that path isn't
exercised by the shipped app either way.

Tested on real hardware: built with the official chiappa SDK
(v3.27.0.97, matching device OS 3.27.1.0) against libqsgepaper.so
pulled from the device, deployed, and ran a live takeover session
that confirmed:
  quill: aux framebuffer 960x1696 format=4 bpl=3840
  riddle: display quill/takeover (960x1696 stride 3840)
  riddle: pen digitizer range 6760x11960
and produced a working oracle reply end-to-end.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d83180331

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread riddle/src/fb.rs
Comment on lines +15 to +16
pub fn screen_w() -> usize {
SCREEN_DIMS.get().expect("init_screen not called before screen_w").0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Initialize geometry before using screen helpers in tests

When unit tests or other non-run() paths call these helpers, SCREEN_DIMS has never been set because fb::init_screen() is only called after Display::open() in run(). Existing tests such as help::modal_renders_and_restores/sleep_page_renders_and_restores call screen_w() directly, and the ink tests reach it through BBox::add, so the suite now panics before exercising those cases; add a test/default initialization path or avoid requiring global display initialization for these helpers.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added fb::test_init_screen() (idempotent OnceLock::set, safe under parallel test execution) and called it at the top of the four affected tests: help::tests::modal_renders_and_restores, help::tests::sleep_page_renders_and_restores, and the two ink::tests that reach BBox::add via Ink::pen_point. cargo test now passes: 26 passed; 0 failed. See fc75f1f.

z3knayr0 added 2 commits July 11, 2026 13:33
Two related problems, both found testing long replies on the Move's
shorter 1696px page:

1. A fresh reply started at ((screen_h - total_h) / 3), centered on
   the *first streamed chunk's* height -- not the full reply's, which
   isn't known yet. Short first chunks (the common case, since models
   stream a few words at a time) pushed the start point a third of
   the way down the page, wasting the space above and leaving less
   room below for what turned out to be a long reply. Replies now
   start at a small fixed top margin instead.

2. Once a reply reached the page-bottom margin, the rest of the
   streamed text was silently dropped ("reply reached the page
   bottom; trailing text dropped") -- still recorded in memory, but
   never shown on screen and unrecoverable since there's no scrolling
   on this hardware.

plan_reply now only lays out as many lines as fit before the page
bottom and hands back whatever didn't as leftover text. When a page
finishes inking with leftover pending, the diary parks (`PageFull`)
rather than auto-advancing -- fading and writing the rest before it's
been read would be pointless. Touching the pen to the page (the same
gesture that already early-dismisses `Lingering`) dissolves the page
and continues writing the rest from the top (`TurningPage`), while
still absorbing further streamed text in the background so it's
ready the moment the page turns. A freshly-turned page is itself
capped to what fits, so accumulated backlog re-paginates correctly
no matter how much piled up while parked.
screen_w()/screen_h() panic until fb::init_screen() runs, which only
happens in run() after Display::open(). Tests that reach them
directly (help::tests::modal_renders_and_restores,
help::tests::sleep_page_renders_and_restores) or transitively via
BBox::add (ink::tests::erase_forgets_covered_points_and_splits_strokes,
ink::tests::erasing_everything_empties_the_ink) never go through
run(), so the suite panicked before exercising those cases.

Adds fb::test_init_screen() (any real dimensions work for a test;
uses the Paper Pro's since that's what these tests were originally
written against) and calls it at the top of each affected test.
OnceLock::set is idempotent, so this is safe under parallel test
execution.

`cargo test` now passes: 26 passed; 0 failed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc75f1ffba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread riddle/src/main.rs
Comment on lines +453 to +454
let (plan, pending) = plan_reply(&font, &oracle_excuse("no oracle"), Some(y));
State::Replying { plan, next: Instant::now(), rx: None, pending }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include retained ink in no-oracle pagination

When the no-oracle message is paginated (for example, on the 960x1696 Move after the user writes near the bottom), this branch deliberately leaves the user's handwriting on the page but only carries the reply plan forward. The later page-turn path dissolves plan.region only, so tapping for the remaining error text leaves the original handwriting underneath the next page; include user_ink.bbox in the paginated region here or avoid paginating this "leave the writing" path.

Useful? React with 👍 / 👎.

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.

1 participant