diff --git a/src/device_controller/keyboard/linux/keyboard_capturer.cpp b/src/device_controller/keyboard/linux/keyboard_capturer.cpp index 0a211a4..2d84ce3 100644 --- a/src/device_controller/keyboard/linux/keyboard_capturer.cpp +++ b/src/device_controller/keyboard/linux/keyboard_capturer.cpp @@ -1,5 +1,8 @@ #include "keyboard_capturer.h" +#include +#include + #include "keyboard_converter.h" #include "rd_log.h" @@ -10,7 +13,7 @@ static void* g_user_ptr = nullptr; static int KeyboardEventHandler(Display* display, XEvent* event) { if (event->xkey.type == KeyPress || event->xkey.type == KeyRelease) { - KeySym keySym = XKeycodeToKeysym(display, event->xkey.keycode, 0); + KeySym keySym = XLookupKeysym(&event->xkey, 0); int key_code = XKeysymToKeycode(display, keySym); bool is_key_down = (event->xkey.type == KeyPress); @@ -21,7 +24,9 @@ static int KeyboardEventHandler(Display* display, XEvent* event) { return 0; } -KeyboardCapturer::KeyboardCapturer() : display_(nullptr), running_(true) { +KeyboardCapturer::KeyboardCapturer() + : display_(nullptr), root_(0), running_(false) { + XInitThreads(); display_ = XOpenDisplay(nullptr); if (!display_) { LOG_ERROR("Failed to open X display."); @@ -29,35 +34,87 @@ KeyboardCapturer::KeyboardCapturer() : display_(nullptr), running_(true) { } KeyboardCapturer::~KeyboardCapturer() { + Unhook(); + if (display_) { XCloseDisplay(display_); + display_ = nullptr; } } int KeyboardCapturer::Hook(OnKeyAction on_key_action, void* user_ptr) { + if (!display_) { + LOG_ERROR("Display not initialized."); + return -1; + } + g_on_key_action = on_key_action; g_user_ptr = user_ptr; - XSelectInput(display_, DefaultRootWindow(display_), - KeyPressMask | KeyReleaseMask); - - while (running_) { - XEvent event; - XNextEvent(display_, &event); - KeyboardEventHandler(display_, &event); + if (running_) { + return 0; } + root_ = DefaultRootWindow(display_); + XSelectInput(display_, root_, KeyPressMask | KeyReleaseMask); + XFlush(display_); + + running_ = true; + const int x11_fd = ConnectionNumber(display_); + event_thread_ = std::thread([this, x11_fd]() { + while (running_) { + while (running_ && XPending(display_) > 0) { + XEvent event; + XNextEvent(display_, &event); + KeyboardEventHandler(display_, &event); + } + + if (!running_) { + break; + } + + struct pollfd pfd = {x11_fd, POLLIN, 0}; + int poll_ret = poll(&pfd, 1, 50); + if (poll_ret < 0) { + if (errno == EINTR) { + continue; + } + LOG_ERROR("poll for X11 events failed."); + running_ = false; + break; + } + + if (poll_ret == 0) { + continue; + } + + if ((pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) { + LOG_ERROR("poll got invalid X11 event fd state."); + running_ = false; + break; + } + + if ((pfd.revents & POLLIN) == 0) { + continue; + } + } + }); + return 0; } int KeyboardCapturer::Unhook() { + running_ = false; + + if (event_thread_.joinable()) { + event_thread_.join(); + } + g_on_key_action = nullptr; g_user_ptr = nullptr; - running_ = false; - - if (display_) { - XSelectInput(display_, DefaultRootWindow(display_), 0); + if (display_ && root_ != 0) { + XSelectInput(display_, root_, 0); XFlush(display_); } diff --git a/src/device_controller/keyboard/linux/keyboard_capturer.h b/src/device_controller/keyboard/linux/keyboard_capturer.h index 222dc47..0d61b50 100644 --- a/src/device_controller/keyboard/linux/keyboard_capturer.h +++ b/src/device_controller/keyboard/linux/keyboard_capturer.h @@ -11,6 +11,9 @@ #include #include +#include +#include + #include "device_controller.h" namespace crossdesk { @@ -28,7 +31,8 @@ class KeyboardCapturer : public DeviceController { private: Display* display_; Window root_; - bool running_; + std::atomic running_; + std::thread event_thread_; }; } // namespace crossdesk #endif \ No newline at end of file