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
|
||||
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)
|
||||
|
||||
|
|
|
@ -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[]) \
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue