From 57075ce864ff39ae812c21fe78c8575ee571178d Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 3 Nov 2023 12:38:07 -0400 Subject: [PATCH] view: ensure that floating views don't overlap top panels The top_left_edge_boundary_check() function in xwayland.c ensures that views trying to position themselves at 0,0 don't end up with a titlebar offscreen. However, it doesn't take into account the usable area and thus these views can still end up overlapping a top panel. Also, there is no good reason for top_left_edge_boundary_check() to be xwayland-specific. This logic should really be part of view_adjust_for_layout_change(). To fix all this, add a new view_adjust_floating_geometry() function, which replaces the existing similar (and duplicated) logic in view_apply_natural_geometry() and view_adjust_for_layout_change(). view_adjust_for_layout_change() is already being called from xwayland's set_initial_position(), so top_left_edge_boundary_check() is now redundant and can just be deleted. Lightly tested with waybar and feh --geometry 640x480+0+0. The feh window is now correctly positioned below waybar, even if started before waybar (in that case, the feh window is moved when waybar starts). --- include/view.h | 1 + src/view.c | 64 +++++++++++++++++++++++++++++++++++++------------- src/xwayland.c | 17 -------------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/include/view.h b/include/view.h index da6299d1..c8640485 100644 --- a/include/view.h +++ b/include/view.h @@ -356,6 +356,7 @@ void view_moved(struct view *view); void view_minimize(struct view *view, bool minimized); bool view_compute_centered_position(struct view *view, const struct wlr_box *ref, int w, int h, int *x, int *y); +bool view_adjust_floating_geometry(struct view *view, struct wlr_box *geometry); void view_store_natural_geometry(struct view *view); /** diff --git a/src/view.c b/src/view.c index 73f699f0..b440fb6f 100644 --- a/src/view.c +++ b/src/view.c @@ -542,6 +542,7 @@ bool view_compute_centered_position(struct view *view, const struct wlr_box *ref, int w, int h, int *x, int *y) { + assert(view); if (w <= 0 || h <= 0) { wlr_log(WLR_ERROR, "view has empty geometry, not centering"); return false; @@ -577,6 +578,47 @@ view_compute_centered_position(struct view *view, const struct wlr_box *ref, return true; } +bool +view_adjust_floating_geometry(struct view *view, struct wlr_box *geometry) +{ + assert(view); + if (!output_is_usable(view->output)) { + wlr_log(WLR_ERROR, "view has no output, not positioning"); + return false; + } + + bool adjusted = false; + /* + * First check whether the view is onscreen. For now, "onscreen" + * is defined as even one pixel of the client area being visible. + */ + if (wlr_output_layout_intersects(view->server->output_layout, + NULL, geometry)) { + /* + * If onscreen, then make sure the titlebar is also + * visible (and not overlapping any panels/docks) + */ + struct border margin = ssd_get_margin(view->ssd); + struct wlr_box usable = + output_usable_area_in_layout_coords(view->output); + + if (geometry->x < usable.x + margin.left) { + geometry->x = usable.x + margin.left; + adjusted = true; + } + if (geometry->y < usable.y + margin.top) { + geometry->y = usable.y + margin.top; + adjusted = true; + } + } else { + /* If offscreen, then just center the view */ + adjusted = view_compute_centered_position(view, NULL, + geometry->width, geometry->height, + &geometry->x, &geometry->y); + } + return adjusted; +} + static void set_fallback_geometry(struct view *view) { @@ -630,19 +672,9 @@ view_apply_natural_geometry(struct view *view) assert(view); assert(view_is_floating(view)); - struct wlr_output_layout *layout = view->server->output_layout; - if (wlr_output_layout_intersects(layout, NULL, &view->natural_geometry) - || wl_list_empty(&layout->outputs)) { - /* restore to original geometry */ - view_move_resize(view, view->natural_geometry); - } else { - /* reposition if original geometry is offscreen */ - struct wlr_box box = view->natural_geometry; - if (view_compute_centered_position(view, NULL, box.width, - box.height, &box.x, &box.y)) { - view_move_resize(view, box); - } - } + struct wlr_box geometry = view->natural_geometry; + view_adjust_floating_geometry(view, &geometry); + view_move_resize(view, geometry); } static void @@ -1147,9 +1179,9 @@ view_adjust_for_layout_change(struct view *view) view_apply_natural_geometry(view); } else { /* reposition view if it's offscreen */ - if (!wlr_output_layout_intersects(view->server->output_layout, - NULL, &view->pending)) { - view_center(view, NULL); + struct wlr_box geometry = view->pending; + if (view_adjust_floating_geometry(view, &geometry)) { + view_move_resize(view, geometry); } } if (view->toplevel.handle) { diff --git a/src/xwayland.c b/src/xwayland.c index 6e4fae7f..eee7eb6c 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -480,19 +480,6 @@ set_initial_position(struct view *view, } } -static void -top_left_edge_boundary_check(struct view *view) -{ - struct wlr_box deco = ssd_max_extents(view); - if (deco.x < 0) { - view->current.x -= deco.x; - } - if (deco.y < 0) { - view->current.y -= deco.y; - } - view->impl->configure(view, view->current); -} - static void init_foreign_toplevel(struct view *view) { @@ -589,10 +576,6 @@ xwayland_view_map(struct view *view) view_moved(view); } - if (view->ssd_enabled && view_is_floating(view)) { - top_left_edge_boundary_check(view); - } - /* Add commit here, as xwayland map/unmap can change the wlr_surface */ wl_signal_add(&xwayland_surface->surface->events.commit, &view->commit); view->commit.notify = handle_commit;