Medius - Rust LibraryEnums

Enums

Command and status enumerations

Command and status enums, each tied to a wire byte. Conversion helpers are listed with each.

DeviceKind

The cloned device's primary kind
enum DeviceKind { Unknown, Keyboard, Mouse }

The kind field of a DeviceInfo, read from the cloned device's USB Boot-interface bInterfaceProtocol. It also drives find_mouse_box and find_keyboard_box. Display prints the lowercase name.

VariantByteMeaning
Unknown0Neither a Boot keyboard nor a Boot mouse.
Keyboard1The device is a keyboard.
Mouse2The device is a mouse.

Button

The button a command acts on
enum Button { Left, Right, Middle, Side1, Side2 }

The button an INJECT command acts on. A Button converts Into<Usage> as class button, so you pass one straight to inject. Convert with as_id() -> u8 and from_id(u8) -> Option<Button>.

VariantidMeaning
Left0Left button.
Right1Right button.
Middle2Middle button.
Side13First thumb button.
Side24Second thumb button.

Action

The shared press / release tri-state
enum Action { SoftRelease, Press, ForceRelease }

The shared override action for an inject call, on any Usage class (button, key, or media). The discriminant is the wire byte. Convert with as_u8() and from_u8(u8) -> Option<Action>.

VariantByteMeaning
SoftRelease0Drop the box's override, press or force; a physical hold stays down.
Press1Force the input down.
ForceRelease2Force the input up, masking a physical hold.
WHAT THE EMITTED REPORT CARRIES

The two releases differ only when the user physically holds the same input:

VariantUser holds nothingUser is holding it
Pressdowndown
SoftReleaseupdown (the physical bit stands)
ForceReleaseupup (masks physical)

Class

The class of a momentary usage
enum Class { Button, Key, Media }

The class byte of a Usage, shared by INJECT, LOCK, and CATCH. Convert with as_u8() and from_u8(u8) -> Option<Class>.

VariantByteMeaning
Button0A mouse button; id is a Button id (0=Left .. 4=Side2).
Key1A keyboard key; id is a HID keycode (0xE0 .. 0xE7 is a modifier).
Media2A media usage; id is a 16-bit Consumer usage.

CatchClass

What a catch subscription addresses
enum CatchClass { Button, Key, Media, Axis, HidIn, HidOut, VendorInterrupt, VendorBulk, Control, Emit, Bus }

The address space a CatchFilter picks from, and the class of a TrafficEvent. Convert with as_u8() and from_u8(u8) -> Option<CatchClass>; split it with is_input() and is_traffic().

The first four are LOCK's own classes at the same byte values. The other seven address USB traffic and have no lock counterpart; TrafficClass is that half on its own.

VariantByteid isBlanket covers
Button0a Button id (0 = Left .. 4 = Side2).every mouse button.
Key1a HID keycode (0xE0 .. 0xE7 is a modifier).every key and modifier.
Media2a 16-bit Consumer usage.every media usage.
Axis3an Axis: X, Y, or the wheel.every axis.
HidIn4an interface number on the real device.every HID interface.
HidOut5an endpoint address.every interrupt-OUT endpoint.
VendorInterrupt6an endpoint address.every vendor interrupt endpoint.
VendorBulk7an endpoint address.every vendor bulk endpoint.
Control8an endpoint number (0 = EP0).every control endpoint.
Emit9an endpoint address on the clone.every emitting endpoint.
Bus10unused; a bus event has no id.every bus event.
BLANKETS AND THE WILDCARD

A blanket is one table entry, not an expansion into one per id. The wire sentinels never appear in Rust: CatchFilter::watch_class(c) and traffic_class(c) are the per-class blankets, and CatchFilter::everything() is the wildcard over all eleven, not a CatchClass variant.

BEFORE AND AFTER

The input classes are captured at the emission merge point, before lock suppression and injection, so a locked input still reports. Emit is the far end of the same path: what the clone actually put on the wire.

real device --> [ merge point ] --> locks --> injection --> [ clone emits ] --> game PC
                       |                                           |
          Button / Key / Media / Axis                            Emit

The traffic classes tap the pipes themselves, each on whichever chip owns that pipe. That split is what each event's ClockDomain records.

EXAMPLE
use medius::{Button, Capture, CatchFilter, Direction, TrafficClass};

// The same target, once as a lock and once as a catch.
device.lock(Button::Side1, Direction::Both)?;                    // suppressed in the emitted report
let stream = device.catch_events([
    CatchFilter::watch(Button::Side1).on_press(),                // the tap is before suppression
])?;

// A byte-oriented class instead: one vendor interrupt endpoint, IN only, 16 bytes a packet.
let trace = device.catch_events([
    CatchFilter::traffic(TrafficClass::VendorInterrupt, 0x83)
        .inbound()
        .with_capture(Capture::First(16)),
])?;

TrafficClass

The byte-oriented half of the address space
enum TrafficClass { HidIn, HidOut, VendorInterrupt, VendorBulk, Control, Emit, Bus }

The seven classes that carry packets, at the same byte values as their CatchClass counterparts. A separate enum so CatchFilter::traffic cannot be handed an input class. TrafficClass::ALL lists them; From and TryFrom convert both ways.

Capture

How much of each packet to keep
enum Capture { Whole, First(u8) }

Traffic classes only. An input class carries no packet, so naming one together with a capture is refused rather than ignored.

A ceiling request, not a guarantee: the box holds one entry per address and cuts once, so another subscriber naming the same address more widely raises yours too. First(0) is Whole; bytes() returns Option<u8> and widest() resolves two.

Input

One decoded input edge
enum Input { Press(Usage), Release(Usage), Motion { dx: i16, dy: i16, dz: i16 } }

What input_events yields, wrapped in an InputEvent with its timestamp. usage(), is_press(), is_release(), direction() and axes() read it without a match.

Usage

A momentary input: (class, id)
struct Usage { class: Class, id: u16 }

What inject drives and LockTarget locks. A Button, Key, and MediaKey each impl Into<Usage>, so you pass one straight to any verb; build one by hand with Usage::new(class, id).

FieldTypeMeaning
classClassThe input class (button, key, or media).
idu16The class-specific id: a button id, a HID keycode, or a Consumer usage.
EXAMPLE
use medius::{Button, Class, Key, Usage};

let from_button: Usage = Button::Left.into();      // Class::Button, id 0
let from_key: Usage = Key::A.into();               // Class::Key, id 0x04
let by_hand = Usage::new(Class::Media, 0x00E9);    // volume up
device.press(from_button)?;                         // press takes any impl Into<Usage>

Motion

A relative axis for move_axis
enum Motion { Cursor { dx: i16, dy: i16 }, Wheel(i16) }

What move_axis drives. Both span the full i16 range. A lock names a single Axis instead.

VariantPayloadMeaning
Cursor{ dx: i16, dy: i16 }Relative pointer movement.
Wheeli16Relative scroll.

MoveTiming

When a delta reaches the game PC
enum MoveTiming { Ride, Now }

The move_axis timing argument, against movement riding. Defaults to Ride.

VariantByteMeaning
Ride0x00Wait for a real cursor move to carry this delta, as movement riding asks.
Now0x01Emit on the box's own clock, whatever movement riding is set to.

PendingMotion

What a move does to held motion
enum PendingMotion { Keep, Flush, Discard }

The move_axis pending argument: what happens to motion the box is already holding for a real move. Defaults to Keep.

VariantByteMeaning
Keep0x00Leave it held.
Flush0x02Emit it now, ignoring the ride window (flush_motion).
Discard0x04Drop it (discard_motion).

Axis

A single relative axis
enum Axis { X, Y, Wheel }

One relative axis. A lock_axis or a LockTarget::Axis names one, with the sign given by a Direction. Convert with as_u16().

VariantidMeaning
X0The X cursor axis.
Y1The Y cursor axis.
Wheel2The wheel.

RebootTarget

Which chip to restart, and how
enum RebootTarget { DeviceDownload, HostDownload, DeviceRun, HostRun }

Which chip a REBOOT restarts, and into what mode. Convert with as_u8() and from_u8(u8) -> Option<RebootTarget>.

VariantByteMeaning
DeviceDownload0Device chip into ROM download mode, ready to flash over the serial link.
HostDownload1Host chip into ROM download mode, ready to flash over its own USB.
DeviceRun2Restart the device chip and run its firmware.
HostRun3Restart the host chip and run its firmware.

EmitPace

What paces injected motion
enum EmitPace { Learned, Interval, Fixed(u16) }

What sets the emit-rate ceiling for injected motion, passed to set_emit_pace and returned in EmitPaceStatus. It raises the ceiling only, so idle stays idle.

VariantMeaning
LearnedPace to the mouse's learnt native report rate (the default).
IntervalPace to the cloned mouse's declared poll rate (its bInterval).
Fixed(u16)Pace to a fixed rate in Hz; snaps to 1000/n and caps at 1 kHz.

LedTarget

Which chip's status LED to drive
enum LedTarget { Device, Host, Both }

Which chip's LED a LED command drives. The discriminant is the wire target byte. Convert with as_u8() and from_u8(u8) -> Option<LedTarget>.

VariantByteMeaning
Device0The device chip's own LED.
Host1The host chip's LED, relayed over the inter-chip link.
Both2Both LEDs at once.

LedMode

What to drive the LED to
enum LedMode { Auto, Off, Solid, Blink }

What a LED command drives the LED to; Auto restores the box's status display. The discriminant is the wire mode byte. Convert with as_u8() and from_u8(u8) -> Option<LedMode>.

VariantByteMeaning
Auto0Restore the chip's own status display.
Off1LED dark.
Solid2Lit steadily at the command's level.
Blink3Blinks at the command's level.

LockTarget

What a lock acts on
enum LockTarget { Axis(Axis), Usage(Usage) }

What a LOCK command blocks. An Axis and any impl Into<Usage> each convert Into<LockTarget>, so you pass one straight to lock. A button locks exactly like a key.

VariantPayloadLocked by
AxisAxisThe sign, a Direction of positive, negative or both, or the bearing-relative With / Against.
UsageUsageThe press or release edge, a Direction.

LockScope

What a reported lock covers
enum LockScope { Target(LockTarget), Blanket(Class) }

What a LockEntry in a query_locks reply covers.

VariantPayloadCovers
TargetLockTargetA specific axis or usage.
BlanketClassEvery button, key, or media usage of the class.

Direction

Which way, which edge, or which transfer direction
enum Direction { Both, Positive, Negative, With, Against }

The one byte LOCK, CLIP and CATCH all carry. The variants are named for the axis reading; which of the three applies is decided by the class, and no class carries two. Convert with as_u8() and from_u8(u8) -> Option<Direction>.

VariantByteOn an axisOn a button or keyOn a traffic class
Both0both signs; on a scale, a full pass to the relative pairpress and releaseIN and OUT
Positive1+pressIN: device to PC
Negative2-releaseOUT: PC to device
With3the sign the box is injectingrefusedno meaning
Against4the sign opposing itrefusedno meaning

A media usage has no edges. An edge named on one goes out as Both, which is what query_locks reports it as.

With and Against are measured against the bearing rather than a fixed sign, so the sign they cover follows the injection; is_relative() tells them apart, and a lock or catch call on any class but an axis refuses one with Error::RelativeDirection. See set_bearing.

The other two readings get their own names: Direction::PRESS and RELEASE for a usage, Direction::IN and OUT for traffic. admits() tests one against another, and of_delta() reads the sign of a movement.

BearingMode

How the box reads the direction it is injecting
enum BearingMode { PerAxis, Vector }

What set_bearing chooses, and what Direction::With and Against are resolved by. Convert with as_u8() and from_u8(u8) -> Option<BearingMode>.

VariantByteMeaning
PerAxis0Each axis compares its own sign against its own bearing, independently. The default.
Vector1The physical delta is projected onto the injected XY vector, and the relative scale weighs only the part along it.

In Vector the relative pair addresses X and Y as one vector: the box takes the lower of X's and Y's scale and applies it to both axes, so address them together with scale_all. What Locks reports back is there.

The projection is the first of two stages. Each axis's Positive / Negative scale then applies to what the projection left on that axis, so it can weigh motion the projection moved there. See set_bearing.

Blanket

A whole-group lock selector
enum Blanket { Aim, Wheel, Buttons, Keys, Media }

A whole input group: which one scale_all / lock_all weigh in one call, and the members of a clip's ClipSettings auto-lock.

VariantMeaningWhat direction picks
AimThe X and Y cursor axes.A sign on each axis, or the relative pair, which is how Vector mode is addressed.
WheelThe wheel.A sign.
ButtonsEvery mouse button.An edge, on each button.
KeysEvery keyboard key and modifier.An edge: Positive blocks presses, Negative releases, Both both.
MediaEvery media (Consumer) usage.Nothing. Media has no edges.

LogLevel

Severity tag on a log line
enum LogLevel { Error, Warn, Info, Debug, Verbose }

The severity tag on a LogLine. from_u8(u8) is infallible: an unknown byte falls back to Info.

VariantByteMeaning
Error0A failure the box could not recover from.
Warn1Something off that the box handled.
Info2Normal operational notices.
Debug3Detail for diagnosing a problem.
Verbose4The finest-grained trace output.

CatchEvent

One caught event off the stream
enum CatchEvent { Motion(MotionEvent), Usages(UsageSnapshot), Traffic(TrafficEvent) }

What an EventStream yields, one variant per event frame the box pushes.

VariantPayloadRaised by
MotionMotionEventA cursor or wheel change, from a CatchClass::Axis filter.
UsagesUsageSnapshotA button, key, or media change, from a Button / Key / Media filter.
TrafficTrafficEventBytes off a pipe, from any TrafficClass filter.

All three carry ts_us and a ClockDomain: a stamp compares only to another from the same domain, until you apply the ClockEstimate.

One rolling seq counter covers all three frame types. It counts what the box sent, not what it saw, so a gap is not how you detect loss. CatchState is.

EXAMPLE
use medius::{CatchEvent, CatchFilter};

let stream = device.catch_events([CatchFilter::everything()])?;
match stream.recv()? {
    CatchEvent::Motion(m)  => println!("{} {} {}", m.dx, m.dy, m.dz),
    CatchEvent::Usages(s)  => println!("{} held", s.usages.len()),
    CatchEvent::Traffic(t) => println!("{:?} 0x{:04X}: {} bytes", t.class, t.id, t.true_len),
}

ClockDomain

Which chip stamped the event
enum ClockDomain { HostChip, DeviceChip }

The clock field beside every event's ts_us. The box runs two microsecond timers and nothing relates them; stamping happens on whichever chip saw the event. Convert with as_u8() and from_u8(u8) -> Option<ClockDomain>.

VariantByteStamped
HostChip0On the host chip, in USB interrupt context, when the real device's transfer completed.
DeviceChip1On the device chip, at the tap, when the clone's own traffic passed it.
WHICH CLASS LANDS WHERE
DomainClasses stamped there
HostChipthe input classes (raising Motion and Usages), HidIn, and the IN direction of the vendor classes.
DeviceChipHidOut, the OUT direction of both vendor classes, and Control / Emit / Bus.

Both timers are box-local, unrelated to any clock on this machine. They wrap every ~71.6 minutes (a u32 of microseconds) and restart at zero when their chip reboots.

A stamp below the one before it is a wrap, a reboot, or a domain change, and only the third is visible in the value itself. To put both domains on one timeline, apply the ClockEstimate from query_catch.

ControlStatus

How a proxied control transfer ended
enum ControlStatus { Ok, Stalled, Naked, Other(u8) }

Read it with TrafficEvent::control_status(), which returns None for any class other than Control.

VariantflagsMeaning
Ok0x00The transfer completed.
Stalled0xFDThe device STALLed the request.
Naked0xFEThe device NAKed until the transfer timed out.
Other(u8)anything elseA status byte with no variant in this build, carried verbatim.

BusEvent

What happened on the USB bus
enum BusEvent { Reset, Suspend, Resume, Configured(u8), Deconfigured,
                SetInterface { interface: u8, alt: u8 },
                DeviceAttached, DeviceDetached, CloneUp, CloneDown }

The kind of a CatchClass::Bus event. Read it with TrafficEvent::bus_event(), which returns Option<BusEvent>, which is None for a kind byte with no variant in this build. The two kinds that carry operands parse them into their own fields.

VariantBytebytesMeaning
Reset0-The game PC reset the clone's bus.
Suspend1-The bus went idle and the PC suspended the clone.
Resume2-The bus came back.
Configured3a = configuration indexThe PC selected a configuration; the clone is live.
Deconfigured4-The PC dropped the clone back to configuration 0.
SetInterface5a = interface, b = alternate settingThe PC switched an interface's alternate setting.
DeviceAttached6-The real device appeared on the host chip.
DeviceDetached7-The real device went away.
CloneUp8-The box started presenting the clone to the game PC.
CloneDown9-The box stopped presenting it.

DeviceAttached and DeviceDetached are the real device on USB3; the other eight are the clone's own USB1 bus, which the control PC is not on.

EXAMPLE
use medius::{BusEvent, CatchEvent, CatchFilter, TrafficClass};

let stream = device.catch_events([CatchFilter::traffic_class(TrafficClass::Bus)])?;
if let CatchEvent::Traffic(t) = stream.recv()? {
    match t.bus_event() {
        Some(BusEvent::Configured(n))              => println!("configuration {n}"),
        Some(BusEvent::SetInterface { interface, alt }) => println!("iface {interface} alt {alt}"),
        Some(kind)                                 => println!("{kind:?}"),
        None                                       => {}
    }
}

ClipState

The buffered-clip lifecycle state
enum ClipState { Idle, Playing, Paused, Faulted }

The device-side clip state on ClipStatus::state, from ClipHandle::query_status().

VariantByteMeaning
Idle0No clip playing (empty, or a loaded clip parked at its start).
Playing1Draining the ring, one entry per native frame.
Paused2Held mid-clip, keeping the cursor and any held input; resumes from the same spot.
Faulted3An append was dropped or the ring overflowed; recover with clear.

Edge

Which edge fires a clip trigger
enum Edge { Both, Press, Release }

Which edge of a bound usage fires a ClipTrigger: its press, its release, or either. It shares wire values with Direction.

VariantByteMeaning
Both0Fire on either edge.
Press1Fire on the press edge.
Release2Fire on the release edge.

ClipAction

What a fired clip trigger does
enum ClipAction { Start, Stop, Pause, Resume, Restart, Toggle }

What a bound ClipTrigger does to the clip when its edge fires. The discriminant doubles as the CLIP_CTRL op byte for the same action.

VariantByteMeaning
Start0Play from the ring's head, or resume a pause.
Stop1Stop playback and rewind to the head.
Pause2Hold playback mid-clip.
Resume3Continue a paused clip from where it stopped.
Restart4Rewind to the head and play from the start.
Toggle5Play if idle or paused, stop if playing.

UpdateTarget

Which chip an update op addresses
VariantWireMeans
Device0The PC-facing chip, written directly over the control port.
Host1The chip that reads the real device, relayed over the inter-chip link.

ImageState

Where a booted image is in its probation

The bootloader's own record for a slot. See rollback.

VariantWireMeans
New0Selected but not yet booted.
PendingVerify1Booted and on probation; the window rollback lives in.
Valid2Confirmed by the image itself.
Invalid3The image asked to be rolled back.
Aborted4Booted once and never confirmed.
Unknown0xFFNo entry for this slot.