Read keys, mouse, touch and gamepads
Read the keyboard, the mouse, touches and gamepads yourself, for a game or a canvas that people play with directly.
Widgets or reads
Widgets need none of this: buttons, fields and focus navigation already answer every device (Keyboard and gamepad). Read input yourself when what you draw is what people play with, as in this program, where the keys, a stick, a click or a tap move a ball.
local input = datumhue.input
local vec2 = datumhue.math.vec2
local color = datumhue.color
local box = datumhue.ui.root:panel({ gap = 8, align_items = "center" })
local canvas = datumhue.draw.new({
width = 320,
height = 240,
background = color("#2d353b"),
})
local view = canvas:mount({ parent = box, width = 640, height = 480 })
local ball = canvas:circle({ radius = 12, color = color("#e67e80") })
local hint = box:label({ text = "" })
input.bind_action("left", { keys = { "arrow_left", "a" } })
input.bind_action("right", { keys = { "arrow_right", "d" } })
input.bind_action("up", { keys = { "arrow_up", "w" } })
input.bind_action("down", { keys = { "arrow_down", "s" } })
input.bind_action("paint", {
keys = { "space" },
gamepad_buttons = { "south" },
})
local paints = { color("#e67e80"), color("#a7c080"), color("#7fbbb3") }
local paint = 1
local pos = vec2(0, 0)
local low, high = vec2(-160, -120), vec2(160, 120)
local function move_to(at)
local world = at and canvas:screen_to_world(at)
if world then
pos = world:clamp(low, high)
end
end
datumhue.on_update(function(dt)
-- action_vector's y grows down; the canvas's grows up.
local x, y = input.action_vector("left", "right", "up", "down")
pos = (pos + vec2(x, -y) * (160 * dt)):clamp(low, high)
if input.is_action_pressed("paint") then
paint = paint % #paints + 1
ball:update({ color = paints[paint] })
end
if input.is_mouse_pressed("left") then
move_to(view:mouse_position())
end
for _, touch in ipairs(input.touches()) do
local at = input.app_touch_position(touch.id)
local box_at = view.bounds
if input.is_touch_pressed(touch.id) and at and box_at then
move_to(at - box_at.min)
end
end
ball.pos = pos
hint.text = "Last input: " .. input.last_source
end)
In this example: datumhue.input datumhue.math.vec2 datumhue.color datumhue.ui.root UiElement:panel datumhue.draw.new DrawCanvas:mount DrawCanvas:circle UiElement:label datumhue.input.bind_action DrawCanvas:screen_to_world datumhue.on_update datumhue.input.action_vector datumhue.input.is_action_pressed DrawPrimitive:update datumhue.input.is_mouse_pressed UiElement:mouse_position datumhue.input.touches datumhue.input.app_touch_position UiElement.bounds datumhue.input.is_touch_pressed DrawPrimitive.pos UiElement.text datumhue.input.last_source
- An action names the inputs that drive it, so people can remap them in
one place.
input.action_vectoradds the left stick and D-pad of the gamepad in use, and each touch stick your app holds, to four actions, and keeps the result no longer than 1. input.action_vectorgrows y downward, as the screen does, and the canvas grows y upward, so the program flips it.- Space or gamepad A changes the ball's color, once per press.
- A click goes through
UiElement:mouse_positionandDrawCanvas:screen_to_worldto a point on the canvas. A tap starts frominput.app_touch_position, less the canvas's top-left corner. input.last_sourcenames the device that sent the latest input.
Presses between frames
input.is_key_pressed, input.is_action_pressed and the other
_pressed and _released reads report a press or a release since your
app's previous update, once each, so none is lost when an update spans
several frames. The _down reads report what is held now.
input.on_key and input.on_button call you on each press and release
instead.
Key names
Keys are named by their place on the keyboard, not by the letter they
type: "a" is the key where a QWERTY keyboard has A, whatever the
layout. "shift", "control", "alt" and "super" match either side,
and names such as "shift_left" name one. "super" is the Super,
Command or Windows key, and has no side names. For text people type,
use input.on_text, which hears the characters their layout produces.
Pointer and touch positions
| Read | Gives |
|---|---|
input.app_mouse_position, input.app_touch_position | UI px from your content area's top-left, as UiElement.bounds |
UiElement:mouse_position | UI px from the element's top-left |
input.mouse_position, input.touches | the window's logical pixels, not scaled by the zoom |
A touch is a device of its own and never presses a mouse button, so you
can read touches beside the mouse buttons without counting a tap twice.
Image:touch gives a touch's position in an image's own pixels, as
Image:cursor gives the mouse's.
Touch controls
UiElement:touch_stick and UiElement:touch_button put a thumb stick
and buttons on a touchscreen. A touch that starts on one belongs to it
until the touch ends: input.touches() and the elements under it never
see that touch. A stick adds to input.action_vector, and a button holds
a named action down, so one set of actions serves keys, gamepads and
touches. By default each control shows only while input.last_source
reads "touch", and the mouse never presses one.
Gamepads
input.gamepads lists the connected pads. An id is a pad's place in
that list and shifts when pads come and go, so list them again rather
than keep an id. input.last_gamepad is the id of the pad in use, the
one last pressed or pushed: a game for one player reads that pad, so
whichever pad someone picks up plays. Read a stick with input.stick,
a trigger with input.trigger and a button with input.is_button_down;
input.rumble shakes a pad.
Prompts and rebinding
input.action_label gives the words a prompt shows for an action: its
key in the keyboard layout people type with (the key in the QWERTY "q"
place reads "A" on a French keyboard), its pad button named as the pad
in use names it, its mouse button, or the text of a touch button
that presses it. Without a device it names the one input.last_source
reads. Rebinding stays with your app: hear the next key with
input.on_key, bind it with input.bind_action, and keep it in
datumhue.storage for the next run.
Only while your app is focused
Only the focused app and the apps above it read real input. Every other
app reads false, nil and zero, so an app in the background cannot
watch keystrokes. The cursor calls (input.set_cursor_visible,
input.set_cursor_locked and input.set_cursor_icon) set your app's
own cursor, which shows while your app is focused.
See also
- Keyboard and gamepad: what the keys and a gamepad do to widgets.
- Draw and animate: canvases and
datumhue.on_update. - Input, focus and Back: which app hears input.
datumhue.input: every input read.
Reference used on this page: datumhue.input datumhue.math.vec2 datumhue.color datumhue.ui.root UiElement:panel datumhue.draw.new DrawCanvas:mount DrawCanvas:circle UiElement:label datumhue.input.bind_action DrawCanvas:screen_to_world datumhue.on_update datumhue.input.action_vector datumhue.input.is_action_pressed DrawPrimitive:update datumhue.input.is_mouse_pressed UiElement:mouse_position datumhue.input.touches datumhue.input.app_touch_position UiElement.bounds datumhue.input.is_touch_pressed DrawPrimitive.pos UiElement.text datumhue.input.last_source datumhue.input.app_mouse_position datumhue.input.mouse_position datumhue.input.is_key_pressed datumhue.input.on_key datumhue.input.on_button datumhue.input.on_text Image:touch Image:cursor UiElement:touch_stick UiElement:touch_button datumhue.input.gamepads datumhue.input.last_gamepad datumhue.input.stick datumhue.input.trigger datumhue.input.is_button_down datumhue.input.rumble datumhue.input.action_label datumhue.storage datumhue.input.set_cursor_visible datumhue.input.set_cursor_locked datumhue.input.set_cursor_icon