xcb:异步指针抓取不传播事件

xcb:异步指针抓取不传播事件

每当我单击桌面时,我试图使图像出现在光标下。我决定使用 xcb 来完成此任务。我想我应该从根窗口捕获指针(我真的不知道更好的方法),因为我总是希望图像出现,无论我单击哪里。显示图像的应用程序不应干扰我的正常工作流程。

到目前为止,我捕获指针的方法如下:

#include <X11/Xutil.h>
#include <X11/Xlib-xcb.h>
#include <X11/Xutil.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
void setup(xcb_connection_t *connection) {
    xcb_generic_error_t *err;
    xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
    xcb_void_cookie_t grab_cookie = xcb_grab_button(connection, True, screen->root, XCB_NONE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);
    xcb_generic_error_t *error = xcb_request_check(connection, grab_cookie);
    if (error != NULL) {
        xcb_disconnect(connection);
        perror("could not subscribe to events on a window, bailing out");
        exit(1);
    }  
    free(error);
        xcb_flush(connection);
}

int main(int argc, char *argv[]) {
    xcb_generic_event_t *e;
    Display *dpy = XOpenDisplay(NULL);
    xcb_connection_t *connection = XGetXCBConnection(dpy);
    setup(connection);
    while ((e = xcb_wait_for_event(connection))) {
        switch(e->response_type & ~0x80) {
            case XCB_BUTTON_PRESS:
                printf("Click.\n");
                break;
            default:
                break;
        }
        free(e);
    }
    xcb_ungrab_pointer(connection, XCB_TIME_CURRENT_TIME);
}

获取光标的代码行在setup方法中:

 xcb_void_cookie_t grab_cookie = xcb_grab_button(connection, True, screen->root, XCB_NONE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);

据我所知,通过“自动更正” man xcb_grab_button,如果我将其作为函数的第五个参数,我的指针事件应该不会受到影响XCB_GRAB_MODE_ASYNC(手册说的不同,但它通常很差,所以如果它不是错误的)。然而,情况并非如此:当我点击时,点击只是被我的应用程序吞没,例如,Firefox 不会对此做出反应。

如何抓住光标以使我的点击事件不被吃掉?如果 X 中不存在这样的功能,您会建议我简单地“重新发送”点击事件还是有更好的选择?我的窗口管理器是 i3,以防此信息发生任何变化。

答案1

我解开了这个谜团。显然XCB_GRAB_MODE_ASYNC没有做我想象的那样:xcb_grab_pointer手册说指针事件处理正常继续。我认为这指的是传播到其他客户端的事件,但事实并非如此。这些事件仍然只能由我的应用程序听到。要传播事件,需要在接收后重播它们:

xcb_generic_event_t *e;
xcb_generic_error_t *err;
xcb_connection_t *connection = xcb_connect(NULL, NULL);
xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
xcb_void_cookie_t grab_cookie = xcb_grab_button_checked(connection, True, screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_1, XCB_MOD_MASK_ANY);
xcb_generic_error_t *error = xcb_request_check(connection, grab_cookie);
if (error != NULL) {
    xcb_disconnect(connection);
    perror("could not subscribe to events on a window, bailing out");
    exit(1);
}  
free(error);
do {
    xcb_allow_events(connection, XCB_ALLOW_REPLAY_POINTER, XCB_CURRENT_TIME);
    e = xcb_poll_for_event(connection);
    if(!e) {
        continue;
    }
    switch(e->response_type & EVENT_MASK) {
        case XCB_BUTTON_RELEASE:
        case XCB_BUTTON_PRESS:
            printf("Hello.\n");
            break;
        default:
            break;
    }
    free(e);
} while(1);
xcb_ungrab_pointer(connection, XCB_TIME_CURRENT_TIME);

在 do-while 循环之前,抓取了 index1 按钮的指针按下和释放。第五个参数必须XCB_GRAB_MODE_SYNC使 X 服务器对事件进行排队,直到xcb_allow_events被调用(Xlib 手册man XGrabPointer提供了详细信息,尽管我不会详细依赖它,因为它不适用于 XCB)。对循环xcb_allow_events内的后续调用while将解冻(解冻)指针事件处理。传递XCB_ALLOW_REPLAY_POINTER参数会重播按钮事件。

在循环中使用很重要xcb_poll_for_event,尽管我不完全理解为什么。

相关内容