Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions src/core/layersurfacecontainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,26 @@ void LayerSurfaceContainer::removeOutput(Output *output)
Q_ASSERT(container);
m_surfaceContainers.removeOne(container);

OutputLayerSurfaceContainer *targetContainer = m_surfaceContainers.isEmpty()
? nullptr
: m_surfaceContainers.first();

const auto surfaces = container->surfaces();
for (SurfaceWrapper *surface : surfaces) {
auto layerSurface = qobject_cast<WLayerSurface *>(surface->shellSurface());
Q_ASSERT(layerSurface);
// Needs to be moved to the new primary output
if (!layerSurface->output() && rootContainer()->primaryOutput()) {
container->removeSurface(surface);
addSurfaceToContainer(surface);
container->removeSurface(surface);
if (targetContainer) {
targetContainer->addSurface(surface);
} else {
layerSurface->closed();
auto layerSurface = qobject_cast<WLayerSurface *>(surface->shellSurface());
if (layerSurface)
layerSurface->closed();
}
}

if (targetContainer) {
targetContainer->output()->arrangeLayerSurfaces();
}

container->deleteLater();
}

Expand Down
29 changes: 29 additions & 0 deletions src/core/rootsurfacecontainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ void RootSurfaceContainer::removeOutput(Output *output)
}
}

// Save positions of remaining outputs before layout removal
QMap<Output *, QPointF> oldPositions;
for (auto o : outputs()) {
if (o != output)
oldPositions[o] = o->outputItem()->position();
}

m_outputLayout->remove(output->output());
if (m_primaryOutput == output) {
const auto outputs = m_outputLayout->outputs();
Expand All @@ -158,6 +165,28 @@ void RootSurfaceContainer::removeOutput(Output *output)
}
}

// Correct positions of surfaces on remaining outputs after layout change
for (auto o : outputs()) {
if (o == output)
continue;
auto it = oldPositions.find(o);
if (it == oldPositions.end())
continue;
QPointF delta = o->outputItem()->position() - it.value();
if (!delta.isNull()) {
for (auto *surface : surfaces()) {
if (surface->type() != SurfaceWrapper::Type::Layer && surface->ownsOutput() == o) {
surface->setPosition(surface->position() + delta);
if (surface->type() == SurfaceWrapper::Type::XdgToplevel) {
auto normalGeo = surface->normalGeometry();
normalGeo.moveTopLeft(normalGeo.topLeft() + delta);
surface->moveNormalGeometryInOutput(normalGeo.topLeft());
}
}
}
}
}

// ensure cursor within output
const auto outputPos = output->outputItem()->position();
if (output->geometry().contains(m_cursor->position()) && m_primaryOutput) {
Expand Down
2 changes: 1 addition & 1 deletion src/output/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class Output : public SurfaceListModel
void adjustToOutputBounds(QPointF &pos,
const QRectF &normalGeo,
const QRectF &outputRect) const;
void arrangeLayerSurfaces();

Q_SIGNALS:
void exclusiveZoneChanged();
Expand All @@ -110,7 +111,6 @@ public Q_SLOTS:
void setExclusiveZone(Qt::Edge edge, QObject *object, int value);
bool removeExclusiveZone(QObject *object);
void arrangeLayerSurface(SurfaceWrapper *surface);
void arrangeLayerSurfaces();
void arrangeNonLayerSurface(SurfaceWrapper *surface, const QSizeF &sizeDiff);
void arrangePopupSurface(SurfaceWrapper *surface);
void arrangeNonLayerSurfaces();
Expand Down
29 changes: 20 additions & 9 deletions src/output/outputlifecyclemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ void OutputLifecycleManager::switchPrimaryOutput(Output *from,
m_rootContainer->moveSurfacesToOutput(surfaces, to, from);
}

void OutputLifecycleManager::migrateSurfacesToNewPrimary(Output *removedOutput,
const QList<SurfaceWrapper *> &surfaces)
{
// NOTE: This must be called while removedOutput's Output item still has valid position
// data. The call chain is: Helper::onOutputRemoved -> removeOutput (switches primary)
// -> onScreenRemoved -> migrateSurfacesToNewPrimary. At this point the removed output
// has been removed from the layout, but the Output object (and its item position) still
// exists because `delete o` happens after onScreenRemoved returns.
if (!m_rootContainer || surfaces.isEmpty())
return;

auto newPrimary = m_rootContainer->primaryOutput();
if (newPrimary) {
m_rootContainer->moveSurfacesToOutput(surfaces, newPrimary, removedOutput);
}
}

void OutputLifecycleManager::onScreenAdded(Output *output)
{
if (!output || !m_configState)
Expand Down Expand Up @@ -91,11 +108,8 @@ void OutputLifecycleManager::onScreenRemoved(Output *output,
markScreenAsPrimaryIntent(output);
}

if (!isCurrentPrimary && !wasPrimaryBeforeRemoval) {
auto primaryOutput = m_rootContainer->primaryOutput();
if (primaryOutput) {
m_rootContainer->moveSurfacesToOutput(surfaces, primaryOutput, output);
}
if (!isCurrentPrimary) {
migrateSurfacesToNewPrimary(output, surfaces);
}
}

Expand All @@ -120,10 +134,7 @@ void OutputLifecycleManager::onScreenDisabled(Output *output,
switchPrimaryOutput(output, nextPrimary, surfaces);
}
} else if (!isCurrentPrimary) {
auto primaryOutput = m_rootContainer->primaryOutput();
if (primaryOutput) {
m_rootContainer->moveSurfacesToOutput(surfaces, primaryOutput, output);
}
migrateSurfacesToNewPrimary(output, surfaces);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/output/outputlifecyclemanager.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 UnionTech Software Technology Co., Ltd.
// Copyright (C) 2025-2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#pragma once
Expand Down Expand Up @@ -32,6 +32,7 @@ class OutputLifecycleManager : public QObject {
void onScreenRemoved(Output *output, const QList<SurfaceWrapper *> &surfaces);
void onScreenDisabled(Output *output, const QList<SurfaceWrapper *> &surfaces);
void onScreenEnabled(Output *output);
void migrateSurfacesToNewPrimary(Output *removedOutput, const QList<SurfaceWrapper *> &surfaces);
void setMode(Mode mode) {
m_mode = mode;
}
Expand Down
Loading