Add reMarkable Paper Pro support (experimental)#2
Merged
Conversation
Pure refactor — extract reMarkable 2 hardware constants into a DeviceProfile record. Behavior is unchanged on rM2; all existing tests pass without value adjustments. The pipeline now reads device-specific facts (event-struct layout, device-node paths, pen/touch axis ranges, max tracked contacts) from a profile instead of a static class. Host-target scale (Windows Ink pressure 0-1024, tilt +/-90) moves to InjectionScale because it is not device-specific. EvdevParser is parameterised by EvdevLayout: 32-bit ARM (rM2) uses the 16-byte input_event with HHi at offsets 8/10/12; 64-bit ARM uses 24-byte input_event with HHi at offsets 16/18/20. Adds regression tests for the 24-byte path so the rMPP desync signature reported in Evidlo/remarkable_mouse Issue #92 cannot land here. Phase 1 of docs/IMPLEMENTATION_PLAN_RMPP.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Constants seeded from Evidlo/remarkable_mouse's rmpro branch (Issue #92). Event layout is Bits64 (24-byte input_event) so the shared EvdevParser decodes the device's aarch64 stream correctly — the regression tests added in the previous commit guard against accidental Bits32 fallback. Items not in the public community data (tilt, distance, touch axis ranges, pen-suppression behavior) carry // TODO(rmpp-phase0) markers and must be confirmed via evtest on real hardware before v0.4.0 ships. Phase 2 of docs/IMPLEMENTATION_PLAN_RMPP.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The input_absinfo struct already had a resolution field declared in interop, but UinputOutput.SetAxis never populated it — the value was silently 0 on every axis. libinput uses this field to recognise the virtual device as a tablet on Wayland; with resolution=0 the compositor categorises us as a generic absolute-axis device, which falls back to mouse semantics rather than pen ones. The fix surfaces resolution as a parameter on SetAxis, takes ABS_X/Y values from the active DeviceProfile, and sets rM2 to 100 ticks/mm (the FreeCap23 value). rMPP uses Evidlo's 2832/2064 which it already declares in its profile. ABS_PRESSURE and ABS_TILT limits also move to InjectionScale rather than literals so the host-target scale lives in one place. Phase 3 of docs/IMPLEMENTATION_PLAN_RMPP.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DeviceDetector probes the connected reMarkable's CPU architecture via `uname -m`: armv7l maps to the rM2 profile, aarch64/arm64 maps to the Paper Pro profile. SshTransport now exposes RunCommandAsync for the one-shot probe; the streaming pipeline opens its own session afterwards as before. CLI gains --device auto|rm2|rmpp (default auto). GUI gains a Device dropdown at the top of the Connection section, persisted in AppSettings.Device. The App's StartPipeline is now a thin wrapper over StartPipelineAsync so detection can happen off-thread without blocking the UI. Unit tests cover the architecture-to-profile mapping and the short-name lookup. Live SSH detection requires real hardware and is exercised in Phase 0 bring-up rather than in CI. Phase 4 of docs/IMPLEMENTATION_PLAN_RMPP.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- README intro and Requirements call out Paper Pro alongside rM2 and the Developer Mode unlock step. - New "Paper Pro status" section enumerates what is verified vs. what still needs hardware (the TODO(rmpp-phase0) markers). - New "Hardware details — Paper Pro" mirrors the existing rM2 section with Evidlo's draft axis ranges flagged as placeholders. - `--device auto|rm2|rmpp` added to the CLI options table. - New docs/RELEASE_NOTES_v0.4.0.md drafts the GitHub release narrative; the project does not maintain a CHANGELOG file. - Feasibility and implementation-plan docs gain a Status header noting Phases 1-5 are landed and Phase 0 is the remaining gate. Phase 5 of docs/IMPLEMENTATION_PLAN_RMPP.md. No code changes in this commit; tag v0.4.0 deferred until Phase 0 hardware verification. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small fixes addressing real-world feedback that the auto-detect
path could hang for 30+ seconds and then transition silently to
"Disconnected" with no indication of what went wrong.
1. SshTransport sets ConnectionInfo.Timeout to 15s. SSH.NET's default
socket-connect timeout is ~30s, which left the user staring at
"Connecting…" through a full half-minute when the rM's USB-Ethernet
had a transient hiccup on cold-start. 15s tolerates normal handshake
variance while still feeling responsive on failure.
2. SshTransport.ConnectAsync is now a no-op when the SshClient is
already connected. This lets the auto-detect probe pass its
connected transport into TabletPipeline; the pipeline's first
ConnectAsync no longer tears down and re-establishes the session,
eliminating the duplicate handshake. Reconnect-after-EOF still
takes the full cleanup-and-rebuild route.
3. App.xaml.cs raises a new ConnectionErrorOccurred event when
StartPipelineAsync catches an exception before reaching the
streaming loop. SettingsWindow subscribes and writes the message
into the status text in red, so probe timeouts and auth failures
are no longer invisible. The Disconnected state change is fired
first so the error message lands on top of it.
Reported after manual smoke test on rM2: auto-detect timed out (now
visible in UI), manual rm2 worked (path unchanged). The Test
Connection button surfaces its own errors via the existing
SetStatus("Failed: ..." ) path and benefits from the new 15s timeout
since it builds its own SshClient with the same default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
StartPipelineAsync's catch already forwarded probe / mapper / output construction failures to ConnectionErrorOccurred so SettingsWindow could display them. Errors raised inside the pipeline's own retry loop (pipeline.Error event — typically socket-level failures during reconnect, e.g. ECONNREFUSED when the rM is asleep or the IP is wrong) went only to the app log; the UI silently transitioned to "Disconnected." with no hint of what failed. Forward those errors through ConnectionErrorOccurred too, on the dispatcher thread because the pipeline raises Error from the thread-pool. Diagnosed from a smoke test over Wi-Fi where the user hit a permanent ECONNREFUSED loop with no useful UI feedback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The docs/ directory is in .gitignore but these five files were tracked before the rule was added. Remove them from the index; the working-tree copies are untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DeviceProfileabstraction so per-model hardware facts (event-struct layout, device paths, axis ranges) live in one place;ReMarkable2ProfileandReMarkablePaperProProfileplug in.--device auto|rm2|rmpp(CLI) and a Device dropdown (GUI).autoprobesuname -mover SSH and picks the right profile; armv7l → rM2, aarch64 → rMPP.input_absinfo.resolutionis now populated on the virtual uinput pen device (rM2 = 100, rMPP = 2832/2064), so libinput-based compositors recognise it as a tablet rather than a generic absolute device.Paper Pro support is experimental — most rMPP constants are seeded from
Evidlo/remarkable_mouse'srmprobranch and have not been verified on hardware. Search the source forTODO(rmpp-phase0)for the gaps (pen tilt + distance ranges, touch axis ranges, pen-suppression assumption). Full plan + release-notes draft indocs/IMPLEMENTATION_PLAN_RMPP.mdanddocs/RELEASE_NOTES_v0.4.0.md.Test plan
dotnet buildclean — 0 warnings, 0 errors, all projects.dotnet testgreen — Core 72/72 (includes new 64-bit parser regression + 19 DeviceDetector tests), Windows 10/10.ReMarkable2Constantsreferences gone from compiled code.udevadm info --query=property --name=<device>showsID_INPUT_TABLET=1).10.11.99.1.evtest /dev/input/event2(pen) andevent3(touch); save captures.TODO(rmpp-phase0)placeholders.PenSuppressesTouch).🤖 Generated with Claude Code