3#include "KeyInjector.h"
4#include <QCoreApplication>
6#include <QProcessEnvironment>
11#include <X11/keysym.h>
12#include <X11/extensions/XTest.h>
18#include <linux/uinput.h>
24KeyInjector::KeyInjector(QObject *parent)
29KeyInjector::~KeyInjector()
34bool KeyInjector::init()
37 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
38 bool hasDisplay = env.contains(
"DISPLAY");
39 bool hasWayland = env.contains(
"WAYLAND_DISPLAY");
46 qDebug() <<
"KeyInjector: using X11/XTest backend";
49 qDebug() <<
"KeyInjector: X11 init failed";
59 qDebug() <<
"KeyInjector: using uinput backend";
62 qDebug() <<
"KeyInjector: uinput init failed or no permission";
66 qWarning() <<
"KeyInjector: no available backend for injecting keys";
70bool KeyInjector::initX11()
74 const char *displayName = getenv(
"DISPLAY");
75 Display *dpy =
nullptr;
77 dpy = XOpenDisplay(displayName);
79 dpy = XOpenDisplay(
nullptr);
82 qDebug() <<
"KeyInjector: XOpenDisplay failed";
87 int event_base, error_base;
88 if (!XTestQueryExtension(dpy, &event_base, &error_base, &event_base, &error_base)) {
90 qDebug() <<
"KeyInjector: XTest extension not available";
102void KeyInjector::cleanupX11()
106 XCloseDisplay(
static_cast<Display*
>(x11Display));
107 x11Display =
nullptr;
112bool KeyInjector::initUInput()
115 const char *device =
"/dev/uinput";
116 int fd = open(device, O_WRONLY | O_NONBLOCK);
118 qDebug() <<
"KeyInjector: open /dev/uinput failed, maybe permission denied";
123 ioctl(fd, UI_SET_EVBIT, EV_KEY);
126 ioctl(fd, UI_SET_KEYBIT, KEY_LEFTMETA);
127 ioctl(fd, UI_SET_KEYBIT, KEY_RIGHTMETA);
128 ioctl(fd, UI_SET_KEYBIT, KEY_LEFTSHIFT);
129 ioctl(fd, UI_SET_KEYBIT, KEY_RIGHTSHIFT);
130 ioctl(fd, UI_SET_KEYBIT, KEY_LEFTCTRL);
131 ioctl(fd, UI_SET_KEYBIT, KEY_RIGHTCTRL);
132 ioctl(fd, UI_SET_KEYBIT, KEY_LEFTALT);
133 ioctl(fd, UI_SET_KEYBIT, KEY_RIGHTALT);
136 for (
int c = KEY_A; c <= KEY_Z; ++c) ioctl(fd, UI_SET_KEYBIT, c);
137 for (
int c = KEY_1; c <= KEY_9; ++c) ioctl(fd, UI_SET_KEYBIT, c);
138 ioctl(fd, UI_SET_KEYBIT, KEY_0);
140 struct uinput_user_dev uidev;
141 memset(&uidev, 0,
sizeof(uidev));
142 snprintf(uidev.name,
sizeof(uidev.name),
"rabbitremote-qt-uinput");
143 uidev.id.bustype = BUS_USB;
144 uidev.id.vendor = 0x1234;
145 uidev.id.product = 0x5678;
146 write(fd, &uidev,
sizeof(uidev));
148 if (ioctl(fd, UI_DEV_CREATE) < 0) {
150 qDebug() <<
"KeyInjector: UI_DEV_CREATE failed";
165void KeyInjector::cleanupUInput()
169 ioctl(uinputFd, UI_DEV_DESTROY);
176void KeyInjector::cleanup()
178 if (useX11) cleanupX11();
179 if (useUinput) cleanupUInput();
184bool KeyInjector::sendKey(Qt::Key key)
186 return sendKeyCombo({}, key);
189bool KeyInjector::sendKeyCombo(
const QList<Qt::Key> &modifiers, Qt::Key key)
193 for (Qt::Key mod : modifiers) {
195 sendKeyX11(mod,
true);
196 }
else if (useUinput) {
197 int lk = qtKeyToLinuxKey(mod, &ok);
198 if (ok) sendKeyUInput(lk, 1);
204 sendKeyX11(key,
true);
206 sendKeyX11(key,
false);
207 }
else if (useUinput) {
208 int lk = qtKeyToLinuxKey(key, &ok);
210 sendKeyUInput(lk, 1);
211 sendKeyUInput(lk, 0);
213 qDebug() <<
"KeyInjector: unknown linux key for key" << key;
218 for (
int i = modifiers.size()-1; i >= 0; --i) {
219 Qt::Key mod = modifiers.at(i);
221 sendKeyX11(mod,
false);
222 }
else if (useUinput) {
223 int lk = qtKeyToLinuxKey(mod, &ok);
224 if (ok) sendKeyUInput(lk, 0);
231bool KeyInjector::sendSuper()
233 return sendKey(Qt::Key_Super_L);
236bool KeyInjector::sendKeyX11(Qt::Key key,
bool press)
239 if (!x11Display)
return false;
240 Display *dpy =
static_cast<Display*
>(x11Display);
243 int ks = qtKeyToKeySym(key, &ok);
244 if (!ok)
return false;
246 KeyCode kc = XKeysymToKeycode(dpy, (KeySym)ks);
248 qDebug() <<
"KeyInjector: XKeysymToKeycode failed for keysym" << ks;
252 XTestFakeKeyEvent(dpy, kc, press ? True : False, CurrentTime);
262bool KeyInjector::sendKeyUInput(
int linux_keycode,
int value)
265 if (uinputFd < 0)
return false;
266 struct input_event ev;
267 memset(&ev, 0,
sizeof(ev));
268 gettimeofday(&ev.time, NULL);
270 ev.code = linux_keycode;
272 if (write(uinputFd, &ev,
sizeof(ev)) < 0) {
273 qDebug() <<
"KeyInjector: write key event failed";
277 memset(&ev, 0,
sizeof(ev));
278 gettimeofday(&ev.time, NULL);
280 ev.code = SYN_REPORT;
282 write(uinputFd, &ev,
sizeof(ev));
285 Q_UNUSED(linux_keycode);
293int KeyInjector::qtKeyToKeySym(Qt::Key key,
bool *ok)
297 case Qt::Key_Super_L:
300 case Qt::Key_Super_R:
307 case Qt::Key_Control:
317 if (key >= Qt::Key_A && key <= Qt::Key_Z) {
319 QChar ch = QChar(
'a' + (key - Qt::Key_A));
320 return XStringToKeysym(QString(ch).toLatin1().constData());
323 if (key >= Qt::Key_0 && key <= Qt::Key_9) {
325 QChar ch = QChar(
'0' + (key - Qt::Key_0));
326 return XStringToKeysym(QString(ch).toLatin1().constData());
333int KeyInjector::qtKeyToLinuxKey(Qt::Key key,
bool *ok)
337 case Qt::Key_Super_L:
340 case Qt::Key_Super_R:
342 return KEY_RIGHTMETA;
346 return KEY_LEFTSHIFT;
347 case Qt::Key_Control:
356 if (key >= Qt::Key_A && key <= Qt::Key_Z) {
358 return KEY_A + (key - Qt::Key_A);
360 if (key >= Qt::Key_0 && key <= Qt::Key_9) {
362 int offset = (key == Qt::Key_0) ? 9 : (key - Qt::Key_1);
364 if (key == Qt::Key_0)
return KEY_0;
365 return KEY_1 + (key - Qt::Key_1);