Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
protocol: add river_window_v1.set_clip_box
protocol/river-window-management-v1.xml | 21 +++++++++++++
river/Window.zig | 55 ++++++++++++++++++++++++++++-----
river/XdgToplevel.zig | 2 +-
3 files changed, 70 insertions(+), 8 deletions(-)
diff --git a/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml
index cda16a5..ea621aa 100644
--- a/protocol/river-window-management-v1.xml
+++ b/protocol/river-window-management-v1.xml
@@ -319,6 +319,8 @@
summary="proposed dimensions out of bounds"/>
<entry name="invalid_border" value="2"
summary="invalid arg to set_borders"/>
+ <entry name="invalid_clip_box" value="3"
+ summary="invalid arg to set_clip_box"/>
</enum>
<request name="destroy" type="destructor">
@@ -945,6 +947,25 @@
state has been sent by the server.
</description>
</event>
+
+ <request name="set_clip_box" since="2">
+ <description summary="clip the window to a given box">
+ Clip the window, including borders and decoration surfaces, to the box
+ specified by the x, y, width, and height arguments. The x/y position of
+ the box is relative to the top left corner of the window.
+
+ The width and height arguments must be greater than or equal to 0.
+
+ Setting a clip box with 0 width or height disables clipping.
+
+ This request modifies rendering state and may only be made as part of a
+ render sequence, see the river_window_manager_v1 description.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
</interface>
<interface name="river_decoration_v1" version="2">
diff --git a/river/Window.zig b/river/Window.zig
index b116136..190fce2 100644
--- a/river/Window.zig
+++ b/river/Window.zig
@@ -223,6 +223,7 @@ rendering_requested: struct {
y: i32 = 0,
hidden: bool = false,
border: Border = .{},
+ clip: wlr.Box = .{ .x = 0, .y = 0, .width = 0, .height = 0 },
} = .{},
/// The currently rendered position/dimensions of the window in the scene graph
@@ -621,6 +622,18 @@ fn handleRequest(
if (!server.wm.ensureWindowing()) return;
wm_requested.fullscreen = null;
},
+ .set_clip_box => |args| {
+ if (!server.wm.ensureRendering()) return;
+ if (args.width < 0 or args.height < 0) {
+ window_v1.postError(.invalid_clip_box, "width/height must be greater than or equal to 0 ");
+ }
+ rendering_requested.clip = .{
+ .x = args.x,
+ .y = args.y,
+ .width = args.width,
+ .height = args.height,
+ };
+ },
}
}
@@ -788,6 +801,21 @@ pub fn renderFinish(window: *Window) void {
window.tree.node.setPosition(window.box.x, window.box.y);
window.popup_tree.node.setPosition(window.box.x, window.box.y);
+ {
+ var surface_clip = window.rendering_requested.clip;
+ switch (window.impl) {
+ .toplevel => |toplevel| {
+ surface_clip.x += toplevel.geometry.x;
+ surface_clip.y += toplevel.geometry.y;
+ },
+ .xwayland, .destroying => {},
+ }
+ // wlroots asserts that a subsurface tree is present.
+ if (!window.surfaces.tree.children.empty()) {
+ window.surfaces.tree.node.subsurfaceTreeSetClip(&surface_clip);
+ }
+ }
+
// f32 cannot represent all u32 values exactly, therefore we must initially use f64
// (which can) and then cast to f32, potentially losing precision.
const border = &window.rendering_requested.border;
@@ -810,13 +838,13 @@ pub fn renderFinish(window: *Window) void {
.width = border.width,
.height = window.box.height,
};
- const top: wlr.Box = .{
+ var top: wlr.Box = .{
.x = 0,
.y = -@as(i32, border.width),
.width = window.box.width,
.height = border.width,
};
- const bottom: wlr.Box = .{
+ var bottom: wlr.Box = .{
.x = 0,
.y = window.box.height,
.width = window.box.width,
@@ -836,11 +864,18 @@ pub fn renderFinish(window: *Window) void {
}
inline for (.{
- .{ .name = "left", .box = left },
- .{ .name = "right", .box = right },
- .{ .name = "top", .box = top },
- .{ .name = "bottom", .box = bottom },
+ .{ .name = "left", .box = &left },
+ .{ .name = "right", .box = &right },
+ .{ .name = "top", .box = &top },
+ .{ .name = "bottom", .box = &bottom },
}) |edge| {
+ if (!window.rendering_requested.clip.empty()) {
+ if (!edge.box.intersection(edge.box, &window.rendering_requested.clip)) {
+ // TODO(wlroots): remove this redundant code after fixed upstream
+ // https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5084
+ edge.box.* = .{ .x = 0, .y = 0, .width = 0, .height = 0 };
+ }
+ }
const rect = @field(window.border, edge.name);
rect.node.setEnabled(@field(border.edges, edge.name));
rect.node.setPosition(edge.box.x, edge.box.y);
@@ -850,7 +885,13 @@ pub fn renderFinish(window: *Window) void {
inline for (.{ &window.decorations_above, &window.decorations_below }) |decorations| {
var it = decorations.iterator(.forward);
- while (it.next()) |decoration| decoration.renderFinish();
+ while (it.next()) |decoration| {
+ decoration.renderFinish();
+ // wlroots asserts that a subsurface tree is present.
+ if (!decoration.surfaces.tree.children.empty()) {
+ decoration.surfaces.tree.node.subsurfaceTreeSetClip(&window.rendering_requested.clip);
+ }
+ }
}
}
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index d1bb9b7..04073db 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -356,7 +356,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
old_geometry.y != toplevel.geometry.y)
{
// We need to update the surface clip box to reflect the geometry change.
- // TODO actually update the clip box when clipping is supported
+ window.renderFinish();
}
},
// If the client has not yet acked our configure, we need to send a