diff --git a/include/labwc.h b/include/labwc.h index af451140..90dea7cd 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -476,10 +476,12 @@ void xdg_shell_finish(struct server *server); void desktop_focus_view(struct view *view, bool raise); /** - * desktop_focus_view_or_surface() - like desktop_focus_view() but can - * also focus other (e.g. xwayland-unmanaged) surfaces + * desktop_focus_for_cursor_update() - like desktop_focus_view() but can + * also focus other (e.g. xwayland-unmanaged) surfaces. + * + * Used only for cursor-driven focus updates. */ -void desktop_focus_view_or_surface(struct seat *seat, struct view *view, +void desktop_focus_for_cursor_update(struct seat *seat, struct view *view, struct wlr_surface *surface, bool raise); void desktop_arrange_all_views(struct server *server); diff --git a/src/desktop.c b/src/desktop.c index df179eb2..70a376fa 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -39,8 +39,27 @@ desktop_arrange_all_views(struct server *server) } } -void -desktop_focus_view(struct view *view, bool raise) +static void +set_or_offer_focus(struct view *view) +{ + struct seat *seat = &view->server->seat; + switch (view_wants_focus(view)) { + case VIEW_WANTS_FOCUS_ALWAYS: + if (view->surface != seat->seat->keyboard_state.focused_surface) { + seat_focus_surface(seat, view->surface); + } + break; + case VIEW_WANTS_FOCUS_LIKELY: + case VIEW_WANTS_FOCUS_UNLIKELY: + view_offer_focus(view); + break; + case VIEW_WANTS_FOCUS_NEVER: + break; + } +} + +static void +_desktop_focus_view(struct view *view, bool raise, bool for_cursor_update) { assert(view); /* @@ -77,34 +96,40 @@ desktop_focus_view(struct view *view, bool raise) workspaces_switch_to(view->workspace, /*update_focus*/ false); } - struct seat *seat = &view->server->seat; - switch (view_wants_focus(view)) { - case VIEW_WANTS_FOCUS_ALWAYS: - if (view->surface != seat->seat->keyboard_state.focused_surface) { - seat_focus_surface(seat, view->surface); - } - break; - case VIEW_WANTS_FOCUS_LIKELY: - case VIEW_WANTS_FOCUS_UNLIKELY: - view_offer_focus(view); - break; - case VIEW_WANTS_FOCUS_NEVER: - break; - } - if (raise) { view_move_to_front(view); } + + /* + * When followMouse=yes, only allow focusing a view that's under + * the cursor (after unminimizing and potentially raising it). + * Note that view_move_to_front() may also have raised sub- or + * sibling views. In that case, focus the one under the cursor. + */ + if (rc.focus_follow_mouse && !for_cursor_update) { + struct cursor_context ctx = get_cursor_context(view->server); + if (ctx.view && view_get_root(ctx.view) == view_get_root(view)) { + set_or_offer_focus(ctx.view); + } + } else { + set_or_offer_focus(view); + } +} + +void +desktop_focus_view(struct view *view, bool raise) +{ + _desktop_focus_view(view, raise, /* for_cursor_update */ false); } /* TODO: focus layer-shell surfaces also? */ void -desktop_focus_view_or_surface(struct seat *seat, struct view *view, +desktop_focus_for_cursor_update(struct seat *seat, struct view *view, struct wlr_surface *surface, bool raise) { assert(view || surface); if (view) { - desktop_focus_view(view, raise); + _desktop_focus_view(view, raise, /* for_cursor_update */ true); #if HAVE_XWAYLAND } else { struct wlr_xwayland_surface *xsurface = diff --git a/src/input/cursor.c b/src/input/cursor.c index d97b93ab..547f5fa9 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -629,7 +629,7 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * * If followMouse=yes, update the keyboard focus when the * cursor enters a surface */ - desktop_focus_view_or_surface(seat, + desktop_focus_for_cursor_update(seat, view_from_wlr_surface(new_focused_surface), new_focused_surface, rc.raise_on_focus); } @@ -649,7 +649,7 @@ _cursor_update_focus(struct server *server) * Always focus the surface below the cursor when * followMouse=yes and followMouseRequiresMovement=no. */ - desktop_focus_view_or_surface(&server->seat, ctx.view, + desktop_focus_for_cursor_update(&server->seat, ctx.view, ctx.surface, rc.raise_on_focus); } @@ -1092,7 +1092,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms } #ifdef HAVE_XWAYLAND } else if (ctx.type == LAB_SSD_UNMANAGED) { - desktop_focus_view_or_surface(seat, NULL, ctx.surface, + desktop_focus_for_cursor_update(seat, NULL, ctx.surface, /*raise*/ false); #endif } diff --git a/src/view-impl-common.c b/src/view-impl-common.c index 3357367b..f395a426 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -11,7 +11,6 @@ void view_impl_map(struct view *view) { - desktop_focus_view(view, /*raise*/ true); view_update_title(view); view_update_app_id(view); if (!view->been_mapped) { diff --git a/src/xdg.c b/src/xdg.c index a31550e5..87d1fb11 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -217,8 +217,18 @@ handle_commit(struct wl_listener *listener, void *data) } if (update_required) { + bool was_empty = wlr_box_empty(current); view_impl_apply_geometry(view, size.width, size.height); + /* + * Try to focus the view once it has a valid surface + * size. Before this point, the under-mouse checks in + * desktop_focus_view() fail with focus-follows-mouse. + */ + if (was_empty && !wlr_box_empty(&size)) { + desktop_focus_view(view, /*raise*/ true); + } + /* * Some views (e.g., terminals that scale as multiples of rows * and columns, or windows that impose a fixed aspect ratio), diff --git a/src/xwayland.c b/src/xwayland.c index dfc38add..1c1581ea 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -826,14 +826,14 @@ xwayland_view_map(struct view *view) /* * 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. + * being mapped, just update the seat focus. Otherwise, try to + * focus the view now. */ if (xwayland_view->focused_before_map) { xwayland_view->focused_before_map = false; seat_focus_surface(&view->server->seat, view->surface); + } else { + desktop_focus_view(view, /*raise*/ true); } view_impl_map(view);