Skip to content

Commit 330c05a

Browse files
committed
fix: code review
1 parent 251172a commit 330c05a

1 file changed

Lines changed: 57 additions & 80 deletions

File tree

src/detection/keyboard/keyboard_linux.c

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

5-
static void addDevice(FFlist* devices, uint64_t* flags, uint32_t index, FFstrbuf* path)
5+
static bool isRealKeyboard(uint32_t index, FFstrbuf* path)
66
{
7-
if (index >= 64 || (*flags & (1ULL << index)))
8-
return;
9-
10-
ffStrbufSetF(path, "/sys/class/input/event%u/device/name", (unsigned) index);
11-
12-
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
13-
if (ffAppendFileBuffer(path->chars, &name))
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);
149
{
15-
*flags |= (1ULL << index);
16-
ffStrbufTrimRightSpace(&name);
17-
ffStrbufSubstrBefore(path, path->length - (uint32_t) strlen("name"));
18-
19-
FFKeyboardDevice* device = (FFKeyboardDevice*) ffListAdd(devices);
20-
ffStrbufInitMove(&device->name, &name);
21-
ffStrbufInit(&device->serial);
22-
23-
ffStrbufAppendS(path, "uniq");
24-
if (ffAppendFileBuffer(path->chars, &device->serial))
25-
ffStrbufTrimRightSpace(&device->serial);
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;
2618
}
27-
}
28-
29-
static bool detectFromByPath(FFlist* devices, uint64_t* flags, FFstrbuf* path)
30-
{
31-
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/dev/input/by-path/");
32-
if (dirp == NULL)
33-
return false;
3419

35-
struct dirent* entry;
36-
while ((entry = readdir(dirp)) != NULL)
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);
3724
{
38-
if (!ffStrEndsWith(entry->d_name, "-event-kbd"))
39-
continue;
25+
FF_STRBUF_AUTO_DESTROY caps = ffStrbufCreate();
26+
if (!ffReadFileBuffer(path->chars, &caps))
27+
return false;
4028

41-
char buffer[32]; // `../eventXX`
42-
ssize_t len = readlinkat(dirfd(dirp), entry->d_name, buffer, ARRAY_SIZE(buffer) - 1);
43-
if (len <= (ssize_t) strlen("../event") || !ffStrStartsWith(buffer, "../event")) continue;
44-
buffer[len] = 0;
29+
ffStrbufTrimRightSpace(&caps);
30+
const char* lastWord = strrchr(caps.chars, ' ');
31+
lastWord = lastWord ? lastWord + 1 : caps.chars;
4532

46-
char* pend = NULL;
47-
uint32_t index = (uint32_t) strtoul(buffer + strlen("../event"), &pend, 10);
48-
if (pend == buffer + strlen("../event")) continue;
49-
50-
addDevice(devices, flags, index, path);
33+
unsigned long val = strtoul(lastWord, NULL, 16);
34+
if (!(val & (1UL << 30))) // KEY_A
35+
return false;
5136
}
5237

5338
return true;
5439
}
5540

56-
static bool hasAutoRepeat(uint32_t index, FFstrbuf* path)
57-
{
58-
// Filter out pseudo-keyboards (Power Button, PC Speaker) by checking for
59-
// EV_REP (auto-repeat, bit 20) in the device's event capabilities.
60-
// Real keyboards and macro pads support auto-repeat; system buttons don't.
61-
ffStrbufSetF(path, "/sys/class/input/event%u/device/capabilities/ev", (unsigned) index);
62-
63-
FF_STRBUF_AUTO_DESTROY caps = ffStrbufCreate();
64-
if (!ffReadFileBuffer(path->chars, &caps))
65-
return false;
66-
67-
ffStrbufTrimRightSpace(&caps);
68-
unsigned long val = strtoul(caps.chars, NULL, 16);
69-
return (val & (1UL << 20)) != 0; // EV_REP
70-
}
71-
72-
static bool detectFromProc(FFlist* devices, uint64_t* flags, FFstrbuf* path)
41+
const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
7342
{
74-
// Bluetooth and other virtual keyboards may not appear in /dev/input/by-path/.
75-
// Parse /proc/bus/input/devices to find any keyboard with a "kbd" handler.
43+
// Parse /proc/bus/input/devices to find keyboards with a "kbd" handler.
44+
// This detects both wired and Bluetooth keyboards uniformly.
7645
FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate();
7746
if (!ffAppendFileBuffer("/proc/bus/input/devices", &content))
78-
return false;
47+
return "ffAppendFileBuffer(\"/proc/bus/input/devices\") == NULL";
48+
49+
uint64_t flags = 0;
50+
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
7951

8052
const char* line = content.chars;
8153
while (line && *line)
@@ -102,36 +74,41 @@ static bool detectFromProc(FFlist* devices, uint64_t* flags, FFstrbuf* path)
10274
else if (wordLen > strlen("event") && memcmp(wordStart, "event", strlen("event")) == 0)
10375
{
10476
char* pend = NULL;
105-
eventIndex = (uint32_t) strtoul(wordStart + 5, &pend, 10);
106-
if (pend == wordStart + 5) eventIndex = UINT32_MAX;
77+
eventIndex = (uint32_t) strtoul(wordStart + strlen("event"), &pend, 10);
78+
if (pend == wordStart + strlen("event")) eventIndex = UINT32_MAX;
10779
}
10880
}
10981

110-
if (hasKbd && eventIndex != UINT32_MAX && hasAutoRepeat(eventIndex, path))
111-
addDevice(devices, flags, eventIndex, path);
112-
}
82+
// Skip duplicates (dedup bitmap covers indices 0-63; higher indices are not deduped)
83+
bool seen = eventIndex < 64 && (flags & (1ULL << eventIndex));
11384

114-
const char* next = strchr(line, '\n');
115-
line = next ? next + 1 : NULL;
116-
}
85+
if (hasKbd && eventIndex != UINT32_MAX && !seen && isRealKeyboard(eventIndex, &path))
86+
{
87+
ffStrbufSetF(&path, "/sys/class/input/event%u/device/name", (unsigned) eventIndex);
11788

118-
return true;
119-
}
89+
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
90+
if (ffAppendFileBuffer(path.chars, &name))
91+
{
92+
if (eventIndex < 64)
93+
flags |= (1ULL << eventIndex);
12094

121-
const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
122-
{
123-
uint64_t flags = 0;
124-
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
95+
ffStrbufTrimRightSpace(&name);
96+
ffStrbufSubstrBefore(&path, path.length - (uint32_t) strlen("name"));
12597

126-
// Prefer /dev/input/by-path/ for wired/USB keyboards
127-
bool byPathOk = detectFromByPath(devices, &flags, &path);
98+
FFKeyboardDevice* device = (FFKeyboardDevice*) ffListAdd(devices);
99+
ffStrbufInitMove(&device->name, &name);
100+
ffStrbufInit(&device->serial);
128101

129-
// Fall back to /proc/bus/input/devices for Bluetooth and other keyboards
130-
// that don't appear in by-path. The flags bitmap prevents duplicates.
131-
bool procOk = detectFromProc(devices, &flags, &path);
102+
ffStrbufAppendS(&path, "uniq");
103+
if (ffAppendFileBuffer(path.chars, &device->serial))
104+
ffStrbufTrimRightSpace(&device->serial);
105+
}
106+
}
107+
}
132108

133-
if (!byPathOk && !procOk)
134-
return "Failed to read both /dev/input/by-path/ and /proc/bus/input/devices";
109+
const char* next = strchr(line, '\n');
110+
line = next ? next + 1 : NULL;
111+
}
135112

136113
return NULL;
137114
}

0 commit comments

Comments
 (0)