From 76424950e373d3b04ac3dd13019151bfba3e8423 Mon Sep 17 00:00:00 2001 From: sumuel Date: Mon, 17 Aug 2026 20:44:55 +0000 Subject: Add the files --- src/detection/keyboard/keyboard.h | 8 +++ src/detection/keyboard/keyboard_apple.c | 36 ++++++++++ src/detection/keyboard/keyboard_bsd.c | 92 ++++++++++++++++++++++++ src/detection/keyboard/keyboard_haiku.cpp | 27 ++++++++ src/detection/keyboard/keyboard_linux.c | 104 ++++++++++++++++++++++++++++ src/detection/keyboard/keyboard_nosupport.c | 5 ++ src/detection/keyboard/keyboard_windows.c | 85 +++++++++++++++++++++++ 7 files changed, 357 insertions(+) create mode 100644 src/detection/keyboard/keyboard.h create mode 100644 src/detection/keyboard/keyboard_apple.c create mode 100644 src/detection/keyboard/keyboard_bsd.c create mode 100644 src/detection/keyboard/keyboard_haiku.cpp create mode 100644 src/detection/keyboard/keyboard_linux.c create mode 100644 src/detection/keyboard/keyboard_nosupport.c create mode 100644 src/detection/keyboard/keyboard_windows.c (limited to 'src/detection/keyboard') diff --git a/src/detection/keyboard/keyboard.h b/src/detection/keyboard/keyboard.h new file mode 100644 index 0000000..6838a9c --- /dev/null +++ b/src/detection/keyboard/keyboard.h @@ -0,0 +1,8 @@ +#include "fastfetch.h" + +typedef struct FFKeyboardDevice { + FFstrbuf serial; + FFstrbuf name; +} FFKeyboardDevice; + +const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */); diff --git a/src/detection/keyboard/keyboard_apple.c b/src/detection/keyboard/keyboard_apple.c new file mode 100644 index 0000000..e2ecc37 --- /dev/null +++ b/src/detection/keyboard/keyboard_apple.c @@ -0,0 +1,36 @@ +#include "keyboard.h" +#include "common/apple/cf_helpers.h" +#include "common/mallocHelper.h" + +#include +#include + +static void enumSet(IOHIDDeviceRef value, FFlist* results) { + FFKeyboardDevice* device = FF_LIST_ADD(FFKeyboardDevice, *results); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->name); + + CFStringRef product = IOHIDDeviceGetProperty(value, CFSTR(kIOHIDProductKey)); + ffCfStrGetString(product, &device->name); + + CFStringRef serialNumber = IOHIDDeviceGetProperty(value, CFSTR(kIOHIDSerialNumberKey)); + ffCfStrGetString(serialNumber, &device->serial); +} + +const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */) { + IOHIDManagerRef FF_CFTYPE_AUTO_RELEASE manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone) != kIOReturnSuccess) { + return "IOHIDManagerOpen() failed"; + } + + CFDictionaryRef FF_CFTYPE_AUTO_RELEASE matching1 = CFDictionaryCreate(kCFAllocatorDefault, (const void**) (CFStringRef[]) { CFSTR(kIOHIDDeviceUsagePageKey), CFSTR(kIOHIDDeviceUsageKey) }, (const void**) (CFNumberRef[]) { ffCfCreateInt(kHIDPage_GenericDesktop), ffCfCreateInt(kHIDUsage_GD_Keyboard) }, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + IOHIDManagerSetDeviceMatching(manager, matching1); + + CFSetRef FF_CFTYPE_AUTO_RELEASE set = IOHIDManagerCopyDevices(manager); + if (set) { + CFSetApplyFunction(set, (CFSetApplierFunction) &enumSet, devices); + } + IOHIDManagerClose(manager, kIOHIDOptionsTypeNone); + + return NULL; +} diff --git a/src/detection/keyboard/keyboard_bsd.c b/src/detection/keyboard/keyboard_bsd.c new file mode 100644 index 0000000..4b0ee57 --- /dev/null +++ b/src/detection/keyboard/keyboard_bsd.c @@ -0,0 +1,92 @@ +#include "keyboard.h" +#include "common/io.h" + +#include +#include +#include +#include + +#if __has_include() + #include // FreeBSD +#else + #include // DragonFly +#endif + +static const char* detectByIoctl(FFlist* devices) { + keyboard_info_t kbdInfo; + if (ioctl(STDIN_FILENO, KDGKBINFO, &kbdInfo) != 0) { + return "ioctl(KDGKBINFO) failed"; + } + + FFKeyboardDevice* device = FF_LIST_ADD(FFKeyboardDevice, *devices); + + switch (kbdInfo.kb_type) { + case KB_84: + ffStrbufInitS(&device->name, "AT 84-key keyboard"); + break; + case KB_101: + ffStrbufInitS(&device->name, "AT 101/102-key keyboard"); + break; + default: + ffStrbufInitS(&device->name, "Unknown keyboard"); + break; + } + + ffStrbufAppendF(&device->name, " (kbd%d)", kbdInfo.kb_index); + + ffStrbufInit(&device->serial); + return NULL; +} + +#define MAX_UHID_KBDS 64 + +static const char* detectByUsbhid(FFlist* devices) { + char path[16]; + for (int i = 0; i < MAX_UHID_KBDS; i++) { + snprintf(path, ARRAY_SIZE(path), "/dev/uhid%d", i); + FF_AUTO_CLOSE_FD int fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + if (errno == ENOENT) { + break; // No more devices + } + continue; // Device not found + } + + report_desc_t repDesc = hid_get_report_desc(fd); + if (!repDesc) { + continue; + } + + int reportId = hid_get_report_id(fd); + + struct hid_data* hData = hid_start_parse(repDesc, 0, reportId); + if (hData) { + struct hid_item hItem; + while (hid_get_item(hData, &hItem) > 0) { + if (HID_PAGE(hItem.usage) != 1 || HID_USAGE(hItem.usage) != 6) { + continue; + } + + struct usb_device_info di; + if (ioctl(fd, USB_GET_DEVICEINFO, &di) != -1) { + FFKeyboardDevice* device = FF_LIST_ADD(FFKeyboardDevice, *devices); + ffStrbufInitS(&device->serial, di.udi_serial); + ffStrbufInitS(&device->name, di.udi_product); + } + } + hid_end_parse(hData); + } + + hid_dispose_report_desc(repDesc); + } + + return NULL; +} + +const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */) { + detectByUsbhid(devices); + if (devices->length > 0) { + return NULL; + } + return detectByIoctl(devices); +} diff --git a/src/detection/keyboard/keyboard_haiku.cpp b/src/detection/keyboard/keyboard_haiku.cpp new file mode 100644 index 0000000..dc1d28a --- /dev/null +++ b/src/detection/keyboard/keyboard_haiku.cpp @@ -0,0 +1,27 @@ +extern "C" { +#include "keyboard.h" +} + +#include +#include + +const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */) { + BList list; + + if (get_input_devices(&list) != B_OK) { + return "get_input_devices() failed"; + } + + for (int32 i = 0, n = list.CountItems(); i < n; i++) { + BInputDevice* device = (BInputDevice*) list.ItemAt(i); + if (device->Type() != B_KEYBOARD_DEVICE || !device->IsRunning()) { + continue; + } + + FFKeyboardDevice* item = FF_LIST_ADD(FFKeyboardDevice, *devices); + ffStrbufInit(&item->serial); + ffStrbufInitS(&item->name, device->Name()); + } + + return NULL; +} diff --git a/src/detection/keyboard/keyboard_linux.c b/src/detection/keyboard/keyboard_linux.c new file mode 100644 index 0000000..1a29abb --- /dev/null +++ b/src/detection/keyboard/keyboard_linux.c @@ -0,0 +1,104 @@ +#include "keyboard.h" +#include "common/io.h" +#include "common/strutil.h" + +#include + +const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */) { + // Parse /proc/bus/input/devices to find keyboards with a "kbd" handler. + // This detects both wired and Bluetooth keyboards uniformly. + FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); + if (!ffAppendFileBuffer("/proc/bus/input/devices", &content)) { + return "ffAppendFileBuffer(\"/proc/bus/input/devices\") == NULL"; + } + + FFstrbuf kbd = ffStrbufCreateStatic("kbd"); + + FFKeyboardDevice device = { + .name = ffStrbufCreate(), + .serial = ffStrbufCreate() + }; + + char* line = NULL; + size_t len = 0; + while (ffStrbufGetline(&line, &len, &content)) { + switch (line[0]) { + 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; + + 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 = FF_LIST_ADD(FFKeyboardDevice, *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; +} diff --git a/src/detection/keyboard/keyboard_nosupport.c b/src/detection/keyboard/keyboard_nosupport.c new file mode 100644 index 0000000..6523ed4 --- /dev/null +++ b/src/detection/keyboard/keyboard_nosupport.c @@ -0,0 +1,5 @@ +#include "keyboard.h" + +const char* ffDetectKeyboard(FF_A_UNUSED FFlist* devices /* List of FFKeyboardDevice */) { + return "No mouse support on this platform"; +} diff --git a/src/detection/keyboard/keyboard_windows.c b/src/detection/keyboard/keyboard_windows.c new file mode 100644 index 0000000..272f632 --- /dev/null +++ b/src/detection/keyboard/keyboard_windows.c @@ -0,0 +1,85 @@ +#define INITGUID + +#include "keyboard.h" +#include "common/io.h" +#include "common/mallocHelper.h" +#include "common/windows/unicode.h" + +#include +#include +#include +#include + +const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */) { + UINT nDevices = 0; + if (GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST))) { + return "GetRawInputDeviceList(NULL) failed"; + } + if (nDevices == 0) { + return "No HID devices found"; + } + + RAWINPUTDEVICELIST* FF_AUTO_FREE pRawInputDeviceList = (RAWINPUTDEVICELIST*) malloc(sizeof(RAWINPUTDEVICELIST) * nDevices); + if ((nDevices = GetRawInputDeviceList(pRawInputDeviceList, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT) -1) { + return "GetRawInputDeviceList(pRawInputDeviceList) failed"; + } + + for (UINT i = 0; i < nDevices; ++i) { + if (pRawInputDeviceList[i].dwType != RIM_TYPEKEYBOARD) { + continue; + } + + HANDLE hDevice = pRawInputDeviceList[i].hDevice; + + RID_DEVICE_INFO rdi; + UINT rdiSize = sizeof(rdi); + if (GetRawInputDeviceInfoW(hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == (UINT) -1) { + continue; + } + + WCHAR devName[MAX_PATH]; + UINT nameSize = MAX_PATH; + if (GetRawInputDeviceInfoW(hDevice, RIDI_DEVICENAME, devName, &nameSize) == (UINT) -1) { + continue; + } + + FFKeyboardDevice* device = FF_LIST_ADD(FFKeyboardDevice, *devices); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->name); + + wchar_t buffer[MAX_PATH]; + + HANDLE FF_AUTO_CLOSE_FD hHidFile = CreateFileW(devName, 0 /* must be 0 instead of GENERIC_READ */, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hHidFile != INVALID_HANDLE_VALUE) { + if (HidD_GetProductString(hHidFile, buffer, (ULONG) sizeof(buffer))) { + ffStrbufSetWS(&device->name, buffer); + } + + if (HidD_GetSerialNumberString(hHidFile, buffer, sizeof(buffer))) { + ffStrbufSetWS(&device->serial, buffer); + } + } + + if (!device->name.length) { + // https://stackoverflow.com/a/64321096/9976392 + DEVPROPTYPE propertyType; + ULONG propertySize = sizeof(buffer); + + if (CM_Get_Device_Interface_PropertyW(devName, &DEVPKEY_Device_InstanceId, &propertyType, (PBYTE) buffer, &propertySize, 0) == CR_SUCCESS) { + DEVINST devInst; + if (CM_Locate_DevNodeW(&devInst, buffer, CM_LOCATE_DEVNODE_NORMAL) == CR_SUCCESS) { + propertySize = sizeof(buffer); + if (CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_NAME, &propertyType, (PBYTE) buffer, &propertySize, 0) == CR_SUCCESS) { + ffStrbufSetWS(&device->name, buffer); + } + } + } + } + + if (!device->name.length) { + ffStrbufSetF(&device->name, "Unknown device %04X-%04X", (unsigned) rdi.hid.dwVendorId, (unsigned) rdi.hid.dwProductId); + } + } + + return NULL; +} -- cgit v1.2.3