Context
Device discovery is a ~2s polling loop (crates/openlogi-gui/src/watchers/inventory.rs) calling openlogi_hid::enumerate. PR #147 cut the per-tick cost (concurrent probing + a per-device probe cache), but the model is still poll-and-reprobe.
Problem (inherent to polling)
- A sleeping / unresponsive device must be actively probed, hit the 5s
PROBE_BUDGET, and be retried — producing the recurring device probe timed out — skipping (asleep/unresponsive) warning and a standing per-tick cost (common when a device is dual-paired over both a Bolt dongle and Bluetooth, with one path dormant).
- Even with the cache, every tick still does cheap per-slot receiver reads and periodic re-probes; nothing reacts to actual device arrival / removal.
Proposed direction (event-driven + stateful)
- React to OS HID hot-plug (arrival / removal) and to the Bolt receiver's own HID++ connection / wake notifications (
BoltEvent::DeviceConnection, which we already drain once per tick) instead of polling.
- Probe a device once on (re)connection; keep a persistent device model; only react to pushed changes (wake, battery).
- This eliminates the periodic re-probe and the asleep-device timeout dance entirely.
Blocker
async-hid 0.4 exposes no hot-plug listener API, and a cross-platform event source (IOKit matching on macOS, udev/netlink on Linux, WM_DEVICECHANGE on Windows) is the hard part — which is why polling was chosen for simplicity (see the module comment in watchers/inventory.rs). Likely needs an async-hid upgrade or platform-specific code.
Large / epic. Tracking the architecture.
Surfaced by the code review of #147 and the surrounding discussion.
Context
Device discovery is a ~2s polling loop (
crates/openlogi-gui/src/watchers/inventory.rs) callingopenlogi_hid::enumerate. PR #147 cut the per-tick cost (concurrent probing + a per-device probe cache), but the model is still poll-and-reprobe.Problem (inherent to polling)
PROBE_BUDGET, and be retried — producing the recurringdevice probe timed out — skipping (asleep/unresponsive)warning and a standing per-tick cost (common when a device is dual-paired over both a Bolt dongle and Bluetooth, with one path dormant).Proposed direction (event-driven + stateful)
BoltEvent::DeviceConnection, which we already drain once per tick) instead of polling.Blocker
async-hid 0.4exposes no hot-plug listener API, and a cross-platform event source (IOKit matching on macOS, udev/netlink on Linux,WM_DEVICECHANGEon Windows) is the hard part — which is why polling was chosen for simplicity (see the module comment inwatchers/inventory.rs). Likely needs anasync-hidupgrade or platform-specific code.Large / epic. Tracking the architecture.
Surfaced by the code review of #147 and the surrounding discussion.