[wayland] Implement window geometry more properly
Summary: So far the window geometry from xdg-shell wasn't implemented as it should be. A toplevel must have two geometries assigned to it - frame and buffer. The frame geometry describes bounds of the client excluding server-side and client-side drop-shadows. The buffer geometry specifies rectangle on the screen occupied by the main surface. State and geometry handling in XdgShellClient is still a bit broken. This change doesn't intend to fix that, it must be done in another patch asap. Test Plan: New tests pass. Reviewers: #kwin, davidedmundson Reviewed By: #kwin, davidedmundson Subscribers: davidedmundson, romangg, kwin Tags: #kwin Maniphest Tasks: T10867 Differential Revision: https://phabricator.kde.org/D24455
This commit is contained in:
parent
b7dbe0e88a
commit
9f7a856d23
12 changed files with 471 additions and 90 deletions
|
@ -38,6 +38,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <KWayland/Client/compositor.h>
|
#include <KWayland/Client/compositor.h>
|
||||||
#include <KWayland/Client/output.h>
|
#include <KWayland/Client/output.h>
|
||||||
#include <KWayland/Client/server_decoration.h>
|
#include <KWayland/Client/server_decoration.h>
|
||||||
|
#include <KWayland/Client/subsurface.h>
|
||||||
#include <KWayland/Client/surface.h>
|
#include <KWayland/Client/surface.h>
|
||||||
#include <KWayland/Client/xdgshell.h>
|
#include <KWayland/Client/xdgshell.h>
|
||||||
#include <KWayland/Client/xdgdecoration.h>
|
#include <KWayland/Client/xdgdecoration.h>
|
||||||
|
@ -114,7 +115,12 @@ private Q_SLOTS:
|
||||||
void testXdgInitiallyMaximised();
|
void testXdgInitiallyMaximised();
|
||||||
void testXdgInitiallyFullscreen();
|
void testXdgInitiallyFullscreen();
|
||||||
void testXdgInitiallyMinimized();
|
void testXdgInitiallyMinimized();
|
||||||
void testXdgWindowGeometry();
|
void testXdgWindowGeometryIsntSet();
|
||||||
|
void testXdgWindowGeometryAttachBuffer();
|
||||||
|
void testXdgWindowGeometryAttachSubSurface();
|
||||||
|
void testXdgWindowGeometryInteractiveResize();
|
||||||
|
void testXdgWindowGeometryFullScreen();
|
||||||
|
void testXdgWindowGeometryMaximize();
|
||||||
};
|
};
|
||||||
|
|
||||||
void TestXdgShellClient::initTestCase()
|
void TestXdgShellClient::initTestCase()
|
||||||
|
@ -1297,40 +1303,338 @@ void TestXdgShellClient::testXdgInitiallyMinimized()
|
||||||
QVERIFY(c->isMinimized());
|
QVERIFY(c->isMinimized());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestXdgShellClient::testXdgWindowGeometry()
|
void TestXdgShellClient::testXdgWindowGeometryIsntSet()
|
||||||
{
|
{
|
||||||
|
// This test verifies that the effective window geometry corresponds to the
|
||||||
|
// bounding rectangle of the main surface and its sub-surfaces if no window
|
||||||
|
// geometry is set by the client.
|
||||||
|
|
||||||
QScopedPointer<Surface> surface(Test::createSurface());
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data(), nullptr, Test::CreationSetup::CreateOnly));
|
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||||
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
|
XdgShellClient *client = Test::renderAndWaitForShown(surface.data(), QSize(200, 100), Qt::red);
|
||||||
|
QVERIFY(client);
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
|
const QPoint oldPosition = client->pos();
|
||||||
|
|
||||||
|
QSignalSpy geometryChangedSpy(client, &AbstractClient::geometryChanged);
|
||||||
|
QVERIFY(geometryChangedSpy.isValid());
|
||||||
|
Test::render(surface.data(), QSize(100, 50), Qt::blue);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->frameGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(100, 50));
|
||||||
|
QCOMPARE(client->bufferGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(100, 50));
|
||||||
|
|
||||||
|
QScopedPointer<Surface> childSurface(Test::createSurface());
|
||||||
|
QScopedPointer<SubSurface> subSurface(Test::createSubSurface(childSurface.data(), surface.data()));
|
||||||
|
QVERIFY(subSurface);
|
||||||
|
subSurface->setPosition(QPoint(-20, -10));
|
||||||
|
Test::render(childSurface.data(), QSize(100, 50), Qt::blue);
|
||||||
surface->commit(Surface::CommitFlag::None);
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->frameGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(120, 60));
|
||||||
|
QCOMPARE(client->bufferGeometry().topLeft(), oldPosition + QPoint(20, 10));
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(100, 50));
|
||||||
|
}
|
||||||
|
|
||||||
configureRequestedSpy.wait();
|
void TestXdgShellClient::testXdgWindowGeometryAttachBuffer()
|
||||||
shellSurface->ackConfigure(configureRequestedSpy.first()[2].toUInt());
|
{
|
||||||
|
// This test verifies that the effective window geometry remains the same when
|
||||||
|
// a new buffer is attached and xdg_surface.set_window_geometry is not called
|
||||||
|
// again. Notice that the window geometry must remain the same even if the new
|
||||||
|
// buffer is smaller.
|
||||||
|
|
||||||
// Create a 160x140 window in with a margin of 10(left), 20(top), 30(right), 40(bottom). Giving a total buffer size 200, 100
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
shellSurface->setWindowGeometry(QRect(10, 20, 160, 40));
|
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||||
auto c = Test::renderAndWaitForShown(surface.data(), QSize(200,100), Qt::blue);
|
XdgShellClient *client = Test::renderAndWaitForShown(surface.data(), QSize(200, 100), Qt::red);
|
||||||
configureRequestedSpy.wait(); //window activated after being shown
|
QVERIFY(client);
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
QSignalSpy geometryChangedSpy(c, &XdgShellClient::geometryChanged);
|
const QPoint oldPosition = client->pos();
|
||||||
// resize to 300,200 in kwin terms
|
|
||||||
c->setFrameGeometry(QRect(100, 100, 300, 200));
|
QSignalSpy geometryChangedSpy(client, &AbstractClient::geometryChanged);
|
||||||
|
QVERIFY(geometryChangedSpy.isValid());
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 180, 80));
|
||||||
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->frameGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
QCOMPARE(client->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
|
Test::render(surface.data(), QSize(100, 50), Qt::blue);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->frameGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
QCOMPARE(client->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(100, 50));
|
||||||
|
|
||||||
|
shellSurface->setWindowGeometry(QRect(5, 5, 90, 40));
|
||||||
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->frameGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(90, 40));
|
||||||
|
QCOMPARE(client->bufferGeometry().topLeft(), oldPosition - QPoint(5, 5));
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(100, 50));
|
||||||
|
|
||||||
|
shellSurface.reset();
|
||||||
|
QVERIFY(Test::waitForWindowDestroyed(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestXdgShellClient::testXdgWindowGeometryAttachSubSurface()
|
||||||
|
{
|
||||||
|
// This test verifies that the effective window geometry remains the same
|
||||||
|
// when a new sub-surface is added and xdg_surface.set_window_geometry is
|
||||||
|
// not called again.
|
||||||
|
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||||
|
XdgShellClient *client = Test::renderAndWaitForShown(surface.data(), QSize(200, 100), Qt::red);
|
||||||
|
QVERIFY(client);
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
|
const QPoint oldPosition = client->pos();
|
||||||
|
|
||||||
|
QSignalSpy geometryChangedSpy(client, &XdgShellClient::geometryChanged);
|
||||||
|
QVERIFY(geometryChangedSpy.isValid());
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 180, 80));
|
||||||
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->frameGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
QCOMPARE(client->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
|
QScopedPointer<Surface> childSurface(Test::createSurface());
|
||||||
|
QScopedPointer<SubSurface> subSurface(Test::createSubSurface(childSurface.data(), surface.data()));
|
||||||
|
QVERIFY(subSurface);
|
||||||
|
subSurface->setPosition(QPoint(-20, -20));
|
||||||
|
Test::render(childSurface.data(), QSize(100, 50), Qt::blue);
|
||||||
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QCOMPARE(client->frameGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
QCOMPARE(client->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
|
shellSurface->setWindowGeometry(QRect(-15, -15, 50, 40));
|
||||||
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->frameGeometry().topLeft(), oldPosition);
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(50, 40));
|
||||||
|
QCOMPARE(client->bufferGeometry().topLeft(), oldPosition - QPoint(-15, -15));
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestXdgShellClient::testXdgWindowGeometryInteractiveResize()
|
||||||
|
{
|
||||||
|
// This test verifies that correct window geometry is provided along each
|
||||||
|
// configure event when an xdg-shell is being interactively resized.
|
||||||
|
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||||
|
XdgShellClient *client = Test::renderAndWaitForShown(surface.data(), QSize(200, 100), Qt::red);
|
||||||
|
QVERIFY(client);
|
||||||
|
QVERIFY(client->isActive());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
|
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
|
||||||
|
QVERIFY(configureRequestedSpy.isValid());
|
||||||
QVERIFY(configureRequestedSpy.wait());
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
// requested geometry should not include the margins we had above
|
QCOMPARE(configureRequestedSpy.count(), 1);
|
||||||
const QSize requestedSize = configureRequestedSpy.last()[0].value<QSize>();
|
|
||||||
QCOMPARE(requestedSize, QSize(300, 200) - QSize(10 + 30, 20 + 40));
|
|
||||||
shellSurface->ackConfigure(configureRequestedSpy.last()[2].toUInt());
|
|
||||||
Test::render(surface.data(), requestedSize + QSize(10 + 30, 20 + 40), Qt::blue);
|
|
||||||
geometryChangedSpy.wait();
|
|
||||||
|
|
||||||
// kwin's concept of geometry should remain the same
|
QSignalSpy geometryChangedSpy(client, &AbstractClient::geometryChanged);
|
||||||
QCOMPARE(c->frameGeometry(), QRect(100, 100, 300, 200));
|
QVERIFY(geometryChangedSpy.isValid());
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 180, 80));
|
||||||
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
|
||||||
c->setFullScreen(true);
|
QSignalSpy clientStartMoveResizedSpy(client, &AbstractClient::clientStartUserMovedResized);
|
||||||
configureRequestedSpy.wait();
|
QVERIFY(clientStartMoveResizedSpy.isValid());
|
||||||
// when full screen, the window geometry (i.e without margins) should fill the screen
|
QSignalSpy clientStepUserMovedResizedSpy(client, &AbstractClient::clientStepUserMovedResized);
|
||||||
const QSize requestedFullScreenSize = configureRequestedSpy.last()[0].value<QSize>();
|
QVERIFY(clientStepUserMovedResizedSpy.isValid());
|
||||||
QCOMPARE(requestedFullScreenSize, QSize(1280, 1024));
|
QSignalSpy clientFinishUserMovedResizedSpy(client, &AbstractClient::clientFinishUserMovedResized);
|
||||||
|
QVERIFY(clientFinishUserMovedResizedSpy.isValid());
|
||||||
|
|
||||||
|
// Start interactively resizing the client.
|
||||||
|
QCOMPARE(workspace()->moveResizeClient(), nullptr);
|
||||||
|
workspace()->slotWindowResize();
|
||||||
|
QCOMPARE(workspace()->moveResizeClient(), client);
|
||||||
|
QCOMPARE(clientStartMoveResizedSpy.count(), 1);
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 2);
|
||||||
|
XdgShellSurface::States states = configureRequestedSpy.last().at(1).value<XdgShellSurface::States>();
|
||||||
|
QVERIFY(states.testFlag(XdgShellSurface::State::Resizing));
|
||||||
|
|
||||||
|
// Go right.
|
||||||
|
QPoint cursorPos = KWin::Cursor::pos();
|
||||||
|
client->keyPressEvent(Qt::Key_Right);
|
||||||
|
client->updateMoveResize(KWin::Cursor::pos());
|
||||||
|
QCOMPARE(KWin::Cursor::pos(), cursorPos + QPoint(8, 0));
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 3);
|
||||||
|
states = configureRequestedSpy.last().at(1).value<XdgShellSurface::States>();
|
||||||
|
QVERIFY(states.testFlag(XdgShellSurface::State::Resizing));
|
||||||
|
QCOMPARE(configureRequestedSpy.last().at(0).toSize(), QSize(188, 80));
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 188, 80));
|
||||||
|
shellSurface->ackConfigure(configureRequestedSpy.last().at(2).value<quint32>());
|
||||||
|
Test::render(surface.data(), QSize(208, 100), Qt::blue);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(clientStepUserMovedResizedSpy.count(), 1);
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(208, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(188, 80));
|
||||||
|
|
||||||
|
// Go down.
|
||||||
|
cursorPos = KWin::Cursor::pos();
|
||||||
|
client->keyPressEvent(Qt::Key_Down);
|
||||||
|
client->updateMoveResize(KWin::Cursor::pos());
|
||||||
|
QCOMPARE(KWin::Cursor::pos(), cursorPos + QPoint(0, 8));
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 4);
|
||||||
|
states = configureRequestedSpy.last().at(1).value<XdgShellSurface::States>();
|
||||||
|
QVERIFY(states.testFlag(XdgShellSurface::State::Resizing));
|
||||||
|
QCOMPARE(configureRequestedSpy.last().at(0).toSize(), QSize(188, 88));
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 188, 88));
|
||||||
|
shellSurface->ackConfigure(configureRequestedSpy.last().at(2).value<quint32>());
|
||||||
|
Test::render(surface.data(), QSize(208, 108), Qt::blue);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(clientStepUserMovedResizedSpy.count(), 2);
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(208, 108));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(188, 88));
|
||||||
|
|
||||||
|
// Finish resizing the client.
|
||||||
|
client->keyPressEvent(Qt::Key_Enter);
|
||||||
|
QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1);
|
||||||
|
QCOMPARE(workspace()->moveResizeClient(), nullptr);
|
||||||
|
#if 0
|
||||||
|
QEXPECT_FAIL("", "XdgShellClient currently doesn't send final configure event", Abort);
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 5);
|
||||||
|
states = configureRequestedSpy.last().at(1).value<XdgShellSurface::States>();
|
||||||
|
QVERIFY(!states.testFlag(XdgShellSurface::State::Resizing));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
shellSurface.reset();
|
||||||
|
QVERIFY(Test::waitForWindowDestroyed(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestXdgShellClient::testXdgWindowGeometryFullScreen()
|
||||||
|
{
|
||||||
|
// This test verifies that an xdg-shell receives correct window geometry when
|
||||||
|
// its fullscreen state gets changed.
|
||||||
|
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||||
|
XdgShellClient *client = Test::renderAndWaitForShown(surface.data(), QSize(200, 100), Qt::red);
|
||||||
|
QVERIFY(client);
|
||||||
|
QVERIFY(client->isActive());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
|
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
|
||||||
|
QVERIFY(configureRequestedSpy.isValid());
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 1);
|
||||||
|
|
||||||
|
QSignalSpy geometryChangedSpy(client, &AbstractClient::geometryChanged);
|
||||||
|
QVERIFY(geometryChangedSpy.isValid());
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 180, 80));
|
||||||
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
|
||||||
|
workspace()->slotWindowFullScreen();
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 2);
|
||||||
|
QCOMPARE(configureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
|
||||||
|
XdgShellSurface::States states = configureRequestedSpy.last().at(1).value<XdgShellSurface::States>();
|
||||||
|
QVERIFY(states.testFlag(XdgShellSurface::State::Fullscreen));
|
||||||
|
shellSurface->setWindowGeometry(QRect(0, 0, 1280, 1024));
|
||||||
|
shellSurface->ackConfigure(configureRequestedSpy.last().at(2).value<quint32>());
|
||||||
|
Test::render(surface.data(), QSize(1280, 1024), Qt::blue);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(1280, 1024));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(1280, 1024));
|
||||||
|
|
||||||
|
workspace()->slotWindowFullScreen();
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 3);
|
||||||
|
QCOMPARE(configureRequestedSpy.last().at(0).toSize(), QSize(180, 80));
|
||||||
|
states = configureRequestedSpy.last().at(1).value<XdgShellSurface::States>();
|
||||||
|
QVERIFY(!states.testFlag(XdgShellSurface::State::Fullscreen));
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 180, 80));
|
||||||
|
shellSurface->ackConfigure(configureRequestedSpy.last().at(2).value<quint32>());
|
||||||
|
Test::render(surface.data(), QSize(200, 100), Qt::blue);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
|
||||||
|
shellSurface.reset();
|
||||||
|
QVERIFY(Test::waitForWindowDestroyed(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestXdgShellClient::testXdgWindowGeometryMaximize()
|
||||||
|
{
|
||||||
|
// This test verifies that an xdg-shell receives correct window geometry when
|
||||||
|
// its maximized state gets changed.
|
||||||
|
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||||
|
XdgShellClient *client = Test::renderAndWaitForShown(surface.data(), QSize(200, 100), Qt::red);
|
||||||
|
QVERIFY(client);
|
||||||
|
QVERIFY(client->isActive());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(200, 100));
|
||||||
|
|
||||||
|
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
|
||||||
|
QVERIFY(configureRequestedSpy.isValid());
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 1);
|
||||||
|
|
||||||
|
QSignalSpy geometryChangedSpy(client, &AbstractClient::geometryChanged);
|
||||||
|
QVERIFY(geometryChangedSpy.isValid());
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 180, 80));
|
||||||
|
surface->commit(Surface::CommitFlag::None);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
|
||||||
|
workspace()->slotWindowMaximize();
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 2);
|
||||||
|
QCOMPARE(configureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
|
||||||
|
XdgShellSurface::States states = configureRequestedSpy.last().at(1).value<XdgShellSurface::States>();
|
||||||
|
QVERIFY(states.testFlag(XdgShellSurface::State::Maximized));
|
||||||
|
shellSurface->setWindowGeometry(QRect(0, 0, 1280, 1024));
|
||||||
|
shellSurface->ackConfigure(configureRequestedSpy.last().at(2).value<quint32>());
|
||||||
|
Test::render(surface.data(), QSize(1280, 1024), Qt::blue);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(1280, 1024));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(1280, 1024));
|
||||||
|
|
||||||
|
workspace()->slotWindowMaximize();
|
||||||
|
QVERIFY(configureRequestedSpy.wait());
|
||||||
|
QCOMPARE(configureRequestedSpy.count(), 3);
|
||||||
|
QCOMPARE(configureRequestedSpy.last().at(0).toSize(), QSize(180, 80));
|
||||||
|
states = configureRequestedSpy.last().at(1).value<XdgShellSurface::States>();
|
||||||
|
QVERIFY(!states.testFlag(XdgShellSurface::State::Maximized));
|
||||||
|
shellSurface->setWindowGeometry(QRect(10, 10, 180, 80));
|
||||||
|
shellSurface->ackConfigure(configureRequestedSpy.last().at(2).value<quint32>());
|
||||||
|
Test::render(surface.data(), QSize(200, 100), Qt::blue);
|
||||||
|
QVERIFY(geometryChangedSpy.wait());
|
||||||
|
QCOMPARE(client->bufferGeometry().size(), QSize(200, 100));
|
||||||
|
QCOMPARE(client->frameGeometry().size(), QSize(180, 80));
|
||||||
|
|
||||||
|
shellSurface.reset();
|
||||||
|
QVERIFY(Test::waitForWindowDestroyed(client));
|
||||||
}
|
}
|
||||||
|
|
||||||
WAYLANDTEST_MAIN(TestXdgShellClient)
|
WAYLANDTEST_MAIN(TestXdgShellClient)
|
||||||
|
|
|
@ -94,6 +94,7 @@ void Deleted::copyToDeleted(Toplevel* c)
|
||||||
{
|
{
|
||||||
Q_ASSERT(dynamic_cast< Deleted* >(c) == nullptr);
|
Q_ASSERT(dynamic_cast< Deleted* >(c) == nullptr);
|
||||||
Toplevel::copyToDeleted(c);
|
Toplevel::copyToDeleted(c);
|
||||||
|
m_bufferGeometry = c->bufferGeometry();
|
||||||
m_bufferScale = c->bufferScale();
|
m_bufferScale = c->bufferScale();
|
||||||
desk = c->desktop();
|
desk = c->desktop();
|
||||||
m_desktops = c->desktops();
|
m_desktops = c->desktops();
|
||||||
|
@ -163,6 +164,11 @@ void Deleted::unrefWindow()
|
||||||
deleteLater();
|
deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect Deleted::bufferGeometry() const
|
||||||
|
{
|
||||||
|
return m_bufferGeometry;
|
||||||
|
}
|
||||||
|
|
||||||
qreal Deleted::bufferScale() const
|
qreal Deleted::bufferScale() const
|
||||||
{
|
{
|
||||||
return m_bufferScale;
|
return m_bufferScale;
|
||||||
|
|
|
@ -43,6 +43,7 @@ public:
|
||||||
void refWindow();
|
void refWindow();
|
||||||
void unrefWindow();
|
void unrefWindow();
|
||||||
void discard();
|
void discard();
|
||||||
|
QRect bufferGeometry() const override;
|
||||||
qreal bufferScale() const override;
|
qreal bufferScale() const override;
|
||||||
int desktop() const override;
|
int desktop() const override;
|
||||||
QStringList activities() const override;
|
QStringList activities() const override;
|
||||||
|
@ -198,6 +199,8 @@ private:
|
||||||
void addTransientFor(AbstractClient *parent);
|
void addTransientFor(AbstractClient *parent);
|
||||||
void removeTransientFor(Deleted *parent);
|
void removeTransientFor(Deleted *parent);
|
||||||
|
|
||||||
|
QRect m_bufferGeometry;
|
||||||
|
|
||||||
int delete_refcount;
|
int delete_refcount;
|
||||||
int desk;
|
int desk;
|
||||||
QStringList activityList;
|
QStringList activityList;
|
||||||
|
|
|
@ -94,6 +94,11 @@ bool InternalClient::eventFilter(QObject *watched, QEvent *event)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect InternalClient::bufferGeometry() const
|
||||||
|
{
|
||||||
|
return frameGeometry() - frameMargins();
|
||||||
|
}
|
||||||
|
|
||||||
QStringList InternalClient::activities() const
|
QStringList InternalClient::activities() const
|
||||||
{
|
{
|
||||||
return QStringList();
|
return QStringList();
|
||||||
|
|
|
@ -35,6 +35,7 @@ public:
|
||||||
|
|
||||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
|
QRect bufferGeometry() const override;
|
||||||
QStringList activities() const override;
|
QStringList activities() const override;
|
||||||
void blockActivityUpdates(bool b = true) override;
|
void blockActivityUpdates(bool b = true) override;
|
||||||
qreal bufferScale() const override;
|
qreal bufferScale() const override;
|
||||||
|
|
16
toplevel.h
16
toplevel.h
|
@ -79,6 +79,13 @@ class KWIN_EXPORT Toplevel : public QObject
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(QRect geometry READ frameGeometry NOTIFY geometryChanged)
|
Q_PROPERTY(QRect geometry READ frameGeometry NOTIFY geometryChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This property holds rectangle that the pixmap or buffer of this Toplevel
|
||||||
|
* occupies on the screen. This rectangle includes invisible portions of the
|
||||||
|
* client, e.g. client-side drop shadows, etc.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QRect bufferGeometry READ bufferGeometry NOTIFY geometryChanged)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This property holds the geometry of the Toplevel, excluding invisible
|
* This property holds the geometry of the Toplevel, excluding invisible
|
||||||
* portions, e.g. server-side and client-side drop-shadows, etc.
|
* portions, e.g. server-side and client-side drop-shadows, etc.
|
||||||
|
@ -298,6 +305,15 @@ public:
|
||||||
* @return a unique identifier for the Toplevel. On X11 same as @ref window
|
* @return a unique identifier for the Toplevel. On X11 same as @ref window
|
||||||
*/
|
*/
|
||||||
virtual quint32 windowId() const;
|
virtual quint32 windowId() const;
|
||||||
|
/**
|
||||||
|
* Returns the geometry of the pixmap or buffer attached to this Toplevel.
|
||||||
|
*
|
||||||
|
* For X11 clients, this method returns server-side geometry of the Toplevel.
|
||||||
|
*
|
||||||
|
* For Wayland clients, this method returns rectangle that the main surface
|
||||||
|
* occupies on the screen, in global screen coordinates.
|
||||||
|
*/
|
||||||
|
virtual QRect bufferGeometry() const = 0;
|
||||||
/**
|
/**
|
||||||
* Returns the geometry of the Toplevel, excluding invisible portions, e.g.
|
* Returns the geometry of the Toplevel, excluding invisible portions, e.g.
|
||||||
* server-side and client-side drop shadows, etc.
|
* server-side and client-side drop shadows, etc.
|
||||||
|
|
|
@ -128,6 +128,11 @@ bool Unmanaged::hasScheduledRelease() const
|
||||||
return m_scheduledRelease;
|
return m_scheduledRelease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect Unmanaged::bufferGeometry() const
|
||||||
|
{
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
int Unmanaged::desktop() const
|
int Unmanaged::desktop() const
|
||||||
{
|
{
|
||||||
return NET::OnAllDesktops; // TODO for some window types should be the current desktop?
|
return NET::OnAllDesktops; // TODO for some window types should be the current desktop?
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
bool track(xcb_window_t w);
|
bool track(xcb_window_t w);
|
||||||
bool hasScheduledRelease() const;
|
bool hasScheduledRelease() const;
|
||||||
static void deleteUnmanaged(Unmanaged* c);
|
static void deleteUnmanaged(Unmanaged* c);
|
||||||
|
QRect bufferGeometry() const override;
|
||||||
int desktop() const override;
|
int desktop() const override;
|
||||||
QStringList activities() const override;
|
QStringList activities() const override;
|
||||||
QVector<VirtualDesktop *> desktops() const override;
|
QVector<VirtualDesktop *> desktops() const override;
|
||||||
|
|
|
@ -1971,6 +1971,11 @@ xcb_window_t X11Client::frameId() const
|
||||||
return m_frame;
|
return m_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect X11Client::bufferGeometry() const
|
||||||
|
{
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
Xcb::Property X11Client::fetchShowOnScreenEdge() const
|
Xcb::Property X11Client::fetchShowOnScreenEdge() const
|
||||||
{
|
{
|
||||||
return Xcb::Property(false, window(), atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 0, 1);
|
return Xcb::Property(false, window(), atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 0, 1);
|
||||||
|
|
|
@ -89,6 +89,8 @@ public:
|
||||||
xcb_window_t inputId() const { return m_decoInputExtent; }
|
xcb_window_t inputId() const { return m_decoInputExtent; }
|
||||||
xcb_window_t frameId() const override;
|
xcb_window_t frameId() const override;
|
||||||
|
|
||||||
|
QRect bufferGeometry() const override;
|
||||||
|
|
||||||
bool isTransient() const override;
|
bool isTransient() const override;
|
||||||
bool groupTransient() const override;
|
bool groupTransient() const override;
|
||||||
bool wasOriginallyGroupTransient() const;
|
bool wasOriginallyGroupTransient() const;
|
||||||
|
|
|
@ -47,6 +47,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <KWayland/Server/server_decoration_interface.h>
|
#include <KWayland/Server/server_decoration_interface.h>
|
||||||
#include <KWayland/Server/server_decoration_palette_interface.h>
|
#include <KWayland/Server/server_decoration_palette_interface.h>
|
||||||
#include <KWayland/Server/shadow_interface.h>
|
#include <KWayland/Server/shadow_interface.h>
|
||||||
|
#include <KWayland/Server/subcompositor_interface.h>
|
||||||
#include <KWayland/Server/surface_interface.h>
|
#include <KWayland/Server/surface_interface.h>
|
||||||
#include <KWayland/Server/xdgdecoration_interface.h>
|
#include <KWayland/Server/xdgdecoration_interface.h>
|
||||||
|
|
||||||
|
@ -92,7 +93,10 @@ void XdgShellClient::init()
|
||||||
createWindowId();
|
createWindowId();
|
||||||
setupCompositing();
|
setupCompositing();
|
||||||
updateIcon();
|
updateIcon();
|
||||||
doSetGeometry(QRect(QPoint(0, 0), m_clientSize));
|
|
||||||
|
// TODO: Initialize with null rect.
|
||||||
|
geom = QRect(0, 0, -1, -1);
|
||||||
|
m_windowGeometry = QRect(0, 0, -1, -1);
|
||||||
|
|
||||||
if (waylandServer()->inputMethodConnection() == surface()->client()) {
|
if (waylandServer()->inputMethodConnection() == surface()->client()) {
|
||||||
m_windowType = NET::OnScreenDisplay;
|
m_windowType = NET::OnScreenDisplay;
|
||||||
|
@ -129,6 +133,7 @@ void XdgShellClient::init()
|
||||||
connect(m_xdgShellSurface, &XdgShellSurfaceInterface::fullscreenChanged, this, &XdgShellClient::handleFullScreenRequested);
|
connect(m_xdgShellSurface, &XdgShellSurfaceInterface::fullscreenChanged, this, &XdgShellClient::handleFullScreenRequested);
|
||||||
connect(m_xdgShellSurface, &XdgShellSurfaceInterface::windowMenuRequested, this, &XdgShellClient::handleWindowMenuRequested);
|
connect(m_xdgShellSurface, &XdgShellSurfaceInterface::windowMenuRequested, this, &XdgShellClient::handleWindowMenuRequested);
|
||||||
connect(m_xdgShellSurface, &XdgShellSurfaceInterface::transientForChanged, this, &XdgShellClient::handleTransientForChanged);
|
connect(m_xdgShellSurface, &XdgShellSurfaceInterface::transientForChanged, this, &XdgShellClient::handleTransientForChanged);
|
||||||
|
connect(m_xdgShellSurface, &XdgShellSurfaceInterface::windowGeometryChanged, this, &XdgShellClient::handleWindowGeometryChanged);
|
||||||
|
|
||||||
auto global = static_cast<XdgShellInterface *>(m_xdgShellSurface->global());
|
auto global = static_cast<XdgShellInterface *>(m_xdgShellSurface->global());
|
||||||
connect(global, &XdgShellInterface::pingDelayed, this, &XdgShellClient::handlePingDelayed);
|
connect(global, &XdgShellInterface::pingDelayed, this, &XdgShellClient::handlePingDelayed);
|
||||||
|
@ -154,6 +159,7 @@ void XdgShellClient::init()
|
||||||
connect(m_xdgShellPopup, &XdgShellPopupInterface::configureAcknowledged, this, &XdgShellClient::handleConfigureAcknowledged);
|
connect(m_xdgShellPopup, &XdgShellPopupInterface::configureAcknowledged, this, &XdgShellClient::handleConfigureAcknowledged);
|
||||||
connect(m_xdgShellPopup, &XdgShellPopupInterface::grabRequested, this, &XdgShellClient::handleGrabRequested);
|
connect(m_xdgShellPopup, &XdgShellPopupInterface::grabRequested, this, &XdgShellClient::handleGrabRequested);
|
||||||
connect(m_xdgShellPopup, &XdgShellPopupInterface::destroyed, this, &XdgShellClient::destroyClient);
|
connect(m_xdgShellPopup, &XdgShellPopupInterface::destroyed, this, &XdgShellClient::destroyClient);
|
||||||
|
connect(m_xdgShellPopup, &XdgShellPopupInterface::windowGeometryChanged, this, &XdgShellClient::handleWindowGeometryChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set initial desktop
|
// set initial desktop
|
||||||
|
@ -289,14 +295,9 @@ void XdgShellClient::deleteClient(XdgShellClient *c)
|
||||||
delete c;
|
delete c;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize XdgShellClient::toWindowGeometry(const QSize &size) const
|
QRect XdgShellClient::bufferGeometry() const
|
||||||
{
|
{
|
||||||
QSize adjustedSize = size - QSize(borderLeft() + borderRight(), borderTop() + borderBottom());
|
return m_bufferGeometry;
|
||||||
// a client going fullscreen should have the window the contents size of the screen
|
|
||||||
if (!isFullScreen() && requestedMaximizeMode() != MaximizeFull) {
|
|
||||||
adjustedSize -= QSize(m_windowMargins.left() + m_windowMargins.right(), m_windowMargins.top() + m_windowMargins.bottom());
|
|
||||||
}
|
|
||||||
return adjustedSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList XdgShellClient::activities() const
|
QStringList XdgShellClient::activities() const
|
||||||
|
@ -312,7 +313,7 @@ QPoint XdgShellClient::clientContentPos() const
|
||||||
|
|
||||||
QSize XdgShellClient::clientSize() const
|
QSize XdgShellClient::clientSize() const
|
||||||
{
|
{
|
||||||
return m_clientSize;
|
return m_windowGeometry.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void XdgShellClient::debug(QDebug &stream) const
|
void XdgShellClient::debug(QDebug &stream) const
|
||||||
|
@ -431,8 +432,7 @@ void XdgShellClient::createDecoration(const QRect &oldGeom)
|
||||||
}
|
}
|
||||||
setDecoration(decoration);
|
setDecoration(decoration);
|
||||||
// TODO: ensure the new geometry still fits into the client area (e.g. maximized windows)
|
// TODO: ensure the new geometry still fits into the client area (e.g. maximized windows)
|
||||||
doSetGeometry(QRect(oldGeom.topLeft(), m_clientSize + (decoration ? QSize(decoration->borderLeft() + decoration->borderRight(),
|
doSetGeometry(QRect(oldGeom.topLeft(), m_windowGeometry.size() + QSize(borderLeft() + borderRight(), borderBottom() + borderTop())));
|
||||||
decoration->borderBottom() + decoration->borderTop()) : QSize())));
|
|
||||||
|
|
||||||
emit geometryShapeChanged(this, oldGeom);
|
emit geometryShapeChanged(this, oldGeom);
|
||||||
}
|
}
|
||||||
|
@ -491,10 +491,9 @@ void XdgShellClient::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_
|
||||||
}
|
}
|
||||||
|
|
||||||
const QSize requestedClientSize = newGeometry.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom());
|
const QSize requestedClientSize = newGeometry.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom());
|
||||||
const QSize requestedWindowGeometrySize = toWindowGeometry(newGeometry.size());
|
|
||||||
|
|
||||||
if (requestedClientSize == m_clientSize &&
|
if (requestedClientSize == m_windowGeometry.size() &&
|
||||||
(m_requestedClientSize.isEmpty() || requestedWindowGeometrySize == m_requestedClientSize)) {
|
(m_requestedClientSize.isEmpty() || requestedClientSize == m_requestedClientSize)) {
|
||||||
// size didn't change, and we don't need to explicitly request a new size
|
// size didn't change, and we don't need to explicitly request a new size
|
||||||
doSetGeometry(newGeometry);
|
doSetGeometry(newGeometry);
|
||||||
updateMaximizeMode(m_requestedMaximizeMode);
|
updateMaximizeMode(m_requestedMaximizeMode);
|
||||||
|
@ -504,30 +503,54 @@ void XdgShellClient::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XdgShellClient::doSetGeometry(const QRect &rect)
|
QRect XdgShellClient::determineBufferGeometry() const
|
||||||
{
|
{
|
||||||
if (geom == rect && pendingGeometryUpdate() == PendingGeometryNone) {
|
// Offset of the main surface relative to the frame rect.
|
||||||
return;
|
const int offsetX = borderLeft() - m_windowGeometry.left();
|
||||||
}
|
const int offsetY = borderTop() - m_windowGeometry.top();
|
||||||
if (!m_unmapped) {
|
|
||||||
addWorkspaceRepaint(visibleRect());
|
QRect bufferGeometry;
|
||||||
|
bufferGeometry.setX(x() + offsetX);
|
||||||
|
bufferGeometry.setY(y() + offsetY);
|
||||||
|
bufferGeometry.setSize(surface()->size());
|
||||||
|
|
||||||
|
return bufferGeometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XdgShellClient::doSetGeometry(const QRect &rect)
|
||||||
|
{
|
||||||
|
bool frameGeometryIsChanged = false;
|
||||||
|
bool bufferGeometryIsChanged = false;
|
||||||
|
|
||||||
|
if (geom != rect) {
|
||||||
geom = rect;
|
geom = rect;
|
||||||
updateWindowRules(Rules::Position | Rules::Size);
|
frameGeometryIsChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRect bufferGeometry = determineBufferGeometry();
|
||||||
|
if (m_bufferGeometry != bufferGeometry) {
|
||||||
|
m_bufferGeometry = bufferGeometry;
|
||||||
|
bufferGeometryIsChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frameGeometryIsChanged && !bufferGeometryIsChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_unmapped && m_geomMaximizeRestore.isEmpty() && !geom.isEmpty()) {
|
if (m_unmapped && m_geomMaximizeRestore.isEmpty() && !geom.isEmpty()) {
|
||||||
// use first valid geometry as restore geometry
|
// use first valid geometry as restore geometry
|
||||||
m_geomMaximizeRestore = geom;
|
m_geomMaximizeRestore = geom;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_unmapped) {
|
if (frameGeometryIsChanged) {
|
||||||
addWorkspaceRepaint(visibleRect());
|
|
||||||
}
|
|
||||||
if (hasStrut()) {
|
if (hasStrut()) {
|
||||||
workspace()->updateClientArea();
|
workspace()->updateClientArea();
|
||||||
}
|
}
|
||||||
|
updateWindowRules(Rules::Position | Rules::Size);
|
||||||
|
}
|
||||||
|
|
||||||
const auto old = geometryBeforeUpdateBlocking();
|
const auto old = geometryBeforeUpdateBlocking();
|
||||||
|
addRepaintDuringGeometryUpdates();
|
||||||
updateGeometryBeforeUpdateBlocking();
|
updateGeometryBeforeUpdateBlocking();
|
||||||
emit geometryShapeChanged(this, old);
|
emit geometryShapeChanged(this, old);
|
||||||
|
|
||||||
|
@ -536,6 +559,13 @@ void XdgShellClient::doSetGeometry(const QRect &rect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XdgShellClient::doMove(int x, int y)
|
||||||
|
{
|
||||||
|
Q_UNUSED(x)
|
||||||
|
Q_UNUSED(y)
|
||||||
|
m_bufferGeometry = determineBufferGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray XdgShellClient::windowRole() const
|
QByteArray XdgShellClient::windowRole() const
|
||||||
{
|
{
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
|
@ -1028,7 +1058,7 @@ void XdgShellClient::requestGeometry(const QRect &rect)
|
||||||
|
|
||||||
QSize size;
|
QSize size;
|
||||||
if (rect.isValid()) {
|
if (rect.isValid()) {
|
||||||
size = toWindowGeometry(rect.size());
|
size = rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom());
|
||||||
} else {
|
} else {
|
||||||
size = QSize(0, 0);
|
size = QSize(0, 0);
|
||||||
}
|
}
|
||||||
|
@ -1080,7 +1110,7 @@ void XdgShellClient::updatePendingGeometry()
|
||||||
}
|
}
|
||||||
//else serialId < m_lastAckedConfigureRequest and the state is now irrelevant and can be ignored
|
//else serialId < m_lastAckedConfigureRequest and the state is now irrelevant and can be ignored
|
||||||
}
|
}
|
||||||
doSetGeometry(QRect(position, m_clientSize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom())));
|
doSetGeometry(QRect(position, m_windowGeometry.size() + QSize(borderLeft() + borderRight(), borderTop() + borderBottom())));
|
||||||
updateMaximizeMode(maximizeMode);
|
updateMaximizeMode(maximizeMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1127,6 +1157,32 @@ void XdgShellClient::handleWindowClassChanged(const QByteArray &windowClass)
|
||||||
setDesktopFileName(windowClass);
|
setDesktopFileName(windowClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QRect subSurfaceTreeRect(const SurfaceInterface *surface, const QPoint &position = QPoint())
|
||||||
|
{
|
||||||
|
QRect rect(position, surface->size());
|
||||||
|
|
||||||
|
const QList<QPointer<SubSurfaceInterface>> subSurfaces = surface->childSubSurfaces();
|
||||||
|
for (const QPointer<SubSurfaceInterface> &subSurface : subSurfaces) {
|
||||||
|
if (Q_UNLIKELY(!subSurface)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const SurfaceInterface *child = subSurface->surface();
|
||||||
|
if (Q_UNLIKELY(!child)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rect |= subSurfaceTreeRect(child, position + subSurface->position());
|
||||||
|
}
|
||||||
|
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XdgShellClient::handleWindowGeometryChanged(const QRect &windowGeometry)
|
||||||
|
{
|
||||||
|
const QRect boundingRect = subSurfaceTreeRect(surface());
|
||||||
|
m_windowGeometry = windowGeometry & boundingRect;
|
||||||
|
m_hasWindowGeometry = true;
|
||||||
|
}
|
||||||
|
|
||||||
void XdgShellClient::handleWindowTitleChanged(const QString &title)
|
void XdgShellClient::handleWindowTitleChanged(const QString &title)
|
||||||
{
|
{
|
||||||
const QString oldSuffix = m_captionSuffix;
|
const QString oldSuffix = m_captionSuffix;
|
||||||
|
@ -1262,9 +1318,10 @@ void XdgShellClient::handleCommitted()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_clientSize = surface()->size();
|
if (!m_hasWindowGeometry) {
|
||||||
|
m_windowGeometry = subSurfaceTreeRect(surface());
|
||||||
|
}
|
||||||
|
|
||||||
updateWindowMargins();
|
|
||||||
updatePendingGeometry();
|
updatePendingGeometry();
|
||||||
|
|
||||||
setDepth((surface()->buffer()->hasAlphaChannel() && !isDesktop()) ? 32 : 24);
|
setDepth((surface()->buffer()->hasAlphaChannel() && !isDesktop()) ? 32 : 24);
|
||||||
|
@ -1303,7 +1360,8 @@ void XdgShellClient::installPlasmaShellSurface(PlasmaShellSurfaceInterface *surf
|
||||||
{
|
{
|
||||||
m_plasmaShellSurface = surface;
|
m_plasmaShellSurface = surface;
|
||||||
auto updatePosition = [this, surface] {
|
auto updatePosition = [this, surface] {
|
||||||
QRect rect = QRect(surface->position(), m_clientSize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()));
|
// That's a mis-use of doSetGeometry method. One should instead use move method.
|
||||||
|
QRect rect = QRect(surface->position(), size());
|
||||||
doSetGeometry(rect);
|
doSetGeometry(rect);
|
||||||
};
|
};
|
||||||
auto updateRole = [this, surface] {
|
auto updateRole = [this, surface] {
|
||||||
|
@ -1902,32 +1960,6 @@ void XdgShellClient::updateClientOutputs()
|
||||||
surface()->setOutputs(clientOutputs);
|
surface()->setOutputs(clientOutputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XdgShellClient::updateWindowMargins()
|
|
||||||
{
|
|
||||||
QRect windowGeometry;
|
|
||||||
QSize clientSize = m_clientSize;
|
|
||||||
|
|
||||||
if (m_xdgShellSurface) {
|
|
||||||
windowGeometry = m_xdgShellSurface->windowGeometry();
|
|
||||||
} else {
|
|
||||||
windowGeometry = m_xdgShellPopup->windowGeometry();
|
|
||||||
if (!clientSize.isValid()) {
|
|
||||||
clientSize = m_xdgShellPopup->initialSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowGeometry.isEmpty() ||
|
|
||||||
windowGeometry.width() > clientSize.width() ||
|
|
||||||
windowGeometry.height() > clientSize.height()) {
|
|
||||||
m_windowMargins = QMargins();
|
|
||||||
} else {
|
|
||||||
m_windowMargins = QMargins(windowGeometry.left(),
|
|
||||||
windowGeometry.top(),
|
|
||||||
clientSize.width() - (windowGeometry.right() + 1),
|
|
||||||
clientSize.height() - (windowGeometry.bottom() + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XdgShellClient::isPopupWindow() const
|
bool XdgShellClient::isPopupWindow() const
|
||||||
{
|
{
|
||||||
if (Toplevel::isPopupWindow()) {
|
if (Toplevel::isPopupWindow()) {
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
XdgShellClient(KWayland::Server::XdgShellPopupInterface *surface);
|
XdgShellClient(KWayland::Server::XdgShellPopupInterface *surface);
|
||||||
~XdgShellClient() override;
|
~XdgShellClient() override;
|
||||||
|
|
||||||
|
QRect bufferGeometry() const override;
|
||||||
QStringList activities() const override;
|
QStringList activities() const override;
|
||||||
QPoint clientContentPos() const override;
|
QPoint clientContentPos() const override;
|
||||||
QSize clientSize() const override;
|
QSize clientSize() const override;
|
||||||
|
@ -139,11 +140,13 @@ protected:
|
||||||
bool acceptsFocus() const override;
|
bool acceptsFocus() const override;
|
||||||
void doMinimize() override;
|
void doMinimize() override;
|
||||||
void updateCaption() override;
|
void updateCaption() override;
|
||||||
|
void doMove(int x, int y) override;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void handleConfigureAcknowledged(quint32 serial);
|
void handleConfigureAcknowledged(quint32 serial);
|
||||||
void handleTransientForChanged();
|
void handleTransientForChanged();
|
||||||
void handleWindowClassChanged(const QByteArray &windowClass);
|
void handleWindowClassChanged(const QByteArray &windowClass);
|
||||||
|
void handleWindowGeometryChanged(const QRect &windowGeometry);
|
||||||
void handleWindowTitleChanged(const QString &title);
|
void handleWindowTitleChanged(const QString &title);
|
||||||
void handleMoveRequested(KWayland::Server::SeatInterface *seat, quint32 serial);
|
void handleMoveRequested(KWayland::Server::SeatInterface *seat, quint32 serial);
|
||||||
void handleResizeRequested(KWayland::Server::SeatInterface *seat, quint32 serial, Qt::Edges edges);
|
void handleResizeRequested(KWayland::Server::SeatInterface *seat, quint32 serial, Qt::Edges edges);
|
||||||
|
@ -173,7 +176,6 @@ private:
|
||||||
void updateIcon();
|
void updateIcon();
|
||||||
bool shouldExposeToWindowManagement();
|
bool shouldExposeToWindowManagement();
|
||||||
void updateClientOutputs();
|
void updateClientOutputs();
|
||||||
void updateWindowMargins();
|
|
||||||
KWayland::Server::XdgShellSurfaceInterface::States xdgSurfaceStates() const;
|
KWayland::Server::XdgShellSurfaceInterface::States xdgSurfaceStates() const;
|
||||||
void updateShowOnScreenEdge();
|
void updateShowOnScreenEdge();
|
||||||
void updateMaximizeMode(MaximizeMode maximizeMode);
|
void updateMaximizeMode(MaximizeMode maximizeMode);
|
||||||
|
@ -184,15 +186,16 @@ private:
|
||||||
void doSetGeometry(const QRect &rect);
|
void doSetGeometry(const QRect &rect);
|
||||||
void unmap();
|
void unmap();
|
||||||
void markAsMapped();
|
void markAsMapped();
|
||||||
|
QRect determineBufferGeometry() const;
|
||||||
static void deleteClient(XdgShellClient *c);
|
static void deleteClient(XdgShellClient *c);
|
||||||
|
|
||||||
QSize toWindowGeometry(const QSize &geometry) const;
|
|
||||||
|
|
||||||
KWayland::Server::XdgShellSurfaceInterface *m_xdgShellSurface;
|
KWayland::Server::XdgShellSurfaceInterface *m_xdgShellSurface;
|
||||||
KWayland::Server::XdgShellPopupInterface *m_xdgShellPopup;
|
KWayland::Server::XdgShellPopupInterface *m_xdgShellPopup;
|
||||||
|
|
||||||
// size of the last buffer
|
QRect m_bufferGeometry;
|
||||||
QSize m_clientSize;
|
QRect m_windowGeometry;
|
||||||
|
bool m_hasWindowGeometry = false;
|
||||||
|
|
||||||
// last size we requested or empty if we haven't sent an explicit request to the client
|
// last size we requested or empty if we haven't sent an explicit request to the client
|
||||||
// if empty the client should choose their own default size
|
// if empty the client should choose their own default size
|
||||||
QSize m_requestedClientSize = QSize(0, 0);
|
QSize m_requestedClientSize = QSize(0, 0);
|
||||||
|
@ -255,8 +258,6 @@ private:
|
||||||
QString m_captionSuffix;
|
QString m_captionSuffix;
|
||||||
QHash<qint32, PingReason> m_pingSerials;
|
QHash<qint32, PingReason> m_pingSerials;
|
||||||
|
|
||||||
QMargins m_windowMargins;
|
|
||||||
|
|
||||||
bool m_isInitialized = false;
|
bool m_isInitialized = false;
|
||||||
|
|
||||||
friend class Workspace;
|
friend class Workspace;
|
||||||
|
|
Loading…
Reference in a new issue