xwayland: handle rare case of focus_in event occurring before map

This fixes a timing-dependent issue where the CLion main window was
sometimes not correctly focused at startup.
This commit is contained in:
John Lindgren 2025-06-11 15:10:10 -04:00 committed by Johan Malm
parent d7f1b021b3
commit 5a1466d355
2 changed files with 32 additions and 0 deletions

View file

@ -39,6 +39,7 @@ struct xwayland_unmanaged {
struct xwayland_view {
struct view base;
struct wlr_xwayland_surface *xwayland_surface;
bool focused_before_map;
/* Events unique to XWayland views */
struct wl_listener associate;

View file

@ -587,6 +587,25 @@ handle_focus_in(struct wl_listener *listener, void *data)
wl_container_of(listener, xwayland_view, focus_in);
struct view *view = &xwayland_view->base;
struct seat *seat = &view->server->seat;
if (!view->surface) {
/*
* It is rare but possible for the focus_in event to be
* received before the map event. This has been seen
* during CLion startup, when focus is initially offered
* to the splash screen but accepted later by the main
* window instead. (In this case, the focus transfer is
* client-initiated but allowed by wlroots because the
* same PID owns both windows.)
*
* Set a flag to record this condition, and update the
* seat focus later when the view is actually mapped.
*/
wlr_log(WLR_DEBUG, "focus_in received before map");
xwayland_view->focused_before_map = true;
return;
}
if (view->surface != seat->seat->keyboard_state.focused_surface) {
seat_focus_surface(seat, view->surface);
}
@ -805,6 +824,18 @@ xwayland_view_map(struct view *view)
wl_signal_add(&xwayland_surface->surface->events.commit, &view->commit);
view->commit.notify = handle_commit;
/*
* If the view was focused (on the xwayland server side) before
* being mapped, update the seat focus now. Note that this only
* really matters in the case of Globally Active input windows.
* In all other cases, it's redundant since view_impl_map()
* results in the view being focused anyway.
*/
if (xwayland_view->focused_before_map) {
xwayland_view->focused_before_map = false;
seat_focus_surface(&view->server->seat, view->surface);
}
view_impl_map(view);
view->been_mapped = true;