Skip to content

fix(gui): choose the receiver before pairing a device - #1614

Merged
AprilNEA merged 4 commits into
AprilNEA:masterfrom
hornej:fix/pairing-receiver-selection
Oct 21, 2026
Merged

AprilNEA merged 4 commits into
AprilNEA:masterfrom
hornej:fix/pairing-receiver-selection

Conversation

@hornej

@hornej hornej commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

With both Unifying and Bolt connected, Add Device used the first enumerated receiver, so a Bolt mouse could never appear when Unifying came first. Add Device now asks which receiver to use and retains that exact choice through retries and window recreation. A refused pairing command also keeps its explanation and Retry action when the agent refreshes an empty pairing snapshot.

Changes

  • Show receivers from live inventory and distinguish matching names with an ID suffix. Cancel and Change receiver wait for cleanup acknowledgement; stale observations cannot reopen the previous flow.
  • Match the selected product/hardware ID without fallback. Preserve selected-receiver diagnostics, verify its live UID before writes, recover transient UID failures, and discard cached identity evidence after confirmed disconnects.
  • Keep local command failures distinct from agent session results. Empty snapshots preserve the local failure and selected receiver; Retry, an acknowledged cancellation, or a new agent session advances the flow.
  • Acknowledge cancellation after restoration and session-lease release, including cancellation without an active session.
  • Append ReceiverSelector::ReceiverUid while preserving existing selector encodings. Document the extension under upstream's unreleased IPC version 37, following the once-per-release protocol policy; the latest release, v0.8.14, uses version 36. Keep client and agent binaries from the same source tree.
  • Add six chooser/cancellation strings to all 24 locale catalogs and document selection and recovery.

Merged upstream master 87ef8daf after v0.8.14. The refresh retains upstream receiver APIs, notification flags, per-keyboard OS layout, pairing icons, passkey behavior, locale additions, and new device settings. This PR remains independent of #1746 and #1807.

Testing

Final tree on Windows x86_64, Rust 1.99.0, with RUSTFLAGS=-D warnings and CARGO_INCREMENTAL=0:

  • Reproduced the refusal regression before the fix: a_refused_start_keeps_its_error_and_target_after_empty_snapshots failed because the Retry action disappeared. It passes after the fix and verifies that Retry targets the same receiver.
  • cargo test --locked -p openlogi-desktop windows::add_device::tests -- --nocapture — 14 passed, including cancellation acknowledgement and receiver changes.
  • cargo fmt --all -- --check
  • cargo clippy --locked --workspace --all-targets -- -D warnings
  • cargo test --locked --workspace — 2,508 passed; two existing ignored doctests.
  • cargo doc --locked --workspace --no-deps --document-private-items --exclude openlogi-ui --exclude openlogi-desktop --exclude openlogi-overlay --exclude openlogi-agent, with RUSTDOCFLAGS=-D warnings.
  • cargo xtask ci wasm ast-grep typos — all three checks passed, none skipped.
  • cargo deny --config .cargo/deny.toml --all-features --manifest-path crates/openlogi/Cargo.toml --locked check
  • git diff --cached --check

Workspace tests cover fixed-byte IPC compatibility, receiver selection and UID recovery, inventory replay, pairing-window interactions, locale parity, desktop localization, and upstream icon/passkey behavior.

macOS validation at b6b76cbe830af4c1c3fbaa2059dcd1ea0f1b6508 on macOS 27.0.1 / arm64 / Rust 1.99.0, with warnings denied:

  • cargo xtask ci — 7 passed, 0 failed, 7 skipped. Passed rustfmt, publish closure, native full-workspace/all-target Clippy, Rust 1.98 arm64 all-target workspace check, non-GUI private-item rustdoc, macOS all-target workspace tests, and portable Wasm checks.
  • Workspace tests — 2,590 passed, 0 failed, two interactive tests ignored across 32 suites. Counts exclude extra focused and boundary runs.
  • Separate pairing-window suite: 14 passed. cargo xtask ci i18n wire passed catalog parity/localization and all 20 fixed-byte wire tests.
  • cargo build --locked -p openlogi-desktop -p openlogi-agent -p openlogi-overlay -p openlogi — passed; all four outputs verified as Mach-O arm64 debug binaries. These builds were not installed; installation and distribution packaging/signing were not validated.
  • Skipped in the Mac run because tools, host, or target were unavailable: typos, ast-grep, shellcheck/shfmt, Linux tests, Windows tests, cargo-deny, and the Windows Clippy proxy. The separate Windows results above remain applicable.

Linux builds and physical KVM, Bluetooth/Bolt handoffs, pairing/passkeys, sleep/wake, modifier recovery, and native-window screenshot qualification remain unperformed on this exact head. Hardware qualification remains: select between mixed and same-family receivers, authenticate the intended device, reopen/retry the flow, wait for cancellation cleanup, and verify disconnect or selected-receiver failure never pairs on another receiver.

The screenshot below is an earlier Windows preview with synthetic inventory, not this refreshed head.

Native receiver chooser with synthetic inventory

@hornej
hornej requested a review from AprilNEA as a code owner September 28, 2026 16:55
@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable defects were found.

Summary

Add Device now asks users to choose a receiver and keeps that choice through retries and window recreation.

  • Add Device lets people choose the receiver for a pairing session.
  • Cancel waits for the agent to finish the pairing session.
  • Refused pairing starts keep their explanation and Retry action.

No new actionable issues were found. The supplied previous threads were rechecked without reposting them; none had a supplied Comment number. No builds, tests, or native-window captures were run during this review.

Diagram

sequenceDiagram
    participant User
    participant Desktop
    participant Agent
    participant Receiver
    User->>Desktop: Choose receiver
    Desktop->>Agent: Start with product ID and UID
    Agent->>Receiver: Read and match live UID
    Agent-->>Desktop: Pairing progress
    User->>Desktop: Cancel or change receiver
    Desktop->>Agent: Cancel pairing
    Agent->>Receiver: Stop pairing and restore state
    Agent->>Agent: Release receiver lease
    Agent-->>Desktop: Cancellation acknowledged
    Desktop-->>User: Show receiver chooser
Loading

Reviews (8) · Last reviewed commit: "fix(i18n): use an ellipsis in the pairin..." · Reviewed by Greptile

Comment thread crates/openlogi-device/src/pairing.rs Resolved
Comment thread crates/openlogi-device/src/pairing.rs Resolved
Comment thread crates/openlogi-device/src/pairing.rs Resolved
@davidbudnick davidbudnick added area: gui Graphical user interface platform: all Cross-platform issue type: bug Something is broken or behaves incorrectly labels Oct 9, 2026
@hornej
hornej requested a review from davidbudnick as a code owner October 10, 2026 21:26
hornej and others added 4 commits October 21, 2026 07:15
With both Unifying and Bolt receivers connected, Add Device used the
first enumerated receiver, so a Bolt device could never pair when the
Unifying receiver enumerated first. Add Device now asks which receiver
to use and keeps that exact choice through retries and window
recreation.

- Append `ReceiverSelector::ReceiverUid { product_id, uid }`. It matches
  the selected product ID and receiver UID and never falls back to
  another receiver.
- Keep selected-receiver open diagnostics through a runtime receiver
  identity cache shared by the inventory and pairing.
- Wait for terminal cleanup before acknowledging `cancel_pairing`.
- Keep a refused pairing command visible across empty pairing snapshots.
- Add the chooser strings to every locale catalog and document the flow.

Squashed from the contributor's ten commits on
`fix/pairing-receiver-selection` before rebasing onto master.
Two Unifying receivers share one product ID, so only the unique ID tells
them apart. Cover choosing the second one: only its channel stays open,
and the first sees nothing beyond its identity read. An ID that matches
neither receiver fails the session with `ReceiverNotFound` and writes
nothing past the identity reads.
`list_pairing_receivers` read the unique ID only for Bolt and returned
`None` for every Unifying receiver, although `read_receiver_uid` already
reads the Unifying serial number. A caller could then not build a
`ReceiverSelector::ReceiverUid` for a Unifying receiver from the listing.
Read the ID for both families, so each listed ID is one a selection can
name.
`pairing.cancelling` was the only catalog value ending in three periods;
every other in-progress string uses `…`. The non-English catalogs carry
the English fill-in for this new key, so they change with it.
@AprilNEA
AprilNEA force-pushed the fix/pairing-receiver-selection branch from b6b76cb to 39008c6 Compare October 21, 2026 07:40
@AprilNEA
AprilNEA merged commit 3077459 into AprilNEA:master Oct 21, 2026
25 checks passed
@aprilnea aprilnea Bot mentioned this pull request Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: gui Graphical user interface 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.

3 participants