KWin
Loading...
Searching...
No Matches
test_wayland_surface.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6// Qt
7#include <QImage>
8#include <QPainter>
9#include <QSignalSpy>
10#include <QTest>
11// KWin
12#include "core/graphicsbuffer.h"
15#include "wayland/compositor.h"
16#include "wayland/display.h"
18#include "wayland/output.h"
19#include "wayland/surface.h"
20
21#include "KWayland/Client/compositor.h"
22#include "KWayland/Client/connection_thread.h"
23#include "KWayland/Client/event_queue.h"
24#include "KWayland/Client/idleinhibit.h"
25#include "KWayland/Client/output.h"
26#include "KWayland/Client/region.h"
27#include "KWayland/Client/registry.h"
28#include "KWayland/Client/shm_pool.h"
29#include "KWayland/Client/surface.h"
30
32
33// Wayland
34#include <wayland-client-protocol.h>
35
36class TestWaylandSurface : public QObject
37{
38 Q_OBJECT
39public:
40 explicit TestWaylandSurface(QObject *parent = nullptr);
41private Q_SLOTS:
42 void init();
43 void cleanup();
44
45 void testStaticAccessor();
46 void testDamage();
47 void testFrameCallback();
48 void testAttachBuffer();
49 void testOpaque();
50 void testInput();
51 void testScale();
52 void testUnmapOfNotMappedSurface();
53 void testSurfaceAt();
54 void testDestroyAttachedBuffer();
55 void testDestroyWithPendingCallback();
56 void testOutput();
57 void testDisconnect();
58 void testInhibit();
59
60private:
61 KWin::Display *m_display;
62 KWin::CompositorInterface *m_compositorInterface;
63 KWin::IdleInhibitManagerV1Interface *m_idleInhibitInterface;
64 KWayland::Client::ConnectionThread *m_connection;
65 KWayland::Client::Compositor *m_compositor;
66 KWayland::Client::ShmPool *m_shm;
67 KWayland::Client::EventQueue *m_queue;
68 KWayland::Client::IdleInhibitManager *m_idleInhibitManager;
69 QThread *m_thread;
70};
71
72static const QString s_socketName = QStringLiteral("kwin-test-wayland-surface-0");
73
75 : QObject(parent)
76 , m_display(nullptr)
77 , m_compositorInterface(nullptr)
78 , m_connection(nullptr)
79 , m_compositor(nullptr)
80 , m_thread(nullptr)
81{
82}
83
84void TestWaylandSurface::init()
85{
86 using namespace KWin;
87 delete m_display;
88 m_display = new KWin::Display(this);
89 m_display->addSocketName(s_socketName);
90 m_display->start();
91 QVERIFY(m_display->isRunning());
92 m_display->createShm();
93
94 m_compositorInterface = new CompositorInterface(m_display, m_display);
95 QVERIFY(m_compositorInterface);
96
97 m_idleInhibitInterface = new IdleInhibitManagerV1Interface(m_display, m_display);
98 QVERIFY(m_idleInhibitInterface);
99
100 // setup connection
101 m_connection = new KWayland::Client::ConnectionThread;
102 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
103 m_connection->setSocketName(s_socketName);
104
105 m_thread = new QThread(this);
106 m_connection->moveToThread(m_thread);
107 m_thread->start();
108
109 /*connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, m_connection,
110 [this]() {
111 if (m_connection->display()) {
112 wl_display_flush(m_connection->display());
113 }
114 }
115 );*/
116
117 m_connection->initConnection();
118 QVERIFY(connectedSpy.wait());
119
120 m_queue = new KWayland::Client::EventQueue(this);
121 QVERIFY(!m_queue->isValid());
122 m_queue->setup(m_connection);
123 QVERIFY(m_queue->isValid());
124
125 KWayland::Client::Registry registry;
126 registry.setEventQueue(m_queue);
127 QSignalSpy compositorSpy(&registry, &KWayland::Client::Registry::compositorAnnounced);
128 QSignalSpy shmSpy(&registry, &KWayland::Client::Registry::shmAnnounced);
129 QSignalSpy allAnnounced(&registry, &KWayland::Client::Registry::interfacesAnnounced);
130 registry.create(m_connection->display());
131 QVERIFY(registry.isValid());
132 registry.setup();
133 QVERIFY(allAnnounced.wait());
134 QVERIFY(!compositorSpy.isEmpty());
135 QVERIFY(!shmSpy.isEmpty());
136
137 m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this);
138 QVERIFY(m_compositor->isValid());
139 m_shm = registry.createShmPool(shmSpy.first().first().value<quint32>(), shmSpy.first().last().value<quint32>(), this);
140 QVERIFY(m_shm->isValid());
141
142 m_idleInhibitManager = registry.createIdleInhibitManager(registry.interface(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1).name,
143 registry.interface(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1).version,
144 this);
145 QVERIFY(m_idleInhibitManager->isValid());
146}
147
148void TestWaylandSurface::cleanup()
149{
150 if (m_compositor) {
151 delete m_compositor;
152 m_compositor = nullptr;
153 }
154 if (m_idleInhibitManager) {
155 delete m_idleInhibitManager;
156 m_idleInhibitManager = nullptr;
157 }
158 if (m_shm) {
159 delete m_shm;
160 m_shm = nullptr;
161 }
162 if (m_queue) {
163 delete m_queue;
164 m_queue = nullptr;
165 }
166 if (m_thread) {
167 m_thread->quit();
168 m_thread->wait();
169 delete m_thread;
170 m_thread = nullptr;
171 }
172 delete m_connection;
173 m_connection = nullptr;
174
175 delete m_display;
176 m_display = nullptr;
177
178 // these are the children of the display
179 m_compositorInterface = nullptr;
180 m_idleInhibitInterface = nullptr;
181}
182
183void TestWaylandSurface::testStaticAccessor()
184{
185 QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
186
187 QVERIFY(!KWin::SurfaceInterface::get(nullptr));
188 QVERIFY(!KWin::SurfaceInterface::get(1, nullptr));
189 QVERIFY(KWayland::Client::Surface::all().isEmpty());
190 std::unique_ptr<KWayland::Client::Surface> s1(m_compositor->createSurface());
191 QVERIFY(s1->isValid());
192 QCOMPARE(KWayland::Client::Surface::all().count(), 1);
193 QCOMPARE(KWayland::Client::Surface::all().first(), s1.get());
194 QCOMPARE(KWayland::Client::Surface::get(*s1), s1.get());
195 QVERIFY(serverSurfaceCreated.wait());
196 auto serverSurface1 = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
197 QVERIFY(serverSurface1);
198 QCOMPARE(KWin::SurfaceInterface::get(serverSurface1->resource()), serverSurface1);
199 QCOMPARE(KWin::SurfaceInterface::get(serverSurface1->id(), serverSurface1->client()), serverSurface1);
200
201 QVERIFY(!s1->size().isValid());
202 QSignalSpy sizeChangedSpy(s1.get(), &KWayland::Client::Surface::sizeChanged);
203 const QSize testSize(200, 300);
204 s1->setSize(testSize);
205 QCOMPARE(s1->size(), testSize);
206 QCOMPARE(sizeChangedSpy.count(), 1);
207 QCOMPARE(sizeChangedSpy.first().first().toSize(), testSize);
208
209 // add another surface
210 std::unique_ptr<KWayland::Client::Surface> s2(m_compositor->createSurface());
211 QVERIFY(s2->isValid());
212 QCOMPARE(KWayland::Client::Surface::all().count(), 2);
213 QCOMPARE(KWayland::Client::Surface::all().first(), s1.get());
214 QCOMPARE(KWayland::Client::Surface::all().last(), s2.get());
215 QCOMPARE(KWayland::Client::Surface::get(*s1), s1.get());
216 QCOMPARE(KWayland::Client::Surface::get(*s2), s2.get());
217 serverSurfaceCreated.clear();
218 QVERIFY(serverSurfaceCreated.wait());
219 auto serverSurface2 = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
220 QVERIFY(serverSurface2);
221 QCOMPARE(KWin::SurfaceInterface::get(serverSurface1->resource()), serverSurface1);
222 QCOMPARE(KWin::SurfaceInterface::get(serverSurface1->id(), serverSurface1->client()), serverSurface1);
223 QCOMPARE(KWin::SurfaceInterface::get(serverSurface2->resource()), serverSurface2);
224 QCOMPARE(KWin::SurfaceInterface::get(serverSurface2->id(), serverSurface2->client()), serverSurface2);
225
226 const quint32 surfaceId1 = serverSurface1->id();
227 const quint32 surfaceId2 = serverSurface2->id();
228
229 // delete s2 again
230 s2.reset();
231 QCOMPARE(KWayland::Client::Surface::all().count(), 1);
232 QCOMPARE(KWayland::Client::Surface::all().first(), s1.get());
233 QCOMPARE(KWayland::Client::Surface::get(*s1), s1.get());
234
235 // and finally delete the last one
236 s1.reset();
237 QVERIFY(KWayland::Client::Surface::all().isEmpty());
238 QVERIFY(!KWayland::Client::Surface::get(nullptr));
239 QSignalSpy destroyedSpy(serverSurface1, &KWin::SurfaceInterface::destroyed);
240 QVERIFY(destroyedSpy.wait());
241 QVERIFY(!KWin::SurfaceInterface::get(nullptr));
242 QVERIFY(!KWin::SurfaceInterface::get(surfaceId1, nullptr));
243 QVERIFY(!KWin::SurfaceInterface::get(surfaceId2, nullptr));
244}
245
246void TestWaylandSurface::testDamage()
247{
248 QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
249 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
250 s->setScale(2);
251 QVERIFY(serverSurfaceCreated.wait());
252 KWin::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
253 QVERIFY(serverSurface);
254 QCOMPARE(serverSurface->bufferDamage(), QRegion());
255 QVERIFY(!serverSurface->isMapped());
256
257 QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
258 QSignalSpy damageSpy(serverSurface, &KWin::SurfaceInterface::damaged);
259
260 // send damage without a buffer
261 {
262 s->damage(QRect(0, 0, 100, 100));
263 s->commit(KWayland::Client::Surface::CommitFlag::None);
264 wl_display_flush(m_connection->display());
265 QCoreApplication::processEvents();
266 QCoreApplication::processEvents();
267 QVERIFY(damageSpy.isEmpty());
268 QVERIFY(!serverSurface->isMapped());
269 QCOMPARE(committedSpy.count(), 1);
270 }
271
272 // surface damage
273 {
274 QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
275 img.fill(Qt::black);
276 auto b = m_shm->createBuffer(img);
277 s->attachBuffer(b, QPoint(55, 55));
278 s->damage(QRect(0, 0, 10, 10));
279 s->commit(KWayland::Client::Surface::CommitFlag::None);
280 QVERIFY(damageSpy.wait());
281 QCOMPARE(serverSurface->offset(), QPoint(55, 55)); // offset is surface local so scale doesn't change this
282 QCOMPARE(serverSurface->bufferDamage(), QRegion(0, 0, 10, 10));
283 QCOMPARE(damageSpy.first().first().value<QRegion>(), QRegion(0, 0, 10, 10));
284 QVERIFY(serverSurface->isMapped());
285 QCOMPARE(committedSpy.count(), 2);
286 }
287
288 // damage multiple times
289 {
290 const QRegion surfaceDamage = QRegion(5, 8, 3, 6).united(QRect(10, 11, 6, 1));
291 const QRegion expectedDamage = QRegion(10, 16, 6, 12).united(QRect(20, 22, 12, 2));
292 QImage img(QSize(80, 70), QImage::Format_ARGB32_Premultiplied);
293 img.fill(Qt::black);
294 auto b = m_shm->createBuffer(img);
295 s->attachBuffer(b);
296 s->damage(surfaceDamage);
297 damageSpy.clear();
298 s->commit(KWayland::Client::Surface::CommitFlag::None);
299 QVERIFY(damageSpy.wait());
300 QCOMPARE(serverSurface->bufferDamage(), expectedDamage);
301 QCOMPARE(damageSpy.first().first().value<QRegion>(), expectedDamage);
302 QVERIFY(serverSurface->isMapped());
303 QCOMPARE(committedSpy.count(), 3);
304 }
305
306 // damage buffer
307 {
308 const QRegion damage(30, 40, 22, 4);
309 QImage img(QSize(80, 70), QImage::Format_ARGB32_Premultiplied);
310 img.fill(Qt::black);
311 auto b = m_shm->createBuffer(img);
312 s->attachBuffer(b);
313 s->damageBuffer(damage);
314 damageSpy.clear();
315 s->commit(KWayland::Client::Surface::CommitFlag::None);
316 QVERIFY(damageSpy.wait());
317 QCOMPARE(serverSurface->bufferDamage(), damage);
318 QCOMPARE(damageSpy.first().first().value<QRegion>(), damage);
319 QVERIFY(serverSurface->isMapped());
320 }
321
322 // combined regular damage and damaged buffer
323 {
324 const QRegion surfaceDamage(10, 20, 5, 5);
325 const QRegion bufferDamage(30, 50, 50, 20);
326 const QRegion expectedDamage = QRegion(20, 40, 10, 10).united(QRect(30, 50, 50, 20));
327 QImage img(QSize(80, 70), QImage::Format_ARGB32_Premultiplied);
328 img.fill(Qt::black);
329 auto b = m_shm->createBuffer(img);
330 s->attachBuffer(b);
331 s->damage(surfaceDamage);
332 s->damageBuffer(bufferDamage);
333 damageSpy.clear();
334 s->commit(KWayland::Client::Surface::CommitFlag::None);
335 QVERIFY(damageSpy.wait());
336 QCOMPARE(serverSurface->bufferDamage(), expectedDamage);
337 QCOMPARE(damageSpy.first().first().value<QRegion>(), expectedDamage);
338 QVERIFY(serverSurface->isMapped());
339 }
340}
341
342void TestWaylandSurface::testFrameCallback()
343{
344 QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
345 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
346 QVERIFY(serverSurfaceCreated.wait());
347 KWin::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
348 QVERIFY(serverSurface);
349
350 QSignalSpy damageSpy(serverSurface, &KWin::SurfaceInterface::damaged);
351
352 QSignalSpy frameRenderedSpy(s.get(), &KWayland::Client::Surface::frameRendered);
353 QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
354 img.fill(Qt::black);
355 auto b = m_shm->createBuffer(img);
356 s->attachBuffer(b);
357 s->damage(QRect(0, 0, 10, 10));
358 s->commit();
359 QVERIFY(damageSpy.wait());
360 serverSurface->frameRendered(10);
361 QVERIFY(frameRenderedSpy.isEmpty());
362 QVERIFY(frameRenderedSpy.wait());
363 QVERIFY(!frameRenderedSpy.isEmpty());
364}
365
366void TestWaylandSurface::testAttachBuffer()
367{
368 // create the surface
369 QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
370 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
371 QVERIFY(serverSurfaceCreated.wait());
372 KWin::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
373 QVERIFY(serverSurface);
374
375 // create three images
376 QImage black(24, 24, QImage::Format_RGB32);
377 black.fill(Qt::black);
378 QImage red(24, 24, QImage::Format_ARGB32); // Note - deliberately not premultiplied
379 red.fill(QColor(255, 0, 0, 128));
380 QImage blue(24, 24, QImage::Format_ARGB32_Premultiplied);
381 blue.fill(QColor(0, 0, 255, 128));
382
383 QSharedPointer<KWayland::Client::Buffer> blackBufferPtr = m_shm->createBuffer(black).toStrongRef();
384 QVERIFY(blackBufferPtr);
385 wl_buffer *blackBuffer = *(blackBufferPtr.data());
386 QSharedPointer<KWayland::Client::Buffer> redBuffer = m_shm->createBuffer(red).toStrongRef();
387 QVERIFY(redBuffer);
388 QSharedPointer<KWayland::Client::Buffer> blueBuffer = m_shm->createBuffer(blue).toStrongRef();
389 QVERIFY(blueBuffer);
390
391 QCOMPARE(blueBuffer->format(), KWayland::Client::Buffer::Format::ARGB32);
392 QCOMPARE(blueBuffer->size(), blue.size());
393 QVERIFY(!blueBuffer->isReleased());
394 QVERIFY(!blueBuffer->isUsed());
395 QCOMPARE(blueBuffer->stride(), blue.bytesPerLine());
396
397 s->attachBuffer(redBuffer.data());
398 s->attachBuffer(blackBuffer);
399 s->damage(QRect(0, 0, 24, 24));
400 s->commit(KWayland::Client::Surface::CommitFlag::None);
401 QSignalSpy damageSpy(serverSurface, &KWin::SurfaceInterface::damaged);
402 QSignalSpy mappedSpy(serverSurface, &KWin::SurfaceInterface::mapped);
403 QSignalSpy unmappedSpy(serverSurface, &KWin::SurfaceInterface::unmapped);
404 QVERIFY(damageSpy.wait());
405 QCOMPARE(mappedSpy.count(), 1);
406 QVERIFY(unmappedSpy.isEmpty());
407
408 // now the ServerSurface should have the black image attached as a buffer
409 KWin::GraphicsBuffer *buffer = serverSurface->buffer();
410 buffer->ref();
411 {
412 KWin::GraphicsBufferView view(buffer);
413 QVERIFY(view.image());
414 QCOMPARE(*view.image(), black);
415 QCOMPARE(view.image()->format(), QImage::Format_RGB32);
416 QCOMPARE(view.image()->size(), QSize(24, 24));
417 }
418
419 // render another frame
420 s->attachBuffer(redBuffer);
421 s->damage(QRect(0, 0, 24, 24));
422 s->commit(KWayland::Client::Surface::CommitFlag::None);
423 damageSpy.clear();
424 QVERIFY(damageSpy.wait());
425 QCOMPARE(mappedSpy.count(), 1);
426 QVERIFY(unmappedSpy.isEmpty());
427 KWin::GraphicsBuffer *buffer2 = serverSurface->buffer();
428 buffer2->ref();
429 {
430 KWin::GraphicsBufferView view(buffer2);
431 QVERIFY(view.image());
432 QCOMPARE(view.image()->format(), QImage::Format_ARGB32_Premultiplied);
433 QCOMPARE(view.image()->size(), QSize(24, 24));
434 for (int i = 0; i < 24; ++i) {
435 for (int j = 0; j < 24; ++j) {
436 // it's premultiplied in the format
437 QCOMPARE(view.image()->pixel(i, j), qRgba(128, 0, 0, 128));
438 }
439 }
440 }
441 buffer2->unref();
442 QVERIFY(buffer2->isReferenced());
443 QVERIFY(!redBuffer.data()->isReleased());
444
445 // render another frame
446 blueBuffer->setUsed(true);
447 QVERIFY(blueBuffer->isUsed());
448 s->attachBuffer(blueBuffer.data());
449 s->damage(QRect(0, 0, 24, 24));
450 QSignalSpy frameRenderedSpy(s.get(), &KWayland::Client::Surface::frameRendered);
451 s->commit();
452 damageSpy.clear();
453 QVERIFY(damageSpy.wait());
454 QCOMPARE(mappedSpy.count(), 1);
455 QVERIFY(unmappedSpy.isEmpty());
456 QVERIFY(!buffer2->isReferenced());
457 // TODO: we should have a signal on when the Buffer gets released
458 QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
459 if (!redBuffer.data()->isReleased()) {
460 QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
461 }
462 QVERIFY(redBuffer.data()->isReleased());
463
464 KWin::GraphicsBuffer *buffer3 = serverSurface->buffer();
465 buffer3->ref();
466 {
467 KWin::GraphicsBufferView view(buffer3);
468 QVERIFY(view.image());
469 QCOMPARE(view.image()->format(), QImage::Format_ARGB32_Premultiplied);
470 QCOMPARE(view.image()->size(), QSize(24, 24));
471 for (int i = 0; i < 24; ++i) {
472 for (int j = 0; j < 24; ++j) {
473 // it's premultiplied in the format
474 QCOMPARE(view.image()->pixel(i, j), qRgba(0, 0, 128, 128));
475 }
476 }
477 }
478 buffer3->unref();
479 QVERIFY(buffer3->isReferenced());
480
481 serverSurface->frameRendered(1);
482 QVERIFY(frameRenderedSpy.wait());
483
484 // commit a different value shouldn't change our buffer
485 QCOMPARE(serverSurface->buffer(), buffer3);
486 damageSpy.clear();
487 s->setInputRegion(m_compositor->createRegion(QRegion(0, 0, 24, 24)).get());
488 s->commit(KWayland::Client::Surface::CommitFlag::None);
489 wl_display_flush(m_connection->display());
490 QCoreApplication::processEvents();
491 QCoreApplication::processEvents();
492 QCOMPARE(serverSurface->buffer(), buffer3);
493 QVERIFY(damageSpy.isEmpty());
494 QCOMPARE(mappedSpy.count(), 1);
495 QVERIFY(unmappedSpy.isEmpty());
496 QVERIFY(serverSurface->isMapped());
497
498 // clear the surface
499 s->attachBuffer(blackBuffer);
500 s->damage(QRect(0, 0, 1, 1));
501 // TODO: better method
502 s->attachBuffer((wl_buffer *)nullptr);
503 s->damage(QRect(0, 0, 10, 10));
504 s->commit(KWayland::Client::Surface::CommitFlag::None);
505 QVERIFY(unmappedSpy.wait());
506 QCOMPARE(mappedSpy.count(), 1);
507 QCOMPARE(unmappedSpy.count(), 1);
508 QVERIFY(damageSpy.isEmpty());
509 QVERIFY(!serverSurface->isMapped());
510
511 // TODO: add signal test on release
512 buffer->unref();
513}
514
515void TestWaylandSurface::testOpaque()
516{
517 using namespace KWin;
518 QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
519 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
520 QVERIFY(serverSurfaceCreated.wait());
521 SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
522 QVERIFY(serverSurface);
523 QSignalSpy opaqueRegionChangedSpy(serverSurface, &KWin::SurfaceInterface::opaqueChanged);
524
525 // by default there should be an empty opaque region
526 QCOMPARE(serverSurface->opaque(), QRegion());
527
528 // let's install an opaque region
529 s->setOpaqueRegion(m_compositor->createRegion(QRegion(0, 10, 20, 30)).get());
530 // the region should only be applied after the surface got committed
531 wl_display_flush(m_connection->display());
532 QCoreApplication::processEvents();
533 QCOMPARE(serverSurface->opaque(), QRegion());
534 QCOMPARE(opaqueRegionChangedSpy.count(), 0);
535
536 // so let's commit to get the new region
537 QImage black(20, 40, QImage::Format_ARGB32_Premultiplied);
538 black.fill(Qt::black);
539 QSharedPointer<KWayland::Client::Buffer> buffer1 = m_shm->createBuffer(black).toStrongRef();
540 s->attachBuffer(buffer1);
541 s->commit(KWayland::Client::Surface::CommitFlag::None);
542 QVERIFY(opaqueRegionChangedSpy.wait());
543 QCOMPARE(opaqueRegionChangedSpy.count(), 1);
544 QCOMPARE(opaqueRegionChangedSpy.last().first().value<QRegion>(), QRegion(0, 10, 20, 30));
545 QCOMPARE(serverSurface->opaque(), QRegion(0, 10, 20, 30));
546
547 // committing without setting a new region shouldn't change
548 s->commit(KWayland::Client::Surface::CommitFlag::None);
549 wl_display_flush(m_connection->display());
550 QCoreApplication::processEvents();
551 QCOMPARE(opaqueRegionChangedSpy.count(), 1);
552 QCOMPARE(serverSurface->opaque(), QRegion(0, 10, 20, 30));
553
554 // let's change the opaque region, it will be clipped with rect(0, 0, 20, 40)
555 s->setOpaqueRegion(m_compositor->createRegion(QRegion(10, 20, 30, 40)).get());
556 s->commit(KWayland::Client::Surface::CommitFlag::None);
557 QVERIFY(opaqueRegionChangedSpy.wait());
558 QCOMPARE(opaqueRegionChangedSpy.count(), 2);
559 QCOMPARE(opaqueRegionChangedSpy.last().first().value<QRegion>(), QRegion(10, 20, 10, 20));
560 QCOMPARE(serverSurface->opaque(), QRegion(10, 20, 10, 20));
561
562 // and let's go back to an empty region
563 s->setOpaqueRegion();
564 s->commit(KWayland::Client::Surface::CommitFlag::None);
565 QVERIFY(opaqueRegionChangedSpy.wait());
566 QCOMPARE(opaqueRegionChangedSpy.count(), 3);
567 QCOMPARE(opaqueRegionChangedSpy.last().first().value<QRegion>(), QRegion());
568 QCOMPARE(serverSurface->opaque(), QRegion());
569}
570
571void TestWaylandSurface::testInput()
572{
573 using namespace KWin;
574 QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
575 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
576 QVERIFY(serverSurfaceCreated.wait());
577 SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
578 QVERIFY(serverSurface);
579 QSignalSpy inputRegionChangedSpy(serverSurface, &KWin::SurfaceInterface::inputChanged);
580 QSignalSpy committedSpy(serverSurface, &SurfaceInterface::committed);
581
582 // the input region should be empty if the surface has no buffer
583 QVERIFY(!serverSurface->isMapped());
584 QCOMPARE(serverSurface->input(), QRegion());
585
586 // the default input region is infinite
587 QImage black(100, 50, QImage::Format_RGB32);
588 black.fill(Qt::black);
589 QSharedPointer<KWayland::Client::Buffer> buffer1 = m_shm->createBuffer(black).toStrongRef();
590 QVERIFY(buffer1);
591 s->attachBuffer(buffer1);
592 s->commit(KWayland::Client::Surface::CommitFlag::None);
593 QVERIFY(committedSpy.wait());
594 QVERIFY(serverSurface->isMapped());
595 QCOMPARE(inputRegionChangedSpy.count(), 1);
596 QCOMPARE(serverSurface->input(), QRegion(0, 0, 100, 50));
597
598 // let's install an input region
599 s->setInputRegion(m_compositor->createRegion(QRegion(0, 10, 20, 30)).get());
600 // the region should only be applied after the surface got committed
601 wl_display_flush(m_connection->display());
602 QCoreApplication::processEvents();
603 QCOMPARE(inputRegionChangedSpy.count(), 1);
604 QCOMPARE(serverSurface->input(), QRegion(0, 0, 100, 50));
605
606 // so let's commit to get the new region
607 s->commit(KWayland::Client::Surface::CommitFlag::None);
608 QVERIFY(committedSpy.wait());
609 QCOMPARE(inputRegionChangedSpy.count(), 2);
610 QCOMPARE(serverSurface->input(), QRegion(0, 10, 20, 30));
611
612 // committing without setting a new region shouldn't change
613 s->commit(KWayland::Client::Surface::CommitFlag::None);
614 wl_display_flush(m_connection->display());
615 QCoreApplication::processEvents();
616 QCOMPARE(inputRegionChangedSpy.count(), 2);
617 QCOMPARE(serverSurface->input(), QRegion(0, 10, 20, 30));
618
619 // let's change the input region, note that the new input region is cropped
620 s->setInputRegion(m_compositor->createRegion(QRegion(10, 20, 30, 40)).get());
621 s->commit(KWayland::Client::Surface::CommitFlag::None);
622 QVERIFY(committedSpy.wait());
623 QCOMPARE(inputRegionChangedSpy.count(), 3);
624 QCOMPARE(serverSurface->input(), QRegion(10, 20, 30, 30));
625
626 // and let's go back to an empty region
627 s->setInputRegion();
628 s->commit(KWayland::Client::Surface::CommitFlag::None);
629 QVERIFY(committedSpy.wait());
630 QCOMPARE(inputRegionChangedSpy.count(), 4);
631 QCOMPARE(serverSurface->input(), QRegion(0, 0, 100, 50));
632}
633
634void TestWaylandSurface::testScale()
635{
636 // this test verifies that updating the scale factor is correctly passed to the Wayland server
637 using namespace KWin;
638 // create surface
639 QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated);
640 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
641 QCOMPARE(s->scale(), 1);
642 QVERIFY(serverSurfaceCreated.wait());
643 SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
644 QVERIFY(serverSurface);
645
646 // changing the scale implicitly changes the size
647 QSignalSpy sizeChangedSpy(serverSurface, &SurfaceInterface::sizeChanged);
648
649 // attach a buffer of 100x100
650 QImage red(100, 100, QImage::Format_ARGB32_Premultiplied);
651 red.fill(QColor(255, 0, 0, 128));
652 QSharedPointer<KWayland::Client::Buffer> redBuffer = m_shm->createBuffer(red).toStrongRef();
653 QVERIFY(redBuffer);
654 s->attachBuffer(redBuffer.data());
655 s->damageBuffer(QRect(0, 0, 100, 100));
656 s->commit(KWayland::Client::Surface::CommitFlag::None);
657 QVERIFY(sizeChangedSpy.wait());
658 QCOMPARE(sizeChangedSpy.count(), 1);
659 QCOMPARE(serverSurface->size(), QSize(100, 100));
660
661 // set the scale to 2, buffer is still 100x100 so size should change to 50x50
662 s->setScale(2);
663 s->commit(KWayland::Client::Surface::CommitFlag::None);
664 QVERIFY(sizeChangedSpy.wait());
665 QCOMPARE(sizeChangedSpy.count(), 2);
666 QCOMPARE(serverSurface->size(), QSize(50, 50));
667
668 // set scale and size in one commit, buffer is 60x60 at scale 3 so size should be 20x20
669 QImage blue(60, 60, QImage::Format_ARGB32_Premultiplied);
670 red.fill(QColor(255, 0, 0, 128));
671 QSharedPointer<KWayland::Client::Buffer> blueBuffer = m_shm->createBuffer(blue).toStrongRef();
672 QVERIFY(blueBuffer);
673 s->attachBuffer(blueBuffer.data());
674 s->setScale(3);
675 s->commit(KWayland::Client::Surface::CommitFlag::None);
676 QVERIFY(sizeChangedSpy.wait());
677 QCOMPARE(sizeChangedSpy.count(), 3);
678 QCOMPARE(serverSurface->size(), QSize(20, 20));
679}
680
681void TestWaylandSurface::testUnmapOfNotMappedSurface()
682{
683 // this test verifies that a surface which doesn't have a buffer attached doesn't trigger the unmapped signal
684 using namespace KWin;
685 // create surface
686 QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated);
687 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
688 QVERIFY(serverSurfaceCreated.wait());
689 SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
690
691 QSignalSpy unmappedSpy(serverSurface, &SurfaceInterface::unmapped);
692 QSignalSpy committedSpy(serverSurface, &SurfaceInterface::committed);
693
694 // let's map a null buffer and change scale to trigger a signal we can wait for
695 s->attachBuffer(KWayland::Client::Buffer::Ptr());
696 s->commit(KWayland::Client::Surface::CommitFlag::None);
697
698 QVERIFY(committedSpy.wait());
699 QVERIFY(unmappedSpy.isEmpty());
700}
701
702void TestWaylandSurface::testSurfaceAt()
703{
704 // this test verifies that surfaceAt(const QPointF&) works as expected for the case of no children
705 using namespace KWin;
706 // create surface
707 QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated);
708 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
709 QVERIFY(serverSurfaceCreated.wait());
710 SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
711
712 // a newly created surface should not be mapped and not provide a surface at a position
713 QVERIFY(!serverSurface->isMapped());
714 QVERIFY(!serverSurface->surfaceAt(QPointF(0, 0)));
715
716 // let's damage this surface
717 QSignalSpy sizeChangedSpy(serverSurface, &SurfaceInterface::sizeChanged);
718 QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
719 image.fill(Qt::red);
720 s->attachBuffer(m_shm->createBuffer(image));
721 s->damage(QRect(0, 0, 100, 100));
722 s->commit(KWayland::Client::Surface::CommitFlag::None);
723 QVERIFY(sizeChangedSpy.wait());
724
725 // now the surface is mapped and surfaceAt should give the surface
726 QVERIFY(serverSurface->isMapped());
727 QCOMPARE(serverSurface->surfaceAt(QPointF(0, 0)), serverSurface);
728 QCOMPARE(serverSurface->surfaceAt(QPointF(99, 99)), serverSurface);
729 // outside the geometry it should not give a surface
730 QVERIFY(!serverSurface->surfaceAt(QPointF(100, 100)));
731 QVERIFY(!serverSurface->surfaceAt(QPointF(-1, -1)));
732}
733
734void TestWaylandSurface::testDestroyAttachedBuffer()
735{
736 // this test verifies that destroying of a buffer attached to a surface works
737 using namespace KWin;
738 // create surface
739 QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated);
740 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
741 QVERIFY(serverSurfaceCreated.wait());
742 SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
743
744 // let's damage this surface
745 QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged);
746 QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
747 image.fill(Qt::red);
748 s->attachBuffer(m_shm->createBuffer(image));
749 s->damage(QRect(0, 0, 100, 100));
750 s->commit(KWayland::Client::Surface::CommitFlag::None);
751 QVERIFY(damagedSpy.wait());
752 QVERIFY(serverSurface->buffer());
753
754 // attach another buffer
755 image.fill(Qt::blue);
756 s->attachBuffer(m_shm->createBuffer(image));
757 m_connection->flush();
758
759 // Let's try to destroy it
760 delete m_shm;
761 m_shm = nullptr;
762 QTRY_VERIFY(serverSurface->buffer()->isDropped());
763}
764
765void TestWaylandSurface::testDestroyWithPendingCallback()
766{
767 // this test tries to verify that destroying a surface with a pending callback works correctly
768 // first create surface
769 using namespace KWin;
770 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
771 QVERIFY(s != nullptr);
772 QVERIFY(s->isValid());
773 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
774 QVERIFY(surfaceCreatedSpy.wait());
775 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
776 QVERIFY(serverSurface);
777
778 // now render to it
779 QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
780 img.fill(Qt::black);
781 auto b = m_shm->createBuffer(img);
782 s->attachBuffer(b);
783 s->damage(QRect(0, 0, 10, 10));
784 // add some frame callbacks
785 for (int i = 0; i < 1000; i++) {
786 wl_surface_frame(*s);
787 }
788 s->commit(KWayland::Client::Surface::CommitFlag::FrameCallback);
789 QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged);
790 QVERIFY(damagedSpy.wait());
791
792 // now try to destroy the Surface again
793 QSignalSpy destroyedSpy(serverSurface, &QObject::destroyed);
794 s.reset();
795 QVERIFY(destroyedSpy.wait());
796}
797
798void TestWaylandSurface::testDisconnect()
799{
800 // this test verifies that the server side correctly tears down the resources when the client disconnects
801 using namespace KWin;
802 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
803 QVERIFY(s != nullptr);
804 QVERIFY(s->isValid());
805 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
806 QVERIFY(surfaceCreatedSpy.wait());
807 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
808 QVERIFY(serverSurface);
809
810 // destroy client
811 QSignalSpy clientDisconnectedSpy(serverSurface->client(), &ClientConnection::disconnected);
812 QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed);
813 if (m_connection) {
814 m_connection->deleteLater();
815 m_connection = nullptr;
816 }
817 QVERIFY(clientDisconnectedSpy.wait());
818 QCOMPARE(clientDisconnectedSpy.count(), 1);
819 if (surfaceDestroyedSpy.isEmpty()) {
820 QVERIFY(surfaceDestroyedSpy.wait());
821 }
822 QTRY_COMPARE(surfaceDestroyedSpy.count(), 1);
823
824 s->destroy();
825 m_shm->destroy();
826 m_compositor->destroy();
827 m_queue->destroy();
828 m_idleInhibitManager->destroy();
829}
830
831void TestWaylandSurface::testOutput()
832{
833 // This test verifies that the enter/leave are sent correctly to the Client
834 using namespace KWin;
835 qRegisterMetaType<KWayland::Client::Output *>();
836 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
837 QVERIFY(s != nullptr);
838 QVERIFY(s->isValid());
839 QVERIFY(s->outputs().isEmpty());
840 QSignalSpy enteredSpy(s.get(), &KWayland::Client::Surface::outputEntered);
841 QSignalSpy leftSpy(s.get(), &KWayland::Client::Surface::outputLeft);
842 // wait for the surface on the Server side
843 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
844 QVERIFY(surfaceCreatedSpy.wait());
845 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
846 QVERIFY(serverSurface);
847 QCOMPARE(serverSurface->outputs(), QList<OutputInterface *>());
848
849 // create another registry to get notified about added outputs
850 KWayland::Client::Registry registry;
851 registry.setEventQueue(m_queue);
852 QSignalSpy allAnnounced(&registry, &KWayland::Client::Registry::interfacesAnnounced);
853 registry.create(m_connection);
854 QVERIFY(registry.isValid());
855 registry.setup();
856 QVERIFY(allAnnounced.wait());
857 QSignalSpy outputAnnouncedSpy(&registry, &KWayland::Client::Registry::outputAnnounced);
858
859 auto outputHandle = std::make_unique<FakeOutput>();
860 auto serverOutput = std::make_unique<OutputInterface>(m_display, outputHandle.get());
861 QVERIFY(outputAnnouncedSpy.wait());
862 std::unique_ptr<KWayland::Client::Output> clientOutput(
863 registry.createOutput(outputAnnouncedSpy.first().first().value<quint32>(), outputAnnouncedSpy.first().last().value<quint32>()));
864 QVERIFY(clientOutput->isValid());
865 m_connection->flush();
866 m_display->dispatchEvents();
867
868 // now enter it
869 serverSurface->setOutputs(QList<OutputInterface *>{serverOutput.get()}, serverOutput.get());
870 QCOMPARE(serverSurface->outputs(), QList<OutputInterface *>{serverOutput.get()});
871 QVERIFY(enteredSpy.wait());
872 QCOMPARE(enteredSpy.count(), 1);
873 QCOMPARE(enteredSpy.first().first().value<KWayland::Client::Output *>(), clientOutput.get());
874 QCOMPARE(s->outputs(), QList<KWayland::Client::Output *>{clientOutput.get()});
875
876 // adding to same should not trigger
877 serverSurface->setOutputs(QList<OutputInterface *>{serverOutput.get()}, serverOutput.get());
878
879 // leave again
880 serverSurface->setOutputs(QList<OutputInterface *>(), nullptr);
881 QCOMPARE(serverSurface->outputs(), QList<OutputInterface *>());
882 QVERIFY(leftSpy.wait());
883 QCOMPARE(enteredSpy.count(), 1);
884 QCOMPARE(leftSpy.count(), 1);
885 QCOMPARE(leftSpy.first().first().value<KWayland::Client::Output *>(), clientOutput.get());
886 QCOMPARE(s->outputs(), QList<KWayland::Client::Output *>());
887
888 // leave again should not trigger
889 serverSurface->setOutputs(QList<OutputInterface *>(), nullptr);
890
891 // and enter again, just to verify
892 serverSurface->setOutputs(QList<OutputInterface *>{serverOutput.get()}, serverOutput.get());
893 QCOMPARE(serverSurface->outputs(), QList<OutputInterface *>{serverOutput.get()});
894 QVERIFY(enteredSpy.wait());
895 QCOMPARE(enteredSpy.count(), 2);
896 QCOMPARE(leftSpy.count(), 1);
897
898 // delete output client is on.
899 // client should get an exit and be left on no outputs (which is allowed)
900 serverOutput.reset();
901 outputHandle.reset();
902 QVERIFY(leftSpy.wait());
903 QCOMPARE(serverSurface->outputs(), QList<OutputInterface *>());
904}
905
906void TestWaylandSurface::testInhibit()
907{
908 using namespace KWin;
909 std::unique_ptr<KWayland::Client::Surface> s(m_compositor->createSurface());
910 // wait for the surface on the Server side
911 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
912 QVERIFY(surfaceCreatedSpy.wait());
913 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
914 QVERIFY(serverSurface);
915 QCOMPARE(serverSurface->inhibitsIdle(), false);
916
917 QSignalSpy inhibitsChangedSpy(serverSurface, &SurfaceInterface::inhibitsIdleChanged);
918
919 // now create an idle inhibition
920 std::unique_ptr<KWayland::Client::IdleInhibitor> inhibitor1(m_idleInhibitManager->createInhibitor(s.get()));
921 QVERIFY(inhibitsChangedSpy.wait());
922 QCOMPARE(serverSurface->inhibitsIdle(), true);
923
924 // creating a second idle inhibition should not trigger the signal
925 std::unique_ptr<KWayland::Client::IdleInhibitor> inhibitor2(m_idleInhibitManager->createInhibitor(s.get()));
926 QVERIFY(!inhibitsChangedSpy.wait(500));
927 QCOMPARE(serverSurface->inhibitsIdle(), true);
928
929 // and also deleting the first inhibitor should not yet change the inhibition
930 inhibitor1.reset();
931 QVERIFY(!inhibitsChangedSpy.wait(500));
932 QCOMPARE(serverSurface->inhibitsIdle(), true);
933
934 // but deleting also the second inhibitor should trigger
935 inhibitor2.reset();
936 QVERIFY(inhibitsChangedSpy.wait());
937 QCOMPARE(serverSurface->inhibitsIdle(), false);
938 QCOMPARE(inhibitsChangedSpy.count(), 2);
939
940 // recreate inhibitor1 should inhibit again
941 inhibitor1.reset(m_idleInhibitManager->createInhibitor(s.get()));
942 QVERIFY(inhibitsChangedSpy.wait());
943 QCOMPARE(serverSurface->inhibitsIdle(), true);
944 // and destroying should uninhibit
945 inhibitor1.reset();
946 QVERIFY(inhibitsChangedSpy.wait());
947 QCOMPARE(serverSurface->inhibitsIdle(), false);
948 QCOMPARE(inhibitsChangedSpy.count(), 4);
949}
950
951QTEST_GUILESS_MAIN(TestWaylandSurface)
952#include "test_wayland_surface.moc"
void surfaceCreated(KWin::SurfaceInterface *surface)
Class holding the Wayland server display loop.
Definition display.h:34
void createShm()
Definition display.cpp:128
void dispatchEvents()
Definition display.cpp:116
bool addSocketName(const QString &name=QString())
Definition display.cpp:68
bool isRunning() const
Definition display.cpp:144
bool start()
Definition display.cpp:92
Resource representing a wl_surface.
Definition surface.h:80
SurfaceInterface * surfaceAt(const QPointF &position)
Definition surface.cpp:953
ClientConnection * client() const
Definition surface.cpp:444
void damaged(const QRegion &)
static SurfaceInterface * get(wl_resource *native)
Definition surface.cpp:819
QRegion bufferDamage() const
Definition surface.cpp:784
void inputChanged(const QRegion &)
bool isMapped() const
Definition surface.cpp:891
QPoint offset() const
Definition surface.cpp:814
GraphicsBuffer * buffer() const
Definition surface.cpp:809
QList< OutputInterface * > outputs() const
Definition surface.cpp:896
void opaqueChanged(const QRegion &)
void frameRendered(quint32 msec)
Definition surface.cpp:459
void setOutputs(const QList< OutputInterface * > &outputs, OutputInterface *primaryOutput)
Definition surface.cpp:901
bool inhibitsIdle() const
Definition surface.cpp:1024
TestWaylandSurface(QObject *parent=nullptr)
KWayland::Client::Registry * registry