From 147d9c2f90fc4a778d48d1517fc784a0809c51cb Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 5 Feb 2022 00:08:21 +0100 Subject: [PATCH] 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); }