<!-- Source: https://medius.k4tech.net/bindings/c/api -->
# API index

_Every C function, linked to what it does_

The whole `medius_*` surface from [`medius.h`](/bindings/c.md), grouped. Each row is the C signature and a one-line summary; follow the link for what the call does. The semantics live in the [Rust library](/library.md) (the [medius crate](https://crates.io/crates/medius)) and the [Native API](/native.md), not here. Structs, enums, and constants are on [Types & errors](/bindings/c/types.md); streams on [Streams](/bindings/c/streams.md).

Most calls are [fire-and-forget](/native/injection.md#fire-and-forget). They return as soon as the [frame](/native/frame.md) is queued and never wait on the box. The queries, plus `open` / `find`, block for the [box](/native/hardware.md)'s [reply](/native/commands/requests.md). Every fallible call returns a [`MediusStatus`](/bindings/c/types.md#errors) (`MEDIUS_STATUS_OK` is 0) and writes its result through an out-param; [`medius_last_error_message()`](/bindings/c/api.md#module) gives the last failure's text on the calling thread.

#### CALLING CONVENTION

```c
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);
```

> **Note**
>
> Opaque handles (`MediusDevice`, `MediusEventStream`, `MediusLogStream`, `MediusMockBox`) each have a `*_free`; you own them. Catch events and log lines are fixed-size structs, so there is nothing to free per event.

## Connecting & lifecycle

_Open, share, and release the link_

See [Connection](/library/connection.md) and [Lifecycle](/library/lifecycle.md).

| Function | Does |
| --- | --- |
| `medius_device_open(const char *path, MediusDevice **out)` | Open a serial path and [handshake](/native/connection.md#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](/library/guides/connection.md#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`](/bindings/c/types.md#portinfo). |

## Discovery

_Enumerate boxes and open one by identity_

Pick a box out of several by a stable identity (device MAC or CH343 serial), or by the kind of device it clones. See [Discovery](/library/discovery.md).

| Function | Does |
| --- | --- |
| `medius_list(MediusBoxInfo *out, uintptr_t cap, uintptr_t *out_total)` | Enumerate every connected box into `out` (up to `cap`): opens, handshakes, and reads each one's version + cloned-device info. Writes the total to `*out_total`, returns the number written. See [`MediusBoxInfo`](/bindings/c/types.md#box-info). |
| `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_device_find_mouse_box(MediusDevice **out)` | Open the first box whose clone is a mouse. |
| `medius_device_find_keyboard_box(MediusDevice **out)` | Open the first box whose clone is a keyboard. |

## Movement

_Relative cursor and wheel_

See [Move](/library/move.md). `+x` right, `+y` down. Build the axis struct with the [motion helpers](/bindings/c/api.md#builders).

| Function | Does |
| --- | --- |
| `medius_device_move_rel(MediusDevice *dev, int16_t dx, int16_t dy)` | Nudge the cursor by a signed 16-bit delta. |
| `medius_device_wheel(MediusDevice *dev, int16_t delta)` | Scroll the wheel. |
| `medius_device_move_axis(MediusDevice *dev, MediusMotion motion)` | Drive one axis from a `medius_motion_cursor(...)` or `medius_motion_wheel(...)`. |

## Inject

_Drive any usage: button, key, or media_

One verb set over a [`MediusUsage`](/bindings/c/types.md#input) (button, key, or media). Build it with the [input helpers](/bindings/c/api.md#builders); see [Inject](/library/inject.md), the [injection model](/native/injection.md), and the id spaces on [Usage IDs](/native/commands/usage.md).

| Function | Does |
| --- | --- |
| `medius_device_inject(MediusDevice *dev, MediusUsage input, MediusAction action)` | Apply a [`MediusAction`](/bindings/c/types.md#action) to a usage. |
| `medius_device_press(MediusDevice *dev, MediusUsage input)` | Hold the usage down (`MEDIUS_ACTION_PRESS`). |
| `medius_device_soft_release(MediusDevice *dev, MediusUsage input)` | Release, unless the user is physically holding it. |
| `medius_device_force_release(MediusDevice *dev, MediusUsage input)` | Release even against a physical hold. |

> **Note**
>
> A [`MediusKey`](/bindings/c/types.md#key) or [`MediusMediaKey`](/bindings/c/types.md#media-key) is a raw HID usage.

## Locks

_Block the user's own input_

See [Lock](/library/lock.md). A [`MediusLockTarget`](/bindings/c/types.md#lock-target) picks an axis or usage (button, key, or media) and a [`MediusLockDirection`](/bindings/c/types.md#lock-direction) picks an edge. Read the returned entries with [`medius_locks_is_locked`](/bindings/c/api.md#inspectors).

| Function | Does |
| --- | --- |
| `medius_device_lock(MediusDevice *dev, MediusLockTarget target, MediusLockDirection dir)` | Lock an axis or usage on an edge. |
| `medius_device_unlock(MediusDevice *dev, MediusLockTarget target, MediusLockDirection dir)` | Release a lock. |
| `medius_device_lock_all(MediusDevice *dev, MediusBlanket what, MediusLockDirection dir)` | Blanket lock a whole class (aim, wheel, buttons, keys, or media). |
| `medius_device_unlock_all(MediusDevice *dev, MediusBlanket what, MediusLockDirection dir)` | Release a blanket lock. |

> **Warning**
>
> A lock auto-clears; it isn't permanent. The [keepalive](/library/guides/connection.md#keepalive) holds it for you (see [Lock](/library/lock.md)).

## 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](/library/led.md). |
| `medius_device_reset(MediusDevice *dev)` | Clear all overrides. See [Admin](/library/admin.md). |
| `medius_device_reapply(MediusDevice *dev)` | Re-send the active settings. |
| `medius_device_reconnect(MediusDevice *dev)` | Force a [reconnect](/library/lifecycle.md) to the mouse. |
| `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 over-capacity devices. See [Options](/library/options.md). |
| `medius_device_set_movement_riding(MediusDevice *dev, bool enabled, uint32_t window_ms)` | Set movement riding; `enabled == false` clears the window (rounded to whole ms). |
| `medius_device_set_emit_pace(MediusDevice *dev, MediusEmitMode mode, uint16_t hz)` | Pick what paces injected motion; `hz` is the target rate for `FIXED`. See [Options](/library/options.md). |
| `medius_device_set_name(MediusDevice *dev, const char *name)` | Set the box's human-readable name (1 to 32 printable ASCII). See [Name](/library/options.md#set-name). |
| `medius_device_clear_name(MediusDevice *dev)` | Clear the name, back to the synthesized default. Read it back on [`MediusVersion.name`](/bindings/c/types.md#version). |

## Queries

_Read box state; each blocks for one reply_

See [Requests](/library/requests.md). Each blocks for the box's reply, writes a struct documented on [Types & errors](/bindings/c/types.md), and returns `MEDIUS_STATUS_ERR_QUERY_TIMEOUT` if no reply arrives.

| Function | Writes to `*out` |
| --- | --- |
| `medius_device_query_version(dev, MediusVersion *out)` | [`MediusVersion`](/bindings/c/types.md#version): protocol + firmware version. |
| `medius_device_query_health(dev, MediusHealth *out)` | [`MediusHealth`](/bindings/c/types.md#health): link, mouse, clone, injection flags. |
| `medius_device_device_info(dev, MediusDeviceInfo *out)` | [`MediusDeviceInfo`](/bindings/c/types.md#device-info): the cloned device's USB identity, kind, and product. |
| `medius_device_caps(dev, MediusCaps *out)` | [`MediusCaps`](/bindings/c/types.md#caps): mouse/keyboard capabilities. |
| `medius_device_query_rate(dev, MediusRate *out)` | [`MediusRate`](/bindings/c/types.md#rate): native report rate and poll period. |
| `medius_device_query_stats(dev, MediusStats *out)` | [`MediusStats`](/bindings/c/types.md#stats): box-side telemetry. |
| `medius_device_query_locks(dev, MediusLocks *out)` | [`MediusLocks`](/bindings/c/types.md#locks): the active locks (entry list). |
| `medius_device_query_catch(dev, MediusCatchState *out)` | [`MediusCatchState`](/bindings/c/types.md#catch-state): subscription mask + dropped count. |
| `medius_device_query_imperfect(dev, MediusImperfectStatus *out)` | [`MediusImperfectStatus`](/bindings/c/types.md#imperfect-status): 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`](/bindings/c/types.md#emit-pace-status): pacing mode + rate in effect. |
| `medius_device_counters(dev, MediusCountersSnapshot *out)` | [`MediusCountersSnapshot`](/bindings/c/types.md#counters): host-side wire counters. |

## Streams

_Subscribe to live input and logs_

Consuming events is covered on [Streams](/bindings/c/streams.md); the catch feature itself on [Catch](/library/catch.md) and logs on [Logs & counters](/library/diagnostics.md). `medius_device_catch_events` takes an OR of the `MEDIUS_CATCH_MASK_*` bits as its subscription.

| Function | Does |
| --- | --- |
| `medius_device_catch_events(MediusDevice *dev, MediusCatchMask mask, MediusEventStream **out)` | Subscribe to physical mouse/key/media events. |
| `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_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`](/bindings/c/types.md#log-line) (block / non-block / timed). |

## Buffered clip playback

_Preload a per-frame stream, box-clocked_

Build an entry stream with an opaque `MediusClipBuilder`, then drive playback through an opaque `MediusClip` handle from `medius_device_clip`. Each owns its allocation: free the builder with `medius_clip_builder_free` and the handle with `medius_clip_free`. Concept on [Clip](/library/clip.md).

#### BUILDER

| Function | Does |
| --- | --- |
| `medius_clip_builder_new() / _free(b) / _clear(b)` | Allocate / free / reset a builder. |
| `medius_clip_builder_gap(b, uint16_t frames)` | A gap run (0 = no-op). |
| `medius_clip_builder_move(b, dx, dy) / _wheel(b, dz)` | A cursor / wheel motion frame. |
| `medius_clip_builder_press / _release / _force_release(b, usage)` | A one-edge press / soft-release / force-release frame; `usage` is a [`MediusUsage`](/bindings/c/types.md#input) (button, key, or media). |
| `medius_clip_builder_edge(b, usage, action)` | A one-edge frame for any [`MediusUsage`](/bindings/c/types.md#input) with an explicit [`MediusAction`](/bindings/c/types.md#action). |
| `medius_clip_builder_frame(b, dx, dy, wheel, inputs, actions, n)` | A motion delta plus up to 8 edges on one frame: parallel [`MediusUsage`](/bindings/c/types.md#input) / [`MediusAction`](/bindings/c/types.md#action) arrays. Build the inputs with `medius_usage_button`/`_key`/`_media`. |

#### MOVE AND CLICK ON ONE FRAME

```c
MediusClipBuilder *b = medius_clip_builder_new();

/* move (+10, -4) AND press Left on the same frame */
MediusUsage  inputs[1]  = { medius_usage_button(MEDIUS_BUTTON_LEFT) };
MediusAction actions[1] = { MEDIUS_ACTION_PRESS };
medius_clip_builder_frame(b, 10, -4, 0, inputs, actions, 1);
```

#### HANDLE

| 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. |
| `medius_clip_set_autolock(clip, const MediusBlanket *scope, uintptr_t scope_len)` | The auto-lock scope: the [`MediusBlanket`](/bindings/c/types.md#blanket) groups `scope` points at (`NULL` / 0 = no lock). Set 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_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`](/bindings/c/types.md#clip-trigger): a [`MediusEdge`](/bindings/c/types.md#edge) of `on` drives a [`MediusClipAction`](/bindings/c/types.md#clip-action); `consume` hides the input from the game. |
| `medius_clip_unbind(clip, MediusUsage usage, MediusEdge edge) / _clear_triggers(clip)` | Remove the binding on that usage + edge; drop every binding. |
| `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_query_status(clip, out)` | Fill a [`MediusClipStatus`](/bindings/c/types.md#clip-status): ring depth, progress, and playback counters. |
| `medius_clip_query_config(clip, out)` | Fill a [`MediusClipSettings`](/bindings/c/types.md#clip-settings): auto-lock scope, loop/retain, finalized, and the trigger set. |

## Usage, motion & lock-target builders

_Make the value structs the calls take_

Pure constructors: no device, no wire traffic. See [Inject](/library/inject.md), [Move](/library/move.md), and [Lock](/library/lock.md).

| Function | Returns |
| --- | --- |
| `medius_usage_button(MediusButton button)` | [`MediusUsage`](/bindings/c/types.md#input) 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`](/bindings/c/types.md#motion) for `medius_device_move_axis`. |
| `medius_motion_wheel(int16_t delta)` | `MediusMotion` for a wheel scroll. |
| `medius_lock_target_axis(MediusLockTargetKind kind)` | [`MediusLockTarget`](/bindings/c/types.md#lock-target) for an axis (`X` / `Y` / `Wheel`). |
| `medius_lock_target_usage(MediusUsage usage)` | `MediusLockTarget` for a usage (button, key, or media). |

## Struct inspectors

_Read query / event results without the wire_

Helpers that interpret a struct you already have. They take it by value (or pointer) and do no I/O. Each mirrors the matching method on the [Rust type](/library/types.md).

| Function | Returns |
| --- | --- |
| `medius_locks_is_locked(const MediusLocks *locks, MediusLockTarget target, MediusLockDirection dir)` | `bool`: is that target/edge locked (`Both` needs both edges). See [Lock](/library/lock.md). |
| `medius_rate_native_hz(MediusRate rate, float *out_hz)` | `bool`: writes the native 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_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](/library/requests.md). |
| `medius_caps_has_keyboard(MediusCaps caps)` | `bool`: a keyboard interface is bound. |
| `medius_caps_is_composite(MediusCaps caps)` | `bool`: the clone is multi-HID-interface. |

## Global 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](/bindings/c/types.md#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_keepalive_cadence_ms()` | The default [keepalive](/library/guides/connection.md#keepalive) interval, in ms. |
| `medius_abi_version()` | The C ABI version (bumped on any breaking header change). |
| `medius_version_string()` | The crate version as a static NUL-terminated string. |
| `medius_flash(const char *port, const char *bin_path, bool host)` | Flash firmware via [esptool](https://github.com/espressif/esptool). `MEDIUS_FEATURE_FLASH` only; see [Flash](/library/features/flash.md) and [Build & features](/bindings/c/build.md). |

## Mock box

_Scriptable fake for tests, feature-gated_

All of these are wrapped in `#ifdef MEDIUS_FEATURE_MOCK` (the `mock` [cargo feature](https://doc.rust-lang.org/cargo/reference/features.html)). The concept lives on [Mock](/library/features/mock.md); turning the feature on is on [Build & features](/bindings/c/build.md).

| Function | Does |
| --- | --- |
| `medius_mock_new()` | A fresh mock that records commands and auto-answers 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 answers to each query. |
| `medius_mock_set_movement_riding(mock, bool enabled, uint32_t window_ms)` | Set the movement-riding window the mock reports. |
| `medius_mock_silent(MediusMockBox *mock)` | Stop answering queries for timeout tests (still records). |
| `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 device's log stream. |
| `medius_mock_push_motion(mock, uint8_t seq, MediusMotionEvent event)` | Push a [`MediusMotionEvent`](/bindings/c/types.md#motion-event) as a `Motion` catch event. |
| `medius_mock_push_usages(mock, uint8_t seq, const MediusUsageEvent *event)` | Push a [`MediusUsageEvent`](/bindings/c/types.md#usage-event) as a `Usages` catch event. |
| `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. |
