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

commit0062ad9456f3e27bf98e40ec9ccd82fc3fc8139c
parentaf3ad5eaeb
authorIsaac Freund <[email protected]>
date2023-03-10 14:15
View: add `none` impl tag for better safety checks

 river/View.zig         | 16 ++++++++++++++++
 river/XdgToplevel.zig  |  4 +++-
 river/XwaylandView.zig |  4 +++-
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/river/View.zig b/river/View.zig
index 5589376..48ccadf 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -46,6 +46,12 @@ pub const Constraints = struct {
 const Impl = union(enum) {
     xdg_toplevel: XdgToplevel,
     xwayland_view: if (build_options.xwayland) XwaylandView else noreturn,
+    /// This state is assigned during destruction after the xdg toplevel
+    /// has been destroyed but while the transaction system is still rendering
+    /// saved surfaces of the view.
+    /// The xdg_toplevel could simply be set to undefined instead, but using a
+    /// tag like this gives us better safety checks.
+    none,
 };
 
 pub const State = struct {
@@ -163,6 +169,8 @@ post_fullscreen_box: wlr.Box = undefined,
 foreign_toplevel_handle: ForeignToplevelHandle = .{},
 
 pub fn create(impl: Impl) error{OutOfMemory}!*Self {
+    assert(impl != .none);
+
     const view = try util.gpa.create(Self);
     errdefer util.gpa.destroy(view);
 
@@ -210,6 +218,8 @@ pub fn create(impl: Impl) error{OutOfMemory}!*Self {
 /// mark this view for destruction when the transaction completes. Otherwise
 /// destroy immediately.
 pub fn destroy(view: *Self) void {
+    assert(view.impl == .none);
+
     view.destroying = true;
 
     // If there are still saved buffers, then this view needs to be kept
@@ -284,6 +294,7 @@ pub fn configure(self: *Self) bool {
             if (build_options.xwayland) return xwayland_view.configure();
             unreachable;
         },
+        .none => unreachable,
     }
 }
 
@@ -292,6 +303,7 @@ pub fn rootSurface(self: Self) *wlr.Surface {
     return switch (self.impl) {
         .xdg_toplevel => |xdg_toplevel| xdg_toplevel.rootSurface(),
         .xwayland_view => |xwayland_view| xwayland_view.rootSurface(),
+        .none => unreachable,
     };
 }
 
@@ -362,6 +374,7 @@ pub fn close(self: Self) void {
     switch (self.impl) {
         .xdg_toplevel => |xdg_toplevel| xdg_toplevel.close(),
         .xwayland_view => |xwayland_view| xwayland_view.close(),
+        .none => unreachable,
     }
 }
 
@@ -370,6 +383,7 @@ pub fn destroyPopups(self: Self) void {
     switch (self.impl) {
         .xdg_toplevel => |xdg_toplevel| xdg_toplevel.destroyPopups(),
         .xwayland_view => {},
+        .none => unreachable,
     }
 }
 
@@ -379,6 +393,7 @@ pub fn getTitle(self: Self) ?[*:0]const u8 {
     return switch (self.impl) {
         .xdg_toplevel => |xdg_toplevel| xdg_toplevel.getTitle(),
         .xwayland_view => |xwayland_view| xwayland_view.getTitle(),
+        .none => unreachable,
     };
 }
 
@@ -388,6 +403,7 @@ pub fn getAppId(self: Self) ?[*:0]const u8 {
     return switch (self.impl) {
         .xdg_toplevel => |xdg_toplevel| xdg_toplevel.getAppId(),
         .xwayland_view => |xwayland_view| xwayland_view.getAppId(),
+        .none => unreachable,
     };
 }
 
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 1a9b983..f0bd28e 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -178,7 +178,9 @@ fn handleDestroy(listener: *wl.Listener(void)) void {
     // The wlr_surface may outlive the wlr_xdg_surface so we must clean up the user data.
     self.xdg_toplevel.base.surface.data = 0;
 
-    self.view.destroy();
+    const view = self.view;
+    view.impl = .none;
+    view.destroy();
 }
 
 fn handleMap(listener: *wl.Listener(void)) void {
diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig
index f36e2af..274e8ed 100644
--- a/river/XwaylandView.zig
+++ b/river/XwaylandView.zig
@@ -153,7 +153,9 @@ fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandS
     self.request_configure.link.remove();
     self.set_override_redirect.link.remove();
 
-    self.view.destroy();
+    const view = self.view;
+    view.impl = .none;
+    view.destroy();
 }
 
 pub fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {