fix(hid): classify direct-path devices via HID++ 0x0005 device type#147
fix(hid): classify direct-path devices via HID++ 0x0005 device type#147AprilNEA wants to merge 3 commits into
Conversation
A Bluetooth-direct / wired device probed through `probe_direct` had its kind hard-coded to `DeviceKind::Unknown` — the receiver pairing register that supplies a kind on the Bolt path doesn't exist there. The GUI's wired-keyboard lighting heuristic (`supports_lighting`, added in #29) then treats any `Unknown` + `Direct` device as a lighting-only keyboard, so a Bluetooth-direct MX Anywhere 3S / MX Master 3 lost its Buttons and Pointer tabs and showed only an irrelevant color panel (#127) — leaving no way to remap back/forward. Read the device's marketing type from HID++ feature `0x0005` (DeviceTypeAndName), folded into the existing `probe_features` session so it costs one extra short round-trip and no new device handshake. The direct path now reports the real kind (Mouse, …); the Bolt path keeps its pairing-register kind and falls back to `0x0005` only when that register reads `Unknown`. Fixes #127
Greptile SummaryThis PR fixes issue #127 where Bluetooth-direct and wired HID++ devices showed only the Lighting tab (no Buttons/Pointer) by querying HID++ feature
Confidence Score: 5/5Safe to merge — self-contained change adding one HID++ round-trip within an already-open session, fully covered by unit tests. The kind-resolution logic is internally consistent across resolve_device_kind, its doc comment, and the three new unit tests. Offline Bolt devices fall back to the register correctly. No regressions are introduced for currently-correct Bolt devices. No files require special attention; the single changed file is well-covered by the new tests. Important Files Changed
Reviews (2): Last reviewed commit: "fix(hid): make HID++ 0x0005 the authorit..." | Re-trigger Greptile |
`probe_features` queried HID++ 0x0005 for every online device, but the Bolt path only uses the result when the pairing register returned `Unknown`. For a well-behaved Bolt device that round-trip was discarded, eating into the shared 5s PROBE_BUDGET (worse with many slots / a slow-waking device). Gate the read behind a `read_device_type` flag: the direct path always asks, the Bolt path only when its register kind is `Unknown`.
…gister The reporter on #127 connects over a Logi Bolt receiver, not Bluetooth- direct — so the fix has to hold on the Bolt path too. A Bolt-routed device only shows the lighting-only UI when it is classified as a keyboard, which means the receiver's pairing register is misreporting an MX Anywhere 3S as `Keyboard`. The previous precedence (register wins, 0x0005 only fills an `Unknown`) could not correct that. Flip it: the device's own `0x0005` (DeviceTypeAndName) report is the authoritative kind for any online device; the pairing-register kind is the fallback for offline devices (no probe) or a `0x0005` type we don't model. This corrects a register that names the wrong concrete kind, not just an `Unknown` one — covering both the Bolt and the Bluetooth-direct paths in #127. The 0x0005 read is no longer conditional, but it is no longer discarded either: it is the primary source and runs only for online devices that just answered two other reads, so it is one cheap short round-trip with a real purpose (addresses the earlier review note about a wasted probe).
|
Pushed a follow-up that strengthens the fix and addresses the review note. Why the precedence flipped. #127's reporter is on a Logi Bolt receiver, not Bluetooth-direct. On the Bolt path a device only renders the lighting-only UI when it's classified as a The device's own HID++ Re: the discarded-round-trip note. The The |
Problem
On
master, a Bluetooth-direct / wired MX Anywhere 3S / MX Master 3 shows only a Lighting (color) tab — the Buttons and Pointer tabs vanish, so there's no way to remap back/forward or anything else (#127).Root cause
Two pieces interact:
inventory.rs::probe_direct(the receiver-less path: Bluetooth-direct, USB-C) hard-codeskind: DeviceKind::Unknown. There's no Bolt pairing register on this path to supply a real kind, and nothing else filled it in.app.rs::supports_lighting(added in feat: add wired G-series keyboard RGB control #29 for wired G-series keyboards) treats anyUnknown+DeviceRoute::Directdevice as a lighting-capable keyboard.So a Bluetooth-direct mouse is indistinguishable from a wired keyboard:
is_configurable_pointer(Unknown)isfalse(no Buttons/Pointer) whilesupports_lightingistrue(Lighting only). A Bolt-connected unit reportsMouse(0x02)from the pairing register and is unaffected — which is why the same model works over the Bolt receiver but breaks over Bluetooth.Fix
Read the device's marketing type from HID++ feature
0x0005(DeviceTypeAndName) and use it for the kind:probe_featuressession, so it's one extra short round-trip on an already-open device — no newDevice::new/enumerate_featureshandshake.0x0005type directly → a Bluetooth-direct mouse is nowMouse, restoring the Buttons + Pointer tabs and dropping the spurious Lighting tab.0x0005only when that register readsUnknown— so currently-correct devices are untouched.map_device_typemaps the0x0005enum to ourDeviceKind;resolve_device_kindencodes the precedence (primary wins unlessUnknown, then probe) and is unit-tested.Verification
cargo test -p openlogi-hid— 3 new precedence tests pass.cargo clippy -p openlogi-hid --all-targetsclean under-D warnings.kind == Mouse; this just needs to confirm the device answers0x0005at index0xffon the direct path.Notes / out of scope
CGEventTap(it's outside button range 0–4; seehook_runtime.rs), plus pointer drift and a scroll-direction request. If that user is on a post-feat: add wired G-series keyboard RGB control #29 build over Bluetooth this fix restores their tabs, but the DPI-button limitation is separate and not addressed here.Keyboard(0x01)rather thanUnknown— would not be covered, but there's no evidence of it.Fixes #127