API index
Every medius_* call in medius.h, grouped. Semantics are in the Rust library (the medius crate) and Native API; structs, enums and constants on Types & errors; streams on Streams.
Most calls are fire-and-forget: they return once the frame is queued. Queries, open and find block for the box's reply.
Every fallible call returns a MediusStatus (MEDIUS_STATUS_OK is 0) and writes its result through an out-param. medius_last_error_message() gives the calling thread's last failure text.
MediusDevice *dev = NULL;
if (medius_device_find(&dev) != MEDIUS_STATUS_OK) {
char buf[256];
medius_last_error_message(buf, sizeof buf); /* why it failed */
return 1;
}
MediusVersion v;
medius_device_query_version(dev, &v); /* result written to &v */
medius_device_free(dev);Opaque handles (MediusDevice, MediusEventStream, MediusInputStream, MediusTimeline, MediusLogStream, MediusMockBox) are yours to free, each with a matching *_free.
Events and log lines are fixed-size structs with nothing to free per event; see Lifecycle.
Connecting & lifecycle Open, share, and release the link
See Connection and Lifecycle.
| Function | Does |
|---|---|
medius_device_open(const char *path, MediusDevice **out) | Open a serial path and handshake. |
medius_device_find(MediusDevice **out) | Open the first box found by USB id. |
medius_device_clone(const MediusDevice *dev) | Another handle to the same link (ref-counted); returns MediusDevice *. Null in → null out. |
medius_device_free(MediusDevice *dev) | Free a handle; joins the reader/keepalive threads when the last clone drops. Null is a no-op. |
medius_find_ports(MediusPortInfo *out, uintptr_t cap, uintptr_t *out_total) | List present ports into out (up to cap); writes total to *out_total, returns the number written. See MediusPortInfo. |
DiscoveryEnumerate boxes and open one by identity
See Discovery.
| Function | Does |
|---|---|
medius_list(MediusBoxInfo *out, uintptr_t cap, uintptr_t *out_total) | Enumerate every connected box into out (up to cap). Reads each box's version, and its cloned-device info if it speaks this protocol (a box on another protocol has has_device 0). Writes the total to *out_total and returns the number written. See MediusBoxInfo. |
medius_device_open_by_id(const char *id, MediusDevice **out) | Open the box whose identity matches id (device MAC hex or CH343 serial) and handshake. MEDIUS_STATUS_ERR_BAD_PROTO_VER when that box speaks another protocol, MEDIUS_STATUS_ERR_NOT_FOUND when no box matches. |
medius_device_find_mouse_box(MediusDevice **out) | Open the first box whose clone is a mouse. If none is found and a connected box is on another protocol, returns MEDIUS_STATUS_ERR_BAD_PROTO_VER (that box's clone can't be read). |
medius_device_find_keyboard_box(MediusDevice **out) | Open the first box whose clone is a keyboard, with the same errors. |
MovementRelative cursor and wheel
See Move. +x right, +y down. Build the axis struct with the motion helpers.
| Function | Does |
|---|---|
medius_device_move_rel(MediusDevice *dev, int16_t dx, int16_t dy) | Move the cursor by a signed 16-bit delta. |
medius_device_wheel(MediusDevice *dev, int16_t delta) | Scroll the wheel. |
medius_device_move_rel_now(MediusDevice *dev, int16_t dx, int16_t dy) | The same, bypassing movement riding. |
medius_device_wheel_now(MediusDevice *dev, int16_t delta) | Scroll, bypassing movement riding. |
medius_device_pan(MediusDevice *dev, int16_t delta) | AC Pan (horizontal scroll), a full peer of the wheel. |
medius_device_pan_now(MediusDevice *dev, int16_t delta) | Pan, bypassing movement riding. |
medius_device_flush_motion(MediusDevice *dev) | Emit the motion held by movement riding, now. |
medius_device_discard_motion(MediusDevice *dev) | Drop the motion held by movement riding. |
medius_device_move_axis(MediusDevice *dev, MediusMotion motion, MediusMoveTiming timing, MediusPendingMotion pending) | Drive one axis from a medius_motion_cursor(...), medius_motion_wheel(...), or medius_motion_pan(...). |
InjectDrive any usage: button, key, or media
One verb set over a MediusUsage (button, key, or media). Build it with the input helpers; see Inject, the injection model, and the id spaces on Usage IDs.
| Function | Does |
|---|---|
medius_device_inject(MediusDevice *dev, MediusUsage input, MediusAction action) | Apply a MediusAction to a usage. |
medius_device_press(MediusDevice *dev, MediusUsage input) | Sends MEDIUS_ACTION_PRESS. |
medius_device_soft_release(MediusDevice *dev, MediusUsage input) | Sends MEDIUS_ACTION_SOFT_RELEASE. |
medius_device_force_release(MediusDevice *dev, MediusUsage input) | Sends MEDIUS_ACTION_FORCE_RELEASE. |
A MediusKey or MediusMediaKey is a raw HID usage.
LocksWeigh native input
See Lock. A MediusLockTarget picks an axis or usage. dir takes a MediusDirection constant and what a MediusBlanket constant; any other value is MEDIUS_STATUS_ERR_INVALID_ARG and sends no frame. Read the entries back with medius_locks_scale_of and medius_locks_is_locked.
| Function | Does |
|---|---|
medius_device_scale(MediusDevice *dev, MediusLockTarget target, uint8_t dir, int16_t scale) | Keep scale percent of an axis or usage in one direction, from _MIN (-255) to _MAX (255): MEDIUS_LOCK_SCALE_BLOCK (0) blocks, _PASS (100) passes untouched, and a negative reverses what it keeps (axes only). |
medius_device_scale_all(MediusDevice *dev, uint8_t what, uint8_t dir, int16_t scale) | The same over a whole class (aim, wheel, buttons, keys, or media). |
medius_device_lock(MediusDevice *dev, MediusLockTarget target, uint8_t dir) | Block an axis or usage on a direction: scale 0. |
medius_device_unlock(MediusDevice *dev, MediusLockTarget target, uint8_t dir) | Back to passing untouched: scale 100. |
medius_device_lock_all(MediusDevice *dev, uint8_t what, uint8_t dir) | Blanket block a whole class. |
medius_device_unlock_all(MediusDevice *dev, uint8_t what, uint8_t dir) | Release a blanket block. |
A negative percent inverts: -100 on an axis flips it exactly. The delta's sign before weighing picks the direction, so a negative on one direction leaves the other alone. A negative on a button, key or media usage is MEDIUS_STATUS_ERR_LOCK_SCALE_USAGE (one bit has nothing to reverse). A magnitude outside the range is ..._ERR_LOCK_SCALE_RANGE.
A scale auto-clears unless the keepalive holds it. MEDIUS_DIRECTION_WITH and _AGAINST need a live bearing (medius_device_set_bearing); their refusal rules are on MediusDirection.
LED, admin & options Status light, resets, persistent settings
| Function | Does |
|---|---|
medius_device_led(MediusDevice *dev, MediusLedTarget target, MediusLedMode mode, uint8_t level) | Drive the status LED. See LED. |
medius_device_reset(MediusDevice *dev) | Clear all overrides. See Admin. |
medius_device_factory_reset(MediusDevice *dev) | Clear all overrides, then the box erases its stored name, options and learned devices and reboots. See factory_reset. |
medius_device_reapply(MediusDevice *dev) | Re-send the active settings. |
medius_device_reconnect(MediusDevice *dev) | Rescan, reopen this box, and re-apply held state (reconnect). MEDIUS_STATUS_ERR_BAD_PROTO_VER if the box replies on another protocol, and the handle stays disconnected. |
medius_device_reboot(MediusDevice *dev, MediusRebootTarget target) | Reboot a chip to run or download mode. |
medius_device_allow_imperfect_clones(MediusDevice *dev, bool allow) | Opt in to cloning a device the box can't clone faithfully; this also opens the advanced control layer. If the toggle re-presents the clone, the session is released, and the library re-sends what it holds once the new clone is up. See Options. |
medius_device_set_movement_riding(MediusDevice *dev, bool enabled, uint32_t window_ms) | Set movement riding (the window is rounded to whole ms); enabled == false clears the window. |
medius_device_set_emit_pace(MediusDevice *dev, uint8_t mode, uint16_t hz, uint16_t force_hz) | Pick the pace (hz is the target for FIXED) and the advertised rate (force_hz, 0 = the native interval). Both go in one frame. A mode no constant names is MEDIUS_STATUS_ERR_INVALID_ARG and sends nothing. See Options. |
medius_device_set_name(MediusDevice *dev, const char *name) | Set the box's human-readable name (1-32 printable ASCII characters). See Name. |
medius_device_clear_name(MediusDevice *dev) | Clear the name, back to the synthesised default. Read it back on MediusVersion.name. |
medius_device_set_bearing(MediusDevice *dev, uint16_t window_ms, uint8_t mode) | Set what MEDIUS_DIRECTION_WITH / _AGAINST are measured against; window_ms == 0 turns the bearing off. |
medius_device_set_spread(MediusDevice *dev, uint16_t percent) | Spread each injected delta over percent of the command interval: 0 puts the whole delta on the next report, 100 spreads it over one interval, >100 overlaps. See Options. |
medius_device_set_render(MediusDevice *dev, uint8_t mode, bool full) | Pick the texture (MediusRenderMode) and whether native motion is rendered by the model rather than relayed. Both share one frame. full is off on a box that has not been set. See Options. |
QueriesRead box state; each blocks for one reply
See Requests. Each writes a struct from Types & errors, or returns MEDIUS_STATUS_ERR_QUERY_TIMEOUT when no reply arrives.
| Function | Writes to *out |
|---|---|
medius_device_query_version(dev, MediusVersion *out) | MediusVersion: protocol + firmware version. |
medius_device_query_health(dev, MediusHealth *out) | MediusHealth: link, mouse, clone, injection flags. |
medius_device_device_info(dev, MediusDeviceInfo *out) | MediusDeviceInfo: the cloned device's USB identity, kind, and product. |
medius_device_caps(dev, MediusCaps *out) | MediusCaps: mouse/keyboard capabilities. |
medius_device_query_rate(dev, MediusRate *out) | MediusRate: native report rate and poll period. |
medius_device_query_stats(dev, MediusStats *out) | MediusStats: box-side telemetry. |
medius_device_query_locks(dev, MediusLocks *out) | MediusLocks: every weighed direction (entry list). |
medius_device_query_catch(dev, MediusCatchState *out) | MediusCatchState: the accepted subscription entries with their per-entry drops, the box-wide drop count, and the inter-chip clock estimate. |
medius_device_query_imperfect(dev, MediusImperfectStatus *out) | MediusImperfectStatus: imperfect-clone state. |
medius_device_query_movement_riding(dev, bool *out_enabled, uint32_t *out_window_ms) | Whether riding is on, and the window in ms (0 when off). |
medius_device_query_emit_pace(dev, MediusEmitPaceStatus *out) | MediusEmitPaceStatus: pacing mode, rate in effect, and the rate the clone advertises. |
medius_device_query_bearing(dev, MediusBearing *out) | MediusBearing: the bearing window and geometry. |
medius_device_query_render(dev, MediusRenderStatus *out) | MediusRenderStatus: the texture, whether native motion goes through it, and whether a profile has armed. |
medius_device_query_spread(dev, MediusSpreadStatus *out) | MediusSpreadStatus: how far an injected delta is spread, and the interval in effect. |
medius_device_counters(dev, MediusCountersSnapshot *out) | MediusCountersSnapshot: host-side wire counters. |
Firmware update Write either chip over the open connection
See Firmware update. Staging blocks for the whole transfer; a refusal returns MEDIUS_STATUS_ERR_UPDATE.
| Function | Does |
|---|---|
medius_device_firmware_info(dev, MediusFirmwareInfo *out) | Both chips' versions, the slot each runs, and what is staged. |
medius_device_stage_firmware(dev, target, image, len, progress, user) | Write len bytes into target's spare slot (0 = device chip, 1 = host chip). Inert until activated; progress may be NULL. |
medius_device_activate_firmware(dev) | Commit every staged image and reboot into it. Blocks while the host chip reboots and comes back. |
medius_device_abort_update(dev, target) | Throw a staged or in-flight transfer away. |
medius_device_update_firmware(dev, target, image, len, progress, user) | Stage one image and activate it. A refused activate aborts the staged image before the error returns. |
medius_device_wait_firmware_confirmed(dev, MediusFirmwareInfo *out) | Block until neither chip is on probation, polling the box through the reboot after an activate. After 55 s: MEDIUS_STATUS_ERR_UPDATE if a chip is still on probation, MEDIUS_STATUS_ERR_QUERY_TIMEOUT if the box has not replied. |
medius_last_error_update(MediusUpdateError *out) | After MEDIUS_STATUS_ERR_UPDATE, write the refused op and its status to out (MediusUpdateError); false if the thread's last error was not an update error. |
void (*progress)(void *user, uintptr_t sent, uintptr_t total)
StreamsSubscribe to live input and logs
Consuming events is on Streams, the catch feature on Catch, and logs on Logs & counters.
medius_device_catch_events takes an array of MediusCatchFilter entries from the filter helpers. The box's table holds 32 entries. More entries, or one the box cannot honour, fail the call.
MediusStatus medius_device_catch_events(MediusDevice *dev,
const MediusCatchFilter *filters,
uintptr_t n_filters,
MediusEventStream **out);
MediusStatus medius_device_input_events(MediusDevice *dev,
const MediusCatchFilter *filters,
uintptr_t n_filters,
MediusInputStream **out);| Function | Does |
|---|---|
medius_device_catch_events(dev, const MediusCatchFilter *filters, uintptr_t n_filters, MediusEventStream **out) | Subscribe to the addressed input and traffic classes; each element becomes one table entry. |
medius_event_stream_clone(const MediusEventStream *stream) | Another handle to the same subscription. Null in → null out. |
medius_event_stream_free(MediusEventStream *stream) | Free a handle; the subscription ends with the last one. |
medius_event_stream_recv(stream, MediusCatchEvent *out) | Block for the next event; MEDIUS_STATUS_ERR_DISCONNECTED on close. |
medius_event_stream_try_recv(stream, MediusCatchEvent *out) | Next buffered event; returns false if none (never blocks). |
medius_event_stream_recv_timeout(stream, uint64_t timeout_ms, MediusCatchEvent *out) | Block up to timeout_ms; false on timeout or close. |
medius_event_stream_dropped(stream) | Events dropped because the consumer fell behind. |
medius_event_stream_is_connected(stream) | Whether the box still delivers. A false from _try_recv or _recv_timeout can't tell a closed stream from an empty one. |
medius_event_stream_close(stream) | End the subscription for every clone. A receive blocked on it in another thread returns at once. Free the handle once no thread uses it. |
medius_device_input_events(dev, const MediusCatchFilter *filters, uintptr_t n_filters, MediusInputStream **out) | Subscribe to decoded press/release edges. Every filter must name an input class and cover both edges. See Decoded input. |
medius_input_stream_recv / _try_recv / _recv_timeout(stream, …, MediusInputEvent *out) | Pull the next MediusInputEvent (block / non-block / timed). |
medius_input_stream_held(stream, MediusClass class_, MediusUsage *out, uintptr_t cap) | Write that class's held usages into out; returns how many there are. |
medius_input_stream_dropped(stream) / medius_input_stream_free(stream) | Events lost behind a slow consumer / release the handle. There is no input-stream clone. |
medius_input_stream_is_connected(stream) / _close(stream) | As for an event stream. |
medius_timeline_new() / _free(t) | Open / release a timeline that maps box stamps onto the caller's clock. |
medius_timeline_observe(t, const MediusCatchEvent *ev, uint64_t now_ns, MediusStamped *out) | Place one event on the caller's monotonic scale, unwrapped past the 32-bit rollover. |
medius_timeline_observe_input(t, const MediusInputEvent *ev, uint64_t now_ns, MediusStamped *out) | The same for an input-stream event. One timeline orders events from both streams. |
medius_timeline_reset(t, MediusClockDomain domain) / _samples(t, domain) | Forget a rebooted chip's rollover count and floor / how many events that domain has fed in. |
medius_device_logs(MediusDevice *dev, MediusLogStream **out) | Open the device log-line stream. |
medius_log_stream_clone / medius_log_stream_free | Clone / free a log-stream handle. |
medius_log_stream_recv / try_recv / recv_timeout(stream, …, MediusLogLine *out) | Pull the next MediusLogLine (block / non-block / timed). |
medius_log_stream_is_connected(stream) / _close(stream) | As for an event stream. Closing one log stream leaves the others reading. |
Catch filters Name one subscription entry, then narrow it
Pure constructors for a MediusCatchFilter: a base names what to observe, a modifier returns a narrowed copy. No device, no wire traffic. See Catch.
| Function | Addresses |
|---|---|
medius_catch_filter_watch(MediusUsage usage) | One momentary usage (button, key, or media), as medius_device_lock takes it. |
medius_catch_filter_watch_axis(MediusAxis axis) | One relative MediusAxis. |
medius_catch_filter_watch_class(MediusClass class_) | Every usage in one momentary class. |
medius_catch_filter_watch_axes() | Every relative axis: X, Y, the wheel, and AC Pan. |
medius_catch_filter_all_input(MediusCatchFilter *out) | Writes the four input-class filters to out[0..4]: buttons, keys, media and axes (everything medius_device_input_events can report). |
medius_catch_filter_traffic(MediusCatchClass class_, uint16_t id) | One traffic address: an endpoint, an interface, or a control endpoint number. |
medius_catch_filter_traffic_class(MediusCatchClass class_) | Every id within one traffic class. |
medius_catch_filter_everything() | Every class, every id, both directions, whole packets. One table entry, not an expansion. |
| Function | Returns a copy of f |
|---|---|
medius_catch_filter_with_direction(f, uint8_t direction) | Restricted to one direction, sign, or edge. |
medius_catch_filter_with_capture(f, uint8_t bytes) | Keeping only the first bytes of each packet; 0 keeps the whole one. Traffic classes only. |
medius_catch_filter_on_press(f) / _on_release(f) | Restricted to the press / release edge. |
medius_catch_filter_inbound(f) / _outbound(f) | Restricted to traffic from the real device to the PC / from the PC to the real device. |
medius_catch_filter_everything includes MEDIUS_CATCH_CLASS_VENDOR_BULK, which can saturate the control link on its own. Pair it with medius_catch_filter_with_capture unless tracing bulk in full. Queue ranking: Delivery.
/* the wheel, scrolled up only */
MediusCatchFilter up = medius_catch_filter_with_direction(
medius_catch_filter_watch_axis(MEDIUS_AXIS_WHEEL), MEDIUS_DIRECTION_POSITIVE);
/* EP0, first 8 bytes: the setup packet and nothing after it */
MediusCatchFilter ep0 = medius_catch_filter_with_capture(
medius_catch_filter_traffic(MEDIUS_CATCH_CLASS_CONTROL, 0), 8);Buffered clip playback Preload a per-frame stream, box-clocked
Build an entry stream with a MediusClipBuilder, fill a multi-field frame with a MediusClipFrame, then drive playback through the MediusClip from medius_device_clip. All three are opaque, each with a matching *_free. See Clip.
| Function | Does |
|---|---|
medius_clip_builder_new() / _free(b) / _clear(b) | Allocate / free / reset a builder. |
medius_clip_builder_byte_len(b) | uintptr_t: ring bytes the entries take; compare with MediusClipStatus.free before an append. |
medius_clip_builder_gap(b, uint16_t frames) | A gap run (0 = no-op). |
medius_clip_builder_move(b, dx, dy) / _wheel(b, dz) / _pan(b, dpan) | A cursor / wheel / pan (horizontal scroll) motion frame. |
medius_clip_builder_press / _release / _force_release(b, usage) | A one-edge press / soft-release / force-release frame; usage is a MediusUsage (button, key, or media). |
medius_clip_builder_edge(b, usage, action) | A one-edge frame for any MediusUsage with an explicit MediusAction. |
medius_clip_builder_raw(b, uint8_t ep_num, uint8_t dir, const uint8_t *bytes, uintptr_t len) | A frame carrying one raw report (see medius_clip_frame_raw). |
medius_clip_builder_transfer(b, uint8_t ep, MediusSetup setup, const uint8_t *out_data, uintptr_t out_len) | A frame carrying one control transfer (see medius_clip_frame_transfer). |
medius_clip_builder_frame(b, const MediusClipFrame *frame) | One frame carrying whatever frame holds. The builder takes a copy, so frame stays usable. |
| Function | Does |
|---|---|
medius_clip_frame_new() / _free(f) / _clear(f) | Allocate / free / reset a frame. |
medius_clip_frame_byte_len(f) | uintptr_t: the ring bytes the frame takes. An append accepts a frame of up to MEDIUS_CLIP_ENTRY_MAX (512). |
medius_clip_frame_move(f, dx, dy) / _wheel(f, dz) / _pan(f, dpan) | Set the frame's cursor / wheel / pan (horizontal scroll) motion. |
medius_clip_frame_press / _release / _force_release(f, usage) | Add a press / soft-release / force-release edge of a MediusUsage. |
medius_clip_frame_edge(f, usage, action) | Add an edge with an explicit MediusAction, up to MEDIUS_CLIP_EDGES_MAX (8) a frame. |
medius_clip_frame_raw(f, uint8_t ep_num, uint8_t dir, const uint8_t *bytes, uintptr_t len) | Add a raw report, as medius_device_raw sends one; up to MEDIUS_CLIP_RAW_MAX (8) a frame. Played only with the imperfect-clone opt-in on. |
medius_clip_frame_transfer(f, uint8_t ep, MediusSetup setup, const uint8_t *out_data, uintptr_t out_len) | Add a control transfer, as medius_device_transfer runs one: out_data is setup.length bytes for an OUT request, none for an IN one. The reply arrives as a MEDIUS_CATCH_CLASS_CLIP_TRANSFER event. The box runs it only while the opt-in is on. |
MediusClipBuilder *b = medius_clip_builder_new();
MediusClipFrame *f = medius_clip_frame_new();
/* move (+10, -4) and press Left on the same frame */
medius_clip_frame_move(f, 10, -4);
medius_clip_frame_press(f, medius_usage_button(MEDIUS_BUTTON_LEFT));
medius_clip_builder_frame(b, f); /* the builder copies it */
/* reuse the frame: the next tick queues a SET_REPORT to the real device */
MediusSetup setup = { .request_type = 0x21, .request = 0x09,
.value = 0x0300, .index = 0, .length = 2 };
const uint8_t out[2] = { 0x04, 0x01 };
medius_clip_frame_clear(f);
medius_clip_frame_transfer(f, 0, setup, out, sizeof out);
medius_clip_builder_frame(b, f);
medius_clip_frame_free(f);| Function | Effect |
|---|---|
medius_device_clip(dev, out) / medius_clip_free(clip) | Open / free a clip handle. |
medius_clip_append(clip, b) | Append the builder's entries to the ring. Every entry is checked first, so a refusal sends nothing: MEDIUS_STATUS_ERR_CLIP_FRAME_COUNT, _CLIP_FRAME_TOO_LONG, _CLIP_TRANSFER_DATA, _RAW_DIRECTION, or _RELATIVE_DIRECTION. |
medius_clip_set_autolock(clip, const MediusBlanket *scope, uintptr_t scope_len) | Set the auto-lock scope to the MediusBlanket groups in scope (NULL / 0 = no lock). Set it before the first append. |
medius_clip_set_loop(clip, uint8_t on) / _set_retain(clip, uint8_t on) | Loop at the clip end (retained only) / retain the loaded clip so it can rewind and replay (0 = streaming, the default). |
medius_clip_set_ride(clip, uint8_t on) | Run the clip's motion under movement riding (0 = the box's clock, the default). While rendering is on with a profile armed, only the clip's wheel and pan ride. |
medius_clip_finalize(clip) | Fix a retained clip's end so it can replay and loop. |
medius_clip_bind(clip, MediusClipTrigger trigger) | Add or overwrite a MediusClipTrigger: a MediusEdge of on drives a MediusClipAction; consume hides the input from the game. |
medius_clip_unbind(clip, MediusUsage usage, MediusEdge edge) | Remove the input trigger on that usage + edge. |
medius_clip_bind_packet(clip, const MediusClipPacketTrigger *trigger) | Add or overwrite a MediusClipPacketTrigger. A matched packet drives its MediusClipAction on the frame clock's next tick; only the most specific matching trigger acts. A trigger the box would refuse is MEDIUS_STATUS_ERR_CLIP_PACKET_TRIGGER before anything is sent: the crate's refusals, or a selector_len without once_per_run. A bind the box itself refuses leaves the set unchanged: compare medius_clip_query_config's readback with what was bound. |
medius_clip_unbind_packet(clip, const MediusClipPacketTrigger *trigger) | Remove the packet trigger keyed by trigger's class, id, direction, match and mask. Its other fields are ignored. A key the box cannot hold is refused, as by medius_clip_bind_packet. |
medius_clip_clear_triggers(clip) | Remove every trigger of both kinds. |
medius_clip_start(clip) / _stop(clip) | Rewind and play (or resume a pause) / stop, flush a streaming clip (rewind a retained one), and release held input and the auto-lock. |
medius_clip_pause(clip) / _resume(clip) | Halt mid-clip, retaining the cursor and held input / continue from the paused cursor. |
medius_clip_restart(clip) / _toggle(clip) | Force a rewind and play, even mid-playback / play if idle or paused, stop if playing. |
medius_clip_clear(clip) | Discard the loaded clip, free the ring, and clear a Faulted state. |
medius_clip_lost(const MediusClip *clip) | bool: the box dropped the clip appended since the last medius_clip_clear (its device chip restarted, the box released the session, or a reconnect found the ring empty). Set once the box can take a reload; reset by the next append or clear. No wire traffic. See lost. |
medius_clip_query_status(clip, out) | Fill a MediusClipStatus: ring depth, progress, and playback counters. |
medius_clip_query_config(clip, out) | Fill a MediusClipSettings: auto-lock scope, loop/retain, finalized, and both kinds of trigger, each packet trigger with its hits. |
Advanced control layer Raw injection, control transfers, rewrite rules, descriptor patches
Gated on the imperfect-clone opt-in, medius_device_allow_imperfect_clones(dev, true). See Raw injection, Control transfers, Rewrite rules, and Descriptor patches. With it off: set_rewrite and apply_patch return MEDIUS_STATUS_ERR_IMPERFECT_REQUIRED; the box drops medius_device_raw (the library sends it unchecked); a transfer returns OK with MEDIUS_TRANSFER_STATUS_REFUSED. Queries, removes, clears and set_patch need no opt-in.
| Function | Does |
|---|---|
medius_device_raw(MediusDevice *dev, uint8_t ep_num, uint8_t dir, const uint8_t *bytes, size_t len) | Write bytes verbatim on cloned endpoint ep_num. dir is a MEDIUS_DIRECTION_* value: POSITIVE (IN) toward the game PC, NEGATIVE (OUT) to the real device. |
medius_device_transfer(MediusDevice *dev, uint8_t ep, MediusSetup setup, const uint8_t *out_data, size_t out_len, MediusTransferOutcome *out) | Run one control transfer against the real device; fill out with its status and IN data. |
medius_device_transfer_timeout(MediusDevice *dev, uint8_t ep, MediusSetup setup, const uint8_t *out_data, size_t out_len, uint32_t timeout_ms, MediusTransferOutcome *out) | The same transfer, waiting timeout_ms for the reply. |
medius_device_set_rewrite(MediusDevice *dev, const MediusRewriteRule *rule) | Install or overwrite one rewrite rule. A payload larger than the room the held rules leave in MEDIUS_REWRITE_PAYLOAD_POOL is MEDIUS_STATUS_ERR_REWRITE_POOL_FULL. A new rule past MEDIUS_MAX_REWRITE_ENTRIES is MEDIUS_STATUS_ERR_REWRITE_TABLE_FULL. |
medius_device_remove_rewrite(MediusDevice *dev, const MediusRewriteRule *rule) | Drop the rule with this rule's key. |
medius_device_clear_rewrite(MediusDevice *dev) | Drop the whole rewrite table. |
medius_device_query_rewrite(MediusDevice *dev, MediusRewriteTable *out) | Read the table summary. |
medius_device_query_rewrite_entry(MediusDevice *dev, uint8_t index, MediusRewriteRule *out) | Read one rule in full, in the shape set_rewrite takes. |
medius_device_set_patch(MediusDevice *dev, const MediusPatch *patch) | Store one descriptor patch (empty bytes removes it). |
medius_device_apply_patch(MediusDevice *dev) | If the stored patch set differs from the one the clone serves, re-present the clone with it (one replug). The re-clone releases the session, and the library re-sends what it holds once the new clone is up (session recovery). See apply_patch. |
medius_device_clear_patch(MediusDevice *dev) | Erase the real device's stored set. A clone serving patches re-presents unpatched (one replug), releasing the session as medius_device_apply_patch does. |
medius_device_query_patches(MediusDevice *dev, MediusPatchSet *out) | Read the stored set and its apply state. |
medius_device_query_patch_entry(MediusDevice *dev, uint8_t index, MediusPatch *out) | Read one patch in full. |
TransformsSwap or remap a field on the wire
Faithful field transforms, no opt-in. See Transform. An axis argument is a MediusAxis value (0 X, 1 Y, 2 wheel, 3 pan). remap takes two MediusLockTargets, so it can move a button onto a key or media usage. To weigh or reverse a field, use medius_device_scale (signed percent).
| Function | Does |
|---|---|
medius_device_transform(MediusDevice *dev, const MediusTransform *transform) | Install or overwrite one transform. |
medius_device_untransform(MediusDevice *dev, const MediusTransform *transform) | Drop the transform with the same (source, dest) key. |
medius_device_clear_transforms(MediusDevice *dev) | Drop the whole transform table. |
medius_device_transform_swap(MediusDevice *dev, uint8_t a, uint8_t b) | Exchange two axes. |
medius_device_transform_remap(MediusDevice *dev, MediusLockTarget source, MediusLockTarget dest) | Move a source field into a destination. |
medius_device_query_transforms(MediusDevice *dev, MediusTransforms *out) | Read the active table, in the order the box applies it. |
BuildersMake the value structs the calls take
Pure constructors: no device, no wire traffic. See Inject, Move, and Lock.
| Function | Returns |
|---|---|
medius_usage_button(MediusButton button) | MediusUsage for medius_device_inject. |
medius_usage_key(MediusKey key) | MediusUsage addressing a keyboard key. |
medius_usage_media(MediusMediaKey media) | MediusUsage addressing a media key. |
medius_motion_cursor(int16_t dx, int16_t dy) | MediusMotion for medius_device_move_axis. |
medius_motion_wheel(int16_t delta) | MediusMotion for a wheel scroll. |
medius_motion_pan(int16_t delta) | MediusMotion for AC Pan (horizontal scroll). |
medius_lock_target_axis(MediusLockTargetKind kind) | MediusLockTarget for an axis (X / Y / Wheel / Pan). |
medius_lock_target_usage(MediusUsage usage) | MediusLockTarget for a usage (button, key, or media). |
Struct inspectors Read query / event results without the wire
Interpret a struct you already hold, by value or pointer, with no I/O. Each mirrors the matching method on the Rust type.
| Function | Returns |
|---|---|
medius_locks_scale_of(const MediusLocks *locks, MediusLockTarget target, uint8_t dir) | int16_t: percent of the native value kept on that target and direction. 100 when nothing weighs it; negative when a scale reverses it. See Lock. |
medius_locks_is_locked(const MediusLocks *locks, MediusLockTarget target, uint8_t dir) | bool: is that target/direction blocked outright (Both needs both fixed signs). A direction merely weighed is not locked. |
medius_rate_native_hz(MediusRate rate, float *out_hz) | bool: writes the native report rate in Hz; false when there is no continuous cadence. |
medius_usage_event_is_held(const MediusUsageEvent *event, MediusUsage usage) | bool: is that usage (button, key, or media) held in the snapshot. |
medius_traffic_event_truncated(const MediusTrafficEvent *ev) | bool: ev->len < ev->true_len, meaning the matching entry's capture cut the packet. See MediusTrafficEvent. |
medius_traffic_event_setup(const MediusTrafficEvent *ev) | const uint8_t *: the 8-byte setup packet of a CONTROL or CLIP_TRANSFER event, or NULL for another class or a capture cut shorter than the setup stage. |
medius_traffic_event_data(const MediusTrafficEvent *ev, uintptr_t *out_len) | const uint8_t *: the data stage of a CONTROL or CLIP_TRANSFER event, the whole packet for any other class. Points into ev, as medius_traffic_event_setup does. |
medius_traffic_event_control_status(const MediusTrafficEvent *ev, MediusControlStatus *out) | bool: the handshake the game PC received, flags bits 0-1; false for any class but CONTROL. |
medius_traffic_event_rule_acted(const MediusTrafficEvent *ev) | bool: a rewrite rule changed, dropped, answered or refused the packet (flags bit 7, on HID_IN, HID_OUT, the vendor classes, CONTROL and EMIT). false for any other class. |
medius_traffic_event_transfer_status(const MediusTrafficEvent *ev, uint8_t *out) | bool: how the transfer ended, as a MEDIUS_TRANSFER_STATUS_* value (_NAK when no reply came); false for any class but CLIP_TRANSFER. |
medius_traffic_event_bus_event(const MediusTrafficEvent *ev, MediusBusEvent *out) | bool: the decoded lifecycle event; false for a class other than BUS, or an unknown kind. |
medius_traffic_event_bulk_end_of_transfer(ev) | bool: the last of its transfer, for a VENDOR_BULK event. |
medius_traffic_event_zlp(ev) | bool: a zero-length packet (flags bit 1, on HID_IN, HID_OUT, the vendor classes and EMIT). A HID_IN or EMIT event on a HID endpoint carries a whole report of ≤64 bytes, so there the bit can also mean a ZLP ended the report after the event's bytes. Every other event is one packet. |
medius_catch_filter_same_address(MediusCatchFilter a, MediusCatchFilter b) | bool: the two name the same box table entry, whatever their captures. |
medius_catch_class_is_input(MediusCatchClass class_) / _is_traffic(class_) | bool: one of the four parsed-input classes, which carry no packet / one of the eight byte-oriented ones. |
medius_clip_status_is_held(const MediusClipStatus *status, MediusUsage usage) | bool: is the clip holding that usage down. |
medius_caps_has_mouse(MediusCaps caps) | bool: a mouse interface is bound. See Requests. |
medius_caps_has_keyboard(MediusCaps caps) | bool: a keyboard interface is bound. |
medius_caps_is_composite(MediusCaps caps) | bool: the clone has more than one HID interface. |
medius_device_info_is_cloned(MediusDeviceInfo info) | bool: a device is cloned, including one with no HID interface (n_hid 0). |
Library functions Library-level helpers and errors
| Function | Does |
|---|---|
medius_last_error_message(char *buf, uintptr_t cap) | Copy the last error's text into buf; returns the full length (size a buffer and retry). See errors. |
medius_last_error_proto_ver() | The proto-version byte from the last MEDIUS_STATUS_ERR_BAD_PROTO_VER, or 0. |
medius_default_query_timeout_ms() | The default query reply wait, in ms. |
medius_default_transfer_timeout_ms() | The default control-transfer reply wait, in ms. |
medius_default_keepalive_cadence_ms() | The default keepalive interval, in ms. |
medius_abi_version() | The C ABI version of the loaded library (currently 10), bumped on any breaking header change. Compare it with the header's MEDIUS_ABI_VERSION once at start-up. On a mismatch the struct layouts differ: call nothing else, and rebuild against the header shipped with that library. |
medius_version_string() | The crate version as a static NUL-terminated string. |
Mock box Scriptable fake for tests, feature-gated
All behind #ifdef MEDIUS_FEATURE_MOCK (the mock cargo feature). Concept: Mock; enabling it: Build & features.
| Function | Does |
|---|---|
medius_mock_new() | A fresh mock that records commands and auto-replies to queries. |
medius_mock_clone / medius_mock_free(MediusMockBox *mock) | Share (same state) / free a mock handle. |
medius_device_with_mock(const MediusMockBox *mock, MediusDevice **out) | Build a MediusDevice over the mock without a handshake. |
medius_device_open_mock(const MediusMockBox *mock, MediusDevice **out) | Build a MediusDevice over the mock and run the handshake. |
medius_mock_set_version / _health / _device_info / _caps / _mouse_caps / _kbd_caps / _rate / _stats / _locks / _catch_state / _imperfect_status | Set the value the mock returns for each query. |
medius_mock_set_movement_riding(mock, bool enabled, uint32_t window_ms) | Set the movement-riding window the mock reports. |
medius_mock_set_bearing(mock, uint16_t window_ms, uint8_t mode) | Set the bearing the mock reports. A mode no constant names is ignored, as the box ignores it. |
medius_mock_set_render(mock, uint8_t mode, bool full, bool ready) | Set the texture the mock reports, whether native motion goes through it, and whether a profile has armed. A mode no constant names leaves the texture alone. |
medius_mock_set_spread_learned(mock, uint32_t period_us) | Set the command period the mock has learned, in microseconds. At 0 (a real box's starting state) the mock reports no interval at any percent. |
medius_mock_set_clip_status(mock, MediusClipStatus value) / _set_clip_settings(mock, MediusClipSettings value) | Set the mock's replies to the two clip queries. The settings' packet_triggers bind in order, as medius_clip_bind_packet binds them, under the opt-in state that medius_mock_set_imperfect_status scripted. Script that first: with the opt-in off, consuming triggers are dropped. The config reply is these settings plus the triggers bound on the mock. set_retain, finalize, input binds and playback are recorded as frames and leave the reply as scripted. What the mock holds. |
medius_mock_clip_packet(mock, uint8_t class_, uint16_t id, uint8_t direction, const uint8_t *head, uintptr_t head_len, uint8_t *out_action, bool *out_consumed) | bool: run one packet through the mock's packet triggers, as the box does. The most specific matching trigger counts the packet in its hits. True when that trigger runs its action on this packet (written to *out_action). False when no trigger matches, or when the trigger is once_per_run and the packet continues a run. *out_consumed is whether the trigger consumes the packet. A null out-pointer is skipped. |
medius_mock_restart(MediusMockBox *mock) | Simulate a device-chip restart: the mock drops its session state, keeps what it stores, and sends its hello now and again on the next frame it receives. See restart. |
medius_mock_link_lost(MediusMockBox *mock) | Simulate the inter-chip link dropping and coming back. The mock releases the session a host set (counted in MediusStats.session); the clone stays up. |
medius_mock_detach(MediusMockBox *mock, bool back_within_grace) | Simulate the real device detaching: the mock releases the session. With back_within_grace the real device re-attaches inside the 250 ms grace and the clone stays up; without, the clone is down until medius_mock_attach. |
medius_mock_attach(MediusMockBox *mock) | Simulate the real device attaching: a clone that was up is cloned again, which releases the session. See restart and release. |
medius_mock_silent(MediusMockBox *mock) | Stop replying to queries, for timeout tests (still records). |
medius_mock_set_emit_pace(mock, uint8_t mode, uint16_t hz, uint16_t force_hz) | Set the pacing the mock reports; hz matters only for MEDIUS_EMIT_MODE_FIXED, force_hz 0 is unforced. |
medius_mock_set_advertised_hz(mock, uint16_t hz) | Set the rate the mock's clone advertises unforced; 0 is no clone. |
medius_mock_set_transfer_reply(mock, uint8_t status, const uint8_t *data, uintptr_t len) | Set the reply to a TRANSFER while the opt-in is on; with it off the mock refuses. A status other than OK carries no data. |
medius_mock_refuse_next_activate(mock, uint8_t status) | Refuse the next ACTIVATE with status, keeping what is staged. |
medius_mock_reboot_after_activate(mock, uint32_t unanswered, uint32_t pending) | After each accepted activate, leave unanswered firmware queries with no reply, then report the device chip on probation for the next pending queries. |
medius_mock_push_raw(mock, const uint8_t *bytes, uintptr_t len) | Inject raw inbound bytes, as if the box sent them. |
medius_mock_push_log(mock, MediusLogLevel level, const char *text) | Push a LOG line onto the log stream. |
medius_mock_push_motion(mock, uint8_t seq, uint32_t ts_us, MediusMotionEvent event) | Push a MediusMotionEvent as a Motion catch event. |
medius_mock_push_usages(mock, uint8_t seq, uint32_t ts_us, const MediusUsageEvent *event) | Push a MediusUsageEvent as a Usages catch event. |
medius_mock_push_traffic(mock, uint8_t seq, uint32_t ts_us, MediusClockDomain clock, const MediusTrafficEvent *event) | Push a MediusTrafficEvent as a Traffic catch event. A true_len above len marks a cut capture. |
medius_mock_recorded(MediusMockBox *mock) | How many commands the host has sent. |
medius_mock_saw(mock, MediusFrameType ty) | Whether at least one frame of that type was sent. |
medius_mock_clear_recorded(MediusMockBox *mock) | Clear the recorded-command log. |
medius_mock_recorded_frame(mock, uintptr_t idx, MediusFrameType *out_ty, uint8_t *out_seq, uint8_t *payload_buf, uintptr_t cap) | Read recorded frame idx: type, SEQ, and payload bytes. |