From b0e54c63965801f6737f9b61746396d473dca226 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 8 Apr 2024 13:34:47 +0200 Subject: [PATCH] Output: fix possible assertion failure on enable Currently if we disable an output due to a wlr-output-power-management protocol request we do not update Output.lock_render_state properly. This is fine if the output is also re-enabled using the wlr-output-power-management protocol but causes an assertion failure if it is re-enabled using wlr-output-management instead. --- river/Output.zig | 19 +++++++++++++------ river/Root.zig | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/river/Output.zig b/river/Output.zig index e8b72b7..6207588 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -432,7 +432,7 @@ fn handleRequestState(listener: *wl.Listener(*wlr.Output.event.RequestState), ev pub fn applyState(output: *Output, state: *wlr.Output.State) error{CommitFailed}!void { // We need to be precise about this state change to make assertions - // in handleEnableDisable() possible. + // in updateLockRenderStateOnEnableDisable() possible. const enable_state_change = state.committed.enabled and (state.enabled != output.wlr_output.enabled); @@ -452,6 +452,18 @@ pub fn applyState(output: *Output, state: *wlr.Output.State) error{CommitFailed} } fn handleEnableDisable(output: *Output) void { + output.updateLockRenderStateOnEnableDisable(); + + if (output.wlr_output.enabled) { + // Add the output to root.active_outputs and the output layout if it has not + // already been added. + server.root.activateOutput(output); + } else { + server.root.deactivateOutput(output); + } +} + +pub fn updateLockRenderStateOnEnableDisable(output: *Output) void { // We can't assert the current state of normal_content/locked_content // here as this output may be newly created. if (output.wlr_output.enabled) { @@ -467,16 +479,11 @@ fn handleEnableDisable(output: *Output) void { output.locked_content.node.setEnabled(true); }, } - // Add the output to root.active_outputs and the output layout if it has not - // already been added. - server.root.activateOutput(output); } else { // Disabling and re-enabling an output always blanks it. output.lock_render_state = .blanked; output.normal_content.node.setEnabled(false); output.locked_content.node.setEnabled(true); - - server.root.deactivateOutput(output); } } diff --git a/river/Root.zig b/river/Root.zig index be2242a..731c0e6 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -846,18 +846,37 @@ fn handlePowerManagerSetMode( _: *wl.Listener(*wlr.OutputPowerManagerV1.event.SetMode), event: *wlr.OutputPowerManagerV1.event.SetMode, ) void { - const enable = event.mode == .on; + // The output may have been destroyed, in which case there is nothing to do + const output = @as(?*Output, @ptrFromInt(event.output.data)) orelse return; - const log_text = if (enable) "Enabling" else "Disabling"; - std.log.scoped(.output_manager).debug( - "{s} dpms for output {s}", - .{ log_text, event.output.name }, - ); + std.log.debug("client requested dpms {s} for output {s}", .{ + @tagName(event.mode), + event.output.name, + }); - event.output.enable(enable); - event.output.commit() catch { - std.log.scoped(.server).err("output commit failed for {s}", .{event.output.name}); - }; + const requested = event.mode == .on; + + if (output.wlr_output.enabled == requested) { + std.log.debug("output {s} dpms is already {s}, ignoring request", .{ + event.output.name, + @tagName(event.mode), + }); + return; + } + + { + var state = wlr.Output.State.init(); + defer state.finish(); + + state.setEnabled(requested); + + if (!output.wlr_output.commitState(&state)) { + std.log.scoped(.server).err("output commit failed for {s}", .{output.wlr_output.name}); + return; + } + } + + output.updateLockRenderStateOnEnableDisable(); } fn handleSetGamma(