X KeyPress/Release事件捕获与焦点窗口无关

7 c linux

我想记录所有传入的按键事件,无论焦点是什么窗口或指针在哪里.

我编写了一个示例代码,它应该捕获当前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并应用上面给出的相同逻辑来获得预期的结果.

Nom*_*mal 6

您需要有一个映射窗口才能抓取键盘.这是一个概念证明:

#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)通信,使窗口无装饰,透明或图标化,以便屏幕上没有可见窗口; 上面的代码没有打扰.

我使用的技巧是,每当用户设法取消映射窗口时 - 例如,通过移动到另一个工作区 - ,代码会破坏旧窗口,创建一个新窗口,然后重新抓取键盘.它应该足够快,不会丢失任何按键.可能还有其他方法可以做到这一点,但我怀疑它们需要与窗口管理器进行更密切的交互.

请注意,我从来不需要如此持久地抓住键盘,所以上述方法可能不是最简单的.这只是我认为有效的方法; 有可能更好的.

  • 您可以使用`XSendEvent(display,InputFocus,True,KeyPressMask | KeyReleaseMask,event)`将抓取的键盘事件发送到具有当前输入焦点的窗口,但这通常是错误的方法.如果你想监视所有的按键,*不要*:我不喜欢键盘嗅探器.要查看通常如何实现热键/自动键,请参阅例如`GtkHotKey`或`libtomboy/tomboykeybinder.c`; 他们在根窗口抓取特定的键(即当没有被其他窗口处理时),并且做得对. (3认同)
  • 你可以使用Linux输入子系统,从`/ dev/input/eventN`读取键盘事件(比如通过`/ dev/input/by-id`中的符号链接)来匹配你的键盘,并通过uinput,`/反馈它们.开发/ uintput`.只需谷歌"Linux输入子系统"和"linux""uinput"的例子和howtos.这种方法要求过滤器进程具有"root"权限,但它是一种更强大的方法. (3认同)

小智 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)