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
This commit is contained in:
parent
fbed3d2d69
commit
0ca7b40da0
3 changed files with 55 additions and 3 deletions
|
@ -372,6 +372,14 @@ Tile *Tile::parentTile() const
|
|||
return m_parentTile;
|
||||
}
|
||||
|
||||
void Tile::visitDescendants(std::function<void(const Tile *child)> callback) const
|
||||
{
|
||||
callback(this);
|
||||
for (const Tile *child : m_children) {
|
||||
child->visitDescendants(callback);
|
||||
}
|
||||
}
|
||||
|
||||
TileManager *Tile::manager() const
|
||||
{
|
||||
return m_tiling;
|
||||
|
|
|
@ -98,6 +98,11 @@ public:
|
|||
*/
|
||||
QList<Tile *> descendants() const;
|
||||
|
||||
/**
|
||||
* Visit all tiles descendant of this tile, recursive
|
||||
*/
|
||||
void visitDescendants(std::function<void(const Tile *child)> callback) const;
|
||||
|
||||
void resizeFromGravity(Gravity gravity, int x_root, int y_root);
|
||||
|
||||
Q_INVOKABLE void resizeByPixels(qreal delta, Qt::Edge edge);
|
||||
|
|
|
@ -1595,12 +1595,51 @@ void Workspace::updateOutputs(const QVector<Output *> &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<Window *> 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<Window *> 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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue