xdg-shell: on xdg_popup.grab serial validation
xdg_popup.grab
:
This request must be used in response to some sort of user action like a button press, key press, or touch down event. The serial number of the event should be passed as 'serial'.
However, a grab may also be started in response to a pointer motion when the user moves the cursor to a popup menu item which opens another popup. Therefore, clients try to use what serial they think fits the most.
Current behavior
Note that the following analysis might be not completely correct.
GTK
GTK uses
- last
wl_keyboard.key
serial, regardless of the key state, - last
wl_pointer.button
press serial, - last
zwp_tablet_tool_v2.{down,button}
serial for one of the tools, regardless of the button state, - last
wl_touch.down
serial for each valid touch point, or - last
wl_touch.down
serial received.
whichever is newer.
Qt
Qt uses
- last
wl_keyboard.key
press serial, - last
wl_pointer.button
press serial, - last
zwp_tablet_tool_v2.down
serial for one of the tools, or - last
wl_touch.down
serial,
whichever is newer.
Note that tablet tool button serials are ignored.
Mutter
Mutter expects the serial to be
- one of the
wl_keyboard.key
press serials matching currently held keys, or - last
wl_keyboard.key
release serial if it's the latest key event, - last
wl_pointer.button
press serial that was corresponding to an implicit grab, or - last
wl_touch.down
serial for each valid touch point, or - last
wl_touch.down
serial sent, or - last
zwp_tablet_tool_v2.{down,button}
serial for one of the tools, regardless of the button state.
The set of currently held keys is reset on keyboard focus change.
The implicit grab mentioned is not required to be active by the time of serial validation.
Weston
Weston expects the serial to be
- last
wl_keyboard.key
serial, regardless of the key state, or - last
wl_pointer.button
press serial that was corresponding to an implicit grab, or - last
wl_touch.down
serial that was corresponding to an implicit grab, or - last
zwp_tablet_tool_v2.{down,button}
serial for one of the tools, regardless of the button state.
The implicit grabs mentioned are not required to be active by the time of serial validation.
Problems
A Qt program (Dolphin) in Mutter/Weston
Astute readers may have noticed that opening a Qt popup in Mutter/Weston with a key press (F10) and trying to open a nested popup by pointer motion will fail:
Qt attempts to use the last key press serial for it, which is already dropped.
Notably, if F10 is kept pressed, then in Weston (but not in Mutter) the serial is still considered valid for a popup grab.
A GTK program in Mutter
Opening a menu popup with a key press (Alt+F) in Mutter and trying open a different menu popup by pointer motion will fail. This happens to due Mutter clearing the pressed key serials set on keyboard focus change. For the same reason, opening a menu popup with a pointer button press, pressing and releasing a key, and trying open a different menu popup by pointer motion fails as well.
Thoughts
The nested popup problem can be resolved by simply™ ignoring xdg_popup.grab
requests for popups if one of their ancestors is a grabbing popup. As per request description, a grab ensures that 1) keyboard focus stays on the topmost grabbing popup, and 2) pointer/touch events are only delivered to the popup client; "toplevel → grabbing popup → grabbing popup" and "toplevel → grabbing popup → normal popup" chains then result in identical behavior.
The "I had a grabbing (menu) popup open, and now I need to close it and open a new one" problem might be solvable by storing the serial that allowed the previous grab (maybe for a limited time) and using it for validation too.
However, this raises a question: is there any benefit to having this serial validation logic in the protocol in the first place? To me, it feels like a "is the toplevel this popup is for focused?" check would be enough to prevent focus stealing and such. Notably, the vast majority of compositors I've looked at (KWin, Jay, Hyprland, as well as all wlroots-based compositors) just ignore xdg_popup.grab
serials.
Is the Qt problem a bug in Qt or in compositors' serial validation logic? It's not immediately clear to me.