// gKeyMapper daemon - virtual keyboard for replaying "key" mappings. #include "uinput_keyboard.h" #include #include #include #include #include #include #include #include #include "keys.h" namespace gkeymapper::daemon { namespace { // Key code for a single modifier bit (the "left" variants are enough). unsigned short modifier_code(unsigned bit) { switch (bit) { case gkeymapper::kModCtrl: return KEY_LEFTCTRL; case gkeymapper::kModAlt: return KEY_LEFTALT; case gkeymapper::kModShift: return KEY_LEFTSHIFT; case gkeymapper::kModSuper: return KEY_LEFTMETA; case gkeymapper::kModAltGr: return KEY_RIGHTALT; default: return 0; } } } // namespace UinputKeyboard::~UinputKeyboard() { close(); } bool UinputKeyboard::open() { if (fd_ >= 0) { return true; } fd_ = ::open("/dev/uinput", O_WRONLY | O_NONBLOCK); if (fd_ < 0) { std::fprintf(stderr, "gkeymapper-daemon: cannot open /dev/uinput (permissions?)\n"); fd_ = -1; return false; } ioctl(fd_, UI_SET_EVBIT, EV_KEY); ioctl(fd_, UI_SET_EVBIT, EV_SYN); for (int code = 0; code <= KEY_MAX; ++code) { ioctl(fd_, UI_SET_KEYBIT, code); } uinput_user_dev device{}; std::snprintf(device.name, UINPUT_MAX_NAME_SIZE, "gKeyMapper virtual keyboard"); device.id.bustype = BUS_USB; device.id.vendor = 0x046d; device.id.product = 0xc32b; device.id.version = 1; if (::write(fd_, &device, sizeof(device)) < 0) { std::fprintf(stderr, "gkeymapper-daemon: uinput setup failed\n"); close(); return false; } if (ioctl(fd_, UI_DEV_CREATE) < 0) { std::fprintf(stderr, "gkeymapper-daemon: UI_DEV_CREATE failed\n"); close(); return false; } return true; } void UinputKeyboard::close() { if (fd_ >= 0) { ioctl(fd_, UI_DEV_DESTROY); ::close(fd_); fd_ = -1; } } void UinputKeyboard::emit(unsigned short type, unsigned short code, int value) { input_event event{}; event.type = type; event.code = code; event.value = value; // A short write is not recoverable here; drop the event. ssize_t written = ::write(fd_, &event, sizeof(event)); (void)written; } void UinputKeyboard::sync() { emit(EV_SYN, SYN_REPORT, 0); } void UinputKeyboard::key_event(unsigned short code, bool down) { emit(EV_KEY, code, down ? 1 : 0); } void UinputKeyboard::play(const std::vector& steps) { if (fd_ < 0) { return; } for (const gkeymapper::ShortcutStep& step : steps) { std::vector modifiers; for (unsigned bit : {gkeymapper::kModCtrl, gkeymapper::kModAlt, gkeymapper::kModShift, gkeymapper::kModSuper, gkeymapper::kModAltGr}) { if ((step.modifiers & bit) != 0) { unsigned short code = modifier_code(bit); if (code != 0) { modifiers.push_back(code); } } } for (unsigned short code : modifiers) { key_event(code, true); } if (step.linux_key >= 0) { key_event(static_cast(step.linux_key), true); } sync(); usleep(2000); if (step.linux_key >= 0) { key_event(static_cast(step.linux_key), false); } for (auto it = modifiers.rbegin(); it != modifiers.rend(); ++it) { key_event(*it, false); } sync(); usleep(2000); } } } // namespace gkeymapper::daemon