10#include "KWayland/Client/compositor.h"
11#include "KWayland/Client/connection_thread.h"
12#include "KWayland/Client/event_queue.h"
13#include "KWayland/Client/pointer.h"
14#include "KWayland/Client/pointerconstraints.h"
15#include "KWayland/Client/registry.h"
16#include "KWayland/Client/seat.h"
17#include "KWayland/Client/shm_pool.h"
18#include "KWayland/Client/surface.h"
39 void testLockPointer_data();
40 void testLockPointer();
42 void testConfinePointer_data();
43 void testConfinePointer();
44 void testAlreadyConstrained_data();
45 void testAlreadyConstrained();
52 KWayland::Client::ConnectionThread *m_connection =
nullptr;
53 QThread *m_thread =
nullptr;
54 KWayland::Client::EventQueue *m_queue =
nullptr;
55 KWayland::Client::Compositor *m_compositor =
nullptr;
56 KWayland::Client::Seat *m_seat =
nullptr;
57 KWayland::Client::ShmPool *m_shm =
nullptr;
58 KWayland::Client::Pointer *m_pointer =
nullptr;
59 KWayland::Client::PointerConstraints *m_pointerConstraints =
nullptr;
62static const QString s_socketName = QStringLiteral(
"kwayland-test-pointer_constraint-0");
64void TestPointerConstraints::init()
78 m_connection =
new KWayland::Client::ConnectionThread;
79 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
80 m_connection->setSocketName(s_socketName);
82 m_thread =
new QThread(
this);
83 m_connection->moveToThread(m_thread);
86 m_connection->initConnection();
87 QVERIFY(connectedSpy.wait());
89 m_queue =
new KWayland::Client::EventQueue(
this);
90 m_queue->setup(m_connection);
93 QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced);
94 QSignalSpy interfaceAnnouncedSpy(®istry, &KWayland::Client::Registry::interfaceAnnounced);
99 QVERIFY(interfacesAnnouncedSpy.wait());
101 m_shm =
new KWayland::Client::ShmPool(
this);
102 m_shm->setup(
registry.bindShm(
registry.interface(KWayland::Client::Registry::Interface::Shm).name,
103 registry.interface(KWayland::Client::Registry::Interface::Shm).version));
104 QVERIFY(m_shm->isValid());
107 registry.createCompositor(
registry.interface(KWayland::Client::Registry::Interface::Compositor).name,
registry.interface(KWayland::Client::Registry::Interface::Compositor).version,
this);
108 QVERIFY(m_compositor);
109 QVERIFY(m_compositor->isValid());
111 m_pointerConstraints =
registry.createPointerConstraints(
registry.interface(KWayland::Client::Registry::Interface::PointerConstraintsUnstableV1).name,
112 registry.interface(KWayland::Client::Registry::Interface::PointerConstraintsUnstableV1).version,
114 QVERIFY(m_pointerConstraints);
115 QVERIFY(m_pointerConstraints->isValid());
117 m_seat =
registry.createSeat(
registry.interface(KWayland::Client::Registry::Interface::Seat).name,
registry.interface(KWayland::Client::Registry::Interface::Seat).version,
this);
119 QVERIFY(m_seat->isValid());
120 QSignalSpy pointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
121 QVERIFY(pointerChangedSpy.wait());
122 m_pointer = m_seat->createPointer(
this);
126void TestPointerConstraints::cleanup()
128#define CLEANUP(variable) \
131 variable = nullptr; \
140 m_connection->deleteLater();
141 m_connection =
nullptr;
154 m_compositorInterface =
nullptr;
155 m_seatInterface =
nullptr;
156 m_pointerConstraintsInterface =
nullptr;
159void TestPointerConstraints::testLockPointer_data()
161 QTest::addColumn<KWayland::Client::PointerConstraints::LifeTime>(
"clientLifeTime");
162 QTest::addColumn<LockedPointerV1Interface::LifeTime>(
"serverLifeTime");
163 QTest::addColumn<bool>(
"hasConstraintAfterUnlock");
164 QTest::addColumn<int>(
"pointerChangedCount");
166 QTest::newRow(
"persistent") << KWayland::Client::PointerConstraints::LifeTime::Persistent << LockedPointerV1Interface::LifeTime::Persistent <<
true << 1;
167 QTest::newRow(
"oneshot") << KWayland::Client::PointerConstraints::LifeTime::OneShot << LockedPointerV1Interface::LifeTime::OneShot <<
false << 2;
170void TestPointerConstraints::testLockPointer()
175 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
176 QVERIFY(surface->isValid());
177 QVERIFY(surfaceCreatedSpy.wait());
179 QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
180 image.fill(Qt::black);
181 surface->attachBuffer(m_shm->createBuffer(image));
182 surface->damage(image.rect());
183 surface->commit(KWayland::Client::Surface::CommitFlag::None);
185 auto serverSurface = surfaceCreatedSpy.first().first().value<
SurfaceInterface *>();
186 QVERIFY(serverSurface);
187 QVERIFY(!serverSurface->lockedPointer());
188 QVERIFY(!serverSurface->confinedPointer());
192 QFETCH(KWayland::Client::PointerConstraints::LifeTime, clientLifeTime);
193 std::unique_ptr<KWayland::Client::LockedPointer> lockedPointer(m_pointerConstraints->lockPointer(surface.get(), m_pointer,
nullptr, clientLifeTime));
194 QSignalSpy lockedSpy(lockedPointer.get(), &KWayland::Client::LockedPointer::locked);
195 QSignalSpy unlockedSpy(lockedPointer.get(), &KWayland::Client::LockedPointer::unlocked);
196 QVERIFY(lockedPointer->isValid());
197 QVERIFY(pointerConstraintsChangedSpy.wait());
199 auto serverLockedPointer = serverSurface->lockedPointer();
200 QVERIFY(serverLockedPointer);
201 QVERIFY(!serverSurface->confinedPointer());
203 QCOMPARE(serverLockedPointer->isLocked(),
false);
204 QCOMPARE(serverLockedPointer->region(), QRegion(0, 0, 100, 100));
206 QCOMPARE(serverLockedPointer->lifeTime(), serverLifeTime);
208 serverLockedPointer->setLocked(
false);
209 QVERIFY(!unlockedSpy.wait(500));
212 QSignalSpy destroyedSpy(serverLockedPointer, &QObject::destroyed);
214 lockedPointer->setRegion(m_compositor->createRegion(QRegion(0, 5, 10, 20), m_compositor));
216 QVERIFY(!regionChangedSpy.wait(500));
217 surface->commit(KWayland::Client::Surface::CommitFlag::None);
218 QVERIFY(regionChangedSpy.wait());
219 QCOMPARE(serverLockedPointer->region(), QRegion(0, 5, 10, 20));
221 lockedPointer->setRegion(
nullptr);
222 surface->commit(KWayland::Client::Surface::CommitFlag::None);
223 QVERIFY(regionChangedSpy.wait());
224 QCOMPARE(serverLockedPointer->region(), QRegion(0, 0, 100, 100));
229 QSignalSpy pointerMotionSpy(m_pointer, &KWayland::Client::Pointer::motion);
232 QVERIFY(pointerMotionSpy.wait());
234 serverLockedPointer->setLocked(
true);
235 QCOMPARE(serverLockedPointer->isLocked(),
true);
238 QCOMPARE(lockedChangedSpy.count(), 1);
239 QCOMPARE(pointerMotionSpy.count(), 1);
240 QVERIFY(lockedSpy.isEmpty());
241 QVERIFY(lockedSpy.wait());
242 QVERIFY(unlockedSpy.isEmpty());
244 const QPointF hint = QPointF(1.5, 0.5);
246 lockedPointer->setCursorPositionHint(hint);
247 QCOMPARE(serverLockedPointer->cursorPositionHint(), QPointF(-1., -1.));
248 surface->commit(KWayland::Client::Surface::CommitFlag::None);
249 QVERIFY(hintChangedSpy.wait());
250 QCOMPARE(serverLockedPointer->cursorPositionHint(), hint);
253 serverLockedPointer->setLocked(
false);
254 QCOMPARE(serverLockedPointer->isLocked(),
false);
255 QCOMPARE(serverLockedPointer->cursorPositionHint(), QPointF(-1., -1.));
256 QCOMPARE(lockedChangedSpy.count(), 2);
257 QTEST(
bool(serverSurface->lockedPointer()),
"hasConstraintAfterUnlock");
258 QFETCH(
int, pointerChangedCount);
259 QCOMPARE(pointerConstraintsChangedSpy.count(), pointerChangedCount);
260 QVERIFY(unlockedSpy.wait());
261 QCOMPARE(unlockedSpy.count(), 1);
262 QCOMPARE(lockedSpy.count(), 1);
267 QVERIFY(pointerMotionSpy.wait());
268 QCOMPARE(pointerMotionSpy.count(), 2);
270 lockedPointer.reset();
271 QVERIFY(destroyedSpy.wait());
272 QCOMPARE(pointerConstraintsChangedSpy.count(), 2);
275void TestPointerConstraints::testConfinePointer_data()
277 QTest::addColumn<KWayland::Client::PointerConstraints::LifeTime>(
"clientLifeTime");
278 QTest::addColumn<ConfinedPointerV1Interface::LifeTime>(
"serverLifeTime");
279 QTest::addColumn<bool>(
"hasConstraintAfterUnlock");
280 QTest::addColumn<int>(
"pointerChangedCount");
282 QTest::newRow(
"persistent") << KWayland::Client::PointerConstraints::LifeTime::Persistent << ConfinedPointerV1Interface::LifeTime::Persistent <<
true << 1;
283 QTest::newRow(
"oneshot") << KWayland::Client::PointerConstraints::LifeTime::OneShot << ConfinedPointerV1Interface::LifeTime::OneShot <<
false << 2;
286void TestPointerConstraints::testConfinePointer()
291 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
292 QVERIFY(surface->isValid());
293 QVERIFY(surfaceCreatedSpy.wait());
295 QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
296 image.fill(Qt::black);
297 surface->attachBuffer(m_shm->createBuffer(image));
298 surface->damage(image.rect());
299 surface->commit(KWayland::Client::Surface::CommitFlag::None);
301 auto serverSurface = surfaceCreatedSpy.first().first().value<
SurfaceInterface *>();
302 QVERIFY(serverSurface);
303 QVERIFY(!serverSurface->lockedPointer());
304 QVERIFY(!serverSurface->confinedPointer());
308 QFETCH(KWayland::Client::PointerConstraints::LifeTime, clientLifeTime);
309 std::unique_ptr<KWayland::Client::ConfinedPointer> confinedPointer(m_pointerConstraints->confinePointer(surface.get(), m_pointer,
nullptr, clientLifeTime));
310 QSignalSpy confinedSpy(confinedPointer.get(), &KWayland::Client::ConfinedPointer::confined);
311 QSignalSpy unconfinedSpy(confinedPointer.get(), &KWayland::Client::ConfinedPointer::unconfined);
312 QVERIFY(confinedPointer->isValid());
313 QVERIFY(pointerConstraintsChangedSpy.wait());
315 auto serverConfinedPointer = serverSurface->confinedPointer();
316 QVERIFY(serverConfinedPointer);
317 QVERIFY(!serverSurface->lockedPointer());
319 QCOMPARE(serverConfinedPointer->isConfined(),
false);
320 QCOMPARE(serverConfinedPointer->region(), QRegion(0, 0, 100, 100));
322 QCOMPARE(serverConfinedPointer->lifeTime(), serverLifeTime);
324 serverConfinedPointer->setConfined(
false);
325 QVERIFY(!unconfinedSpy.wait(500));
328 QSignalSpy destroyedSpy(serverConfinedPointer, &QObject::destroyed);
330 confinedPointer->setRegion(m_compositor->createRegion(QRegion(0, 5, 10, 20), m_compositor));
332 QVERIFY(!regionChangedSpy.wait(500));
333 surface->commit(KWayland::Client::Surface::CommitFlag::None);
334 QVERIFY(regionChangedSpy.wait());
335 QCOMPARE(serverConfinedPointer->region(), QRegion(0, 5, 10, 20));
337 confinedPointer->setRegion(
nullptr);
338 surface->commit(KWayland::Client::Surface::CommitFlag::None);
339 QVERIFY(regionChangedSpy.wait());
340 QCOMPARE(serverConfinedPointer->region(), QRegion(0, 0, 100, 100));
345 serverConfinedPointer->setConfined(
true);
346 QCOMPARE(serverConfinedPointer->isConfined(),
true);
347 QCOMPARE(confinedChangedSpy.count(), 1);
348 QVERIFY(confinedSpy.isEmpty());
349 QVERIFY(confinedSpy.wait());
350 QVERIFY(unconfinedSpy.isEmpty());
353 serverConfinedPointer->setConfined(
false);
354 QCOMPARE(serverConfinedPointer->isConfined(),
false);
355 QCOMPARE(confinedChangedSpy.count(), 2);
356 QTEST(
bool(serverSurface->confinedPointer()),
"hasConstraintAfterUnlock");
357 QFETCH(
int, pointerChangedCount);
358 QCOMPARE(pointerConstraintsChangedSpy.count(), pointerChangedCount);
359 QVERIFY(unconfinedSpy.wait());
360 QCOMPARE(unconfinedSpy.count(), 1);
361 QCOMPARE(confinedSpy.count(), 1);
363 confinedPointer.reset();
364 QVERIFY(destroyedSpy.wait());
365 QCOMPARE(pointerConstraintsChangedSpy.count(), 2);
375void TestPointerConstraints::testAlreadyConstrained_data()
377 QTest::addColumn<Constraint>(
"firstConstraint");
378 QTest::addColumn<Constraint>(
"secondConstraint");
386void TestPointerConstraints::testAlreadyConstrained()
390 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
391 QVERIFY(surface->isValid());
393 std::unique_ptr<KWayland::Client::ConfinedPointer> confinedPointer;
394 std::unique_ptr<KWayland::Client::LockedPointer> lockedPointer;
395 switch (firstConstraint) {
397 lockedPointer.reset(m_pointerConstraints->lockPointer(surface.get(), m_pointer,
nullptr, KWayland::Client::PointerConstraints::LifeTime::OneShot));
400 confinedPointer.reset(m_pointerConstraints->confinePointer(surface.get(), m_pointer,
nullptr, KWayland::Client::PointerConstraints::LifeTime::OneShot));
405 QVERIFY(confinedPointer || lockedPointer);
407 QSignalSpy errorSpy(m_connection, &KWayland::Client::ConnectionThread::errorOccurred);
409 std::unique_ptr<KWayland::Client::ConfinedPointer> confinedPointer2;
410 std::unique_ptr<KWayland::Client::LockedPointer> lockedPointer2;
411 switch (secondConstraint) {
413 lockedPointer2.reset(m_pointerConstraints->lockPointer(surface.get(), m_pointer,
nullptr, KWayland::Client::PointerConstraints::LifeTime::OneShot));
416 confinedPointer2.reset(m_pointerConstraints->confinePointer(surface.get(), m_pointer,
nullptr, KWayland::Client::PointerConstraints::LifeTime::OneShot));
421 QVERIFY(errorSpy.wait());
422 QVERIFY(m_connection->hasError());
423 if (confinedPointer2) {
424 confinedPointer2->destroy();
426 if (lockedPointer2) {
427 lockedPointer2->destroy();
429 if (confinedPointer) {
430 confinedPointer->destroy();
433 lockedPointer->destroy();
436 m_compositor->destroy();
437 m_pointerConstraints->destroy();
438 m_pointer->destroy();
444#include "test_pointer_constraints.moc"
void surfaceCreated(KWin::SurfaceInterface *surface)
Class holding the Wayland server display loop.
bool addSocketName(const QString &name=QString())
void cursorPositionHintChanged()
Represents a Seat on the Wayland Display.
void notifyPointerMotion(const QPointF &pos)
void setHasPointer(bool has)
void notifyPointerEnter(SurfaceInterface *surface, const QPointF &position, const QPointF &surfacePosition=QPointF())
void notifyPointerFrame()
Resource representing a wl_surface.
void pointerConstraintsChanged()
KWayland::Client::Registry * registry
#define CLEANUP(variable)