Commit b44a8fb5 authored by Martin Flöser's avatar Martin Flöser
Browse files

Implementation of PointerConstraints protcol

Summary:
The pointer constraints protocol is an unstable protocol and thus
the implementation follows the semantics of unstable protocols.

The protocol allows to create a constraint on the pointer - either a
lock or a confinement on a surface. Those are not activated at once, but
when the compositor actively grants it.

During lock no further pointer motion is emitted, during confinement the
pointer is kept in a certain area.

This implements T4451.

Reviewers: #plasma_on_wayland

Subscribers: plasma-devel

Tags: #plasma_on_wayland

Differential Revision: https://phabricator.kde.org/D3466
parent c72510c9
......@@ -21,6 +21,8 @@ set(SERVER_LIB_SRCS
pointer_interface.cpp
plasmashell_interface.cpp
plasmawindowmanagement_interface.cpp
pointerconstraints_interface.cpp
pointerconstraints_interface_v1.cpp
pointergestures_interface.cpp
pointergestures_interface_v1.cpp
qtsurfaceextension_interface.cpp
......@@ -134,6 +136,11 @@ ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
BASENAME pointer-gestures-unstable-v1
)
ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-constraints-unstable-v1.xml
BASENAME pointer-constraints-unstable-v1
)
add_library(KF5WaylandServer ${SERVER_LIB_SRCS})
generate_export_header(KF5WaylandServer
BASE_NAME
......@@ -185,6 +192,7 @@ install(FILES
outputmanagement_interface.h
output_interface.h
pointer_interface.h
pointerconstraints_interface.h
pointergestures_interface.h
plasmashell_interface.h
plasmawindowmanagement_interface.h
......
......@@ -348,3 +348,10 @@ target_link_libraries( testXdgShellV5 Qt5::Test Qt5::Gui KF5::WaylandServer KF5:
add_test(kwayland-testXdgShellV5 testXdgShellV5)
ecm_mark_as_test(testXdgShellV5)
########################################################
# Test Pointer Constraints
########################################################
add_executable(testPointerConstraints test_pointer_constraints.cpp)
target_link_libraries( testPointerConstraints Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client)
add_test(kwayland-testPointerConstraints testPointerConstraints)
ecm_mark_as_test(testPointerConstraints)
/********************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
// Qt
#include <QtTest/QtTest>
// client
#include "../../src/client/connection_thread.h"
#include "../../src/client/compositor.h"
#include "../../src/client/event_queue.h"
#include "../../src/client/pointer.h"
#include "../../src/client/pointerconstraints.h"
#include "../../src/client/registry.h"
#include "../../src/client/seat.h"
#include "../../src/client/surface.h"
// server
#include "../../src/server/display.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/pointerconstraints_interface.h"
#include "../../src/server/seat_interface.h"
#include "../../src/server/surface_interface.h"
using namespace KWayland::Client;
using namespace KWayland::Server;
Q_DECLARE_METATYPE(KWayland::Client::PointerConstraints::LifeTime)
Q_DECLARE_METATYPE(KWayland::Server::ConfinedPointerInterface::LifeTime)
Q_DECLARE_METATYPE(KWayland::Server::LockedPointerInterface::LifeTime)
class TestPointerConstraints : public QObject
{
Q_OBJECT
private Q_SLOTS:
void init();
void cleanup();
void testLockPointer_data();
void testLockPointer();
void testConfinePointer_data();
void testConfinePointer();
void testAlreadyConstrained_data();
void testAlreadyConstrained();
private:
Display *m_display = nullptr;
CompositorInterface *m_compositorInterface = nullptr;
SeatInterface *m_seatInterface = nullptr;
PointerConstraintsInterface *m_pointerConstraintsInterface = nullptr;
ConnectionThread *m_connection = nullptr;
QThread *m_thread = nullptr;
EventQueue *m_queue = nullptr;
Compositor *m_compositor = nullptr;
Seat *m_seat = nullptr;
Pointer *m_pointer = nullptr;
PointerConstraints *m_pointerConstraints = nullptr;
};
static const QString s_socketName = QStringLiteral("kwayland-test-pointer_constraint-0");
void TestPointerConstraints::init()
{
delete m_display;
m_display = new Display(this);
m_display->setSocketName(s_socketName);
m_display->start();
QVERIFY(m_display->isRunning());
m_display->createShm();
m_seatInterface = m_display->createSeat(m_display);
m_seatInterface->setHasPointer(true);
m_seatInterface->create();
m_compositorInterface = m_display->createCompositor(m_display);
m_compositorInterface->create();
m_pointerConstraintsInterface = m_display->createPointerConstraints(PointerConstraintsInterfaceVersion::UnstableV1, m_display);
m_pointerConstraintsInterface->create();
// setup connection
m_connection = new KWayland::Client::ConnectionThread;
QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected);
QVERIFY(connectedSpy.isValid());
m_connection->setSocketName(s_socketName);
m_thread = new QThread(this);
m_connection->moveToThread(m_thread);
m_thread->start();
m_connection->initConnection();
QVERIFY(connectedSpy.wait());
m_queue = new EventQueue(this);
m_queue->setup(m_connection);
Registry registry;
QSignalSpy interfacesAnnouncedSpy(&registry, &Registry::interfacesAnnounced);
QVERIFY(interfacesAnnouncedSpy.isValid());
QSignalSpy interfaceAnnouncedSpy(&registry, &Registry::interfaceAnnounced);
QVERIFY(interfaceAnnouncedSpy.isValid());
registry.setEventQueue(m_queue);
registry.create(m_connection);
QVERIFY(registry.isValid());
registry.setup();
QVERIFY(interfacesAnnouncedSpy.wait());
m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this);
QVERIFY(m_compositor);
QVERIFY(m_compositor->isValid());
m_pointerConstraints = registry.createPointerConstraints(registry.interface(Registry::Interface::PointerConstraintsUnstableV1).name,
registry.interface(Registry::Interface::PointerConstraintsUnstableV1).version, this);
QVERIFY(m_pointerConstraints);
QVERIFY(m_pointerConstraints->isValid());
m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this);
QVERIFY(m_seat);
QVERIFY(m_seat->isValid());
QSignalSpy pointerChangedSpy(m_seat, &Seat::hasPointerChanged);
QVERIFY(pointerChangedSpy.isValid());
QVERIFY(pointerChangedSpy.wait());
m_pointer = m_seat->createPointer(this);
QVERIFY(m_pointer);
}
void TestPointerConstraints::cleanup()
{
#define CLEANUP(variable) \
if (variable) { \
delete variable; \
variable = nullptr; \
}
CLEANUP(m_compositor)
CLEANUP(m_pointerConstraints)
CLEANUP(m_pointer)
CLEANUP(m_seat)
CLEANUP(m_queue)
if (m_connection) {
m_connection->deleteLater();
m_connection = nullptr;
}
if (m_thread) {
m_thread->quit();
m_thread->wait();
delete m_thread;
m_thread = nullptr;
}
CLEANUP(m_compositorInterface)
CLEANUP(m_seatInterface);
CLEANUP(m_pointerConstraintsInterface)
CLEANUP(m_display)
#undef CLEANUP
}
void TestPointerConstraints::testLockPointer_data()
{
QTest::addColumn<PointerConstraints::LifeTime>("clientLifeTime");
QTest::addColumn<LockedPointerInterface::LifeTime>("serverLifeTime");
QTest::addColumn<bool>("hasConstraintAfterUnlock");
QTest::addColumn<int>("pointerChangedCount");
QTest::newRow("persistent") << PointerConstraints::LifeTime::Persistent << LockedPointerInterface::LifeTime::Persistent << true << 1;
QTest::newRow("oneshot") << PointerConstraints::LifeTime::OneShot << LockedPointerInterface::LifeTime::OneShot << false << 2;
}
void TestPointerConstraints::testLockPointer()
{
// this test verifies the basic interaction for lock pointer
// first create a surface
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
QVERIFY(surfaceCreatedSpy.isValid());
QScopedPointer<Surface> surface(m_compositor->createSurface());
QVERIFY(surface->isValid());
QVERIFY(surfaceCreatedSpy.wait());
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
QVERIFY(serverSurface);
QVERIFY(serverSurface->lockedPointer().isNull());
QVERIFY(serverSurface->confinedPointer().isNull());
// now create the locked pointer
QSignalSpy pointerConstraintsChangedSpy(serverSurface, &SurfaceInterface::pointerConstraintsChanged);
QVERIFY(pointerConstraintsChangedSpy.isValid());
QFETCH(PointerConstraints::LifeTime, clientLifeTime);
QScopedPointer<LockedPointer> lockedPointer(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, clientLifeTime));
QSignalSpy lockedSpy(lockedPointer.data(), &LockedPointer::locked);
QVERIFY(lockedSpy.isValid());
QSignalSpy unlockedSpy(lockedPointer.data(), &LockedPointer::unlocked);
QVERIFY(unlockedSpy.isValid());
QVERIFY(lockedPointer->isValid());
QVERIFY(pointerConstraintsChangedSpy.wait());
auto serverLockedPointer = serverSurface->lockedPointer();
QVERIFY(serverLockedPointer);
QVERIFY(serverSurface->confinedPointer().isNull());
QCOMPARE(serverLockedPointer->isLocked(), false);
QCOMPARE(serverLockedPointer->region(), QRegion());
QFETCH(LockedPointerInterface::LifeTime, serverLifeTime);
QCOMPARE(serverLockedPointer->lifeTime(), serverLifeTime);
// setting to unlocked now should not trigger an unlocked spy
serverLockedPointer->setLocked(false);
QVERIFY(!unlockedSpy.wait());
// try setting a region
QSignalSpy destroyedSpy(serverLockedPointer.data(), &QObject::destroyed);
QVERIFY(destroyedSpy.isValid());
QSignalSpy regionChangedSpy(serverLockedPointer.data(), &LockedPointerInterface::regionChanged);
QVERIFY(regionChangedSpy.isValid());
lockedPointer->setRegion(m_compositor->createRegion(QRegion(0, 5, 10, 20), m_compositor));
// it's double buffered
QVERIFY(!regionChangedSpy.wait());
surface->commit(Surface::CommitFlag::None);
QVERIFY(regionChangedSpy.wait());
QCOMPARE(serverLockedPointer->region(), QRegion(0, 5, 10, 20));
// let's lock the surface
QSignalSpy lockedChangedSpy(serverLockedPointer.data(), &LockedPointerInterface::lockedChanged);
QVERIFY(lockedChangedSpy.isValid());
m_seatInterface->setFocusedPointerSurface(serverSurface);
QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion);
QVERIFY(pointerMotionSpy.isValid());
m_seatInterface->setPointerPos(QPoint(0, 1));
QVERIFY(pointerMotionSpy.wait());
serverLockedPointer->setLocked(true);
QCOMPARE(serverLockedPointer->isLocked(), true);
m_seatInterface->setPointerPos(QPoint(1, 1));
QCOMPARE(lockedChangedSpy.count(), 1);
QCOMPARE(pointerMotionSpy.count(), 1);
QVERIFY(lockedSpy.isEmpty());
QVERIFY(lockedSpy.wait());
QVERIFY(unlockedSpy.isEmpty());
// and unlock again
serverLockedPointer->setLocked(false);
QCOMPARE(serverLockedPointer->isLocked(), false);
QCOMPARE(lockedChangedSpy.count(), 2);
QTEST(!serverSurface->lockedPointer().isNull(), "hasConstraintAfterUnlock");
QTEST(pointerConstraintsChangedSpy.count(), "pointerChangedCount");
QVERIFY(unlockedSpy.wait());
QCOMPARE(unlockedSpy.count(), 1);
QCOMPARE(lockedSpy.count(), 1);
// now motion should work again
m_seatInterface->setPointerPos(QPoint(0, 1));
QVERIFY(pointerMotionSpy.wait());
QCOMPARE(pointerMotionSpy.count(), 2);
lockedPointer.reset();
QVERIFY(destroyedSpy.wait());
QCOMPARE(pointerConstraintsChangedSpy.count(), 2);
}
void TestPointerConstraints::testConfinePointer_data()
{
QTest::addColumn<PointerConstraints::LifeTime>("clientLifeTime");
QTest::addColumn<ConfinedPointerInterface::LifeTime>("serverLifeTime");
QTest::addColumn<bool>("hasConstraintAfterUnlock");
QTest::addColumn<int>("pointerChangedCount");
QTest::newRow("persistent") << PointerConstraints::LifeTime::Persistent << ConfinedPointerInterface::LifeTime::Persistent << true << 1;
QTest::newRow("oneshot") << PointerConstraints::LifeTime::OneShot << ConfinedPointerInterface::LifeTime::OneShot << false << 2;
}
void TestPointerConstraints::testConfinePointer()
{
// this test verifies the basic interaction for confined pointer
// first create a surface
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
QVERIFY(surfaceCreatedSpy.isValid());
QScopedPointer<Surface> surface(m_compositor->createSurface());
QVERIFY(surface->isValid());
QVERIFY(surfaceCreatedSpy.wait());
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
QVERIFY(serverSurface);
QVERIFY(serverSurface->lockedPointer().isNull());
QVERIFY(serverSurface->confinedPointer().isNull());
// now create the confined pointer
QSignalSpy pointerConstraintsChangedSpy(serverSurface, &SurfaceInterface::pointerConstraintsChanged);
QVERIFY(pointerConstraintsChangedSpy.isValid());
QFETCH(PointerConstraints::LifeTime, clientLifeTime);
QScopedPointer<ConfinedPointer> confinedPointer(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, clientLifeTime));
QSignalSpy confinedSpy(confinedPointer.data(), &ConfinedPointer::confined);
QVERIFY(confinedSpy.isValid());
QSignalSpy unconfinedSpy(confinedPointer.data(), &ConfinedPointer::unconfined);
QVERIFY(unconfinedSpy.isValid());
QVERIFY(confinedPointer->isValid());
QVERIFY(pointerConstraintsChangedSpy.wait());
auto serverConfinedPointer = serverSurface->confinedPointer();
QVERIFY(serverConfinedPointer);
QVERIFY(serverSurface->lockedPointer().isNull());
QCOMPARE(serverConfinedPointer->isConfined(), false);
QCOMPARE(serverConfinedPointer->region(), QRegion());
QFETCH(ConfinedPointerInterface::LifeTime, serverLifeTime);
QCOMPARE(serverConfinedPointer->lifeTime(), serverLifeTime);
// setting to unconfined now should not trigger an unconfined spy
serverConfinedPointer->setConfined(false);
QVERIFY(!unconfinedSpy.wait());
// try setting a region
QSignalSpy destroyedSpy(serverConfinedPointer.data(), &QObject::destroyed);
QVERIFY(destroyedSpy.isValid());
QSignalSpy regionChangedSpy(serverConfinedPointer.data(), &ConfinedPointerInterface::regionChanged);
QVERIFY(regionChangedSpy.isValid());
confinedPointer->setRegion(m_compositor->createRegion(QRegion(0, 5, 10, 20), m_compositor));
// it's double buffered
QVERIFY(!regionChangedSpy.wait());
surface->commit(Surface::CommitFlag::None);
QVERIFY(regionChangedSpy.wait());
QCOMPARE(serverConfinedPointer->region(), QRegion(0, 5, 10, 20));
// let's confine the surface
QSignalSpy confinedChangedSpy(serverConfinedPointer.data(), &ConfinedPointerInterface::confinedChanged);
QVERIFY(confinedChangedSpy.isValid());
m_seatInterface->setFocusedPointerSurface(serverSurface);
serverConfinedPointer->setConfined(true);
QCOMPARE(serverConfinedPointer->isConfined(), true);
QCOMPARE(confinedChangedSpy.count(), 1);
QVERIFY(confinedSpy.isEmpty());
QVERIFY(confinedSpy.wait());
QVERIFY(unconfinedSpy.isEmpty());
// and unconfine again
serverConfinedPointer->setConfined(false);
QCOMPARE(serverConfinedPointer->isConfined(), false);
QCOMPARE(confinedChangedSpy.count(), 2);
QTEST(!serverSurface->confinedPointer().isNull(), "hasConstraintAfterUnlock");
QTEST(pointerConstraintsChangedSpy.count(), "pointerChangedCount");
QVERIFY(unconfinedSpy.wait());
QCOMPARE(unconfinedSpy.count(), 1);
QCOMPARE(confinedSpy.count(), 1);
confinedPointer.reset();
QVERIFY(destroyedSpy.wait());
QCOMPARE(pointerConstraintsChangedSpy.count(), 2);
}
enum class Constraint {
Lock,
Confine
};
Q_DECLARE_METATYPE(Constraint)
void TestPointerConstraints::testAlreadyConstrained_data()
{
QTest::addColumn<Constraint>("firstConstraint");
QTest::addColumn<Constraint>("secondConstraint");
QTest::newRow("confine-confine") << Constraint::Confine << Constraint::Confine;
QTest::newRow("lock-confine") << Constraint::Lock << Constraint::Confine;
QTest::newRow("confine-lock") << Constraint::Confine << Constraint::Lock;
QTest::newRow("lock-lock") << Constraint::Lock << Constraint::Lock;
}
void TestPointerConstraints::testAlreadyConstrained()
{
// this test verifies that creating a pointer constraint for an already constrained surface triggers an error
// first create a surface
QScopedPointer<Surface> surface(m_compositor->createSurface());
QVERIFY(surface->isValid());
QFETCH(Constraint, firstConstraint);
QScopedPointer<ConfinedPointer> confinedPointer;
QScopedPointer<LockedPointer> lockedPointer;
switch (firstConstraint) {
case Constraint::Lock:
lockedPointer.reset(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot));
break;
case Constraint::Confine:
confinedPointer.reset(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot));
break;
default:
Q_UNREACHABLE();
}
QVERIFY(confinedPointer || lockedPointer);
QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred);
QVERIFY(errorSpy.isValid());
QFETCH(Constraint, secondConstraint);
QScopedPointer<ConfinedPointer> confinedPointer2;
QScopedPointer<LockedPointer> lockedPointer2;
switch (secondConstraint) {
case Constraint::Lock:
lockedPointer2.reset(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot));
break;
case Constraint::Confine:
confinedPointer2.reset(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot));
break;
default:
Q_UNREACHABLE();
}
QVERIFY(errorSpy.wait());
QVERIFY(m_connection->hasError());
if (confinedPointer2) {
confinedPointer2->destroy();
}
if (lockedPointer2) {
lockedPointer2->destroy();
}
if (confinedPointer) {
confinedPointer->destroy();
}
if (lockedPointer) {
lockedPointer->destroy();
}
surface->destroy();
m_compositor->destroy();
m_pointerConstraints->destroy();
m_pointer->destroy();
m_seat->destroy();
m_queue->destroy();
}
QTEST_GUILESS_MAIN(TestPointerConstraints)
#include "test_pointer_constraints.moc"
......@@ -26,6 +26,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include "../../src/client/event_queue.h"
#include "../../src/client/registry.h"
#include "../../src/client/output.h"
#include "../../src/client/pointerconstraints.h"
#include "../../src/client/pointergestures.h"
#include "../../src/client/seat.h"
#include "../../src/client/relativepointer.h"
......@@ -47,6 +48,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include "../../src/server/subcompositor_interface.h"
#include "../../src/server/outputmanagement_interface.h"
#include "../../src/server/outputdevice_interface.h"
#include "../../src/server/pointerconstraints_interface.h"
#include "../../src/server/pointergestures_interface.h"
#include "../../src/server/textinput_interface.h"
#include "../../src/server/xdgshell_interface.h"
......@@ -60,6 +62,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include <wayland-xdg-shell-v5-client-protocol.h>
#include <wayland-relativepointer-unstable-v1-client-protocol.h>
#include <wayland-pointer-gestures-unstable-v1-client-protocol.h>
#include <wayland-pointer-constraints-unstable-v1-client-protocol.h>
class TestWaylandRegistry : public QObject
{
......@@ -88,6 +91,7 @@ private Q_SLOTS:
void testBindXdgShellUnstableV5();
void testBindRelativePointerManagerUnstableV1();
void testBindPointerGesturesUnstableV1();
void testBindPointerConstraintsUnstableV1();
void testGlobalSync();
void testGlobalSyncThreaded();
void testRemoval();
......@@ -111,6 +115,7 @@ private:
KWayland::Server::XdgShellInterface *m_xdgShellUnstableV5;
KWayland::Server::RelativePointerManagerInterface *m_relativePointerV1;
KWayland::Server::PointerGesturesInterface *m_pointerGesturesV1;
KWayland::Server::PointerConstraintsInterface *m_pointerConstraintsV1;
};
static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0");
......@@ -132,6 +137,7 @@ TestWaylandRegistry::TestWaylandRegistry(QObject *parent)
, m_xdgShellUnstableV5(nullptr)
, m_relativePointerV1(nullptr)
, m_pointerGesturesV1(nullptr)
, m_pointerConstraintsV1(nullptr)
{
}
......@@ -179,6 +185,9 @@ void TestWaylandRegistry::init()
m_pointerGesturesV1 = m_display->createPointerGestures(KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1);
m_pointerGesturesV1->create();
QCOMPARE(m_pointerGesturesV1->interfaceVersion(), KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1);
m_pointerConstraintsV1 = m_display->createPointerConstraints(KWayland::Server::PointerConstraintsInterfaceVersion::UnstableV1);
m_pointerConstraintsV1->create();
QCOMPARE(m_pointerConstraintsV1->interfaceVersion(), KWayland::Server::PointerConstraintsInterfaceVersion::UnstableV1);
}
void TestWaylandRegistry::cleanup()
......@@ -331,6 +340,11 @@ void TestWaylandRegistry::testBindPointerGesturesUnstableV1()
TEST_BIND(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1, SIGNAL(pointerGesturesUnstableV1Announced(quint32,quint32)), bindPointerGesturesUnstableV1, zwp_pointer_gestures_v1_destroy)
}
void TestWaylandRegistry::testBindPointerConstraintsUnstableV1()
{
TEST_BIND(KWayland::Client::Registry::Interface::PointerConstraintsUnstableV1, SIGNAL(pointerConstraintsUnstableV1Announced(quint32,quint32)), bindPointerConstraintsUnstableV1, zwp_pointer_constraints_v1_destroy)
}
#undef TEST_BIND
void TestWaylandRegistry::testRemoval()
......
......@@ -30,6 +30,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include "output_interface.h"
#include "plasmashell_interface.h"
#include "plasmawindowmanagement_interface.h"
#include "pointerconstraints_interface_p.h"
#include "pointergestures_interface_p.h"
#include "qtsurfaceextension_interface.h"
#include "seat_interface.h"
......@@ -395,6 +396,18 @@ PointerGesturesInterface *Display::createPointerGestures(const PointerGesturesIn
return p;
}
PointerConstraintsInterface *Display::createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent)
{
PointerConstraintsInterface *p = nullptr;
switch (version) {
case PointerConstraintsInterfaceVersion::UnstableV1:
p = new PointerConstraintsUnstableV1Interface(this, parent);
break;
}
connect(this, &Display::aboutToTerminate, p, [p] { delete p; });
return p;
}
void Display::createShm()
{
Q_ASSERT(d->display);
......
......@@ -78,6 +78,8 @@ enum class RelativePointerInterfaceVersion;
class RelativePointerManagerInterface;
enum class PointerGesturesInterfaceVersion;
class PointerGesturesInterface;
enum class PointerConstraintsInterfaceVersion;
class PointerConstraintsInterface;
/**
* @brief Class holding the Wayland server display loop.
......@@ -209,6 +211,14 @@ public:
**/
PointerGesturesInterface *createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent = nullptr);
/**
* Creates the PointerConstraintsInterface in interface @p version
*
* @returns The created manager object
* @since 5.29
**/
PointerConstraintsInterface *createPointerConstraints(const PointerConstraintsInterfaceVersion &