Devices

One cycle of sampled keyboard, text, mouse and gamepad state.

A Snapshot is an observation: it says what the input devices looked like when the host sampled them at the start of a cycle, and nothing else. It carries no authority to change anything, so it is safe to hold in a model and safe to hand to a pure function. App.Input carries one as input.devices.

A test writes an observation of its own. Devices.none is a neutral snapshot with the host's packed list lengths, and the with_key_* and with_mouse_* receivers state one device's state at a time, so a test can say exactly the thing it is about and nothing else.

Devices.none.with_key_pressed(KeySpace)
none : Snapshot

A neutral snapshot with the host's own packed list lengths, for tests.

empty and none answer every query the same way -- nothing held, nothing typed, pointer at the origin, no gamepad connected. They differ in what they are made of. empty's packed lists are empty, which is all a model seed needs and costs nothing. none's are the lengths the host actually samples: 349 key bytes, 7 mouse-button bytes, 4 gamepad availability bytes, 4 x 18 gamepad button bytes, and 4 x 6 axes. That is what makes it writable, so the with_key_* and with_mouse_* receivers have somewhere to put a bit.

devices = Devices.none.with_key_pressed(KeyEscape)

Seed a model with empty; build a test's input from none.

empty : Snapshot

A snapshot in which nothing is pressed, nothing was typed, the pointer is at the origin, and no gamepad is connected.

Use this to initialize models before the first device snapshot is sampled. Devices.none is the same observation with the host's packed list lengths, which is what a test that wants to say "SPACE was pressed" needs.

Snapshot

:= {
    keys : List(U8),
    text_input : List(U32),
    gamepads : Snapshot,
    mouse : Snapshot,
}

Everything the host sampled from the input devices for one cycle.

Reach for the receivers rather than indexing the packed lists directly: input.key_pressed(KeyW), input.mouse.position(), input.gamepad(One).

key_down : Snapshot, Key -> Bool

Check whether a key is currently held. Receiver form: input.key_down(KeyW).

key_up : Snapshot, Key -> Bool

Check whether a key is currently up. Receiver form: input.key_up(KeyW).

key_pressed : Snapshot, Key -> Bool

Check whether a key was pressed during this input interval. Receiver form: input.key_pressed(KeyW). Static Keys.key_pressed(input, KeyW) remains available; combine singular queries with or when checking alternatives.

key_released : Snapshot, Key -> Bool

Check whether a key was released during this input interval. Receiver form: input.key_released(KeyW). Static Keys.key_released(input, KeyW) remains.

gamepad : Snapshot, Id -> [Connected(View), Disconnected]

Resolve a gamepad slot in this input's sampled snapshot. The returned pad is snapshot-scoped; query it now rather than retaining it in the model.

with_key_down : Snapshot, Key -> Snapshot

Say that one key is held, as Devices.none.with_key_down(KeyW).

Each with_key_* states the complete state of the key it names, replacing whatever that key had, because the host samples exactly one of held, pressed, or released per key per cycle. Different keys are independent bytes, so these compose: Devices.none.with_key_pressed(KeySpace).with_key_down(KeyLeftShift).

Only a snapshot with the host's packed list lengths can carry these. Start from Devices.none, not Devices.empty.

with_key_pressed : Snapshot, Key -> Snapshot

Say that one key went down this cycle, as Devices.none.with_key_pressed(KeySpace).

A key pressed this cycle is also held, which is how the host packs it, so this satisfies key_down as well as key_pressed.

with_key_released : Snapshot, Key -> Snapshot

Say that one key came up this cycle, as Devices.none.with_key_released(KeySpace).

A key released this cycle is no longer held, which is how the host packs it, so this satisfies key_released and key_up.

with_mouse_position : Snapshot, { x : F32, y : F32 } -> Snapshot

Place the pointer, as Devices.none.with_mouse_position({ x: 40, y: 12 }).

with_mouse_delta : Snapshot, { x : F32, y : F32 } -> Snapshot

Say how far the pointer moved this cycle, which is what input.mouse.delta() reports.

with_mouse_wheel : Snapshot, { x : F32, y : F32 } -> Snapshot

Say how far the wheel moved this cycle, which is what input.mouse.wheel_delta() reports.

wheel is set to the vertical movement, matching how the host samples a one-dimensional wheel alongside the two-axis one.

with_mouse_button_down : Snapshot, Button -> Snapshot

Say that one mouse button is held. The with_mouse_button_* family states the complete state of the button it names, exactly as the with_key_* family does.

with_mouse_button_pressed : Snapshot, Button -> Snapshot

Say that one mouse button went down this cycle. A button pressed this cycle is also held.

with_mouse_button_released : Snapshot, Button -> Snapshot

Say that one mouse button came up this cycle. A button released this cycle is no longer held.