#include #include #include #include #include #include #include #include #include #include #define fatal(str) perror(str); exit(1); #define UINPUT_DEVICE_NAME "Auxillary Corsair K95 Key Input" #define HID_DEVICE_ID "1B1C:1B11" #define REPORT_ID 0x03 // unused, but doesnt affect output. "documentation," i guess #define KEY_G1 0x00000001 #define KEY_G2 0x00000002 #define KEY_G3 0x00000004 #define KEY_G4 0x00000008 #define KEY_G5 0x00000010 #define KEY_G6 0x00000020 #define KEY_G7 0x00000040 #define KEY_G8 0x00000080 #define KEY_G9 0x00000100 #define KEY_G10 0x00000200 #define KEY_G11 0x00000400 #define KEY_G12 0x00000800 #define KEY_G13 0x00001000 #define KEY_G14 0x00002000 #define KEY_G15 0x00004000 #define KEY_G16 0x00008000 #define KEY_G17 0x00010000 #define KEY_G18 0x00020000 #define KEY_BRIGHTNESS 0x00040000 #define KEY_SUPER_LOCK 0x00080000 #define KEY_MR 0x00100000 #define KEY_M1 0x00200000 #define KEY_M2 0x00400000 #define KEY_M3 0x00800000 bool locked = false; unsigned int old_state; unsigned int state; struct uinput_user_dev uinput_device; unsigned short mapping[24] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24 }; // also unused void _debug_print_state() { printf("\n--------\n"); printf("%d %d %d\n", (state & KEY_G1) > 0, (state & KEY_G2) > 0, (state & KEY_G3) > 0); printf("%d %d %d\n", (state & KEY_G4) > 0, (state & KEY_G5) > 0, (state & KEY_G6) > 0); printf("\n"); printf("%d %d %d\n", (state & KEY_G7) > 0, (state & KEY_G8) > 0, (state & KEY_G9) > 0); printf("%d %d %d\n", (state & KEY_G10) > 0, (state & KEY_G11) > 0, (state & KEY_G12) > 0); printf("\n"); printf("%d %d %d\n", (state & KEY_G13) > 0, (state & KEY_G14) > 0, (state & KEY_G15) > 0); printf("%d %d %d\n", (state & KEY_G16) > 0, (state & KEY_G17) > 0, (state & KEY_G18) > 0); printf("\n"); printf("%d %d %d %d", (state & KEY_MR) > 0, (state & KEY_M1) > 0, (state & KEY_M2) > 0, (state & KEY_M3) > 0); printf(" | "); printf("%d %d", (state & KEY_BRIGHTNESS) > 0, (state & KEY_SUPER_LOCK) > 0); printf("\n"); printf("\n%06x\n", state); printf("--------\n"); } void read_report(int fd) { unsigned char buf[64]; int n = read(fd, buf, sizeof(buf)); if (n > 0) { unsigned char id = buf[0]; if (id == REPORT_ID) { // horrible bit fuckery old_state = state; // G keys state = buf[16]; state |= (buf[17] & 0x0f) << 8; state |= buf[18] << 10; // brightness / super lock state |= buf[9] << 11; state |= buf[13] << 19; // M keys state |= (buf[17] & 0xf0) << 16; } } } void create_uinput_device(int fd) { memset(&uinput_device, 0, sizeof(uinput_device)); strncpy(uinput_device.name, UINPUT_DEVICE_NAME, UINPUT_MAX_NAME_SIZE); uinput_device.id.bustype = BUS_USB; uinput_device.id.vendor = 0x1; uinput_device.id.product = 0x1; uinput_device.id.version = 1; if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0) { fatal("ioctl UI_SET_EVBIT EV_KEY"); } for (int i = 0; i < 256; i++) { if (ioctl(fd, UI_SET_KEYBIT, i) < 0) { fatal("ioctl UI_SET_KEYBIT"); } } if (write(fd, &uinput_device, sizeof(uinput_device)) < 0) { fatal("failed to write uinput device"); } if (ioctl(fd, UI_DEV_CREATE) < 0) { fatal("failed to create uinput device"); } } void clear_event(struct input_event *event) { memset(event, 0, sizeof(event)); gettimeofday(&(event->time), NULL); } void write_state(int fd) { struct input_event event; bool dirty = false; for (int i = 0; i < 24; i++) { unsigned short code = mapping[i]; if (code == 0) continue; bool pressed = ((state >> i) & 1) && !((old_state >> i) & 1); bool released = ((old_state >> i) & 1) && !((state >> i) & 1); if (!pressed && !released) continue; clear_event(&event); event.type = EV_KEY; event.code = code; event.value = pressed ? 1 : 0; if (write(fd, &event, sizeof(event)) < 0) { fatal("failed to write uinput press/release event"); } dirty = true; } if (dirty) { clear_event(&event); event.type = EV_SYN; event.code = SYN_REPORT; if (write(fd, &event, sizeof(event)) < 0) { fatal("failed to write uinput syn event"); } } } void load_mapping() { const char *keys[24] = { "G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "G9", "G10", "G11", "G12", "G13", "G14", "G15", "G16", "G17", "G18", "BRIGHTNESS", "SUPER_LOCK", "MR", "M1", "M2", "M3" }; char path[256] = "/etc/k95aux/mapping"; if (geteuid() != 0) { const char* home = getenv("HOME"); if (home == NULL) { printf("$HOME not set, using default mapping"); return; }; snprintf(path, sizeof(path), "%s/%s", home, ".config/k95aux/mapping"); } FILE *f = fopen(path, "r"); if (f == NULL) { printf("mapping file does not exist, using default mapping"); return; }; char line[256]; while (fgets(line, sizeof(line), f)) { char key[256]; unsigned short code; if (sscanf(line, "%s %hu", key, &code) < 2) { continue; } for (int i = 0; i < sizeof(keys); i++) { if (strcmp(key, keys[i]) == 0) { mapping[i] = code; break; } } // invalid key names just ignored } fclose(f); } void find_hidraw(char buf[]) { DIR *d = opendir("/sys/class/hidraw"); if (d == NULL) { fatal("failed to iterate /sys/class/hidraw") } struct dirent *entry; char path[256]; char device[256]; while ((entry = readdir(d)) != NULL) { if (entry->d_type != DT_LNK) continue; snprintf(path, sizeof(path), "/sys/class/hidraw/%s/device", entry->d_name); if (readlink(path, device, sizeof(device)) < 0) { fatal("failed to read /sys/class/hidraw/hidrawX/device") } device[23] = 0; if (strcmp(&device[14], HID_DEVICE_ID) == 0) { snprintf(buf, sizeof(path), "/dev/%s", entry->d_name); break; } } closedir(d); } int main(int argc, char **argv) { char buf[256]; find_hidraw(buf); char *device = buf; if (argc > 1) { device = argv[1]; } load_mapping(); printf("opening %s\n", device); int hid_fd = open(device, O_RDONLY); if (hid_fd < 0) { fatal("failed to open hid device"); } printf("opening /dev/uinput\n"); int uinput_fd = open("/dev/uinput", O_WRONLY | O_NDELAY); if (uinput_fd < 0) { fatal("failed to open uinput device"); } create_uinput_device(uinput_fd); while (1) { read_report(hid_fd); if (state & KEY_MR) locked = !locked; if (!locked) write_state(uinput_fd); } close(hid_fd); }