Keys

Keyboard state and the key constants that name it.

Read a key through the snapshot App.Input contains -- 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.

from_code : U64 -> Try(Key, [InvalidKeyCode])

Validate a raw raylib key code and name it.

A code with a named key decodes to that key, so a value that came from the host -- a Devices.Event -- pattern-matches on KeyA exactly as one written in the app does. A code in range with no name is Raw.

key_code : Key -> U64

raylib key code for a key (index into the snapshot key-state list).

key_down : { ..state, keys : List(U8) }, Key -> Bool

Check if a specific key is held down at the cycle boundary. A state sample. Pass host directly.

key_up : { ..state, keys : List(U8) }, Key -> Bool

Check if a specific key is up at the cycle boundary. Pass host directly.

key_pressed : { ..state, keys : List(U8) }, Key -> Bool

Check if a key was pressed at least once since the previous input. An interval event: a key tapped between two cycles is pressed and released in the next input and held in neither. Coalesced per key; the ordered record with every press is Devices.Snapshot.events. Pass host directly.

key_released : { ..state, keys : List(U8) }, Key -> Bool

Check if a key was released at least once since the previous input. Pass host directly.

set_exit_key! : ExitKey => {}

Set which key ends the app.

NoExitKey stops any key from ending it; App.default starts with ExitKey(KeyEscape). The exit key ends the app directly, before update! sees the press, whatever App.Config.with_close_request chose. The window's close button follows with_close_request and is unaffected by this.

Legal in init!, update!, and tasks; refused in render!.

holding : List(Key) -> Source

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 holds 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]))
typing : Str -> List(U32)

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.

set_text! : List(U32) => {}

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 input; a longer script has the excess discarded and text_input_overflow set, exactly as for hardware input.

Legal in init!, update!, and tasks; refused in render!.

Keys.set_text!(Keys.typing("hello"))

Key

:= [
    KeyAndroidBack,
    KeyAndroidMenu,
    KeyVolumeUp,
    KeyVolumeDown,
    KeyApostrophe,
    KeyComma,
    KeyMinus,
    KeyPeriod,
    KeySlash,
    Key0,
    Key1,
    Key2,
    Key3,
    Key4,
    Key5,
    Key6,
    Key7,
    Key8,
    Key9,
    KeySemicolon,
    KeyEqual,
    KeyA,
    KeyB,
    KeyC,
    KeyD,
    KeyE,
    KeyF,
    KeyG,
    KeyH,
    KeyI,
    KeyJ,
    KeyK,
    KeyL,
    KeyM,
    KeyN,
    KeyO,
    KeyP,
    KeyQ,
    KeyR,
    KeyS,
    KeyT,
    KeyU,
    KeyV,
    KeyW,
    KeyX,
    KeyY,
    KeyZ,
    KeyLeftBracket,
    KeyBackslash,
    KeyRightBracket,
    KeyGrave,
    KeySpace,
    KeyEscape,
    KeyEnter,
    KeyTab,
    KeyBackspace,
    KeyInsert,
    KeyDelete,
    KeyRight,
    KeyLeft,
    KeyDown,
    KeyUp,
    KeyPageUp,
    KeyPageDown,
    KeyHome,
    KeyEnd,
    KeyCapsLock,
    KeyScrollLock,
    KeyNumLock,
    KeyPrintScreen,
    KeyPause,
    KeyF1,
    KeyF2,
    KeyF3,
    KeyF4,
    KeyF5,
    KeyF6,
    KeyF7,
    KeyF8,
    KeyF9,
    KeyF10,
    KeyF11,
    KeyF12,
    KeyLeftShift,
    KeyLeftControl,
    KeyLeftAlt,
    KeyLeftSuper,
    KeyRightShift,
    KeyRightControl,
    KeyRightAlt,
    KeyRightSuper,
    KeyKbMenu,
    KeyKp0,
    KeyKp1,
    KeyKp2,
    KeyKp3,
    KeyKp4,
    KeyKp5,
    KeyKp6,
    KeyKp7,
    KeyKp8,
    KeyKp9,
    KeyKpDecimal,
    KeyKpDivide,
    KeyKpMultiply,
    KeyKpSubtract,
    KeyKpAdd,
    KeyKpEnter,
    KeyKpEqual,
    Raw(U64),
]

Named raylib keyboard keys plus a validated backend-specific escape hatch.

is_eq : _

Compare two of these values.

ExitKey

:= [NoExitKey, ExitKey(Key)]

Which key, if any, ends the app. NoExitKey disables the behaviour.

Source : [Hardware, Virtual(List(Key))]

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 derivation 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. Hardware edges are shut out entirely while a script is the source.