From 0ca7b40da0457e1ee5ce079d78d304c76b7592b5 Mon Sep 17 00:00:00 2001 From: Dominique Hummel Date: Tue, 14 Feb 2023 12:03:40 +0000 Subject: [PATCH] tiling: Evacuate tiled windows from custom & quick tiling on output removal Context: If a display is removed, the corresponding TileManager is removed with it. This in turn removes every one of its Tiles with it, and when a Tile's destructor is called, it attempts to find a new replacement tile for any windows it was previously managing. However, if the Tile is removed because its corresponding TileManager has been removed, this has the potential to cause a segfault in KWin, causing it to crash (I suspect a possible race condition? but not sure). This MR correctly evacuates custom tiled windows & migrates quick tiled windows upon output removal. BUG: 465522 --- src/tiles/tile.cpp | 8 ++++++++ src/tiles/tile.h | 5 +++++ src/workspace.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/tiles/tile.cpp b/src/tiles/tile.cpp index 328c7043d4..e11cf4450c 100644 --- a/src/tiles/tile.cpp +++ b/src/tiles/tile.cpp @@ -372,6 +372,14 @@ Tile *Tile::parentTile() const return m_parentTile; } +void Tile::visitDescendants(std::function callback) const +{ + callback(this); + for (const Tile *child : m_children) { + child->visitDescendants(callback); + } +} + TileManager *Tile::manager() const { return m_tiling; diff --git a/src/tiles/tile.h b/src/tiles/tile.h index 8eefff400b..22e309a7c9 100644 --- a/src/tiles/tile.h +++ b/src/tiles/tile.h @@ -98,6 +98,11 @@ public: */ QList descendants() const; + /** + * Visit all tiles descendant of this tile, recursive + */ + void visitDescendants(std::function callback) const; + void resizeFromGravity(Gravity gravity, int x_root, int y_root); Q_INVOKABLE void resizeByPixels(qreal delta, Qt::Edge edge); diff --git a/src/workspace.cpp b/src/workspace.cpp index 74f4e94467..5f71da7135 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -1595,12 +1595,51 @@ void Workspace::updateOutputs(const QVector &outputOrder) Q_EMIT outputAdded(output); } - desktopResized(); - const auto removed = oldOutputsSet - outputsSet; for (Output *output : removed) { - m_tileManagers.erase(output); Q_EMIT outputRemoved(output); + auto tileManager = std::move(m_tileManagers[output]); + m_tileManagers.erase(output); + + // Evacuate windows from the defunct custom tile tree. + tileManager->rootTile()->visitDescendants([](const Tile *child) { + const QList windows = child->windows(); + for (Window *window : windows) { + window->setTile(nullptr); + } + }); + + // Migrate windows from the defunct quick tile to a quick tile tree on another output. + static constexpr QuickTileMode quickTileModes[] = { + QuickTileFlag::Left, + QuickTileFlag::Right, + QuickTileFlag::Top, + QuickTileFlag::Bottom, + QuickTileFlag::Top | QuickTileFlag::Left, + QuickTileFlag::Top | QuickTileFlag::Right, + QuickTileFlag::Bottom | QuickTileFlag::Left, + QuickTileFlag::Bottom | QuickTileFlag::Right, + }; + + for (const QuickTileMode &quickTileMode : quickTileModes) { + Tile *quickTile = tileManager->quickTile(quickTileMode); + const QList windows = quickTile->windows(); + if (windows.isEmpty()) { + continue; + } + + Output *bestOutput = outputAt(output->geometry().center()); + Tile *bestTile = m_tileManagers[bestOutput]->quickTile(quickTileMode); + + for (Window *window : windows) { + window->setTile(bestTile); + } + } + } + + desktopResized(); + + for (Output *output : removed) { output->unref(); }