每当我单击桌面时,我试图使图像出现在光标下。我决定使用 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
,尽管我不完全理解为什么。