Medius - Native APIClip

CLIP

Preload input and let the box play it back, frame by frame

CLIP_APPEND preloads a sequence of per-frame entries into a ring on the box, then CLIP_CTRL drives the playback engine and the box drains one entry per native frame into the same injection state that INJECT and MOVE feed. Playback is box-clocked, so it carries no host scheduling jitter and no per-command send floor. Like INJECT it is field-generic and additive: one clip mixes mouse motion, buttons, keyboard, and media, each routed to its own interface, and follows movement riding and the emit rate. 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).

TWO MODES

A clip runs in one of two shapes, set by the retain flag on CLIP_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 FINALIZEd 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
OpcodeCommandDirectionDoes
0x12CLIP_APPENDPC→boxappend a batch of entries to the ring
0x13CLIP_CTRLPC→boxdrive the playback engine (start, stop, pause, ...)
0x14CLIP_SETPC→boxset a clip setting (auto-lock, loop, retain)
0x15CLIP_TRIGGERPC→boxbind 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.

OffsetFieldTypeNotes
0tagu80x00
1countu16frames 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.

OffsetFieldTypePresent when
0flagsu8always; OR of the bits below
+dx, dyi16 × 2flags & XY (0x01), cursor delta
+wheeli16flags & WHEEL (0x02)
+nu8flags & EDGES (0x04), edge count (max 8)
+edgesn × 4 byteseach edge is [class u8][id u16][action u8]
EDGES

An edge reuses INJECT's tuple, so one clip drives every input class.

CLASS
classValueid is
button0a button id (0=Left .. 4=Side2)
key1a HID keycode (0xE0-0xE7 = modifier)
media2a 16-bit Consumer usage
ACTION
ActionValueEffect
soft-release0Drop the override; a physical hold stays active.
press1Force the usage active.
force-release2Force 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.

FrameEntryThe PC sees
0motioncursor moves
1Left pressleft button down
2-4gap 3nothing sent (NAK); left stays down
5Left releaseleft button up
6motioncursor moves
7key A pressA 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 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 0x12.

CLIP_APPEND 0x12 · payload = one or more whole entries

Fire-and-forget

PAYLOAD
OffsetFieldTypeNotes
0entriesbytesa whole number of entries, back to back (up to the 512-byte frame limit)
DROP DETECTION

The frame 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, a lost frame shows up as a SEQ gap, and the box marks the clip faulted in QUERY(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)'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 + append, 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 0x13. There are no args: settings live on CLIP_SET, so a control frame is just the verb.

CLIP_CTRL 0x13 · payload [op u8]

Fire-and-forget

OPS
opNameEffect
0STARTplay from the ring head, applying the autolock setting
1STOPhalt playback and release the clip's auto-lock; buffered entries survive in retained mode
2PAUSEfreeze the playhead where it is; held levels stay down, motion stops
3RESUMEcontinue a paused clip from where it stopped
4RESTARTjump back to the head and play from the top (retained clip)
5TOGGLEstart if stopped, stop if playing
6CLEARstop and empty the ring, dropping every buffered entry and clearing a fault
7FINALIZEmark the buffered clip complete; the box stops treating an emptied ring as an underrun

Ops 0-5 (START through TOGGLE) double as theaction byte a CLIP_TRIGGER fires on a physical edge; CLEAR and FINALIZE are host-only.

STATE

QUERY(CLIP) reports one of four states.

ValueStateMeans
0idlenot playing; ring may hold a retained clip
1playingdraining one entry per frame
2pausedfrozen mid-clip by PAUSE, holding its levels
3faulteda 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 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, detach, link loss) also flushes the ring. The 1 s safety net and a RESET reach a clip like any other injection. With movement riding 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().

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-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). Opcode 0x14.

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

Fire-and-forget

SETTINGS
idSettingvalueEffect
0autolockclass bitmaskthe physical-input classes START locks while playing (below)
1loop0 / 1a finalized clip replays from the head instead of ending
2retain0 / 1keep 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 is untouched. 0 = no auto-lock; 0x1F = every class.

BitMaskLocks
b00x01the X and Y aim axes
b10x02the wheel
b20x04every mouse button
b30x08every keyboard key
b40x10every media usage

Library binding: set_autolock, set_loop, set_retain.

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 verb the instant the user physically moves an input (the same physical edge CATCH reports), with no host round-trip, so even the first emitted frame is box-timed. Triggers are a LOCK-shaped managed set of up to eight bindings, keyed by (class, id, edge). Opcode 0x15.

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

Fire-and-forget

PAYLOAD
OffsetFieldTypeNotes
0classu8input class (below)
1idu16usage within the class, little-endian; 0xFFFF = any
3edgeu8which edge fires (below)
4actionu8the CLIP_CTRL op to fire, 0-5
5flagsu8bit0 present, bit1 consume (below)
CLASS
classValueid is
button0a button id
key1a HID keycode
media2a Consumer usage
any0xFFignored (any input fires)
EDGE
EdgeValueFires on
both0press and release
press1the physical press
release2the physical release
FLAGS
BitMaskEffect
b00x01present: set to add or replace the binding, clear to remove it
b10x02consume: 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 FINALIZEd) before you bind.

Library binding: bind, unbind, clear_triggers.

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  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+