Deamon management
This commit is contained in:
140
daemon/uinput_keyboard.cpp
Normal file
140
daemon/uinput_keyboard.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
// gKeyMapper daemon - virtual keyboard for replaying "key" mappings.
|
||||
|
||||
#include "uinput_keyboard.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fcntl.h>
|
||||
#include <initializer_list>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/uinput.h>
|
||||
|
||||
#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<gkeymapper::ShortcutStep>& steps) {
|
||||
if (fd_ < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const gkeymapper::ShortcutStep& step : steps) {
|
||||
std::vector<unsigned short> 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<unsigned short>(step.linux_key), true);
|
||||
}
|
||||
sync();
|
||||
usleep(2000);
|
||||
|
||||
if (step.linux_key >= 0) {
|
||||
key_event(static_cast<unsigned short>(step.linux_key), false);
|
||||
}
|
||||
for (auto it = modifiers.rbegin(); it != modifiers.rend(); ++it) {
|
||||
key_event(*it, false);
|
||||
}
|
||||
sync();
|
||||
usleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gkeymapper::daemon
|
||||
Reference in New Issue
Block a user