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.

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

Validate and wrap a raw raylib key code.

key_code : Key -> U64

raylib key code for a key (index into the sampled key-state lists).

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

Check if a specific key is currently held down. Pass input.devices directly.

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

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!.

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 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]))
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 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.

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 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.