-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate-ecodes.lua
More file actions
executable file
·304 lines (264 loc) · 7.28 KB
/
Copy pathgenerate-ecodes.lua
File metadata and controls
executable file
·304 lines (264 loc) · 7.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#!/usr/bin/env lua
local fmt = string.format
local concat = table.concat
local input = arg[1] or "/usr/include/linux/input-event-codes.h"
local generated_from = fmt("-- Generated by %q from %q", arg[0], input)
---@type {ABS:{},BTN:{},EV:{},INPUT_PROP:{},KEY:{},LED:{},MSC:{},REL:{},REP:{},SND:{},SW:{},SYN:{}}
local ecodes_by_prefix = {}
-- stylua: ignore
local prefixes = {
ABS = false,
BTN = true,
EV = true,
INPUT_PROP = false,
KEY = true,
LED = false,
MSC = false,
REL = true,
REP = false,
SND = false,
SW = false,
SYN = true,
}
local function readfile(path)
local file = assert(io.open(path, "r"))
local text = file:read("*a")
file:close()
return text
end
local function writefile(path, ...)
local file = assert(io.open(path, "w"))
file:write(...)
file:close()
end
local function collect_keys_values(t)
local keys, values = {}, {}
for k, v in pairs(t) do
keys[#keys + 1], values[#values + 1] = k, v
end
return keys, values
end
local function tbl_flatten(t)
local out = {}
for _, child in pairs(t) do
if type(child) == "table" then
for key, value in pairs(child) do
out[key] = value
end
end
end
return out
end
local function spairs(t)
local keys = collect_keys_values(t)
table.sort(keys)
local i = 0
return function()
i = i + 1
local k = keys[i]
return k, t[k]
end
end
local function max_string_width(ls)
local width = 0
for _, v in ipairs(ls) do
local text = tostring(v)
if #text > width then
width = #text
end
end
return width
end
local function ljust(text, width)
return ("%-" .. width .. "s"):format(text)
end
local function rjust(text, width)
return ("%" .. width .. "s"):format(text)
end
local function strip_comment(line)
return (line:gsub("/%*.-%*/", ""):gsub("//.*$", ""))
end
---@type fun(v:table<string,integer>):string
local function stringify(t)
local parts = {}
local keys, values = collect_keys_values(t)
local kw = max_string_width(keys)
local vw = max_string_width(values)
for k, v in spairs(t) do
parts[#parts + 1] = fmt("%s = %s", ljust(k, kw), rjust(v, vw))
end
return fmt("{\n %s\n}", concat(parts, ",\n "))
end
local function wanted(name)
for prefix, enabled in pairs(prefixes) do
if enabled and name:sub(1, #prefix + 1) == prefix .. "_" then
return true
end
end
return false
end
-- stylua: ignore
local function normalize_expr(expr)
return expr
:gsub("^%s+", "")
:gsub("%s+$", "")
:gsub("^[%(]+", "")
:gsub("[%)]+$", "")
end
local function parse_number(expr)
local sign = 1
expr = normalize_expr(expr)
if expr:sub(1, 1) == "-" then
sign = -1
expr = expr:sub(2)
end
local hex = expr:match("^0[xX](%x+)$")
if hex then
return sign * tonumber(hex, 16)
end
if expr:match("^%d+$") then
return sign * tonumber(expr, 10)
end
end
local function resolve_expr(expr, resolved_values)
expr = normalize_expr(expr)
local number = parse_number(expr)
if number ~= nil then
return number
end
local alias = expr:match("^([A-Z0-9_]+)$")
if alias then
return resolved_values[alias]
end
local left, op, right = expr:match("^%(?%s*([A-Z0-9_]+)%s*([%+%-])%s*(%d+)%s*%)?$")
if left and resolved_values[left] then
if op == "+" then
return resolved_values[left] + tonumber(right)
end
return resolved_values[left] - tonumber(right)
end
end
---@type fun(text:string):(ecodes:table<string,integer>)
local function parse_defines(text)
local resolved_values = {}
local unresolved = {}
for raw_line in text:gmatch("[^\r\n]+") do
local line = strip_comment(raw_line)
local name, expr = line:match("^%s*#define%s+([A-Z0-9_]+)%s+(.+)%s*$")
if name and wanted(name) then
expr = normalize_expr(expr)
if not expr:find("%s") then
local v = resolve_expr(expr, resolved_values)
if v then
resolved_values[name] = v
else
unresolved[name] = expr
end
end
end
end
local changed = true
while changed do
changed = false
for name, expr in pairs(unresolved) do
local v = resolve_expr(expr, resolved_values)
if v then
resolved_values[name] = v
changed = true
end
end
end
return resolved_values
end
---@type fun(ecodes:table<string,integer>):{[string]:table<string,integer>}
local function group_by_prefix(ecodes)
local groups = {}
for prefix in pairs(prefixes) do
groups[prefix] = {}
end
for name, code in pairs(ecodes) do
for prefix in pairs(prefixes) do
if name:sub(1, #prefix + 1) == prefix .. "_" then
groups[prefix][name] = code
break
end
end
end
return groups
end
local function load_ecodes()
local ecodes = parse_defines(readfile(input))
local groups = group_by_prefix(ecodes)
for prefix, g in pairs(groups) do
ecodes_by_prefix[prefix] = g
end
end
local function write_runtime_ecodes(path)
local ecodes = tbl_flatten(ecodes_by_prefix)
local text = {
generated_from,
"",
"-- stylua: ignore",
fmt("return %s", stringify(ecodes)),
}
writefile(path, concat(text, "\n"), "\n")
end
local function write_ecodes_class(path)
local ecodes = tbl_flatten(ecodes_by_prefix)
local text = {
"---@meta evdev.ecodes",
"",
generated_from,
"",
"---",
"---Linux input event codes.",
"---",
"-- stylua: ignore",
"---@class evdev.ecodes",
fmt("local M = %s", stringify(ecodes)),
"",
"return M",
}
writefile(path, concat(text, "\n"), "\n")
end
-- stylua: ignore
local descriptions = {
ABS = "Absolute axis codes (e.g. joysticks, touchscreens, tablets).",
BTN = "Button codes (e.g. mouse buttons, joysticks, gamepads).",
EV = "Event types (e.g. key press, relative movement, absolute position, synchronization).",
INPUT_PROP = "Input device property flags (e.g. direct touch, semi-mt, pointer).",
KEY = "Keyboard key codes.",
LED = "LED status indicator codes (e.g. num lock, caps lock, scroll lock).",
MSC = "Miscellaneous event codes that do not fit into other types.",
REL = "Relative axis codes (e.g. mouse movement, scroll wheels).",
REP = "Autorepeat configuration event codes.",
SND = "Sound output event codes (e.g. system bell, sound tones).",
SW = "Binary switch state codes (e.g. lid closed, tablet mode).",
SYN = "Synchronization event codes used to group events or signal device status changes.",
}
local function write_ecodes_enums(path)
local text = {
"---@meta _",
"",
generated_from,
}
for prefix, ecodes in spairs(ecodes_by_prefix) do
if prefixes[prefix] then
text[#text + 1] = ""
text[#text + 1] = "---"
text[#text + 1] = fmt("---%s", descriptions[prefix] or (prefix .. " codes."))
text[#text + 1] = "---"
text[#text + 1] = "-- stylua: ignore"
text[#text + 1] = fmt("---@enum evdev.ecodes.%s", prefix:lower())
text[#text + 1] = fmt("local %s = %s", prefix, stringify(ecodes))
end
end
writefile(path, concat(text, "\n"), "\n")
end
local function main()
load_ecodes()
write_runtime_ecodes("src/evdev/ecodes.lua")
write_ecodes_class("types/ecodes.d.lua")
write_ecodes_enums("types/_enums.d.lua")
end
main()