简体   繁体   English

XCB - 未在所有窗口上接收运动通知事件

[英]XCB – Not receiving motion notify events on all windows

I am trying to be notified about any pointer motion.我正在尝试收到有关任何指针运动的通知。 Since I don't want to run as the window manager, I need to set XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION由于不想作为窗口管理器运行,所以需要设置XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION on all windows which I do both on startup and when I get a create notify event. XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION在我启动时和收到创建通知事件时执行的所有窗口上。

This seems to work fine in general and I receive motion notify events on all windows.这在一般情况下似乎工作正常,我在所有窗口上都收到了运动通知事件。 However, somehow, this isn't true for Google Chrome windows.然而,不知何故,这对于谷歌浏览器窗口来说并非如此。 I checked the event mask by explicitly querying it afterwards and it is correctly set.我之后通过显式查询来检查事件掩码,并且它已正确设置。 I also don't see anything unusual in the propagation mask.我也没有在传播掩码中看到任何异常。

What could cause Google Chrome to not report motion notify events?什么可能导致 Google Chrome 不报告运动通知事件? AFAIK, the X protocol doesn't allow that except for active pointer grabs which Chrome surely doesn't have. AFAIK,除了 Chrome 肯定没有的主动指针抓取之外,X 协议不允许这样做。

Here is how I register myself on all existing windows.这是我在所有现有窗口上注册自己的方法。 I call register_events on the root window and whenever I receive a create notify event as well:我在根窗口上调用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);
}

The Chrome windows appear to be comprised of quite the tree of nested child windows. Chrome 窗口似乎由相当多的嵌套子窗口树组成。 It appears you'll need to walk the tree of windows and monitor them all.看来您需要遍历窗口树并监视它们。 This code picks up pointer motion events across the entirety of my Chrome windows:此代码在我的整个 Chrome 窗口中获取指针运动事件:

#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++);
  }
}

(That's just intended as proof of concept, and not very nice.) (这只是作为概念证明,并不是很好。)

While @Jay Kominek's answer was helpful and valid, I've come to realize now that using the Xinput extension provides a much better approach as it won't interfere with applications whatsoever.虽然@Jay Kominek 的回答很有帮助且有效,但我现在意识到使用 Xinput 扩展提供了一种更好的方法,因为它不会干扰任何应用程序。

Simply selecting on the entire tree causes all kinds of issues, eg, hover doesn't work in Chrome anymore.简单地在整个树上进行选择会导致各种问题,例如,悬停在 Chrome 中不再起作用。

xcb provides xcb_grab_pointer to capture pointer event without registe on specific window. xcb 提供 xcb_grab_pointer 来捕获指针事件,无需在特定窗口上注册。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM