<!-- Source: https://medius.k4tech.net/native/commands/clip -->
# CLIP

_Preload input and let the box play it back, frame by frame_

[`CLIP_APPEND`](/native/commands/clip.md#append) preloads a sequence of per-frame entries into a ring on the box, then [`CLIP_CTRL`](/native/commands/clip.md#ctrl) drives the playback engine and the box drains one entry per native frame into the same [injection state](/native/injection.md#state) that [`INJECT`](/native/commands/inject.md) and [`MOVE`](/native/commands/move.md) feed. Playback is box-clocked, so it carries no host scheduling jitter and no per-command send floor. Like [`INJECT`](/native/commands/inject.md) it is field-generic and [additive](/native/injection.md#state): one clip mixes mouse motion, buttons, keyboard, and media, each routed to its own interface, and follows [movement riding](/native/commands/option.md#move-ride) and [the emit rate](/native/commands/option.md#emit). A clip needs a cloned mouse, whose native report tick is the box's frame clock; read the ring depth, playback state, and settings back with [`QUERY(CLIP)`](/native/commands/requests.md#clip).

#### TWO MODES

A clip runs in one of two shapes, set by the `retain` flag on [`CLIP_SET`](/native/commands/clip.md#set).

-   **Streaming** (default): the box frees each entry as it plays, so a real-time host keeps appending to the tail while the head drains. Good for open-ended or generated input; an emptied ring underruns.
-   **Retained**: the box keeps entries after playing them, so once you've appended the whole clip and marked it [`FINALIZE`](/native/commands/clip.md#ctrl)d you can `START`, `RESTART`, or `loop` it as many times as you like without re-appending. Good for a fixed macro you replay on a trigger.

```
control PC                     box  (drains one entry per native frame)
      |                       +-----------------------------------------+
      |  CLIP_APPEND [e0][e1] |  ring [e0][e1][e2][e3][e4] ...          |
      | --------------------> |    |                                    |
      |  CLIP_SET loop/retain |    | one entry / frame                  |
      |  CLIP_TRIGGER bind    |    v                                    |
      |  CLIP_CTRL START      |  injection state --> mouse    report    |
      | --------------------> |                  --> keyboard report    |
      |                       |                  --> media    report    |
      |  QUERY(CLIP)          |                                         |
      | --------------------> |  ring depth + state + settings          |
      | <-------------------- |                                         |
      |                       +-----------------------------------------+
                box-clocked: host does no per-frame timing
```

| Opcode | Command | Direction | Does |
| --- | --- | --- | --- |
| `0x12` | [`CLIP_APPEND`](/native/commands/clip.md#append) | PC→box | append a batch of entries to the ring |
| `0x13` | [`CLIP_CTRL`](/native/commands/clip.md#ctrl) | PC→box | drive the playback engine (start, stop, pause, ...) |
| `0x14` | [`CLIP_SET`](/native/commands/clip.md#set) | PC→box | set a clip setting (auto-lock, loop, retain) |
| `0x15` | [`CLIP_TRIGGER`](/native/commands/clip.md#trigger) | PC→box | bind a physical edge to an engine verb |

## Entry format

_The bytes CLIP_APPEND carries_

A clip is a byte stream of variable-length entries, little-endian. The first byte of each entry is a tag: `0x00` is a `gap run`, any other value is a `content tick`'s flags. One entry is one native frame.

#### GAP RUN

Emit nothing for `count` frames. The endpoint NAKs, byte-identical to an idle mouse, so a gap is the faithful way to hold still or pace between actions.

| Offset | Field | Type | Notes |
| --- | --- | --- | --- |
| 0 | `tag` | `u8` | `0x00` |
| 1 | `count` | `u16` | frames to NAK, little-endian |

#### CONTENT TICK

A motion delta and/or a list of edges applied on one frame. The `flags` byte (nonzero, so it can't be mistaken for a gap tag) selects which fields follow.

| Offset | Field | Type | Present when |
| --- | --- | --- | --- |
| 0 | `flags` | `u8` | always; OR of the bits below |
| + | `dx`, `dy` | `i16 × 2` | `flags & XY (0x01)`, cursor delta |
| + | `wheel` | `i16` | `flags & WHEEL (0x02)` |
| + | `n` | `u8` | `flags & EDGES (0x04)`, edge count (max 8) |
| + | `edges` | `n × 4 bytes` | each edge is `[class u8][id u16][action u8]` |

#### EDGES

An edge reuses [`INJECT`](/native/commands/inject.md#inject)'s tuple, so one clip drives every input class.

#### CLASS

| `class` | Value | `id` is |
| --- | --- | --- |
| button | `0` | a [button id](/native/commands/usage.md#buttons) (0=Left .. 4=Side2) |
| key | `1` | a [HID keycode](/native/commands/usage.md#keycodes) (0xE0-0xE7 = modifier) |
| media | `2` | a 16-bit [Consumer usage](/native/commands/usage.md#consumer) |

#### ACTION

| Action | Value | Effect |
| --- | --- | --- |
| soft-release | `0` | Drop the override; a physical hold stays active. |
| press | `1` | Force the usage active. |
| force-release | `2` | Force it inactive, masking a physical hold too. |

An edge is a level: it sticks until a later tick changes it, and the box NAKs while it is held still. Motion (`dx`/`dy`/`wheel`) is a per-frame delta.

#### MOTION AND EDGES ON ONE TICK

Set several flag bits and the fields stack in a single tick, so a move and a press land on the same frame and the PC sees one report. That is what keeps "aim and hold fire" faithful: motion every frame, the fire button pressed on the frame it goes down.

```
05 0A 00 FC FF 01 00 00 00 01
   flags=XY|EDGES   dx=+10 dy=-4   n=1   edge[class=0 button, id=0 Left, action=1 press]
```

#### A CLIP IS A TIMELINE

Entries play out one per frame, left to right.

| Frame | Entry | The PC sees |
| --- | --- | --- |
| `0` | motion | cursor moves |
| `1` | Left press | left button down |
| `2-4` | gap 3 | nothing sent (NAK); left stays down |
| `5` | Left release | left button up |
| `6` | motion | cursor moves |
| `7` | key `A` press | `A` down |

#### EXAMPLE ENTRIES

The bytes for three example entries: move up-right, press `A`, idle 5 frames.

```
01 0A 00 F6 FF        flags=XY,   dx=+10  dy=-10
04 01 01 04 00 01     flags=EDGES n=1  edge[class=1 key, id=0x04 'A', action=1 press]
00 05 00              tag=gap,    count=5  (NAK 5 frames)
```

## CLIP_APPEND

_Fill the ring_

Append a batch of whole [entries](/native/commands/clip.md#entries) to the tail of the ring. Send it while stopped to preload, or while playing (streaming mode) to keep topping up in real time. [Opcode](/native/frame.md#opcodes) `0x12`.

```text
CLIP_APPEND 0x12 · payload = one or more whole entries
```

_Fire-and-forget_

#### PAYLOAD

| Offset | Field | Type | Notes |
| --- | --- | --- | --- |
| 0 | `entries` | `bytes` | a whole number of [entries](/native/commands/clip.md#entries), back to back (up to the 512-byte frame limit) |

#### DROP DETECTION

The frame [`SEQ`](/native/frame.md#seq) doubles as an append sequence number: the box expects each `CLIP_APPEND` to be the previous `SEQ` plus one. Because the link is [fire-and-forget](/native/injection.md#fire-and-forget), a lost frame shows up as a `SEQ` gap, and the box marks the clip `faulted` in [`QUERY(CLIP)`](/native/commands/requests.md#clip) so the host re-syncs (`CLEAR`, then rebuild) instead of playing a stream with a hole in it. Pack whole entries per frame; never split one entry across two appends.

#### FLOW CONTROL

An append that doesn't fit the ring is dropped whole and faults the clip, never written as a partial entry that would desync the stream. Keep an append under [`QUERY(CLIP)`](/native/commands/requests.md#clip)'s `free` bytes to avoid it: in streaming mode the box drains from the head while you append to the tail, so a real-time host tops up as `free` opens back up.

```
  the ring, read by QUERY(CLIP):

       free                        buffered (total)
  +----------------+----------------------------------------+
  |    (append     | [e5][e6][e7][e8][e9] ...               | --> drained
  |    here, <=    |     buffered, not yet played           |     1 / frame
  |    free)       |                                        |
  +----------------+----------------------------------------+
   append > free  -->  dropped whole + clip faulted
```

Library binding: [`ClipBuilder`](/library/clip.md#builder) + [`append`](/library/clip.md#handle), which splits a large clip into whole-entry frames for you.

#### EXAMPLE

Append one content tick, cursor `dx = 10` (a 5-byte entry, so `LEN = 5`):

```
+--------+--------+--------+--------+--------+--------+--------+--------+
| A5     | 12     | 00     | 05 00  | 01     | 0A 00  | 00 00  | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | flags  | dx     | dy     | CRC16  |
+--------+--------+--------+--------+--------+--------+--------+--------+
                    ^ append seq     \- one XY content tick --/
```

## CLIP_CTRL

_Drive the playback engine_

One byte of `op` selects an engine verb. [Opcode](/native/frame.md#opcodes) `0x13`. There are no args: settings live on [`CLIP_SET`](/native/commands/clip.md#set), so a control frame is just the verb.

```text
CLIP_CTRL 0x13 · payload [op u8]
```

_Fire-and-forget_

#### OPS

| op | Name | Effect |
| --- | --- | --- |
| `0` | `START` | play from the ring head, applying the `autolock` setting |
| `1` | `STOP` | halt playback and release the clip's auto-lock; buffered entries survive in retained mode |
| `2` | `PAUSE` | freeze the playhead where it is; held levels stay down, motion stops |
| `3` | `RESUME` | continue a paused clip from where it stopped |
| `4` | `RESTART` | jump back to the head and play from the top (retained clip) |
| `5` | `TOGGLE` | start if stopped, stop if playing |
| `6` | `CLEAR` | stop and empty the ring, dropping every buffered entry and clearing a fault |
| `7` | `FINALIZE` | mark the buffered clip complete; the box stops treating an emptied ring as an underrun |

Ops `0`\-`5` (`START` through `TOGGLE`) double as the`action` byte a [`CLIP_TRIGGER`](/native/commands/clip.md#trigger) fires on a physical edge; `CLEAR` and `FINALIZE` are host-only.

#### STATE

[`QUERY(CLIP)`](/native/commands/requests.md#clip) reports one of four states.

| Value | State | Means |
| --- | --- | --- |
| `0` | `idle` | not playing; ring may hold a retained clip |
| `1` | `playing` | draining one entry per frame |
| `2` | `paused` | frozen mid-clip by `PAUSE`, holding its levels |
| `3` | `faulted` | a `SEQ` gap or overflow desynced the stream; `CLEAR` and rebuild |

#### UNDERRUN

In streaming mode, if the ring drains with no `FINALIZE`, the box idles (NAKs, holding its levels) and stays `playing` until you refill it; a topping-up host or any keepalive holds the clip alive. A finalized clip ends when the ring empties, or replays from the head if [`loop`](/native/commands/clip.md#set) is set.

#### STOPS ON

```
STOP        an explicit STOP or CLEAR op
silence     a full 1 s of control-PC silence
RESET       a RESET command
detach      the cloned mouse unplugs
link loss   the inter-chip link drops
```

Each halts playback and releases the clip's lock; a hard stop (`silence`, [`RESET`](/native/commands/admin.md#reset), detach, link loss) also flushes the ring. The [1 s safety net](/native/injection.md#safety) and a [`RESET`](/native/commands/admin.md#reset) reach a clip like any other injection. With [movement riding](/native/commands/option.md#move-ride) on, clip motion rides native reports and is additive to the user's own movement, so the frame-exact use case runs riding off.

Library binding: [`Device::clip()`](/library/clip.md).

#### EXAMPLE

Start playback (`op = 0`, a single-byte payload so `LEN = 1`):

```
+--------+--------+--------+--------+--------+--------+
| A5     | 13     | 00     | 01 00  | 00     | lo hi  |
+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | op     | CRC16  |
+--------+--------+--------+--------+--------+--------+
```

Every op has the same shape; only the `op` byte changes.

## CLIP_SET

_Set a clip setting_

Set one of the clip's settings, [OPTION](/native/commands/option.md)\-shaped: an `id` byte picks the setting, a `value` byte carries it. A setting sticks until you change it or the clip is torn down; read them all back with [`QUERY(CLIP)`](/native/commands/requests.md#clip). [Opcode](/native/frame.md#opcodes) `0x14`.

```text
CLIP_SET 0x14 · payload [id u8][value u8]
```

_Fire-and-forget_

#### SETTINGS

| id | Setting | value | Effect |
| --- | --- | --- | --- |
| `0` | `autolock` | class bitmask | the physical-input classes `START` locks while playing (below) |
| `1` | `loop` | `0` / `1` | a finalized clip replays from the head instead of ending |
| `2` | `retain` | `0` / `1` | keep entries after playing so `START` / `RESTART` can replay them |

#### AUTO-LOCK

The `autolock` value is a bitmask of the physical-input classes `START` locks while the clip plays (clip-owned, released on `STOP`), leaving the ones you don't name free. A host [`LOCK`](/native/commands/lock.md) is untouched. `0` = no auto-lock; `0x1F` = every class.

| Bit | Mask | Locks |
| --- | --- | --- |
| `b0` | `0x01` | the X and Y aim axes |
| `b1` | `0x02` | the wheel |
| `b2` | `0x04` | every mouse button |
| `b3` | `0x08` | every keyboard key |
| `b4` | `0x10` | every media usage |

Library binding: [`set_autolock`](/library/clip.md#handle), [`set_loop`](/library/clip.md#handle), [`set_retain`](/library/clip.md#handle).

#### EXAMPLE

Turn looping on (`id = 1`, `value = 1`, so `LEN = 2`):

```
+--------+--------+--------+--------+--------+--------+--------+
| A5     | 14     | 00     | 02 00  | 01     | 01     | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | id     | value  | CRC16  |
+--------+--------+--------+--------+--------+--------+--------+
```

## CLIP_TRIGGER

_Bind a physical edge to an engine verb_

Fire a [`CLIP_CTRL`](/native/commands/clip.md#ctrl) verb the instant the user physically moves an input (the same physical edge [`CATCH`](/native/commands/catch.md) reports), with no host round-trip, so even the first emitted frame is box-timed. Triggers are a [`LOCK`](/native/commands/lock.md)\-shaped managed set of up to eight bindings, keyed by `(class, id, edge)`. [Opcode](/native/frame.md#opcodes) `0x15`.

```text
CLIP_TRIGGER 0x15 · payload [class u8][id u16][edge u8][action u8][flags u8]
```

_Fire-and-forget_

#### PAYLOAD

| Offset | Field | Type | Notes |
| --- | --- | --- | --- |
| 0 | `class` | `u8` | input class (below) |
| 1 | `id` | `u16` | usage within the class, little-endian; `0xFFFF` = any |
| 3 | `edge` | `u8` | which edge fires (below) |
| 4 | `action` | `u8` | the [`CLIP_CTRL`](/native/commands/clip.md#ctrl) op to fire, `0`\-`5` |
| 5 | `flags` | `u8` | bit0 present, bit1 consume (below) |

#### CLASS

| `class` | Value | `id` is |
| --- | --- | --- |
| button | `0` | a [button id](/native/commands/usage.md#buttons) |
| key | `1` | a [HID keycode](/native/commands/usage.md#keycodes) |
| media | `2` | a [Consumer usage](/native/commands/usage.md#consumer) |
| any | `0xFF` | ignored (any input fires) |

#### EDGE

| Edge | Value | Fires on |
| --- | --- | --- |
| both | `0` | press and release |
| press | `1` | the physical press |
| release | `2` | the physical release |

#### FLAGS

| Bit | Mask | Effect |
| --- | --- | --- |
| `b0` | `0x01` | present: set to add or replace the binding, clear to remove it |
| `b1` | `0x02` | consume: swallow the physical edge so the app never sees it |

A binding is keyed by `(class, id, edge)`, so re-sending the same key replaces it and clearing `present` removes it. To wipe the whole set in one frame send the clear-all sentinel: `class = 0xFF`, `id = 0xFFFF`, `edge = 0` (both), `flags = 0`. Preload the ring (and, for a replayable macro, mark it [`FINALIZE`](/native/commands/clip.md#ctrl)d) before you bind.

Library binding: [`bind`](/library/clip.md#handle), [`unbind`](/library/clip.md#handle), [`clear_triggers`](/library/clip.md#handle).

#### EXAMPLE

Bind the `A` key's press to `START` (`class = 1`, `id = 0x04`, `edge = 1` press, `action = 0` start, `flags = 0x01` present):

```
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| A5     | 15     | 00     | 06 00  | 01     | 04 00  | 01     | 00     | 01     | lo hi  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| SOF    | TYPE   | SEQ    | LEN    | class  | id     | edge   | action | flags  | CRC16  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
```
