summaryrefslogtreecommitdiffstats
path: root/src/detection/keyboard
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/keyboard')
-rw-r--r--src/detection/keyboard/keyboard.h8
-rw-r--r--src/detection/keyboard/keyboard_apple.c36
-rw-r--r--src/detection/keyboard/keyboard_bsd.c92
-rw-r--r--src/detection/keyboard/keyboard_haiku.cpp27
-rw-r--r--src/detection/keyboard/keyboard_linux.c104
-rw-r--r--src/detection/keyboard/keyboard_nosupport.c5
-rw-r--r--src/detection/keyboard/keyboard_windows.c85
7 files changed, 357 insertions, 0 deletions
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 <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDLib.h>
+
+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 <stdio.h>
+#include <fcntl.h>
+#include <usbhid.h>
+#include <sys/kbio.h>
+
+#if __has_include(<dev/usb/usb_ioctl.h>)
+ #include <dev/usb/usb_ioctl.h> // FreeBSD
+#else
+ #include <bus/u4b/usb_ioctl.h> // 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 <interface/Input.h>
+#include <support/List.h>
+
+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 <linux/input-event-codes.h>
+
+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 <windows.h>
+#include <hidsdi.h>
+#include <cfgmgr32.h>
+#include <devpkey.h>
+
+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;
+}