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/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 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