我想记录所有传入的按键事件,无论焦点是什么窗口或指针在哪里.
我编写了一个示例代码,它应该捕获当前Window的关键按下事件.
#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;
}
Run Code Online (Sandbox Code Playgroud)
我正在调用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;
}
Run Code Online (Sandbox Code Playgroud)
它使用一个小窗口(我甚至懒得为它设置标题)它降低到窗口堆栈的底部,因此它落后于任何现有的窗口.您可以与窗口管理器(WM)通信,使窗口无装饰,透明或图标化,以便屏幕上没有可见窗口; 上面的代码没有打扰.
我使用的技巧是,每当用户设法取消映射窗口时 - 例如,通过移动到另一个工作区 - ,代码会破坏旧窗口,创建一个新窗口,然后重新抓取键盘.它应该足够快,不会丢失任何按键.可能还有其他方法可以做到这一点,但我怀疑它们需要与窗口管理器进行更密切的交互.
请注意,我从来不需要如此持久地抓住键盘,所以上述方法可能不是最简单的.这只是我认为有效的方法; 有可能更好的.
小智 6
以下命令将整个 X 会话的所有事件的列表打印到控制台:
$ xinput test-xi2 --root
Run Code Online (Sandbox Code Playgroud)
示例输出:
? 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:
Run Code Online (Sandbox Code Playgroud)