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;