Skip to content

Input

Static methods for reading mouse, keyboard, modifier keys, and gamepad state. All methods are static.

Returns: Num — Current mouse X or Y position, in window coordinates, exactly as SDL reports them.

var x = Input.mouseX()
var y = Input.mouseY()
Gui.label("Mouse: %(x), %(y)")

Input.mouseLeft() / Input.mouseRight() / Input.mouseMiddle()

Section titled “Input.mouseLeft() / Input.mouseRight() / Input.mouseMiddle()”

Returns: Bool — true if the left, right, or middle button is currently held.

if (Input.mouseLeft()) {
// drag or hold logic
}

Input.mouseJustPressedLeft() / Input.mouseJustPressedRight()

Section titled “Input.mouseJustPressedLeft() / Input.mouseJustPressedRight()”

Returns: Bool — true if the button was just pressed this frame (edge-triggered, so only true for one frame per press).

if (Input.mouseJustPressedLeft()) {
tryStartDrag()
}

Returns: Num — Scroll delta for this frame (horizontal and vertical wheel). Zero when not scrolling.

For first-person and orbit cameras you usually want to capture the mouse: hide the cursor, lock it to the window center, and read motion as per-frame deltas — so looking around never lets the cursor drift off the window or stall at its edge. Provide your own key to release it (for menus, GUI, or quitting).

Input.setRelativeMouse(v) / Input.relativeMouse()

Section titled “Input.setRelativeMouse(v) / Input.relativeMouse()”

Enter (true) or leave (false) relative mode: the cursor is hidden and pinned to the window center, and mouse motion is delivered as deltas via Input.mouseDeltaX/Y. The built-in cameras (Cam.firstPerson, Cam.freeFly, orbit) automatically use those deltas, so they look correctly the instant relative mode is on. Input.relativeMouse() returns the current desired state.

The engine also auto-releases capture when the window loses focus (alt-tab / cmd-tab) and restores it on return, so the cursor is never trapped.

Input.setCursorVisible(v) / Input.cursorVisible()

Section titled “Input.setCursorVisible(v) / Input.cursorVisible()”

Show (true) or hide (false) the OS cursor. Relative mode hides the cursor itself, so this only has a visible effect when relativeMouse() is false.

Returns: Num — Relative mouse motion this frame (pixels). Works in both normal and relative mode; use it to drive a custom camera’s look so it never stalls when the cursor reaches the window edge.

class Game {
init() { Input.setRelativeMouse(true) } // capture on start
update(dt) {
// free the cursor for a menu / to quit
if (Input.keyJustPressed("escape")) {
Input.setRelativeMouse(false)
Input.setCursorVisible(true)
}
}
}

Returns: Bool — true if the key with the given name is currently held.

Parameters:

  • name (String) — SDL scancode name, matched case-insensitively (e.g. "a", "space", "escape", "Left", "Right"). Keys are read by scancode, so they follow physical key position, not the user’s keyboard layout — "w" is the same physical key on QWERTY and AZERTY.
if (Input.key("a")) position = position - speed * dt
if (Input.key("d")) position = position + speed * dt

Returns: Bool — true if the key was just pressed this frame (edge-triggered).

Parameters:

  • name (String) — SDL scancode name, as for Input.key.
if (Input.keyJustPressed("space")) {
Audio.play(_jumpSound)
}

Input.modShift() / Input.modCtrl() / Input.modAlt()

Section titled “Input.modShift() / Input.modCtrl() / Input.modAlt()”

Returns: Bool — true if the Shift, Ctrl, or Alt modifier is held.

if (Input.keyJustPressed("s") && Input.modCtrl()) saveGame()

Up to 4 gamepads are tracked. Buttons and axes are addressed by numeric index only — see the tables below.

Returns: Num — One past the highest occupied gamepad slot — not the number of gamepads currently connected.

Returns: Bool — Whether the given button is held. Returns false if player or button is out of range, or if no gamepad occupies that slot.

Parameters:

  • player (Num) — Gamepad slot index, 0–3.
  • button (Num) — Button index, 0-based. Numeric only — there is no name lookup.

Button indices are SDL3’s gamepad button order, which is positional (South is the bottom face button — A on Xbox, Cross on PlayStation, B on a Nintendo layout):

IndexButtonIndexButton
0South (Xbox A)8Right stick click
1East (Xbox B)9Left shoulder
2West (Xbox X)10Right shoulder
3North (Xbox Y)11D-pad up
4Back12D-pad down
5Guide13D-pad left
6Start14D-pad right
7Left stick click15+Misc / paddles / touchpad
// Slot 0, button 0 = bottom face button (Xbox A / PS Cross).
if (Input.gamepadButton(0, 0)) jump()

Returns: Num — Axis value, normalized. Sticks read -1 to 1; triggers read 0 to 1. Returns 0 if player or axis is out of range, or if no gamepad occupies that slot.

Parameters:

  • player (Num) — Gamepad slot index, 0–3.
  • axis (Num) — Axis index, 0-based. Numeric only — there is no name lookup.
IndexAxisRange
0Left stick X-1 → 1
1Left stick Y-1 → 1
2Right stick X-1 → 1
3Right stick Y-1 → 1
4Left trigger0 → 1
5Right trigger0 → 1
// Left stick on slot 0, with a hand-rolled dead zone.
var moveX = Input.gamepadAxis(0, 0)
var moveY = Input.gamepadAxis(0, 1)
if (moveX.abs < 0.25) moveX = 0
if (moveY.abs < 0.25) moveY = 0