Clip
A clip is a ring of per-frame entries on the box. The box drains one entry per native frame into the same injection state that INJECT and MOVE feed.
Playback is box-clocked, independent of host send timing.
Like INJECT, a clip is field-generic and additive. One clip mixes mouse motion, buttons, keyboard and media, each routed to the matching interface, at the emit rate.
A clip plays on any clone. One tick is the cloned mouse's native frame. With no mouse, a tick is the rate OPTION(EMIT) fixes, or 1 ms if it fixes none. A field for a class with no interface on the clone is discarded.
QUERY(CLIP) reads back ring depth, playback state, and settings.
The retain flag on CLIP_SET picks the mode.
| Mode | Ring | Replay | Suits |
|---|---|---|---|
| streaming (default) | Each entry is freed as it plays, so the host appends to the tail while the head drains. | None. An emptied ring underruns. | Open-ended or generated input. |
| retained | Entries survive playback. | START, RESTART or loop without re-appending, once the clip is appended whole and marked FINALIZEd. | A fixed macro replayed 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 | PC→box | append a batch of entries to the ring |
0x13 | CLIP_CTRL | PC→box | drive the playback engine (start, stop, pause, ...) |
0x14 | CLIP_SET | PC→box | set a clip setting (auto-lock, loop, retain, ride) |
0x15 | CLIP_TRIGGER | PC→box | bind a native edge or a matched packet to an engine verb |
Entry format CLIP_APPEND bytes
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.
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | tag | u8 | 0x00 |
| 1 | count | u16 | frames the endpoint NAKs, byte-identical to an idle mouse; little-endian |
Motion, edges, raw reports and control transfers applied on one frame. flags selects which fields follow.
| Offset | Field | Type | Present when |
|---|---|---|---|
| 0 | flags | u8 | always; OR of the bits below, 0x40 and 0x80 reserved |
| + | dx, dy | i16 x 2 | flags & XY (0x01), per-frame cursor delta |
| + | wheel | i16 | flags & WHEEL (0x02), per-frame delta |
| + | pan | i16 | flags & PAN (0x08), per-frame delta |
| + | n | u8 | flags & EDGES (0x04), edge count (1-8) |
| + | edges | n x 4 bytes | each edge is [class u8][id u16][action u8] |
| + | n | u8 | flags & RAW (0x10), raw item count (1-8) |
| + | raw | n items | each a raw item |
| + | n | u8 | flags & XFER (0x20), transfer item count (≥1) |
| + | xfer | n items | each a transfer item |
The fields follow in the table's order. An entry is ≤512 bytes, one CLIP_APPEND payload.
An invalid entry (a reserved bit, a count out of range, a length >512 bytes) faults the clip when playback reaches it.
An edge reuses INJECT's tuple, so one clip drives every input class.
| Name | Value | id is |
|---|---|---|
| button | 0 | a button id (0=Left .. 4=Side2) |
| key | 1 | a HID keycode (0xE0-0xE7 = modifier) |
| media | 2 | a 16-bit Consumer usage |
| Action | Value | Effect |
|---|---|---|
| soft-release | 0 | Drop the override. A hold on the real device stays active. |
| press | 1 | Force the usage active. |
| force-release | 2 | Force the usage inactive, masking a hold on the real device too. |
An edge is a level: it holds until a later tick changes it, and the box NAKs while the level is unchanged.
Several flag bits stack their fields in one tick: a move, a pan and a press on the same frame, in one report.
0D 0A 00 FC FF 02 00 01 00 00 00 01 flags=XY|PAN|EDGES dx=+10 dy=-4 pan=+2 n=1 edge[class=0 button, id=0 Left, action=1 press]
| Frame | Entry | Clone emits |
|---|---|---|
0 | motion | cursor moves |
1 | Left press | left button down |
2-4 | gap 3 | nothing emitted (NAK); left stays down |
5 | Left release | left button up |
6 | motion | cursor moves |
7 | key A press | A down |
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)
Raw and transfer items Raw reports and control transfers on a content tick
A raw item is RAW's payload with its length, and a transfer item is TRANSFER's payload. Both need OPTION(IMPERFECT), checked as the tick plays. With the opt-in off, the item is discarded and counted in gated.
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | ep_num | u8 | cloned endpoint number |
| 1 | dir | u8 | 1 = IN, onto cloned IN endpoint ep_num; 2 = OUT, relayed to the real device |
| 2 | len | u16 | length of bytes, little-endian |
| 4 | bytes | u8[] | report, verbatim; sent in entry order, ahead of the tick's report |
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | ep | u8 | 0 = EP0, or a control endpoint the real device declares |
| 1 | setup | u8[8] | setup packet |
| 9 | data | u8[] | OUT data: wLength bytes when bit 7 of the first setup byte (the request type) is clear, absent when it is set |
The box queues transfers (1 KiB) and runs them one at a time. Each result arrives as a CLIP_XFER event, in drain order.
| Event | Effect |
|---|---|
PAUSE, the natural end | still run |
STOP, RESTART, CLEAR, a fault, a hard stop | dropped |
a 0xFE (no answer) status that took ≥250 ms | those behind it dropped into xfer_errs |
OPTION(IMPERFECT) turned off | dropped, each counted in gated |
| Report | On release |
|---|---|
| a raw IN item on the mouse, keyboard or media report the box injects into | emitted once in its native state, if that differs from the raw bytes outside the relative fields |
| a raw IN item on any other report | stays as sent, as after a RAW command |
The clip releases its held input on STOP, its natural end, a loop wrap, RESTART, CLEAR, a fault and a hard stop. A native report or a later raw report emitted first replaces the raw bytes.
One raw report OUT on endpoint 2, then a SET_REPORT with two bytes of OUT data on EP0.
10 01 02 02 03 00 A1 B2 C3 flags=RAW n=1 item[ep_num=2, dir=2 OUT, len=3, bytes=A1 B2 C3] 20 01 00 21 09 00 03 00 00 02 00 04 01 flags=XFER n=1 item[ep=0, setup=21 09 00 03 00 00 02 00 (wLength=2), data=04 01]
CLIP_APPEND Fill the ring
Appends whole entries to the ring's tail: while stopped to preload, or while playing (streaming mode) to top up. Opcode 0x12.
CLIP_APPEND 0x12 · payload = one or more whole entries
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | entries | bytes | a whole number of entries, back to back (up to the 512-byte frame limit) |
The frame SEQ doubles as an append sequence number: the box expects each CLIP_APPEND to be the previous SEQ plus one.
The link is fire-and-forget, so a lost frame shows as a SEQ gap. The box then marks the clip faulted in QUERY(CLIP). Recover with CLEAR, then rebuild.
Pack whole entries per frame. Never split one entry across two appends.
An append that doesn't fit the ring is dropped whole and faults the clip, never written as a partial entry.
Keep an append under QUERY(CLIP)'s free bytes. In streaming mode the head drains while the host appends to the tail, so free recovers as it plays. The ring is 64 KB with PSRAM and 16 KB without. Never assume a size.
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 faultedLibrary binding: ClipBuilder + append, which splits a large clip into whole-entry frames.
Append one content tick, cursor dx = 10 (a 5-byte entry, so LEN = 5):
SEQ is the append sequence. flags, dx and dy are the one XY content tick.
CLIP_CTRL Drive the playback engine
One byte of op selects an engine verb. Settings live on CLIP_SET. Opcode 0x13.
CLIP_CTRL 0x13 · payload [op u8]
Fire-and-forget
| 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; held levels stay down, motion stops |
3 | RESUME | continue a paused clip |
4 | RESTART | play from the head (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 fires on a native edge or a matched packet. CLEAR and FINALIZE are host-only.
QUERY(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, an overflow or an invalid entry broke the stream; CLEAR and rebuild |
In streaming mode, if the ring drains with no FINALIZE, the box idles (NAKs, holding its levels) and stays playing until refilled. A topping-up host or any keepalive resets the 1 s silence timer.
A finalized clip ends when the ring empties, or replays from the head if loop is set.
STOP an explicit STOP or CLEAR op silence a full 1 s of control-PC silence RESET a RESET command detach the real device unplugs link loss the inter-chip link drops re-clone the box clones a device again
Each halts playback and releases the clip's lock. A hard stop (silence, RESET, detach, link loss, re-clone) also clears the ring, settings and trigger set, which a host reloads.
A hard stop moves the session count.
Library binding: Device::clip().
Start playback (op = 0, a single-byte payload so LEN = 1):
CLIP_SET Set a clip setting
Sets one clip setting, OPTION-shaped. A setting holds until changed or cleared by a hard stop. QUERY(CLIP) reads them all back. Opcode 0x14.
CLIP_SET 0x14 · payload [id u8][value u8]
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | id | u8 | setting (table below) |
| 1 | value | u8 | new value |
| id | Setting | value | Effect |
|---|---|---|---|
0 | autolock | class bitmask | the native-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 |
3 | ride | 0 / 1 | the clip's motion waits to ride a native report; 0 (the default) plays it on the box's clock (below) |
autolock locks are clip-owned and released on STOP. A host LOCK is untouched. 0 = no auto-lock; 0x1F = every class.
| Bit | Mask | Locks |
|---|---|---|
b0 | 0x01 | the X and Y cursor axes |
b1 | 0x02 | the wheel and pan |
b2 | 0x04 | every mouse button |
b3 | 0x08 | every keyboard key |
b4 | 0x10 | every media usage |
A clip's motion bypasses movement riding by default, so it plays on the box's clock. ride puts it back on the ride: additive to native motion, and dropped while the mouse is still.
While rendering is on with a profile armed, a clip's cursor motion is rendered like a MOVE with no flag and rides the same way. ride then applies to the wheel and pan alone. Edges stay on the clip tick, ahead of the rendered motion.
Library binding: set_autolock, set_loop, set_retain, set_ride.
Turn looping on (id = 1, value = 1, so LEN = 2):
CLIP_TRIGGER Bind a native edge or a matched packet to an engine verb
Fires a CLIP_CTRL verb on the box from a native edge (as CATCH reports it) or a matched packet, with no host round-trip, so even the first emitted report is box-timed.
Triggers are a LOCK-shaped managed set. The class byte picks the kind. An input class makes one of up to 8 bindings, keyed by (class, id, edge). A traffic class makes one of up to 8 packet triggers. Opcode 0x15.
CLIP_TRIGGER 0x15 · payload [class u8][id u16][edge u8][action u8][flags u8]
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | class | u8 | input class (below) |
| 1 | id | u16 | usage within the class, little-endian; 0xFFFF = any |
| 3 | edge | u8 | firing edge (below) |
| 4 | action | u8 | the CLIP_CTRL op to fire, 0-5 |
| 5 | flags | u8 | bit0 present, bit1 consume (below) |
| Name | Value | id is |
|---|---|---|
| button | 0 | a button id |
| key | 1 | a HID keycode |
| media | 2 | a Consumer usage |
| any | 0xFF | ignored (any input fires) |
| Edge | Value | Fires on |
|---|---|---|
| both | 0 | press and release |
| press | 1 | the native press |
| release | 2 | the native release |
| Bit | Mask | Effect |
|---|---|---|
b0 | 0x01 | present: set to add or replace the binding, clear to remove it |
b1 | 0x02 | consume: lock the trigger usage while it stays active. Press edge only: a release-edge binding stores the flag and never acts on it |
class 0..2, 0xFF class 4..9
a native edge a packet on a traffic surface
button, key, media HID_IN, HID_OUT, VEND_INTR, VEND_BULK, CONTROL, EMIT
| |
v v
[ 8 input bindings ] [ 8 packet triggers ]
key (class, id, edge) key (class, id, dir, mlen, match, mask)
the most specific acts on the edge the most specific acts on the packet
| |
+------------> one CLIP_CTRL op <---------+
0 START .. 5 TOGGLE, run on the boxRe-sending the same (class, id, edge) key replaces that binding. Clearing present removes it.
The clear-all sentinel wipes the whole set, input bindings and packet triggers alike: class = 0xFF, id = 0xFFFF, edge = 0 (both), flags = 0.
Preload the ring (and FINALIZE it for a replayable macro) before binding.
Library binding: bind, unbind, clear_triggers.
Bind the A key's press to START (class = 1, id = 0x04, edge = 1 press, action = 0 start, flags = 0x01 present):
Packet triggers CLIP_TRIGGER with a traffic class
A traffic class in the class byte makes a packet trigger. A packet on that surface whose head matches runs the verb, as an input edge does. The match follows the six bytes every binding has. Opcode 0x15.
CLIP_TRIGGER 0x15 · packet payload 8 + 2 x mlen bytes
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | class | u8 | traffic class, 4-9 (below) |
| 1 | id | u16 | class address as in CATCH, little-endian; 0xFFFF = any |
| 3 | dir | u8 | packet direction: 0 both, 1 IN, 2 OUT |
| 4 | action | u8 | the CLIP_CTRL op to fire, 0-5 |
| 5 | flags | u8 | bit0 present, bit1 consume, bit2 RUN (below) |
| 6 | slen | u8 | selector length: below mlen with RUN, 0 without |
| 7 | mlen | u8 | match length, 0-16; 0 takes every packet on the address |
| 8 | match | u8[] | mlen head bytes to compare |
| 8+mlen | mask | u8[] | mlen mask bytes: a packet matches when each head byte ANDed with its mask byte equals the match byte; bytes past the mask are ignored |
| Class | Value | Carries | Head |
|---|---|---|---|
HID_IN | 4 | IN | the native report as it arrived, ahead of any rewrite |
HID_OUT | 5 | OUT | a report the PC writes to the real device |
VEND_INTR | 6 | IN, OUT | a relayed vendor interrupt packet |
VEND_BULK | 7 | IN, OUT | a relayed vendor bulk packet |
CONTROL | 8 | IN, OUT | a control request: the 8 setup bytes, then the first 8 OUT data bytes |
EMIT | 9 | IN | an emitted report, the clip's frames included; a clip's raw items pass unmatched |
| Bit | Mask | Effect |
|---|---|---|
b0 | 0x01 | present: set to add or replace the trigger, clear to remove it |
b1 | 0x02 | consume: drop every packet the trigger is the most specific match for, as a DROP rewrite rule would, before a rule sees it |
b2 | 0x04 | RUN: run the verb on the first of a run of matching packets. Without RUN, a trigger runs the verb on every packet it is the most specific match for |
The key is (class, id, dir, mlen, match, mask). The same key overwrites, and the same key with present clear removes. The box holds 8 packet triggers beside the 8 bindings, with 112 match bytes between them.
packet at a surface
|
v
[ packet triggers ] read the bytes as they arrived; only the most specific one acts
| |
| +--> the top-ranked trigger's verb, run on the frame clock's next tick
| +--> consume: the packet stops here
v
[ rewrite table ] sees the packet next, and applies the top-ranked rule
|
v
delivered to the game PC (IN) or the real device (OUT)| Name | Behaviour |
|---|---|
| surfaces | A trigger sees a packet at every surface a rewrite rule does, ahead of it. The two are independent: one packet can run a verb and then match a rule. |
| rank | Only the most specific trigger a packet matches acts on it, ranked in the rewrite table's order: an exact id over 0xFFFF, more masked bits over fewer, a named dir over both, then the earlier one. One verb runs per packet. |
| tick | The verb runs on the frame clock's next tick, within one frame of the matching packet (1 ms at the default pace). A verb that starts the clip plays its first entry on that tick. |
| consume | A consumed HID_IN report is the whole report, so a release edge in it is emitted with the next report. Consuming needs OPTION(IMPERFECT). Turning the opt-in off removes the consuming triggers. A trigger that only watches works with the opt-in off. |
| catch | CATCH reports a consumed HID_IN or OUT packet, whose taps sit ahead of the triggers. A consumed vendor IN packet or emitted report goes unreported. |
| run | With RUN, a device that repeats a held state every poll fires once per hold. The release is a second trigger matching the released bytes. The first slen match bytes select the stream within the address (a report ID) and the rest are the condition. A packet that fails the selector leaves the run as it was. |
| first packet | A run starts on the first matching packet the trigger sees. A trigger whose condition already holds when it is set fires on the next packet. A trigger matching the at-rest bytes of a device that reports every poll therefore fires once, when it is set. |
| shadowed | A trigger a more specific one outranks still tracks its run, so removing that trigger mid-hold fires nothing. |
| re-send | An identical re-send keeps the run and the count. An overwrite starts both again, and a bus reset or a configuration change starts every run again. |
| hits | Each trigger counts the packets it matched as the top-ranked trigger, saturating at 65535, read back in QUERY(CLIP). An outranked trigger's count stays still while it tracks its run. |
| lifetime | Packet triggers are clip config: soft state, cleared with the rest of it on a hard stop. |
A refused frame is dropped whole, with no reply, and leaves the set unchanged: a new key isn't added and an existing key keeps its trigger. Compare what QUERY(CLIP) reads back with what was sent.
| Frame | Why |
|---|---|
shorter than 8 + 2 x mlen, mlen >16, dir >2, action >5, a reserved flag bit | Malformed. |
| a ninth trigger, or match bytes past the pool of 112 | The set is full. |
consume with OPTION(IMPERFECT) off | Consuming is dropping traffic. |
consume on CONTROL | A control transfer always runs to completion. |
RUN on CONTROL, with id = 0xFFFF, or with dir = 0 | A run is over one stream: a report class, a concrete id and a concrete dir. |
RUN with slen at or above mlen, or slen above 0 without RUN | The bytes past the selector are the condition. |
RUN whose condition bytes have no masked bit | Every packet of the stream would meet it, so the run would never end. |
| a match bit outside its mask | No packet can match it. |
dir = 2 on HID_IN or EMIT, dir = 1 on HID_OUT | The class never carries that direction. |
No reply. Library binding: bind_packet, unbind_packet, clear_triggers.
Bind HID_IN interface 2, IN, to START, with consume and RUN (flags = 0x07): report ID 0x07 is the selector (slen = 1) and bit 5 of the next byte is the condition, so LEN = 12.
To remove the same trigger, keep the key and zero action, flags and slen.
04 02 00 01 00 00 00 02 07 20 FF 20 class=4 HID_IN id=2 dir=1 IN action=0 flags=0 slen=0 mlen=2 match=07 20 mask=FF 20