From 382068e452a4bf354eb6171d243070cab825452e Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:07:13 +0100 Subject: [PATCH 1/2] src/view.c: store outputs the view is visible on --- include/view.h | 13 ++++++++++++- src/foreign.c | 5 ++--- src/view.c | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/include/view.h b/include/view.h index 3d300dd6..fdedbe14 100644 --- a/include/view.h +++ b/include/view.h @@ -124,7 +124,7 @@ struct view { struct wl_list link; /* - * The output that the view is displayed on. Specifically: + * The primary output that the view is displayed on. Specifically: * * - For floating views, this is the output nearest to the * center of the view. It is computed automatically when the @@ -139,6 +139,16 @@ struct view { * by calling view_set_output() beforehand. */ struct output *output; + + /* + * The outputs that the view is displayed on. + * This is used to notify the foreign toplevel + * implementation and to update the SSD invisible + * resize area. + * It is a bitset of output->scene_output->index. + */ + uint64_t outputs; + struct workspace *workspace; struct wlr_surface *surface; struct wlr_scene_tree *scene_tree; @@ -460,6 +470,7 @@ void view_move_to_front(struct view *view); void view_move_to_back(struct view *view); struct view *view_get_root(struct view *view); void view_append_children(struct view *view, struct wl_array *children); +bool view_on_output(struct view *view, struct output *output); /** * view_is_related() - determine if view and surface are owned by the diff --git a/src/foreign.c b/src/foreign.c index 092752b3..f76144a7 100644 --- a/src/foreign.c +++ b/src/foreign.c @@ -107,11 +107,10 @@ void foreign_toplevel_update_outputs(struct view *view) { assert(view->toplevel.handle); - struct wlr_output_layout *layout = view->server->output_layout; + struct output *output; wl_list_for_each(output, &view->server->outputs, link) { - if (output_is_usable(output) && wlr_output_layout_intersects( - layout, output->wlr_output, &view->current)) { + if (view_on_output(view, output)) { wlr_foreign_toplevel_handle_v1_output_enter( view->toplevel.handle, output->wlr_output); } else { diff --git a/src/view.c b/src/view.c index 9773e070..066d4fa3 100644 --- a/src/view.c +++ b/src/view.c @@ -333,6 +333,34 @@ view_close(struct view *view) } } +static void +view_update_outputs(struct view *view) +{ + struct output *output; + struct wlr_output_layout *layout = view->server->output_layout; + + view->outputs = 0; + wl_list_for_each(output, &view->server->outputs, link) { + if (output_is_usable(output) && wlr_output_layout_intersects( + layout, output->wlr_output, &view->current)) { + view->outputs |= (1ull << output->scene_output->index); + } + } + + if (view->toplevel.handle) { + foreign_toplevel_update_outputs(view); + } +} + +bool +view_on_output(struct view *view, struct output *output) +{ + assert(view); + assert(output); + return output->scene_output + && (view->outputs & (1ull << output->scene_output->index)); +} + void view_move(struct view *view, int x, int y) { @@ -358,11 +386,9 @@ view_moved(struct view *view) if (view_is_floating(view)) { view_discover_output(view, NULL); } + view_update_outputs(view); ssd_update_geometry(view->ssd); cursor_update_focus(view->server); - if (view->toplevel.handle) { - foreign_toplevel_update_outputs(view); - } if (rc.resize_indicator && view->server->grabbed_view == view) { resize_indicator_update(view); } @@ -1467,9 +1493,7 @@ view_adjust_for_layout_change(struct view *view) } } - if (view->toplevel.handle) { - foreign_toplevel_update_outputs(view); - } + view_update_outputs(view); } void From b013cbba3a787906b2b178479ef7fb2d2470652c Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:09:15 +0100 Subject: [PATCH 2/2] src/ssd: allow invisible resize area across outputs This uses the new `view->outputs` bitset to calculate the intersection with *all* outputs the view is currently visible on. This ensures that the invisible resize handle works across outputs while still making sure that it won't leak into neighboring ones just because it is positioned closely to an output edge (either manually, maximized or snapped via SnapToEdge or SnapToRegion). Co-Authored-By: @johanmalm Fixes: #1486 Reported-By: @lurch --- src/ssd/ssd_extents.c | 64 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/src/ssd/ssd_extents.c b/src/ssd/ssd_extents.c index 9a926a2b..4bb5b69d 100644 --- a/src/ssd/ssd_extents.c +++ b/src/ssd/ssd_extents.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include "common/mem.h" #include "common/scene-helpers.h" #include "labwc.h" @@ -100,11 +101,6 @@ ssd_extents_update(struct ssd *ssd) if (!view->output) { return; } - struct wlr_output_layout_output *l_output = wlr_output_layout_get( - view->server->output_layout, view->output->wlr_output); - if (!l_output) { - return; - } struct theme *theme = view->server->theme; @@ -127,10 +123,25 @@ ssd_extents_update(struct ssd *ssd) -(theme->border_width + extended_area), -(ssd->titlebar.height + theme->border_width + extended_area)); - /* Convert usable area into layout coordinates */ - struct wlr_box usable_area = view->output->usable_area; - usable_area.x += l_output->x; - usable_area.y += l_output->y; + /* + * Convert all output usable areas that the + * view is currently on into a pixman region + */ + int nrects; + pixman_region32_t usable; + pixman_region32_t intersection; + pixman_region32_init(&usable); + pixman_region32_init(&intersection); + struct output *output; + wl_list_for_each(output, &view->server->outputs, link) { + if (!view_on_output(view, output)) { + continue; + } + struct wlr_box usable_area = + output_usable_area_in_layout_coords(output); + pixman_region32_union_rect(&usable, &usable, usable_area.x, + usable_area.y, usable_area.width, usable_area.height); + } /* Remember base layout coordinates */ int base_x, base_y; @@ -176,11 +187,40 @@ ssd_extents_update(struct ssd *ssd) part_box.height = target->height; /* Constrain part to output->usable_area */ - if (!wlr_box_intersection(&result_box, &part_box, &usable_area)) { + pixman_region32_clear(&intersection); + pixman_region32_intersect_rect(&intersection, &usable, + part_box.x, part_box.y, part_box.width, part_box.height); + const pixman_box32_t *inter_rects = + pixman_region32_rectangles(&intersection, &nrects); + + if (nrects == 0) { /* Not visible */ wlr_scene_node_set_enabled(part->node, false); continue; - } else if (!part->node->enabled) { + } + + /* + * For each edge, the invisible grab area is resized + * to not cover layer-shell clients such as panels. + * However, only one resize operation is used per edge, + * so if a window is in the unlikely position that it + * is near a panel but also overspills onto another screen, + * the invisible grab-area on the other screen would be + * smaller than would normally be the case. + * + * Thus only use the first intersecting rect, this is + * a compromise as it doesn't require us to create + * multiple scene rects for a given extent edge + * and still works in 95% of the cases. + */ + result_box = (struct wlr_box) { + .x = inter_rects[0].x1, + .y = inter_rects[0].y1, + .width = inter_rects[0].x2 - inter_rects[0].x1, + .height = inter_rects[0].y2 - inter_rects[0].y1 + }; + + if (!part->node->enabled) { wlr_scene_node_set_enabled(part->node, true); } @@ -204,6 +244,8 @@ ssd_extents_update(struct ssd *ssd) wlr_scene_rect_set_size(rect, target->width, target->height); } } + pixman_region32_fini(&intersection); + pixman_region32_fini(&usable); } void