evdev

index

Overview

Linux input devices from /dev/input/event*: keyboards, sticks and gamepads. It lists and opens devices, keeps each one's identity and axis calibration, and normalizes axis readings. It never decides what a key or an axis means.

A program reads a device in one of two ways:

nodes and capabilities read what is attached without opening a node. The module exports EV_SYN, EV_KEY, EV_ABS, ABS_X and ABS_Y, the codes a reader of dev:read() compares against. Every other code is a number from the kernel's input-event-codes.h, and a binding table holds its own.

Functions

NameSignature
normalizenormalize(ai, raw, deadzone) -> v
curvecurve(v, kind, amount) -> shaped
looks_like_sticklooks_like_stick(dev) -> stick
looks_like_keyboardlooks_like_keyboard(dev) -> keyboard
openopen(path, nonblock) -> dev, err, errno
capabilitiescapabilities(name, root) -> caps
nodesnodes(root) -> names
ignoredignored(info) -> ignore
listlist() -> devices
list_keyboardslist_keyboards() -> devices

normalize(ai, raw, deadzone) -> v

Map a raw axis reading to [-1, 1]

ai is the axis's calibration from dev.axes. The deadzone is the larger of the hardware flat and deadzone, a fraction of the half-range. A reading inside it returns 0. Outside it, the rest of the range is scaled so the value rises from 0 at the deadzone's edge.

curve(v, kind, amount) -> shaped

Shape a normalized axis value

kind = "expo" softens the centre and keeps the ends: (1 - amount) * v + amount * v³. amount runs from 0, linear, to 1, cubic, and defaults to 0.5. The sign is kept. Any other kind returns v.

looks_like_stick(dev) -> stick

Check whether a device is a stick or a gamepad

dev needs axes and buttons, each keyed by code, as an open device has them. Any true value marks a code, so plain tables serve.

True when the device reports ABS_X, ABS_Y and one key udev counts as a joystick button:

A touchscreen and a touchpad report X and Y too, but their keys sit in the digitizer block, 0x140 to 0x14f, so neither passes. A mouse with more than sixteen buttons runs into the joystick range. It reports 0x11f, the code below BTN_JOYSTICK, and a device with that code is never a stick.

looks_like_keyboard(dev) -> keyboard

Check whether an open device is a keyboard

True when the device reports KEY_A, KEY_ENTER and KEY_SPACE and is not stick-like. The key test rules out power buttons, lid switches and media-key nodes, which also report EV_KEY. The stick test keeps a gamepad with a keyboard profile out.

open(path, nonblock) -> dev, err, errno

Open one input device

Opens path, non-blocking unless nonblock is false, and reads the device's identity and capabilities once. Returns nil, an error and the errno when the node does not open, and when it answers no identity: a regular file refuses with ENOTTY.

fielddescription
dev.paththe node's path
dev.info{ name, bustype, vendor, product, version }
dev.axesABS code to { code, value, min, max, fuzz, flat, resolution }
dev.buttonsthe set of EV_KEY codes the device reports
methoddescription
dev:read()the pending events, each { type, code, value }; an empty table when none wait; nil, an error and the errno when the device is gone
dev:fileno()the descriptor, for lev.core.wait_readable
dev:close()close the node; safe to call twice

One read returns up to 64 events.

capabilities(name, root) -> caps

Read one event node's identity and capabilities from sysfs

name is the node's name under /dev/input, such as event4, as nodes lists them. Reads <root>/<name>/device/uevent, root being /sys/class/input by default, and opens nothing. Returns nil when the file does not read or names no device.

fielddescription
caps.info{ name, bustype, vendor, product, version }, as in dev.info
caps.axesthe set of EV_ABS codes the device reports
caps.buttonsthe set of EV_KEY codes the device reports

looks_like_stick and looks_like_keyboard take caps as they take an open device, and ignored takes caps.info. An axis carries no calibration here: that needs the node open.

nodes(root) -> names

List the event nodes without opening them

The event* entries under root, /dev/input by default, in number order. Nothing is opened, so a node this process may not open is still listed. A root that does not read gives an empty table.

capabilities takes a name from here, and open takes <root>/<name>.

ignored(info) -> ignore

Check whether the environment asks a game to ignore a device

info is a device's dev.info. The rule is SDL3's, from the variables Steam sets for the games it launches. They are read on every call.

  1. Steam's virtual pad, 0x28de/0x11ff, is kept only when SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD is set and not empty, does not start with 0, and does not read false in any case. The two lists do not apply to it.
  2. When SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT holds an entry, a device on it is kept and every other device is ignored. SDL_GAMECONTROLLER_IGNORE_DEVICES is not read then.
  3. Otherwise a device on SDL_GAMECONTROLLER_IGNORE_DEVICES is ignored.

A list holds 0xVVVV/0xPPPP entries split by commas or whitespace. The hex digits match in either case, up to four of them. Any other entry is skipped. A value that starts with @ names a file that holds the list. A file that does not read gives an empty list. A missing vendor or product reads as 0.

list() -> devices

Open every stick and gamepad

Returns the open, non-blocking devices under /dev/input that looks_like_stick accepts, in node number order: list()[1] is the lowest-numbered stick. A node the process cannot open is skipped, so with nothing attached the list is empty.

A device ignored accepts is left out. open on its path still opens it.

looks_like_stick and ignored run on each node's capabilities first, and only a node that passes is opened. A close waits out an RCU grace period, tens of milliseconds on some kernels. So a node turned down costs one sysfs read and no close.

list_keyboards() -> devices

Open every keyboard

Returns the open, non-blocking devices under /dev/input that looks_like_keyboard accepts, in node number order. A node the process cannot open is skipped. As in list, only a node whose capabilities pass is opened.