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

commit1fb1ecec9e773ef20f93be36eeac044bd71227d5
parenta0f0b38842
authorIsaac Freund <[email protected]>
date2025-12-04 14:43
OutputManager: fix disabling outputs

 river/Output.zig        |  9 +++++----
 river/OutputManager.zig | 36 +++++++++++++++++++-----------------
 2 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/river/Output.zig b/river/Output.zig
index dba4d3e..2a83e31 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -98,8 +98,10 @@ pub const State = struct {
     }
 
     pub fn applyModeset(state: *const State, wlr_state: *wlr.Output.State) void {
+        const enabled = state.state == .enabled;
+        wlr_state.setEnabled(enabled);
+        if (!enabled) return;
         state.applyNoModeset(wlr_state);
-        wlr_state.setEnabled(state.state == .enabled);
         switch (state.mode) {
             .standard => |mode| wlr_state.setMode(mode),
             .custom => |mode| wlr_state.setCustomMode(mode.width, mode.height, mode.refresh),
@@ -283,7 +285,6 @@ pub fn manageStart(output: *Output) void {
             }
 
             output.sent = output.scheduled;
-
             output.link_sent.remove();
             server.wm.wm_sent.outputs.append(output);
         },
@@ -295,8 +296,7 @@ pub fn manageStart(output: *Output) void {
                 output.object = null;
             }
 
-            output.link_sent.remove();
-            output.link_sent.init();
+            output.sent = output.scheduled;
 
             if (output.scheduled.state == .destroying) {
                 {
@@ -316,6 +316,7 @@ pub fn manageStart(output: *Output) void {
                     }
                 }
                 output.link.remove();
+                output.link_sent.remove();
 
                 util.gpa.destroy(output);
             }
diff --git a/river/OutputManager.zig b/river/OutputManager.zig
index ff2ff27..bce5cbf 100644
--- a/river/OutputManager.zig
+++ b/river/OutputManager.zig
@@ -147,9 +147,13 @@ fn handleManagerApply(_: *wl.Listener(*wlr.OutputConfigurationV1), config: *wlr.
     var it = config.heads.iterator(.forward);
     while (it.next()) |head| {
         const output: *Output = @ptrCast(@alignCast(head.state.output.data));
-
+        if (!head.state.enabled) {
+            // Avoid overwriting and losing all other output state on disable.
+            output.scheduled.state = .disabled_hard;
+            continue;
+        }
         output.scheduled = .{
-            .state = if (head.state.enabled) .enabled else .disabled_hard,
+            .state = .enabled,
             .mode = blk: {
                 if (head.state.mode) |mode| {
                     break :blk .{ .standard = mode };
@@ -252,7 +256,6 @@ pub fn autoLayout(om: *OutputManager) void {
 
 pub fn commitOutputState(om: *OutputManager) void {
     const wm = &server.wm;
-
     {
         var it = wm.wm_sent.outputs.iterator(.forward);
         while (it.next()) |output| {
@@ -309,6 +312,8 @@ pub fn commitOutputState(om: *OutputManager) void {
     };
 
     if (need_modeset) {
+        log.debug("committing output state requires modeset", .{});
+
         var states: std.ArrayList(wlr.Backend.OutputState) = .empty;
         defer states.deinit(util.gpa);
         defer for (states.items) |*s| s.base.finish();
@@ -386,16 +391,6 @@ 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| {
@@ -405,13 +400,20 @@ pub fn commitOutputState(om: *OutputManager) void {
     }
 
     {
-        var it = wm.wm_sent.outputs.iterator(.forward);
+        var it = wm.wm_sent.outputs.safeIterator(.forward);
         while (it.next()) |output| {
-            output.current = output.sent;
-
             if (output.wlr_output) |wlr_output| {
-                wlr_output.scheduleFrame();
+                if (wlr_output.enabled) {
+                    wlr_output.scheduleFrame();
+                } else {
+                    output.lock_render_state = .blanked;
+                }
             }
+            if (output.sent.state == .disabled_hard) {
+                output.link_sent.remove();
+                output.link_sent.init();
+            }
+            output.current = output.sent;
         }
     }