diff options
author | Johan Klokkhammer Helsing <johan.helsing@qt.io> | 2018-09-24 13:04:51 +0200 |
---|---|---|
committer | Johan Helsing <johan.helsing@qt.io> | 2018-10-26 11:05:00 +0000 |
commit | 88a0246a46c30e08e9730d16cf8739773447d058 (patch) | |
tree | f9d20ff21e77031b7ddcb33394aa0e35de491933 /tests | |
parent | ba53384387dcfd2606a513b114eff488d3fdb940 (diff) | |
download | qtwayland-88a0246a46c30e08e9730d16cf8739773447d058.tar.gz |
Client: Don't attach buffers to unexposed windows
QBackingStore::flush is sometimes called with an unxeposed window, in that
case, don't attach the buffer to the wl_surface immediately, as that causes
protocol errors with xdg_shell.
Flushed buffers are instead stored until we get the first configure event.
[ChangeLog][QPA plugin][xdg-shell] Fixed a bug where buffers were sometimes
attached and committed before the first configure event, causing protocol
errors.
Fixes: QTBUG-71345
Change-Id: If9409d97bd25f6b13940c56141920a664c349c8e
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Reviewed-by: David Edmundson <davidedmundson@kde.org>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/client/shared/mocksurface.cpp | 12 | ||||
-rw-r--r-- | tests/auto/client/shared/mocksurface.h | 7 | ||||
-rw-r--r-- | tests/auto/client/shared/mockxdgshellv6.cpp | 14 | ||||
-rw-r--r-- | tests/auto/client/shared/mockxdgshellv6.h | 6 | ||||
-rw-r--r-- | tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp | 35 |
5 files changed, 65 insertions, 9 deletions
diff --git a/tests/auto/client/shared/mocksurface.cpp b/tests/auto/client/shared/mocksurface.cpp index c0d51618..84dcda6b 100644 --- a/tests/auto/client/shared/mocksurface.cpp +++ b/tests/auto/client/shared/mocksurface.cpp @@ -74,7 +74,7 @@ void Compositor::sendShellSurfaceConfigure(void *data, const QList<QVariant> &pa auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()), states.size() * static_cast<int>(sizeof(uint))); toplevel->send_configure(size.width(), size.height(), statesBytes); - toplevel->xdgSurface()->send_configure(compositor->nextSerial()); + toplevel->xdgSurface()->sendConfigure(compositor->nextSerial()); } else if (auto wlShellSurface = surface->wlShellSurface()) { const uint edges = 0; wlShellSurface->send_configure(edges, size.width(), size.height()); @@ -123,13 +123,17 @@ void Surface::surface_destroy(Resource *resource) if (m_wlShellSurface) // on wl-shell the shell surface is automatically destroyed with the surface wl_resource_destroy(m_wlShellSurface->resource()->handle); Q_ASSERT(!m_wlShellSurface); - Q_ASSERT(!m_xdgToplevelV6); + Q_ASSERT(!m_xdgSurfaceV6); wl_resource_destroy(resource->handle); } -void Surface::surface_attach(Resource *resource, - struct wl_resource *buffer, int x, int y) +void Surface::surface_attach(Resource *resource, struct wl_resource *buffer, int x, int y) { + if (m_xdgSurfaceV6) { + // It's a protocol error to attach a buffer to an xdgSurface that's not configured + Q_ASSERT(xdgSurfaceV6()->configureSent()); + } + Q_UNUSED(resource); Q_UNUSED(x); Q_UNUSED(y); diff --git a/tests/auto/client/shared/mocksurface.h b/tests/auto/client/shared/mocksurface.h index 3b0f01fd..949dc23d 100644 --- a/tests/auto/client/shared/mocksurface.h +++ b/tests/auto/client/shared/mocksurface.h @@ -50,7 +50,8 @@ public: static Surface *fromResource(struct ::wl_resource *resource); void map(); bool isMapped() const; - XdgToplevelV6 *xdgToplevelV6() const { return m_xdgToplevelV6; } + XdgSurfaceV6 *xdgSurfaceV6() const { return m_xdgSurfaceV6; } + XdgToplevelV6 *xdgToplevelV6() const { return m_xdgSurfaceV6 ? m_xdgSurfaceV6->toplevel() : nullptr; } WlShellSurface *wlShellSurface() const { return m_wlShellSurface; } QSharedPointer<MockSurface> mockSurface() const { return m_mockSurface; } @@ -69,7 +70,7 @@ protected: void surface_commit(Resource *resource) override; private: wl_resource *m_buffer = nullptr; - XdgToplevelV6 *m_xdgToplevelV6 = nullptr; + XdgSurfaceV6 *m_xdgSurfaceV6 = nullptr; WlShellSurface *m_wlShellSurface = nullptr; Compositor *m_compositor = nullptr; @@ -77,7 +78,7 @@ private: QList<wl_resource *> m_frameCallbackList; bool m_mapped = false; - friend class XdgToplevelV6; + friend class XdgSurfaceV6; friend class WlShellSurface; }; diff --git a/tests/auto/client/shared/mockxdgshellv6.cpp b/tests/auto/client/shared/mockxdgshellv6.cpp index 5dc8da59..05eff74a 100644 --- a/tests/auto/client/shared/mockxdgshellv6.cpp +++ b/tests/auto/client/shared/mockxdgshellv6.cpp @@ -49,6 +49,18 @@ XdgSurfaceV6::XdgSurfaceV6(XdgShellV6 *shell, Surface *surface, wl_client *clien , m_surface(surface) , m_shell(shell) { + m_surface->m_xdgSurfaceV6 = this; +} + +XdgSurfaceV6::~XdgSurfaceV6() +{ + m_surface->m_xdgSurfaceV6 = nullptr; +} + +void XdgSurfaceV6::sendConfigure(uint32_t serial) +{ + send_configure(serial); + m_configureSent = true; } void XdgSurfaceV6::zxdg_surface_v6_get_toplevel(QtWaylandServer::zxdg_surface_v6::Resource *resource, uint32_t id) @@ -78,7 +90,6 @@ XdgToplevelV6::XdgToplevelV6(XdgSurfaceV6 *xdgSurface, wl_client *client, uint32 , m_mockToplevel(new MockXdgToplevelV6(this)) { auto *surface = m_xdgSurface->surface(); - surface->m_xdgToplevelV6 = this; m_xdgSurface->shell()->addToplevel(this); surface->map(); } @@ -86,7 +97,6 @@ XdgToplevelV6::XdgToplevelV6(XdgSurfaceV6 *xdgSurface, wl_client *client, uint32 XdgToplevelV6::~XdgToplevelV6() { m_xdgSurface->shell()->removeToplevel(this); - m_xdgSurface->surface()->m_xdgToplevelV6 = nullptr; m_mockToplevel->m_toplevel = nullptr; } diff --git a/tests/auto/client/shared/mockxdgshellv6.h b/tests/auto/client/shared/mockxdgshellv6.h index bd5e1306..a238fa56 100644 --- a/tests/auto/client/shared/mockxdgshellv6.h +++ b/tests/auto/client/shared/mockxdgshellv6.h @@ -46,8 +46,13 @@ class XdgSurfaceV6 : public QtWaylandServer::zxdg_surface_v6 { public: XdgSurfaceV6(XdgShellV6 *shell, Surface *surface, wl_client *client, uint32_t id); + ~XdgSurfaceV6() override; XdgShellV6 *shell() const { return m_shell; } Surface *surface() const { return m_surface; } + XdgToplevelV6 *toplevel() const { return m_toplevel; } + + void sendConfigure(uint32_t serial); + bool configureSent() const { return m_configureSent; } protected: void zxdg_surface_v6_destroy_resource(Resource *) override { delete this; } @@ -59,6 +64,7 @@ private: Surface *m_surface = nullptr; XdgToplevelV6 *m_toplevel = nullptr; XdgShellV6 *m_shell = nullptr; + bool m_configureSent = false; friend class XdgToplevelV6; }; diff --git a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp index 91679cb5..3fd8153e 100644 --- a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp +++ b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp @@ -101,6 +101,7 @@ private slots: void windowStateChangedEvents(); void windowGeometrySimple(); void windowGeometryFixed(); + void flushUnconfiguredXdgSurface(); private: MockCompositor *m_compositor = nullptr; @@ -360,6 +361,40 @@ void tst_WaylandClientXdgShellV6::windowGeometryFixed() QCOMPARE(geometrySpy.takeFirst().at(0).toRect().size(), initialWindowGeometry.size()); } +void tst_WaylandClientXdgShellV6::flushUnconfiguredXdgSurface() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockSurface> surface; + QTRY_VERIFY(surface = m_compositor->surface()); + + // Paint and flush some magenta + QBackingStore backingStore(&window); + QRect rect(QPoint(), window.size()); + backingStore.resize(rect.size()); + backingStore.beginPaint(rect); + QColor color = Qt::magenta; + QPainter p(backingStore.paintDevice()); + p.fillRect(rect, color); + p.end(); + backingStore.endPaint(); + backingStore.flush(rect); + + // We're not allowed to send buffer on this surface since it isn't yet configured. + // So, from the compositor's point of view there should be no buffer data yet. + m_compositor->processWaylandEvents(); + QVERIFY(surface->image.isNull()); + QVERIFY(!window.isExposed()); + + // Finally sending the configure should trigger an attach and commit with the + // right buffer. + m_compositor->sendShellSurfaceConfigure(surface); + QTRY_COMPARE(surface->image.size(), window.frameGeometry().size()); + QTRY_COMPARE(surface->image.pixel(window.frameMargins().left(), window.frameMargins().top()), color.rgba()); + QVERIFY(window.isExposed()); +} + int main(int argc, char **argv) { setenv("XDG_RUNTIME_DIR", ".", 1); |