User-feedback iteration on S2 PoC template: - Replace Markdown Badges-Table with bold key:value list (trailing-spaces line-breaks). - Add ## Topology H2 heading directly above the topology-block marker. Verified: --lint --fix exits with 0 structural-warnings (remaining stale_docs warnings are sugar-form caveat, pre-existing concern, not template-related). Idempotent: hash unchanged on re-run.
lib-core.input
P.0 action-mapping lib. Decouples physical keys from logical actions.
Polling-based (is_action_down) + edge-based (was_action_pressed, was_action_released).
Direction-vector helper composes 4 actions into {x, y} in {-1, 0, +1}^2.
Version: 0.4.0
Lib-ID: lib-core.input
Requires: (none)
Tags: input, action-mapping
Topology
graph LR
this["lib-core.input"]
engine["engine.*"]
this --> engine
API
input.bind(action_name, keys)
Syntax: input.bind(action_name: string, keys: string[]) -> void
Example:
input.bind("move_left", { "a", "left" })
Description: Bindet einen logischen Action-Namen an ein Array von Key-Names. Atomic: wenn ein Key invalid ist, fehlt der gesamte Bind ohne partial state. Lua error(...) für bind() misuse (caller-bug fast-fail).
input.unbind(action_name)
Syntax: input.unbind(action_name: string) -> void
Example:
input.unbind("move_left")
Description: Entfernt eine Action-Binding. No-op wenn Action nicht gebunden.
input.is_action_down(action) -> bool
Syntax: input.is_action_down(action: string) -> bool
Example:
if input.is_action_down("move_left") then ... end
Description: Returns true wenn irgendein gebundener Key der Action gerade gedrückt ist. Silent-false für undefined actions.
input.was_action_pressed(action) -> bool
Syntax: input.was_action_pressed(action: string) -> bool
Example:
if input.was_action_pressed("quit") then engine.exit(0) end
Description: Press-Edge — true für genau einen Frame pro Key-Down-Event. Triggers einmal pro Press-Release-Cycle.
input.was_action_released(action) -> bool
Syntax: input.was_action_released(action: string) -> bool
Example:
if input.was_action_released("attack") then finish_swing() end
Description: Release-Edge — true für genau einen Frame pro Key-Up-Event. v0.4.0+. Triggers einmal pro Press-Release-Cycle.
input.direction(left, right, up, down)
Syntax: input.direction(left: string, right: string, up: string, down: string) -> {x: int, y: int}
Example:
local d = input.direction("move_left", "move_right", "move_up", "move_down")
-- d.x, d.y in {-1, 0, +1}
Description: Komponiert 4 Actions in einen Direction-Vector. Y-down-positive per ADR-0031.
input.action_count() -> number
Syntax: input.action_count() -> number
Description: Returns Anzahl der gebundenen Actions. Debug-friendly.
Key-Names
Strict-lowercase. Engine-internal engine.input.KEY_* integer-args are rejected.
| Category | Names |
|---|---|
| Letters | a, b, ..., z |
| Numbers | 0, 1, ..., 9 |
| Arrows | left, right, up, down |
| Whitespace + control | space, tab, enter, backspace, escape |
| Modifiers (left-side) | shift (= lshift), ctrl (= lctrl), alt (= lalt) |
| Console-toggle | ^ (DE: Zirkumflex / US: backtick) |
| Mouse | mouse_left, mouse_right, mouse_middle (v0.3.0+) |
Right-modifier-aliases (rshift, rctrl, ralt) + function-keys (f1..f12) + special-keys (delete, insert, ...) are forthcoming.
Unknown key-names raise a Lua-error with hint to this README.
Conventions
- Y-down-positive per Sporel pixel-convention (ADR-0031).
- Multi-key per action (e.g.,
{"a", "left"}for both A and Left-Arrow). - Silent-false for undefined actions (debug-friendly).
- Lua
error(...)forbind()misuse (caller-bug fast-fail). Atomic.
Consumer pattern
local input = require("lib-core.input")
input.bind("quit", { "escape" })
input.bind("move_left", { "a", "left" })
input.bind("move_right", { "d", "right" })
input.bind("move_up", { "w", "up" })
input.bind("move_down", { "s", "down" })
function update(ctx, dt)
if input.was_action_pressed("quit") then engine.exit(0) end
local d = input.direction("move_left", "move_right", "move_up", "move_down")
-- d.x, d.y in {-1, 0, +1}
end
Migration v0.1.0 -> v0.2.0
bind() switched from integer engine.input.KEY_* arguments to lowercase string-keys (per P.2.6). Reason: stop leaking engine-internal namespace into modules.
| v0.1.0 | v0.2.0 |
|---|---|
bind("quit", { engine.input.KEY_ESCAPE }) |
bind("quit", { "escape" }) |
CHANGELOG
v0.4.0 (P.3.10, 2026-05-15)
- Added
was_action_released(action_name)— release-edge detection per action.
v0.3.0 (P.3.3, 2026-05-12)
bind()now accepts unified key+mouse strings via BIND_MAP with kind-tagged entries.
v0.2.0 (P.2.6, 2026-05-11)
bind()accepts string-key-names only.
v0.1.0 (P.0)
- Initial release.
References
- Spec v0.4.0 (P.3.10):
meta/docs/superpowers/specs/2026-05-15-p3-housekeeping-design.md - Spec v0.3.0 (P.3.3):
meta/docs/superpowers/specs/2026-05-12-p3-3-mouse-input-design.md - Spec v0.2.0 (P.2.6):
meta/docs/superpowers/specs/2026-05-11-engine-input-symbolic-keys-design.md - Spec v0.1.0 (P.0):
meta/docs/superpowers/specs/2026-05-09-p0-lib-input-design.md - ADR-0001 (engine knows verbs, libs bring nouns)
- ADR-0031 (pixel-convention: Y-down-positive)
- ADR-0038 (API-Doc-Convention)