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);

私が知っている限り、「autocordirect」を使用すると、ポインタman xcb_grab_buttonイベントを関数の5番目の引数として渡してもポインタイベントが影響を受け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ボタンへのポインタをつかんで置きました。 5番目の引数は、XCB_GRAB_MODE_SYNCXサーバーがxcb_allow_events呼び出されるまでイベントをキューに追加する必要があります(詳細はXlibのドキュメントに記載さman XGrabPointerれていますが、XCBには適用されないため、詳細には依存しません)。ループxcb_allow_events内の後続の呼び出しは、whileポインターイベント処理を固定解除(固定解除)します。パラメータを渡すとXCB_ALLOW_REPLAY_POINTERボタンイベントが再生されます。

xcb_poll_for_event理由は完全にはわかりませんが、ループ内で使用することが重要です。

関連情報