デスクトップをクリックするたびに、カーソルの下に画像が表示されるようにします。私はこのタスクに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_SYNC
Xサーバーがxcb_allow_events
呼び出されるまでイベントをキューに追加する必要があります(詳細はXlibのドキュメントに記載さman XGrabPointer
れていますが、XCBには適用されないため、詳細には依存しません)。ループxcb_allow_events
内の後続の呼び出しは、while
ポインターイベント処理を固定解除(固定解除)します。パラメータを渡すとXCB_ALLOW_REPLAY_POINTER
ボタンイベントが再生されます。
xcb_poll_for_event
理由は完全にはわかりませんが、ループ内で使用することが重要です。