Feat/linked host switching - #479
Conversation
- add keyboard-initiated host-switch session handling - switch configured pointing devices before the keyboard leaves the host - resolve configured host-switch targets through the agent inventory - keep sessions synchronized with device availability changes - add TOML configuration and tests for host-switch targets
- add shared runtime storage for resolved host-switch links - rebuild links when configuration or device inventory changes - require the initiating keyboard to be online - preserve routes for sleeping linked target devices - ignore missing targets and empty link configurations - add coverage for online keyboard and sleeping target behavior
Handle poisoned DPI cycle locks explicitly in the online-state test so the workspace passes Clippy with unwrap_used denied.
| .get_ctrl_id_info(index) | ||
| .await | ||
| .map_err(hidpp_error)?; |
There was a problem hiding this comment.
Partial arming leaks diversion
When a later HID++ query or reporting write fails after an earlier host control was armed, the error exits run_host_switch_session before restore_host_controls runs, leaving that host key diverted and its native Easy-Switch behavior disabled until the keyboard reconnects or resets.
Knowledge Base Used: openlogi-hid: device inventory, pairing, and HID++ feature control
| shared.thumbwheel_sensitivity.clone(), | ||
| shared.receiver_access.clone(), | ||
| ); | ||
| watchers::host_switch::spawn(shared.host_switch_links.clone()); |
There was a problem hiding this comment.
Receiver access bypasses arbitration
When a configured Bolt or Unifying receiver is already used by gesture capture or pairing, this watcher opens another channel without participating in ReceiverAccess, causing HID++ responses or unsolicited events to reach the wrong channel and breaking host switching, capture, or pairing.
Knowledge Base Used:
| { | ||
| debug!(%error, route = %keyboard, "host switch session ended"); | ||
| } | ||
| let _ = done.send(()); |
There was a problem hiding this comment.
Stale completions cancel replacements
When a link change stops existing sessions and starts replacements, the stopped tasks later send indistinguishable () completions; the receive branch then stops the replacement sessions and clears their state, repeatedly leaving host keys unmonitored between re-arm attempts.
Knowledge Base Used: openlogi-agent-core
Greptile SummaryAdds configured keyboard-to-pointing-device Easy-Switch synchronization and repairs capture routing across online-state transitions.
Confidence Score: 2/5This PR should not merge until host-control cleanup, receiver-channel arbitration, and stale session-completion handling are corrected. Partial setup failures can strand native keyboard controls in diverted mode, host-switch sessions can conflict with pairing or capture on the same receiver, and stale completion notifications can repeatedly cancel newly armed links. Files Needing Attention: crates/openlogi-hid/src/host_switch.rs, crates/openlogi-agent-core/src/watchers/host_switch.rs, crates/openlogi-agent/src/main.rs
|
| Filename | Overview |
|---|---|
| crates/openlogi-hid/src/host_switch.rs | Implements HID++ host-switch sessions, but partial arming failures can leave keyboard controls diverted and channel opens bypass receiver arbitration. |
| crates/openlogi-agent-core/src/watchers/host_switch.rs | Maintains host-switch sessions, but untagged stale completion messages can cancel replacement sessions. |
| crates/openlogi-agent-core/src/orchestrator.rs | Resolves configured links and synchronizes capture targets with device online state while retaining DPI-cycle position. |
| crates/openlogi-core/src/config/device.rs | Adds backward-compatible TOML persistence for physical host-switch target keys. |
| crates/openlogi-agent/src/main.rs | Starts the new watcher without the receiver-access dependency required to coordinate receiver HID channels. |
Sequence Diagram
sequenceDiagram
participant Keyboard
participant Watcher as Host-switch watcher
participant HID as HID++ session
participant Targets as Linked pointing devices
Watcher->>HID: Arm keyboard host controls
Keyboard->>HID: Host-channel key event
loop Each configured target
HID->>Targets: Set corresponding host
end
HID->>Keyboard: Set corresponding host last
HID-->>Watcher: Session completed
Watcher->>HID: Re-arm when keyboard returns
Reviews (1): Last reviewed commit: "test(agent): avoid unwraps in capture li..." | Re-trigger Greptile
Summary
Add keyboard-initiated Easy-Switch synchronization for linked Logitech pointing devices.
When an Easy-Switch key is pressed on a compatible keyboard, OpenLogi switches every configured pointing device to the corresponding host channel before switching the keyboard. This allows the
configured devices to move together between hosts.
Implementing this feature also exposed a HID++ capture lifecycle issue: a device returning after an Easy-Switch transition retained its route but did not restart capture. The capture target now
follows the device's online state without resetting its DPI-cycle position.
Changes
openlogi-corehost_switch_targetsto the per-device TOML configuration.openlogi-hidopenlogi-agent-coreDocumentation
host_switch_targetsand provide a TOML configuration example.Testing
Automated verification:
devenv tasks run openlogi:checkOpenLogi hardware testing was performed on Linux using the OpenLogi 0.6.22 RPM package with:
Both combinations were tested with the new host-switch synchronization functionality.
Cross-agent behavior was additionally checked using the official Logi Options+ 2.5 client:
These checks confirmed that the devices can switch between corresponding host channels regardless of which agent manages them. OpenLogi itself was not runtime-tested on Windows or macOS as part
of this change.
Known limitations