XCB – 未收到所有 windows 上的运动通知事件
XCB – Not receiving motion notify events on all windows
我正在尝试接收有关任何指针移动的通知。由于我不想 运行 作为 window 经理,我需要在所有 windows 上设置 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION
,我在启动时和收到创建通知时都会这样做事件。
这似乎总体上工作正常,我在所有 windows 上都收到了动作通知事件。然而,不知何故,Google Chrome windows 并非如此。我之后通过显式查询检查了事件掩码,并且它已正确设置。我也没有在传播掩码中看到任何异常。
什么会导致 Google Chrome 不报告动作通知事件? AFAIK,X 协议不允许,除了 Chrome 肯定没有的主动指针抓取。
这是我在所有现有 windows 上注册自己的方法。我在根 window 上调用 register_events
并且每当我收到创建通知事件时:
static void register_events(xcb_window_t window) {
xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(connection,
window, XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_LEAVE_WINDOW });
xcb_generic_error_t *error = xcb_request_check(connection, cookie);
if (error != NULL) {
xcb_disconnect(connection);
errx(EXIT_FAILURE, "could not subscribe to events on a window, bailing out");
}
}
static void register_existing_windows(void) {
xcb_query_tree_reply_t *reply;
if ((reply = xcb_query_tree_reply(connection, xcb_query_tree(connection, root), 0)) == NULL) {
return;
}
int len = xcb_query_tree_children_length(reply);
xcb_window_t *children = xcb_query_tree_children(reply);
for (int i = 0; i < len; i++) {
register_events(children[i]);
}
xcb_flush(connection);
free(reply);
}
Chrome windows 似乎完全由嵌套子树 windows 组成。看来您需要遍历 windows 的树并监视它们。此代码在我的整个 Chrome windows:
中获取指针运动事件
#include <stdio.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <X11/Xlib.h>
static void register_events(xcb_connection_t *conn,
xcb_window_t window) {
xcb_void_cookie_t cookie =
xcb_change_window_attributes_checked(conn,
window, XCB_CW_EVENT_MASK,
(uint32_t[]) {
XCB_EVENT_MASK_POINTER_MOTION });
xcb_generic_error_t *error = xcb_request_check(conn, cookie);
if (error != NULL) {
xcb_disconnect(conn);
exit(-1);
}
}
static void register_existing_windows(xcb_connection_t *conn,
xcb_window_t root) {
int i, len;
xcb_window_t *children;
xcb_query_tree_reply_t *reply;
if ((reply = xcb_query_tree_reply(conn,
xcb_query_tree(conn, root), 0))
== NULL)
{
return;
}
len = xcb_query_tree_children_length(reply);
children = xcb_query_tree_children(reply);
for (i = 0; i < len; i++) {
register_events(conn, children[i]);
register_existing_windows(conn, children[i]);
}
xcb_flush(conn);
}
void main(void) {
int i=0;
/* Open the connection to the X server */
xcb_connection_t *conn = xcb_connect (NULL, NULL);
/* Get the first screen */
xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (conn)).data;
register_existing_windows(conn, screen->root);
while(1) {
xcb_generic_event_t *evt;
evt = xcb_wait_for_event(conn);
printf("%i\n", i++);
}
}
(这只是为了证明概念,不是很好。)
虽然@Jay Kominek 的回答很有帮助且有效,但我现在开始意识到使用 Xinput 扩展提供了一种更好的方法,因为它不会干扰任何应用程序。
简单地选择整个树会导致各种问题,例如,悬停在 Chrome 中不再起作用。
xcb 提供 xcb_grab_pointer 捕获指针事件,无需在特定 window 上注册。
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
void
print_modifiers (uint32_t mask)
{
const char **mod, *mods[] = {
"Shift", "Lock", "Ctrl", "Alt",
"Mod2", "Mod3", "Mod4", "Mod5",
"Button1", "Button2", "Button3", "Button4", "Button5"
};
printf ("Modifier mask: ");
for (mod = mods ; mask; mask >>= 1, mod++)
if (mask & 1)
printf(*mod);
putchar ('\n');
}
int
main ()
{
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_window_t win;
xcb_generic_event_t *e;
uint32_t mask = 0;
/* Open the connection to the X server */
c = xcb_connect (NULL, NULL);
/* Get the first screen */
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
mask = XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION;
xcb_grab_pointer(c, false, screen->root, mask, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME);
xcb_flush (c);
while ((e = xcb_wait_for_event (c))) {
switch (e->response_type & ~0x80) {
case XCB_BUTTON_PRESS: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
print_modifiers(ev->state);
switch (ev->detail) {
case 4:
printf ("Wheel Button up in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
case 5:
printf ("Wheel Button down in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
default:
printf ("Button %d pressed in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
}
break;
}
case XCB_BUTTON_RELEASE: {
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)e;
print_modifiers(ev->state);
printf ("Button %d released in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
break;
}
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)e;
printf ("Mouse moved in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
}
default:
/* Unknown event type, ignore it */
printf("Unknown event: %d\n", e->response_type);
break;
}
/* Free the Generic Event */
free (e);
}
return 0;
}
我正在尝试接收有关任何指针移动的通知。由于我不想 运行 作为 window 经理,我需要在所有 windows 上设置 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION
,我在启动时和收到创建通知时都会这样做事件。
这似乎总体上工作正常,我在所有 windows 上都收到了动作通知事件。然而,不知何故,Google Chrome windows 并非如此。我之后通过显式查询检查了事件掩码,并且它已正确设置。我也没有在传播掩码中看到任何异常。
什么会导致 Google Chrome 不报告动作通知事件? AFAIK,X 协议不允许,除了 Chrome 肯定没有的主动指针抓取。
这是我在所有现有 windows 上注册自己的方法。我在根 window 上调用 register_events
并且每当我收到创建通知事件时:
static void register_events(xcb_window_t window) {
xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(connection,
window, XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_LEAVE_WINDOW });
xcb_generic_error_t *error = xcb_request_check(connection, cookie);
if (error != NULL) {
xcb_disconnect(connection);
errx(EXIT_FAILURE, "could not subscribe to events on a window, bailing out");
}
}
static void register_existing_windows(void) {
xcb_query_tree_reply_t *reply;
if ((reply = xcb_query_tree_reply(connection, xcb_query_tree(connection, root), 0)) == NULL) {
return;
}
int len = xcb_query_tree_children_length(reply);
xcb_window_t *children = xcb_query_tree_children(reply);
for (int i = 0; i < len; i++) {
register_events(children[i]);
}
xcb_flush(connection);
free(reply);
}
Chrome windows 似乎完全由嵌套子树 windows 组成。看来您需要遍历 windows 的树并监视它们。此代码在我的整个 Chrome windows:
中获取指针运动事件#include <stdio.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <X11/Xlib.h>
static void register_events(xcb_connection_t *conn,
xcb_window_t window) {
xcb_void_cookie_t cookie =
xcb_change_window_attributes_checked(conn,
window, XCB_CW_EVENT_MASK,
(uint32_t[]) {
XCB_EVENT_MASK_POINTER_MOTION });
xcb_generic_error_t *error = xcb_request_check(conn, cookie);
if (error != NULL) {
xcb_disconnect(conn);
exit(-1);
}
}
static void register_existing_windows(xcb_connection_t *conn,
xcb_window_t root) {
int i, len;
xcb_window_t *children;
xcb_query_tree_reply_t *reply;
if ((reply = xcb_query_tree_reply(conn,
xcb_query_tree(conn, root), 0))
== NULL)
{
return;
}
len = xcb_query_tree_children_length(reply);
children = xcb_query_tree_children(reply);
for (i = 0; i < len; i++) {
register_events(conn, children[i]);
register_existing_windows(conn, children[i]);
}
xcb_flush(conn);
}
void main(void) {
int i=0;
/* Open the connection to the X server */
xcb_connection_t *conn = xcb_connect (NULL, NULL);
/* Get the first screen */
xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (conn)).data;
register_existing_windows(conn, screen->root);
while(1) {
xcb_generic_event_t *evt;
evt = xcb_wait_for_event(conn);
printf("%i\n", i++);
}
}
(这只是为了证明概念,不是很好。)
虽然@Jay Kominek 的回答很有帮助且有效,但我现在开始意识到使用 Xinput 扩展提供了一种更好的方法,因为它不会干扰任何应用程序。
简单地选择整个树会导致各种问题,例如,悬停在 Chrome 中不再起作用。
xcb 提供 xcb_grab_pointer 捕获指针事件,无需在特定 window 上注册。
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
void
print_modifiers (uint32_t mask)
{
const char **mod, *mods[] = {
"Shift", "Lock", "Ctrl", "Alt",
"Mod2", "Mod3", "Mod4", "Mod5",
"Button1", "Button2", "Button3", "Button4", "Button5"
};
printf ("Modifier mask: ");
for (mod = mods ; mask; mask >>= 1, mod++)
if (mask & 1)
printf(*mod);
putchar ('\n');
}
int
main ()
{
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_window_t win;
xcb_generic_event_t *e;
uint32_t mask = 0;
/* Open the connection to the X server */
c = xcb_connect (NULL, NULL);
/* Get the first screen */
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
mask = XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION;
xcb_grab_pointer(c, false, screen->root, mask, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME);
xcb_flush (c);
while ((e = xcb_wait_for_event (c))) {
switch (e->response_type & ~0x80) {
case XCB_BUTTON_PRESS: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
print_modifiers(ev->state);
switch (ev->detail) {
case 4:
printf ("Wheel Button up in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
case 5:
printf ("Wheel Button down in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
default:
printf ("Button %d pressed in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
}
break;
}
case XCB_BUTTON_RELEASE: {
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)e;
print_modifiers(ev->state);
printf ("Button %d released in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
break;
}
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)e;
printf ("Mouse moved in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
}
default:
/* Unknown event type, ignore it */
printf("Unknown event: %d\n", e->response_type);
break;
}
/* Free the Generic Event */
free (e);
}
return 0;
}