Keyboard (Linux): optimises detection code & reduces syscalls

Ref: #2220
This commit is contained in:
Carter Li
2026-03-11 10:41:54 +08:00
parent 7cadece9a0
commit 4a4afdadb8
+76 -78
View File
@@ -2,41 +2,7 @@
#include "common/io.h"
#include "common/stringUtils.h"
static bool isRealKeyboard(uint32_t index, FFstrbuf* path)
{
// Check EV_REP (auto-repeat, bit 20) to filter pseudo-keyboards (Power Button, PC Speaker).
ffStrbufSetF(path, "/sys/class/input/event%u/device/capabilities/ev", (unsigned) index);
{
FF_STRBUF_AUTO_DESTROY caps = ffStrbufCreate();
if (!ffReadFileBuffer(path->chars, &caps))
return false;
ffStrbufTrimRightSpace(&caps);
unsigned long val = strtoul(caps.chars, NULL, 16);
if (!(val & (1UL << 20))) // EV_REP
return false;
}
// Check KEY_A (bit 30) to filter media remotes and headset controls.
// The key capability bitmap is space-separated hex longs, MSB first;
// KEY_A falls in the last (least significant) word on all architectures.
ffStrbufSetF(path, "/sys/class/input/event%u/device/capabilities/key", (unsigned) index);
{
FF_STRBUF_AUTO_DESTROY caps = ffStrbufCreate();
if (!ffReadFileBuffer(path->chars, &caps))
return false;
ffStrbufTrimRightSpace(&caps);
const char* lastWord = strrchr(caps.chars, ' ');
lastWord = lastWord ? lastWord + 1 : caps.chars;
unsigned long val = strtoul(lastWord, NULL, 16);
if (!(val & (1UL << 30))) // KEY_A
return false;
}
return true;
}
#include <linux/input-event-codes.h>
const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
{
@@ -46,58 +12,90 @@ const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
if (!ffAppendFileBuffer("/proc/bus/input/devices", &content))
return "ffAppendFileBuffer(\"/proc/bus/input/devices\") == NULL";
uint64_t flags = 0;
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
FFstrbuf kbd = ffStrbufCreateStatic("kbd");
FFKeyboardDevice device = {
.name = ffStrbufCreate(),
.serial = ffStrbufCreate()
};
char* line = NULL;
size_t len = 0;
while (ffStrbufGetline(&line, &len, &content))
{
if (!ffStrStartsWith(line, "H: Handlers="))
continue;
const char* handlers = line + strlen("H: Handlers=");
if (!ffStrbufMatchSeparatedS(&kbd, handlers, ' '))
continue;
// Find "eventN" token and extract the index
const char* eventStr = strstr(handlers, "event");
if (!eventStr)
continue;
char* pend = NULL;
uint32_t eventIndex = (uint32_t) strtoul(eventStr + strlen("event"), &pend, 10);
if (pend == eventStr + strlen("event"))
continue;
// Skip duplicates (dedup bitmap covers indices 0-63; higher indices are not deduped)
if (eventIndex < 64 && (flags & (1ULL << eventIndex)))
continue;
if (!isRealKeyboard(eventIndex, &path))
continue;
ffStrbufSetF(&path, "/sys/class/input/event%u/device/name", (unsigned) eventIndex);
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
if (ffAppendFileBuffer(path.chars, &name))
switch (line[0])
{
if (eventIndex < 64)
flags |= (1ULL << eventIndex);
case 'N': {
const uint32_t prefixLen = strlen("N: Name=");
if (__builtin_expect(len <= prefixLen, false)) continue;
const char* name = line + prefixLen;
const uint32_t nameLen = (uint32_t) len - prefixLen;
ffStrbufSetNS(&device.name, nameLen, name);
ffStrbufTrim(&device.name, '"');
continue;
}
case 'H': {
const uint32_t prefixLen = strlen("H: Handlers=");
if (__builtin_expect(len <= prefixLen, false)) continue;
const char* handlers = line + prefixLen;
const uint32_t handlersLen = (uint32_t) len - prefixLen;
if (!ffStrbufMatchSeparatedNS(&kbd, handlersLen, handlers, ' '))
goto skipDevice;
continue;
}
case 'B': {
const char* bits = line + strlen("B: ");
if (ffStrStartsWith(bits, "EV="))
{
// Check EV_REP (auto-repeat, bit 20) to filter pseudo-keyboards (Power Button, PC Speaker).
const char* evBits = bits + strlen("EV=");
uint64_t val = strtoull(evBits, NULL, 16);
if (!(val & (1ULL << EV_REP)))
goto skipDevice;
}
else if (ffStrStartsWith(bits, "KEY="))
{
// Check KEY_A (bit 30) to filter media remotes and headset controls.
// The key capability bitmap is space-separated hex longs, MSB first;
// KEY_A falls in the last (least significant) word on all architectures.
const char* keyBits = bits + strlen("KEY=");
const char* lastWord = memrchr(keyBits, ' ', len - (size_t) (keyBits - line));
lastWord = lastWord ? lastWord + 1 : keyBits;
ffStrbufTrimRightSpace(&name);
ffStrbufSubstrBefore(&path, path.length - (uint32_t) strlen("name"));
FFKeyboardDevice* device = (FFKeyboardDevice*) ffListAdd(devices);
ffStrbufInitMove(&device->name, &name);
ffStrbufInit(&device->serial);
ffStrbufAppendS(&path, "uniq");
if (ffAppendFileBuffer(path.chars, &device->serial))
ffStrbufTrimRightSpace(&device->serial);
uint64_t val = strtoull(lastWord, NULL, 16);
if (!(val & (1ULL << KEY_A)))
goto skipDevice;
}
continue;
}
case 'U': {
const uint32_t prefixLen = strlen("U: Uniq=");
if (__builtin_expect(len <= prefixLen, false)) continue;
const char* uniq = line + prefixLen;
const uint32_t uniqLen = (uint32_t) len - prefixLen;
ffStrbufSetNS(&device.serial, uniqLen, uniq);
continue;
}
case '\0':
// End of device entry; add to list if it has a name.
if (device.name.length > 0)
{
FFKeyboardDevice* added = (FFKeyboardDevice*) ffListAdd(devices);
ffStrbufInitMove(&added->name, &device.name);
ffStrbufInitMove(&added->serial, &device.serial);
}
continue;
default:
continue;
}
skipDevice:
// Skip to the end of the current device entry.
while (line[0] != '\0' && ffStrbufGetline(&line, &len, &content))
;
// Despite the fn name, it resets the string buffer to initial state
ffStrbufDestroy(&device.name);
ffStrbufDestroy(&device.serial);
}
return NULL;