git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit94492e0e9adf61eb559139622baf2dd72e955e5e
parent6d940b7710
authorIsaac Freund <[email protected]>
date2025-01-04 16:59
Window: implement river_window_v1.set_borders

 river/Config.zig       |  6 ----
 river/Window.zig       | 78 ++++++++++++++++++++++++++++++++++++++++++++------
 rivercompat/Window.zig |  9 ++++++
 3 files changed, 79 insertions(+), 14 deletions(-)

diff --git a/river/Config.zig b/river/Config.zig
index 438cc9d..dcde0f1 100644
--- a/river/Config.zig
+++ b/river/Config.zig
@@ -31,12 +31,6 @@ const Switch = @import("Switch.zig");
 /// Color of background in RGBA with premultiplied alpha (alpha should only affect nested sessions)
 background_color: [4]f32 = [_]f32{ 0.0, 0.16862745, 0.21176471, 1.0 }, // Solarized base03
 
-/// Width of borders in pixels
-border_width: u31 = 2,
-
-/// Color of border in RGBA with premultiplied alpha
-border_color: [4]f32 = [_]f32{ 0.34509804, 0.43137255, 0.45882353, 1.0 }, // Solarized base01
-
 switch_mappings: std.ArrayListUnmanaged(struct {
     switch_type: Switch.Type,
     switch_state: Switch.State,
diff --git a/river/Window.zig b/river/Window.zig
index 7a10c77..a62cc95 100644
--- a/river/Window.zig
+++ b/river/Window.zig
@@ -139,8 +139,12 @@ impl: Impl,
 tree: *wlr.SceneTree,
 surface_tree: *wlr.SceneTree,
 saved_surface_tree: *wlr.SceneTree,
-/// Order is left, right, top, bottom
-borders: [4]*wlr.SceneRect,
+border: struct {
+    left: *wlr.SceneRect,
+    right: *wlr.SceneRect,
+    top: *wlr.SceneRect,
+    bottom: *wlr.SceneRect,
+},
 popup_tree: *wlr.SceneTree,
 
 /// Set to true once the window manager client has made its first commit
@@ -216,11 +220,11 @@ pub fn create(impl: Impl) error{OutOfMemory}!*Window {
         .tree = tree,
         .surface_tree = try tree.createSceneTree(),
         .saved_surface_tree = try tree.createSceneTree(),
-        .borders = .{
-            try tree.createSceneRect(0, 0, &server.config.border_color),
-            try tree.createSceneRect(0, 0, &server.config.border_color),
-            try tree.createSceneRect(0, 0, &server.config.border_color),
-            try tree.createSceneRect(0, 0, &server.config.border_color),
+        .border = .{
+            .left = try tree.createSceneRect(0, 0, &.{ 0, 0, 0, 0 }),
+            .right = try tree.createSceneRect(0, 0, &.{ 0, 0, 0, 0 }),
+            .top = try tree.createSceneRect(0, 0, &.{ 0, 0, 0, 0 }),
+            .bottom = try tree.createSceneRect(0, 0, &.{ 0, 0, 0, 0 }),
         },
         .popup_tree = popup_tree,
         .link = undefined,
@@ -636,7 +640,65 @@ pub fn updateSceneState(window: *Window) void {
     window.tree.node.setPosition(box.x, box.y);
     window.popup_tree.node.setPosition(box.x, box.y);
 
-    // TODO borders
+    // 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.sent.border;
+    const color: [4]f32 = .{
+        @floatCast(@as(f64, @floatFromInt(border.r)) / math.maxInt(u32)),
+        @floatCast(@as(f64, @floatFromInt(border.g)) / math.maxInt(u32)),
+        @floatCast(@as(f64, @floatFromInt(border.b)) / math.maxInt(u32)),
+        @floatCast(@as(f64, @floatFromInt(border.a)) / math.maxInt(u32)),
+    };
+
+    var left: wlr.Box = .{
+        .x = -@as(i32, border.width),
+        .y = 0,
+        .width = border.width,
+        .height = box.height,
+    };
+    var right: wlr.Box = .{
+        .x = box.width,
+        .y = 0,
+        .width = border.width,
+        .height = box.height,
+    };
+    const top: wlr.Box = .{
+        .x = 0,
+        .y = -@as(i32, border.width),
+        .width = box.width,
+        .height = border.width,
+    };
+    const bottom: wlr.Box = .{
+        .x = 0,
+        .y = box.height,
+        .width = box.width,
+        .height = border.width,
+    };
+
+    // Use left and right scene rects to draw the corners if needed
+    if (border.edges.top) {
+        left.y -= border.width;
+        left.height += border.width;
+        right.y -= border.width;
+        right.height += border.width;
+    }
+    if (border.edges.bottom) {
+        left.height += border.width;
+        right.height += border.width;
+    }
+
+    inline for (.{
+        .{ .name = "left", .box = left },
+        .{ .name = "right", .box = right },
+        .{ .name = "top", .box = top },
+        .{ .name = "bottom", .box = bottom },
+    }) |edge| {
+        const rect = @field(window.border, edge.name);
+        rect.node.setEnabled(@field(border.edges, edge.name));
+        rect.node.setPosition(edge.box.x, edge.box.y);
+        rect.setSize(edge.box.width, edge.box.height);
+        rect.setColor(&color);
+    }
 }
 
 /// Returns null if the window is currently being destroyed and no longer has
diff --git a/rivercompat/Window.zig b/rivercompat/Window.zig
index 75c8ff7..cc87ef4 100644
--- a/rivercompat/Window.zig
+++ b/rivercompat/Window.zig
@@ -42,6 +42,15 @@ pub fn create(window_v1: *river.WindowV1, wm: *WindowManager) void {
     wm.windows.append(window);
     window_v1.setListener(*Window, handleEvent, window);
     window.node_v1.placeTop();
+    window_v1.useSsd();
+    window_v1.setBorders(
+        .{ .left = true, .bottom = true, .top = false, .right = true },
+        6, // width
+        std.math.maxInt(u32),
+        std.math.maxInt(u32),
+        std.math.maxInt(u32),
+        std.math.maxInt(u32),
+    );
 }
 
 fn handleEvent(window_v1: *river.WindowV1, event: river.WindowV1.Event, window: *Window) void {