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

时间:2012-07-03 09:19:05

标签: linux c

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

我编写了一个示例代码,该代码应该捕获当前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;
}

我正在调用XGrabKeyboard捕获键盘,因为创建窗口的应用程序可能已经抓取了键盘事件。使用上面提到的代码,我可以抓住键盘,但是无法接收while循环内键盘上任何键的KeyPress或KeyRelease事件。由于我无法接收事件,代码中是否有任何遗漏?任何帮助都非常感谢。

我的最终目标是捕捉屏幕上的关键新闻事件,而不管窗口是否对焦。我只给出了焦点窗口的示例代码,以便代码可读。我会做XQueryTree来获取所有的Windows并应用上面给出的相同逻辑来获得预期的结果。

2 个答案:

答案 0 :(得分:5)

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

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

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

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

答案 1 :(得分:3)

以下命令将整个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: