Medius - BindingsTypes & errors

Types & errors

Every C struct, enum, and status code, by table

The Medius* types and MEDIUS_* enumerators from medius.h. The calls that produce and consume them are on API index; streams on Streams. What each value means lives with the canonical type docs in the Rust Library and Native API sections.

Each enum has a fixed-width backing: MediusStatus is int32_t; every other enum is uint8_t.

On C23 and C++ the tag carries that underlying type directly (enum MediusButton : uint8_t); on C99 the tag is typedef'd to the integer and you pass the prefixed enumerators (MEDIUS_BUTTON_LEFT).

Structs are plain PODs, nothing heap-allocated: pass by value, read fields directly, free nothing per value. Only the opaque handles have a *_free.

Anything variable-length on the wire lands in an inline fixed-cap array with a count beside it, never a pointer you own. The shapes on this page are ABI version 5, the number medius_abi_version() returns.

Sizing constants

Fixed-cap arrays sized to the wire limits

The event, lock, and log PODs embed fixed-cap arrays sized to the protocol's own limits, with a count field saying how many slots are live. 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_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 is never what truncates: a short len means the box cut the packet at your capture.

Enums

uint8_t-backed selectors (MediusStatus is int32_t)

Each value is a wire byte; the canonical meaning lives on Enums. The *Kind enums (and MediusClass) tag which arm of a built value (MediusUsage, MediusMotion, MediusCatchEvent) is populated.

MediusDeviceKind

The cloned device's primary kind
enum MediusDeviceKind : uint8_t

The kind field of a MediusDeviceInfo, from the cloned device's Boot-interface protocol. Also what medius_device_find_mouse_box / _find_keyboard_box select on. See DeviceKind.

EnumeratorValueMeaning
MEDIUS_DEVICE_KIND_UNKNOWN0Neither a Boot keyboard nor mouse.
MEDIUS_DEVICE_KIND_KEYBOARD1The device is a keyboard.
MEDIUS_DEVICE_KIND_MOUSE2The device is a mouse.

MediusButton

A mouse 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.

MediusAxis

A relative 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.

MediusAction

The press / release tri-state
enum MediusAction : uint8_t

The override action shared by inject calls, whether a button, key, or media usage. See the injection model.

EnumeratorValueMeaning
MEDIUS_ACTION_SOFT_RELEASE0Drop the box's override, press or force; a physical hold stays down.
MEDIUS_ACTION_PRESS1Force the input down.
MEDIUS_ACTION_FORCE_RELEASE2Force the input up, masking a physical hold.

MediusClass

Which 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.

MediusMotionKind

Which arm of a MediusMotion is set
enum MediusMotionKind : uint8_t

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

EnumeratorValueMeaning
MEDIUS_MOTION_KIND_CURSOR0dx/dy apply.
MEDIUS_MOTION_KIND_WHEEL1wheel applies.

MediusLockTargetKind

Which 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_USAGE3A momentary usage (the struct's usage field selects which).

MediusDirection

An axis sign, a usage edge, or a transfer direction
enum MediusDirection : uint8_t

One enum with three readings, picked by what it is attached to: an axis sign, a usage edge, or the transfer direction on a MediusCatchFilter naming a byte-oriented catch class. See LOCK and Catch.

EnumeratorValueOn an axis or wheelOn a button or keyOn a traffic-class filter
MEDIUS_DIRECTION_BOTH0Both signs; on a scale, a full pass to the relative pair.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 anywhere else is MEDIUS_STATUS_ERR_RELATIVE_DIRECTION. A media usage has no edges: an edge named on one goes out as MEDIUS_DIRECTION_BOTH, which is what MediusLocks reports it as.

UNNAMED DIRECTION BYTES

Parameters and struct fields carrying a direction are declared uint8_t, so a value outside the enum reaches the boundary rather than the wire.

CallComes back as
Any call returning a MediusStatusMEDIUS_STATUS_ERR_INVALID_ARG; no frame goes out.
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.

MediusEdge

Which 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.

MediusClipAction

The engine action a clip trigger drives
enum MediusClipAction : uint8_t

What a bound MediusClipTrigger does to the clip on its edge; the same verbs as 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.

MediusBlanket

A whole-group lock selector
enum MediusBlanket : uint8_t

A whole input group: which one medius_device_lock_all/_unlock_all block in one call, and the scope medius_clip_set_autolock auto-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.

EnumeratorValueMeaningWhat dir picks
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.

MediusLedTarget

Which chip's status LED to drive
enum MediusLedTarget : uint8_t

See LED.

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

MediusLedMode

What 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 own status display.
MEDIUS_LED_MODE_OFF1LED dark.
MEDIUS_LED_MODE_SOLID2Lit steadily at level.
MEDIUS_LED_MODE_BLINK3Blinks at level.

MediusRebootTarget

Which 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 its own USB).
MEDIUS_REBOOT_TARGET_DEVICE_RUN2Restart the device chip and run its firmware.
MEDIUS_REBOOT_TARGET_HOST_RUN3Restart the host chip and run its firmware.

MediusEmitMode

What paces injected motion
enum MediusEmitMode : uint8_t

See Options.

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

MediusCatchEventKind

Which arm of a MediusCatchEvent is set
enum MediusCatchEventKind : uint8_t

Tells you which member of the MediusCatchEvent union to read. Which arms you can see follows from the MediusCatchClass values you subscribed to: the four input classes decode into motion and usages, every byte-oriented class into traffic. 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

MediusClockDomain

Which chip's clock stamped an event
enum MediusClockDomain : uint8_t

The clock field of a MediusCatchEvent, beside ts_us. Which domain an event carries is fixed by where it is tapped.

The box is two ESP32-S3s that boot independently, so a stamp is only meaningful against other 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 the IN direction of VENDOR_INTERRUPT / VENDOR_BULK.
MEDIUS_CLOCK_DOMAIN_DEVICE_CHIP1At the tap on the device chip, the side facing the game PC.HID_OUT, both OUT directions, CONTROL, EMIT, and BUS.

Both clocks are box-local and unrelated to any PC clock. Each is a uint32_t of microseconds: it wraps every ~71.6 minutes and returns to zero when that chip reboots.

A stamp below the previous one is a wrap, a reboot, or a domain change.

To put both on this machine's clock, feed events to a MediusTimeline. To relate the two box domains directly, read the estimate on MediusCatchState.

MediusInputKind

Which 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.

MediusLogLevel

Severity 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.

MediusCatchClass

What 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 the box's USB plumbing and surface 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, or the wheel.Every axis.
MEDIUS_CATCH_CLASS_HID_IN4A cloned HID interface number.Every HID interface.
MEDIUS_CATCH_CLASS_HID_OUT5An interrupt-OUT endpoint address.Every interrupt-OUT endpoint.
MEDIUS_CATCH_CLASS_VENDOR_INTERRUPT6A vendor interrupt endpoint address.Every vendor interrupt endpoint.
MEDIUS_CATCH_CLASS_VENDOR_BULK7A vendor bulk endpoint address.Every vendor bulk endpoint.
MEDIUS_CATCH_CLASS_CONTROL8A control endpoint number (0 is EP0).Every control endpoint.
MEDIUS_CATCH_CLASS_EMIT9An emitting endpoint address.Every emitting endpoint.
MEDIUS_CATCH_CLASS_BUS10Nothing; pass MEDIUS_CATCH_ID_ANY.Every bus event.
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 seven byte-oriented ones).

WHERE EACH CLASS IS TAPPED

The four input classes are captured at the emission merge point before lock suppression and before injection, so a locked input still reports.

MEDIUS_CATCH_CLASS_EMIT is the mirror image: what the clone put on the wire after injection, locks, and the suppression gate.

MediusCatchFilter

One 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 you hand to medius_device_catch_events or medius_device_input_events. Build each one with a medius_catch_filter_* helper, then 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 spelled class_ because class is a C++ keyword and the header compiles as both.

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 exactly 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 one byte carries either reading unambiguously.
captureuint8_tBytes kept per event; 0 keeps the whole packet. Traffic classes only: an input class carries no packet, and 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. A blanket is one table entry, not an expansion into per-id entries, matching how a blanket lock works.
MEDIUS_CATCH_CLASS_ANY255Every class. A real id beside it addresses nothing and is refused.
MEDIUS_MAX_CATCH_ENTRIES32Entries the box's table holds. Ask for more and the call fails with MEDIUS_STATUS_ERR_CATCH_TABLE_FULL.

The useful capture length differs between classes by orders of magnitude. A 64-byte vendor interrupt report is worth having whole; a bulk pipe traced only for framing is worth 16 bytes. So it lives on the entry that matched, not box-wide.

Matching is most-specific-first, and the winning entry is the one whose capture applies. That is what makes "everything at 16 bytes, except endpoint 0x83 in full" two entries rather than an impossibility.

  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
/* everything the box can see, first 16 bytes only */
MediusCatchFilter blanket =
    medius_catch_filter_with_capture(medius_catch_filter_everything(), 16);

/* except this endpoint's IN traffic, which we want 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 now fails the whole call with its own MediusStatus, rather than being dropped with no status. Two filters naming the same table entry, whatever their captures, are what medius_catch_filter_same_address tests.

MediusKey

A HID keyboard/keypad usage
typedef uint8_t MediusKey;   /* modifiers are 0xE0 to 0xE7 */

A raw HID keyboard usage passed to the key calls. Pass any usage byte, or one of the MEDIUS_KEY_* macros. The full set of usages is on 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)

MediusMediaKey

A 16-bit HID Consumer usage
typedef uint16_t MediusMediaKey;

A raw Consumer usage passed to the media calls. Pass any 16-bit usage, or a MEDIUS_MEDIA_* macro. The full set is on 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

MediusFrameType

A wire 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 structs

Tagged values you build, then pass in

Three small PODs you build with a helper and hand to a call. The medius_*_* constructors set the kind tag and the right field for you.

MediusUsage

A momentary 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.

MediusMoveTiming

When a move reaches the game PC
enum MediusMoveTiming : uint8_t

The timing argument of medius_device_move_axis, against movement riding. See Move.

EnumeratorValueMeaning
MEDIUS_MOVE_TIMING_RIDE0Wait for a real cursor move to carry the delta.
MEDIUS_MOVE_TIMING_NOW1Emit on the box's own clock.

MediusPendingMotion

What 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 for a ride alone.
MEDIUS_PENDING_MOTION_FLUSH1Emit it now, ignoring the ride window.
MEDIUS_PENDING_MOTION_DISCARD2Drop it.

MediusMotion

A relative axis for move_axis

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

FieldC typeMeaning
kindMediusMotionKindCursor vs wheel.
dxint16_tX movement (Cursor only).
dyint16_tY movement (Cursor only).
wheelint16_tScroll delta (Wheel only).

MediusLockTarget

What 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, or Usage.
usageMediusUsageThe button, key, or media usage, when kind == USAGE.

Query values

PODs 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.

MediusVersion

Decoded 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 synthesized default when unset).

MediusHealth

Box 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_tThe native-rate estimator window is full.
lock_onuint8_tAt least one input is off a full pass: blocked, or merely weighed.
catch_onuint8_tA catch subscription is streaming.
kbd_attacheduint8_tA keyboard is attached, cloned, and injectable.

MediusDeviceInfo

The cloned device's USB identity, kind, and product

From medius_device_device_info; all-zero/empty when nothing is cloned. product is a NUL-terminated UTF-8 string.

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 device's primary kind (Boot-interface protocol).
productchar[MEDIUS_MAX_PRODUCT]The product string (NUL-terminated; empty when none).

MediusCaps

The whole 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.

MediusMouseCaps

What 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.
has_report_iduint8_tThe mouse report sits behind a HID report ID.
n_hiduint8_tCloned HID interfaces; >1 = composite.

MediusKbdCaps

What 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 (passthrough-only).
has_report_iduint8_tThe keyboard report sits behind a HID report ID.

MediusRate

The native 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_tCloned inject-endpoint poll period in µs.
confidentuint8_tThe estimator window is full and the value is trustworthy.
change_drivenuint8_tThe active input is event-driven (keyboard/media), so no continuous cadence.

MediusChipFirmware

What 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.

MediusFirmwareInfo

Both 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 answered 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.

MediusStats

Box-side delivery / telemetry counters

From medius_device_query_stats. A nonzero tx_drops or tx_wedges means delivery degraded under load.

FieldC typeMeaning
inject_emitsuint32_tPure-injection reports emitted.
tx_dropsuint16_tReports dropped on TX-queue overflow (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 issued.
reset_countuint16_tUSB bus resets seen.
config_countuint16_tSET_CONFIGURATION events (re-enumerations).

MediusLocks & MediusLockEntry

The active 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 ask whether it is blocked outright with medius_locks_is_locked; both count a covering whole-class is_blanket entry. Wire layout on the native LOCKS reply.

Asked with MEDIUS_DIRECTION_BOTHAnswers about
medius_locks_scale_ofThe lowest scale across every direction, relative pair included. Not the figure a delta meets: a delta picks up one from each pair, multiplied.
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 physical value.
MEDIUS_LOCK_SCALE_PASS100Keep all of it, untouched.
MEDIUS_LOCK_SCALE_MAX2552.55x, the ceiling.
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.
scaleuint8_tPercent of the physical value kept; 0 is blocked. A momentary usage carries one bit, so the box stores the block or pass it renders and this never reads between them.
READBACK
CaseWhat the array holds
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 96 entries, well under MEDIUS_MAX_LOCKS; past that the rest is absent, with nothing marking it. See the native LOCKS budget.
A direction byte no constant namesThe entry is dropped rather than trusted, and n moves with the drop.

MediusBearing & MediusBearingMode

What 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 own sign against its own bearing, independently. The default.
MEDIUS_BEARING_MODE_VECTOR1The delta is projected onto the injected XY vector, and the relative scale weighs only the part along it. The fixed-sign scales still reach what the projection leaves on each axis. One relative scale, the lower of X's and Y's, governs the whole aim; what MediusLocks reports back is there.
CONSTANT
MacroValueMeaning
MEDIUS_BEARING_WINDOW_DEFAULT_MS20The factory window. A box that has been set boots at its own value.

MediusCatchState & MediusCatchEntry

The live subscription table, plus the inter-chip clock estimate

From medius_device_query_catch: a header followed by entries[0..n], one per accepted MediusCatchFilter. The array is inline and capped at MEDIUS_MAX_CATCH_ENTRIES (32), the box's own table size, so the reply always carries the whole table.

The order is the order the box accepted them in, not the order it matches in: matching is worked out per event, most-specific-first, with ties going to the earlier entry.

FieldC typeMeaning
table_fulluint8_t1 when an entry was refused because the table was already full.
droppeduint32_tBox-wide events shed under back-pressure, across every entry.
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 four strict-priority queues: input and bus first, then the byte-oriented traffic classes, then control, then vendor bulk. Under a busy mouse, bulk can starve completely.

So the count is kept twice. The header's dropped says you are losing events; the per-entry one says which subscription is losing them, and those want different fixes.

MEDIUSCLOCKESTIMATE

The box measures the difference with a four-timestamp exchange across the inter-chip link, stamping each frame as it reaches the wire rather than when it is queued. Queueing is the largest and most variable delay on that link.

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 none was fitted, which is a different answer from a fitted 0.
delay_usuint16_tThe best round trip measured in the window; the offset is good to about half of it. This is what says whether a cross-domain comparison is worth making.
age_msuint32_tHow old the estimate is. MEDIUS_CLOCK_AGE_NONE keeps "never measured" distinct from an offset that happens to be zero; both read as offset_us == 0 and only one is usable.

MediusImperfectStatus

The imperfect-clone state (each 0 or 1)

From medius_device_query_imperfect. See Options.

FieldC typeTrue (1) when
alloweduint8_tThe opt-in toggle; cloning an over-capacity device is allowed.
over_capacityuint8_tThe device needs an interrupt-IN endpoint the box can't service.
clone_imperfectuint8_tThe live clone is over-capacity and was cloned anyway, so one interface is dead.

MediusEmitPaceStatus

The emit-rate pacing state

From medius_device_query_emit_pace. See Options.

FieldC typeMeaning
modeMediusEmitModeThe selected mode.
fixed_hzuint16_tThe rate requested for FIXED (0 otherwise).
resolved_hzuint16_tThe ceiling in effect; 0 = learnt/adaptive, or no device yet in INTERVAL.
force_hzuint16_tThe forced wire rate requested; 0 leaves the device's own.
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.

MediusCountersSnapshot

Host-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.

MediusPortInfo

A discovered medius serial port

Filled by medius_find_ports; path and serial are NUL-terminated. 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.

MediusBoxInfo

One discovered box: port, version, and cloned device

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

FieldC typeMeaning
portMediusPortInfoThe box's control port (path + CH343 serial).
versionMediusVersionIts firmware version, with the box MAC and name.
deviceMediusDeviceInfoThe device it clones.

Event & log types

Fixed-size PODs off the streams

The values you read off the catch and log streams. Catch semantics on Catch; canonical docs on Structs.

MediusMotionEvent

One physical relative-axis snapshot

The user's real motion at the merge point, before any lock suppression or injection. Surfaces as the Motion arm of a MediusCatchEvent, raised by a MEDIUS_CATCH_CLASS_AXIS subscription.

It carries no timestamp of its own: the enclosing event's ts_us and clock cover it, and 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).

MediusUsageEvent

One held-usage snapshot for a class

The held usages of one class (button, key, or media; modifiers are key usages 0xE0 to 0xE7) in usages[0..n], buttons and keys the same shape. Test one with medius_usage_event_is_held(&event, usage). Raised by a BTN, KEY, or MEDIA subscription.

Like motion, it carries no timestamp of its own: it uses the enclosing event's ts_us and clock, which for a usage snapshot is always MEDIUS_CLOCK_DOMAIN_HOST_CHIP.

FieldC typeMeaning
class_MediusClassWhich class this snapshot is of. Carried here rather than read off the first entry, because the snapshot that most needs it is the one with n == 0.
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).

Diffing successive snapshots into press and release edges is what the decoded-input stream does for you.

MediusTrafficEvent

One 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 rather than a pointer, capped at MEDIUS_MAX_TRAFFIC_BYTES (180), so the event stays a fixed-size POD you can 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 address, 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].

Without true_len, a packet the box cut at your capture and a genuinely short packet are indistinguishable. medius_traffic_event_truncated(&ev) is that comparison.

FLAGS, BY CLASS
Classflags reads asDecode it with
MEDIUS_CATCH_CLASS_VENDOR_BULKBit 0: end of transfer. Bit 1: a zero-length packet.medius_traffic_event_bulk_end_of_transfer, medius_traffic_event_bulk_zlp
MEDIUS_CATCH_CLASS_CONTROLThe real device's answer: 0 it completed, 0xFD it STALLed, 0xFE it NAKed to timeout.medius_traffic_event_control_status, into a MediusControlStatus
MEDIUS_CATCH_CLASS_BUSThe bus event kind (table below).medius_traffic_event_bus_event, into a MediusBusEvent
every other class0.-
CONTROL: ONE EVENT PER TRANSACTION

A CONTROL event covers a whole completed transaction, not one stage of one: bytes is the 8-byte SETUP packet followed by the data stage, and direction says which way that data went. Split them with medius_traffic_event_setup and medius_traffic_event_data.

A request the box answered from its own descriptor cache still produces an event.

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

None of these announces a chip reboot, which is the only thing that restarts a stamping clock. Call medius_timeline_reset for a chip you know restarted.

MediusCatchEvent

One 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 friends. 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 your own clock with a MediusTimeline.
data.motionMediusMotionEventRead when kind == MOTION.
data.usagesMediusUsageEventRead when kind == USAGES.
data.trafficMediusTrafficEventRead when kind == TRAFFIC.
HOW BIG ONE EVENT IS
PartBytesMade of
data.motion6Three int16_t deltas.
data.traffic19010 bytes of header once padded, plus bytes[180].
data.usages1028class_, direction, n, plus 256 x 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. The exact padding is the compiler's, so use sizeof if you need the number itself.

MediusLogLine

One 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).

MediusInputEvent

One 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 physical input  */
    MediusUsage       usage;   /* the edge's usage; zeroed for MOTION  */
    int16_t           dx, dy, dz;
};

Written by medius_input_stream_recv and its non-blocking siblings. The box sends held-usage snapshots; the stream diffs them, so a caller reads edges instead of sets.

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: physical input is stamped on the host chip.
usageMediusUsageThe button, key, or media usage this is an edge on; zeroed for MOTION.
dx, dy, dzint16_tRelative X, Y, and wheel this report; zero unless kind is MOTION.

MediusStamped

One event placed on this machine's clock
struct MediusStamped {
    uint64_t host_ns;     /* on the caller's own 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 own stamp, unwrapped: a raw ts_us wraps every ~71.6 minutes.
excess_nsuint64_tHow much later than the measured floor this event reached you. Jitter, not latency.

MediusClipTrigger

One physical-input binding that drives the clip

A managed binding you add with medius_clip_bind: when on hits edge, the box runs action on the clip. Build the on usage with the medius_usage_* helpers. Concept on Clip.

FieldC typeMeaning
onMediusUsageThe physical button, key, or media usage that fires the binding.
edgeMediusEdgeWhich edge of on fires it.
actionMediusClipActionWhat it does to the clip.
consumeuint8_t1 to suppress the input so it never reaches the PC; 0 to let it pass through.

MediusClipSettings

The clip configuration read back from the box

From medius_clip_query_config: the auto-lock scope, the loop/retain/finalize scalars, and the live trigger set. Concept on 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 real move under movement riding.
triggersMediusClipTrigger[MEDIUS_CLIP_TRIG_MAX]The bound triggers, triggers[0..n].
nuint8_tLive entries in triggers.

MediusClipStatus & MediusClipState

Buffered-clip ring and playback state

From medius_clip_query_status; state is a MediusClipState. Concept on 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_tThe retained clip size in bytes; streaming, the buffered-but-undrained bytes.
playeduint32_tBytes played from the clip start (retained progress; ~0 while streaming).
ticksuint32_tContent frames drained since the last start (gap runs are not counted).
underrunsuint16_tEmpty-ring episodes.
overrunsuint16_tAppends dropped because the ring was full.
seq_gapsuint16_tDropped append frames detected.
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.

Errors

MediusStatus 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 lives in thread-local state. 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 device matched the expected VID/PID.
MEDIUS_STATUS_ERR_NO_REPLY3The box never answered the version query during the handshake.
MEDIUS_STATUS_ERR_BAD_PROTO_VER4The box answered with an unexpected proto_ver (see medius_last_error_proto_ver).
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 are resolved against the bearing at emit time, which is after the call is made.
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, so you can size a buffer and retry.
medius_last_error_proto_ver(void)uint8_tThe version byte from a BAD_PROTO_VER error, or 0.
EXAMPLE
MediusDevice *dev = NULL;
if (medius_device_find(&dev) != MEDIUS_STATUS_OK) {
    char buf[256];
    medius_last_error_message(buf, sizeof buf);
    fprintf(stderr, "open failed: %s\n", buf);
    return 1;
}