Validate and wrap a raw raylib key code.
Keys
Keyboard state and the key constants that name it.
Read a key through the snapshot App.Input carries --
input.devices.key_pressed(KeySpace) -- rather than through the packed
bytes. set_source! and set_text! are the other half: they let a run
drive its own keyboard, which is what a scripted demo or a headless test
needs.
The types and pure helpers live in the companion roc-ray-types package so
reusable packages can depend on them without depending on this platform.
This module re-exports them, so Key here and in the package are the
same nominal type and values pass between them freely.
raylib key code for a key (index into the sampled key-state lists).
Check if a specific key is currently held down. Pass input.devices directly.
Check if a specific key is currently not pressed (up). Pass input.devices directly.
key_pressed : { ..state, keys : List(U8) }, Key -> Bool
Check if a key was pressed during this input interval. Pass input.devices directly.
key_released : { ..state, keys : List(U8) }, Key -> Bool
Check if a key was released during this input interval. Pass input.devices directly.
set_exit_key! : ExitKey => { }
Set which key closes the window.
NoExitKey stops any key from closing it; raylib defaults to
ExitKey(KeyEscape). The window close button is unaffected either way,
so an app that disables the exit key should still handle shutdown itself
by returning Err(Exit(code)) from update!.
Legal in init!, update!, and tasks; refused in render!.
A scripted source holding exactly these keys down.
Keys.holding([]) is a scripted keyboard with nothing held, which is not
the same as Hardware: it keeps the real keyboard shut out.
set_source! : Source => { }
Hand keyboard state to a scripted source, or back to the hardware keyboard.
What the app reads is unchanged: input.devices still carries packed key
state and Keys.key_pressed still reports edges, so widget code cannot
tell a scripted key from a struck one. That is the point -- a recorded
demo or a headless test exercises the real input path rather than a
parallel fake one.
The source installed on one cycle is what the host samples for the next,
the same way Mouse.set_source! places the pointer.
Legal in init!, update!, and tasks; refused in render!.
Keys.set_source!(Keys.holding([KeyRight]))
Codepoints for a string, ready for Keys.set_text!.
Keys.typing("hi") is the two codepoints a keyboard would have queued
had those two characters been typed, so the layout-dependent text channel
is scripted with text rather than with key codes.
Enter text as though it had been typed on the next frame.
Text is a separate channel from key state: it follows the active keyboard
layout, so a scripted key code cannot produce it and this cannot produce
key state. An app that reads both wants Keys.set_source! as well.
The codepoints arrive on the next cycle's input.devices.text_input and
are gone the cycle after, the way a real keyboard's characters arrive on
one frame and not the next. At most 32 codepoints are delivered per
frame and the excess is discarded, exactly as for hardware input.
Legal in init!, update!, and tasks; refused in render!.
Keys.set_text!(Keys.typing("hello"))
Key : Key
Named raylib keyboard keys plus a validated backend-specific escape hatch.
Declared in the roc-ray-types package's Keys and re-exported here,
which is also where its receivers are documented.
ExitKey : ExitKey
Which key, if any, closes the window. NoExitKey disables the behaviour.
Where keyboard state comes from: the hardware, or a script.
Virtual names the keys held down on the next frame, and only those.
The host runs the same edge detector over a scripted source that it runs
over hardware, so a key that appears in one frame's list and not the
previous one is pressed, and one that disappears is released.