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