Handle XdgShell window geometry in configure request sizes
Summary: The size passed to an XDG shell configure request should match the window size of the given window, we don't want to include the size of any shadows that may be drawn by the client. Kwin has the same concept of geometry for both window management, input and rendering. In order to approach this in a way that does not risk any regressions with kwin's current structure AbstractClient::geometry remains the canonical source and we handle the window within that internally within ShellClient treating the windowGeometry as a set of margins from this. This is part of a much bigger task (T10867). This patch addresses windows growing when starting a drag based resize. BUG: 403376 Test Plan: Unit test gtk3-demo Reviewers: #kwin, zzag Reviewed By: #kwin, zzag Subscribers: zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D20937icc-effect-5.17.5
parent
ce1a5eae15
commit
bc83065ceb
|
@ -109,6 +109,7 @@ private Q_SLOTS:
|
||||||
void testXdgInitialState();
|
void testXdgInitialState();
|
||||||
void testXdgInitiallyMaximised();
|
void testXdgInitiallyMaximised();
|
||||||
void testXdgInitiallyMinimized();
|
void testXdgInitiallyMinimized();
|
||||||
|
void testXdgWindowGeometry();
|
||||||
};
|
};
|
||||||
|
|
||||||
void TestShellClient::initTestCase()
|
void TestShellClient::initTestCase()
|
||||||
|
@ -1463,6 +1464,41 @@ void TestShellClient::testXdgInitiallyMinimized()
|
||||||
QVERIFY(c->isMinimized());
|
QVERIFY(c->isMinimized());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestShellClient::testXdgWindowGeometry()
|
||||||
|
{
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> 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<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
|
||||||
|
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<QSize>();
|
||||||
|
QCOMPARE(requestedFullScreenSize, QSize(1280, 1024));
|
||||||
|
}
|
||||||
|
|
||||||
WAYLANDTEST_MAIN(TestShellClient)
|
WAYLANDTEST_MAIN(TestShellClient)
|
||||||
#include "shell_client_test.moc"
|
#include "shell_client_test.moc"
|
||||||
|
|
|
@ -372,6 +372,7 @@ void ShellClient::finishInit() {
|
||||||
SurfaceInterface *s = surface();
|
SurfaceInterface *s = surface();
|
||||||
disconnect(s, &SurfaceInterface::committed, this, &ShellClient::finishInit);
|
disconnect(s, &SurfaceInterface::committed, this, &ShellClient::finishInit);
|
||||||
|
|
||||||
|
updateWindowMargins();
|
||||||
if (!isInitialPositionSet()) {
|
if (!isInitialPositionSet()) {
|
||||||
QRect area = workspace()->clientArea(PlacementArea, Screens::self()->current(), desktop());
|
QRect area = workspace()->clientArea(PlacementArea, Screens::self()->current(), desktop());
|
||||||
placeIn(area);
|
placeIn(area);
|
||||||
|
@ -427,6 +428,16 @@ void ShellClient::deleteClient(ShellClient *c)
|
||||||
delete c;
|
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
|
QStringList ShellClient::activities() const
|
||||||
{
|
{
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
|
@ -519,6 +530,7 @@ void ShellClient::addDamage(const QRegion &damage)
|
||||||
auto s = surface();
|
auto s = surface();
|
||||||
if (s->size().isValid()) {
|
if (s->size().isValid()) {
|
||||||
m_clientSize = s->size();
|
m_clientSize = s->size();
|
||||||
|
updateWindowMargins();
|
||||||
updatePendingGeometry();
|
updatePendingGeometry();
|
||||||
}
|
}
|
||||||
markAsMapped();
|
markAsMapped();
|
||||||
|
@ -617,11 +629,11 @@ void ShellClient::setGeometry(int x, int y, int w, int h, ForceGeometry_t force)
|
||||||
// reset geometry to the one before blocking, so that we can compare properly
|
// reset geometry to the one before blocking, so that we can compare properly
|
||||||
geom = geometryBeforeUpdateBlocking();
|
geom = geometryBeforeUpdateBlocking();
|
||||||
}
|
}
|
||||||
// TODO: better merge with Client's implementation
|
|
||||||
const QSize requestedClientSize = QSize(w, h) - QSize(borderLeft() + borderRight(), borderTop() + borderBottom());
|
const QSize requestedClientSize = QSize(w, h) - QSize(borderLeft() + borderRight(), borderTop() + borderBottom());
|
||||||
|
const QSize requestedWindowGeometrySize = toWindowGeometry(QSize(w, h));
|
||||||
|
|
||||||
if (requestedClientSize == m_clientSize && !isWaitingForMoveResizeSync() &&
|
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
|
// size didn't change, and we don't need to explicitly request a new size
|
||||||
doSetGeometry(QRect(x, y, w, h));
|
doSetGeometry(QRect(x, y, w, h));
|
||||||
updateMaximizeMode(m_requestedMaximizeMode);
|
updateMaximizeMode(m_requestedMaximizeMode);
|
||||||
|
@ -1107,7 +1119,7 @@ bool ShellClient::requestGeometry(const QRect &rect)
|
||||||
|
|
||||||
QSize size;
|
QSize size;
|
||||||
if (rect.isValid()) {
|
if (rect.isValid()) {
|
||||||
size = rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom());
|
size = toWindowGeometry(rect.size());
|
||||||
} else {
|
} else {
|
||||||
size = QSize(0, 0);
|
size = QSize(0, 0);
|
||||||
}
|
}
|
||||||
|
@ -1126,7 +1138,7 @@ bool ShellClient::requestGeometry(const QRect &rect)
|
||||||
if (parent) {
|
if (parent) {
|
||||||
const QPoint globalClientContentPos = parent->geometry().topLeft() + parent->clientPos();
|
const QPoint globalClientContentPos = parent->geometry().topLeft() + parent->clientPos();
|
||||||
const QPoint relativeOffset = rect.topLeft() - globalClientContentPos;
|
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 @@ void ShellClient::updateClientOutputs()
|
||||||
surface()->setOutputs(clientOutputs);
|
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
|
bool ShellClient::isPopupWindow() const
|
||||||
{
|
{
|
||||||
if (Toplevel::isPopupWindow()) {
|
if (Toplevel::isPopupWindow()) {
|
||||||
|
|
|
@ -214,6 +214,7 @@ private:
|
||||||
void setTransient();
|
void setTransient();
|
||||||
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);
|
||||||
|
@ -222,6 +223,8 @@ private:
|
||||||
QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSize popupSize) const;
|
QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSize popupSize) const;
|
||||||
static void deleteClient(ShellClient *c);
|
static void deleteClient(ShellClient *c);
|
||||||
|
|
||||||
|
QSize toWindowGeometry(const QSize &geometry) const;
|
||||||
|
|
||||||
KWayland::Server::ShellSurfaceInterface *m_shellSurface;
|
KWayland::Server::ShellSurfaceInterface *m_shellSurface;
|
||||||
KWayland::Server::XdgShellSurfaceInterface *m_xdgShellSurface;
|
KWayland::Server::XdgShellSurfaceInterface *m_xdgShellSurface;
|
||||||
KWayland::Server::XdgShellPopupInterface *m_xdgShellPopup;
|
KWayland::Server::XdgShellPopupInterface *m_xdgShellPopup;
|
||||||
|
@ -291,6 +294,8 @@ private:
|
||||||
QString m_captionSuffix;
|
QString m_captionSuffix;
|
||||||
QHash<qint32, PingReason> m_pingSerials;
|
QHash<qint32, PingReason> m_pingSerials;
|
||||||
|
|
||||||
|
QMargins m_windowMargins;
|
||||||
|
|
||||||
bool m_compositingSetup = false;
|
bool m_compositingSetup = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue