Skip to content

Commit 9960e41

Browse files
deepin-wmagent
authored andcommitted
fix: migrate windows and layer surfaces on primary hot-unplug
1. Fix onScreenRemoved missing primary migration when removeOutput already switched primary output before onScreenRemoved is called 2. Extract migrateSurfacesToNewPrimary as a public method to eliminate duplicated migration logic in onScreenRemoved and onScreenDisabled 3. Add position correction for remaining output surfaces after output layout change, with normalGeometry sync for XdgToplevel 4. Migrate layer surfaces to remaining output instead of closing them when output is removed, and trigger arrangeLayerSurfaces for final layout Log: Fix windows and layer surfaces not displaying on remaining screen after primary output hot-unplug in extension mode Influence: 1. Test hot-unplug primary output in dual-screen extension mode 2. Verify windows on secondary screen move correctly to remaining screen 3. Verify layer surfaces (panel/dock) migrate correctly after unplug 4. Test maximize/restore window after output removal 5. Verify Copy mode is not affected by the changes fix: 主屏热插拔时正确迁移窗口和层表面 1. 修复 onScreenRemoved 缺失主屏迁移逻辑,当 removeOutput 在 onScreenRemoved 之前已切换主屏输出时 2. 提取 migrateSurfacesToNewPrimary 为公共方法,消除 onScreenRemoved 和 onScreenDisabled 中的重复迁移逻辑 3. 添加输出布局变化后剩余输出上表面位置校正,同步更新 XdgToplevel 的 normalGeometry 4. 输出移除时将层表面迁移到剩余输出而非关闭,并触发 arrangeLayerSurfaces 进行最终布局 Log: 修复扩展模式下拔掉主屏后窗口和面板不显示在剩余屏幕的问题 Influence: 1. 测试双屏扩展模式下拔掉主屏 2. 验证副屏上的窗口正确移动到剩余屏幕 3. 验证层表面(面板/任务栏)在拔屏后正确迁移 4. 测试拔屏后最大化/还原窗口功能 5. 验证复制模式不受此变更影响 Fixes: #1
1 parent e25d5b9 commit 9960e41

5 files changed

Lines changed: 66 additions & 18 deletions

File tree

src/core/layersurfacecontainer.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,26 @@ void LayerSurfaceContainer::removeOutput(Output *output)
6464
Q_ASSERT(container);
6565
m_surfaceContainers.removeOne(container);
6666

67+
OutputLayerSurfaceContainer *targetContainer = m_surfaceContainers.isEmpty()
68+
? nullptr
69+
: m_surfaceContainers.first();
70+
6771
const auto surfaces = container->surfaces();
6872
for (SurfaceWrapper *surface : surfaces) {
69-
auto layerSurface = qobject_cast<WLayerSurface *>(surface->shellSurface());
70-
Q_ASSERT(layerSurface);
71-
// Needs to be moved to the new primary output
72-
if (!layerSurface->output() && rootContainer()->primaryOutput()) {
73-
container->removeSurface(surface);
74-
addSurfaceToContainer(surface);
73+
container->removeSurface(surface);
74+
if (targetContainer) {
75+
targetContainer->addSurface(surface);
7576
} else {
76-
layerSurface->closed();
77+
auto layerSurface = qobject_cast<WLayerSurface *>(surface->shellSurface());
78+
if (layerSurface)
79+
layerSurface->closed();
7780
}
7881
}
7982

83+
if (targetContainer) {
84+
targetContainer->output()->arrangeLayerSurfaces();
85+
}
86+
8087
container->deleteLater();
8188
}
8289

src/core/rootsurfacecontainer.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ void RootSurfaceContainer::removeOutput(Output *output)
149149
}
150150
}
151151

152+
// Save positions of remaining outputs before layout removal
153+
QMap<Output *, QPointF> oldPositions;
154+
for (auto o : outputs()) {
155+
if (o != output)
156+
oldPositions[o] = o->outputItem()->position();
157+
}
158+
152159
m_outputLayout->remove(output->output());
153160
if (m_primaryOutput == output) {
154161
const auto outputs = m_outputLayout->outputs();
@@ -158,6 +165,28 @@ void RootSurfaceContainer::removeOutput(Output *output)
158165
}
159166
}
160167

168+
// Correct positions of surfaces on remaining outputs after layout change
169+
for (auto o : outputs()) {
170+
if (o == output)
171+
continue;
172+
auto it = oldPositions.find(o);
173+
if (it == oldPositions.end())
174+
continue;
175+
QPointF delta = o->outputItem()->position() - it.value();
176+
if (!delta.isNull()) {
177+
for (auto *surface : surfaces()) {
178+
if (surface->type() != SurfaceWrapper::Type::Layer && surface->ownsOutput() == o) {
179+
surface->setPosition(surface->position() + delta);
180+
if (surface->type() == SurfaceWrapper::Type::XdgToplevel) {
181+
auto normalGeo = surface->normalGeometry();
182+
normalGeo.moveTopLeft(normalGeo.topLeft() + delta);
183+
surface->moveNormalGeometryInOutput(normalGeo.topLeft());
184+
}
185+
}
186+
}
187+
}
188+
}
189+
161190
// ensure cursor within output
162191
const auto outputPos = output->outputItem()->position();
163192
if (output->geometry().contains(m_cursor->position()) && m_primaryOutput) {

src/output/output.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class Output : public SurfaceListModel
9090
void adjustToOutputBounds(QPointF &pos,
9191
const QRectF &normalGeo,
9292
const QRectF &outputRect) const;
93+
void arrangeLayerSurfaces();
9394

9495
Q_SIGNALS:
9596
void exclusiveZoneChanged();
@@ -110,7 +111,6 @@ public Q_SLOTS:
110111
void setExclusiveZone(Qt::Edge edge, QObject *object, int value);
111112
bool removeExclusiveZone(QObject *object);
112113
void arrangeLayerSurface(SurfaceWrapper *surface);
113-
void arrangeLayerSurfaces();
114114
void arrangeNonLayerSurface(SurfaceWrapper *surface, const QSizeF &sizeDiff);
115115
void arrangePopupSurface(SurfaceWrapper *surface);
116116
void arrangeNonLayerSurfaces();

src/output/outputlifecyclemanager.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@ void OutputLifecycleManager::switchPrimaryOutput(Output *from,
5959
m_rootContainer->moveSurfacesToOutput(surfaces, to, from);
6060
}
6161

62+
void OutputLifecycleManager::migrateSurfacesToNewPrimary(Output *removedOutput,
63+
const QList<SurfaceWrapper *> &surfaces)
64+
{
65+
// NOTE: This must be called while removedOutput's Output item still has valid position
66+
// data. The call chain is: Helper::onOutputRemoved -> removeOutput (switches primary)
67+
// -> onScreenRemoved -> migrateSurfacesToNewPrimary. At this point the removed output
68+
// has been removed from the layout, but the Output object (and its item position) still
69+
// exists because `delete o` happens after onScreenRemoved returns.
70+
if (!m_rootContainer || surfaces.isEmpty())
71+
return;
72+
73+
auto newPrimary = m_rootContainer->primaryOutput();
74+
if (newPrimary) {
75+
m_rootContainer->moveSurfacesToOutput(surfaces, newPrimary, removedOutput);
76+
}
77+
}
78+
6279
void OutputLifecycleManager::onScreenAdded(Output *output)
6380
{
6481
if (!output || !m_configState)
@@ -91,11 +108,8 @@ void OutputLifecycleManager::onScreenRemoved(Output *output,
91108
markScreenAsPrimaryIntent(output);
92109
}
93110

94-
if (!isCurrentPrimary && !wasPrimaryBeforeRemoval) {
95-
auto primaryOutput = m_rootContainer->primaryOutput();
96-
if (primaryOutput) {
97-
m_rootContainer->moveSurfacesToOutput(surfaces, primaryOutput, output);
98-
}
111+
if (!isCurrentPrimary) {
112+
migrateSurfacesToNewPrimary(output, surfaces);
99113
}
100114
}
101115

@@ -120,10 +134,7 @@ void OutputLifecycleManager::onScreenDisabled(Output *output,
120134
switchPrimaryOutput(output, nextPrimary, surfaces);
121135
}
122136
} else if (!isCurrentPrimary) {
123-
auto primaryOutput = m_rootContainer->primaryOutput();
124-
if (primaryOutput) {
125-
m_rootContainer->moveSurfacesToOutput(surfaces, primaryOutput, output);
126-
}
137+
migrateSurfacesToNewPrimary(output, surfaces);
127138
}
128139
}
129140

src/output/outputlifecyclemanager.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2025 UnionTech Software Technology Co., Ltd.
1+
// Copyright (C) 2025-2026 UnionTech Software Technology Co., Ltd.
22
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
33

44
#pragma once
@@ -32,6 +32,7 @@ class OutputLifecycleManager : public QObject {
3232
void onScreenRemoved(Output *output, const QList<SurfaceWrapper *> &surfaces);
3333
void onScreenDisabled(Output *output, const QList<SurfaceWrapper *> &surfaces);
3434
void onScreenEnabled(Output *output);
35+
void migrateSurfacesToNewPrimary(Output *removedOutput, const QList<SurfaceWrapper *> &surfaces);
3536
void setMode(Mode mode) {
3637
m_mode = mode;
3738
}

0 commit comments

Comments
 (0)