Multi-device support: DeviceProfile abstraction, hotplug, contribution pipeline - #1
Merged
Conversation
Move all per-device identity, report layouts, mode-switch and touch-enable sequences into a data-driven DeviceProfile (NibCore/DeviceProfile.swift), so adding a tablet model is data rather than edits to the decode path. - PenReport/TouchReport parse from a PenLayout/TouchLayout (default .dth2420). - main.swift threads a resolved `profile` through display match, mode-switch, touch-enable, transports and report guards. - C shim gains nib_hid_enumerate() and reports each device's real VID/PID on attach/detach; Swift adds HIDTransport.enumerate + DeviceProfile.detectConnected. - profile is now chosen from connected hardware at startup, falling back to the DTH2420 reference when nothing recognized is attached. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the pen + touch transport lifecycle out of main.swift into a DeviceManager that watches every known vendor for attach/detach and, on each event, re-detects the connected profile. When it changes, it tears down and rebuilds the pen/touch pipeline for the new device — so one running instance can drive whichever supported tablet is plugged in, come up cleanly when a tablet is attached after launch, and switch models without a restart. - HIDTransport gains an onDetach callback and applyFeatures/applyOutputs helpers. - main.swift's `run` builds a DeviceManager; rebuildMappings recomputes pen + touch maps (using each profile's own logical maxes) on profile change; one-shot modes resolve once via detectProfile(). - StatusBar takes a calibrationBounds closure so recalibration uses the active tablet's display. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On replug the pen/touch HID interface re-attaches before the tablet's DisplayPort output re-registers with the window server, so the attach-time mapping fails to find the panel and falls back to the main display — the pen then tracks on the wrong screen. Register a CGDisplayReconfiguration callback that rebuilds the pen + touch mappings for the active profile once the display layout settles, so the mapping self-corrects when the panel appears. Also covers resolution and monitor-arrangement changes while the tablet stays connected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make it possible to add a tablet from a capture bundle without the maintainer owning the hardware. - `nib capture`: opens every connected interface of known vendors, sends the best-guess Wacom enable sequences, and dumps raw reports tagged by PID — one command a contributor runs while performing labeled gestures. - DeviceFixtures + ProfileConformanceTests: a data-driven registry where each device contributes its profile plus real captured gestures; the suite asserts the profile decodes them. Adding a device with fixtures adds its coverage with no per-device test code. Seeded with the DTH2420 captures (17 tests total). - docs/ADDING-A-DEVICE.md: the end-to-end workflow (capture bundle format, profile template, fixtures, on-hardware sign-off) and an honest note on what still needs the physical device (mode-switch / touch-enable recovery). - ROADMAP/README updated: device-config abstraction marked done; pointers added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Runs `swift build` (all products, incl. the AppKit app target) and `swift test` (NibCore unit tests + ProfileConformanceTests) on every push to main and every PR, so contributed device profiles get validated against their fixtures automatically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- HIDTransport: balance the start() passRetained with a release in stop() (stored as selfRef). Transports are now ephemeral (a fresh pen/touch pair per reconcile), so the previously-harmless unbalanced retain leaked one wrapper per hotplug swap. - DeviceManager.bringUp: weakly capture penT/touchT in their own onAttach closures to break the transport→closure→transport retain cycle (leaked a pen+touch wrapper on every device swap). - enableTouch: label the log by index + report ID instead of the positional "multitouch"/"configure", which only held for the DTH2420's exact sequence. - Document the intentional last-known-profile fallback when no tablet is connected. Verified: swift test (17) green; rebuilt/reloaded the signed app, still drives the DTH2420 (detection, mode-switch, touch-enable, mapping). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reframe as a driver for Wacom pen displays with the DTH2420 as the verified reference device; add a Supported devices section; note runtime profile-based detection + hotplug in How it works. Repo description updated to match. Co-Authored-By: Claude Opus 4.8 <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
Restructures Nib so supporting another tablet is data, not new decode code, and adds the tooling to bring up a device from a capture bundle without the maintainer owning the hardware. Answers "how do we support more tablets when I only have the DTH2420?"
Four self-contained commits:
DeviceProfileabstraction + attach-time enumeration — all model-specific identity, report layouts, mode-switch and touch-enable move into aDeviceProfile;PenReport/TouchReportparse from a layout; the profile is resolved from connected hardware at startup. Pure refactor, tests stay green.DeviceManager: hotplug-driven profile re-resolution — per-vendor watcher transports re-detect the connected profile on attach/detach and rebuild the pen/touch pipeline when it changes. One running instance drives whichever supported tablet is plugged in, comes up cleanly when a tablet is attached after launch, and switches models without a restart.CGDisplayReconfigurationcallback rebuilds the mappings once the display layout settles (also covers resolution/arrangement changes).nib capture(raw multi-interface dump), a data-drivenDeviceFixtures/ProfileConformanceTestsharness (a new profile with fixtures is validated against real hardware output, no per-device test code), anddocs/ADDING-A-DEVICE.mdcovering the capture-bundle format, profile template, and on-hardware sign-off.Testing
swift test— 17 tests green (14 existing + 3 conformance).Nib.app, reloaded the LaunchAgent, and verified live on the DTH2420: profile detection, mode-switch, touch-enable, hotplug re-resolution (unplug →no supported tablet connected, replug →now driving …), and the replug wrong-screen fix.nib capturesmoke-tested: enumerates and opens the pen / ExpressKey Remote / touch interfaces.Notes / follow-ups
DeviceProfilegenerator, to build once a real second device or capture bundle exists.🤖 Generated with Claude Code