diff --git a/autotests/integration/shell_client_test.cpp b/autotests/integration/shell_client_test.cpp --- a/autotests/integration/shell_client_test.cpp +++ b/autotests/integration/shell_client_test.cpp @@ -109,6 +109,7 @@ void testXdgInitialState(); void testXdgInitiallyMaximised(); void testXdgInitiallyMinimized(); + void testXdgWindowGeometry(); }; void TestShellClient::initTestCase() @@ -1463,6 +1464,41 @@ QVERIFY(c->isMinimized()); } +void TestShellClient::testXdgWindowGeometry() +{ + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createXdgShellStableSurface(surface.data(), nullptr, Test::CreationSetup::CreateOnly)); + QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested); + surface->commit(Surface::CommitFlag::None); + + configureRequestedSpy.wait(); + shellSurface->ackConfigure(configureRequestedSpy.first()[2].toUInt()); + + // Create a 160x140 window in with a margin of 10(left), 20(top), 30(right), 40(bottom). Giving a total buffer size 200, 100 + shellSurface->setWindowGeometry(QRect(10, 20, 160, 40)); + auto c = Test::renderAndWaitForShown(surface.data(), QSize(200,100), Qt::blue); + configureRequestedSpy.wait(); //window activated after being shown + + QSignalSpy geometryChangedSpy(c, &ShellClient::geometryChanged); + // resize to 300,200 in kwin terms + c->setGeometry(QRect(100, 100, 300, 200)); + QVERIFY(configureRequestedSpy.wait()); + // requested geometry should not include the margins we had above + const QSize requestedSize = configureRequestedSpy.last()[0].value(); + 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 + QCOMPARE(c->geometry(), QRect(100, 100, 300, 200)); + + c->setFullScreen(true); + configureRequestedSpy.wait(); + // when full screen, the window geometry (i.e without margins) should fill the screen + const QSize requestedFullScreenSize = configureRequestedSpy.last()[0].value(); + QCOMPARE(requestedFullScreenSize, QSize(1280, 1024)); +} WAYLANDTEST_MAIN(TestShellClient) #include "shell_client_test.moc" diff --git a/shell_client.h b/shell_client.h --- a/shell_client.h +++ b/shell_client.h @@ -214,14 +214,17 @@ void setTransient(); bool shouldExposeToWindowManagement(); void updateClientOutputs(); + void updateWindowMargins(); KWayland::Server::XdgShellSurfaceInterface::States xdgSurfaceStates() const; void updateShowOnScreenEdge(); void updateMaximizeMode(MaximizeMode maximizeMode); // called on surface commit and processes all m_pendingConfigureRequests up to m_lastAckedConfigureReqest void updatePendingGeometry(); QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSize popupSize) const; static void deleteClient(ShellClient *c); + QSize toWindowGeometry(const QSize &geometry) const; + KWayland::Server::ShellSurfaceInterface *m_shellSurface; KWayland::Server::XdgShellSurfaceInterface *m_xdgShellSurface; KWayland::Server::XdgShellPopupInterface *m_xdgShellPopup; @@ -291,6 +294,8 @@ QString m_captionSuffix; QHash m_pingSerials; + QMargins m_windowMargins; + bool m_compositingSetup = false; }; diff --git a/shell_client.cpp b/shell_client.cpp --- a/shell_client.cpp +++ b/shell_client.cpp @@ -372,6 +372,7 @@ SurfaceInterface *s = surface(); disconnect(s, &SurfaceInterface::committed, this, &ShellClient::finishInit); + updateWindowMargins(); if (!isInitialPositionSet()) { QRect area = workspace()->clientArea(PlacementArea, Screens::self()->current(), desktop()); placeIn(area); @@ -427,6 +428,16 @@ delete c; } +QSize ShellClient::toWindowGeometry(const QSize &size) const +{ + QSize adjustedSize = size - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()); + // 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 ShellClient::activities() const { // TODO: implement @@ -519,6 +530,7 @@ auto s = surface(); if (s->size().isValid()) { m_clientSize = s->size(); + updateWindowMargins(); updatePendingGeometry(); } markAsMapped(); @@ -617,11 +629,11 @@ // reset geometry to the one before blocking, so that we can compare properly geom = geometryBeforeUpdateBlocking(); } - // TODO: better merge with Client's implementation const QSize requestedClientSize = QSize(w, h) - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()); + const QSize requestedWindowGeometrySize = toWindowGeometry(QSize(w, h)); if (requestedClientSize == m_clientSize && !isWaitingForMoveResizeSync() && - (m_requestedClientSize.isEmpty() || requestedClientSize == m_requestedClientSize)) { + (m_requestedClientSize.isEmpty() || requestedWindowGeometrySize == m_requestedClientSize)) { // size didn't change, and we don't need to explicitly request a new size doSetGeometry(QRect(x, y, w, h)); updateMaximizeMode(m_requestedMaximizeMode); @@ -1107,7 +1119,7 @@ QSize size; if (rect.isValid()) { - size = rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()); + size = toWindowGeometry(rect.size()); } else { size = QSize(0, 0); } @@ -1126,7 +1138,7 @@ if (parent) { const QPoint globalClientContentPos = parent->geometry().topLeft() + parent->clientPos(); const QPoint relativeOffset = rect.topLeft() - globalClientContentPos; - serialId = m_xdgShellPopup->configure(QRect(relativeOffset, rect.size())); + serialId = m_xdgShellPopup->configure(QRect(relativeOffset, size)); } } @@ -1867,6 +1879,34 @@ surface()->setOutputs(clientOutputs); } +void ShellClient::updateWindowMargins() +{ + QRect windowGeometry; + QSize clientSize = m_clientSize; + + if (m_xdgShellSurface) { + windowGeometry = m_xdgShellSurface->windowGeometry(); + } else if (m_xdgShellPopup) { + windowGeometry = m_xdgShellPopup->windowGeometry(); + if (!clientSize.isValid()) { + clientSize = m_xdgShellPopup->initialSize(); + } + } else { + return; + } + + 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 ShellClient::isPopupWindow() const { if (Toplevel::isPopupWindow()) {