Skip to content

reMarkable 2 support: windowed qtfb mode, arm32 fixes, one-command installer#4

Open
m4ndolore wants to merge 13 commits into
MaximeRivest:mainfrom
m4ndolore:rm2-support
Open

reMarkable 2 support: windowed qtfb mode, arm32 fixes, one-command installer#4
m4ndolore wants to merge 13 commits into
MaximeRivest:mainfrom
m4ndolore:rm2-support

Conversation

@m4ndolore

Copy link
Copy Markdown

What this adds

Support for the reMarkable 2 as a second device, tested end-to-end on rM2 hardware (OS build 20260612, xovi + AppLoad v0.5.3). The rM2 runs the windowed (AppLoad/qtfb) backend only — takeover is inherently Paper Pro (libqsgepaper).

  • rm2 cargo feature: 1404×1872 geometry, FBFMT_RM2FB, and the rM2's rotated wacom digitizer transform (Rot270 mount, inverted long axis — verified against libremarkable and on hardware).
  • arm32 fix, affects any 32-bit device (incl. rM1): evdev input_event is 16 bytes on 32-bit userspace, not 24 — the parsers now use the kernel ABI for the target.
  • arm32 fix, qtfb protocol: ServerMessage's payload union contains a size_t, so it sits at offset 8 with a u64 size on 64-bit servers but offset 4 with a u32 size on arm32. The client was reading the SHM key from the size field on rM2 (instant ENOENT). Parsing is now arch-correct for both.
  • Cross-build: build-rm2.sh produces a static armv7-musl binary via cargo-zigbuild — no SDK needed for windowed mode.
  • One-command installer (scripts/install-rm2.sh): rM2 needs no developer mode, so this goes from factory tablet to working diary — SSH key install, device check, xovi + AppLoad from official arm32 releases, xovi-tripletap persistence, riddle bundle, oracle config with provider-aware defaults, end-to-end --oracle-test. Fills the gap that remagic (Paper-Pro-only) leaves for rM2 owners.
  • Docs (docs/rm2-setup.md): full walkthrough including the rM2 SSH quirks we hit in the field (dropbear 2020.81 hangs up if an RSA host key is negotiated — pin ed25519; stale known_hosts entries force exactly that failure).
  • Feel tuning: RIDDLE_FLUSH_MS / RIDDLE_IDLE_MS env vars (windowed ink-update coalescing was the dominant tunable latency; the pause-to-commit is taste).
  • Reply placement: Tom now writes below the user's ink when there's room — in windowed mode the drink dissolve leaves e-ink ghosting that can't be scrubbed without a flash, and the reply used to land on top of it.

Honest state on rM2

Works end to end: write → drink → oracle (OpenRouter, ~2.4 s to first ink) → streamed reply → fade. Two limits are structural to windowed mode and worth knowing:

  • Ink latency is visibly above the stock notes app — every stroke rides shared memory → xochitl compositor → SWTCON. Great demo, not a place to write at length.
  • Replies render fast-waveform gray, and client waveform-mode requests appear to be ignored by the compositor path on rM2. A per-update waveform hint in the qtfb protocol (an AppLoad change) would fix this for every qtfb client; happy to raise it with asivery if there's interest.

Where we're taking it (context, not part of this PR)

We looked at two roads to production-quality writing on rM2: (A) a takeover backend on rmkit's reverse-engineered SWTCON — feasible, unclaimed, may pursue later; (B) keeping stock xochitl notes as the writing surface and summoning the oracle onto the page. We're building B in our fork for knowledge-management use. This PR is the shared foundation for either.

Testing

  • rM2 hardware: full flow, gestures, oracle streaming, window close/reopen.
  • Paper Pro: aarch64-unknown-linux-gnu windowed build verified compiling (no takeover regression; no PP hardware here to run it).

Release suggestion: publish a riddle-appload-arm32.zip alongside the aarch64 bundle — build-rm2.sh assembles exactly that layout.

Field-tested and driven by @m4ndolore.

🤖 Generated with Claude Code

m4ndolore and others added 13 commits July 6, 2026 11:39
- rm2 cargo feature: 1404x1872 geometry, FBFMT_RM2FB qtfb format
- rM2 wacom digitizer transform (Rot270 mount, inverted long axis)
- Device discovery matches rM2 input names (wacom, pt_mt/cyttsp5)
- Fix evdev parsing on 32-bit ARM: input_event is 16 bytes there, not 24
- build-rm2.sh: static armv7 musl cross-build + AppLoad bundle assembly
- Windowed AppLoad launcher (qtfb: true manifest, oracle.env sourcing)

Takeover mode remains Paper Pro-only (compile_error if combined with rm2).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
libc's ioctl request parameter is c_int on 32-bit musl but c_ulong on
64-bit glibc — cast at call sites so both build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
install-rm2.sh mirrors remagic's flow for the rM2: SSH key install,
device check, xovi + AppLoad from official arm32 releases, tripletap
persistence, riddle bundle, oracle.env prompt + end-to-end test.
Handles the legacy ssh-rsa negotiation modern OpenSSH rejects.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt both failure modes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The reply union contains a size_t, so it sits at offset 8 with a u64
size on 64-bit servers but offset 4 with a u32 size on arm32 (rM2).
Riddle was reading the SHM key from the size field and dying with
ENOENT on /dev/shm/qtfb_<garbage>. Input-event parsing had the same
shift. Verified against rm-appload src/qtfb/common.h struct defs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…RIDDLE_IDLE_MS)

Windowed-mode ink latency is dominated by update coalescing; drop the
default from 35ms to 12ms and let users trade CPU for feel. The 2.8s
pause-to-send is now tunable too — natural handwriting pauses can
exceed it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fast-waveform fades leave e-ink ghosting; the reply then wrote over the
residue. One flashing refresh at drink completion, hidden inside the
oracle wait, gives the reply clean paper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ults, PIN heads-up

- BatchMode probe so ssh-copy-id actually runs on password-only tablets
  (interactive probe previously 'succeeded' and skipped it)
- SSH connection multiplexing: the password is typed at most once
- sk-or-* keys default base/model to OpenRouter (an OpenRouter key on
  the OpenAI base 401s)
- Warn that one step restarts the tablet UI / may show the PIN screen
- README: honest windowed-latency expectations for the rM2

All field-tested on rM2 hardware by @m4ndolore.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fast-waveform strokes render gray and uneven; when the reply finishes
animating, re-push its region with the quality (UI) waveform so the
text sets to solid black in place. ~2s of queued updates during the
linger, when nothing is being drawn.

Field-tested on rM2 hardware by @m4ndolore.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The windowed qtfb path can't clear e-ink ghosting without a flashing
refresh (distracting) and ignores client waveform-mode requests (the
darkening pass was a no-op that stalled the server ~2s). Remove both;
instead place the reply below where the user wrote when there's room,
so ghost residue never sits under Tom's words.

Field-tested on rM2 hardware by @m4ndolore.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Send rule: a long flat stroke ruled under the words sends the page
  deliberately. The rule must span ~60% of the written text's width
  (with an absolute floor), is absorbed off the page, and never reaches
  the oracle. RIDDLE_IDLE_MS=0 now disables auto-send entirely, making
  the rule the only trigger.
- RIDDLE_PERSONA_FILE / persona.txt next to the binary replaces Tom
  with any system prompt.
- RIDDLE_FONT_FILE: drop any TTF/OTF next to the binary to change the
  reply hand (default remains the embedded Dancing Script).
- RIDDLE_REPLY_WIDTH: reply pen thickness (default 4); thicker ink
  reads darker on fast e-ink waveforms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@m4ndolore

Copy link
Copy Markdown
Author

UX from rM2 field testing — pushed to this branch (030e7cb), all from a couple of weeks of daily use:

  • Send rule: a long flat stroke ruled under the words — like signing off a diary entry — sends the page deliberately. It has to span ~60% of the written text's width, gets absorbed off the page, and never reaches the oracle. Setting RIDDLE_IDLE_MS=0 disables the idle auto-send so the rule is the only trigger; in practice this felt much better than waiting for the timeout to fire mid-thought.
  • RIDDLE_PERSONA_FILE (or a persona.txt next to the binary): swap Tom for any system prompt without rebuilding.
  • RIDDLE_FONT_FILE: drop any TTF/OTF next to the binary to change the reply hand; unset keeps the embedded Dancing Script.
  • RIDDLE_REPLY_WIDTH (default 4): thicker reply ink reads noticeably darker on the fast e-ink waveforms the windowed mode is stuck with.

All opt-in via env vars; defaults are unchanged except the reply pen width. Documented in oracle.env.example.

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