Linux

xcb:非同步指針抓取不傳播事件

  • January 8, 2020

每當我點擊桌面時,我都會嘗試使圖像出現在游標下。我決定使用 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,儘管我不完全理解為什麼。

引用自:https://unix.stackexchange.com/questions/397339