Lock
Weigh one physical input by classLOCK sets how much of the physical device reaches the game PC on one input. Host injection drives that same input at full strength whatever the scale says.
0 block --X nothing reaches the PC 40 -.-> 40 of every 100 counts, carried across reports 100 pass --> all of it, byte for byte 255 max ==> 2.55x, clamped to the field's declared range injection --> full strength whatever the scale
A relative direction weighs physical motion against the bearing, in one of two geometries.
| Class | Value | id is |
|---|---|---|
| button | 0 | a button id (0=Left .. 4=Side2) |
| key | 1 | a HID keyboard usage (0xE0-0xE7 = modifier) |
| media | 2 | a 16-bit Consumer usage |
| axis | 3 | 0=X, 1=Y, 2=wheel (the sign is the direction) |
LOCK
Block, pass, or amplifyA momentary usage shares INJECT's (class, id) space, so a button locks exactly like a key. Opcode 0x0A.
LOCK 0x0A · payload 5 bytes
Fire-and-forget
| Offset | Field | Type | Notes |
|---|---|---|---|
| 0 | class | u8 | the input class, as the table above |
| 1 | id | u16 | which input within the class, little-endian; 0xFFFF = a blanket |
| 3 | direction | u8 | which sign or which edge, 0-4 |
| 4 | scale | u8 | percent of the physical value kept, 0-255 |
| Name | Value | Effect |
|---|---|---|
| block | 0 | None of the physical value reaches the PC. |
| pass | 100 | All of it, byte for byte. |
| max | 255 | 2.55x. Anything above 100 amplifies. |
| Rule | What the box does |
|---|---|
| combine | A delta picks up one fixed-sign scale and one relative scale, multiplied. A 0 in either blocks. |
| carry | A physical delta at 1 kHz is almost always +/-1, so the dropped fraction is banked per axis and sign: 40 on a run of -1 emits 0 0 -1 0 -1. |
| saturate | A weighed value clamps to the field's declared range, never wraps, and forfeits the fraction it could not carry. |
| one bit | A button, key or media usage locks below 100 and passes at 100. The box stores that, not the number sent. |
Weighing runs before injected motion drains, so a gain that fills an axis leaves the box's own motion no room. It is held, not dropped, and leaves as one report once there is room.
The threshold is the field's declared maximum divided by the gain: at 255 on an 8-bit axis the field fills at a physical delta of 50, and the clamp bites at 51.
MOVE_RIDE does not bound it: a moved report re-opens the ride window, and riding never governed the immediate accumulator.
| Direction | Value | Axis | Button, key, media |
|---|---|---|---|
| both | 0 | Both signs. | Press and release. |
| positive | 1 | Positive sign only (+). | Press only (0 to 1). |
| negative | 2 | Negative sign only (-). | Release only (1 to 0). |
| with | 3 | The sign the box is injecting. | Refused on a button or key. Media locks the whole usage, as 0 would. |
| against | 4 | The sign opposing it. | Refused on a button or key. Media locks the whole usage, as 0 would. |
Media has no sign and no edge, so RESP(LOCKS) always reports its direction as 0. Every shipped client refuses 3 and 4 on all three momentary classes rather than depend on which.
On an axis, 0 writes the scale to the two fixed-sign slots and a pass to the relative pair: written to all four it would land at 50% with no bearing and 25% with one. An unlock clears all four.
An id of 0xFFFF addresses the whole class in one command.
| Class | Covers | Direction | Reads back as |
|---|---|---|---|
| button | All five buttons. | As a named button. | One entry per button and edge, under its own id. |
| key | Every keyboard usage. | Honoured: 1 blocks press edges, 2 release edges, 0 both. | One entry per blocked edge, id 0xFFFF. |
| media | Every Consumer usage. | Ignored. | One entry, id 0xFFFF, direction 0. |
| axis | X, Y, and the wheel. | As a named axis. | One entry per axis and direction, under its own id. |
| Blanket | How the box holds it |
|---|---|
| button, axis | Expands as it lands, so a later command on one member overwrites just that member. |
| key, media | One flag, which a per-usage unlock does not lift. |
A scale weighs the physical device. Host injection drives the same field at full strength, so a blocked axis still moves when the box moves it.
unlock the matching unlock (scale = 100); direction 0 clears
all four slots of that target
silence ~1 s with no control-PC frame
RESET a RESET command
link loss the inter-chip link drops
detach the real device goes awayHold one with a keepalive if it has to outlast a second of quiet. Injection auto-clears on the same events, described on Injection.
Scales are PC-owned and never visible to the game PC. QUERY(LOCKS) reads the active set; the HEALTH LOCK_ON bit is set while anything is off a full pass.
Library bindings: scale, lock, unlock, and scale_all, which sends this frame for buttons, keys and media and per-axis frames for X, Y and the wheel.
Block the wheel's negative (scroll-down) sign: class = 3 (axis), id = 2 (wheel), direction = 2, scale = 0:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+ | A5 | 0A | 00 | 05 00 | 03 | 02 00 | 02 | 00 | lo hi | +--------+--------+--------+--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | class | id | dir | scale | CRC16 | +--------+--------+--------+--------+--------+--------+--------+--------+--------+
Keep 40% of leftward movement while the bearing on X is positive: direction = 4 (against), scale = 40. A physical -10 then leaves as -4, and as -10 again once the bearing lapses:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+ | A5 | 0A | 01 | 05 00 | 03 | 00 00 | 04 | 28 | lo hi | +--------+--------+--------+--------+--------+--------+--------+--------+--------+ | SOF | TYPE | SEQ | LEN | class | id | dir | scale | CRC16 | +--------+--------+--------+--------+--------+--------+--------+--------+--------+
The bearing
The direction the box is currently injectingwith and against weigh a physical delta by its sign relative to the bearing. The box reads it at the merge point, where the pending injection and the arriving report are in hand at once.
MOVE(+10) MOVE(+10) idle
| |
v v
----+----------------+---------------------------------------> t
|<-- restarted ->|<------ window ------->|
bearing +X bearing +X no bearingEach axis carries its own bearing and its own deadline, set by OPTION(BEARING). A window of 0 holds no bearing at all, so with and against stop weighing without being cleared.
| Event | Effect on the bearing |
|---|---|
An injected delta on that axis, from a MOVE on either accumulator or from a clip | Sets it, and restarts the deadline. A zero component leaves that axis standing. |
| Injected motion still owed on that axis, held for a ride or queued behind a slow emit gate | Keeps restarting the deadline, and points at the net pending delta rather than the last one sent. |
| The window elapses | That axis has no bearing. Both relative directions stop applying and it passes at its fixed-sign scale alone. |
MOVE with DISCARD | Cleared, then set again by that same command's own delta. Only a zero-delta discard leaves the axis without one. |
| Motion held for a ride goes stale | Cleared with the held motion, on the next native move that would have carried it. |
A change to OPTION(MOVE_RIDE) | Cleared, along with the held motion it pointed with. |
A change to OPTION(BEARING) | Cleared, and the banked carry with it. |
RESET, ~1 s of control-PC silence, link loss, detach | Cleared with the rest of the PC-owned state. |
Geometry
Per axis, or projected onto the bearingSet by OPTION(BEARING). The two modes agree while the bearing has one nonzero component, and differ once both are.
| Mode | Value | Weighs |
|---|---|---|
| per axis | 0 | Each axis against its own bearing, independently. |
| vector | 1 | Only the part of the movement lying along the injected direction. |
the bearing is down-right at 45 degrees, the device moves straight right
o-----------> h (+12, 0)
\ / across b (+6, -6) untouched by with / against
\ /
\ /
\ /
\ /
+ along b (+6, +6) weighed by with / againstIn vector mode the relative pair addresses the XY bearing as a whole: the box takes the lower of the X and Y scales and applies it to both.
RESP(LOCKS) reports that effective number on both axes, so a readback replayed as commands levels the higher stored byte down to it.
The wheel is never projected; it weighs against its own bearing.
| Stage | Reads | Acts on |
|---|---|---|
| 1. project | The relative pair, one number for both axes. | The part lying along the bearing. What survives is written back to both axes. |
| 2. weigh | Each axis's own fixed pair, chosen by the sign now standing in the field. | What stage 1 left, not the delta the report carried. |
A fixed-sign scale governs what reaches the game PC, so it covers the across part as well as the physical delta. Only the relative pair is redistributed; the fixed pair is per axis in both modes.
bearing +X +Y
LOCK(axis X, with, 0) LOCK(axis Y, with, 0) the bearing's relative scale is 0
LOCK(axis Y, negative, 0) Y's negative sign is blocked
X Y
physical +12 0 straight right, nothing on Y
stage 1 along b +6 +6 scaled by 0, so it goes
across b +6 -6 left alone, and is all that remains
leaves +6 -6
stage 2 scale 100 0 on the sign now in each field
emitted +6 0Per axis, stage 2 would have left Y alone: the report carried nothing there. Swap that block for a scale of 200 and the same -6 leaves as -12.