From df2fc30238fe445cf04aaf64d0b906195129f726 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 6 Jan 2023 18:51:40 +0100 Subject: [PATCH] session-lock: wait for present before locking Currently we send the locked event after rendering and commit of blank or lock surfaces buffers on all outputs. However, this is technically not enough to ensure that the buffers have been presented. Instead, listen to the wlr_output present event to ensure that no normal, "unlocked" content is possibly visible. --- river/LockManager.zig | 2 +- river/Output.zig | 41 +++++++++++++++++++++++++++++++++++++++++ river/render.zig | 7 ++++--- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/river/LockManager.zig b/river/LockManager.zig index cc4b56a..6723bd4 100644 --- a/river/LockManager.zig +++ b/river/LockManager.zig @@ -149,7 +149,7 @@ pub fn maybeLock(manager: *LockManager) void { while (it) |node| : (it = node.next) { const output = &node.data; switch (output.lock_render_state) { - .unlocked => { + .unlocked, .pending_blank, .pending_lock_surface => { all_outputs_blanked = false; all_outputs_rendered_lock_surface = false; }, diff --git a/river/Output.zig b/river/Output.zig index d1469d9..0d5a541 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -70,8 +70,17 @@ views: ViewStack(View) = .{}, lock_surface: ?*LockSurface = null, lock_render_state: enum { + /// Normal, "unlocked" content may be visible. unlocked, + /// Submitted a blank buffer but the buffer has not yet been presented. + /// Normal, "unlocked" content may be visible. + pending_blank, + /// A blank buffer has been presented. blanked, + /// Submitted the lock surface buffer but the buffer has not yet been presented. + /// Normal, "unlocked" content may be visible. + pending_lock_surface, + /// The lock surface buffer has been presented. lock_surface, } = .unlocked, @@ -102,6 +111,7 @@ status_trackers: std.SinglyLinkedList(OutputStatus) = .{}, destroy: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleDestroy), enable: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleEnable), mode: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleMode), +present: wl.Listener(*wlr.Output.event.Present) = wl.Listener(*wlr.Output.event.Present).init(handlePresent), frame: wl.Listener(*wlr.OutputDamage) = wl.Listener(*wlr.OutputDamage).init(handleFrame), damage_destroy: wl.Listener(*wlr.OutputDamage) = wl.Listener(*wlr.OutputDamage).init(handleDamageDestroy), @@ -140,6 +150,7 @@ pub fn init(self: *Self, wlr_output: *wlr.Output) !void { wlr_output.events.destroy.add(&self.destroy); wlr_output.events.enable.add(&self.enable); wlr_output.events.mode.add(&self.mode); + wlr_output.events.present.add(&self.present); self.damage.?.events.frame.add(&self.frame); self.damage.?.events.destroy.add(&self.damage_destroy); @@ -478,6 +489,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void { self.enable.link.remove(); self.frame.link.remove(); self.mode.link.remove(); + self.present.link.remove(); // Free all memory and clean up the wlr.Output if (self.layout_demand) |demand| demand.deinit(); @@ -511,6 +523,35 @@ fn handleMode(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void { server.root.startTransaction(); } +fn handlePresent( + listener: *wl.Listener(*wlr.Output.event.Present), + event: *wlr.Output.event.Present, +) void { + const self = @fieldParentPtr(Self, "present", listener); + + switch (self.lock_render_state) { + .unlocked => return, + .pending_blank, .pending_lock_surface => { + if (!event.presented) { + self.lock_render_state = .unlocked; + self.damage.?.addWhole(); + return; + } + + self.lock_render_state = switch (self.lock_render_state) { + .pending_blank => .blanked, + .pending_lock_surface => .lock_surface, + .unlocked, .blanked, .lock_surface => unreachable, + }; + + if (server.lock_manager.state != .locked) { + server.lock_manager.maybeLock(); + } + }, + .blanked, .lock_surface => unreachable, + } +} + fn setTitle(self: Self) void { const title = fmt.allocPrintZ(util.gpa, "river - {s}", .{self.wlr_output.name}) catch return; defer util.gpa.free(title); diff --git a/river/render.zig b/river/render.zig index adef939..a238cd9 100644 --- a/river/render.zig +++ b/river/render.zig @@ -98,9 +98,10 @@ pub fn renderOutput(output: *Output) void { return; }; - output.lock_render_state = if (output.lock_surface != null) .lock_surface else .blanked; - if (server.lock_manager.state != .locked) { - server.lock_manager.maybeLock(); + if (output.lock_surface == null) { + output.lock_render_state = .pending_blank; + } else { + output.lock_render_state = .pending_lock_surface; } return;