placementtracker: fix restoring of windows in custom tiles

There were two problems preventing it from working:
1. The placement tracker didn't handle them correctly; now it sets the custom tile mode
after moving the window to its location
2. The window code used `output()` instead of `moveResizeOutput()`, which means when KWin
just moved the window to a different screen and immediately changes the tile mode
afterwards, it would tile the window on the wrong output
This commit is contained in:
Xaver Hugl 2024-04-25 02:32:00 +02:00
parent 14c08422ca
commit 33fe211471
3 changed files with 124 additions and 10 deletions

View file

@ -10,6 +10,7 @@
#include "core/outputbackend.h"
#include "core/outputconfiguration.h"
#include "pointer_input.h"
#include "tiles/tilemanager.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
@ -42,6 +43,8 @@ private Q_SLOTS:
void testMaximizedWindowRestoredAfterEnablingOutput();
void testFullScreenWindowRestoredAfterEnablingOutput();
void testQuickTiledWindowRestoredAfterEnablingOutput();
void testCustomTiledWindowRestoredAfterEnablingOutput_data();
void testCustomTiledWindowRestoredAfterEnablingOutput();
void testWindowRestoredAfterChangingScale();
void testMaximizeStateRestoredAfterEnablingOutput_data();
void testMaximizeStateRestoredAfterEnablingOutput();
@ -526,6 +529,119 @@ void OutputChangesTest::testQuickTiledWindowRestoredAfterEnablingOutput()
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
}
void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput_data()
{
const auto outputs = kwinApp()->outputBackend()->outputs();
const size_t tileCount = workspace()->tileManager(outputs[1])->rootTile()->childTiles().size();
QTest::addColumn<size_t>("tileIndex");
for (size_t i = 0; i < tileCount; i++) {
QTest::addRow("tile %lu", i) << i;
}
}
void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput()
{
// This test verifies that a custom tiled window will be moved to
// its original output and tile when the output is re-enabled
const auto outputs = kwinApp()->outputBackend()->outputs();
// start with only one output
{
OutputConfiguration config;
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = false;
workspace()->applyOutputConfiguration(config);
}
// Create a window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
const auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
// kwin will send a configure event with the actived state.
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.wait());
const QRectF originalGeometry = window->moveResizeGeometry();
// Enable the right output
{
OutputConfiguration config;
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = true;
workspace()->applyOutputConfiguration(config);
}
QFETCH(size_t, tileIndex);
const QRectF customTileGeom = workspace()->tileManager(outputs[1])->rootTile()->childTiles()[tileIndex]->windowGeometry();
// Move the window to the right monitor and put it in the middle tile.
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
window->move(customTileGeom.topLeft() + QPointF(50, 50));
const auto geomBeforeTiling = window->moveResizeGeometry();
window->setQuickTileMode(QuickTileFlag::Custom, true);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), customTileGeom.size().toSize());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), customTileGeom.size().toSize(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), customTileGeom);
QCOMPARE(window->moveResizeGeometry(), customTileGeom);
QCOMPARE(window->output(), outputs[1]);
QCOMPARE(window->quickTileMode(), QuickTileFlag::Custom);
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Custom);
QCOMPARE(window->geometryRestore(), geomBeforeTiling);
// Disable the right output.
{
OutputConfiguration config;
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = false;
workspace()->applyOutputConfiguration(config);
}
// The window will be moved to the left monitor, and the original geometry restored
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), originalGeometry.size().toSize());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), originalGeometry.size().toSize(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), originalGeometry);
QCOMPARE(window->moveResizeGeometry(), originalGeometry);
QCOMPARE(window->output(), outputs[0]);
QCOMPARE(window->quickTileMode(), QuickTileFlag::None);
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::None);
// Enable the right monitor again
{
OutputConfiguration config;
auto changeSet = config.changeSet(outputs[1]);
changeSet->enabled = true;
workspace()->applyOutputConfiguration(config);
}
// The window will be moved back to the right monitor, and put in the correct tile
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), customTileGeom.size().toSize());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), customTileGeom.size().toSize(), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), customTileGeom);
QCOMPARE(window->moveResizeGeometry(), customTileGeom);
QCOMPARE(window->output(), outputs[1]);
QCOMPARE(window->quickTileMode(), QuickTileFlag::Custom);
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Custom);
QCOMPARE(window->geometryRestore(), geomBeforeTiling);
}
void OutputChangesTest::testWindowRestoredAfterChangingScale()
{
// This test verifies that a window will be moved to its original position after changing the scale of an output

View file

@ -119,10 +119,15 @@ void PlacementTracker::restore(const QString &key)
// setting the desired quick tile mode
// TODO fix this more properly
window->setQuickTileMode(QuickTileFlag::None, true);
window->setQuickTileMode(newData.quickTile, true);
if (newData.quickTile != QuickTileFlag::Custom) {
window->setQuickTileMode(newData.quickTile, true);
}
window->setMaximize(newData.maximize & MaximizeMode::MaximizeVertical, newData.maximize & MaximizeMode::MaximizeHorizontal);
window->setFullScreen(newData.fullscreen);
window->moveResize(newData.geometry);
if (newData.quickTile == QuickTileFlag::Custom) {
window->setQuickTileMode(QuickTileFlag::Custom, true);
}
window->setGeometryRestore(newData.geometryRestore);
window->setFullscreenGeometryRestore(newData.fullscreenGeometryRestore);
m_lastRestoreData[window] = dataForWindow(window);

View file

@ -3559,19 +3559,12 @@ void Window::setQuickTileMode(QuickTileMode mode, bool keyboard)
}
} else if (mode == QuickTileMode(QuickTileFlag::Custom)) {
// Custom tileMode is the only one that gets immediately assigned without a roundtrip
Tile *tile = nullptr;
if (keyboard) {
tile = workspace()->tileManager(output())->bestTileForPosition(moveResizeGeometry().center());
} else {
Output *output = workspace()->outputAt(Cursors::self()->mouse()->pos());
tile = workspace()->tileManager(output)->bestTileForPosition(Cursors::self()->mouse()->pos());
}
m_requestedQuickTileMode = mode;
setTile(tile);
setTile(workspace()->tileManager(workspace()->outputAt(whichScreen))->bestTileForPosition(whichScreen));
// Don't go into setTileMode as custom tiles don't go trough configure events
return;
} else {
Tile *newTile = workspace()->tileManager(output())->quickTile(m_requestedQuickTileMode);
Tile *newTile = workspace()->tileManager(moveResizeOutput())->quickTile(m_requestedQuickTileMode);
if (newTile) {
moveResize(newTile->absoluteGeometry());
} else if (tile()) {