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:
parent
fe68ddd9ad
commit
2691b84c99
3 changed files with 294 additions and 2 deletions
|
@ -17,6 +17,10 @@ ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework_SOURCES
|
||||||
PROTOCOL protocols/wlr-layer-shell-unstable-v1.xml
|
PROTOCOL protocols/wlr-layer-shell-unstable-v1.xml
|
||||||
BASENAME wlr-layer-shell-unstable-v1
|
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})
|
add_library(KWinIntegrationTestFramework STATIC ${KWinIntegrationTestFramework_SOURCES})
|
||||||
target_link_libraries(KWinIntegrationTestFramework kwin Qt5::Test Wayland::Client)
|
target_link_libraries(KWinIntegrationTestFramework kwin Qt5::Test Wayland::Client)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <KWayland/Client/xdgshell.h>
|
#include <KWayland/Client/xdgshell.h>
|
||||||
|
|
||||||
#include "qwayland-wlr-layer-shell-unstable-v1.h"
|
#include "qwayland-wlr-layer-shell-unstable-v1.h"
|
||||||
|
#include "qwayland-xdg-shell.h"
|
||||||
|
|
||||||
namespace KWayland
|
namespace KWayland
|
||||||
{
|
{
|
||||||
|
@ -107,6 +108,106 @@ Q_SIGNALS:
|
||||||
void configureRequested(quint32 serial, const QSize &size);
|
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 {
|
enum class AdditionalWaylandInterface {
|
||||||
Seat = 1 << 0,
|
Seat = 1 << 0,
|
||||||
Decoration = 1 << 1,
|
Decoration = 1 << 1,
|
||||||
|
@ -188,6 +289,15 @@ KWayland::Client::XdgShellPopup *createXdgShellStablePopup(KWayland::Client::Sur
|
||||||
QObject *parent = nullptr,
|
QObject *parent = nullptr,
|
||||||
CreationSetup = CreationSetup::CreateAndConfigure);
|
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
|
* 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_OPERATORS_FOR_FLAGS(KWin::Test::AdditionalWaylandInterfaces)
|
||||||
|
Q_DECLARE_METATYPE(KWin::Test::XdgToplevel::States)
|
||||||
|
|
||||||
#define WAYLANDTEST_MAIN_HELPER(TestObject, DPI, OperationMode) \
|
#define WAYLANDTEST_MAIN_HELPER(TestObject, DPI, OperationMode) \
|
||||||
int main(int argc, char *argv[]) \
|
int main(int argc, char *argv[]) \
|
||||||
|
|
|
@ -74,6 +74,114 @@ void LayerSurfaceV1::zwlr_layer_surface_v1_closed()
|
||||||
emit closeRequested();
|
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 {
|
static struct {
|
||||||
ConnectionThread *connection = nullptr;
|
ConnectionThread *connection = nullptr;
|
||||||
EventQueue *queue = nullptr;
|
EventQueue *queue = nullptr;
|
||||||
|
@ -81,7 +189,8 @@ static struct {
|
||||||
SubCompositor *subCompositor = nullptr;
|
SubCompositor *subCompositor = nullptr;
|
||||||
ServerSideDecorationManager *decoration = nullptr;
|
ServerSideDecorationManager *decoration = nullptr;
|
||||||
ShadowManager *shadowManager = nullptr;
|
ShadowManager *shadowManager = nullptr;
|
||||||
XdgShell *xdgShellStable = nullptr;
|
KWayland::Client::XdgShell *xdgShellStable = nullptr;
|
||||||
|
XdgShell *xdgShell = nullptr;
|
||||||
ShmPool *shm = nullptr;
|
ShmPool *shm = nullptr;
|
||||||
Seat *seat = nullptr;
|
Seat *seat = nullptr;
|
||||||
PlasmaShell *plasmaShell = nullptr;
|
PlasmaShell *plasmaShell = nullptr;
|
||||||
|
@ -213,6 +322,10 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
|
||||||
s_waylandConnection.layerShellV1->init(*registry, name, version);
|
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);
|
QSignalSpy allAnnounced(registry, &Registry::interfacesAnnounced);
|
||||||
|
@ -341,6 +454,8 @@ void destroyWaylandConnection()
|
||||||
s_waylandConnection.pointerConstraints = nullptr;
|
s_waylandConnection.pointerConstraints = nullptr;
|
||||||
delete s_waylandConnection.xdgShellStable;
|
delete s_waylandConnection.xdgShellStable;
|
||||||
s_waylandConnection.xdgShellStable = nullptr;
|
s_waylandConnection.xdgShellStable = nullptr;
|
||||||
|
delete s_waylandConnection.xdgShell;
|
||||||
|
s_waylandConnection.xdgShell = nullptr;
|
||||||
delete s_waylandConnection.shadowManager;
|
delete s_waylandConnection.shadowManager;
|
||||||
s_waylandConnection.shadowManager = nullptr;
|
s_waylandConnection.shadowManager = nullptr;
|
||||||
delete s_waylandConnection.idleInhibit;
|
delete s_waylandConnection.idleInhibit;
|
||||||
|
@ -617,7 +732,7 @@ QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(Surface *surfac
|
||||||
return s;
|
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) {
|
if (!s_waylandConnection.xdgShellStable) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -653,6 +768,68 @@ void initXdgShellPopup(KWayland::Client::Surface *surface, KWayland::Client::Xdg
|
||||||
shellPopup->ackConfigure(configureRequestedSpy.last()[1].toInt());
|
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)
|
bool waitForWindowDestroyed(AbstractClient *client)
|
||||||
{
|
{
|
||||||
QSignalSpy destroyedSpy(client, &QObject::destroyed);
|
QSignalSpy destroyedSpy(client, &QObject::destroyed);
|
||||||
|
|
Loading…
Reference in a new issue