mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-03-26 02:40:22 +08:00
[fix] move keyboard capturer to a background thread and use poll-based X11 event handling to avoid main-thread blocking
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
#include "keyboard_capturer.h"
|
#include "keyboard_capturer.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
#include "keyboard_converter.h"
|
#include "keyboard_converter.h"
|
||||||
#include "rd_log.h"
|
#include "rd_log.h"
|
||||||
|
|
||||||
@@ -10,7 +13,7 @@ static void* g_user_ptr = nullptr;
|
|||||||
|
|
||||||
static int KeyboardEventHandler(Display* display, XEvent* event) {
|
static int KeyboardEventHandler(Display* display, XEvent* event) {
|
||||||
if (event->xkey.type == KeyPress || event->xkey.type == KeyRelease) {
|
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);
|
int key_code = XKeysymToKeycode(display, keySym);
|
||||||
bool is_key_down = (event->xkey.type == KeyPress);
|
bool is_key_down = (event->xkey.type == KeyPress);
|
||||||
|
|
||||||
@@ -21,7 +24,9 @@ static int KeyboardEventHandler(Display* display, XEvent* event) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardCapturer::KeyboardCapturer() : display_(nullptr), running_(true) {
|
KeyboardCapturer::KeyboardCapturer()
|
||||||
|
: display_(nullptr), root_(0), running_(false) {
|
||||||
|
XInitThreads();
|
||||||
display_ = XOpenDisplay(nullptr);
|
display_ = XOpenDisplay(nullptr);
|
||||||
if (!display_) {
|
if (!display_) {
|
||||||
LOG_ERROR("Failed to open X display.");
|
LOG_ERROR("Failed to open X display.");
|
||||||
@@ -29,35 +34,87 @@ KeyboardCapturer::KeyboardCapturer() : display_(nullptr), running_(true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KeyboardCapturer::~KeyboardCapturer() {
|
KeyboardCapturer::~KeyboardCapturer() {
|
||||||
|
Unhook();
|
||||||
|
|
||||||
if (display_) {
|
if (display_) {
|
||||||
XCloseDisplay(display_);
|
XCloseDisplay(display_);
|
||||||
|
display_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeyboardCapturer::Hook(OnKeyAction on_key_action, void* user_ptr) {
|
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_on_key_action = on_key_action;
|
||||||
g_user_ptr = user_ptr;
|
g_user_ptr = user_ptr;
|
||||||
|
|
||||||
XSelectInput(display_, DefaultRootWindow(display_),
|
if (running_) {
|
||||||
KeyPressMask | KeyReleaseMask);
|
return 0;
|
||||||
|
|
||||||
while (running_) {
|
|
||||||
XEvent event;
|
|
||||||
XNextEvent(display_, &event);
|
|
||||||
KeyboardEventHandler(display_, &event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeyboardCapturer::Unhook() {
|
int KeyboardCapturer::Unhook() {
|
||||||
|
running_ = false;
|
||||||
|
|
||||||
|
if (event_thread_.joinable()) {
|
||||||
|
event_thread_.join();
|
||||||
|
}
|
||||||
|
|
||||||
g_on_key_action = nullptr;
|
g_on_key_action = nullptr;
|
||||||
g_user_ptr = nullptr;
|
g_user_ptr = nullptr;
|
||||||
|
|
||||||
running_ = false;
|
if (display_ && root_ != 0) {
|
||||||
|
XSelectInput(display_, root_, 0);
|
||||||
if (display_) {
|
|
||||||
XSelectInput(display_, DefaultRootWindow(display_), 0);
|
|
||||||
XFlush(display_);
|
XFlush(display_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
#include <X11/extensions/XTest.h>
|
#include <X11/extensions/XTest.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "device_controller.h"
|
#include "device_controller.h"
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
@@ -28,7 +31,8 @@ class KeyboardCapturer : public DeviceController {
|
|||||||
private:
|
private:
|
||||||
Display* display_;
|
Display* display_;
|
||||||
Window root_;
|
Window root_;
|
||||||
bool running_;
|
std::atomic<bool> running_;
|
||||||
|
std::thread event_thread_;
|
||||||
};
|
};
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user