<!-- Source: https://medius.k4tech.net/native/injection -->
# Injection model

_A set of fields, two verbs, added on top of the user's input_

A connected device is a set of _fields_: each is an _Axis_ (relative motion: X, Y, wheel) or a _Usage_ (a momentary button, key, or media control). [`MOVE`](/native/commands/move.md#move) drives an Axis, [`INJECT`](/native/commands/inject.md#inject) drives a Usage, and a Usage is one `(class, id)` shape for buttons, keys, and media alike.

| Device | Axes ([`MOVE`](/native/commands/move.md#move)) | Momentary ([`INJECT`](/native/commands/inject.md#inject)) |
| --- | --- | --- |
| mouse | cursor X/Y, wheel | buttons |
| keyboard | none | keys, modifiers |
| media | none | volume, play/pause, ... |

Whatever you send is _added on top of_ the user's own input, never replacing it:

```
  physical input  (real device)  --+
                                   +-->  one combined report  -->  game PC
  injected input  (your program) --+
```

| You send | The PC sees |
| --- | --- |
| a `MOVE` while the real mouse moves | The sum of both. |
| an `INJECT` press while the user holds nothing | The injected press. |
| nothing | Only the real device. |

> **Note**
>
> [`INJECT`](/native/commands/inject.md#inject), [`LOCK`](/native/commands/lock.md#lock), and [`CATCH`](/native/commands/catch.md#catch) all name an input with the same Axis and Usage vocabulary, so one `(class, id)` works across inject, lock, and catch.

## Fire-and-forget

_No per-command acknowledgement_

Command frames get no echo and no acknowledgement, so you can stream input fast (up to about one command per millisecond). The exception is [`QUERY`](/native/commands/requests.md#requests), which returns a [`RESP`](/native/commands/requests.md#resp).

Correctness comes from three places, not per-command tracking:

| Mechanism | What it does |
| --- | --- |
| frame [checksum](/native/frame.md#crc) | Drops corrupted frames. |
| [safety rules](/native/injection.md#safety) | Keep a dropped command from leaving the box stuck. |
| [`HEALTH`](/native/commands/requests.md#health) | Reads the box's actual state. |

A lost movement frame costs one millisecond of motion; the next frame carries on.

## What the box tracks

_Pending motion and held usages_

| State | What it holds |
| --- | --- |
| accumulator | A running total of sent motion and scroll not yet delivered to the PC. Each [`MOVE`](/native/commands/move.md#move), cursor or wheel, adds in; the box drains it into outgoing reports. |
| usage override | Per usage (button, key, or media), whether the box forces it active, forces it inactive, or leaves it to the real device. Set by the [`INJECT`](/native/commands/inject.md#inject) actions: press forces active, force-release forces inactive, soft-release hands it back. |

A report can only carry a limited movement size. A large injected move sends what fits and keeps the remainder in the accumulator for the next report. Nothing is clipped (`total seen = total sent`), just spread over as many reports as it takes.

## When the box sends a report

_At the mouse's own report rate, only on activity_

| When… | The box sends |
| --- | --- |
| the real mouse reported | The real movement plus whatever's drained from the accumulator, with buttons combining the physical state and your overrides. |
| the real mouse was still, but you have motion pending | A report carrying just the drained accumulator, paced to the mouse's own report rate (not one every millisecond). |
| an [`INJECT`](/native/commands/inject.md#inject) or [`RESET`](/native/commands/admin.md#reset) changed a usage | One report reflecting the new state. |

Otherwise the box sends nothing, like a real mouse sitting idle. A held usage is a single report (the edge), then silence until it changes.

## Safety

_Injected state can't trap the real device_

A [force-release](/native/commands/inject.md#inject) always wins: it clears an injected hold and masks a physical press, so any held input can always be forced back to inactive.

The box also clears all injection if your program goes quiet, dropping every override and pending move and returning to plain passthrough. Any of these resets it:

| Trigger | What happens |
| --- | --- |
| silence timeout | No valid frame arrives within the timeout (default `1000 ms`), so a crash while holding a button releases it a second later. |
| link drop | The link to the host chip drops. |
| mouse unplugged | The real mouse is detached, so there's nothing left to inject into. |
| [`RESET`](/native/commands/admin.md#reset) | You send the reset command explicitly. |

To hold an injected button deliberately, keep the link busy: any valid frame resets the timer, so a periodic [`QUERY(HEALTH)`](/native/commands/requests.md#health) suffices.

> **Note**
>
> The [medius library](/library/lifecycle.md) automates this: it sends keepalives while you hold something, and reconnects and re-applies your state if the link drops.
