[plasma/kwin] /: wayland: Make window geometry handling spec compliant

Vlad Zahorodnii <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 6f25aac2720cd933fe76646cbc3c2efe54f6d015 by Vlad Zahorodnii.
Committed on 27/07/2026 at 12:05.
Pushed by vladz into branch 'master'.

wayland: Make window geometry handling spec compliant

The effective window geometry should be computed at the commit time
after set_window_geometry request has been called only once.

Currently kwin computes the window geometry after every commit because
of ambiguities in the spec that had existed at the time, and also Qt
prior to version 6.9 not adhering to the spec.

M  +3    -7    autotests/integration/xdgshellwindow_test.cpp
M  +15   -4    src/wayland/xdgshell.cpp
M  +1    -1    src/wayland/xdgshell.h
M  +2    -1    src/wayland/xdgshell_p.h
M  +1    -41   src/xdgshellwindow.cpp

https://invent.kde.org/plasma/kwin/-/commit/6f25aac2720cd933fe76646cbc3c2efe54f6d015

diff --git a/autotests/integration/xdgshellwindow_test.cpp b/autotests/integration/xdgshellwindow_test.cpp
index c654fac3b6c..7a66e107c68 100644
--- a/autotests/integration/xdgshellwindow_test.cpp
+++ b/autotests/integration/xdgshellwindow_test.cpp
@@ -1227,17 +1227,13 @@ void TestXdgShellWindow::testXdgWindowGeometryAttachBuffer()
     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
 
     Test::render(surface.get(), QSize(100, 50), Qt::blue);
-    QVERIFY(frameGeometryChangedSpy.wait());
-    QCOMPARE(frameGeometryChangedSpy.count(), 2);
-    QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
-    QCOMPARE(window->frameGeometry().size(), QSize(90, 40));
-    QCOMPARE(window->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
-    QCOMPARE(window->bufferGeometry().size(), QSize(100, 50));
+    QVERIFY(Test::waylandSync());
+    QCOMPARE(frameGeometryChangedSpy.count(), 1);
 
     shellSurface->xdgSurface()->set_window_geometry(0, 0, 100, 50);
     surface->commit(KWayland::Client::Surface::CommitFlag::None);
     QVERIFY(frameGeometryChangedSpy.wait());
-    QCOMPARE(frameGeometryChangedSpy.count(), 3);
+    QCOMPARE(frameGeometryChangedSpy.count(), 2);
     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
     QCOMPARE(window->frameGeometry().size(), QSize(100, 50));
     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition);
diff --git a/src/wayland/xdgshell.cpp b/src/wayland/xdgshell.cpp
index 43f987afde4..c0943c9be93 100644
--- a/src/wayland/xdgshell.cpp
+++ b/src/wayland/xdgshell.cpp
@@ -158,17 +158,28 @@ void XdgSurfaceInterfacePrivate::apply(XdgSurfaceCommit *commit)
     }
 
     if (commit->windowGeometry.has_value()) {
-        windowGeometry = commit->windowGeometry.value();
-        Q_EMIT q->windowGeometryChanged(windowGeometry);
+        explicitWindowGeometry = true;
+        const RectF geometry = commit->windowGeometry.value() & surface->boundingRect();
+        if (effectiveWindowGeometry != geometry) {
+            effectiveWindowGeometry = geometry;
+            Q_EMIT q->windowGeometryChanged();
+        }
+    } else if (!explicitWindowGeometry) {
+        const RectF geometry = surface->boundingRect();
+        if (effectiveWindowGeometry != geometry) {
+            effectiveWindowGeometry = geometry;
+            Q_EMIT q->windowGeometryChanged();
+        }
     }
 }
 
 void XdgSurfaceInterfacePrivate::reset()
 {
+    effectiveWindowGeometry = RectF();
+    explicitWindowGeometry = false;
     firstBufferAttached = false;
     isConfigured = false;
     isInitialized = false;
-    windowGeometry = Rect();
     Q_EMIT q->resetOccurred();
 }
 
@@ -319,7 +330,7 @@ bool XdgSurfaceInterface::isConfigured() const
 
 RectF XdgSurfaceInterface::windowGeometry() const
 {
-    return d->windowGeometry;
+    return d->effectiveWindowGeometry;
 }
 
 XdgSurfaceInterface *XdgSurfaceInterface::get(::wl_resource *resource)
diff --git a/src/wayland/xdgshell.h b/src/wayland/xdgshell.h
index 7e89a12b24f..7d1fd08b6d4 100644
--- a/src/wayland/xdgshell.h
+++ b/src/wayland/xdgshell.h
@@ -192,7 +192,7 @@ Q_SIGNALS:
     /**
      * This signal is emitted when the window geometry has been changed.
      */
-    void windowGeometryChanged(const RectF &rect);
+    void windowGeometryChanged();
 
     /**
      * This signal is emitted when the surface has been unmapped and its state has been reset.
diff --git a/src/wayland/xdgshell_p.h b/src/wayland/xdgshell_p.h
index 38e82eb23ed..a1e5877134c 100644
--- a/src/wayland/xdgshell_p.h
+++ b/src/wayland/xdgshell_p.h
@@ -122,7 +122,8 @@ public:
     QPointer<XdgPopupInterface> popup;
     QPointer<XXPipV1Interface> pip;
     QPointer<SurfaceInterface> surface;
-    RectF windowGeometry;
+    RectF effectiveWindowGeometry;
+    bool explicitWindowGeometry = false;
     bool firstBufferAttached = false;
     bool isConfigured = false;
     bool isInitialized = false;
diff --git a/src/xdgshellwindow.cpp b/src/xdgshellwindow.cpp
index c8e47ac9a27..a7b86bd6462 100644
--- a/src/xdgshellwindow.cpp
+++ b/src/xdgshellwindow.cpp
@@ -21,7 +21,6 @@
 #include "killprompt.h"
 #include "placement.h"
 #include "tiles/tilemanager.h"
-#include "utils/subsurfacemonitor.h"
 #include "virtualdesktops.h"
 #include "wayland/appmenu.h"
 #include "wayland/output.h"
@@ -58,29 +57,8 @@ XdgSurfaceWindow::XdgSurfaceWindow(XdgSurfaceInterface *shellSurface)
             this, &XdgSurfaceWindow::destroyWindow);
     connect(shellSurface->surface(), &SurfaceInterface::aboutToBeDestroyed,
             this, &XdgSurfaceWindow::destroyWindow);
-
-    // The effective window geometry is determined by two things: (a) the rectangle that bounds
-    // the main surface and all of its sub-surfaces, (b) the client-specified window geometry, if
-    // any. If the client hasn't provided the window geometry, we fallback to the bounding sub-
-    // surface rectangle. If the client has provided the window geometry, we intersect it with
-    // the bounding rectangle and that will be the effective window geometry. It's worth to point
-    // out that geometry updates do not occur that frequently, so we don't need to recompute the
-    // bounding geometry every time the client commits the surface.
-
-    SubSurfaceMonitor *treeMonitor = new SubSurfaceMonitor(surface(), this);
-
-    connect(treeMonitor, &SubSurfaceMonitor::subSurfaceAdded,
-            this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
-    connect(treeMonitor, &SubSurfaceMonitor::subSurfaceRemoved,
-            this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
-    connect(treeMonitor, &SubSurfaceMonitor::subSurfaceMoved,
-            this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
-    connect(treeMonitor, &SubSurfaceMonitor::subSurfaceResized,
-            this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
     connect(shellSurface, &XdgSurfaceInterface::windowGeometryChanged,
             this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
-    connect(surface(), &SurfaceInterface::sizeChanged,
-            this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
 
     // Configure events are not sent immediately, but rather scheduled to be sent when the event
     // loop is about to be idle. By doing this, we can avoid sending configure events that do
@@ -202,26 +180,8 @@ void XdgSurfaceWindow::handleNextWindowGeometry()
     if (const XdgSurfaceConfigure *configureEvent = lastAcknowledgedConfigure()) {
         setTargetScale(configureEvent->scale);
     }
-    const RectF boundingGeometry = surface()->boundingRect();
-
-    // The effective window geometry is defined as the intersection of the window geometry
-    // and the rectangle that bounds the main surface and all of its sub-surfaces. If the
-    // client hasn't specified the window geometry, we must fallback to the bounding geometry.
-    // Note that the xdg-shell spec is not clear about when exactly we have to clamp the
-    // window geometry.
 
-    m_windowGeometry = m_shellSurface->windowGeometry();
-    if (m_windowGeometry.isValid()) {
-        m_windowGeometry &= boundingGeometry;
-    } else {
-        m_windowGeometry = boundingGeometry;
-    }
-
-    if (m_windowGeometry.isEmpty()) {
-        qCWarning(KWIN_CORE) << "Committed empty window geometry, dealing with a buggy client!";
-    } else {
-        m_windowGeometry = snapToPixels(m_windowGeometry, targetScale());
-    }
+    m_windowGeometry = snapToPixels(m_shellSurface->windowGeometry(), targetScale());
 
     RectF frameGeometry(pos(), clientSizeToFrameSize(m_windowGeometry.size()));
     if (const XdgSurfaceConfigure *configureEvent = lastAcknowledgedConfigure()) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.