Catch
Stream the physical mouse and keyboard inputcatch_events subscribes to the user's real input and hands back an EventStream of CatchEvent snapshots, relative motion and held-usage sets, captured before any lock suppression or injection. Drop the stream to unsubscribe.
catch_events
Subscribe to the physical-input streamfn catch_events(&self, mask: CatchMask) -> Result<EventStream>
Fire-and-forget
CatchMask picks which classes of change emit an event: MOTION, WHEEL, BUTTONS,KEYS, MEDIA, combined with |, or CatchMask::all() for the full mirror. The returned EventStream receives every event; the subscribe itself sends one frame and doesn't wait for a reply.
| Parameter | Type | Description |
|---|---|---|
mask | CatchMask | Bitmask selecting which input classes emit events (see above). |
The subscription is held alive by the library's keepalive (which re-asserts it after a device-side blip) and across a reconnect; it clears like injection: on control-PC silence, a reset (which ends the stream, so its recv returns Err), or link loss. The reported input is the user's physical input; a locked or injected target still reports its real hand value here. See the native CATCH command for the wire layout.
use medius::{Device, CatchMask, CatchEvent, Button};
let device = Device::find()?;
let events = device.catch_events(CatchMask::all())?; // or MOTION | BUTTONS | KEYS | MEDIA
while let Ok(event) = events.recv() {
match event {
CatchEvent::Motion(m) => println!("dx={} dy={} dz={}", m.dx, m.dy, m.dz),
CatchEvent::Usages(u) if u.is_held(Button::Side1) => {
// the side button is held; rebind it...
}
CatchEvent::Usages(u) => println!("{} usages held", u.usages.len()),
}
}
// dropping `events` unsubscribesEventStream
Receive physical-input reportsThe handle catch_events returns. Pull CatchEvent snapshots with whichever method fits your loop; cloning shares the queue (like LogStream). When the stream and all its clones drop, the subscription ends and the box returns to passthrough.
| Method | Returns | Description |
|---|---|---|
recv() | Result<CatchEvent> | Block until the next event. |
try_recv() | Option<CatchEvent> | The next buffered event, or None (never blocks). |
recv_timeout(dur) | Option<CatchEvent> | Block up to dur; None on timeout. |
try_iter() | impl Iterator | Drain every buffered event without blocking. |
recv_async().await | Result<CatchEvent> | Await the next event (async feature), runtime-agnostic. |
dropped() | u64 | Events lost host-side because this consumer fell behind. |
The buffer is bounded and lossy: a slow consumer drops the OLDEST events, keeping the freshest input (count them with dropped()). The box's own drop count, under back-pressure on the wire, is on query_catch.
On AsyncDevice
catch_events fires, the stream awaitsAsyncDevice keeps catch_events synchronous (it just sends the subscribe and returns the stream) while the stream itself offers recv_async().await. query_catch is a future, like the other queries.
use medius::{AsyncDevice, CatchMask};
let device = AsyncDevice::open("/dev/ttyACM0")?;
let events = device.catch_events(CatchMask::BUTTONS)?; // sync, no await
let report = events.recv_async().await?; // stream awaits