Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 on all windows which I do both on startup and when I get a create notify event.

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? AFAIK, the X protocol doesn't allow that except for active pointer grabs which Chrome surely doesn't have.

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:

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);
}
like image 819
Ingo Bürk Avatar asked May 04 '15 14:05

Ingo Bürk


2 Answers

The Chrome windows appear to be comprised of quite the tree of nested child windows. 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:

#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.)

like image 154
Jay Kominek Avatar answered Nov 07 '22 07:11

Jay Kominek


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.

Simply selecting on the entire tree causes all kinds of issues, e.g., hover doesn't work in Chrome anymore.

like image 2
Ingo Bürk Avatar answered Nov 07 '22 09:11

Ingo Bürk