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

commit225fa0c6fb7dfc0c6503fc1aed36c09a8a6fdcd7
parent155043e253
authorIsaac Freund <[email protected]>
date2022-02-05 00:08
View: use last set fullscreen state in applyPending()

This avoids a race where the fullscreen set is e.g. set then unset
before the transaction has been completed and the current state has
been updated.

 river/View.zig         | 15 +++++++++++----
 river/VoidView.zig     |  4 ++++
 river/XdgToplevel.zig  |  4 ++++
 river/XwaylandView.zig | 18 ++++++++++++++++--
 4 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/river/View.zig b/river/View.zig
index d88c536..d61c5b4 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -179,7 +179,7 @@ pub fn applyPending(self: *Self) void {
         self.pending.box = self.float_box;
     }
 
-    if (!self.current.fullscreen and self.pending.fullscreen) {
+    if (!self.lastSetFullscreenState() and self.pending.fullscreen) {
         // If switching to fullscreen, set the dimensions to the full area of the output
         self.setFullscreen(true);
         self.post_fullscreen_box = self.current.box;
@@ -190,7 +190,7 @@ pub fn applyPending(self: *Self) void {
             .width = dimensions.width,
             .height = dimensions.height,
         };
-    } else if (self.current.fullscreen and !self.pending.fullscreen) {
+    } else if (self.lastSetFullscreenState() and !self.pending.fullscreen) {
         self.setFullscreen(false);
         self.pending.box = self.post_fullscreen_box;
     }
@@ -219,6 +219,13 @@ pub fn configure(self: *Self) void {
     }
 }
 
+fn lastSetFullscreenState(self: Self) bool {
+    return switch (self.impl) {
+        .xdg_toplevel => |xdg_toplevel| xdg_toplevel.lastSetFullscreenState(),
+        .xwayland_view => |xwayland_view| xwayland_view.lastSetFullscreenState(),
+    };
+}
+
 pub fn sendFrameDone(self: Self) void {
     var now: os.timespec = undefined;
     os.clock_gettime(os.CLOCK.MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
@@ -330,11 +337,11 @@ pub fn setActivated(self: Self, activated: bool) void {
     }
 }
 
-fn setFullscreen(self: Self, fullscreen: bool) void {
+fn setFullscreen(self: *Self, fullscreen: bool) void {
     if (self.foreign_toplevel_handle) |handle| handle.setFullscreen(fullscreen);
     switch (self.impl) {
         .xdg_toplevel => |xdg_toplevel| xdg_toplevel.setFullscreen(fullscreen),
-        .xwayland_view => |xwayland_view| xwayland_view.setFullscreen(fullscreen),
+        .xwayland_view => |*xwayland_view| xwayland_view.setFullscreen(fullscreen),
     }
 }
 
diff --git a/river/VoidView.zig b/river/VoidView.zig
index 0e07d12..dfd9e82 100644
--- a/river/VoidView.zig
+++ b/river/VoidView.zig
@@ -30,6 +30,10 @@ pub fn configure(_: Self) void {
     unreachable;
 }
 
+pub fn lastSetFullscreenState(_: Self) bool {
+    unreachable;
+}
+
 pub fn setActivated(_: Self, _: bool) void {
     unreachable;
 }
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 6a56b70..dd02087 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -96,6 +96,10 @@ pub fn configure(self: *Self) void {
     self.acked_pending_serial = false;
 }
 
+pub fn lastSetFullscreenState(self: Self) bool {
+    return self.xdg_surface.role_data.toplevel.scheduled.fullscreen;
+}
+
 /// Close the view. This will lead to the unmap and destroy events being sent
 pub fn close(self: Self) void {
     self.xdg_surface.role_data.toplevel.sendClose();
diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig
index 4d7e9f5..e879201 100644
--- a/river/XwaylandView.zig
+++ b/river/XwaylandView.zig
@@ -34,6 +34,11 @@ view: *View,
 /// The corresponding wlroots object
 xwayland_surface: *wlr.XwaylandSurface,
 
+/// The wlroots Xwayland implementation overwrites xwayland_surface.fullscreen
+/// immediately when the client requests it, so we track this state here to be
+/// able to match the XdgToplevel API.
+last_set_fullscreen_state: bool,
+
 // Listeners that are always active over the view's lifetime
 destroy: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleDestroy),
 map: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleMap),
@@ -51,7 +56,11 @@ request_minimize: wl.Listener(*wlr.XwaylandSurface.event.Minimize) =
     wl.Listener(*wlr.XwaylandSurface.event.Minimize).init(handleRequestMinimize),
 
 pub fn init(self: *Self, view: *View, xwayland_surface: *wlr.XwaylandSurface) void {
-    self.* = .{ .view = view, .xwayland_surface = xwayland_surface };
+    self.* = .{
+        .view = view,
+        .xwayland_surface = xwayland_surface,
+        .last_set_fullscreen_state = xwayland_surface.fullscreen,
+    };
     xwayland_surface.data = @ptrToInt(self);
 
     // Add listeners that are active over the view's entire lifetime
@@ -85,6 +94,10 @@ pub fn configure(self: Self) void {
     );
 }
 
+pub fn lastSetFullscreenState(self: Self) bool {
+    return self.last_set_fullscreen_state;
+}
+
 /// Close the view. This will lead to the unmap and destroy events being sent
 pub fn close(self: Self) void {
     self.xwayland_surface.close();
@@ -99,7 +112,8 @@ pub fn setActivated(self: Self, activated: bool) void {
     self.xwayland_surface.restack(null, .above);
 }
 
-pub fn setFullscreen(self: Self, fullscreen: bool) void {
+pub fn setFullscreen(self: *Self, fullscreen: bool) void {
+    self.last_set_fullscreen_state = fullscreen;
     self.xwayland_surface.setFullscreen(fullscreen);
 }