我想记录所有传入的按键事件,无论哪个窗口处于焦点状态或指针在哪里。
我编写了一个示例代码,它应该捕获当前焦点窗口的按键事件。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <locale.h>
#include <stdint.h>
#include <stdarg.h>
#include <errno.h>
#include <pthread.h>
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Xfuncs.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
int _invalid_window_handler(Display *dsp, XErrorEvent *err) {
return 0;
}
int main()
{
Display *display = XOpenDisplay(NULL);
int iError;
KeySym k;
int revert_to;
Window window;
XEvent event;
Time time;
XSetErrorHandler(_invalid_window_handler);
XGetInputFocus(display, &window, &revert_to);
XSelectInput(display, window, KeyPressMask | KeyReleaseMask );
iError = XGrabKeyboard(display, window,
KeyPressMask | KeyReleaseMask,
GrabModeAsync,
GrabModeAsync,
CurrentTime);
if (iError != GrabSuccess && iError == AlreadyGrabbed) {
XUngrabPointer(display, CurrentTime);
XFlush(display);
printf("Already Grabbed\n");
} else if (iError == GrabSuccess) {
printf("Grabbed\n");
}
while(1) {
XNextEvent(display,&event);
switch (event.type) {
case KeyPress : printf("Key Pressed\n"); break;
case KeyRelease : printf("Key Released\n"); break;
case EnterNotify : printf("Enter\n"); break;
}
}
XCloseDisplay(display);
return 0;
}
我正在调用 XGrabKeyboard 来捕获键盘,因为创建窗口的应用程序可能已经捕获了键盘事件。使用上面提到的代码,我能够抓住键盘,但无法接收 while 循环内键盘上任何键的 KeyPress 或 KeyRelease 事件。代码中是否缺少任何内容导致我无法接收事件?非常感谢任何帮助。
我的最终目标是捕获屏幕上的按键事件,而不考虑焦点窗口。我已经给出了仅焦点窗口的示例代码,以便代码可读。我会做 XQueryTree 来获取所有 Windows 并应用上面给出的相同逻辑来获得预期结果。
您需要有一个映射窗口才能抓取键盘。这是概念证明:
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <stdio.h>
int main()
{
Display *display;
Window window, rootwindow;
XEvent event;
KeySym escape;
display = XOpenDisplay(NULL);
rootwindow = DefaultRootWindow(display);
window = XCreateWindow(display, rootwindow,
-99, -99, 1, 1, /* x, y, width, height */
0, 0, InputOnly, /* border, depth, class */
CopyFromParent, /* visual */
0, NULL); /* valuemask and attributes */
XSelectInput(display, window, StructureNotifyMask | SubstructureRedirectMask | ResizeRedirectMask | KeyPressMask | KeyReleaseMask);
XLowerWindow(display, window);
XMapWindow(display, window);
do {
XNextEvent(display, &event);
} while (event.type != MapNotify);
XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
XLowerWindow(display, window);
escape = XKeysymToKeycode(display, XK_Escape);
printf("\nPress ESC to exit.\n\n");
fflush(stdout);
while (1) {
XNextEvent(display, &event);
if (event.type == KeyPress) {
printf("KeyPress: keycode %u state %u\n", event.xkey.keycode, event.xkey.state);
fflush(stdout);
} else
if (event.type == KeyRelease) {
printf("KeyRelease: keycode %u state %u\n", event.xkey.keycode, event.xkey.state);
fflush(stdout);
if (event.xkey.keycode == escape)
break;
} else
if (event.type == UnmapNotify) {
XUngrabKeyboard(display, CurrentTime);
XDestroyWindow(display, window);
XCloseDisplay(display);
display = XOpenDisplay(NULL);
rootwindow = DefaultRootWindow(display);
window = XCreateWindow(display, rootwindow,
-99, -99, 1, 1, /* x, y, width, height */
0, 0, InputOnly, /* border, depth, class */
CopyFromParent, /* visual */
0, NULL); /* valuemask and attributes */
XSelectInput(display, window, StructureNotifyMask | SubstructureRedirectMask | ResizeRedirectMask | KeyPressMask | KeyReleaseMask);
XLowerWindow(display, window);
XMapWindow(display, window);
do {
XNextEvent(display, &event);
} while (event.type != MapNotify);
XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
XLowerWindow(display, window);
escape = XKeysymToKeycode(display, XK_Escape);
} else {
printf("Event type %d\n", event.type);
fflush(stdout);
}
}
XUngrabKeyboard(display, CurrentTime);
XDestroyWindow(display, window);
XCloseDisplay(display);
return 0;
}
它使用一个小窗口(我什至没有为它设置标题),它降低到窗口堆栈的底部,因此它位于任何现有窗口的后面。您可以与窗口管理器(WM)通信,使窗口无装饰且透明,或图标化,以便屏幕上没有可见的窗口;上面的代码不麻烦。
我使用的技巧是,每当用户设法取消映射窗口时(例如,通过移动到另一个工作区),代码都会销毁旧窗口,创建一个新窗口,然后重新获取键盘。它应该足够快,不会丢失任何按键。可能还有其他方法可以做到这一点,但我怀疑它们需要与窗口管理器进行更密切的交互。
请注意,我从来没有需要如此持久地真正抓住键盘,因此上述方法可能不是最简单的。这只是我认为有效的一种方法;可能有更好的。
以下命令会将整个 X 会话的所有事件列表打印到控制台:
$ xinput test-xi2 --root
输出示例:
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ USB Mouse id=10 [slave pointer (2)]
⎜ ↳ MCE IR Keyboard/Mouse (ite-cir) id=11 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Video Bus id=7 [slave keyboard (3)]
↳ Power Button id=8 [slave keyboard (3)]
↳ Oracle USB Keyboard id=9 [slave keyboard (3)]
↳ ITE8713 CIR transceiver id=12 [slave keyboard (3)]
EVENT type 14 (RawKeyRelease)
device: 3 (9)
detail: 36
valuators:
EVENT type 3 (KeyRelease)
device: 9 (9)
detail: 36
flags:
root: 1324.55/821.81
event: 1324.55/821.81
buttons:
modifiers: locked 0x10 latched 0 base 0 effective: 0x10
group: locked 0 latched 0 base 0 effective: 0
valuators:
windows: root 0x9c event 0x9c child 0x7291d5
EVENT type 15 (RawButtonPress)
device: 2 (10)
detail: 1
valuators:
flags:
EVENT type 4 (ButtonPress)
device: 10 (10)
detail: 1
flags:
root: 1324.55/821.81
event: 1324.55/821.81
buttons:
modifiers: locked 0x10 latched 0 base 0 effective: 0x10
group: locked 0 latched 0 base 0 effective: 0
valuators:
windows: root 0x9c event 0x9c child 0x7291d5
EVENT type 16 (RawButtonRelease)
device: 2 (10)
detail: 1
valuators:
flags:
对于 X11,可以使用
XQueryKeymap()
,它通常用于修饰键状态访问。
CWOverrideRedirect
和 attr.override_redirect
。但是,您需要对这 256 位进行一些处理,其中每个位显示特定键是否处于按住状态:now(在
XQueryKeymap()
时)。另请参阅XKeysymToKeycode()
。
#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h> // gcc: -lX11
unsigned char keys[32];
void main() {
Display* dpy = XOpenDisplay(NULL);
while (1) {
XQueryKeymap(dpy, keys);
for (int i = 0; i < 32; i++)
printf("%08b%s", keys[i], ((i+1) % 8) ? " ":"\n");
printf("\n");
sleep(1);
}
XCloseDisplay(dpy);
}
备注:
Ctrl
+Alt
+Fn
)。