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

commit4f5014f3b6e46106551272d82e0a1d488c103b9d
parentee1aac5edc
authorIsaac Freund <[email protected]>
date2024-12-27 11:13
river: handle rwm object destruction properly

If the window manager crashes river must survive.

 river/Output.zig        | 16 ++++++++++++----
 river/Seat.zig          | 11 +++++++++--
 river/Window.zig        | 12 ++++++++++--
 river/WindowManager.zig | 15 ++++++++++-----
 4 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/river/Output.zig b/river/Output.zig
index 9b846ab..0828ee0 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -251,7 +251,7 @@ pub fn sendDirty(output: *Output) void {
                     };
                     output.object = output_v1;
 
-                    output_v1.setHandler(*Output, handleRequest, null, output);
+                    output_v1.setHandler(*Output, handleRequest, handleObjectDestroy, output);
                     wm_v1.sendOutput(output_v1);
 
                     break :blk output_v1;
@@ -301,13 +301,21 @@ fn handleRequestInert(
     if (request == .destroy) output_v1.destroy();
 }
 
+fn handleObjectDestroy(_: *river.OutputV1, output: *Output) void {
+    output.object = null;
+}
+
 fn handleRequest(
-    _: *river.OutputV1,
+    output_v1: *river.OutputV1,
     request: river.OutputV1.Request,
-    _: ?*Output,
+    output: *Output,
 ) void {
+    assert(output.object == output_v1);
     switch (request) {
-        .destroy => {}, // XXX send protocol error
+        .destroy => {
+            // XXX send protocol error
+            output_v1.destroy();
+        },
     }
 }
 
diff --git a/river/Seat.zig b/river/Seat.zig
index f665e42..8f9e3c0 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -299,7 +299,7 @@ pub fn sendDirty(seat: *Seat) void {
             };
             seat.object = seat_v1;
 
-            seat_v1.setHandler(*Seat, handleRequest, null, seat);
+            seat_v1.setHandler(*Seat, handleRequest, handleDestroy, seat);
             wm_v1.sendSeat(seat_v1);
 
             seat.link_sent.remove();
@@ -348,6 +348,10 @@ fn handleRequestInert(
     if (request == .destroy) seat_v1.destroy();
 }
 
+fn handleDestroy(_: *river.SeatV1, seat: *Seat) void {
+    seat.object = null;
+}
+
 fn handleRequest(
     seat_v1: *river.SeatV1,
     request: river.SeatV1.Request,
@@ -355,7 +359,10 @@ fn handleRequest(
 ) void {
     assert(seat.object == seat_v1);
     switch (request) {
-        .destroy => {}, // XXX send protocol error
+        .destroy => {
+            // XXX send protocol error
+            seat_v1.destroy();
+        },
         .focus_window => |args| {
             const data = args.window.getUserData() orelse return;
             const window: *Window = @ptrCast(@alignCast(data));
diff --git a/river/Window.zig b/river/Window.zig
index b0a14d6..35ddbbe 100644
--- a/river/Window.zig
+++ b/river/Window.zig
@@ -332,7 +332,7 @@ pub fn sendDirty(window: *Window) void {
                     return; // try again next update
                 };
                 window.object = window_v1;
-                window_v1.setHandler(*Window, handleRequest, null, window);
+                window_v1.setHandler(*Window, handleRequest, handleDestroy, window);
                 wm_v1.sendWindow(window_v1);
 
                 server.wm.uncommitted.render_list.append(&window.node);
@@ -377,6 +377,11 @@ fn handleRequestInert(
     if (request == .destroy) window_v1.destroy();
 }
 
+fn handleDestroy(_: *river.WindowV1, window: *Window) void {
+    window.object = null;
+    window.node.makeInert();
+}
+
 fn handleRequest(
     window_v1: *river.WindowV1,
     request: river.WindowV1.Request,
@@ -385,7 +390,10 @@ fn handleRequest(
     assert(window.object == window_v1);
     const uncommitted = &window.uncommitted;
     switch (request) {
-        .destroy => {}, // XXX send protocol error
+        .destroy => {
+            // XXX send protocol error
+            window_v1.destroy();
+        },
         .close => uncommitted.closing = true,
         .get_node => |args| {
             if (window.node.object != null) {
diff --git a/river/WindowManager.zig b/river/WindowManager.zig
index ce0978d..6fecfdf 100644
--- a/river/WindowManager.zig
+++ b/river/WindowManager.zig
@@ -159,7 +159,7 @@ fn bind(client: *wl.Client, wm: *WindowManager, version: u32, id: u32) void {
     }
 
     wm.object = object;
-    object.setHandler(*WindowManager, handleRequest, null, wm);
+    object.setHandler(*WindowManager, handleRequest, handleDestroy, wm);
 }
 
 fn handleRequestInert(
@@ -170,20 +170,25 @@ fn handleRequestInert(
     if (request == .destroy) object.destroy();
 }
 
+fn handleDestroy(_: *river.WindowManagerV1, wm: *WindowManager) void {
+    wm.object = null;
+}
+
 fn handleRequest(
-    object: *river.WindowManagerV1,
+    wm_v1: *river.WindowManagerV1,
     request: river.WindowManagerV1.Request,
     wm: *WindowManager,
 ) void {
-    assert(wm.object == object);
+    assert(wm.object == wm_v1);
     switch (request) {
         .stop => {
             wm.object = null;
-            object.sendFinished();
-            object.setHandler(?*anyopaque, handleRequestInert, null, null);
+            wm_v1.sendFinished();
+            wm_v1.setHandler(?*anyopaque, handleRequestInert, null, null);
         },
         .destroy => {
             // XXX send protocol error
+            wm_v1.destroy();
         },
         .ack_update => |args| {
             switch (wm.state) {