Search code examples
rustx11window-managersxcb

xcb: LeaveNotify received immediately after EnterNotify


I am in the process of writing a window manager in Rust for learning purposes, using the xcb library. My code and several test windows (xterm instances) are all running inside of a Xephyr session. I set my event mask on the root window as

xproto::EVENT_MASK_SUBSTRUCTURE_REDIRECT
| xproto::EVENT_MASK_SUBSTRUCTURE_NOTIFY
| xproto::EVENT_MASK_POINTER_MOTION
| xproto::EVENT_MASK_LEAVE_WINDOW
| xproto::EVENT_MASK_ENTER_WINDOW
| xproto::EVENT_MASK_BUTTON_PRESS
| xproto::EVENT_MASK_PROPERTY_CHANGE
| xproto::EVENT_MASK_FOCUS_CHANGE

and all child windows have an event mask of

xproto::EVENT_MASK_ENTER_WINDOW
| xproto::EVENT_MASK_LEAVE_WINDOW
| xproto::EVENT_MASK_BUTTON_PRESS
| xproto::EVENT_MASK_PROPERTY_CHANGE
| xproto::EVENT_MASK_POINTER_MOTION
| xproto::EVENT_MASK_FOCUS_CHANGE
| xproto::EVENT_MASK_STRUCTURE_NOTIFY
| xproto::EVENT_MASK_EXPOSURE

When I move the mouse over a non-root window, I use the EnterNotify event to grab mouse buttons on that window, for click-to-focus purposes, and ungrab on LeaveNotify. The sequence of events is:

  • Mouse on root window
  • Move mouse over non-root window
  • Receive EnterNotify for non-root window
  • Grab buttons on the non-root window
  • Immediately receive LeaveNotify, while the mouse is still over the non-root window
  • Ungrab buttons due to LeaveNotify
  • Attempt clicking on the non-root window
  • Receive a second LeaveNotify for the non-root window
  • Receive an EnterNotify for the root window, and grab mouse buttons on the root window
  • ButtonPress is sent for the root window, despite the cursor being over a non-root window

I'm genuinely unsure what the cause of this might be; using Google and the like has turned up nothing useful.


For anyone who might stumble across this later, a partial solution is:

  • Only listen on window enter events to grab buttons on a window
  • Mask root to be SUBSTRUCTURE_REDIRECT | SUBSTRUCTURE_NOTIFY | BUTTON_PRESS only
  • Don't mask LEAVE_WINDOW onto non-root windows, and don't handle those events

I don't want to add this as an answer because:

  1. I'm unsure it's the correct way of doing it.
  2. I've not tested this thoroughly.
  3. It doesn't answer my primary question of "why are events received like this?"

Solution

  • Take a look at the protocol reference manual. It describes (among lots of other things) the exact algorithm for generating enter and leave events: https://www.x.org/releases/X11R7.6/doc/xproto/x11protocol.html#events:pointer_window

    In your specific case, I would expect the LeaveNotify event to have mode: Grab, which means that the window no longer has the "normal" pointer focus, because something (your program) grabbed the input.

    If this is not the answer, I can recommend running your WM under xtrace / x11trace (available in Debian-based distros as package xtrace). This program prints all X11 traffic that "goes through". This might help figuring out what is going on.