From c2c9755925299b028ead54273a38ba42b4b7fb3b Mon Sep 17 00:00:00 2001 From: groveer Date: Tue, 25 Aug 2026 15:11:10 +0800 Subject: [PATCH] fix(output): keep copy mode when switching display parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. In copy mode, treat wlr-output-management requests that only change display parameters (mode/refresh/scale) as non-topology changes and keep the mirror setup, instead of always converting mirror outputs to normal outputs and leaving copy mode. 2. Only tear down copy mode when an output is actually enabled/disabled; layout positions are ignored because the client may send positions remembered from a previous extension layout. 3. Only clear the persisted copy configuration when copy mode is really left, so a pure refresh-rate switch keeps the mirror topology. Log: fix mirror output being broken after switching refresh rate in copy mode Influence: 1. Enter copy mode, switch refresh rate, verify both screens stay mirrored 2. Enter copy mode, adjust positions in extension mode first, then switch refresh rate, verify mirror topology survives 3. Disable/enable an output in copy mode, verify it exits to extension mode as before fix(output): 复制模式下切换显示参数时保持复制模式 1. 复制模式下,仅改变显示参数(mode/refresh/scale)的输出管理请求不再 被当作拓扑变化处理,保持镜像输出不被拆分。 2. 仅当输出被真正启用或禁用时才退出复制模式;不再比较布局位置,因为 客户端可能发送扩展布局时记忆的位置。 3. 仅真正退出复制模式时才清除持久化的复制配置,纯刷新率切换保留镜像拓扑。 Log: 修复复制模式下切换刷新率后镜像输出被破坏的问题 Influence: 1. 进入复制模式后切换刷新率,验证两屏仍保持镜像 2. 先在扩展模式调整屏幕位置再进入复制模式,切换刷新率,验证镜像拓扑保留 3. 在复制模式下禁用/启用某输出,验证仍按原逻辑退出为扩展模式 PMS: BUG-374563 --- src/seat/helper.cpp | 52 ++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/seat/helper.cpp b/src/seat/helper.cpp index 10f85a55db..adf4961dcb 100644 --- a/src/seat/helper.cpp +++ b/src/seat/helper.cpp @@ -1079,26 +1079,42 @@ void Helper::onOutputTestOrApply(wlr_output_configuration_v1 *config, bool onlyT } if (m_mode == OutputMode::Copy) { - // Output-management positions describe independent outputs. Convert copy - // proxies before applying the requested layout so their target-output - // binding cannot keep them overlapping the copy source at (0, 0). - for (int i = 0; i < m_outputList.size(); ++i) { - Output *copyOutput = m_outputList.at(i); - if (copyOutput->isSource()) { - continue; + // A refresh-rate / mode switch is a display-parameter change, not a + // topology change. Keep copy mode unless an output is enabled/disabled. + bool topologyChanged = false; + for (const auto &state : std::as_const(states)) { + if (state.enabled != state.output->isEnabled()) { + topologyChanged = true; + break; } - - removeOutputFromRootContainer(copyOutput); - Output *normalOutput = createNormalOutput(copyOutput->output()); - copyOutput->deleteLater(); - m_outputList.replace(i, normalOutput); } - } - if (m_mode != OutputMode::Extension) { + if (topologyChanged) { + // Output-management positions describe independent outputs. Convert copy + // proxies before applying the requested layout so their target-output + // binding cannot keep them overlapping the copy source at (0, 0). + for (int i = 0; i < m_outputList.size(); ++i) { + Output *copyOutput = m_outputList.at(i); + if (copyOutput->isSource()) { + continue; + } + + removeOutputFromRootContainer(copyOutput); + Output *normalOutput = createNormalOutput(copyOutput->output()); + copyOutput->deleteLater(); + m_outputList.replace(i, normalOutput); + } + + if (m_mode != OutputMode::Extension) { + m_mode = OutputMode::Extension; + Q_EMIT outputModeChanged(); + } + } + } else if (m_mode != OutputMode::Extension) { m_mode = OutputMode::Extension; Q_EMIT outputModeChanged(); } + if (m_outputManagerHelper) { m_outputManagerHelper->clearCopyModeRestoreIntent(); } @@ -1278,9 +1294,11 @@ void Helper::onOutputCommitFinished(wlr_output_configuration_v1 *config, bool su bool ok = m_pendingOutputConfig.allSuccess; if (ok) { m_outputManagerHelper->storeSingleOutputConfig(); - // An output-management enable/disable transaction describes an - // extension/single-output topology, never a copy topology. - m_outputManagerHelper->storeCopyOutputConfig(false); + // Only clear the persisted copy configuration when copy mode was + // actually left; a pure refresh-rate switch keeps it. + if (m_mode != OutputMode::Copy) { + m_outputManagerHelper->storeCopyOutputConfig(false); + } const auto enabledOutputCount = std::count_if( m_pendingOutputConfig.states.cbegin(),