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,以防此資訊發生任何變化。
我解開了這個謎。顯然
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)。隨後對-loopxcb_allow_events
內的呼叫解凍(解凍)指針事件處理。while
傳遞XCB_ALLOW_REPLAY_POINTER
參數重放按鈕事件。在循環中使用很重要
xcb_poll_for_event
,儘管我不完全理解為什麼。