데스크탑을 클릭할 때마다 커서 아래에 이미지가 나타나도록 하려고 합니다. 나는 이 작업에 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_SYNC
X 서버가 xcb_allow_events
호출될 때까지 이벤트를 대기열에 추가해야 합니다(자세한 내용은 Xlib 설명서에 man XGrabPointer
나와 있지만 XCB에는 적용되지 않으므로 자세히 의존하지는 않겠습니다). 루프 xcb_allow_events
내의 후속 호출은 while
포인터 이벤트 처리를 고정 해제(고정 해제)합니다. 매개변수를 전달하면 XCB_ALLOW_REPLAY_POINTER
버튼 이벤트가 재생됩니다.
xcb_poll_for_event
이유를 완전히 이해하지는 못하지만 loop 내에서 사용하는 것이 중요합니다 .