Repository navigation
feat: clear the "another app" warning once the other app is gone - #20
Conversation
The bridge now combines two signals in updateContention(): foreign reply ids (trafficSeen) and an IORegistry scan of who else holds the pad. On the USB vendor interface the scan raises the warning by itself and clears it once nobody else is there; over Bluetooth it only names the apps. Scans run on panel open, Recheck, device open and after a foreign reply (at most once a second), never while off. The panel names the other apps. An empty scan clears trafficSeen only when it runs, so a reply that lands after it still raises the warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: high / medium findingsOverall the implementation follows the table in 🟠 Medium — A process that holds the vendor interface but never sends now raises a warning that nothing can dismissWhere: contendingClient = trafficSeen || !others.isEmptyProblem: On
Status: Not verified on hardware. This comes from reading the code and from how IOKit HID matching works: a manager with nil matching opens the 0xFF00 vendor interface like any other Suggested fixes (smallest first):
|
An app that opens every HID device (an input remapper, a macro or streaming tool) sits on the vendor interface without touching the lighting. Each authoritative scan listed it, so the warning could not be cleared short of quitting that app: Recheck and an off/on toggle both rescan and find it again. Recheck now records the pids its scan lists, and only other clients raise the warning. Acknowledged clients stay named, still raise it by sending, and are forgotten when a scan no longer lists them (so a relaunch raises again) or the bridge stops or starts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 3 of
design/pad-contention.md(option C, part 2). Builds on the registry scan from #19.What
Until now, once the "another app is driving this pad" warning came up, it stayed until Recheck or an off/on toggle, even after the other app had quit. Now the bridge also asks the IORegistry who else holds the pad, so the warning names the other app and clears by itself.
contendingClientis still the one flag the panel reads. It now comes from one place,updateContention(), which combines two signals:trafficSeen(a reply id we never issued, the old flag renamed) and the lastHIDClientScan:.unavailable(emulator, failed walk): the replies alone decide, as in Phase 1..authoritative(the USB vendor interface): any other client raises the warning by itself..advisory(Bluetooth, where the device also carries the keyboard): the list only names the apps; it cannot raise the warning.trafficSeen. This only happens when a scan runs, so a reply that lands after an empty scan still raises the warning.scanContention(), from both existing hooks), on Recheck (clear traffic, scan, repaint), afteropenDevice()connects, and after a foreign reply, at most once a second. Never while off.contendersis published for the panel copy: "Also driving this pad: Input — colours may fight." The old text is kept when no names are known. Recheck stays, with updated help text.README.mdandCLAUDE.mddescribe both signals and the Bluetooth caveat.Tests
BridgeContentionTestsgains 12 tests on the virtual pad. ThescanClientsanduptimeseams are stubbed. They cover every row of the table, the one-per-second limit on reply-triggered scans, naming the sender of a reply, the scan on open, Recheck, the off bridge and the unplug. The emulator's real scan is.unavailable, so the Phase 1 tests still check Phase 1 behaviour unchanged.swift build -c release: zero warningsenv -u HERDR_SOCKET_PATH swift test: 226 tests, 0 failures (2 remote live tests skipped)User checks
Not done in-session (no hardware or UI check):
🤖 Generated with Claude Code