Skip to content

Commit cf49132

Browse files
committed
docs(evdev): add uinput-actions.md
1 parent 268b9a7 commit cf49132

1 file changed

Lines changed: 185 additions & 0 deletions

File tree

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
---
2+
order: 5
3+
title: Building UInput Actions
4+
description:
5+
Build tap, hold, chord, sequence, and macro helpers on top of uinput events.
6+
---
7+
8+
# Building UInput actions
9+
10+
This tutorial shows how to build helper actions (tap, hold, macro, ..) on top of
11+
the low-level `emit` and `sync` primitives.
12+
13+
## Preparation
14+
15+
Initialize the virtual device and allow the kernel time to register the input
16+
node. The ui object created here is used throughout the rest of this tutorial.
17+
18+
```lua
19+
local evdev = require "evdev"
20+
21+
local UInput = evdev.uinput.create
22+
local ecodes = evdev.ecodes
23+
24+
local ui = assert(UInput())
25+
26+
local function delay(ms)
27+
os.execute(string.format("sleep %.3f", ms / 1000))
28+
end
29+
```
30+
31+
## The basic building blocks
32+
33+
Every action is built from:
34+
35+
- `dev:emit(type, code, value)` — queues one input event
36+
- `dev:sync()` — flushes queued events to the kernel
37+
38+
Each press or release needs its own `sync()` so the input subsystem sees the
39+
state change before the next event:
40+
41+
```lua
42+
dev:emit(ecodes.EV_KEY, code, evdev.events.PRESS) -- [!code error]
43+
dev:sync() -- key is down
44+
45+
dev:emit(ecodes.EV_KEY, code, evdev.events.RELEASE) -- [!code error]
46+
dev:sync() -- key is up
47+
```
48+
49+
## Keyboard Actions
50+
51+
### Press
52+
53+
Send a key-down event.
54+
55+
```lua
56+
local function press(dev, code)
57+
dev:emit(ecodes.EV_KEY, code, 1)
58+
dev:sync()
59+
end
60+
61+
press(ui, ecodes.KEY_A)
62+
```
63+
64+
### Release
65+
66+
Send a key-up event.
67+
68+
```lua
69+
local function release(dev, code)
70+
dev:emit(ecodes.EV_KEY, code, evdev.events.RELEASE)
71+
dev:sync()
72+
end
73+
74+
release(ui, ecodes.KEY_A)
75+
```
76+
77+
### Tap
78+
79+
Press and release a key.
80+
81+
```lua
82+
local function tap(dev, code)
83+
press(dev, code)
84+
release(dev, code)
85+
end
86+
87+
tap(ui, ecodes.KEY_A)
88+
tap(ui, ecodes.KEY_B)
89+
```
90+
91+
### Macro
92+
93+
Tap several keys in order.
94+
95+
```lua
96+
local function macro(dev, codes)
97+
for _, code in ipairs(codes) do
98+
dev:emit(ecodes.EV_KEY, code, 1)
99+
dev:sync()
100+
dev:emit(ecodes.EV_KEY, code, 0)
101+
dev:sync()
102+
end
103+
end
104+
105+
macro(ui, {
106+
ecodes.KEY_H,
107+
ecodes.KEY_E,
108+
ecodes.KEY_L,
109+
ecodes.KEY_L,
110+
ecodes.KEY_O,
111+
})
112+
```
113+
114+
## Mouse actions
115+
116+
### Move cursor
117+
118+
Move the mouse pointer by relative pixels.
119+
120+
```lua
121+
local function move(dev, dx, dy)
122+
if dx ~= 0 then
123+
dev:emit(ecodes.EV_REL, ecodes.REL_X, dx)
124+
end
125+
if dy ~= 0 then
126+
dev:emit(ecodes.EV_REL, ecodes.REL_Y, dy)
127+
end
128+
dev:sync()
129+
end
130+
131+
move(ui, 10, 0) -- 10 pixels right
132+
move(ui, 0, -5) -- 5 pixels up
133+
```
134+
135+
### Scroll wheel
136+
137+
Emit vertical or horizontal scroll notches.
138+
139+
```lua
140+
local function scroll(dev, clicks)
141+
dev:emit(ecodes.EV_REL, ecodes.REL_WHEEL_HI_RES, clicks * 120)
142+
dev:sync()
143+
end
144+
145+
local function scroll_h(dev, clicks)
146+
dev:emit(ecodes.EV_REL, ecodes.REL_HWHEEL_HI_RES, clicks * 120)
147+
dev:sync()
148+
end
149+
150+
-- Focus a scrollable window (like a web browser or editor) now
151+
-- to see the scrolling effect.
152+
delay(2000)
153+
154+
-- Scroll down 3 notches
155+
scroll(ui, -3)
156+
delay(500)
157+
158+
-- Scroll up 3 notches
159+
scroll(ui, 3)
160+
delay(500)
161+
162+
-- Scroll right 3 notches
163+
scroll_h(ui, 3)
164+
delay(500)
165+
166+
-- Scroll left 3 notches
167+
scroll_h(ui, -3)
168+
```
169+
170+
### Click
171+
172+
Press and release a mouse button.
173+
174+
```lua
175+
tap(ui, ecodes.BTN_RIGHT) -- left click
176+
delay(500)
177+
tap(ui, ecodes.BTN_LEFT) -- right click
178+
delay(500)
179+
tap(ui, ecodes.BTN_MIDDLE) -- middle click
180+
```
181+
182+
Because the Linux kernel treats mouse buttons ( [BTN_*] ) as keys (both use the
183+
`EV_KEY` event type under the hood), we can use `tap()` to perform clicks.
184+
185+
[BTN_*]: ../api/ecodes.md#btn

0 commit comments

Comments
 (0)