Structs
Values the box reports backPlain value types you get back from queries and discovery. Their fields are public.
Version
Firmware identity and box idFirmware identity from query_version(). Display prints fw M.m.p and omits proto_ver; read it from the field.
| Field | Type | Meaning |
|---|---|---|
proto_ver | u8 | Wire-protocol version the firmware speaks (3 here). |
fw_major | u8 | Firmware major version. |
fw_minor | u8 | Firmware minor version. |
fw_patch | u8 | Firmware patch version. |
mac | [u8; 6] | The device chip's base MAC, a stable per-box id. |
name | String | The box's human-readable name (a synthesized default when unset), set via set_name. |
| Method | Returns | Meaning |
|---|---|---|
mac_hex() | String | The MAC as 12 lowercase hex digits, the id used by open_by_id. |
use medius::Version;
let v = Version { proto_ver: 3, fw_major: 3, fw_minor: 0, fw_patch: 0, mac: [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc], name: "Loki".into() };
assert_eq!(v.to_string(), "fw 3.0.0"); // Display omits proto_ver
assert_eq!(v.mac_hex(), "123456789abc");
println!("{v} (protocol {}, box {}, name {})", v.proto_ver, v.mac_hex(), v.name);Health
Box readiness flagsBox readiness from query_health(), one bool per bit. from_flags(u8) and to_flags() convert the byte.
| Field | Type | True when |
|---|---|---|
link_up | bool | The link to the host chip is up. |
mouse_attached | bool | A real mouse is plugged in. |
clone_configured | bool | The PC has set up the cloned mouse. |
injection_active | bool | The box is holding at least one injected button or move. |
rate_confident | bool | The native-rate estimator window is full, so Rate is trustworthy. |
lock_on | bool | At least one input lock is active. |
catch_on | bool | A catch subscription is streaming physical-input events. |
kbd_attached | bool | A keyboard is attached on the host chip, cloned and injectable. |
use medius::Health;
let h = Health::from_flags(0b0000_0011); // link_up | mouse_attached
assert!(h.link_up && h.mouse_attached);
assert!(!h.clone_configured);
assert_eq!(h.to_flags(), 0b0000_0011); // round-trips to the same byteDeviceInfo
The cloned device's USB identity, kind, and productUSB identity from device_info(). Every field is zero/empty when nothing is cloned. Display prints VVVV:PPPP product.
| Field | Type | Meaning |
|---|---|---|
vid | u16 | USB vendor id (idVendor). |
pid | u16 | USB product id (idProduct). |
bcd_device | u16 | Device release (bcdDevice). |
bcd_usb | u16 | USB version (bcdUSB), e.g. 0x0200. |
has_serial | bool | The clone serves a serial string. |
has_bos | bool | The clone serves a BOS descriptor. |
kind | DeviceKind | The device's primary kind, from its Boot-interface protocol. |
product | String | The product string the device serves (empty when it serves none). |
use medius::{DeviceInfo, DeviceKind};
let d = DeviceInfo {
vid: 0x046D, pid: 0xC08B, bcd_device: 0, bcd_usb: 0x0201,
has_serial: true, has_bos: true, kind: DeviceKind::Mouse, product: "G502".into(),
};
assert_eq!(d.to_string(), "046D:C08B G502"); // Display is VVVV:PPPP productCaps
The whole device, mouse and keyboardOne caps() query, returned as one struct: a MouseCaps half and a KbdCaps half, plus the per-class change-driven flags. has_mouse() / has_keyboard() tell you which are bound; is_composite() is true when the device has more than one HID interface.
| Field | Type | Meaning |
|---|---|---|
mouse | MouseCaps | The mouse half (all-zero when no mouse is bound). |
keyboard | KbdCaps | The keyboard half (all-zero when no keyboard is bound). |
mouse_change_driven | bool | Always false: mouse motion is continuous, so its Rate has a learned cadence. |
kbd_change_driven | bool | True when a keyboard is bound: it reports only on a key change, so its rate has no continuous cadence. |
let caps = device.caps()?;
if caps.has_keyboard() && caps.keyboard.has_consumer {
// media injection is real on this board
}
println!("{} mouse buttons", caps.mouse.n_buttons);MouseCaps
What the cloned mouse can doSemantic capabilities from caps(). Every field is zero when no relative-axis mouse interface is bound. is_composite() is true when n_hid > 1.
| Field | Type | Meaning |
|---|---|---|
n_buttons | u8 | Buttons the mouse report carries. |
has_x | bool | The report carries an X axis. |
has_y | bool | The report carries a Y axis. |
has_wheel | bool | The report carries a wheel. |
has_report_id | bool | The mouse report sits behind a HID report ID. |
n_hid | u8 | Cloned HID interfaces; >1 = composite. |
use medius::MouseCaps;
let c = MouseCaps { n_buttons: 5, has_x: true, has_y: true, has_wheel: true, has_report_id: false, n_hid: 1 };
assert!(!c.is_composite()); // single HID interfaceRate
The native report rate the box tracksLive rate from query_rate(). native_hz() converts the period to a frequency, returning None while native_period_us is still 0. The rate is class-aware: a change-driven input (a keyboard or media device) has no continuous cadence, so it sets change_driven and leaves native_period_us at 0, with poll_period_us the honest figure.
| Field | Type | Meaning |
|---|---|---|
native_period_us | u16 | Realised native report period in µs; 0 = not learned, or change-driven. |
poll_period_us | u16 | Cloned inject-endpoint poll period in µs. |
confident | bool | The estimator window is full and the value is trustworthy. |
change_driven | bool | The active input is event-driven (keyboard / media), so there is no continuous cadence. |
use medius::Rate;
let r = Rate { native_period_us: 1000, poll_period_us: 1000, confident: true, change_driven: false };
assert_eq!(r.native_hz(), Some(1000.0));Stats
Delivery and telemetry countersDelivery counters from query_stats(). A nonzero tx_drops or tx_wedges means delivery degraded under load. The narrowed fields saturate instead of wrapping.
| Field | Type | Meaning |
|---|---|---|
inject_emits | u32 | Pure-injection reports emitted. |
tx_drops | u16 | Reports dropped on TX-queue overflow (should stay 0). |
tx_merges | u16 | Backed-up reports merged instead of queued. |
tx_maxdepth | u8 | Deepest the TX queue has reached. |
tx_wedges | u8 | Wedged-endpoint recoveries. |
wakeups | u16 | Remote-wakeups issued. |
reset_count | u16 | USB bus resets seen. |
config_count | u16 | SET_CONFIGURATION events (re-enumerations). |
Locks
The active input locksActive locks from query_locks(), a list of LockEntry across every class, so mouse, key, and media locks read the same way. is_locked(target, dir) tests one lock; entries() is the whole list. See the native LOCKS reply for the wire format.
| Method | Returns | Meaning |
|---|---|---|
entries() | &[LockEntry] | Every active lock, one entry per locked target or whole-class blanket. |
is_locked(target, dir) | bool | Whether that target and direction is locked, by a specific entry or a covering whole-class blanket; target is any impl Into<LockTarget>. |
from_entries(Vec<LockEntry>) | Locks | Build one from entries, for tests and the MockBox. |
use medius::{Axis, Button, LockDirection};
let locks = device.query_locks()?;
if locks.is_locked(Axis::X, LockDirection::Positive) {
// the real mouse can't move right
}
if locks.is_locked(Button::Left, LockDirection::Negative) {
// a left-click is latched down: the hand can't release it
}
println!("{} locks active", locks.entries().len());LockEntry
One entry in a Locks liststruct LockEntry { scope: LockScope, positive: bool, negative: bool }One active lock in a Locks list: what is locked (its LockScope) and which edges.
| Field | Type | Meaning |
|---|---|---|
scope | LockScope | A specific axis or usage, or a whole-class blanket. |
positive | bool | The positive/press edge is locked. |
negative | bool | The negative/release edge is locked. |
CatchMask
Which physical reports raise an eventA bitflags newtype you hand to catch_events(). It gates which reports raise a CatchEvent; the payload is always the full snapshot. Combine the consts with | (BitOr), e.g. CatchMask::MOTION | CatchMask::BUTTONS.
| Const | Bit | Triggers on |
|---|---|---|
MOTION | 0x01 | The mouse moved (dx/dy). |
WHEEL | 0x02 | The wheel turned. |
BUTTONS | 0x04 | A button changed. |
KEYS | 0x08 | A keyboard key or modifier changed. |
MEDIA | 0x10 | A media (Consumer) usage changed. |
| Method | Returns | Meaning |
|---|---|---|
empty() | CatchMask | No bits set (unsubscribe). |
all() | CatchMask | Every class set (0x1F). |
bits() | u8 | The raw mask byte. |
is_empty() | bool | No bits set. |
contains(other) | bool | Every bit in other is set here. |
union(other) | CatchMask | The two masks ORed together. |
use medius::CatchMask;
let mask = CatchMask::MOTION | CatchMask::BUTTONS;
assert!(mask.contains(CatchMask::BUTTONS));
assert_eq!(mask.bits(), 0x05);
let stream = device.catch_events(mask)?;MotionEvent
One physical relative-axis eventThe payload of a CatchEvent::Motion, read off an EventStream. The real hand motion at the merge point, before lock suppression or injection, so a locked or injected axis still reports the true delta.
| Field | Type | Meaning |
|---|---|---|
dx | i16 | X movement this report (right positive). |
dy | i16 | Y movement this report (down positive). |
dz | i16 | Wheel movement this report (up positive). |
use medius::{CatchMask, CatchEvent};
let stream = device.catch_events(CatchMask::MOTION | CatchMask::WHEEL)?;
if let CatchEvent::Motion(m) = stream.recv()? {
println!("moved {} {}, wheel {}", m.dx, m.dy, m.dz);
}UsageSnapshot
One physical held-usage snapshotThe payload of a CatchEvent::Usages: every held Usage of one class (buttons, keys, or media, all one shape), captured before injection. Diff successive snapshots for press/release edges, or test one with is_held; a dropped frame self-corrects on the next.
| Field | Type | Meaning |
|---|---|---|
usages | Vec<Usage> | The currently-held usages, all of one class per event. |
| Method | Returns | Meaning |
|---|---|---|
is_held(usage) | bool | Whether usage is held; takes any impl Into<Usage>. |
class() | Option<Class> | The class of this snapshot, from its first usage, or None when empty. |
use medius::{Button, CatchMask, CatchEvent};
let stream = device.catch_events(CatchMask::BUTTONS)?;
if let CatchEvent::Usages(s) = stream.recv()? {
if s.is_held(Button::Left) {
println!("left button held");
}
}Key
A HID keyboard keycodeA newtype over a HID keyboard/keypad usage. It converts Into<Usage>, so you pass one straight to inject or press. Named consts cover the common keys (Key::A, Key::ENTER, Key::LEFT_SHIFT); build any other with Key::new(u8). Modifiers are the usages 0xE0-0xE7.
| Item | Returns | Meaning |
|---|---|---|
Key::A .. Key::LEFT_SHIFT | Key | Named consts for common keycodes and modifiers. |
new(u8) | Key | Wrap any raw HID keycode. |
usage() | u8 | The raw keycode byte. |
use medius::Key;
let a = Key::A; // 0x04
let custom = Key::new(0x04);
assert_eq!(a.usage(), custom.usage());MediaKey
A 16-bit Consumer usageA newtype over a 16-bit Consumer usage. It converts Into<Usage>, so you pass one straight to inject or press. Named consts cover the common media keys (MediaKey::VOLUME_UP, MediaKey::PLAY_PAUSE, MediaKey::MUTE); build any other with MediaKey::new(u16).
| Item | Returns | Meaning |
|---|---|---|
MediaKey::VOLUME_UP .. MediaKey::MUTE | MediaKey | Named consts for common media usages. |
new(u16) | MediaKey | Wrap any raw Consumer usage. |
usage() | u16 | The raw Consumer usage. |
use medius::MediaKey;
let vol_up = MediaKey::VOLUME_UP; // 0x00E9
let custom = MediaKey::new(0xE9);
assert_eq!(vol_up.usage(), custom.usage());KbdCaps
What the cloned keyboard can doSemantic capabilities from caps(). Every field is zero when no keyboard is bound. has_consumer gates media injection.
| Field | Type | Meaning |
|---|---|---|
n_keys | u8 | Keycode-array slots, or 0xFF for an NKRO bitmap. |
nkro | bool | The keyboard reports an NKRO bitmap. |
has_consumer | bool | A Consumer collection is present (media keys injectable). |
has_system | bool | A system-control collection is present (passthrough-only). |
has_report_id | bool | The keyboard report sits behind a HID report ID. |
CatchState
The active catch subscriptionThe current subscription from query_catch(). A nonzero dropped means the box shed events under back-pressure.
| Field | Type | Meaning |
|---|---|---|
mask | CatchMask | Which reports are subscribed; empty = none. |
dropped | u32 | Box-side events dropped under back-pressure. |
ImperfectStatus
The imperfect-clone stateThe imperfect-clone state from query_imperfect().
| Field | Type | Meaning |
|---|---|---|
allowed | bool | The opt-in toggle; cloning an over-capacity device is allowed. |
over_capacity | bool | The attached device needs an interrupt-IN endpoint the box can't service. |
clone_imperfect | bool | The live clone is over-capacity and was cloned anyway, so one interface is dead. |
EmitPaceStatus
The emit-rate pacing stateThe emit-rate pacing state from query_emit_pace().
| Field | Type | Meaning |
|---|---|---|
mode | EmitPace | The selected mode; Fixed carries the requested rate. |
resolved_hz | u16 | The ceiling in effect (Hz); 0 = learnt/adaptive, or no device yet in Interval. |
PortInfo
A discovered serial portA serial port that looks like a Medius box, from find_medius(). serial is the CH343 adapter's serial string, part of the box identity.
| Field | Type | Meaning |
|---|---|---|
path | String | Serial port path. |
vid | u16 | USB vendor id (0x1A86). |
pid | u16 | USB product id (0x55D3). |
serial | Option<String> | The CH343 adapter's serial string, when it serves one. |
CountersSnapshot
Link statistics snapshotFour running link totals from counters().
| Field | Type | Meaning |
|---|---|---|
frames_tx | u64 | Frames sent to the box. |
frames_rx | u64 | Frames received from the box. |
crc_drops | u64 | Inbound frames dropped on a bad checksum. |
reconnects | u64 | Times the library reopened the port. |
LogStream
Receiver for the device LOG streamReceives the box's LOG frames as LogLine values off a local channel, from device.logs(). Pull lines with recv / try_recv / recv_timeout / try_iter / recv_async (or for line in stream); none touch the wire, so cloning shares the queue. The methods and an example are on Logs & counters.
ClipSettings
A clip's persistent config, read backA clip's configuration from ClipHandle::query_config(): the auto-lock set, the loop and retain flags, whether it's finalized, and the bound ClipTrigger list. You set these with the handle setters (set_autolock, set_loop, set_retain, finalize, bind); this is the readback.
| Field | Type | Meaning |
|---|---|---|
autolock | Vec<Blanket> | The Blanket groups auto-locked while playing (clip-owned, released on stop); empty = no auto-lock. |
loop_ | bool | Playback restarts from the top instead of stopping at the end. |
retain | bool | The buffered content survives a stop, so a restart replays it instead of needing a fresh append. |
finalized | bool | The clip is sealed: no more appends, ready to replay as a fixed sequence. |
triggers | Vec<ClipTrigger> | The bound input triggers (up to 8), each firing a playback action on a physical edge. |
let cfg = handle.query_config()?;
if cfg.loop_ && cfg.finalized {
println!("sealed looping clip, {} triggers", cfg.triggers.len());
}ClipTrigger
One input binding that drives a clipOne physical-input binding for a clip: on a given Usage and Edge, run a ClipAction. You hand these to ClipHandle::bind; the box keeps up to 8, keyed by usage and edge. consume hides the triggering input from the PC.
Build one with ClipTrigger::new(usage, edge, action) (consume defaults false), then chain .consume() to swallow the input:
fn new(on: impl Into<Usage>, edge: Edge, action: ClipAction) -> ClipTrigger
| Field | Type | Meaning |
|---|---|---|
on | Usage | The button, key, or media usage that fires the trigger. |
edge | Edge | Which edge fires it: Press, Release, or Both. |
action | ClipAction | The playback action to run (Start, Stop, Toggle, ...). |
consume | bool | Swallow the triggering input so the PC never sees it; the .consume() builder sets it true. |
use medius::{Button, ClipAction, ClipTrigger, Edge};
// Toggle the clip on a Side1 press, and hide that press from the PC.
let trig = ClipTrigger::new(Button::Side1, Edge::Press, ClipAction::Toggle).consume();
handle.bind(trig)?;ClipStatus
The buffered-clip ring and playback stateThe clip ring depth and playback counters from ClipHandle::query_status(). Pace top-ups off free; a ClipState::Faulted state means re-sync (stop, then rebuild).
| Field | Type | Meaning |
|---|---|---|
state | ClipState | The lifecycle state (idle / playing / paused / faulted). |
free | u32 | Free bytes in the ring, the headroom for the next append. |
total | u32 | The retained clip size in bytes; while streaming, the buffered-but-undrained bytes. |
played | u32 | Bytes played from the clip start (retained progress; ~0 while streaming). |
ticks | u32 | Content frames drained since the last start (gap runs are not counted). |
underruns | u16 | Underrun episodes (the ring ran dry mid-playback). |
overruns | u16 | Appends dropped because the ring was full. |
seq_gaps | u16 | Append-sequence gaps seen (a dropped append frame). |
held | Vec<Usage> | The usages the clip is holding down, buttons, keys, and media in one list like a UsageSnapshot; test one with is_held(usage). |