Input
Purpose
Input turns device observations into semantic actions and, when requested, ordered physical/text records. Games consume an immutable tick view without polling native devices or taking ownership of another consumer’s records.
Interface
Types below are in CE::Input unless another namespace is shown.
Types and operations
API and purpose |
|---|
|
|
|
|
|
|
|
|
Controls for bindings
Pass these Engine enums directly with the corresponding device ID when binding buttons, chords or axes.
Enum or constants |
Purpose and details |
|---|---|
Letter/digit positions, editing/navigation keys, |
|
|
|
Named buttons, D-pad, sticks, triggers and optional sensor axes; pair with |
|
Absolute |
|
|
Keyboard positions do not supply localized text. Unknown is not bindable;
standard enums are converted by Control, while adapters normalize native codes.
The control guide
explains this distinction and lists the headers without repeating every key here.
Capture, records and routing values
Enum or value |
Purpose and details |
|---|---|
|
|
|
|
Identify record source, press/release/repeat and modifier flags. Record interpretation. |
|
Ordered button, axis, pointer, scroll or committed-text payload with target/epoch metadata. Record ownership; target filtering. |
|
|
Usage
Bind physical controls during platform initialization, then read semantic actions
through the update’s immutable Input::Tick. A reusable
controller retains its own action context so local action IDs and bindings do
not collide with another controller’s.
---
config:
theme: base
themeVariables:
fontFamily: system-ui, sans-serif
fontSize: 14px
flowchart:
htmlLabels: true
curve: linear
nodeSpacing: 22
rankSpacing: 38
padding: 12
---
flowchart LR
accTitle: Input · simple gameplay actions
accDescr: Local names omit Input::; ambiguous nested namespaces remain qualified. Arrows point toward dependencies. Application roles are rounded; named API operations have rounded corners. Dashed arrows mark optional choices.
k0(["Application<br/>init_platform()"]):::app
k1["System"]:::focal
k2["Actions::<br/>Bindings"]:::focal
k3["Control"]:::focal
k4["KeyboardKey"]:::focal
k5["PollSnapshot"]:::focal
k6(["Application<br/>update()"]):::app
k7["Tick"]:::focal
k8["Actions::<br/>ButtonTickState"]:::focal
k0 --> k1
k1 --> k2
k2 --> k3
k3 --> k4
k1 --> k5
k6 --> k7
k7 --> k8
classDef focal fill:#e7f3ef,stroke:#387d6b,color:#153d33,stroke-width:1.2px
classDef neighbor fill:#f0f3f8,stroke:#8c9bb1,color:#35445b,stroke-width:1px
classDef app fill:#fff2df,stroke:#c09452,color:#674515,stroke-dasharray:5 3
classDef system fill:#eaf0fc,stroke:#617fab,color:#263f69,stroke-width:1.2px
linkStyle default stroke:#7e8fa7,stroke-width:1.2px
Local names omit Input:: where unambiguous. Explore this diagram and its other views →
Explore the full application path, UI capture and focus, or engine input dependencies.
Choose what to consume
Reader’s task |
API path |
Setup and ownership |
|---|---|---|
Read a few game-wide actions |
|
Register direct bindings on platform; retain tokens and remove them during cleanup. Bind input. |
Reuse a player/controller mapping |
|
Acquire |
Feed a menu or text editor |
|
Request Events/Text separately from keyboard focus. Read ordered records on the UI owner, preferably through a toolkit session. |
Retain a complete native poll |
Acquire after publication on platform; the immutable handle can cross threads and survive later polls. |
|
React to convenience-gamepad availability |
|
Subscribe and read the seed together on platform; select queued delivery for a simulation-owned target. Availability contract. |
Actions, capture and focus are independent
Actions describe gameplay meaning. Capture requests activate ordered physical or committed-text records; they do not consume a private queue. Focus chooses a record recipient and may gate keyboard gameplay. Focusing alone does not request Events or Text, and action-context lifetime does not establish UI focus.
Keep capture/focus leases while their UI consumer is active. Each poll latches those requests once, so a mid-poll change takes effect on the next poll and cannot reassign records already captured. Multiple consumers can inspect the same immutable record batch. See capture and routing.
Uses
Dependency |
Input’s use |
|---|---|
A concrete adapter borrows a compatible live window while native callbacks are attached. Display. |
|
Evaluate direct and context-local mappings into immutable action snapshots. |
|
Collect supported records and latch focus/capture for one complete poll. |
|
Publish optional gamepad observations through a weak route. Runtime selects its Context bus; standalone input opts in. Events. |
Used by
Consumer |
What it reads or requests |
|---|---|
Poll on platform and transfer complete polling intervals to immutable tick input. Runtime. |
|
Application controllers |
Read direct or context-local actions from |
Request supported record/focus capabilities and feed widgets during their update. GUI. |
|
Standalone host or custom adapter |
Drive complete polling/publication explicitly when Runtime is absent. Adapter publication. |
Contracts
Directly bind and unbind controls on the platform owner. For runtime consumers,
obtain action_contexts() there once and retain its endpoint; attach definitions
or reset context leases on the consumer’s owner. Each context has independent
local action IDs, and accepted changes take effect at the next poll.
Button edges/counts and observed
hold durations describe the consumed polling interval; they do not schedule
updates or replace tick.delta_seconds. Relative axes accumulate changes,
whereas absolute axes retain their final value. For direct actions,
button_simulation_seconds() maps observed down-time proportion onto the
selected simulation delta when that is the desired movement policy.
Records preserve collection order and committed text comes from the backend’s text input, not inferred key names. Readers may inspect the same record batch. Capture activation and focus are latched at poll start; requests cannot retroactively reroute already captured records. Exclusive keyboard focus gates keyboard gameplay input while leaving other devices available. A superseded focus lease cannot clear a newer request.
Lifecycle
Runtime::Context may own input or explicitly borrow it. Borrowed input must survive
runtime cleanup/context lifetime and detach from a still-live window. Register
bindings during game initialization, retain their IDs and remove them during
cleanup, including partial initialization. Context leases release their mappings
when their controller leaves; the endpoint remains safe after input destruction but
rejects new attachments. Keep capture/focus leases only while
their consumer needs them. Lease state and delivered immutable snapshots can
survive adapter destruction; they do not retain a native window.
Shutdown discards pending, unpublished input after simulation stops. It does not perform an extra game update to consume those records. Already delivered handles remain valid.
Execution
Polling, native callback collection, direct binding changes and publication run on the platform owner. Runtime transfers complete batches to simulation. Update reads the tick’s borrowed view during that hook; copy owned data for longer use. Context attachment/removal, capture and routing requests support concurrent callers, with exclusive access when moving/resetting each individual lease. Delivered records are read-only. GUI widgets are called on their UI/simulation owner, never from a native callback.
Integration
After binding application actions during init_platform(), an update can select direction
and speed without referring to physical keys:
const float direction = (tick.input.button(move_right).held() ? 1.0f : 0.0f)
- (tick.input.button(move_left).held() ? 1.0f : 0.0f);
position.x += direction * speed * static_cast<float>(tick.delta_seconds);
Here position, speed and the action IDs belong to the game. Held-state
movement is a policy that moves for the whole selected update when the button
ends held; a tap released before consumption contributes no movement in this
example. For proportional contribution from observed taps, use
tick.button_simulation_seconds(action)
and multiply its result by speed instead of multiplying held() by delta.
For a menu or
editor, route ordered records through a toolkit session instead of deriving text
from these actions. See binding actions
and GUI construction.
A reusable controller instead reads tick.input.actions(its_context).button(id).
The Controller tutorial puts those
local IDs and the context lease into one class; Character
consumes its commands, including runtime player joins and departures.
Limits and further reading
Backend capabilities determine whether Events, committed Text and focus routing are available. The GLFW backend does not promise composition/preedit, clipboard editing or a complete IME/editor protocol. Read actions, snapshots, polling, records and focus, timing and the input interface.