Catch
Stream the physical mouse, keyboard, and media input to the PCCATCH subscribes to the user's real input. While subscribed, the box pushes a MOTION_EVENT for movement and the wheel, and a USAGE_EVENT for buttons, keys, and media, captured at the merge point before any LOCK suppression or injection, so you can lock an input and still see it to rebind it. Subscribing is fire-and-forget; the box streams until you unsubscribe.
CATCH
Subscribe to the physical-input event streamCATCH sends a one-byte class mask. A non-zero mask subscribes; 0 unsubscribes. Opcode 0x0B.
CATCH 0x0B · payload 1 byte
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | mask | u8 | which classes to stream (see below); 0 = unsubscribe |
Each bit turns on one class of change. The mask only chooses which reports trigger an event (every event carries the full held-usage snapshot), so a buttons-only subscription stays sparse even though the mouse reports at ~1 kHz. Combine bits with OR; 0x1F subscribes to every class.
| Class | Bit | Triggers on |
|---|---|---|
| Motion | 0x01 | a non-zero X or Y delta (a MOTION_EVENT). |
| Wheel | 0x02 | a non-zero wheel delta (a MOTION_EVENT). |
| Buttons | 0x04 | a mouse-button edge (a USAGE_EVENT). |
| Keys | 0x08 | a keyboard key or modifier change (a USAGE_EVENT). |
| Media | 0x10 | a media (Consumer) usage change (a USAGE_EVENT). |
The stream reports the user's physical input, never your injected overrides, and it reads the report before LOCK clamps it, so a locked axis or blocked button is still reported here. That's the intercept-and-rebind loop: lock an input to hide it from the game, catch it to act on it.
The box streams from the moment a non-zero mask arrives until you send 0. The subscription is PC-owned and clears on the same triggers as injection: control-PC silence (the ~1 s timeout), a RESET, a mouse detach, or inter-chip link loss, plus an explicit unsubscribe. The host library holds an open subscription alive with its keepalive (re-asserting it after a device-side blip) and across a reconnect. QUERY(CATCH) reads the active mask and a dropped-event count; the HEALTH CATCH_ON bit is set while subscribed. Library binding: catch_events.
Subscribe to every class (mask = 0x1F):
+--------+--------+--------+--------+--------+--------+ | A5 | 0B | 00 | 01 00 | 1F | lo hi | +--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | mask | CRC16 | +--------+--------+--------+--------+--------+--------+
MOTION_EVENT
One physical relative-axis snapshot, box → PCWhile a subscription with Motion or Wheel is active the box pushes a MOTION_EVENT for each physical report whose motion changed. It's unsolicited (there's no QUERY to correlate), so SEQ is a rolling per-event counter shared with USAGE_EVENT: a host detects dropped events as SEQ gaps. Opcode 0x0C.
MOTION_EVENT 0x0C · payload 6 bytes
Unsolicited
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | dx | i16 | physical X this report; + = right, little-endian |
| 2 | dy | i16 | physical Y this report; + = down, little-endian |
| 4 | dz | i16 | physical wheel delta this report; + = up, little-endian |
Delivery is best-effort: under back-pressure the box drops events (counted in QUERY(CATCH)) rather than stalling the report path, so the stream never delays the game-PC-facing reports.
The user moves +10 right, no vertical or wheel motion (dx = 10):
+--------+--------+--------+--------+--------+--------+--------+--------+ | A5 | 0C | 2A | 06 00 | 0A 00 | 00 00 | 00 00 | lo hi | +--------+--------+--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | dx | dy | dz | CRC16 | +--------+--------+--------+--------+--------+--------+--------+--------+
USAGE_EVENT
One physical held-usage snapshot, box → PCWhile a subscription with Buttons, Keys, or Media is active the box pushes a USAGE_EVENT when that class changes: a class-tagged snapshot of the usages currently held, so a mouse-button press and a key press have the same shape. It's a full snapshot, not edge deltas, so a dropped frame self-corrects on the next one; diff successive snapshots per class for press / release edges. Opcode 0x0F.
USAGE_EVENT 0x0F · payload 1 + 3n bytes
Unsolicited
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | n | u8 | number of held usages that follow |
| + | class | u8 | per usage: 0=button 1=key 2=media (as INJECT) |
| + | id | u16 | the held usage's id (a button id, HID keycode with 0xE0-0xE7 modifiers, or Consumer usage), little-endian |
Each entry is 3 bytes; the snapshot is n of them, all one class (one physical report is one class), so a Keys event lists every held key and a Buttons event every held button. Best-effort like MOTION_EVENT: dropped events are counted in QUERY(CATCH).
Left Shift held while pressing A (a keys snapshot, two usages both class = 1: Left Shift id = 0xE1, then A id = 0x04):
+--------+--------+--------+--------+--------+----------+----------+--------+ | A5 | 0F | 2B | 07 00 | 02 | 01 E1 00 | 01 04 00 | lo hi | +--------+--------+--------+--------+--------+----------+----------+--------+ | SOF | TYPE | SEQ | LEN | n | usage[0] | usage[1] | CRC16 | +--------+--------+--------+--------+--------+----------+----------+--------+