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

commita0f0b38842a5f84f83ee57d1bb398e366cb71f25
parentc27084859d
authorIsaac Freund <[email protected]>
date2025-12-03 12:14
session-lock: unbreak implementation

This has been broken for a while due to river-window-management related
refactoring.

Still needs more testing, but I need to fix disabling outputs before
complete testing is possible.

 river/LockManager.zig   | 15 ++++++----
 river/LockSurface.zig   | 13 +++++----
 river/Output.zig        | 78 ++++++++++++++++++++++++-------------------------
 river/OutputManager.zig | 22 +++++++++++---
 river/Scene.zig         |  1 +
 5 files changed, 75 insertions(+), 54 deletions(-)

diff --git a/river/LockManager.zig b/river/LockManager.zig
index 3e12242..a4be23e 100644
--- a/river/LockManager.zig
+++ b/river/LockManager.zig
@@ -86,6 +86,8 @@ pub fn deinit(manager: *LockManager) void {
 fn handleLock(listener: *wl.Listener(*wlr.SessionLockV1), lock: *wlr.SessionLockV1) void {
     const manager: *LockManager = @fieldParentPtr("new_lock", listener);
 
+    log.debug("session lock client made lock request", .{});
+
     if (manager.lock != null) {
         log.info("denying new session lock client, an active one already exists", .{});
         lock.destroy();
@@ -97,9 +99,8 @@ fn handleLock(listener: *wl.Listener(*wlr.SessionLockV1), lock: *wlr.SessionLock
     if (manager.state == .unlocked) {
         manager.state = .waiting_for_lock_surfaces;
 
-        if (build_options.xwayland) {
-            server.scene.layers.override_redirect.node.setEnabled(false);
-        }
+        assert(!server.scene.locked_tree.node.enabled);
+        server.scene.locked_tree.node.setEnabled(true);
 
         manager.lock_surfaces_timer.timerUpdate(200) catch {
             log.err("error setting lock surfaces timer, imperfect frames may be shown", .{});
@@ -133,7 +134,7 @@ fn handleLockSurfacesTimeout(manager: *LockManager) c_int {
     assert(manager.state == .waiting_for_lock_surfaces);
     manager.state = .waiting_for_blank;
 
-    if (true) @panic("XXX blank all outputs");
+    server.scene.normal_tree.node.setEnabled(false);
 
     // This call is necessary in the case that all outputs in the layout are disabled.
     manager.maybeLock();
@@ -147,7 +148,8 @@ pub fn maybeLock(manager: *LockManager) void {
     {
         var it = server.om.outputs.iterator(.forward);
         while (it.next()) |output| {
-            if (!output.wlr_output.?.enabled) continue;
+            const wlr_output = output.wlr_output orelse continue;
+            if (!wlr_output.enabled) continue;
 
             switch (output.lock_render_state) {
                 .pending_unlock, .unlocked, .pending_blank, .pending_lock_surface => {
@@ -168,6 +170,7 @@ pub fn maybeLock(manager: *LockManager) void {
             // The lock client may have been destroyed, for example due to a protocol error.
             if (manager.lock) |lock| lock.sendLocked();
             manager.state = .locked;
+            server.scene.normal_tree.node.setEnabled(false);
             manager.lock_surfaces_timer.timerUpdate(0) catch {};
         },
         .waiting_for_blank => if (all_outputs_blanked) {
@@ -191,7 +194,7 @@ fn handleUnlock(listener: *wl.Listener(void)) void {
     server.scene.normal_tree.node.setEnabled(true);
 
     assert(server.scene.locked_tree.node.enabled);
-    server.scene.locked_tree.node.setEnabled(true);
+    server.scene.locked_tree.node.setEnabled(false);
 
     {
         var it = server.input_manager.seats.iterator(.forward);
diff --git a/river/LockSurface.zig b/river/LockSurface.zig
index e453de2..a3a0ca2 100644
--- a/river/LockSurface.zig
+++ b/river/LockSurface.zig
@@ -30,6 +30,7 @@ const SceneNodeData = @import("SceneNodeData.zig");
 
 wlr_lock_surface: *wlr.SessionLockSurfaceV1,
 lock: *wlr.SessionLockV1,
+tree: *wlr.SceneTree,
 
 idle_update_focus: ?*wl.EventSource = null,
 
@@ -40,17 +41,17 @@ pub fn create(wlr_lock_surface: *wlr.SessionLockSurfaceV1, lock: *wlr.SessionLoc
     const lock_surface = try util.gpa.create(LockSurface);
     errdefer util.gpa.destroy(lock_surface);
 
+    const tree = try server.scene.locked_tree.createSceneSubsurfaceTree(wlr_lock_surface.surface);
+    errdefer tree.node.destroy();
+
     lock_surface.* = .{
         .wlr_lock_surface = wlr_lock_surface,
         .lock = lock,
+        .tree = tree,
     };
     wlr_lock_surface.data = lock_surface;
 
-    const tree = try server.scene.locked_tree.createSceneSubsurfaceTree(wlr_lock_surface.surface);
-    errdefer tree.node.destroy();
-
     try SceneNodeData.attach(&tree.node, .{ .lock_surface = lock_surface });
-
     wlr_lock_surface.surface.data = &tree.node;
 
     wlr_lock_surface.surface.events.map.add(&lock_surface.map);
@@ -59,7 +60,7 @@ pub fn create(wlr_lock_surface: *wlr.SessionLockSurfaceV1, lock: *wlr.SessionLoc
     lock_surface.configure();
 }
 
-pub fn destroy(lock_surface: *LockSurface) void {
+fn destroy(lock_surface: *LockSurface) void {
     {
         var surface_it = lock_surface.lock.surfaces.iterator(.forward);
         const new_focus: Seat.Focus = while (surface_it.next()) |surface| {
@@ -102,6 +103,8 @@ pub fn configure(lock_surface: *LockSurface) void {
 
 fn handleMap(listener: *wl.Listener(void)) void {
     const lock_surface: *LockSurface = @fieldParentPtr("map", listener);
+    const output = lock_surface.getOutput();
+    lock_surface.tree.node.setPosition(output.sent.x, output.sent.y);
 
     // Unfortunately the surface commit handlers for the scene subsurface tree corresponding to
     // this lock surface won't be called until after this function returns, which means that we cannot
diff --git a/river/Output.zig b/river/Output.zig
index bffff9b..dba4d3e 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -400,42 +400,43 @@ fn renderAndCommit(output: *Output) !void {
 
     if (!wlr_output.commitState(&state)) return error.CommitFailed;
 
-    const lock_surface_mapped = blk: {
-        if (server.lock_manager.lockSurfaceFromOutput(output)) |lock_surface| {
-            break :blk lock_surface.wlr_lock_surface.surface.mapped;
-        } else {
-            break :blk false;
-        }
-    };
-
-    if (server.lock_manager.state == .locked or
-        (server.lock_manager.state == .waiting_for_lock_surfaces and lock_surface_mapped) or
-        server.lock_manager.state == .waiting_for_blank)
-    {
-        assert(!server.scene.normal_tree.node.enabled);
-        assert(server.scene.locked_tree.node.enabled);
-
-        switch (server.lock_manager.state) {
-            .unlocked => unreachable,
-            .locked => switch (output.lock_render_state) {
+    switch (server.lock_manager.state) {
+        .unlocked => {
+            if (output.lock_render_state != .unlocked) {
+                output.lock_render_state = .pending_unlock;
+            }
+        },
+        .locked => {
+            assert(!server.scene.normal_tree.node.enabled);
+            switch (output.lock_render_state) {
                 .pending_unlock, .unlocked, .pending_blank, .pending_lock_surface => unreachable,
                 .blanked, .lock_surface => {},
-            },
-            .waiting_for_blank => {
-                if (output.lock_render_state != .blanked) {
-                    output.lock_render_state = .pending_blank;
+            }
+        },
+        .waiting_for_blank => {
+            assert(!server.scene.normal_tree.node.enabled);
+            if (output.lock_render_state != .blanked) {
+                output.lock_render_state = .pending_blank;
+            }
+        },
+        .waiting_for_lock_surfaces => {
+            const lock_surface_mapped = blk: {
+                if (server.lock_manager.lockSurfaceFromOutput(output)) |lock_surface| {
+                    break :blk lock_surface.wlr_lock_surface.surface.mapped;
+                } else {
+                    break :blk false;
                 }
-            },
-            .waiting_for_lock_surfaces => {
+            };
+            if (lock_surface_mapped) {
                 if (output.lock_render_state != .lock_surface) {
                     output.lock_render_state = .pending_lock_surface;
                 }
-            },
-        }
-    } else {
-        if (output.lock_render_state != .unlocked) {
-            output.lock_render_state = .pending_unlock;
-        }
+            } else {
+                if (output.lock_render_state != .unlocked) {
+                    output.lock_render_state = .pending_unlock;
+                }
+            }
+        },
     }
 }
 
@@ -444,24 +445,23 @@ fn handlePresent(
     event: *wlr.Output.event.Present,
 ) void {
     const output: *Output = @fieldParentPtr("present", listener);
-
     if (!event.presented) {
         return;
     }
-
     switch (output.lock_render_state) {
         .pending_unlock => {
             assert(server.lock_manager.state != .locked);
             output.lock_render_state = .unlocked;
         },
         .unlocked => assert(server.lock_manager.state != .locked),
-        .pending_blank, .pending_lock_surface => {
-            output.lock_render_state = switch (output.lock_render_state) {
-                .pending_blank => .blanked,
-                .pending_lock_surface => .lock_surface,
-                .pending_unlock, .unlocked, .blanked, .lock_surface => unreachable,
-            };
-
+        .pending_blank => {
+            output.lock_render_state = .blanked;
+            if (server.lock_manager.state != .locked) {
+                server.lock_manager.maybeLock();
+            }
+        },
+        .pending_lock_surface => {
+            output.lock_render_state = .lock_surface;
             if (server.lock_manager.state != .locked) {
                 server.lock_manager.maybeLock();
             }
diff --git a/river/OutputManager.zig b/river/OutputManager.zig
index bd037d1..ff2ff27 100644
--- a/river/OutputManager.zig
+++ b/river/OutputManager.zig
@@ -264,6 +264,9 @@ pub fn commitOutputState(om: *OutputManager) void {
                         log.err("out of memory", .{});
                         continue; // Try again next time
                     };
+                    if (server.lock_manager.lockSurfaceFromOutput(output)) |lock_surface| {
+                        lock_surface.tree.node.setPosition(output.sent.x, output.sent.y);
+                    }
                 },
                 .disabled_hard, .destroying => {
                     om.output_layout.remove(wlr_output);
@@ -279,12 +282,14 @@ pub fn commitOutputState(om: *OutputManager) void {
         var it = wm.wm_sent.outputs.iterator(.forward);
         while (it.next()) |output| {
             const wlr_output = output.wlr_output orelse continue;
-
             switch (output.sent.state) {
                 .enabled => if (!wlr_output.enabled) break :blk true,
-                .disabled_soft, .disabled_hard, .destroying => continue,
+                // Technically disabling an output does not require a modeset,
+                // but handling both enabled and disable here simplifies
+                // lock_render_state tracking.
+                .disabled_soft, .disabled_hard => if (wlr_output.enabled) break :blk true,
+                .destroying => unreachable, // output.wlr_output must be null.
             }
-
             switch (output.sent.mode) {
                 .standard => |mode| {
                     if (mode != wlr_output.current_mode) break :blk true;
@@ -300,7 +305,6 @@ pub fn commitOutputState(om: *OutputManager) void {
                 break :blk true;
             }
         }
-
         break :blk false;
     };
 
@@ -382,6 +386,16 @@ pub fn commitOutputState(om: *OutputManager) void {
         }
 
         swapchain_manager.apply();
+
+        {
+            var it = wm.wm_sent.outputs.iterator(.forward);
+            while (it.next()) |output| {
+                const wlr_output = output.wlr_output orelse continue;
+                if (!wlr_output.enabled) {
+                    output.lock_render_state = .blanked;
+                }
+            }
+        }
     }
 
     if (wm.wm_sent.output_config) |config| {
diff --git a/river/Scene.zig b/river/Scene.zig
index 33eb69e..6e23c83 100644
--- a/river/Scene.zig
+++ b/river/Scene.zig
@@ -73,6 +73,7 @@ pub fn init(scene: *Scene) !void {
 
     const normal_tree = try interactive_tree.createSceneTree();
     const locked_tree = try interactive_tree.createSceneTree();
+    locked_tree.node.setEnabled(false);
 
     scene.* = .{
         .wlr_scene = wlr_scene,