<!-- Source: https://medius.k4tech.net/library/types/structs -->
# Structs

_Values the box reports back_

Plain value types you get back from queries and discovery. Their fields are public.

## Version

_Firmware identity and box id_

Firmware identity from [`query_version()`](/library/requests.md#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`](/library/options.md#set-name). |

| Method | Returns | Meaning |
| --- | --- | --- |
| `mac_hex()` | `String` | The MAC as 12 lowercase hex digits, the id used by [`open_by_id`](/library/discovery.md#open-by-id). |

#### EXAMPLE

```rust
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 flags_

Box readiness from [`query_health()`](/library/requests.md#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`](/library/types/structs.md#rate) is trustworthy. |
| `lock_on` | `bool` | At least one input [`lock`](/library/lock.md#lock) is active. |
| `catch_on` | `bool` | A [`catch`](/library/catch.md#catch-events) subscription is streaming physical-input events. |
| `kbd_attached` | `bool` | A keyboard is attached on the host chip, cloned and injectable. |

#### EXAMPLE

```rust
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 byte
```

## DeviceInfo

_The cloned device's USB identity, kind, and product_

USB identity from [`device_info()`](/library/requests.md#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`](/library/types/enums.md#device-kind) | The device's primary kind, from its Boot-interface protocol. |
| `product` | `String` | The product string the device serves (empty when it serves none). |

#### EXAMPLE

```rust
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 product
```

## Caps

_The whole device, mouse and keyboard_

One [`caps()`](/library/requests.md#caps) query, returned as one struct: a [`MouseCaps`](/library/types/structs.md#mouse-caps) half and a [`KbdCaps`](/library/types/structs.md#kbd-caps) 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`](/library/types/structs.md#mouse-caps) | The mouse half (all-zero when no mouse is bound). |
| `keyboard` | [`KbdCaps`](/library/types/structs.md#kbd-caps) | The keyboard half (all-zero when no keyboard is bound). |
| `mouse_change_driven` | `bool` | Always false: mouse motion is continuous, so its [`Rate`](/library/types/structs.md#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. |

#### EXAMPLE

```rust
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 do_

Semantic capabilities from [`caps()`](/library/requests.md#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. |

#### EXAMPLE

```rust
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 interface
```

## Rate

_The native report rate the box tracks_

Live rate from [`query_rate()`](/library/requests.md#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. |

#### EXAMPLE

```rust
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 counters_

Delivery counters from [`query_stats()`](/library/requests.md#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 locks_

Active locks from [`query_locks()`](/library/requests.md#query-locks), a list of [`LockEntry`](/library/types/structs.md#lock-entry) 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`](/native/commands/requests.md#locks) reply for the wire format.

| Method | Returns | Meaning |
| --- | --- | --- |
| `entries()` | `&[[LockEntry](/library/types/structs.md#lock-entry)]` | 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`](/library/features/mock.md). |

#### EXAMPLE

```rust
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 list_

```text
struct LockEntry { scope: LockScope, positive: bool, negative: bool }
```

One active lock in a [`Locks`](/library/types/structs.md#locks) list: what is locked (its [`LockScope`](/library/types/enums.md#lock-scope)) and which edges.

| Field | Type | Meaning |
| --- | --- | --- |
| `scope` | [`LockScope`](/library/types/enums.md#lock-scope) | 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 event_

A bitflags newtype you hand to [`catch_events()`](/library/catch.md#catch-events). It gates _which_ reports raise a [`CatchEvent`](/library/types/enums.md#catch-event); 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. |

#### EXAMPLE

```rust
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 event_

The payload of a [`CatchEvent::Motion`](/library/types/enums.md#catch-event), read off an [`EventStream`](/library/catch.md#event-stream). 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). |

#### EXAMPLE

```rust
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 snapshot_

The payload of a [`CatchEvent::Usages`](/library/types/enums.md#catch-event): every held [`Usage`](/library/types/enums.md#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](/library/types/enums.md#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](/library/types/enums.md#class)>` | The class of this snapshot, from its first usage, or `None` when empty. |

#### EXAMPLE

```rust
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 keycode_

A newtype over a HID keyboard/keypad usage. It converts `Into<[Usage](/library/types/enums.md#usage)>`, so you pass one straight to [`inject`](/library/inject.md#inject) or [`press`](/library/inject.md#inject). 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. |

#### EXAMPLE

```rust
use medius::Key;

let a = Key::A;            // 0x04
let custom = Key::new(0x04);
assert_eq!(a.usage(), custom.usage());
```

## MediaKey

_A 16-bit Consumer usage_

A newtype over a 16-bit Consumer usage. It converts `Into<[Usage](/library/types/enums.md#usage)>`, so you pass one straight to [`inject`](/library/inject.md#inject) or [`press`](/library/inject.md#inject). 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. |

#### EXAMPLE

```rust
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 do_

Semantic capabilities from [`caps()`](/library/requests.md#caps). Every field is zero when no keyboard is bound. `has_consumer` gates [media injection](/library/inject.md#inject).

| 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 subscription_

The current subscription from [`query_catch()`](/library/requests.md#query-catch). A nonzero `dropped` means the box shed events under back-pressure.

| Field | Type | Meaning |
| --- | --- | --- |
| `mask` | [`CatchMask`](/library/types/structs.md#catch-mask) | Which reports are subscribed; empty = none. |
| `dropped` | `u32` | Box-side events dropped under back-pressure. |

## ImperfectStatus

_The imperfect-clone state_

The imperfect-clone state from [`query_imperfect()`](/library/options.md#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 state_

The emit-rate pacing state from [`query_emit_pace()`](/library/options.md#query-emit-pace).

| Field | Type | Meaning |
| --- | --- | --- |
| `mode` | [`EmitPace`](/library/types/enums.md#emit-pace) | 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`. |

## LogLine

_One line from the LOG stream_

One line from the box's [`LOG`](/native/commands/admin.md#log) stream, read off a [`LogStream`](/library/types/structs.md#logstream).

| Field | Type | Meaning |
| --- | --- | --- |
| `level` | [`LogLevel`](/library/types/enums.md#log-level) | Severity tag. |
| `text` | `String` | The decoded message. |

## PortInfo

_A discovered serial port_

A serial port that looks like a Medius box, from [`find_medius()`](/library/guides/connection.md#choosing-a-port). `serial` is the CH343 adapter's serial string, part of the box [identity](/library/discovery.md).

| 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 snapshot_

Four running link totals from [`counters()`](/library/diagnostics.md#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](/native/frame.md#crc). |
| `reconnects` | `u64` | Times the library reopened the port. |

## LogStream

_Receiver for the device LOG stream_

Receives the box's [`LOG`](/native/commands/admin.md#log) frames as [`LogLine`](/library/types/structs.md#log-line) values off a local channel, from [`device.logs()`](/library/diagnostics.md#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](/library/diagnostics.md#logs).

## ClipSettings

_A clip's persistent config, read back_

A clip's configuration from [`ClipHandle::query_config()`](/library/requests.md#clip-config): the auto-lock set, the loop and retain flags, whether it's finalized, and the bound [`ClipTrigger`](/library/types/structs.md#clip-trigger) 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](/library/types/enums.md#blanket)>` | The [`Blanket`](/library/types/enums.md#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](/library/types/structs.md#clip-trigger)>` | The bound input triggers (up to 8), each firing a playback action on a physical edge. |

#### EXAMPLE

```rust
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 clip_

One physical-input binding for a clip: on a given [`Usage`](/library/types/enums.md#usage) and [`Edge`](/library/types/enums.md#edge), run a [`ClipAction`](/library/types/enums.md#clip-action). You hand these to [`ClipHandle::bind`](/library/clip.md#triggers); 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:

```text
fn new(on: impl Into<Usage>, edge: Edge, action: ClipAction) -> ClipTrigger
```

| Field | Type | Meaning |
| --- | --- | --- |
| `on` | [`Usage`](/library/types/enums.md#usage) | The button, key, or media usage that fires the trigger. |
| `edge` | [`Edge`](/library/types/enums.md#edge) | Which edge fires it: `Press`, `Release`, or `Both`. |
| `action` | [`ClipAction`](/library/types/enums.md#clip-action) | 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. |

#### EXAMPLE

```rust
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 state_

The clip ring depth and playback counters from [`ClipHandle::query_status()`](/library/requests.md#clip-status). Pace top-ups off `free`; a [`ClipState::Faulted`](/library/types/enums.md#clip-state) state means re-sync (stop, then rebuild).

| Field | Type | Meaning |
| --- | --- | --- |
| `state` | [`ClipState`](/library/types/enums.md#clip-state) | 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](/library/types/enums.md#usage)>` | The usages the clip is holding down, buttons, keys, and media in one list like a [`UsageSnapshot`](/library/types/structs.md#usage-snapshot); test one with `is_held(usage)`. |
