Skip to content

fix(hid): handle analytics-only keyboards in host_switch_targets - #718

Open
sam-phinizy wants to merge 4 commits into
AprilNEA:masterfrom
phin-tech:fix/analytics-host-switch-race
Open

fix(hid): handle analytics-only keyboards in host_switch_targets#718
sam-phinizy wants to merge 4 commits into
AprilNEA:masterfrom
phin-tech:fix/analytics-host-switch-race

Conversation

@sam-phinizy

Copy link
Copy Markdown

Summary

  • host_switch_targets silently fails on keyboards whose Easy-Switch controls are analytics-only (divertable=false). The keyboard departs before switch_linked_hosts can probe the device, so the DeviceNotFound error propagates via ? and the target loop is never reached — the mouse never receives its setCurrentHost command.
  • When the keyboard is unreachable (already departed), the function now falls through to switch_targets_directly, which opens each target's own channel and switches it independently.

Changes

  • openlogi-hid (crates/openlogi-hid/src/session/host_switch.rs):
    • switch_linked_hosts now catches DeviceNotFound during keyboard preparation and falls through to target-only switching instead of aborting.
    • New helper switch_targets_directly opens each target's own HID++ channel and sends setCurrentHost without relying on the keyboard's channel.

Testing

Verified on MX Keys S for Mac (B37C, firmware 82.1.12) + MX Master 4 (B042) over Bolt receiver on macOS. Not runtime-tested on hardware for Linux or Windows.

OPENLOGI_LOG=debug trace before fix:

keyboard host switch failed error=HID++ protocol error: opening host-change device: DeviceNotFound route=slot 6 host=1
host switch session ended error=HID++ protocol error: opening keyboard device: DeviceNotFound

After fix:

keyboard unreachable; switching targets only error=HID++ protocol error: opening host-change device: DeviceNotFound route=slot 6 host=1

Mouse successfully followed the keyboard.

Fixes a race condition affecting all keyboards that report divertable=false analytics=true for their host switch controls (CIDs 0x00d1–0x00d3).

Keyboards whose Easy-Switch controls are analytics-only (divertable=false)
depart before switch_linked_hosts can probe the device. The previous code
prepared the keyboard's host change first, and the DeviceNotFound error
propagated via ?, skipping the target loop entirely — the mouse never
received its setCurrentHost command.

Now, when the keyboard is unreachable (already departed), the function
falls through to switch_targets_directly, which opens each target's own
channel and switches it independently.

Verified on MX Keys S for Mac (B37C) + MX Master 4 (B042) over Bolt.
@sam-phinizy
sam-phinizy requested a review from AprilNEA as a code owner August 20, 2026 14:45
@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown

Greptile Summary

The PR narrows the analytics-only keyboard fallback so ordinary HID++ timeouts no longer count as confirmed keyboard departure.

  • Propagates preparation timeouts instead of directly switching linked targets.
  • Retains target-only switching for errors that specifically indicate an unreachable keyboard.
  • Preserves host-slot validation before any linked target moves.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
crates/openlogi-hid/src/session/host_switch.rs Narrows the direct-target fallback to explicit unreachable-device failures, resolving both previously reported overbroad fallback cases.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Prepare keyboard host change] --> B{Result}
  B -->|Success| C[Switch linked targets]
  C --> D[Switch keyboard last]
  B -->|Device unreachable| E[Switch targets directly]
  B -->|Timeout or validation error| F[Propagate error without switching targets]
Loading

Reviews (4): Last reviewed commit: "fix(hid): remove TimedOut from departure..." | Re-trigger Greptile

Comment thread crates/openlogi-hid/src/session/host_switch.rs Outdated
The catch-all error branch also caught HostSlotEmpty, which would have
moved the targets to an unpaired host while the keyboard stayed put.
Add HostSwitchError::is_device_unreachable() to distinguish transport
and discovery failures (Hid, KeyboardNotFound, TimedOut, Hidpp with
DeviceNotFound) from validation failures (HostSlotEmpty,
UnsupportedKeyboard) and only fall through on the former.
Comment thread crates/openlogi-hid/src/session/host_switch.rs Outdated
@davidbudnick davidbudnick added type: bug Something is broken or behaves incorrectly platform: all Cross-platform issue labels Aug 20, 2026
sam-phinizy and others added 2 commits August 21, 2026 08:24
A timeout on a reachable-but-slow keyboard is not a departure signal.
The Hidpp("DeviceNotFound") and Hid variants already cover the real
analytics-mode departure race.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: all Cross-platform issue type: bug Something is broken or behaves incorrectly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants