diff --git a/autotests/integration/kwin_wayland_test.h b/autotests/integration/kwin_wayland_test.h --- a/autotests/integration/kwin_wayland_test.h +++ b/autotests/integration/kwin_wayland_test.h @@ -137,12 +137,43 @@ XdgShellV6, XdgShellStable }; + +enum class CreationSetup { + CreateOnly, + CreateAndConfigure, /// commit and wait for the configure event, making this surface ready to commit buffers +}; + +/** + * Creates either a ShellSurface * or XdgShellSurface * as defined by @arg type + * For XDG top levels this method will block for a configure event, make this surface ready to commit buffers + */ QObject *createShellSurface(ShellSurfaceType type, KWayland::Client::Surface *surface, QObject *parent = nullptr); -KWayland::Client::ShellSurface *createShellSurface(KWayland::Client::Surface *surface, QObject *parent = nullptr); -KWayland::Client::XdgShellSurface *createXdgShellV5Surface(KWayland::Client::Surface *surface, QObject *parent = nullptr); -KWayland::Client::XdgShellSurface *createXdgShellV6Surface(KWayland::Client::Surface *surface, QObject *parent = nullptr); -KWayland::Client::XdgShellSurface *createXdgShellStableSurface(KWayland::Client::Surface *surface, QObject *parent = nullptr); -KWayland::Client::XdgShellPopup *createXdgShellStablePopup(KWayland::Client::Surface *surface, KWayland::Client::XdgShellSurface *parentSurface, const KWayland::Client::XdgPositioner &positioner, QObject *parent = nullptr); + +KWayland::Client::ShellSurface *createShellSurface(KWayland::Client::Surface *surface, + QObject *parent = nullptr); +KWayland::Client::XdgShellSurface *createXdgShellV5Surface(KWayland::Client::Surface *surface, + QObject *parent = nullptr, + CreationSetup = CreationSetup::CreateAndConfigure); +KWayland::Client::XdgShellSurface *createXdgShellV6Surface(KWayland::Client::Surface *surface, + QObject *parent = nullptr, + CreationSetup = CreationSetup::CreateAndConfigure); +KWayland::Client::XdgShellSurface *createXdgShellStableSurface(KWayland::Client::Surface *surface, + QObject *parent = nullptr, + CreationSetup = CreationSetup::CreateAndConfigure); +KWayland::Client::XdgShellPopup *createXdgShellStablePopup(KWayland::Client::Surface *surface, + KWayland::Client::XdgShellSurface *parentSurface, + const KWayland::Client::XdgPositioner &positioner, + QObject *parent = nullptr, + CreationSetup = CreationSetup::CreateAndConfigure); + + +/** + * Commits the XdgShellSurface to the given surface, and waits for the configure event from the compositor + */ +void initXdgShellSurface(KWayland::Client::Surface *surface, KWayland::Client::XdgShellSurface *shellSurface); +void initXdgShellPopup(KWayland::Client::Surface *surface, KWayland::Client::XdgShellPopup *popup); + + /** * Creates a shared memory buffer of @p size in @p color and attaches it to the @p surface. diff --git a/autotests/integration/test_helpers.cpp b/autotests/integration/test_helpers.cpp --- a/autotests/integration/test_helpers.cpp +++ b/autotests/integration/test_helpers.cpp @@ -424,6 +424,27 @@ return clientAddedSpy.first().first().value(); } + +ShellClient *renderShellClientAndWaitForShown(QObject *shellClient, Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format, int timeout) +{ + QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + if (!clientAddedSpy.isValid()) { + return nullptr; + } + if (auto xdgShellSurface = qobject_cast(shellClient)) { + initXdgShellSurface(surface, xdgShellSurface); + } else if (auto xdgShellPopup = qobject_cast(shellClient)) { + initXdgShellPopup(surface, xdgShellPopup); + } + render(surface, size, color, format); + flushWaylandConnection(); + + if (!clientAddedSpy.wait(timeout)) { + return nullptr; + } + return clientAddedSpy.first().first().value(); +} + void flushWaylandConnection() { if (s_waylandConnection.connection) { @@ -457,7 +478,7 @@ return s; } -XdgShellSurface *createXdgShellV5Surface(Surface *surface, QObject *parent) +XdgShellSurface *createXdgShellV5Surface(Surface *surface, QObject *parent, CreationSetup creationSetup) { if (!s_waylandConnection.xdgShellV5) { return nullptr; @@ -467,10 +488,13 @@ delete s; return nullptr; } + if (creationSetup == CreationSetup::CreateAndConfigure) { + initXdgShellSurface(surface, s); + } return s; } -XdgShellSurface *createXdgShellV6Surface(Surface *surface, QObject *parent) +XdgShellSurface *createXdgShellV6Surface(Surface *surface, QObject *parent, CreationSetup creationSetup) { if (!s_waylandConnection.xdgShellV6) { return nullptr; @@ -480,10 +504,13 @@ delete s; return nullptr; } + if (creationSetup == CreationSetup::CreateAndConfigure) { + initXdgShellSurface(surface, s); + } return s; } -XdgShellSurface *createXdgShellStableSurface(Surface *surface, QObject *parent) +XdgShellSurface *createXdgShellStableSurface(Surface *surface, QObject *parent, CreationSetup creationSetup) { if (!s_waylandConnection.xdgShellStable) { return nullptr; @@ -493,10 +520,13 @@ delete s; return nullptr; } + if (creationSetup == CreationSetup::CreateAndConfigure) { + initXdgShellSurface(surface, s); + } return s; } -XdgShellPopup *createXdgShellStablePopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent) +XdgShellPopup *createXdgShellStablePopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent, CreationSetup creationSetup) { if (!s_waylandConnection.xdgShellStable) { return nullptr; @@ -506,20 +536,43 @@ delete s; return nullptr; } + if (creationSetup == CreationSetup::CreateAndConfigure) { + initXdgShellPopup(surface, s); + } return s; } +void initXdgShellSurface(KWayland::Client::Surface *surface, KWayland::Client::XdgShellSurface *shellSurface) +{ + //wait for configure + QSignalSpy configureRequestedSpy(shellSurface, SIGNAL(configureRequested(QSize, KWayland::Client::XdgShellSurface::States, quint32))); + QVERIFY(configureRequestedSpy.isValid()); + surface->commit(); + configureRequestedSpy.wait(); + shellSurface->ackConfigure(configureRequestedSpy.last()[2].toInt()); +} + +void initXdgShellPopup(KWayland::Client::Surface *surface, KWayland::Client::XdgShellPopup *shellPopup) +{ + //wait for configure + QSignalSpy configureRequestedSpy(shellPopup, SIGNAL(configureRequested(QPoint, quint32))); + QVERIFY(configureRequestedSpy.isValid()); + surface->commit(); + configureRequestedSpy.wait(); +} + + QObject *createShellSurface(ShellSurfaceType type, KWayland::Client::Surface *surface, QObject *parent) { switch (type) { case ShellSurfaceType::WlShell: return createShellSurface(surface, parent); case ShellSurfaceType::XdgShellV5: - return createXdgShellV5Surface(surface, parent); + return createXdgShellV5Surface(surface, parent, CreationSetup::CreateAndConfigure); case ShellSurfaceType::XdgShellV6: - return createXdgShellV6Surface(surface, parent); + return createXdgShellV6Surface(surface, parent, CreationSetup::CreateAndConfigure); case ShellSurfaceType::XdgShellStable: - return createXdgShellStableSurface(surface, parent); + return createXdgShellStableSurface(surface, parent, CreationSetup::CreateAndConfigure); default: Q_UNREACHABLE(); return nullptr; diff --git a/shell_client.cpp b/shell_client.cpp --- a/shell_client.cpp +++ b/shell_client.cpp @@ -87,8 +87,12 @@ , m_xdgShellPopup(nullptr) , m_internal(surface->client() == waylandServer()->internalConnection()) { + ready_for_painting = false; + setSurface(surface->surface()); - init(); + // as wl_surface is double buffered, the XdgShellInterface for this surface + // is only valid when the surface is committed + connect(surface->surface(), &SurfaceInterface::committed, this, &ShellClient::init); } ShellClient::ShellClient(XdgShellPopupInterface *surface) @@ -98,8 +102,10 @@ , m_xdgShellPopup(surface) , m_internal(surface->client() == waylandServer()->internalConnection()) { + ready_for_painting = false; + setSurface(surface->surface()); - init(); + connect(surface->surface(), &SurfaceInterface::committed, this, &ShellClient::init); } ShellClient::~ShellClient() = default; @@ -208,12 +214,13 @@ void ShellClient::init() { + SurfaceInterface *s = surface(); + disconnect(s, &SurfaceInterface::committed, this, &ShellClient::init); connect(this, &ShellClient::desktopFileNameChanged, this, &ShellClient::updateIcon); findInternalWindow(); createWindowId(); setupCompositing(); updateIcon(); - SurfaceInterface *s = surface(); Q_ASSERT(s); if (s->buffer()) { setReadyForPainting();