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).
This commit is contained in:
John Lindgren 2023-11-03 12:38:07 -04:00 committed by Johan Malm
parent 984aeb0b0b
commit 57075ce864
3 changed files with 49 additions and 33 deletions

View file

@ -356,6 +356,7 @@ void view_moved(struct view *view);
void view_minimize(struct view *view, bool minimized); void view_minimize(struct view *view, bool minimized);
bool view_compute_centered_position(struct view *view, bool view_compute_centered_position(struct view *view,
const struct wlr_box *ref, int w, int h, int *x, int *y); 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); void view_store_natural_geometry(struct view *view);
/** /**

View file

@ -542,6 +542,7 @@ bool
view_compute_centered_position(struct view *view, const struct wlr_box *ref, view_compute_centered_position(struct view *view, const struct wlr_box *ref,
int w, int h, int *x, int *y) int w, int h, int *x, int *y)
{ {
assert(view);
if (w <= 0 || h <= 0) { if (w <= 0 || h <= 0) {
wlr_log(WLR_ERROR, "view has empty geometry, not centering"); wlr_log(WLR_ERROR, "view has empty geometry, not centering");
return false; return false;
@ -577,6 +578,47 @@ view_compute_centered_position(struct view *view, const struct wlr_box *ref,
return true; 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 static void
set_fallback_geometry(struct view *view) set_fallback_geometry(struct view *view)
{ {
@ -630,19 +672,9 @@ view_apply_natural_geometry(struct view *view)
assert(view); assert(view);
assert(view_is_floating(view)); assert(view_is_floating(view));
struct wlr_output_layout *layout = view->server->output_layout; struct wlr_box geometry = view->natural_geometry;
if (wlr_output_layout_intersects(layout, NULL, &view->natural_geometry) view_adjust_floating_geometry(view, &geometry);
|| wl_list_empty(&layout->outputs)) { view_move_resize(view, geometry);
/* 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);
}
}
} }
static void static void
@ -1147,9 +1179,9 @@ view_adjust_for_layout_change(struct view *view)
view_apply_natural_geometry(view); view_apply_natural_geometry(view);
} else { } else {
/* reposition view if it's offscreen */ /* reposition view if it's offscreen */
if (!wlr_output_layout_intersects(view->server->output_layout, struct wlr_box geometry = view->pending;
NULL, &view->pending)) { if (view_adjust_floating_geometry(view, &geometry)) {
view_center(view, NULL); view_move_resize(view, geometry);
} }
} }
if (view->toplevel.handle) { if (view->toplevel.handle) {

View file

@ -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 static void
init_foreign_toplevel(struct view *view) init_foreign_toplevel(struct view *view)
{ {
@ -589,10 +576,6 @@ xwayland_view_map(struct view *view)
view_moved(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 */ /* Add commit here, as xwayland map/unmap can change the wlr_surface */
wl_signal_add(&xwayland_surface->surface->events.commit, &view->commit); wl_signal_add(&xwayland_surface->surface->events.commit, &view->commit);
view->commit.notify = handle_commit; view->commit.notify = handle_commit;