Linux X11 - 全局键盘钩

Xeo*_*eon 27 c++ linux x11 hook

是否有可能(或如何)创建一个与Windows中的全局钩子(SetWindowsHookEx())类似的机制(在Linux X11,C++中)?

我希望能够抓住关键事件,但有可能进一步传播.我正在尝试使用XGrabKey解决方案(就像在xbindkeys中一样),但是当我设置捕获键事件时,此事件被"消耗".

该机制的要求如下:

  1. 全局/系统范围 - 无论具有焦点的窗口如何都捕获事件
  2. "抓住"和"抓住通过"的可能性
  3. 它一定很快

示例代码如下所示:

bool myFlagIsSet = false;
XEvent event;
while (true) {
    while (XPending(display) > 0) {
        usleep(SLEEP_TIME);
    }

    XNextEvent(display, &event);
    switch (e.type) {
        case KeyPress:
            if (myFlagIsSet) {
                //do not propagate
            }
            // propagate
            break;
        case KeyRelease:
            if (myFlagIsSet) {
                //do not propagate
            }
            // propagate
            break;
    }
}
Run Code Online (Sandbox Code Playgroud)

在Windows上我简单地写道:

if(event.isConsumed()) {
    return LRESULT(1);
}
//...
return CallNextHookEx(hookHandle, nCode, wParam, lParam);
Run Code Online (Sandbox Code Playgroud)

我也尝试过使用XUngrabKey和XSendEvent:

switch (event.type) {
    case KeyPress:
        if (myFlagIsSet) {
            //do not propagate
        }
        // propagate
        XUngrabKey(...);
        XSendEvent(..., &event);
        XGrabKey(...);
        break;
    case KeyRelease:
        ...
    }
Run Code Online (Sandbox Code Playgroud)

不幸的是XSendEvent对我来说不明原因 - 即使XGrabKey行被评论,也不要发送此事件.

是否有可能成功完成这种方法?

如果我被判失败,请建议其他方法

编辑

我想使用Compiz Window Manager在Ubuntu Gnome上实现它

Xin*_*rea 6

尝试从此页面编译简单的代码:

\n\n

http://webhamster.ru/site/page/index/articles/comp/367

\n\n

这是获取全局键盘事件的示例。这个小应用程序作为xinput

\n\n

备注1:将设备ID写入mian.cpp(不带参数运行xinput获取ID):

\n\n
sprintf(deviceId, "9");\n
Run Code Online (Sandbox Code Playgroud)\n\n

备注2:编译命令:

\n\n
gcc ./main.cpp -lstdc++ -lX11 -lXext -lXi\n
Run Code Online (Sandbox Code Playgroud)\n\n

Remakr 3:编译前,安装libxi-dev包:

\n\n
apt-get install libxi-dev\n
Run Code Online (Sandbox Code Playgroud)\n\n

文件main.h

\n\n
#include <X11/Xlib.h>\n#include <X11/extensions/XInput.h>\n\n#ifdef HAVE_XI2\n#include <X11/extensions/XInput2.h>\n#endif\n\n#include <X11/Xutil.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nextern int xi_opcode; /* xinput extension op code */\n\nXDeviceInfo* find_device_info( Display *display, char *name, Bool only_extended);\n\n#if HAVE_XI2\nXIDeviceInfo* xi2_find_device_info(Display *display, char *name);\nint xinput_version(Display* display);\n#endif\n
Run Code Online (Sandbox Code Playgroud)\n\n

文件主.cpp

\n\n
#include <iostream>\n#include <cstdio>\n#include <cstdlib>\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include "main.h"\n#include <ctype.h>\n#include <string.h>\n\nusing namespace std;\n\nint xi_opcode;\n\n#define INVALID_EVENT_TYPE -1\n\nstatic int motion_type = INVALID_EVENT_TYPE;\nstatic int button_press_type = INVALID_EVENT_TYPE;\nstatic int button_release_type = INVALID_EVENT_TYPE;\nstatic int key_press_type = INVALID_EVENT_TYPE;\nstatic int key_release_type = INVALID_EVENT_TYPE;\nstatic int proximity_in_type = INVALID_EVENT_TYPE;\nstatic int proximity_out_type = INVALID_EVENT_TYPE;\n\nstatic int register_events(Display *dpy,\n                           XDeviceInfo *info,\n                           char *dev_name,\n                           Bool handle_proximity)\n{\n    int            number = 0;    /* number of events registered */\n    XEventClass        event_list[7];\n    int            i;\n    XDevice        *device;\n    Window        root_win;\n    unsigned long    screen;\n    XInputClassInfo    *ip;\n\n    screen = DefaultScreen(dpy);\n    root_win = RootWindow(dpy, screen);\n\n    device = XOpenDevice(dpy, info->id);\n\n    if (!device) {\n    printf("unable to open device \'%s\'\\n", dev_name);\n    return 0;\n    }\n\n    if (device->num_classes > 0) {\n    for (ip = device->classes, i=0; i<info->num_classes; ip++, i++) {\n        switch (ip->input_class) {\n        case KeyClass:\n        DeviceKeyPress(device, key_press_type, event_list[number]); number++;\n        DeviceKeyRelease(device, key_release_type, event_list[number]); number++;\n        break;\n\n        case ButtonClass:\n        DeviceButtonPress(device, button_press_type, event_list[number]); number++;\n        DeviceButtonRelease(device, button_release_type, event_list[number]); number++;\n        break;\n\n        case ValuatorClass:\n        DeviceMotionNotify(device, motion_type, event_list[number]); number++;\n        if (handle_proximity) {\n            ProximityIn(device, proximity_in_type, event_list[number]); number++;\n            ProximityOut(device, proximity_out_type, event_list[number]); number++;\n        }\n        break;\n\n        default:\n        printf("unknown class\\n");\n        break;\n        }\n    }\n\n    if (XSelectExtensionEvent(dpy, root_win, event_list, number)) {\n        printf("error selecting extended events\\n");\n        return 0;\n    }\n    }\n    return number;\n}\n\n\nstatic void print_events(Display *dpy)\n{\n    XEvent        Event;\n\n    setvbuf(stdout, NULL, _IOLBF, 0);\n\n    while(1) {\n    XNextEvent(dpy, &Event);\n\n    if (Event.type == motion_type) {\n        int    loop;\n        XDeviceMotionEvent *motion = (XDeviceMotionEvent *) &Event;\n\n        printf("motion ");\n\n        for(loop=0; loop<motion->axes_count; loop++) {\n        printf("a[%d]=%d ", motion->first_axis + loop, motion->axis_data[loop]);\n        }\n        printf("\\n");\n    } else if ((Event.type == button_press_type) ||\n           (Event.type == button_release_type)) {\n        int    loop;\n        XDeviceButtonEvent *button = (XDeviceButtonEvent *) &Event;\n\n        printf("button %s %d ", (Event.type == button_release_type) ? "release" : "press  ",\n           button->button);\n\n        for(loop=0; loop<button->axes_count; loop++) {\n        printf("a[%d]=%d ", button->first_axis + loop, button->axis_data[loop]);\n        }\n        printf("\\n");\n    } else if ((Event.type == key_press_type) ||\n           (Event.type == key_release_type)) {\n        int    loop;\n        XDeviceKeyEvent *key = (XDeviceKeyEvent *) &Event;\n\n        printf("key %s %d ", (Event.type == key_release_type) ? "release" : "press  ",\n           key->keycode);\n\n        for(loop=0; loop<key->axes_count; loop++) {\n        printf("a[%d]=%d ", key->first_axis + loop, key->axis_data[loop]);\n        }\n        printf("\\n");\n    } else if ((Event.type == proximity_out_type) ||\n           (Event.type == proximity_in_type)) {\n        int    loop;\n        XProximityNotifyEvent *prox = (XProximityNotifyEvent *) &Event;\n\n        printf("proximity %s ", (Event.type == proximity_in_type) ? "in " : "out");\n\n        for(loop=0; loop<prox->axes_count; loop++) {\n        printf("a[%d]=%d ", prox->first_axis + loop, prox->axis_data[loop]);\n        }\n        printf("\\n");\n    }\n    else {\n        printf("what\'s that %d\\n", Event.type);\n    }\n    }\n}\n\n\n// \xd0\x9e\xd0\xbf\xd1\x80\xd0\xb5\xd0\xb4\xd0\xb5\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5 \xd0\xb2\xd0\xb5\xd1\x80\xd1\x81\xd0\xb8\xd0\xb8 \xd0\xb1\xd0\xb8\xd0\xb1\xd0\xbb\xd0\xb8\xd0\xbe\xd1\x82\xd0\xb5\xd0\xba\xd0\xb8 \xd1\x80\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x80\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb9, \xd1\x83\xd1\x81\xd1\x82\xd0\xb0\xd0\xbd\xd0\xbe\xd0\xb2\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xbd\xd0\xbe\xd0\xb9 \xd0\xb4\xd0\xbb\xd1\x8f X11\nint xinput_version(Display    *display)\n{\n    XExtensionVersion    *version;\n    static int vers = -1;\n\n    if (vers != -1)\n        return vers;\n\n    version = XGetExtensionVersion(display, INAME);\n\n    if (version && (version != (XExtensionVersion*) NoSuchExtension)) {\n    vers = version->major_version;\n    XFree(version);\n    }\n\n#if HAVE_XI2\n    /* Announce our supported version so the server treats us correctly. */\n    if (vers >= XI_2_Major)\n    {\n        const char *forced_version;\n        int maj = 2,\n            min = 0;\n\n#if HAVE_XI22\n        min = 2;\n#elif HAVE_XI21\n        min = 1;\n#endif\n\n        forced_version = getenv("XINPUT_XI2_VERSION");\n        if (forced_version) {\n            if (sscanf(forced_version, "%d.%d", &maj, &min) != 2) {\n                fprintf(stderr, "Invalid format of XINPUT_XI2_VERSION "\n                                "environment variable. Need major.minor\\n");\n                exit(1);\n            }\n            printf("Overriding XI2 version to: %d.%d\\n", maj, min);\n        }\n\n        XIQueryVersion(display, &maj, &min);\n    }\n#endif\n\n    return vers;\n}\n\n\n// \xd0\x9f\xd0\xbe\xd0\xb8\xd1\x81\xd0\xba \xd0\xb8\xd0\xbd\xd1\x84\xd0\xbe\xd1\x80\xd0\xbc\xd0\xb0\xd1\x86\xd0\xb8\xd0\xb8 \xd0\xbe\xd0\xb1 \xd1\x83\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd1\x81\xd1\x82\xd0\xb2\xd0\xb5\nXDeviceInfo* find_device_info(Display *display,\n                              char *name,\n                              Bool only_extended)\n{\n    XDeviceInfo *devices;\n    XDeviceInfo *found = NULL;\n    int        loop;\n    int        num_devices;\n    int        len = strlen(name);\n    Bool    is_id = True;\n    XID        id = (XID)-1;\n\n    for(loop=0; loop<len; loop++) {\n    if (!isdigit(name[loop])) {\n        is_id = False;\n        break;\n    }\n    }\n\n    if (is_id) {\n    id = atoi(name);\n    }\n\n    devices = XListInputDevices(display, &num_devices);\n\n    for(loop=0; loop<num_devices; loop++) {\n    if ((!only_extended || (devices[loop].use >= IsXExtensionDevice)) &&\n        ((!is_id && strcmp(devices[loop].name, name) == 0) ||\n         (is_id && devices[loop].id == id))) {\n        if (found) {\n            fprintf(stderr,\n                    "Warning: There are multiple devices named \'%s\'.\\n"\n                    "To ensure the correct one is selected, please use "\n                    "the device ID instead.\\n\\n", name);\n        return NULL;\n        } else {\n        found = &devices[loop];\n        }\n    }\n    }\n    return found;\n}\n\n\nint test(Display *display, char *deviceId)\n{\n    XDeviceInfo *info;\n\n    Bool handle_proximity = True;\n\n    info = find_device_info(display, deviceId, True);\n\n    if(!info)\n    {\n      printf("unable to find device \'%s\'\\n", deviceId);\n      exit(1);\n    }\n    else\n    {\n      if(register_events(display, info, deviceId, handle_proximity))\n         print_events(display);\n      else\n      {\n        fprintf(stderr, "no event registered...\\n");\n        exit(1);\n      }\n    }\n\n    return 0;\n}\n\n\nint main()\n{\n  Display *display;\n  int event, error;\n\n  // \xd0\x98\xd0\xbd\xd0\xb8\xd1\x86\xd0\xb8\xd0\xb8\xd1\x80\xd1\x83\xd0\xb5\xd1\x82\xd1\x81\xd1\x8f \xd1\x83\xd0\xba\xd0\xb0\xd0\xb7\xd0\xb0\xd1\x82\xd0\xb5\xd0\xbb\xd1\x8c \xd0\xbd\xd0\xb0 \xd1\x82\xd0\xb5\xd0\xba\xd1\x83\xd1\x89\xd0\xb8\xd0\xb9 \xd0\xb4\xd0\xb8\xd1\x81\xd0\xbf\xd0\xbb\xd0\xb5\xd0\xb9\n  display = XOpenDisplay(NULL);\n  if (display == NULL)\n  {\n    printf("Unable to connect to X server\\n");\n    exit(1);\n  }\n\n  // \xd0\x9f\xd1\x80\xd0\xbe\xd0\xb2\xd0\xb5\xd1\x80\xd1\x8f\xd0\xb5\xd1\x82\xd1\x81\xd1\x8f \xd0\xbd\xd0\xb0\xd0\xbb\xd0\xb8\xd1\x87\xd0\xb8\xd0\xb5 \xd1\x80\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x80\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb9\n  if(!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error))\n  {\n    printf("X Input extension not available.\\n");\n    exit(1);\n  }\n\n  // \xd0\x9f\xd1\x80\xd0\xbe\xd0\xb2\xd0\xb5\xd1\x80\xd1\x8f\xd0\xb5\xd1\x82\xd1\x81\xd1\x8f \xd0\xb2\xd0\xb5\xd1\x80\xd1\x81\xd0\xb8\xd1\x8f \xd1\x80\xd0\xb0\xd1\x81\xd1\x88\xd0\xb8\xd1\x80\xd0\xb5\xd0\xbd\xd0\xb8\xd1\x8f, \xd0\xbe\xd0\xbd\xd0\xb0 \xd0\xbd\xd0\xb5 \xd0\xb4\xd0\xbe\xd0\xbb\xd0\xb6\xd0\xbd\xd0\xb0 \xd0\xb1\xd1\x8b\xd1\x82\xd1\x8c \xd0\xbd\xd1\x83\xd0\xbb\xd0\xb5\xd0\xb2\xd0\xbe\xd0\xb9\n  if(!xinput_version(display))\n  {\n    printf("%s extension not available\\n", INAME);\n    exit(1);\n  }\n\n  char deviceId[10];\n  sprintf(deviceId, "9");\n\n  test(display, deviceId);\n\n  XSync(display, False);\n  XCloseDisplay(display);\n\n  return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n


gee*_*aur 3

XSendEvent()可能确实发送了;但由于它被广泛认为是一个安全漏洞,因此大多数程序都会忽略send_event设置了该标志的 UI 事件。

标准 X11 协议不允许这样做。XInput 2.0 扩展可能会,但我对此表示怀疑;虽然 Windows 假定每个程序都侦听一个事件队列,以便程序可以拦截事件并防止将其从队列发送到其他侦听器,但每个 X11 客户端都有自己独立的队列,并且所有对某个事件感兴趣的客户端事件在其队列中接收它的独立副本。这意味着在正常情况下,错误的程序不可能阻止其他程序的运行;但这也意味着,当客户端必须阻止其他客户端时,它必须执行服务器抓取以防止服务器处理任何其他客户端的事件。