mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-03-25 01:27:25 +08:00
180 lines
4.1 KiB
C++
180 lines
4.1 KiB
C++
#include "keyboard_capturer.h"
|
|
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
|
|
#include "keyboard_converter.h"
|
|
#include "platform.h"
|
|
#include "rd_log.h"
|
|
|
|
namespace crossdesk {
|
|
|
|
static OnKeyAction g_on_key_action = nullptr;
|
|
static void* g_user_ptr = nullptr;
|
|
|
|
static KeySym NormalizeKeySym(KeySym key_sym) {
|
|
if (key_sym >= XK_a && key_sym <= XK_z) {
|
|
return key_sym - XK_a + XK_A;
|
|
}
|
|
return key_sym;
|
|
}
|
|
|
|
static int KeyboardEventHandler(Display* display, XEvent* event) {
|
|
(void)display;
|
|
if (event->xkey.type == KeyPress || event->xkey.type == KeyRelease) {
|
|
KeySym key_sym = NormalizeKeySym(XLookupKeysym(&event->xkey, 0));
|
|
auto key_it = x11KeySymToVkCode.find(static_cast<int>(key_sym));
|
|
if (key_it == x11KeySymToVkCode.end()) {
|
|
key_sym = NormalizeKeySym(XLookupKeysym(&event->xkey, 1));
|
|
key_it = x11KeySymToVkCode.find(static_cast<int>(key_sym));
|
|
}
|
|
|
|
if (key_it == x11KeySymToVkCode.end()) {
|
|
return 0;
|
|
}
|
|
|
|
int key_code = key_it->second;
|
|
bool is_key_down = (event->xkey.type == KeyPress);
|
|
|
|
if (g_on_key_action) {
|
|
g_on_key_action(key_code, is_key_down, g_user_ptr);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
KeyboardCapturer::KeyboardCapturer()
|
|
: display_(nullptr),
|
|
root_(0),
|
|
running_(false),
|
|
use_wayland_portal_(false),
|
|
wayland_init_attempted_(false),
|
|
dbus_connection_(nullptr) {
|
|
XInitThreads();
|
|
display_ = XOpenDisplay(nullptr);
|
|
if (!display_) {
|
|
LOG_ERROR("Failed to open X display.");
|
|
}
|
|
}
|
|
|
|
KeyboardCapturer::~KeyboardCapturer() {
|
|
Unhook();
|
|
CleanupWaylandPortal();
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
if (display_ && root_ != 0) {
|
|
XSelectInput(display_, root_, 0);
|
|
XFlush(display_);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down) {
|
|
if (IsWaylandSession()) {
|
|
if (!use_wayland_portal_ && !wayland_init_attempted_) {
|
|
wayland_init_attempted_ = true;
|
|
if (InitWaylandPortal()) {
|
|
use_wayland_portal_ = true;
|
|
LOG_INFO("Keyboard controller initialized with Wayland portal backend");
|
|
} else {
|
|
LOG_WARN("Wayland keyboard control init failed, falling back to X11/XTest backend");
|
|
}
|
|
}
|
|
|
|
if (use_wayland_portal_) {
|
|
return SendWaylandKeyboardCommand(key_code, is_down);
|
|
}
|
|
}
|
|
|
|
if (!display_) {
|
|
LOG_ERROR("Display not initialized.");
|
|
return -1;
|
|
}
|
|
|
|
if (vkCodeToX11KeySym.find(key_code) != vkCodeToX11KeySym.end()) {
|
|
int x11_key_code = vkCodeToX11KeySym[key_code];
|
|
KeyCode keycode = XKeysymToKeycode(display_, x11_key_code);
|
|
XTestFakeKeyEvent(display_, keycode, is_down, CurrentTime);
|
|
XFlush(display_);
|
|
}
|
|
return 0;
|
|
}
|
|
} // namespace crossdesk
|