Medius - Native APILock

Lock

Weigh one physical input by class

LOCK 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.

ClassValueid is
button0a button id (0=Left .. 4=Side2)
key1a HID keyboard usage (0xE0-0xE7 = modifier)
media2a 16-bit Consumer usage
axis30=X, 1=Y, 2=wheel (the sign is the direction)

LOCK

Block, pass, or amplify

A 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

PAYLOAD
OffsetFieldTypeNotes
0classu8the input class, as the table above
1idu16which input within the class, little-endian; 0xFFFF = a blanket
3directionu8which sign or which edge, 0-4
4scaleu8percent of the physical value kept, 0-255
SCALE
NameValueEffect
block0None of the physical value reaches the PC.
pass100All of it, byte for byte.
max2552.55x. Anything above 100 amplifies.
RULES
RuleWhat the box does
combineA delta picks up one fixed-sign scale and one relative scale, multiplied. A 0 in either blocks.
carryA 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.
saturateA weighed value clamps to the field's declared range, never wraps, and forfeits the fraction it could not carry.
one bitA 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
DirectionValueAxisButton, key, media
both0Both signs.Press and release.
positive1Positive sign only (+).Press only (0 to 1).
negative2Negative sign only (-).Release only (1 to 0).
with3The sign the box is injecting.Refused on a button or key. Media locks the whole usage, as 0 would.
against4The 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.

BLANKET

An id of 0xFFFF addresses the whole class in one command.

ClassCoversDirectionReads back as
buttonAll five buttons.As a named button.One entry per button and edge, under its own id.
keyEvery keyboard usage.Honoured: 1 blocks press edges, 2 release edges, 0 both.One entry per blocked edge, id 0xFFFF.
mediaEvery Consumer usage.Ignored.One entry, id 0xFFFF, direction 0.
axisX, Y, and the wheel.As a named axis.One entry per axis and direction, under its own id.
BlanketHow the box holds it
button, axisExpands as it lands, so a later command on one member overwrites just that member.
key, mediaOne flag, which a per-usage unlock does not lift.
PHYSICAL ONLY

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.

A SCALE CLEARS ON
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 away

Hold one with a keepalive if it has to outlast a second of quiet. Injection auto-clears on the same events, described on Injection.

EFFECT

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.

EXAMPLE

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 injecting

with 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 bearing
LIFETIME

Each 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.

EventEffect on the bearing
An injected delta on that axis, from a MOVE on either accumulator or from a clipSets 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 gateKeeps restarting the deadline, and points at the net pending delta rather than the last one sent.
The window elapsesThat axis has no bearing. Both relative directions stop applying and it passes at its fixed-sign scale alone.
MOVE with DISCARDCleared, 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 staleCleared 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, detachCleared with the rest of the PC-owned state.

Geometry

Per axis, or projected onto the bearing

Set by OPTION(BEARING). The two modes agree while the bearing has one nonzero component, and differ once both are.

ModeValueWeighs
per axis0Each axis against its own bearing, independently.
vector1Only 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 / against

In 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.

THE TWO STAGES
StageReadsActs on
1. projectThe relative pair, one number for both axes.The part lying along the bearing. What survives is written back to both axes.
2. weighEach 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.

EXAMPLE
  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     0

Per 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.