玉兔远程控制 0.1.0-bate5
载入中...
搜索中...
未找到
KeyInjector.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include "KeyInjector.h"
4#include <QCoreApplication>
5#include <QDebug>
6#include <QProcessEnvironment>
7
8#ifdef Q_OS_UNIX
9// X11
10#include <X11/Xlib.h>
11#include <X11/keysym.h>
12#include <X11/extensions/XTest.h>
13#endif
14
15#ifdef __linux__
16#include <fcntl.h>
17#include <unistd.h>
18#include <linux/uinput.h>
19#include <sys/ioctl.h>
20#include <string.h>
21#include <sys/time.h>
22#endif
23
24KeyInjector::KeyInjector(QObject *parent)
25 : QObject(parent)
26{
27}
28
29KeyInjector::~KeyInjector()
30{
31 cleanup();
32}
33
34bool KeyInjector::init()
35{
36 // 先检测 DISPLAY / WAYLAND 环境,优先 X11/XTest(无 root),否则尝试 uinput
37 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
38 bool hasDisplay = env.contains("DISPLAY");
39 bool hasWayland = env.contains("WAYLAND_DISPLAY");
40
41#ifdef Q_OS_UNIX
42 if (hasDisplay) {
43 if (initX11()) {
44 useX11 = true;
45 useUinput = false;
46 qDebug() << "KeyInjector: using X11/XTest backend";
47 return true;
48 } else {
49 qDebug() << "KeyInjector: X11 init failed";
50 }
51 }
52#endif
53
54#ifdef __linux__
55 // 尝试 uinput(需要权限)
56 if (initUInput()) {
57 useUinput = true;
58 useX11 = false;
59 qDebug() << "KeyInjector: using uinput backend";
60 return true;
61 } else {
62 qDebug() << "KeyInjector: uinput init failed or no permission";
63 }
64#endif
65
66 qWarning() << "KeyInjector: no available backend for injecting keys";
67 return false;
68}
69
70bool KeyInjector::initX11()
71{
72#ifdef Q_OS_UNIX
73 // 使用 XOpenDisplay 以减少对 QtX11Extras 的依赖
74 const char *displayName = getenv("DISPLAY");
75 Display *dpy = nullptr;
76 if (displayName)
77 dpy = XOpenDisplay(displayName);
78 else
79 dpy = XOpenDisplay(nullptr);
80
81 if (!dpy) {
82 qDebug() << "KeyInjector: XOpenDisplay failed";
83 return false;
84 }
85
86 // 测试 XTest 扩展是否可用
87 int event_base, error_base;
88 if (!XTestQueryExtension(dpy, &event_base, &error_base, &event_base, &error_base)) {
89 XCloseDisplay(dpy);
90 qDebug() << "KeyInjector: XTest extension not available";
91 return false;
92 }
93
94 x11Display = dpy;
95 return true;
96#else
97 Q_UNUSED(x11Display);
98 return false;
99#endif
100}
101
102void KeyInjector::cleanupX11()
103{
104#ifdef Q_OS_UNIX
105 if (x11Display) {
106 XCloseDisplay(static_cast<Display*>(x11Display));
107 x11Display = nullptr;
108 }
109#endif
110}
111
112bool KeyInjector::initUInput()
113{
114#ifdef __linux__
115 const char *device = "/dev/uinput";
116 int fd = open(device, O_WRONLY | O_NONBLOCK);
117 if (fd < 0) {
118 qDebug() << "KeyInjector: open /dev/uinput failed, maybe permission denied";
119 return false;
120 }
121
122 // 申明事件类型和按键
123 ioctl(fd, UI_SET_EVBIT, EV_KEY);
124
125 // 注册常用按键(Super / letters / digits / modifiers),可按需扩展
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);
134
135 // 注册字母与数字的一小部分(a-z, 0-9)
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);
139
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));
147
148 if (ioctl(fd, UI_DEV_CREATE) < 0) {
149 close(fd);
150 qDebug() << "KeyInjector: UI_DEV_CREATE failed";
151 return false;
152 }
153
154 // 保存 fd
155 uinputFd = fd;
156 // 小延时等待设备创建
157 sleep(1);
158 return true;
159#else
160 Q_UNUSED(uinputFd);
161 return false;
162#endif
163}
164
165void KeyInjector::cleanupUInput()
166{
167#ifdef __linux__
168 if (uinputFd >= 0) {
169 ioctl(uinputFd, UI_DEV_DESTROY);
170 close(uinputFd);
171 uinputFd = -1;
172 }
173#endif
174}
175
176void KeyInjector::cleanup()
177{
178 if (useX11) cleanupX11();
179 if (useUinput) cleanupUInput();
180 useX11 = false;
181 useUinput = false;
182}
183
184bool KeyInjector::sendKey(Qt::Key key)
185{
186 return sendKeyCombo({}, key);
187}
188
189bool KeyInjector::sendKeyCombo(const QList<Qt::Key> &modifiers, Qt::Key key)
190{
191 bool ok;
192 // 按下修饰键
193 for (Qt::Key mod : modifiers) {
194 if (useX11) {
195 sendKeyX11(mod, true);
196 } else if (useUinput) {
197 int lk = qtKeyToLinuxKey(mod, &ok);
198 if (ok) sendKeyUInput(lk, 1);
199 }
200 }
201
202 // 按下主键
203 if (useX11) {
204 sendKeyX11(key, true);
205 // 释放主键
206 sendKeyX11(key, false);
207 } else if (useUinput) {
208 int lk = qtKeyToLinuxKey(key, &ok);
209 if (ok) {
210 sendKeyUInput(lk, 1);
211 sendKeyUInput(lk, 0);
212 } else {
213 qDebug() << "KeyInjector: unknown linux key for key" << key;
214 }
215 }
216
217 // 释放修饰键(倒序以模拟真实顺序)
218 for (int i = modifiers.size()-1; i >= 0; --i) {
219 Qt::Key mod = modifiers.at(i);
220 if (useX11) {
221 sendKeyX11(mod, false);
222 } else if (useUinput) {
223 int lk = qtKeyToLinuxKey(mod, &ok);
224 if (ok) sendKeyUInput(lk, 0);
225 }
226 }
227
228 return true;
229}
230
231bool KeyInjector::sendSuper()
232{
233 return sendKey(Qt::Key_Super_L);
234}
235
236bool KeyInjector::sendKeyX11(Qt::Key key, bool press)
237{
238#ifdef Q_OS_UNIX
239 if (!x11Display) return false;
240 Display *dpy = static_cast<Display*>(x11Display);
241
242 bool ok;
243 int ks = qtKeyToKeySym(key, &ok);
244 if (!ok) return false;
245
246 KeyCode kc = XKeysymToKeycode(dpy, (KeySym)ks);
247 if (kc == 0) {
248 qDebug() << "KeyInjector: XKeysymToKeycode failed for keysym" << ks;
249 return false;
250 }
251
252 XTestFakeKeyEvent(dpy, kc, press ? True : False, CurrentTime);
253 XFlush(dpy);
254 return true;
255#else
256 Q_UNUSED(key);
257 Q_UNUSED(press);
258 return false;
259#endif
260}
261
262bool KeyInjector::sendKeyUInput(int linux_keycode, int value)
263{
264#ifdef __linux__
265 if (uinputFd < 0) return false;
266 struct input_event ev;
267 memset(&ev, 0, sizeof(ev));
268 gettimeofday(&ev.time, NULL);
269 ev.type = EV_KEY;
270 ev.code = linux_keycode;
271 ev.value = value; // 1 down, 0 up
272 if (write(uinputFd, &ev, sizeof(ev)) < 0) {
273 qDebug() << "KeyInjector: write key event failed";
274 return false;
275 }
276 // sync
277 memset(&ev, 0, sizeof(ev));
278 gettimeofday(&ev.time, NULL);
279 ev.type = EV_SYN;
280 ev.code = SYN_REPORT;
281 ev.value = 0;
282 write(uinputFd, &ev, sizeof(ev));
283 return true;
284#else
285 Q_UNUSED(linux_keycode);
286 Q_UNUSED(value);
287 return false;
288#endif
289}
290
291// 简单映射函数,覆盖常见键(可扩展)
292// 返回 X11 keysym(例如 XK_Super_L)或 0
293int KeyInjector::qtKeyToKeySym(Qt::Key key, bool *ok)
294{
295 if (ok) *ok = false;
296 switch (key) {
297 case Qt::Key_Super_L:
298 if (ok) *ok = true;
299 return XK_Super_L;
300 case Qt::Key_Super_R:
301 if (ok) *ok = true;
302 return XK_Super_R;
303 case Qt::Key_Shift:
304 //case Qt::Key_ShiftL:
305 if (ok) *ok = true;
306 return XK_Shift_L;
307 case Qt::Key_Control:
308 //case Qt::Key_ControlL:
309 if (ok) *ok = true;
310 return XK_Control_L;
311 case Qt::Key_Alt:
312 //case Qt::Key_AltL:
313 if (ok) *ok = true;
314 return XK_Alt_L;
315 default:
316 // 字母
317 if (key >= Qt::Key_A && key <= Qt::Key_Z) {
318 if (ok) *ok = true;
319 QChar ch = QChar('a' + (key - Qt::Key_A));
320 return XStringToKeysym(QString(ch).toLatin1().constData());
321 }
322 // 数字
323 if (key >= Qt::Key_0 && key <= Qt::Key_9) {
324 if (ok) *ok = true;
325 QChar ch = QChar('0' + (key - Qt::Key_0));
326 return XStringToKeysym(QString(ch).toLatin1().constData());
327 }
328 }
329 return 0;
330}
331
332// 返回 Linux input keycode (KEY_*),0 表示失败
333int KeyInjector::qtKeyToLinuxKey(Qt::Key key, bool *ok)
334{
335 if (ok) *ok = false;
336 switch (key) {
337 case Qt::Key_Super_L:
338 if (ok) *ok = true;
339 return KEY_LEFTMETA;
340 case Qt::Key_Super_R:
341 if (ok) *ok = true;
342 return KEY_RIGHTMETA;
343 case Qt::Key_Shift:
344 //case Qt::Key_ShiftL:
345 if (ok) *ok = true;
346 return KEY_LEFTSHIFT;
347 case Qt::Key_Control:
348 //case Qt::Key_ControlL:
349 if (ok) *ok = true;
350 return KEY_LEFTCTRL;
351 case Qt::Key_Alt:
352 //case Qt::Key_AltL:
353 if (ok) *ok = true;
354 return KEY_LEFTALT;
355 default:
356 if (key >= Qt::Key_A && key <= Qt::Key_Z) {
357 if (ok) *ok = true;
358 return KEY_A + (key - Qt::Key_A);
359 }
360 if (key >= Qt::Key_0 && key <= Qt::Key_9) {
361 if (ok) *ok = true;
362 int offset = (key == Qt::Key_0) ? 9 : (key - Qt::Key_1);
363 // 按 linux headers, KEY_1 .. KEY_9, KEY_0
364 if (key == Qt::Key_0) return KEY_0;
365 return KEY_1 + (key - Qt::Key_1);
366 }
367 }
368 return 0;
369}