Reputation: 41
The following code attempts to remap the home key to 'b' by creating a uinput device from a real event device, intercepting the real device's events and sending them to uinput with the remapping.
The problem is that if a key is held down when the grab occurs that key behaves as if it's stuck. If the program is started on the command line, this usually results in the enter key getting stuck when the program is started. There is a sleep to avoid this. However, I can go to another window and then hold down a key during the sleep so that that key ends up getting stuck.
I believe the issue is that the key up signal gets lost in this process. I've tried checking for down keys with libevdev_get_event_value which correctly shows keys as I'm pressing them but doesn't show the stuck key.
Also, after terminating the program, the stuck key will not echo the first time it is pressed, but will echo afterwards.
How can I prevent the key from sticking and how can I have it work immediately after terminating this program?
#include <csignal>
#include <cstring>
#include <fcntl.h>
#include <functional>
#include <iostream>
#include <libevdev/libevdev-uinput.h>
#include <libevdev/libevdev.h>
#include <unistd.h>
int fd;
struct libevdev *dev;
struct libevdev_uinput *uidev;
void cleanup() {
if (uidev) libevdev_uinput_destroy(uidev);
if (dev) libevdev_free(dev);
if (fd) close(fd);
}
void sigint_handler(int sig) {
cleanup();
std::cout << "Interrupted. Cleanup done." << std::endl;
exit(0);
}
void flush_key_events(struct libevdev* dev) {
struct input_event ev;
int err;
do {
err = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
} while (err != -EAGAIN);
}
bool maybe_error(std::function<bool()> predicate, const std::string &msg, int err) {
if (err == 0) return false;
std::cerr << msg << std::endl;
return true;
}
void maybe_error_exit(std::function<bool()> predicate, const std::string &msg, int err) {
if (maybe_error(predicate, msg, err)) {
cleanup();
exit(1);
}
}
int main(int argc, char **argv)
{
signal(SIGTERM, sigint_handler);
signal(SIGINT, sigint_handler);
fd = open("/dev/input/event4", O_RDONLY);
maybe_error_exit([&](){ return fd < 0; }, "Error opening device", errno);
int err = libevdev_new_from_fd(fd, &dev);
maybe_error_exit([&](){ return err != 0; }, "Error initializing libevdev", err);
err = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev);
maybe_error_exit([&](){ return err != 0; }, "Error creating uinput device", err);
sleep(3);
std::cerr << "Grabbing device" << std::endl;
err = libevdev_grab(dev, LIBEVDEV_GRAB);
maybe_error_exit([&](){ return err != 0; }, "Error grabbing device", err);
struct input_event ev;
while (true) {
err = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
if (err == LIBEVDEV_READ_STATUS_SUCCESS) {
if (ev.type == EV_KEY && ev.code == KEY_HOME) {
ev.code = KEY_B;
}
libevdev_uinput_write_event(uidev, ev.type, ev.code, ev.value);
} else if (err == LIBEVDEV_READ_STATUS_SYNC) {
flush_key_events(dev);
} else if (err == -EAGAIN) {
continue;
} else {
std::cerr << "Error reading event" << std::endl;
break;
}
}
cleanup();
}
Upvotes: 1
Views: 214