View: clip scene tree to output

There are some cases in which a view can end up with a size/position
that places some part of it outside its output. For example, a window
with a large minimum size in a tiled layout that is placed near the
right or bottom edge of the output may extend past the output bounds.

The problem with this is that part of the view outside the output bounds
may be rendered on another output where it does not belong in a multi-
monitor setup.

To fix this, clip the surfaces of the view and the borders to the output
bounds.
This commit is contained in:
Isaac Freund 2024-02-10 14:17:29 +01:00
parent 93b8bbc2fd
commit f0b0606e9f
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11

View File

@ -123,12 +123,8 @@ link: wl.list.Link,
tree: *wlr.SceneTree,
surface_tree: *wlr.SceneTree,
saved_surface_tree: *wlr.SceneTree,
borders: struct {
left: *wlr.SceneRect,
right: *wlr.SceneRect,
top: *wlr.SceneRect,
bottom: *wlr.SceneRect,
},
/// Order is left, right, top, bottom
borders: [4]*wlr.SceneRect,
popup_tree: *wlr.SceneTree,
/// Bounds on the width/height of the view, set by the xdg_toplevel/xwayland_view implementation.
@ -190,10 +186,10 @@ pub fn create(impl: Impl) error{OutOfMemory}!*Self {
.surface_tree = try tree.createSceneTree(),
.saved_surface_tree = try tree.createSceneTree(),
.borders = .{
.left = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
.right = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
.top = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
.bottom = try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
try tree.createSceneRect(0, 0, &server.config.border_color_unfocused),
},
.popup_tree = popup_tree,
@ -317,38 +313,86 @@ pub fn updateCurrent(view: *Self) void {
view.current = view.inflight;
view.dropSavedSurfaceTree();
const color = blk: {
if (view.current.urgent) break :blk &config.border_color_urgent;
if (view.current.focus != 0) break :blk &config.border_color_focused;
break :blk &config.border_color_unfocused;
};
const box = &view.current.box;
view.tree.node.setPosition(box.x, box.y);
view.popup_tree.node.setPosition(box.x, box.y);
const enable_borders = view.current.ssd and !view.current.fullscreen;
var output_box: wlr.Box = .{ .x = 0, .y = 0, .width = 0, .height = 0 };
if (view.current.output) |output| {
output.wlr_output.effectiveResolution(&output_box.width, &output_box.height);
}
const border_width: c_int = config.border_width;
view.borders.left.node.setEnabled(enable_borders);
view.borders.left.node.setPosition(-border_width, -border_width);
view.borders.left.setSize(border_width, box.height + 2 * border_width);
view.borders.left.setColor(color);
{
var surface_clip: wlr.Box = undefined;
_ = surface_clip.intersection(box, &output_box);
view.borders.right.node.setEnabled(enable_borders);
view.borders.right.node.setPosition(box.width, -border_width);
view.borders.right.setSize(border_width, box.height + 2 * border_width);
view.borders.right.setColor(color);
// The clip is applied relative to the root node of the subsurface tree.
surface_clip.x -= box.x;
surface_clip.y -= box.y;
view.borders.top.node.setEnabled(enable_borders);
view.borders.top.node.setPosition(0, -border_width);
view.borders.top.setSize(box.width, border_width);
view.borders.top.setColor(color);
switch (view.impl) {
.xdg_toplevel => |xdg_toplevel| {
surface_clip.x += xdg_toplevel.geometry.x;
surface_clip.y += xdg_toplevel.geometry.y;
},
.xwayland_view, .none => {},
}
view.borders.bottom.node.setEnabled(enable_borders);
view.borders.bottom.node.setPosition(0, box.height);
view.borders.bottom.setSize(box.width, border_width);
view.borders.bottom.setColor(color);
if (!view.surface_tree.children.empty()) {
view.surface_tree.node.subsurfaceTreeSetClip(&surface_clip);
}
}
{
const border_width: c_int = config.border_width;
const border_color = blk: {
if (view.current.urgent) break :blk &config.border_color_urgent;
if (view.current.focus != 0) break :blk &config.border_color_focused;
break :blk &config.border_color_unfocused;
};
// Order is left, right, top, bottom
// left and right borders include the corners, top and bottom do not.
var border_boxes = [4]wlr.Box{
.{
.x = -border_width,
.y = -border_width,
.width = border_width,
.height = box.height + 2 * border_width,
},
.{
.x = box.width,
.y = -border_width,
.width = border_width,
.height = box.height + 2 * border_width,
},
.{
.x = 0,
.y = -border_width,
.width = box.width,
.height = border_width,
},
.{
.x = 0,
.y = box.height,
.width = box.width,
.height = border_width,
},
};
for (&view.borders, &border_boxes) |border, *border_box| {
border_box.x += box.x;
border_box.y += box.y;
_ = border_box.intersection(border_box, &output_box);
border_box.x -= box.x;
border_box.y -= box.y;
border.node.setEnabled(view.current.ssd and !view.current.fullscreen);
border.node.setPosition(border_box.x, border_box.y);
border.setSize(border_box.width, border_box.height);
border.setColor(border_color);
}
}
}
/// Returns true if the configure should be waited for by the transaction system.