Sizing constantsFixed-cap arrays sized to the wire limits

Event, lock and log PODs embed arrays sized to the protocol limits, with a count of live slots. char arrays are NUL-terminated.

MacroValueCaps
MEDIUS_MAX_USAGES256MediusUsageEvent.usages, MediusClipStatus.held
MEDIUS_MAX_LOCKS256MediusLocks.entries
MEDIUS_MAX_PATH512MediusPortInfo.path
MEDIUS_MAX_LOG_TEXT512MediusLogLine.text
MEDIUS_MAX_PRODUCT128MediusDeviceInfo.product
MEDIUS_MAX_SERIAL128MediusPortInfo.serial
MEDIUS_MAX_NAME33MediusVersion.name
MEDIUS_CLIP_TRIG_MAX8MediusClipSettings.triggers
MEDIUS_CLIP_PKT_TRIG_MAX8MediusClipSettings.packet_triggers
MEDIUS_CLIP_PKT_MATCH_POOL112Match bytes the box holds across every MediusClipPacketTrigger
MEDIUS_REWRITE_PAYLOAD_POOL2048Payload bytes the box holds across every MediusRewriteRule
MEDIUS_MAX_PKT_MATCH16MediusClipPacketTrigger.match_bytes and .mask
MEDIUS_CLIP_EDGES_MAX8Edges in one MediusClipFrame
MEDIUS_CLIP_RAW_MAX8Raw reports in one MediusClipFrame
MEDIUS_CLIP_ENTRY_MAX512Encoded bytes of one MediusClipFrame: one CLIP_APPEND payload
MEDIUS_MAX_CATCH_ENTRIES32MediusCatchState.entries
MEDIUS_MAX_TRAFFIC_BYTES180MediusTrafficEvent.bytes

MEDIUS_MAX_CATCH_ENTRIES is the box's subscription table size, so a MediusCatchState always carries the whole live table.

MEDIUS_MAX_TRAFFIC_BYTES is the largest payload one traffic event carries, so the inline array never truncates. A short len means your capture cut the packet.

Enumsuint8_t-backed selectors (MediusStatus is int32_t)

Each value is a wire byte; meanings are on Enums. The *Kind enums and MediusClass tag which arm is set in a built value (MediusUsage, MediusMotion, MediusCatchEvent).

MediusDeviceKindCloned device's primary kind

enum MediusDeviceKind : uint8_t

The kind field of a MediusDeviceInfo, from the cloned device's HID report descriptors and Boot declarations. A device carrying both kinds reads keyboard if it has a full key bitmap (unless it declares Boot for both), or if it declares a Boot keyboard and no Boot mouse. Otherwise it reads mouse. medius_device_find_mouse_box / _find_keyboard_box select on this field. See DeviceKind.

EnumeratorValueMeaning
MEDIUS_DEVICE_KIND_UNKNOWN0Nothing cloned, or a clone that is neither a mouse nor a keyboard (a device with no HID interface reads this). medius_device_info_is_cloned tells them apart.
MEDIUS_DEVICE_KIND_KEYBOARD1The real device is a keyboard.
MEDIUS_DEVICE_KIND_MOUSE2The real device is a mouse.

MediusButtonMouse button id

enum MediusButton : uint8_t   /* values match the firmware button id */

The button an inject call drives. Ids on Usage IDs.

EnumeratorValueMeaning
MEDIUS_BUTTON_LEFT0Left button.
MEDIUS_BUTTON_RIGHT1Right button.
MEDIUS_BUTTON_MIDDLE2Middle button.
MEDIUS_BUTTON_SIDE13First thumb button.
MEDIUS_BUTTON_SIDE24Second thumb button.

MediusAxisRelative axis id

enum MediusAxis : uint8_t   /* values match the wire axis id */

The axis a medius_catch_filter_watch_axis subscribes to: the same ids a catch or lock entry carries on the wire.

EnumeratorValueMeaning
MEDIUS_AXIS_X0Horizontal cursor movement.
MEDIUS_AXIS_Y1Vertical cursor movement.
MEDIUS_AXIS_WHEEL2The scroll wheel.
MEDIUS_AXIS_PAN3AC Pan (horizontal scroll).

MediusActionPress / release tri-state

enum MediusAction : uint8_t

The override action of inject calls on a button, key, or media usage. See the injection model.

EnumeratorValueMeaning
MEDIUS_ACTION_SOFT_RELEASE0Drop the box's press or force release. A hold on the real device stays down.
MEDIUS_ACTION_PRESS1Force the input down.
MEDIUS_ACTION_FORCE_RELEASE2Force the input up, masking a hold on the real device.

MediusClassWhich arm of a MediusUsage is set

enum MediusClass : uint8_t

The kind tag of a MediusUsage you build with medius_usage_button/_key/_media.

EnumeratorValueMeaning
MEDIUS_CLASS_BUTTON0id is a mouse button id.
MEDIUS_CLASS_KEY1id is a HID keyboard usage.
MEDIUS_CLASS_MEDIA2id is a 16-bit Consumer usage.

MediusMotionKindWhich arm of a MediusMotion is set

enum MediusMotionKind : uint8_t

Tags the MediusMotion you build with medius_motion_cursor/_wheel/_pan. See Move.

EnumeratorValueMeaning
MEDIUS_MOTION_KIND_CURSOR0dx/dy apply.
MEDIUS_MOTION_KIND_WHEEL1wheel applies.
MEDIUS_MOTION_KIND_PAN2pan applies (AC Pan, horizontal scroll).

MediusLockTargetKindWhich input a MediusLockTarget addresses

enum MediusLockTargetKind : uint8_t

The kind of a MediusLockTarget. See Lock.

EnumeratorValueMeaning
MEDIUS_LOCK_TARGET_KIND_X0Horizontal movement.
MEDIUS_LOCK_TARGET_KIND_Y1Vertical movement.
MEDIUS_LOCK_TARGET_KIND_WHEEL2Scroll wheel.
MEDIUS_LOCK_TARGET_KIND_PAN3AC Pan (horizontal scroll).
MEDIUS_LOCK_TARGET_KIND_USAGE4A momentary usage (the struct's usage field selects which).

MediusDirectionAxis sign, usage edge, or transfer direction

enum MediusDirection : uint8_t

Its meaning depends on what it is attached to. The transfer direction applies to a MediusCatchFilter naming a byte-oriented catch class. See LOCK and Catch.

EnumeratorValueAxis or wheelButton or keyTraffic-class filter
MEDIUS_DIRECTION_BOTH0Both signs. A scale sets the two fixed signs and passes WITH / AGAINST in full.Press and release.Both directions.
MEDIUS_DIRECTION_POSITIVE1Positive (+).The press edge.IN, device to PC.
MEDIUS_DIRECTION_NEGATIVE2Negative (-).The release edge.OUT, PC to device.
MEDIUS_DIRECTION_WITH3The sign the box is injecting.Refused.No meaning.
MEDIUS_DIRECTION_AGAINST4The sign opposing it.Refused.No meaning.

Only an axis has a bearing, so WITH or AGAINST on a lock of anything else is MEDIUS_STATUS_ERR_RELATIVE_DIRECTION. A media usage has no edges: an edge named on one is sent as MEDIUS_DIRECTION_BOTH, and reads back that way in MediusLocks.

UNNAMED DIRECTION BYTES

Direction parameters and struct fields are declared uint8_t, so a value no enumerator names can be passed. The library handles it at the boundary; it never reaches the wire.

CallComes back as
Any call returning a MediusStatusMEDIUS_STATUS_ERR_INVALID_ARG; no frame is sent.
medius_locks_scale_ofIt names no entry, so MEDIUS_LOCK_SCALE_PASS.
medius_locks_is_lockedUnlocked, for the same reason.
medius_catch_filter_with_directionStored, then refused at subscribe time, where there is a status to carry it.

MediusEdgeWhich edge of a trigger usage fires a clip binding

enum MediusEdge : uint8_t

The edge of a MediusClipTrigger's on usage that runs its action. Same wire values as MediusDirection. See Clip.

EnumeratorValueMeaning
MEDIUS_EDGE_BOTH0Both press and release.
MEDIUS_EDGE_PRESS1The press edge only.
MEDIUS_EDGE_RELEASE2The release edge only.

MediusClipActionEngine action a clip trigger drives

enum MediusClipAction : uint8_t

What a bound MediusClipTrigger or MediusClipPacketTrigger does to the clip when it fires. The verbs match the medius_clip_start/_stop/... calls. See Clip.

EnumeratorValueMeaning
MEDIUS_CLIP_ACTION_START0Rewind and play (resume from a pause).
MEDIUS_CLIP_ACTION_STOP1Stop and release held input and the auto-lock.
MEDIUS_CLIP_ACTION_PAUSE2Halt mid-clip, retaining the cursor and held input.
MEDIUS_CLIP_ACTION_RESUME3Continue from the paused cursor.
MEDIUS_CLIP_ACTION_RESTART4Force a rewind and play, even mid-playback.
MEDIUS_CLIP_ACTION_TOGGLE5Play if idle/paused, stop if playing.

MediusBlanketWhole-group lock selector

enum MediusBlanket : uint8_t

A whole input group: what medius_device_lock_all/_unlock_all block in one call, and the scope medius_clip_set_autolock locks while a clip plays. See Lock.

The values are ABI-local ordinals (matching the crate's Blanket order), not the CLIP_LOCK_* wire bits.

EnumeratorValueMeaningDirection
MEDIUS_BLANKET_AIM0The X and Y cursor axes.A sign, on each axis.
MEDIUS_BLANKET_WHEEL1The wheel.A sign.
MEDIUS_BLANKET_BUTTONS2Every mouse button.An edge, on each button.
MEDIUS_BLANKET_KEYS3Every keyboard key and modifier.An edge: POSITIVE blocks presses, NEGATIVE releases, BOTH both.
MEDIUS_BLANKET_MEDIA4Every media (Consumer) usage.Nothing. Media has no edges.

MediusLedTargetWhich chip's status LED to drive

enum MediusLedTarget : uint8_t

See LED.

EnumeratorValueMeaning
MEDIUS_LED_TARGET_DEVICE0The device chip's LED.
MEDIUS_LED_TARGET_HOST1The host chip's LED, relayed over the inter-chip link.
MEDIUS_LED_TARGET_BOTH2Both LEDs at once.

MediusLedModeWhat to drive the LED to

enum MediusLedMode : uint8_t

See LED. Solid / Blink use the command's level.

EnumeratorValueMeaning
MEDIUS_LED_MODE_AUTO0Restore the chip's status display.
MEDIUS_LED_MODE_OFF1LED dark.
MEDIUS_LED_MODE_SOLID2Lit steadily at level.
MEDIUS_LED_MODE_BLINK3Blinks at level.

MediusRebootTargetWhich chip to restart, and how

enum MediusRebootTarget : uint8_t

See Admin.

EnumeratorValueMeaning
MEDIUS_REBOOT_TARGET_DEVICE_DOWNLOAD0Device chip into ROM download mode (flash over the serial link).
MEDIUS_REBOOT_TARGET_HOST_DOWNLOAD1Host chip into ROM download mode (flash over USB3).
MEDIUS_REBOOT_TARGET_DEVICE_RUN2Restart the device chip and run its firmware.
MEDIUS_REBOOT_TARGET_HOST_RUN3Restart the host chip and run its firmware.

MediusEmitModeWhat paces injected motion

enum MediusEmitMode : uint8_t

See Options.

EnumeratorValueMeaning
MEDIUS_EMIT_MODE_LEARNED0Pace to the learnt native report rate (the default).
MEDIUS_EMIT_MODE_INTERVAL1Pace to the clone's declared poll rate (its bInterval).
MEDIUS_EMIT_MODE_FIXED2Pace to a fixed rate in Hz (snapped to 1000/n, capped 1 kHz).

MediusRenderModeTexture the box renders motion with

enum MediusRenderMode : uint8_t

Passed to medius_device_set_render and returned in MediusRenderStatus. See Options.

EnumeratorValueMeaning
MEDIUS_RENDER_MODE_OFF0Even fill at the paced rate, no model.
MEDIUS_RENDER_MODE_STOCK1Render with the bit-exact triangular smoother.
MEDIUS_RENDER_MODE_DESPIKED2Render with the smoother's onset ramped rather than stepped. The boot default.
MEDIUS_RENDER_MODE_UNSMOOTHED3Render with no smoother; the model receives the raw injection.

MediusCatchEventKindWhich arm of a MediusCatchEvent is set

enum MediusCatchEventKind : uint8_t

Which member of the MediusCatchEvent union to read. The last column lists the MediusCatchClass values that raise each kind. See Catch.

EnumeratorValueReadRaised by
MEDIUS_CATCH_EVENT_KIND_MOTION0data.motionAXIS
MEDIUS_CATCH_EVENT_KIND_USAGES1data.usagesBUTTON, KEY, MEDIA
MEDIUS_CATCH_EVENT_KIND_TRAFFIC2data.trafficHID_IN, HID_OUT, VENDOR_INTERRUPT, VENDOR_BULK, CONTROL, EMIT, BUS, CLIP_TRANSFER

MediusClockDomainWhich chip's clock stamped an event

enum MediusClockDomain : uint8_t

The clock field of a MediusCatchEvent, beside ts_us. Where an event is tapped fixes its domain.

The box is two ESP32-S3s (a device chip and a host chip) that boot independently. A stamp only compares with stamps from the same domain.

EnumeratorValueStampedCarries
MEDIUS_CLOCK_DOMAIN_HOST_CHIP0In USB interrupt context on the host chip, when the real device's transfer completed.Motion and usage events, HID_IN, and native IN traffic on VENDOR_INTERRUPT / VENDOR_BULK.
MEDIUS_CLOCK_DOMAIN_DEVICE_CHIP1At the tap on the device chip, the side facing the game PC.HID_OUT, OUT traffic on VENDOR_INTERRUPT / VENDOR_BULK, a raw packet on a vendor IN endpoint, CONTROL, EMIT, BUS, and CLIP_TRANSFER.

Each clock is a box-local uint32_t of microseconds. It wraps every ~71.6 minutes and returns to zero when its chip reboots. A stamp below the previous one is a wrap, a reboot, or a domain change.

A MediusTimeline puts both on this machine's clock. The clock estimate in MediusCatchState relates the two box domains directly.

MediusInputKindWhich arm of a MediusInputEvent is set

enum MediusInputKind : uint8_t

Tags a MediusInputEvent off the decoded-input stream. The box sends held-usage snapshots; the stream diffs them into these edges.

EnumeratorValueRead
MEDIUS_INPUT_KIND_PRESS0usage: a momentary usage went down.
MEDIUS_INPUT_KIND_RELEASE1usage: a momentary usage came up.
MEDIUS_INPUT_KIND_MOTION2dx / dy / dz: one relative-motion report.

MediusLogLevelSeverity tag on a log line

enum MediusLogLevel : uint8_t

The severity of a MediusLogLine. See Logs & counters.

EnumeratorValueMeaning
MEDIUS_LOG_LEVEL_ERROR0A failure the box couldn't recover from.
MEDIUS_LOG_LEVEL_WARN1Something off that the box handled.
MEDIUS_LOG_LEVEL_INFO2Normal operational notices.
MEDIUS_LOG_LEVEL_DEBUG3Detail for diagnosing a problem.
MEDIUS_LOG_LEVEL_VERBOSE4The finest-grained trace output.

MediusCatchClassWhat a catch filter addresses

typedef uint8_t MediusCatchClass;   /* one MEDIUS_CATCH_CLASS_* value */

The class half of a MediusCatchFilter, and the class_ field of both MediusTrafficEvent and MediusCatchEntry.

Classes 0 to 3 are the same vocabulary Lock uses; the rest address USB interfaces, endpoints and the bus, and arrive as byte-oriented traffic. See Catch.

EnumeratorValueid addressesWith MEDIUS_CATCH_ID_ANY
MEDIUS_CATCH_CLASS_BTN0A mouse button id.Every button.
MEDIUS_CATCH_CLASS_KEY1A HID keyboard usage (modifiers are 0xE0 to 0xE7).Every key and modifier.
MEDIUS_CATCH_CLASS_MEDIA2A 16-bit Consumer usage.Every media usage.
MEDIUS_CATCH_CLASS_AXIS3A MediusAxis: X, Y, the wheel, or AC Pan.Every axis.
MEDIUS_CATCH_CLASS_HID_IN4A cloned HID interface number.Every HID interface.
MEDIUS_CATCH_CLASS_HID_OUT5An interrupt-OUT endpoint number.Every interrupt-OUT endpoint.
MEDIUS_CATCH_CLASS_VENDOR_INTERRUPT6A vendor interrupt endpoint number.Every vendor interrupt endpoint.
MEDIUS_CATCH_CLASS_VENDOR_BULK7A vendor bulk endpoint number.Every vendor bulk endpoint.
MEDIUS_CATCH_CLASS_CONTROL8A control endpoint number (0 is EP0; on EP0, the requests the clone relays to the real device).Every control endpoint.
MEDIUS_CATCH_CLASS_EMIT9An emitting endpoint number.Every emitting endpoint.
MEDIUS_CATCH_CLASS_BUS10Nothing; pass MEDIUS_CATCH_ID_ANY.Every bus event.
MEDIUS_CATCH_CLASS_CLIP_TRANSFER11The control endpoint number (0 is EP0) a clip's transfer ran on.Every control endpoint.
MEDIUS_CATCH_CLASS_ANY0xFFNothing; must be MEDIUS_CATCH_ID_ANY.Every class at once.

Test one with medius_catch_class_is_input (the four parsed classes, which arrive decoded and carry no packet) or medius_catch_class_is_traffic (the eight byte-oriented ones).

Where each class is tapped, and why a locked input still reports, is on Catch.

MediusCatchFilterOne subscription entry: class, id, direction, capture

struct MediusCatchFilter {
    MediusCatchClass class_;      /* MEDIUS_CATCH_CLASS_*                   */
    uint16_t         id;          /* class-specific, or MEDIUS_CATCH_ID_ANY */
    uint8_t          direction;   /* MEDIUS_DIRECTION_*: edge, sign, or flow */
    uint8_t          capture;     /* 0 = whole packet; traffic classes only */
};

The array passed to medius_device_catch_events or medius_device_input_events. Build each with a medius_catch_filter_* helper and narrow it with a modifier.

Each element becomes one entry in the box's table; read the accepted set back with medius_device_query_catch. The field is class_ because class is a C++ keyword.

FieldC typeMeaning
class_MediusCatchClassWhich address space this entry subscribes in.
iduint16_tThe id inside that class, or MEDIUS_CATCH_ID_ANY for every id in it.
directionuint8_t, a MEDIUS_DIRECTION_* valueFor an input class, the press/release edge, as for a lock. For a traffic class, the transfer direction: POSITIVE is IN (device to PC), NEGATIVE is OUT (PC to device). No class is both, so the byte is unambiguous.
captureuint8_tBytes kept per event; 0 keeps the whole packet. Traffic classes only: an input class carries no packet, so a non-zero capture on one is refused with MEDIUS_STATUS_ERR_CAPTURE_NOT_APPLICABLE.
SENTINELS AND CAPACITY
MacroValueMeans
MEDIUS_CATCH_ID_ANY65535Every id in the class, as one table entry rather than one per id, like a blanket lock.
MEDIUS_CATCH_CLASS_ANY255Every class. A real id beside it addresses nothing and is refused.
MEDIUS_MAX_CATCH_ENTRIES32Entries the box's table holds; more fails the call with MEDIUS_STATUS_ERR_CATCH_TABLE_FULL.

capture is per entry, not box-wide: keep a 64-byte vendor interrupt report whole, and only 16 bytes of a bulk pipe traced for framing.

The top-ranked matching entry's capture applies, so "everything at 16 bytes, except endpoint 0x83 in full" is two entries. Ranking: The table.

  a report arrives on VENDOR_INTERRUPT endpoint 0x83
          │
          ├─ exact   { class_ = VENDOR_INTERRUPT, id = 0x0083 }  ──▶ resolves first
          ├─ blanket { class_ = VENDOR_INTERRUPT, id = ID_ANY }  ──▶ used if no exact entry
          └─ any     { class_ = ANY,              id = ID_ANY }  ──▶ used if neither matched
                     ties inside one tier go to the earlier entry in your array
EXAMPLE
/* every class and id, first 16 bytes only */
MediusCatchFilter blanket =
    medius_catch_filter_with_capture(medius_catch_filter_everything(), 16);

/* except this endpoint's IN traffic, kept whole */
MediusCatchFilter whole = medius_catch_filter_inbound(
    medius_catch_filter_traffic(MEDIUS_CATCH_CLASS_VENDOR_INTERRUPT, 0x83));

MediusCatchFilter filters[2] = { blanket, whole };
MediusEventStream *events = NULL;
medius_device_catch_events(dev, filters, 2, &events);

A malformed entry fails the whole call with a distinct MediusStatus. medius_catch_filter_same_address tests whether two filters name the same table entry, whatever their captures.

MediusKeyHID keyboard/keypad usage

typedef uint8_t MediusKey;   /* modifiers are 0xE0 to 0xE7 */

A raw HID keyboard usage for the key calls: any usage byte, or a MEDIUS_KEY_* macro. Full set: Usage IDs.

Macro groupExampleUsage
LettersMEDIUS_KEY_A .. MEDIUS_KEY_Z4 to 29
DigitsMEDIUS_KEY_1 .. MEDIUS_KEY_030 to 39
FunctionMEDIUS_KEY_F1 .. MEDIUS_KEY_F1258 to 69
Editing / navMEDIUS_KEY_ENTER, _ESCAPE, _TAB, _SPACE, _INSERT, _HOME, _DELETE, arrowsvarious
ModifiersMEDIUS_KEY_LEFT_CTRL .. MEDIUS_KEY_RIGHT_GUI224 to 231 (0xE0 to 0xE7)

MediusMediaKey16-bit HID Consumer usage

typedef uint16_t MediusMediaKey;

A raw Consumer usage for the media calls: any 16-bit usage, or a MEDIUS_MEDIA_* macro. Full set: Usage IDs.

MacroUsageMacroUsage
MEDIUS_MEDIA_PLAY_PAUSE205MEDIUS_MEDIA_MUTE226
MEDIUS_MEDIA_NEXT_TRACK181MEDIUS_MEDIA_VOLUME_UP233
MEDIUS_MEDIA_PREV_TRACK182MEDIUS_MEDIA_VOLUME_DOWN234
MEDIUS_MEDIA_STOP183MEDIUS_MEDIA_PLAY176
MEDIUS_MEDIA_PAUSE177

MediusFrameTypeWire frame TYPE byte (mock only)

enum MediusFrameType : uint8_t   /* always defined; read only by the mock recorder */

The TYPE byte of a wire frame, used with the mock recorder (medius_mock_saw / medius_mock_recorded_frame). See the mock feature.

EnumeratorValueEnumeratorValue
MEDIUS_FRAME_TYPE_MOVE1MEDIUS_FRAME_TYPE_LOCK10
MEDIUS_FRAME_TYPE_INJECT3MEDIUS_FRAME_TYPE_CATCH11
MEDIUS_FRAME_TYPE_RESET4MEDIUS_FRAME_TYPE_MOTION_EVENT12
MEDIUS_FRAME_TYPE_QUERY5MEDIUS_FRAME_TYPE_USAGE_EVENT15
MEDIUS_FRAME_TYPE_RESP6MEDIUS_FRAME_TYPE_OPTION17
MEDIUS_FRAME_TYPE_REBOOT_DL7MEDIUS_FRAME_TYPE_CLIP_APPEND18
MEDIUS_FRAME_TYPE_LOG8MEDIUS_FRAME_TYPE_CLIP_CTRL19
MEDIUS_FRAME_TYPE_LED9MEDIUS_FRAME_TYPE_CLIP_SET20
MEDIUS_FRAME_TYPE_TRAFFIC_EVENT22MEDIUS_FRAME_TYPE_CLIP_TRIGGER21

Argument structsTagged values you build, then pass in

Three PODs built with a helper and passed to a call; the medius_*_* constructors set the kind tag and the matching field.

MediusUsageMomentary usage for inject

What medius_device_inject drives. Build with medius_usage_button(...), _key(...), or _media(...); id holds the button id or usage per kind. See Inject.

FieldC typeMeaning
kindMediusClassWhich class id names.
iduint16_tButton id, key usage, or media usage.

MediusMoveTimingWhen a move is emitted

enum MediusMoveTiming : uint8_t

The timing argument of medius_device_move_axis: follow movement riding or bypass it. See Move.

EnumeratorValueMeaning
MEDIUS_MOVE_TIMING_RIDE0Follow movement riding: while it is on, wait for a native cursor move to carry the delta.
MEDIUS_MOVE_TIMING_NOW1Emit on the next mouse report, native or not, whatever movement riding is set to.

MediusPendingMotionWhat a move does to held motion

enum MediusPendingMotion : uint8_t

The pending argument of medius_device_move_axis. See Move.

EnumeratorValueMeaning
MEDIUS_PENDING_MOTION_KEEP0Leave motion held by riding as it is.
MEDIUS_PENDING_MOTION_FLUSH1Emit held motion now, ignoring the ride window.
MEDIUS_PENDING_MOTION_DISCARD2Drop held motion.

MediusMotionRelative axis for move_axis

What medius_device_move_axis drives. Build with medius_motion_cursor(dx, dy), medius_motion_wheel(delta), or medius_motion_pan(delta). See Move.

FieldC typeMeaning
kindMediusMotionKindCursor, wheel, or pan.
dxint16_tX movement (Cursor only).
dyint16_tY movement (Cursor only).
wheelint16_tScroll delta (Wheel only).
panint16_tAC Pan delta (Pan only).

MediusLockTargetWhat a lock acts on

Passed to medius_device_lock / _unlock. Build it with medius_lock_target_axis or medius_lock_target_usage; usage is read only when kind is USAGE. See Lock.

FieldC typeMeaning
kindMediusLockTargetKindX, Y, Wheel, Pan, or Usage.
usageMediusUsageThe button, key, or media usage, when kind == USAGE.

Query valuesPODs written through a query's out-param

Each medius_device_query_* / _caps / _counters call fills one of these by value. Canonical field docs are on Structs; query semantics on Requests.

MediusVersionDecoded firmware version and box name

From medius_device_query_version. Set the box's name with medius_device_set_name.

FieldC typeMeaning
proto_veruint8_tWire-protocol version the firmware speaks.
fw_majoruint8_tFirmware major version.
fw_minoruint8_tFirmware minor version.
fw_patchuint8_tFirmware patch version.
macuint8_t[6]The device chip's base MAC, a stable per-box id.
namechar[MEDIUS_MAX_NAME]The box's human-readable name (NUL-terminated; a synthesised default when unset).

MediusHealthBox readiness flags (each 0 or 1)

From medius_device_query_health.

FieldC typeTrue (1) when
link_upuint8_tThe link to the host chip is up.
mouse_attacheduint8_tA real mouse is plugged in.
clone_configureduint8_tThe PC has set up the cloned mouse.
injection_activeuint8_tAt least one injected button or move is held.
rate_confidentuint8_tA mouse is bound and the native-rate estimator window is full.
lock_onuint8_tAt least one input is not at a full pass (blocked or weighed).
catch_onuint8_tA catch subscription is streaming.
kbd_attacheduint8_tA keyboard is attached, cloned, and injectable.
rewrite_onuint8_tThe rewrite-rule table is non-empty (v3.4.0).
patch_onuint8_tA descriptor-patch set is applied to the clone (v3.4.0).
transform_onuint8_tA field transform is active (v3.4.0).

MediusDeviceInfoCloned device's USB identity, kind, and product

From medius_device_device_info. All-zero/empty when nothing is cloned, which medius_device_info_is_cloned tests. A clone with no HID interface (n_hid 0) still has its identity filled in. product is NUL-terminated ASCII, with ? for each non-ASCII character.

FieldC typeMeaning
viduint16_tUSB vendor id (idVendor).
piduint16_tUSB product id (idProduct).
bcd_deviceuint16_tDevice release (bcdDevice).
bcd_usbuint16_tUSB version (bcdUSB), e.g. 0x0200.
has_serialuint8_tThe clone serves a serial string.
has_bosuint8_tThe clone serves a BOS descriptor.
kindMediusDeviceKindThe real device's primary kind, from its HID report descriptors.
productchar[MEDIUS_MAX_PRODUCT]The product string (NUL-terminated; empty when none).

MediusCapsWhole cloned device's capabilities

From medius_device_caps: a mouse half and a keyboard half plus the per-class change-driven flags. Test it with medius_caps_has_mouse, medius_caps_has_keyboard, medius_caps_is_composite.

FieldC typeMeaning
mouseMediusMouseCapsThe mouse half (all-zero when no mouse is bound).
keyboardMediusKbdCapsThe keyboard half (all-zero when no keyboard is bound).
mouse_change_drivenuint8_tAlways 0: mouse motion is continuous, so it has a learned cadence.
kbd_change_drivenuint8_t1 when a keyboard is bound: it reports only on a key change.

MediusMouseCapsWhat the cloned mouse can do

The mouse half of MediusCaps; all-zero when no mouse interface is bound.

FieldC typeMeaning
n_buttonsuint8_tButtons the mouse report carries.
has_xuint8_tThe report carries an X axis.
has_yuint8_tThe report carries a Y axis.
has_wheeluint8_tThe report carries a wheel.
panuint8_tThe report carries AC Pan (horizontal scroll).
has_report_iduint8_tThe mouse report sits behind a HID report ID.
n_hiduint8_tCloned HID interfaces; >1 = composite.

MediusKbdCapsWhat the cloned keyboard can do

The keyboard half of MediusCaps; all-zero when no keyboard is bound. n_keys == 0xFF signals an NKRO bitmap.

FieldC typeMeaning
n_keysuint8_tKeycode-array slots, or 0xFF for an NKRO bitmap.
nkrouint8_tThe keyboard reports an NKRO bitmap.
has_consumeruint8_tA Consumer collection is present (media injectable).
has_systemuint8_tA system-control collection is present (relayed only).
has_report_iduint8_tThe keyboard report sits behind a HID report ID.

MediusRateNative report rate and clone poll period

From medius_device_query_rate. Convert to Hz with medius_rate_native_hz(rate, &hz) (returns false when there's no continuous cadence).

FieldC typeMeaning
native_period_usuint16_tRealised native period in µs; 0 = not learned, or change-driven.
poll_period_usuint16_tPoll period of the clone's inject endpoint, in µs.
confidentuint8_tThe estimator window is full and the value is trustworthy.
change_drivenuint8_tThe active input is event-driven (keyboard/media), so there is no continuous cadence.

MediusChipFirmwareWhat one chip is running

One chip's half of medius_device_firmware_info.

FieldC typeMeaning
major, minor, patchuint8_tThe firmware version this chip is running.
slotuint8_tWhich app slot it booted: 0 or 1.
stateuint8_tImage state: 0 new, 1 pending-verify, 2 valid, 3 invalid, 4 aborted, 0xFF unknown. See rollback.

MediusFirmwareInfoBoth chips, and what is staged

From medius_device_firmware_info.

FieldC typeMeaning
deviceMediusChipFirmwareThe PC-facing chip.
host_presentuint8_t0 when the host chip has not replied over the inter-chip link; host is then meaningless.
hostMediusChipFirmwareThe chip that reads the real device.
slot_sizeuint32_tUsable bytes in a spare slot; the same on both chips.
device_stageduint8_tAn image is written and waiting to be activated.
host_stageduint8_tThe same, for the host chip.

MediusUpdateErrorUpdate op the box refused, and why

From medius_last_error_update after MEDIUS_STATUS_ERR_UPDATE. A confirm that timed out gives op 0 and status 0x1B, the refusal the next update would meet.

FieldC typeMeaning
opuint8_t0 begin, 1 data, 2 end, 3 abort, 4 activate.
statusuint8_tThe update status, such as 0x1B for a chip on probation.
arguint32_tThe status argument; what it holds depends on the status.

MediusStatsBox-side delivery / telemetry counters

From medius_device_query_stats. Nonzero tx_drops or tx_wedges: input was lost between the box and the game PC. Nonzero link_rx_drops or host_rx_drops: input was lost between the box's two chips. relay_drops counts relayed traffic and commands that went no further, never lost native input.

FieldC typeMeaning
inject_emitsuint32_tPure-injection reports emitted.
tx_dropsuint16_tReports the clone's IN queue could not hold, so they were never emitted to the game PC (should stay 0).
tx_mergesuint16_tBacked-up reports merged instead of queued.
tx_maxdepthuint8_tDeepest the TX queue has reached.
tx_wedgesuint8_tWedged-endpoint recoveries.
wakeupsuint16_tRemote wakeups the clone signalled to the game PC, for the real device or an injection.
reset_countuint16_tUSB bus resets seen.
config_countuint16_tSET_CONFIGURATION events (re-enumerations).
link_rx_dropsuint32_tInput frames the device chip could not take off the link from the host chip (should stay 0).
host_rx_dropsuint32_tThe same count on the host chip, relayed over the link (should stay 0).
relay_dropsuint32_tRelayed traffic and commands that went no further, never native input. One count each for: a vendor IN packet whose endpoint queue was full of the control PC's RAW packets; a native zero-length packet answering a poll the PC never made (the box runs one poll ahead of a suspended PC); an OUT packet with no room in the queue, that failed three times on the real device's bus, or that a bus reset or SET_INTERFACE overtook; a RAW OUT packet with no room to wait; a relayed request the box could not queue; a flagged MOVE the motion batch could not take; a frame other than native input that either chip's link receive ring dropped; a control-PC frame its receive ring had no room for. A control-port UART overrun counts once, however many frames it cost.
sessionuint16_tTimes the box released some or all of the session state a host set. 0 at boot; it wraps, so compare for inequality. The library watches it for session recovery.

MediusLocks & MediusLockEntryActive scales, as an entry list

From medius_device_query_locks: entries[0..n], one per weighed direction. Read one with medius_locks_scale_of(&locks, target, dir), or test for an outright block with medius_locks_is_locked. Both take a covering whole-class (is_blanket) entry into account. Wire layout on the native LOCKS reply.

Asked with MEDIUS_DIRECTION_BOTHReports
medius_locks_scale_ofThe scale of smallest magnitude across every direction, WITH and AGAINST included (a negative one on a tie). A block outranks a reversal of any size; a pass outranks a reversal past -100. A delta meets a different figure: its fixed-sign scale times its relative scale.
medius_locks_is_lockedThe two fixed signs only. Name _WITH or _AGAINST to ask about one of those.
FieldC typeMeaning
nuint16_tLive entries in entries.
entriesMediusLockEntry[MEDIUS_MAX_LOCKS]One per weighed direction of an axis or usage.
SCALE CONSTANTS
MacroValueMeaning
MEDIUS_LOCK_SCALE_BLOCK0Keep none of the native value.
MEDIUS_LOCK_SCALE_PASS100Keep all of it, untouched.
MEDIUS_LOCK_SCALE_MAX2552.55×, the ceiling.
MEDIUS_LOCK_SCALE_MIN-2552.55× reversed, the floor. A negative reverses what it keeps (-100 inverts an axis); axes only.
MEDIUSLOCKENTRY
FieldC typeMeaning
targetMediusLockTargetThe weighed axis or usage.
is_blanketboolThe entry covers a whole class; target.usage.kind names it and target.usage.id is unused.
directionuint8_t, a MEDIUS_DIRECTION_* valueWhich direction of the target this entry weighs.
scaleint16_tPercent of the native value kept; 0 is blocked and a negative reverses what it keeps. A momentary usage is one bit, so the box stores a block or a pass: never a value between, never negative.
READBACK
CaseContents
A blanket key lockOne entry per blocked edge, never MEDIUS_DIRECTION_BOTH.
A media lock, blanket or specificMEDIUS_DIRECTION_BOTH, always.
A relative direction under MEDIUS_BEARING_MODE_VECTORThe effective scale, the lower of X's and Y's, on both axes.
The wire capOne reply carries ≤85 entries, fewer than MEDIUS_MAX_LOCKS. Entries past 85 are left out, and nothing marks the cut. See the native LOCKS budget.
A direction byte no constant namesThe entry is dropped and n excludes it.

MediusBearing & MediusBearingModeWhat WITH and AGAINST are measured against

struct MediusBearing {
    uint16_t          window_ms;  /* 0 = off */
    MediusBearingMode mode;
};

From medius_device_query_bearing, set with medius_device_set_bearing. See the native bearing.

FieldC typeMeaning
window_msuint16_tHow long an axis holds the direction of its last injected delta. 0 is off, leaving WITH and AGAINST inert whatever their scale.
modeMediusBearingModeHow the bearing is read; see below.
MEDIUSBEARINGMODE
EnumeratorValueMeaning
MEDIUS_BEARING_MODE_PER_AXIS0Each axis compares its sign against its bearing, independently. The default.
MEDIUS_BEARING_MODE_VECTOR1The native delta is projected onto the injected XY vector. The relative scale weighs only the part along that vector; the fixed-sign scales still apply to what is left on each axis. One relative scale (the lower of X's and Y's) governs X and Y as one vector, and MediusLocks reports it.
CONSTANT
MacroValueMeaning
MEDIUS_BEARING_WINDOW_DEFAULT_MS20The factory window; a box that has been set boots at its stored value.

MediusCatchState & MediusCatchEntryLive subscription table, plus the inter-chip clock estimate

From medius_device_query_catch: a header, then entries[0..n], one per accepted MediusCatchFilter. The inline cap, MEDIUS_MAX_CATCH_ENTRIES (32), is the box's table size, so the reply always carries the whole table.

Entries are in acceptance order, not match order; each event is matched by the ranking.

FieldC typeMeaning
table_fulluint8_t1 when an entry was refused because the table was already full.
droppeduint32_tEvents shed under back-pressure, box-wide.
clockMediusClockEstimateThe measured difference between the two chips' clocks (below).
nuint16_tLive entries in entries; 0 means nothing is subscribed.
entriesMediusCatchEntry[MEDIUS_MAX_CATCH_ENTRIES]One per accepted filter, in insertion order.
MEDIUSCATCHENTRY
FieldC typeMeaning
filterMediusCatchFilterThe accepted subscription: class, id, direction, and the capture that applies when this entry is the match.
droppeduint16_tEvents this entry could not queue.

Delivery is ranked: under a busy mouse, bulk can go completely undrained (queue order: Delivery). The header's dropped says events are being lost; the per-entry one says which subscription loses them.

MEDIUSCLOCKESTIMATE

The box measures the difference with a four-timestamp exchange across the inter-chip link. Each frame is stamped as it reaches the wire, not when queued, because queueing is that link's largest and most variable delay.

FieldC typeMeaning
offset_usint32_tThe host chip's clock minus the device chip's. Add it to a DEVICE_CHIP stamp to read that stamp on the HOST_CHIP timeline; subtract to go the other way.
rate_ppbint32_tRelative drift in parts per billion, for extrapolating between exchanges. MEDIUS_CLOCK_RATE_NONE means no drift was fitted, which differs from a fitted 0.
delay_usuint16_tThe best round trip measured in the window. The offset is good to ~half of it, which tells you whether a cross-domain comparison is worth making.
age_msuint32_tHow old the estimate is. MEDIUS_CLOCK_AGE_NONE means never measured. Never measured and a measured zero offset both read offset_us == 0; only the measured one is usable.

MediusImperfectStatusImperfect-clone state (each 0 or 1)

From medius_device_query_imperfect. See Options.

FieldC typeTrue (1) when
alloweduint8_tThe opt-in toggle; cloning a device the box can't clone faithfully is allowed.
over_capacityuint8_tThe real device has >4 IN endpoints live at once (the box has four transmit FIFOs), >6 HID interfaces, or runs at high speed.
clone_imperfectuint8_tThe live clone is imperfect: an opted-in device the box can't clone faithfully, a forced rate, or an applied patch set.

MediusEmitPaceStatusEmit-rate pacing state

From medius_device_query_emit_pace. See Options.

FieldC typeMeaning
modeMediusEmitModeThe pace.
fixed_hzuint16_tThe rate requested for FIXED (0 otherwise).
resolved_hzuint16_tThe ceiling in effect. 0 = learnt/adaptive, or INTERVAL with no device yet; 1000 once the renderer has a profile.
force_hzuint16_tThe forced wire rate requested; 0 leaves the native interval.
advertised_hzuint16_tWhat the clone's input endpoints advertise now, forced or native; 0 = no clone.
force_activeuint8_t1 when a forced interval is written into the descriptor being served.

MediusRenderStatusRendering texture

From medius_device_query_render. See Options.

FieldC typeMeaning
modeMediusRenderModeThe texture stored on the box.
fulluint8_t1 when native motion is rendered by the model rather than relayed.
readyuint8_t1 once a profile has armed for the real device. Nothing is rendered until it has.

MediusSpreadStatusHow far an injected delta is spread in time

From medius_device_query_spread. See Options.

FieldC typeMeaning
percentuint16_tShare of the command interval a delta is released across; 0 is the whole delta on the next report.
span_usuint32_tThe interval in effect, in microseconds; 0 when nothing is released across an interval.

MediusCountersSnapshotHost-side always-on link counters

From medius_device_counters. See Logs & counters.

FieldC typeMeaning
frames_txuint64_tFrames sent to the box.
frames_rxuint64_tFrames received from the box.
crc_dropsuint64_tInbound frames dropped on a bad checksum.
reconnectsuint64_tTimes the library reopened the port.
restartsuint64_tDevice-chip restarts the library recovered from by re-sending the state it holds. A session release (counted in MediusStats.session) does not count here. See session recovery.

MediusPortInfoDiscovered medius serial port

Filled by medius_find_ports. Canonical docs on PortInfo.

FieldC typeMeaning
pathchar[MEDIUS_MAX_PATH]Serial port path (NUL-terminated).
viduint16_tUSB vendor id (0x1A86).
piduint16_tUSB product id (0x55D3).
serialchar[MEDIUS_MAX_SERIAL]The CH343 adapter's serial (NUL-terminated); empty when has_serial == 0.
has_serialuint8_tWhether the adapter serves a serial string.

MediusBoxInfoOne discovered box: port, version, and cloned device

Filled by medius_list: one entry per connected box, each read in turn. See BoxInfo.

FieldC typeMeaning
portMediusPortInfoThe box's control port (path + CH343 serial).
versionMediusVersionIts firmware version and control protocol, with the box MAC and name.
deviceMediusDeviceInfoThe device it clones; zeroed when has_device is 0.
has_deviceuint8_t0 for a box whose version.proto_ver isn't the library's protocol; opening it returns MEDIUS_STATUS_ERR_BAD_PROTO_VER.
EXAMPLE
MediusBoxInfo boxes[8];
uintptr_t total = 0;
uintptr_t n = medius_list(boxes, 8, &total);
for (uintptr_t i = 0; i < n; i++) {
    const MediusBoxInfo *b = &boxes[i];
    if (b->has_device)
        printf("%s  %04x:%04x %s\n", b->port.path, b->device.vid, b->device.pid, b->device.product);
    else
        printf("%s  protocol %u: update its firmware\n", b->port.path, b->version.proto_ver);
}

Event & log typesFixed-size PODs off the streams

Values off the catch and log streams. Semantics on Catch; canonical docs on Structs.

MediusMotionEventOne native relative-axis snapshot

Native motion at the merge point, before locks or injection apply. The Motion arm of a MediusCatchEvent, raised by a MEDIUS_CATCH_CLASS_AXIS subscription.

The enclosing event's ts_us and clock stamp it; for motion the domain is always MEDIUS_CLOCK_DOMAIN_HOST_CHIP.

FieldC typeMeaning
dxint16_tRelative X this report (right positive).
dyint16_tRelative Y this report (down positive).
dzint16_tWheel delta this report (up positive).
panint16_tAC Pan (horizontal-scroll) delta this report (right positive).

MediusUsageEventOne held-usage snapshot for a class

The held usages of one class (button, key, or media) in usages[0..n], one shape for every class. Modifiers are key usages 0xE0 to 0xE7. Test one with medius_usage_event_is_held(&event, usage). Raised by a BTN, KEY, or MEDIA subscription.

Like motion, it is stamped by the enclosing event's ts_us and clock, always MEDIUS_CLOCK_DOMAIN_HOST_CHIP.

FieldC typeMeaning
class_MediusClassWhich class this snapshot is of; carried here because an n == 0 snapshot has no entry to read it from.
directionuint8_t, a MEDIUS_DIRECTION_* valueThe edge that produced it: the subscribed set grew (POSITIVE) or shrank (NEGATIVE).
nuint16_tLive usages in usages.
usagesMediusUsage[MEDIUS_MAX_USAGES]The held MediusUsage usages (button, key, or media).

The decoded-input stream diffs successive snapshots into press and release edges.

MediusTrafficEventOne captured packet off a traffic class

struct MediusTrafficEvent {
    MediusCatchClass class_;
    uint16_t         id;
    uint8_t          direction;
    uint8_t          flags;
    uint16_t         true_len;   /* length before capture truncation */
    uint16_t         len;        /* bytes actually kept              */
    uint8_t          bytes[MEDIUS_MAX_TRAFFIC_BYTES];
};

The Traffic arm of a MediusCatchEvent, raised by any of the byte-oriented catch classes.

bytes is an inline array capped at MEDIUS_MAX_TRAFFIC_BYTES (180), so the event is a fixed-size POD to copy, queue, and drop with nothing to free.

FieldC typeMeaning
class_MediusCatchClassWhich class produced the event; it also selects how flags reads.
iduint16_tThe endpoint number or interface number, per the class.
directionuint8_t, a MEDIUS_DIRECTION_* valuePOSITIVE = IN (device to PC), NEGATIVE = OUT (PC to device).
flagsuint8_tClass-specific; see the table below. 0 for classes that define none.
true_lenuint16_tThe packet's length on the bus, before capture cut it.
lenuint16_tBytes actually captured; the live prefix of bytes.
bytesuint8_t[MEDIUS_MAX_TRAFFIC_BYTES]The capture, valid over bytes[0..len].

true_len separates a packet cut at your capture from a short one; medius_traffic_event_truncated(&ev) makes that comparison.

FLAGS, BY CLASS
Classflags reads asDecode it with
MEDIUS_CATCH_CLASS_HID_IN, _EMITBit 1: a zero-length packet (on a HID endpoint, possibly one that ended the report after the event's bytes). Bit 7: a rewrite rule changed, dropped, answered or refused the packet.medius_traffic_event_zlp, medius_traffic_event_rule_acted
MEDIUS_CATCH_CLASS_HID_OUT, _VENDOR_INTERRUPTBit 1: a zero-length packet. Bit 7: a rule acted.medius_traffic_event_zlp, medius_traffic_event_rule_acted
MEDIUS_CATCH_CLASS_VENDOR_BULKBit 0: end of transfer. Bit 1: a zero-length packet. Bit 7: a rule acted.medius_traffic_event_bulk_end_of_transfer, medius_traffic_event_zlp, medius_traffic_event_rule_acted
MEDIUS_CATCH_CLASS_CONTROLBits 0-1: the handshake the game PC received, below. Bit 7: a rule acted.medius_traffic_event_control_status, into a MediusControlStatus; medius_traffic_event_rule_acted
MEDIUS_CATCH_CLASS_CLIP_TRANSFERHow the transfer ended, a MEDIUS_TRANSFER_STATUS_* byte; 0xFE when no reply came.medius_traffic_event_transfer_status
MEDIUS_CATCH_CLASS_BUSThe bus event kind (table below).medius_traffic_event_bus_event, into a MediusBusEvent
every other class0.-
CONTROL EVENTS

A CONTROL or CLIP_TRANSFER event is one completed transaction. bytes holds the 8-byte SETUP packet, then the data stage (IN data only on CLIP_TRANSFER); direction says which way the data went. Split them with medius_traffic_event_setup and medius_traffic_event_data.

A CONTROL event is the transaction as the game PC received it, on any control endpoint. IN data is the reply the PC received, after any reply rule or ANSWER. OUT data is what the PC sent, before any rule rewrote it. A request the box served from its value cache still produces an event.

MediusControlStatusBits 0-1PC received
MEDIUS_CONTROL_STATUS_OK0A completed transaction.
MEDIUS_CONTROL_STATUS_STALLED1A STALL from the real device, from a rule that refused the request, or (above endpoint 0) for a failed request.
MEDIUS_CONTROL_STATUS_NAKED2NAKs until the PC gave up, on endpoint 0 only.
MEDIUS_CONTROL_STATUS_OTHER3A value this build does not know; read flags for it.
BUS EVENT KINDS

A BUS event puts the kind in flags and up to two operands in bytes. medius_traffic_event_bus_event decodes both into a MediusBusEvent.

MediusBusEventKindflagsOperands
MEDIUS_BUS_EVENT_KIND_RESET0none
MEDIUS_BUS_EVENT_KIND_SUSPEND1none
MEDIUS_BUS_EVENT_KIND_RESUME2none
MEDIUS_BUS_EVENT_KIND_CONFIGURED3configuration
MEDIUS_BUS_EVENT_KIND_DECONFIGURED4none
MEDIUS_BUS_EVENT_KIND_SET_INTERFACE5interface, alt
MEDIUS_BUS_EVENT_KIND_DEVICE_ATTACHED6none
MEDIUS_BUS_EVENT_KIND_DEVICE_DETACHED7none
MEDIUS_BUS_EVENT_KIND_CLONE_UP8none
MEDIUS_BUS_EVENT_KIND_CLONE_DOWN9none

A chip reboot restarts its stamping clock. Call medius_timeline_reset for a chip that restarted; a device-chip restart raises MediusCountersSnapshot.restarts.

MediusCatchEventOne catch-stream event (a tagged union)

struct MediusCatchEvent {
    MediusCatchEventKind kind;
    uint32_t             ts_us;
    MediusClockDomain    clock;
    union MediusCatchEventData {
        MediusMotionEvent  motion;
        MediusUsageEvent   usages;
        MediusTrafficEvent traffic;
    } data;
}

Written by medius_event_stream_recv and its variants. Read the union member named by kind. See MediusClockDomain for which class lands in which domain.

FieldC typeMeaning
kindMediusCatchEventKindWhich union member is live.
ts_usuint32_tWhen the report or packet was seen, in box microseconds. Wraps every ~71.6 minutes and restarts at a chip reboot. See Catch timestamps.
clockMediusClockDomainWhich chip's clock ts_us came from. Compare stamps only within one domain, or map both onto this machine's clock with a MediusTimeline.
data.motionMediusMotionEventRead when kind == MOTION.
data.usagesMediusUsageEventRead when kind == USAGES.
data.trafficMediusTrafficEventRead when kind == TRAFFIC.
EVENT SIZE
PartBytesMade of
data.motion8Four int16_t deltas.
data.traffic19010 bytes of header once padded, plus bytes[180].
data.usages1028class_, direction, n, plus 256 × 4-byte MediusUsage = 1024.
the union1028The largest arm, so usages sets it.
MediusCatchEvent1040kind, ts_us, clock, their padding, and the union.

An event is 1040 bytes whichever arm is live. Padding is the compiler's; use sizeof for the exact number.

MediusLogLineOne device log line

Written by medius_log_stream_recv; text is NUL-terminated.

FieldC typeMeaning
levelMediusLogLevelSeverity tag.
textchar[MEDIUS_MAX_LOG_TEXT]The decoded message (NUL-terminated).

MediusInputEventOne decoded press, release, or motion report

struct MediusInputEvent {
    MediusInputKind   kind;    /* PRESS = 0, RELEASE = 1, MOTION = 2   */
    uint32_t          ts_us;
    MediusClockDomain clock;   /* always HOST_CHIP for native input    */
    MediusUsage       usage;   /* the edge's usage; zeroed for MOTION  */
    int16_t           dx, dy, dz, pan;
};

Written by medius_input_stream_recv and its non-blocking siblings, as edges diffed from the box's held-usage snapshots.

FieldC typeMeaning
kindMediusInputKindWhich fields are live.
ts_usuint32_tThe report's arrival stamp, in the clock chip's microseconds.
clockMediusClockDomainAlways MEDIUS_CLOCK_DOMAIN_HOST_CHIP: native input is stamped on the host chip.
usageMediusUsageThe button, key, or media usage that changed; zeroed for MOTION.
dx, dy, dz, panint16_tRelative X, Y, wheel, and AC Pan this report; zero unless kind is MOTION.

MediusStampedOne event placed on this machine's clock

struct MediusStamped {
    uint64_t host_ns;     /* on the caller's monotonic scale     */
    uint64_t box_us;      /* unwrapped past the 32-bit rollover  */
    uint64_t excess_ns;   /* jitter above the measured floor     */
};

Written by medius_timeline_observe. host_ns comes back on the same scale as the now_ns you passed in.

FieldC typeMeaning
host_nsuint64_tWhen the event happened, on the caller's monotonic clock.
box_usuint64_tThe event's stamp, unwrapped: a raw ts_us wraps every ~71.6 minutes.
excess_nsuint64_tHow much later than the measured floor this event reached the caller. Jitter, not latency.

MediusClipTriggerOne native-input binding that drives the clip

A managed binding added with medius_clip_bind: when on hits edge, the box runs action on the clip. Build on with the medius_usage_* helpers. See Clip.

FieldC typeMeaning
onMediusUsageThe native button, key, or media usage that fires the binding.
edgeMediusEdgeWhich edge of on fires it.
actionMediusClipActionWhat the binding does to the clip.
consumeuint8_t1 hides the input from the game PC; 0 relays it.

MediusClipPacketTriggerOne packet binding that drives the clip

A managed binding added with medius_clip_bind_packet, keyed by (class_, id, direction, match_bytes, mask). When a packet's head matches under the mask, the box runs action on the frame clock's next tick. medius_clip_query_config reads back the same shape, so a trigger read back can be bound again as is. See Clip.

FieldC typeMeaning
class_uint8_tThe surface the packet crosses: MEDIUS_CATCH_CLASS_HID_IN, _HID_OUT, _VENDOR_INTERRUPT, _VENDOR_BULK, _CONTROL or _EMIT.
iduint16_tThe interface number for HID_IN, the endpoint number for the rest, or MEDIUS_CATCH_ID_ANY.
directionuint8_tA MEDIUS_DIRECTION_* value: POSITIVE (IN), NEGATIVE (OUT) or BOTH.
actionuint8_tA MEDIUS_CLIP_ACTION_* value.
consumeuint8_t1 drops every packet this trigger matches as the top-ranked trigger, before the rewrite table sees it. The box holds a consuming trigger only with the imperfect-clone opt-in on (medius_device_allow_imperfect_clones), and on any class but CONTROL.
once_per_runuint8_t1 runs action on the first packet of a run of matching packets; 0 runs it on each. A run needs one stream: a class other than CONTROL, a concrete id, and POSITIVE or NEGATIVE.
selector_lenuint8_tWith once_per_run: how many leading match bytes pick the run's stream within the address (a report ID, for example). The remaining bytes are the condition, so selector_len < match_len. 0 without once_per_run.
match_len, mask_lenuint16_tValid bytes in match_bytes / mask (equal). 0 takes every packet on the address.
match_bytes, maskuint8_t[MEDIUS_MAX_PKT_MATCH]The head compare: a packet matches when head[i] & mask[i] == match_bytes[i] for each byte.
hitsuint16_tPackets matched as the top-ranked trigger since the trigger was bound or overwritten (saturating). Filled by medius_clip_query_config and read by medius_mock_set_clip_settings; medius_clip_bind_packet does not send it.
EXAMPLE
/* Report ID 7 on interface 2 carries a button in bit 5 of its second byte. Start once per hold. */
MediusClipPacketTrigger held = {
    .class_ = MEDIUS_CATCH_CLASS_HID_IN, .id = 2,
    .direction = MEDIUS_DIRECTION_POSITIVE,
    .action = MEDIUS_CLIP_ACTION_START,
    .once_per_run = 1, .selector_len = 1,
    .match_len = 2, .mask_len = 2,
    .match_bytes = { 0x07, 0x20 }, .mask = { 0xFF, 0x20 },
};
MediusStatus st = medius_clip_bind_packet(clip, &held);
if (st == MEDIUS_STATUS_ERR_CLIP_PACKET_TRIGGER) { /* medius_last_error_message() says why */ }

MediusClipSettings cfg;
medius_clip_query_config(clip, &cfg);
for (uint8_t i = 0; i < cfg.packet_n; i++)
    printf("action %u, %u hits\n", cfg.packet_triggers[i].action, cfg.packet_triggers[i].hits);

medius_clip_unbind_packet(clip, &held);   /* by key */

MediusClipSettingsClip configuration read back from the box

From medius_clip_query_config. See Clip.

FieldC typeMeaning
autolock_bitsuint8_tThe auto-lock scope as CLIP_LOCK_* wire bits (set with medius_clip_set_autolock).
loop_uint8_tPlayback loops at the clip end (retained mode only).
retainuint8_tThe loaded clip is retained so it can rewind and replay (0 = streaming).
finalizeduint8_tA retained clip's end is fixed, so it can replay and loop.
rideuint8_tThe clip's motion waits for a native move under movement riding.
triggersMediusClipTrigger[MEDIUS_CLIP_TRIG_MAX]The bound input triggers, triggers[0..n].
nuint8_tLive entries in triggers.
packet_triggersMediusClipPacketTrigger[MEDIUS_CLIP_PKT_TRIG_MAX]The bound packet triggers, packet_triggers[0..packet_n], in the order the box holds them, each with its hits.
packet_nuint8_tLive entries in packet_triggers.

MediusClipStatus & MediusClipStateBuffered-clip ring and playback state

From medius_clip_query_status; state is a MediusClipState. See Clip.

MEDIUSCLIPSTATE
EnumeratorValueMeaning
MEDIUS_CLIP_STATE_IDLE0No clip playing (empty, or a loaded clip parked at its start).
MEDIUS_CLIP_STATE_PLAYING1Draining the ring, one entry per native frame.
MEDIUS_CLIP_STATE_PAUSED2Halted mid-clip; the cursor and any held usages are retained.
MEDIUS_CLIP_STATE_FAULTED3An append was dropped or the ring overflowed; recover with medius_clip_clear.
MEDIUSCLIPSTATUS
FieldC typeMeaning
stateMediusClipStateThe lifecycle state.
freeuint32_tRing bytes free; pace top-ups off this.
totaluint32_tRetained: the clip size in bytes. Streaming: the bytes buffered but not yet played.
playeduint32_tBytes played from the clip start (retained progress; ~0 while streaming).
ticksuint32_tContent frames played since the box booted (gap runs are not counted).
underrunsuint16_tEmpty-ring episodes.
overrunsuint16_tAppends dropped because the ring was full.
seq_gapsuint16_tDropped append frames detected.
xfersuint16_tClip transfers the real device completed.
xfer_errsuint16_tClip transfers that ended any other way: a refusal, no reply, no room in the box's queue, or dropped behind a transfer the real device did not reply to.
gateduint16_tRaw reports and transfers the box discarded because the imperfect-clone opt-in was off.
held_nuint16_tHeld usages in held.
heldMediusUsage[MEDIUS_MAX_USAGES]The buttons, keys, and media the clip is holding down; test one with medius_clip_status_is_held.

MediusSetupUSB control-transfer setup packet

Passed by value to medius_device_transfer and medius_clip_frame_transfer. The eight bytes of a USB §9.3 setup packet, little-endian on the wire. See Control transfers.

FieldC typeMeaning
request_typeuint8_tbmRequestType: direction in bit 7 (set = device-to-host).
requestuint8_tbRequest.
valueuint16_twValue.
indexuint16_twIndex.
lengthuint16_twLength: the data-stage length.

MediusTransferOutcomeReal device's reply to a control transfer

Filled by medius_device_transfer. MEDIUS_STATUS_OK means only that the box replied; status is the transfer's result.

FieldC typeMeaning
statusuint8_tA MEDIUS_TRANSFER_STATUS_* value (see below).
lenuint16_tValid bytes in data.
datauint8_t[MEDIUS_MAX_DEV_PAYLOAD]The IN data the real device returned.
EnumeratorValueMeaning
MEDIUS_TRANSFER_STATUS_OK0x00Completed; data is in data.
MEDIUS_TRANSFER_STATUS_REFUSED0xFCThe box refused it before it reached the real device: opt-in off, malformed, wLength >504 or OUT data shorter than wLength, or the host chip's control queue full.
MEDIUS_TRANSFER_STATUS_STALL0xFDThe real device STALLed.
MEDIUS_TRANSFER_STATUS_NAK0xFEOne of: the real device did not finish within 500 ms, the transfer failed on the bus, the endpoint is undeclared, no reply crossed the link within 800 ms, or the game PC has suspended the clone (and with it the real device).
MEDIUS_TRANSFER_STATUS_NO_DEVICE0xFFNo real device attached to the host chip.

MediusRewriteRuleOne rewrite rule, as set and read back in full

Passed to medius_device_set_rewrite and filled by medius_device_query_rewrite_entry. A rule is keyed by (class_, id, direction, match, mask). See Rewrite rules.

FieldC typeMeaning
class_uint8_tA MEDIUS_REWRITE_CLASS_* value (see below).
iduint16_tInterface number or endpoint number.
directionuint8_tA MEDIUS_DIRECTION_* value (BOTH/POSITIVE/NEGATIVE).
actionuint8_tA MEDIUS_REWRITE_ACTION_* value (see below).
offsetuint16_tWhere a patching action writes.
match_len, mask_lenuint16_tValid bytes in match_bytes / mask (equal).
match_bytes, mask, payloaduint8_t[]The head compare and the action's payload.
MediusRewriteClassValueMediusRewriteActionValue
_HID_IN4_PASS0
_HID_OUT5_DROP (report only)1
_VENDOR_INTERRUPT6_PATCH2
_VENDOR_BULK7_REPLACE3
_CONTROL8_ANSWER (control)4
_EMIT9_STALL / _NAK (control)5 / 6
_ANY0xFF_REPLY_PATCH / _REPLY_REPLACE (control)7 / 8

MediusRewriteTable & MediusRewriteEntryRewrite-table summary

Filled by medius_device_query_rewrite: a full flag, a generation counter, and one MediusRewriteEntry per rule (address, action, match_len, offset, payload_len, and saturating hits) without the bytes.

FieldC typeMeaning
table_fulluint8_tThe box refused the last new rule or overwrite for lack of room: all 32 entries in use, or the 2048-byte payload pool full. The next change to the table, or a clear, resets it.
generationuint8_tGoes up by one on each change to the table, including a clear or silence that empties it; an identical re-send leaves it unchanged. Reset, detach, link loss, re-clone and opt-in off return it to 0. The keepalive re-sends every held rule whatever generation reads.
nuint16_tValid entries in entries.
entriesMediusRewriteEntry[MEDIUS_MAX_REWRITE_ENTRIES]One row per rule.

MediusPatchOne descriptor patch, as set and read back in full

Passed to medius_device_set_patch and filled by medius_device_query_patch_entry. Keyed by (section, cfg, index, offset); a len of 0 removes the patch at that key. See Descriptor patches.

FieldC typeMeaning
sectionuint8_tA MEDIUS_PATCH_SECTION_* value: _DEVICE 0, _CONFIG 1, _REPORT 2, _STRING 3, _BOS 4.
cfguint8_tConfiguration index, for Config/Report. 0 is the first configuration, not bConfigurationValue.
indexuint8_tInterface or string index, for Report/String.
offsetuint16_tByte offset within the descriptor.
lenuint16_tValid bytes in bytes; 0 removes the patch.
bytesuint8_t[MEDIUS_MAX_DEV_PAYLOAD]The overwrite bytes.

MediusPatchSet & MediusPatchEntryStored patch set and its apply state

Filled by medius_device_query_patches: the four apply-state flags and one MediusPatchEntry per patch (section, cfg, index, offset, len) without the bytes.

FieldC typeSet when
applieduint8_tThe clone serves a non-empty patched set. A later store leaves the flag as is until the next presentation.
pendinguint8_tThe stored set differs from the one the clone serves: not applied yet, changed or emptied since, refused, or held back by the opt-in.
refuseduint8_tThe stored set failed a clone or consistency check when last presented, and hasn't changed since. The clone is presented unpatched; the box log names the check.
table_fulluint8_tThe box refused the last new patch or overwrite for lack of room: 16 patches, or the 1024-byte pool full. The next change to the set, or a clear, resets it.
n, entriesuint16_t, MediusPatchEntry[]The stored patches.

MediusTransformOne field transform

Passed to medius_device_transform and returned in the query table. source and dest are MediusLockTargets, and may not name the same field. To weigh or reverse a field, use medius_device_scale (signed percent). See Transform.

FieldC typeMeaning
opuint8_tA MEDIUS_TRANSFORM_OP_* value: _REMAP 0, _SWAP 1.
sourceMediusLockTargetThe field the transform reads.
destMediusLockTargetThe field it writes.

MediusTransformsTransform-table summary

Filled by medius_device_query_transforms: a full flag and one MediusTransform per entry, up to MEDIUS_MAX_TRANSFORM_ENTRIES (32).

FieldC typeMeaning
table_fulluint8_tA further entry was, or would be, refused.
nuint16_tValid entries in entries.
entriesMediusTransform[MEDIUS_MAX_TRANSFORM_ENTRIES]One entry per transform.

ErrorsMediusStatus plus a thread-local message

enum MediusStatus : int32_t   /* MEDIUS_STATUS_OK == 0; everything else is a failure */

Every fallible call returns a MediusStatus and writes its result through an out-param. On failure the detail is thread-local; read it before the next call on that thread overwrites it. Canonical mapping on Errors.

EnumeratorValueMeaning
MEDIUS_STATUS_OK0Success.
MEDIUS_STATUS_ERR_IO1An underlying serial or OS error.
MEDIUS_STATUS_ERR_NOT_FOUND2No port has the box's VID/PID, or no box matches the discovery id or kind.
MEDIUS_STATUS_ERR_NO_REPLY3The box never replied to the version query during the handshake.
MEDIUS_STATUS_ERR_BAD_PROTO_VER4The box replied with an unexpected proto_ver (see medius_last_error_proto_ver), including a box the discovery openers matched.
MEDIUS_STATUS_ERR_QUERY_TIMEOUT5A query waited past its timeout with no reply.
MEDIUS_STATUS_ERR_DISCONNECTED6The link dropped (also returned by a stream when it closes).
MEDIUS_STATUS_ERR_FRAME_TOO_LONG7An outbound frame exceeded the wire limit.
MEDIUS_STATUS_ERR_UPDATE8The box refused a firmware update op.
MEDIUS_STATUS_ERR_INVALID_ARG9A bad argument (e.g. a null required pointer).
MEDIUS_STATUS_ERR_PANIC10A Rust panic was caught at the boundary.
MEDIUS_STATUS_ERR_UNKNOWN11An unclassified failure.
MEDIUS_STATUS_ERR_CATCH_TABLE_FULL12The subscription needs more entries than the box's table holds.
MEDIUS_STATUS_ERR_EMPTY_SUBSCRIPTION13A catch subscription with no filters, which would never yield an event.
MEDIUS_STATUS_ERR_CAPTURE_NOT_APPLICABLE14A non-zero capture on an input class, which carries no packet.
MEDIUS_STATUS_ERR_NOT_AN_INPUT_FILTER15A traffic class passed to medius_device_input_events, which cannot decode one.
MEDIUS_STATUS_ERR_WILDCARD_NOT_INPUT16The everything filter passed to medius_device_input_events; it covers traffic too.
MEDIUS_STATUS_ERR_HALF_EDGE_INPUT_FILTER17An input filter narrowed to one edge, which cannot be decoded into press and release.
MEDIUS_STATUS_ERR_RESERVED_ID18An exact id equal to the blanket sentinel, which would address the whole class.
MEDIUS_STATUS_ERR_RELATIVE_DIRECTION19MEDIUS_DIRECTION_WITH or _AGAINST where only a fixed sign or edge can be addressed. They resolve against the bearing at emit time, after the call.
MEDIUS_STATUS_ERR_LOCK_SCALE_RANGE20A lock scale outside MEDIUS_LOCK_SCALE_MIN to MEDIUS_LOCK_SCALE_MAX.
MEDIUS_STATUS_ERR_LOCK_SCALE_USAGE21A negative (reversing) lock scale on a button, key or media usage, which carries one bit and has nothing to reverse.
MEDIUS_STATUS_ERR_IMPERFECT_REQUIRED22medius_device_set_rewrite or medius_device_apply_patch with the imperfect-clone opt-in off.
MEDIUS_STATUS_ERR_REWRITE_MASK_LENGTH23A rewrite rule whose match and mask are different lengths.
MEDIUS_STATUS_ERR_REWRITE_ACTION_CLASS24A rewrite action that is not valid for its class.
MEDIUS_STATUS_ERR_REWRITE_PAYLOAD_TOO_LARGE25A rewrite payload larger than the head the box holds for its class.
MEDIUS_STATUS_ERR_REWRITE_TABLE_FULL26A rewrite rule added to a table that already holds MEDIUS_MAX_REWRITE_ENTRIES.
MEDIUS_STATUS_ERR_TRANSFORM_OP_FIELDS27A transform op that cannot address its source/dest pair, or one field named as both.
MEDIUS_STATUS_ERR_TRANSFORM_TABLE_FULL28A transform added to a table that already holds MEDIUS_MAX_TRANSFORM_ENTRIES.
MEDIUS_STATUS_ERR_RAW_DIRECTION29A raw injection direction other than MEDIUS_DIRECTION_POSITIVE (IN) or MEDIUS_DIRECTION_NEGATIVE (OUT).
MEDIUS_STATUS_ERR_CLIP_FRAME_COUNT30A clip frame with more than MEDIUS_CLIP_EDGES_MAX edges or MEDIUS_CLIP_RAW_MAX raw reports.
MEDIUS_STATUS_ERR_CLIP_FRAME_TOO_LONG31A clip frame that encodes to more than MEDIUS_CLIP_ENTRY_MAX bytes.
MEDIUS_STATUS_ERR_CLIP_TRANSFER_DATA32A clip transfer whose data is not what its setup packet announces: length bytes for an OUT request, none for an IN one.
MEDIUS_STATUS_ERR_CLIP_PACKET_TRIGGER33A MediusClipPacketTrigger the box would refuse; medius_last_error_message says why.
MEDIUS_STATUS_ERR_REWRITE_MATCH_TOO_LONG34A rewrite rule with more than MEDIUS_MAX_REWRITE_MATCH (16) match bytes.
MEDIUS_STATUS_ERR_REWRITE_POOL_FULL35A rewrite payload larger than the room the held rules leave in MEDIUS_REWRITE_PAYLOAD_POOL (2048). An overwrite counts the bytes it replaces as free.
FunctionReturnsMeaning
medius_last_error_message(char *buf, uintptr_t cap)uintptr_tCopies the last error's text (NUL-terminated, truncated to cap); returns the full length, for a retry with a larger buffer.
medius_last_error_proto_ver(void)uint8_tThe version byte from a BAD_PROTO_VER error, or 0.