mirror of
https://github.com/labwc/labwc.git
synced 2026-04-10 08:21:07 -04:00
desktop: enforce followMouse focus policy more strictly
With followMouse=yes in the config, a client activation request was still allowed to focus a view that wasn't under the mouse cursor, but it would then be immediately unfocused again. With some clients using modal dialogs (e.g. Scribus), this could lead to an infinite cycle of the client and labwc continually switching the focus back and forth. To break the cycle, only allow focusing a view under the cursor when when followMouse=yes. v2: allow focusing a new/raised view appearing under the cursor v3: allow focusing a sub-view/sibling of the requested view v4: fix new xdg-shell views under mouse not being focused Fixes: #2722
This commit is contained in:
parent
5a50a02ba3
commit
34892c35be
6 changed files with 66 additions and 30 deletions
|
|
@ -476,10 +476,12 @@ void xdg_shell_finish(struct server *server);
|
||||||
void desktop_focus_view(struct view *view, bool raise);
|
void desktop_focus_view(struct view *view, bool raise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* desktop_focus_view_or_surface() - like desktop_focus_view() but can
|
* desktop_focus_for_cursor_update() - like desktop_focus_view() but can
|
||||||
* also focus other (e.g. xwayland-unmanaged) surfaces
|
* 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);
|
struct wlr_surface *surface, bool raise);
|
||||||
|
|
||||||
void desktop_arrange_all_views(struct server *server);
|
void desktop_arrange_all_views(struct server *server);
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,27 @@ desktop_arrange_all_views(struct server *server)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
desktop_focus_view(struct view *view, bool raise)
|
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);
|
assert(view);
|
||||||
/*
|
/*
|
||||||
|
|
@ -77,34 +96,40 @@ desktop_focus_view(struct view *view, bool raise)
|
||||||
workspaces_switch_to(view->workspace, /*update_focus*/ false);
|
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) {
|
if (raise) {
|
||||||
view_move_to_front(view);
|
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? */
|
/* TODO: focus layer-shell surfaces also? */
|
||||||
void
|
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)
|
struct wlr_surface *surface, bool raise)
|
||||||
{
|
{
|
||||||
assert(view || surface);
|
assert(view || surface);
|
||||||
if (view) {
|
if (view) {
|
||||||
desktop_focus_view(view, raise);
|
_desktop_focus_view(view, raise, /* for_cursor_update */ true);
|
||||||
#if HAVE_XWAYLAND
|
#if HAVE_XWAYLAND
|
||||||
} else {
|
} else {
|
||||||
struct wlr_xwayland_surface *xsurface =
|
struct wlr_xwayland_surface *xsurface =
|
||||||
|
|
|
||||||
|
|
@ -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
|
* If followMouse=yes, update the keyboard focus when the
|
||||||
* cursor enters a surface
|
* cursor enters a surface
|
||||||
*/
|
*/
|
||||||
desktop_focus_view_or_surface(seat,
|
desktop_focus_for_cursor_update(seat,
|
||||||
view_from_wlr_surface(new_focused_surface),
|
view_from_wlr_surface(new_focused_surface),
|
||||||
new_focused_surface, rc.raise_on_focus);
|
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
|
* Always focus the surface below the cursor when
|
||||||
* followMouse=yes and followMouseRequiresMovement=no.
|
* 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);
|
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
|
#ifdef HAVE_XWAYLAND
|
||||||
} else if (ctx.type == LAB_SSD_UNMANAGED) {
|
} 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);
|
/*raise*/ false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
void
|
void
|
||||||
view_impl_map(struct view *view)
|
view_impl_map(struct view *view)
|
||||||
{
|
{
|
||||||
desktop_focus_view(view, /*raise*/ true);
|
|
||||||
view_update_title(view);
|
view_update_title(view);
|
||||||
view_update_app_id(view);
|
view_update_app_id(view);
|
||||||
if (!view->been_mapped) {
|
if (!view->been_mapped) {
|
||||||
|
|
|
||||||
10
src/xdg.c
10
src/xdg.c
|
|
@ -217,8 +217,18 @@ handle_commit(struct wl_listener *listener, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update_required) {
|
if (update_required) {
|
||||||
|
bool was_empty = wlr_box_empty(current);
|
||||||
view_impl_apply_geometry(view, size.width, size.height);
|
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
|
* Some views (e.g., terminals that scale as multiples of rows
|
||||||
* and columns, or windows that impose a fixed aspect ratio),
|
* and columns, or windows that impose a fixed aspect ratio),
|
||||||
|
|
|
||||||
|
|
@ -826,14 +826,14 @@ xwayland_view_map(struct view *view)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the view was focused (on the xwayland server side) before
|
* If the view was focused (on the xwayland server side) before
|
||||||
* being mapped, update the seat focus now. Note that this only
|
* being mapped, just update the seat focus. Otherwise, try to
|
||||||
* really matters in the case of Globally Active input windows.
|
* focus the view now.
|
||||||
* In all other cases, it's redundant since view_impl_map()
|
|
||||||
* results in the view being focused anyway.
|
|
||||||
*/
|
*/
|
||||||
if (xwayland_view->focused_before_map) {
|
if (xwayland_view->focused_before_map) {
|
||||||
xwayland_view->focused_before_map = false;
|
xwayland_view->focused_before_map = false;
|
||||||
seat_focus_surface(&view->server->seat, view->surface);
|
seat_focus_surface(&view->server->seat, view->surface);
|
||||||
|
} else {
|
||||||
|
desktop_focus_view(view, /*raise*/ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
view_impl_map(view);
|
view_impl_map(view);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue