Wayland: seal xdg-activation tokens with the last pointer-press serial#4612
Wayland: seal xdg-activation tokens with the last pointer-press serial#4612Le-Syl21 wants to merge 1 commit into
Conversation
|
Note on CI: the |
kchibisov
left a comment
There was a problem hiding this comment.
there's already latest button serial stored on wl pointer, can just use it.
|
Thanks for the review! Reworked in 5660929 — the serial now lives on One nuance on reusing the existing implicit_grab = (clutter_event_type (event) == CLUTTER_BUTTON_PRESS) && (pointer->button_count == 1);
...
if (implicit_grab)
pointer->grab_serial = wl_display_get_serial (seat->wl_display);and |
kchibisov
left a comment
There was a problem hiding this comment.
Not in the mood to talk with robot.
Tokens issued by Window::request_activation_token and the user-attention path were built with set_surface + commit but never set_serial, so compositors enforcing focus-stealing prevention (mutter, kwin, niri) refused the ensuing activation and the launched application's window opened without focus. Read the serial already stored on the pointer, through the same apply_on_pointer pattern used by drag_window / show_window_menu.
5660929 to
cf2aad6
Compare
|
You were right — I misread your comment. Tested on plain master: requesting the token from the window and passing it to the spawned app already transfers focus fine, no set_serial needed. This PR isn't necessary. Sorry for the noise. |
The winit set_serial patch turned out unnecessary: empirical probing (examples-based test app, GNOME Wayland) shows stock winit's request_activation_token — set_surface + commit, no serial — already mints tokens mutter honors. Upstream PR rust-windowing/winit#4612 closed accordingly; PinReady now builds against crates.io winit 0.30.13. Also drop the emath/epaint/egui-winit patch entries: nothing in the tree depends on emath or epaint from crates.io anymore (the old rotation fork did; the egui-rotate plugin only depends on egui), and egui-winit resolves to the fork automatically as an internal path dep of the patched eframe. Lockfile confirms a single copy of each. The launcher-side activation flow is unchanged and still requires the egui fork (ViewportCommand::RequestActivationToken + Event::ActivationTokenReceived, upstream PR emilk/egui#8282).
Problem
Window::request_activation_token()(and the xdg-activation path ofrequest_user_attention) builds itsxdg_activation_token_v1request withset_surface+commit, but never callsset_serial.The protocol marks
set_serialas optional, but adds:In practice, compositors enforcing focus-stealing prevention (mutter, KWin, niri in strict mode) validate the token's serial against the pointer's grab serial and silently refuse the ensuing
activate()when it's absent. On mutter the minted token string ends in_TIME0and the activation degrades to demands-attention at best. Net effect: an app that requests a token on user click and hands it to a child process viaXDG_ACTIVATION_TOKENsees the child's window open without focus — defeating the exact purpose of the API.This mirrors what GTK4/GDK and Qt do internally (both seal launch tokens with the serial of the triggering input event), and is the missing piece behind several "launched app opens behind the launcher on GNOME Wayland" reports across the ecosystem (e.g. wezterm/wezterm#3619, ghostty-org/ghostty#5812 discuss the same compositor behavior).
Fix
(WlSeat, serial)of the last pointer button press on the window'sWindowState(recorded in the pointer handler, which already locks that state).request_activation_token,request_user_attention) with it viaset_serial(serial, &seat)beforecommit.Why press only: compositors update the pointer grab serial exclusively on button press (mutter
meta-wayland-pointer.c). UI toolkits fire click actions on release, so recording the release serial too would always overwrite the press serial with one the compositor doesn't recognize — we hit exactly this during testing (token still refused when sealed with the release serial; accepted with the press serial).Storing per-
WindowState(rather than globally) also keeps the serial and theset_surfacetarget referring to the same surface, which matches how compositors validate the pair, and avoids growing the platformWindowenum.If the window has never seen a button press, behavior is unchanged (token issued without serial, best-effort as before).
Testing
Tested on a GNOME 46 / mutter Wayland cabinet setup (winit-based launcher app, fullscreen):
ActivationTokenDone, and passes it inXDG_ACTIVATION_TOKENto a spawned process.meta-wayland-activation.c:token_can_activate→meta_wayland_seat_get_grab_info).gnome-text-editor(GTK4, reference consumer ofXDG_ACTIVATION_TOKEN) with the sealed token transfers keyboard focus reliably (3/3 runs, including >30 s after launcher startup, i.e. long past any startup token's validity).set_serialtime.cargo +nightly fmt,cargo clippy -p winit-waylandandcargo test -p winit-waylandare clean; the change is confined towinit-wayland.Future work
Keyboard (
wl_keyboard.key) and touch (wl_touch.down) serials can be recorded the same way for apps driven without a pointer; this PR keeps the scope to the pointer path.