> Cinder 0.1.0 documentation: cinder.input API
> Canonical page: https://cinder.ash10.dev/docs/api/input/ · Source: `docs/api/input.md` in the engine repository (engine commit 47c9352, imported 2026-11-09)
> Index of every page for agents: https://cinder.ash10.dev/llms.txt

# `cinder.input`


## `cinder.input`

Source: `cinder/input/__init__.mojo`

Input: action and axis mapping over keyboard, mouse and gamepad, rebindable and saved to disk.


## `cinder.input.map`

Source: `cinder/input/map.mojo`

Input mapping: named actions (buttons) and axes over keyboard, mouse and gamepad, rebindable at runtime and saved as a diffable text file. Feed every platform `Event` to `process`, call `end_frame` once per frame (it clears the just-pressed and just-released edges and the per-frame mouse deltas), and query by name. An axis sums its bindings: a key contributes `scale` while held, an analog source its value times `scale` after the dead zone. `listen` arms a capture: the next key, button or axis event becomes the binding, which is how a settings menu rebinds a control.

- `comptime DEVICE_KEY = 0` — Keyboard source; `code` is an SDL scancode (0-511 are tracked).
- `comptime DEVICE_MOUSE_BUTTON = 1` — Mouse button source; `code` is the SDL button (1 = left, 2 = middle, 3 = right; 0-7 are tracked).
- `comptime DEVICE_MOUSE_AXIS = 2` — Codes: 0 = x delta, 1 = y delta, 2 = wheel.
- `comptime DEVICE_GAMEPAD_BUTTON = 3` — Gamepad button source; `code` is an SDL_GamepadButton (0-31 are tracked). All connected gamepads share one state.
- `comptime DEVICE_GAMEPAD_AXIS = 4` — Gamepad axis source; `code` is an SDL_GamepadAxis (0-7 are tracked), value in [-1, 1]. All connected gamepads share one state.
- `comptime INPUT_MAP_VERSION = 1` — Version in the `cinder-input` header; `InputMap.from_text` rejects any other.
### struct `Binding`

```mojo
struct Binding(Equatable, ImplicitlyCopyable, Movable, Writable)
```

One source: `device` and its `code` (scancode, button or axis index). `scale` multiplies the contribution to an axis (digital sources contribute `scale` while held), `dead_zone` applies to analog sources.

Fields:

- `device`: Int
- `code`: Int
- `scale`: Float32
- `dead_zone`: Float32

- **`key`** `key(scancode: Int, scale: Float32 = 1.0) -> Binding`  
  A keyboard binding for SDL scancode `scancode`.
- **`mouse_button`** `mouse_button(button: Int, scale: Float32 = 1.0) -> Binding`  
  A mouse button binding (1 = left, 2 = middle, 3 = right).
- **`mouse_axis`** `mouse_axis(axis: Int, scale: Float32 = 1.0) -> Binding`  
  A mouse axis binding: 0 = x delta, 1 = y delta, any other code = wheel.
- **`gamepad_button`** `gamepad_button(button: Int, scale: Float32 = 1.0) -> Binding`  
  A gamepad button binding (SDL_GamepadButton index).
- **`gamepad_axis`** `gamepad_axis( axis: Int, scale: Float32 = 1.0, dead_zone: Float32 = 0.15 ) -> Binding`  
  An analog gamepad axis binding (SDL_GamepadAxis index) with a dead zone.
- **`same_source`** `same_source(self, o: Binding) -> Bool`  
  True when both bindings read the same device and code, whatever their scale and dead zone.
- **`write_to`** `write_to(self, mut w: Some[Writer])`

### struct `InputMap`

```mojo
struct InputMap(Movable)
```

Named actions and axes with their bindings, plus the device state they are evaluated against. Text format (`to_text`, `from_text`): a `cinder-input 1` header, then one `action NAME DEVICE CODE SCALE DEAD_ZONE` or `axis NAME ...` line per binding, where DEVICE is `key`, `mouse_button`, `mouse_axis`, `gamepad_button` or `gamepad_axis`; an entry without bindings is a bare `action NAME` or `axis NAME` line. Fields are separated by single spaces, so names must not contain spaces.

Fields:

- `actions`: List[_Action]
- `axes`: List[_Axis]
- `keys_down`: List[Bool]
- `mouse_buttons_down`: List[Bool]
- `gamepad_buttons_down`: List[Bool]
- `gamepad_axes`: List[Float32]
- `mouse_delta_x`: Float32
- `mouse_delta_y`: Float32
- `wheel`: Float32
- `listening_action`: Int
- `listening_axis`: Int
- `listening_scale`: Float32

- **`__init__`** `__init__(out self)`
- **`add_action`** `add_action(mut self, name: String) raises -> Int`  
  Adds an action without bindings and returns its index; raises when the name is taken.
- **`add_axis`** `add_axis(mut self, name: String) raises -> Int`  
  Adds an axis without bindings and returns its index; raises when the name is taken.
- **`find_action`** `find_action(self, name: String) -> Int`  
  Index of the action called `name`, or -1.
- **`find_axis`** `find_axis(self, name: String) -> Int`  
  Index of the axis called `name`, or -1.
- **`bind_action`** `bind_action(mut self, name: String, binding: Binding) raises`  
  Appends `binding` to the action, creating the action when it does not exist. When the new source is already down the action becomes held (with a just-pressed edge).
- **`bind_axis`** `bind_axis(mut self, name: String, binding: Binding) raises`  
  Appends `binding` to the axis, creating the axis when it does not exist.
- **`clear_action_bindings`** `clear_action_bindings(mut self, name: String) raises`  
  Removes every binding of the action; raises for an unknown action. An action held at that moment is released (`pressed` turns False and `just_released` fires), so it cannot stay stuck with no binding to release it.
- **`clear_axis_bindings`** `clear_axis_bindings(mut self, name: String) raises`  
  Removes every binding of the axis; raises for an unknown axis.
- **`rebind_action`** `rebind_action(mut self, name: String, binding: Binding) raises`  
  Replaces every binding of the action with `binding`.
- **`action_bindings`** `action_bindings(self, name: String) raises -> List[Binding]`  
  A copy of the action's bindings; raises for an unknown action.
- **`axis_bindings`** `axis_bindings(self, name: String) raises -> List[Binding]`  
  A copy of the axis' bindings; raises for an unknown axis.
- **`listen_action`** `listen_action(mut self, name: String) raises`  
  The next key, mouse button or gamepad button event replaces the action's bindings. An action held through its old bindings is released at that point (see `clear_action_bindings`).
- **`listen_axis`** `listen_axis(mut self, name: String, scale: Float32 = 1.0) raises`  
  The next key, button or analog event is added to the axis with `scale`.
- **`is_listening`** `is_listening(self) -> Bool`  
  True while `listen_action` or `listen_axis` waits for an event.
- **`cancel_listening`** `cancel_listening(mut self)`  
  Disarms a pending `listen_action` or `listen_axis` without changing any binding.
- **`process`** `process(mut self, ev: Event)`  
  Updates device state from one platform event (and captures it while listening).
- **`end_frame`** `end_frame(mut self)`  
  Clears edges and per-frame deltas; call once per frame after reading input.
- **`pressed`** `pressed(self, name: String) raises -> Bool`  
  True while any key or button bound to the action is held; mouse and gamepad axis bindings never press an action. Raises for an unknown action.
- **`just_pressed`** `just_pressed(self, name: String) raises -> Bool`  
  True when the action went from released to held since the last `end_frame`; raises for an unknown action.
- **`just_released`** `just_released(self, name: String) raises -> Bool`  
  True when the action went from held to released since the last `end_frame`; raises for an unknown action.
- **`axis`** `axis(self, name: String) raises -> Float32`  
  Sum of the axis' bindings, clamped to [-1, 1] for digital-only axes; analog and mouse contributions are not clamped (mouse deltas are in pixels).
- **`to_text`** `to_text(self) -> String`  
  Diffable text: one `action`/`axis` line per binding, sorted as defined.
- **`from_text`** `from_text(text: String) raises -> InputMap`  
  Parses the text format written by `to_text`; blank lines are skipped. Raises on a missing header, another version, a line that is not 2 or 6 fields, an unknown entry or device, or a malformed number.
- **`save`** `save(self, path: String) raises`  
  Writes `to_text()` to `path`; raises when the file cannot be written.
- **`load`** `load(path: String) raises -> InputMap`  
  Reads `path` and parses it with `from_text`; raises when the file cannot be read or does not parse.
