Skip to content

Commit 4a4afda

Browse files
committed
Keyboard (Linux): optimises detection code & reduces syscalls
Ref: #2220
1 parent 7cadece commit 4a4afda

1 file changed

Lines changed: 77 additions & 79 deletions

File tree

src/detection/keyboard/keyboard_linux.c

Lines changed: 77 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,7 @@
22
#include "common/io.h"
33
#include "common/stringUtils.h"
44

5-
static bool isRealKeyboard(uint32_t index, FFstrbuf* path)
6-
{
7-
// Check EV_REP (auto-repeat, bit 20) to filter pseudo-keyboards (Power Button, PC Speaker).
8-
ffStrbufSetF(path, "/sys/class/input/event%u/device/capabilities/ev", (unsigned) index);
9-
{
10-
FF_STRBUF_AUTO_DESTROY caps = ffStrbufCreate();
11-
if (!ffReadFileBuffer(path->chars, &caps))
12-
return false;
13-
14-
ffStrbufTrimRightSpace(&caps);
15-
unsigned long val = strtoul(caps.chars, NULL, 16);
16-
if (!(val & (1UL << 20))) // EV_REP
17-
return false;
18-
}
19-
20-
// Check KEY_A (bit 30) to filter media remotes and headset controls.
21-
// The key capability bitmap is space-separated hex longs, MSB first;
22-
// KEY_A falls in the last (least significant) word on all architectures.
23-
ffStrbufSetF(path, "/sys/class/input/event%u/device/capabilities/key", (unsigned) index);
24-
{
25-
FF_STRBUF_AUTO_DESTROY caps = ffStrbufCreate();
26-
if (!ffReadFileBuffer(path->chars, &caps))
27-
return false;
28-
29-
ffStrbufTrimRightSpace(&caps);
30-
const char* lastWord = strrchr(caps.chars, ' ');
31-
lastWord = lastWord ? lastWord + 1 : caps.chars;
32-
33-
unsigned long val = strtoul(lastWord, NULL, 16);
34-
if (!(val & (1UL << 30))) // KEY_A
35-
return false;
36-
}
37-
38-
return true;
39-
}
5+
#include <linux/input-event-codes.h>
406

417
const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
428
{
@@ -46,58 +12,90 @@ const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
4612
if (!ffAppendFileBuffer("/proc/bus/input/devices", &content))
4713
return "ffAppendFileBuffer(\"/proc/bus/input/devices\") == NULL";
4814

49-
uint64_t flags = 0;
50-
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
5115
FFstrbuf kbd = ffStrbufCreateStatic("kbd");
5216

17+
FFKeyboardDevice device = {
18+
.name = ffStrbufCreate(),
19+
.serial = ffStrbufCreate()
20+
};
21+
5322
char* line = NULL;
5423
size_t len = 0;
5524
while (ffStrbufGetline(&line, &len, &content))
5625
{
57-
if (!ffStrStartsWith(line, "H: Handlers="))
58-
continue;
59-
60-
const char* handlers = line + strlen("H: Handlers=");
61-
62-
if (!ffStrbufMatchSeparatedS(&kbd, handlers, ' '))
63-
continue;
64-
65-
// Find "eventN" token and extract the index
66-
const char* eventStr = strstr(handlers, "event");
67-
if (!eventStr)
68-
continue;
69-
70-
char* pend = NULL;
71-
uint32_t eventIndex = (uint32_t) strtoul(eventStr + strlen("event"), &pend, 10);
72-
if (pend == eventStr + strlen("event"))
73-
continue;
74-
75-
// Skip duplicates (dedup bitmap covers indices 0-63; higher indices are not deduped)
76-
if (eventIndex < 64 && (flags & (1ULL << eventIndex)))
77-
continue;
78-
79-
if (!isRealKeyboard(eventIndex, &path))
80-
continue;
81-
82-
ffStrbufSetF(&path, "/sys/class/input/event%u/device/name", (unsigned) eventIndex);
83-
84-
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
85-
if (ffAppendFileBuffer(path.chars, &name))
26+
switch (line[0])
8627
{
87-
if (eventIndex < 64)
88-
flags |= (1ULL << eventIndex);
89-
90-
ffStrbufTrimRightSpace(&name);
91-
ffStrbufSubstrBefore(&path, path.length - (uint32_t) strlen("name"));
92-
93-
FFKeyboardDevice* device = (FFKeyboardDevice*) ffListAdd(devices);
94-
ffStrbufInitMove(&device->name, &name);
95-
ffStrbufInit(&device->serial);
96-
97-
ffStrbufAppendS(&path, "uniq");
98-
if (ffAppendFileBuffer(path.chars, &device->serial))
99-
ffStrbufTrimRightSpace(&device->serial);
28+
case 'N': {
29+
const uint32_t prefixLen = strlen("N: Name=");
30+
if (__builtin_expect(len <= prefixLen, false)) continue;
31+
const char* name = line + prefixLen;
32+
const uint32_t nameLen = (uint32_t) len - prefixLen;
33+
ffStrbufSetNS(&device.name, nameLen, name);
34+
ffStrbufTrim(&device.name, '"');
35+
continue;
36+
}
37+
case 'H': {
38+
const uint32_t prefixLen = strlen("H: Handlers=");
39+
if (__builtin_expect(len <= prefixLen, false)) continue;
40+
const char* handlers = line + prefixLen;
41+
const uint32_t handlersLen = (uint32_t) len - prefixLen;
42+
if (!ffStrbufMatchSeparatedNS(&kbd, handlersLen, handlers, ' '))
43+
goto skipDevice;
44+
continue;
45+
}
46+
case 'B': {
47+
const char* bits = line + strlen("B: ");
48+
if (ffStrStartsWith(bits, "EV="))
49+
{
50+
// Check EV_REP (auto-repeat, bit 20) to filter pseudo-keyboards (Power Button, PC Speaker).
51+
const char* evBits = bits + strlen("EV=");
52+
uint64_t val = strtoull(evBits, NULL, 16);
53+
if (!(val & (1ULL << EV_REP)))
54+
goto skipDevice;
55+
}
56+
else if (ffStrStartsWith(bits, "KEY="))
57+
{
58+
// Check KEY_A (bit 30) to filter media remotes and headset controls.
59+
// The key capability bitmap is space-separated hex longs, MSB first;
60+
// KEY_A falls in the last (least significant) word on all architectures.
61+
const char* keyBits = bits + strlen("KEY=");
62+
const char* lastWord = memrchr(keyBits, ' ', len - (size_t) (keyBits - line));
63+
lastWord = lastWord ? lastWord + 1 : keyBits;
64+
65+
uint64_t val = strtoull(lastWord, NULL, 16);
66+
if (!(val & (1ULL << KEY_A)))
67+
goto skipDevice;
68+
}
69+
continue;
70+
}
71+
case 'U': {
72+
const uint32_t prefixLen = strlen("U: Uniq=");
73+
if (__builtin_expect(len <= prefixLen, false)) continue;
74+
const char* uniq = line + prefixLen;
75+
const uint32_t uniqLen = (uint32_t) len - prefixLen;
76+
ffStrbufSetNS(&device.serial, uniqLen, uniq);
77+
continue;
78+
}
79+
case '\0':
80+
// End of device entry; add to list if it has a name.
81+
if (device.name.length > 0)
82+
{
83+
FFKeyboardDevice* added = (FFKeyboardDevice*) ffListAdd(devices);
84+
ffStrbufInitMove(&added->name, &device.name);
85+
ffStrbufInitMove(&added->serial, &device.serial);
86+
}
87+
continue;
88+
default:
89+
continue;
10090
}
91+
92+
skipDevice:
93+
// Skip to the end of the current device entry.
94+
while (line[0] != '\0' && ffStrbufGetline(&line, &len, &content))
95+
;
96+
// Despite the fn name, it resets the string buffer to initial state
97+
ffStrbufDestroy(&device.name);
98+
ffStrbufDestroy(&device.serial);
10199
}
102100

103101
return NULL;

0 commit comments

Comments
 (0)