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포인터 이벤트를 함수의 다섯 번째 인수로 전달해도 포인터 이벤트가 영향을 받지 않아야 합니다 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_SYNCX 서버가 xcb_allow_events호출될 때까지 이벤트를 대기열에 추가해야 합니다(자세한 내용은 Xlib 설명서에 man XGrabPointer나와 있지만 XCB에는 적용되지 않으므로 자세히 의존하지는 않겠습니다). 루프 xcb_allow_events내의 후속 호출은 while포인터 이벤트 처리를 고정 해제(고정 해제)합니다. 매개변수를 전달하면 XCB_ALLOW_REPLAY_POINTER버튼 이벤트가 재생됩니다.

xcb_poll_for_event이유를 완전히 이해하지는 못하지만 loop 내에서 사용하는 것이 중요합니다 .

관련 정보