autotests: Introduce helper classes for testing xdg-shell clients

After splitting out the server part of KWayland into a separate repo,
all non-core protocol wrappers in KWayland::Client had become obsolete
and using them in new projects is highly discouraged.
This commit is contained in:
Vlad Zahorodnii 2020-09-02 12:27:08 +03:00
parent fe68ddd9ad
commit 2691b84c99
3 changed files with 294 additions and 2 deletions

View file

@ -17,6 +17,10 @@ ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework_SOURCES
PROTOCOL protocols/wlr-layer-shell-unstable-v1.xml
BASENAME wlr-layer-shell-unstable-v1
)
ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework_SOURCES
PROTOCOL ${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml
BASENAME xdg-shell
)
add_library(KWinIntegrationTestFramework STATIC ${KWinIntegrationTestFramework_SOURCES})
target_link_libraries(KWinIntegrationTestFramework kwin Qt5::Test Wayland::Client)

View file

@ -18,6 +18,7 @@
#include <KWayland/Client/xdgshell.h>
#include "qwayland-wlr-layer-shell-unstable-v1.h"
#include "qwayland-xdg-shell.h"
namespace KWayland
{
@ -107,6 +108,106 @@ Q_SIGNALS:
void configureRequested(quint32 serial, const QSize &size);
};
/**
* The XdgShell class represents the @c xdg_wm_base global.
*/
class XdgShell : public QtWayland::xdg_wm_base
{
public:
~XdgShell() override;
};
/**
* The XdgSurface class represents an xdg_surface object.
*/
class XdgSurface : public QObject, public QtWayland::xdg_surface
{
Q_OBJECT
public:
explicit XdgSurface(XdgShell *shell, KWayland::Client::Surface *surface, QObject *parent = nullptr);
~XdgSurface() override;
KWayland::Client::Surface *surface() const;
Q_SIGNALS:
void configureRequested(quint32 serial);
protected:
void xdg_surface_configure(uint32_t serial) override;
private:
KWayland::Client::Surface *m_surface;
};
/**
* The XdgToplevel class represents an xdg_toplevel surface. Note that the XdgToplevel surface
* takes the ownership of the underlying XdgSurface object.
*/
class XdgToplevel : public QObject, public QtWayland::xdg_toplevel
{
Q_OBJECT
public:
enum class State {
Maximized = 1 << 0,
Fullscreen = 1 << 1,
Resizing = 1 << 2,
Activated = 1 << 3
};
Q_DECLARE_FLAGS(States, State)
explicit XdgToplevel(XdgSurface *surface, QObject *parent = nullptr);
~XdgToplevel() override;
XdgSurface *xdgSurface() const;
Q_SIGNALS:
void configureRequested(const QSize &size, KWin::Test::XdgToplevel::States states);
void closeRequested();
protected:
void xdg_toplevel_configure(int32_t width, int32_t height, wl_array *states) override;
void xdg_toplevel_close() override;
private:
QScopedPointer<XdgSurface> m_xdgSurface;
};
/**
* The XdgPositioner class represents an xdg_positioner object.
*/
class XdgPositioner : public QtWayland::xdg_positioner
{
public:
explicit XdgPositioner(XdgShell *shell);
~XdgPositioner() override;
};
/**
* The XdgPopup class represents an xdg_popup surface. Note that the XdgPopup surface takes
* the ownership of the underlying XdgSurface object.
*/
class XdgPopup : public QObject, public QtWayland::xdg_popup
{
Q_OBJECT
public:
XdgPopup(XdgSurface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, QObject *parent = nullptr);
~XdgPopup() override;
XdgSurface *xdgSurface() const;
Q_SIGNALS:
void configureRequested(const QRect &rect);
protected:
void xdg_popup_configure(int32_t x, int32_t y, int32_t width, int32_t height) override;
private:
QScopedPointer<XdgSurface> m_xdgSurface;
};
enum class AdditionalWaylandInterface {
Seat = 1 << 0,
Decoration = 1 << 1,
@ -188,6 +289,15 @@ KWayland::Client::XdgShellPopup *createXdgShellStablePopup(KWayland::Client::Sur
QObject *parent = nullptr,
CreationSetup = CreationSetup::CreateAndConfigure);
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent = nullptr,
CreationSetup configureMode = CreationSetup::CreateAndConfigure);
XdgPositioner *createXdgPositioner();
XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface,
XdgPositioner *positioner, QObject *parent = nullptr,
CreationSetup configureMode = CreationSetup::CreateAndConfigure);
/**
* Commits the XdgShellSurface to the given surface, and waits for the configure event from the compositor
@ -240,6 +350,7 @@ bool unlockScreen();
}
Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::Test::AdditionalWaylandInterfaces)
Q_DECLARE_METATYPE(KWin::Test::XdgToplevel::States)
#define WAYLANDTEST_MAIN_HELPER(TestObject, DPI, OperationMode) \
int main(int argc, char *argv[]) \

View file

@ -74,6 +74,114 @@ void LayerSurfaceV1::zwlr_layer_surface_v1_closed()
emit closeRequested();
}
XdgShell::~XdgShell()
{
destroy();
}
XdgSurface::XdgSurface(XdgShell *shell, Surface *surface, QObject *parent)
: QObject(parent)
, QtWayland::xdg_surface(shell->get_xdg_surface(*surface))
, m_surface(surface)
{
}
XdgSurface::~XdgSurface()
{
destroy();
}
Surface *XdgSurface::surface() const
{
return m_surface;
}
void XdgSurface::xdg_surface_configure(uint32_t serial)
{
emit configureRequested(serial);
}
XdgToplevel::XdgToplevel(XdgSurface *surface, QObject *parent)
: QObject(parent)
, QtWayland::xdg_toplevel(surface->get_toplevel())
, m_xdgSurface(surface)
{
}
XdgToplevel::~XdgToplevel()
{
destroy();
}
XdgSurface *XdgToplevel::xdgSurface() const
{
return m_xdgSurface.data();
}
void XdgToplevel::xdg_toplevel_configure(int32_t width, int32_t height, wl_array *states)
{
States requestedStates;
const uint32_t *stateData = static_cast<const uint32_t *>(states->data);
const size_t stateCount = states->size / sizeof(uint32_t);
for (size_t i = 0; i < stateCount; ++i) {
switch (stateData[i]) {
case QtWayland::xdg_toplevel::state_maximized:
requestedStates |= State::Maximized;
break;
case QtWayland::xdg_toplevel::state_fullscreen:
requestedStates |= State::Fullscreen;
break;
case QtWayland::xdg_toplevel::state_resizing:
requestedStates |= State::Resizing;
break;
case QtWayland::xdg_toplevel::state_activated:
requestedStates |= State::Activated;
break;
}
}
emit configureRequested(QSize(width, height), requestedStates);
}
void XdgToplevel::xdg_toplevel_close()
{
emit closeRequested();
}
XdgPositioner::XdgPositioner(XdgShell *shell)
: QtWayland::xdg_positioner(shell->create_positioner())
{
}
XdgPositioner::~XdgPositioner()
{
destroy();
}
XdgPopup::XdgPopup(XdgSurface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, QObject *parent)
: QObject(parent)
, QtWayland::xdg_popup(surface->get_popup(parentSurface->object(), positioner->object()))
, m_xdgSurface(surface)
{
}
XdgPopup::~XdgPopup()
{
destroy();
}
XdgSurface *XdgPopup::xdgSurface() const
{
return m_xdgSurface.data();
}
void XdgPopup::xdg_popup_configure(int32_t x, int32_t y, int32_t width, int32_t height)
{
emit configureRequested(QRect(x, y, width, height));
}
static struct {
ConnectionThread *connection = nullptr;
EventQueue *queue = nullptr;
@ -81,7 +189,8 @@ static struct {
SubCompositor *subCompositor = nullptr;
ServerSideDecorationManager *decoration = nullptr;
ShadowManager *shadowManager = nullptr;
XdgShell *xdgShellStable = nullptr;
KWayland::Client::XdgShell *xdgShellStable = nullptr;
XdgShell *xdgShell = nullptr;
ShmPool *shm = nullptr;
Seat *seat = nullptr;
PlasmaShell *plasmaShell = nullptr;
@ -213,6 +322,10 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
s_waylandConnection.layerShellV1->init(*registry, name, version);
}
}
if (interface == QByteArrayLiteral("xdg_wm_base")) {
s_waylandConnection.xdgShell = new XdgShell();
s_waylandConnection.xdgShell->init(*registry, name, version);
}
});
QSignalSpy allAnnounced(registry, &Registry::interfacesAnnounced);
@ -341,6 +454,8 @@ void destroyWaylandConnection()
s_waylandConnection.pointerConstraints = nullptr;
delete s_waylandConnection.xdgShellStable;
s_waylandConnection.xdgShellStable = nullptr;
delete s_waylandConnection.xdgShell;
s_waylandConnection.xdgShell = nullptr;
delete s_waylandConnection.shadowManager;
s_waylandConnection.shadowManager = nullptr;
delete s_waylandConnection.idleInhibit;
@ -617,7 +732,7 @@ QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(Surface *surfac
return s;
}
XdgShellPopup *createXdgShellStablePopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent, CreationSetup creationSetup)
XdgShellPopup *createXdgShellStablePopup(Surface *surface, XdgShellSurface *parentSurface, const KWayland::Client::XdgPositioner &positioner, QObject *parent, CreationSetup creationSetup)
{
if (!s_waylandConnection.xdgShellStable) {
return nullptr;
@ -653,6 +768,68 @@ void initXdgShellPopup(KWayland::Client::Surface *surface, KWayland::Client::Xdg
shellPopup->ackConfigure(configureRequestedSpy.last()[1].toInt());
}
static void waitForConfigured(XdgSurface *shellSurface)
{
QSignalSpy surfaceConfigureRequestedSpy(shellSurface, &XdgSurface::configureRequested);
QVERIFY(surfaceConfigureRequestedSpy.isValid());
shellSurface->surface()->commit(Surface::CommitFlag::None);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->ack_configure(surfaceConfigureRequestedSpy.last().first().toUInt());
}
XdgToplevel *createXdgToplevelSurface(Surface *surface, QObject *parent, CreationSetup configureMode)
{
XdgShell *shell = s_waylandConnection.xdgShell;
if (!shell) {
qWarning() << "Could not create an xdg_toplevel surface because xdg_wm_base global is not bound";
return nullptr;
}
XdgSurface *xdgSurface = new XdgSurface(shell, surface, parent);
XdgToplevel *xdgToplevel = new XdgToplevel(xdgSurface, parent);
if (configureMode == CreationSetup::CreateAndConfigure) {
waitForConfigured(xdgSurface);
}
return xdgToplevel;
}
XdgPositioner *createXdgPositioner()
{
XdgShell *shell = s_waylandConnection.xdgShell;
if (!shell) {
qWarning() << "Could not create an xdg_positioner object because xdg_wm_base global is not bound";
return nullptr;
}
return new XdgPositioner(shell);
}
XdgPopup *createXdgPopupSurface(Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner,
QObject *parent, CreationSetup configureMode)
{
XdgShell *shell = s_waylandConnection.xdgShell;
if (!shell) {
qWarning() << "Could not create an xdg_popup surface because xdg_wm_base global is not bound";
return nullptr;
}
XdgSurface *xdgSurface = new XdgSurface(shell, surface, parent);
XdgPopup *xdgPopup = new XdgPopup(xdgSurface, parentSurface, positioner, parent);
if (configureMode == CreationSetup::CreateAndConfigure) {
waitForConfigured(xdgSurface);
}
return xdgPopup;
}
bool waitForWindowDestroyed(AbstractClient *client)
{
QSignalSpy destroyedSpy(client, &QObject::destroyed);