1 /********************************************************************
2 Copyright 2014  Martin Gräßlin <mgraesslin@kde.org>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
20 #include <QImage>
21 #include <QPainter>
22 #include <QtTest>
23 
24 #include "../../src/client/compositor.h"
25 #include "../../src/client/connection_thread.h"
26 #include "../../src/client/event_queue.h"
27 #include "../../src/client/idleinhibit.h"
28 #include "../../src/client/output.h"
29 #include "../../src/client/region.h"
30 #include "../../src/client/registry.h"
31 #include "../../src/client/shm_pool.h"
32 #include "../../src/client/surface.h"
33 
34 #include "../../server/buffer.h"
35 #include "../../server/client.h"
36 #include "../../server/compositor.h"
37 #include "../../server/display.h"
38 #include "../../server/idle_inhibit_v1.h"
39 #include "../../server/output.h"
40 #include "../../server/surface.h"
41 
42 #include "../../tests/helpers.h"
43 
44 #include <wayland-client-protocol.h>
45 
46 class TestSurface : public QObject
47 {
48     Q_OBJECT
49 public:
50     explicit TestSurface(QObject* parent = nullptr);
51 private Q_SLOTS:
52     void init();
53     void cleanup();
54 
55     void testStaticAccessor();
56     void testDamage();
57     void testFrameCallback();
58     void testAttachBuffer();
59     void testMultipleSurfaces();
60     void testOpaque();
61     void testInput();
62     void testScale();
63     void testDestroy();
64     void testUnmapOfNotMappedSurface();
65     void testDamageTracking();
66     void testSurfaceAt();
67     void testDestroyAttachedBuffer();
68     void testDestroyWithPendingCallback();
69     void testDisconnect();
70     void testOutput();
71     void testInhibit();
72 
73 private:
74     Wrapland::Server::Display* m_display;
75     Wrapland::Server::Compositor* m_serverCompositor;
76     Wrapland::Server::IdleInhibitManagerV1* m_idleInhibitInterface;
77     Wrapland::Client::ConnectionThread* m_connection;
78     Wrapland::Client::Compositor* m_compositor;
79     Wrapland::Client::ShmPool* m_shm;
80     Wrapland::Client::EventQueue* m_queue;
81     Wrapland::Client::IdleInhibitManager* m_idleInhibitManager;
82     QThread* m_thread;
83 };
84 
85 static const QString s_socketName = QStringLiteral("wrapland-test-wayland-surface-0");
86 
TestSurface(QObject * parent)87 TestSurface::TestSurface(QObject* parent)
88     : QObject(parent)
89     , m_display(nullptr)
90     , m_serverCompositor(nullptr)
91     , m_connection(nullptr)
92     , m_compositor(nullptr)
93     , m_thread(nullptr)
94 {
95 }
96 
init()97 void TestSurface::init()
98 {
99     qRegisterMetaType<Wrapland::Server::Surface*>();
100 
101     m_display = new Wrapland::Server::Display(this);
102     m_display->setSocketName(s_socketName);
103     m_display->start();
104     QVERIFY(m_display->running());
105     m_display->createShm();
106 
107     m_serverCompositor = m_display->createCompositor(m_display);
108     QVERIFY(m_serverCompositor);
109 
110     m_idleInhibitInterface = m_display->createIdleInhibitManager(m_display);
111     QVERIFY(m_idleInhibitInterface);
112 
113     // setup connection
114     m_connection = new Wrapland::Client::ConnectionThread;
115     QSignalSpy connectedSpy(m_connection, &Wrapland::Client::ConnectionThread::establishedChanged);
116     m_connection->setSocketName(s_socketName);
117 
118     m_thread = new QThread(this);
119     m_connection->moveToThread(m_thread);
120     m_thread->start();
121 
122     /*connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock,
123     m_connection, [this]() { if (m_connection->display()) {
124                 wl_display_flush(m_connection->display());
125             }
126         }
127     );*/
128 
129     m_connection->establishConnection();
130     QVERIFY(connectedSpy.count() || connectedSpy.wait());
131     QCOMPARE(connectedSpy.count(), 1);
132 
133     m_queue = new Wrapland::Client::EventQueue(this);
134     QVERIFY(!m_queue->isValid());
135     m_queue->setup(m_connection);
136     QVERIFY(m_queue->isValid());
137 
138     Wrapland::Client::Registry registry;
139     registry.setEventQueue(m_queue);
140     QSignalSpy compositorSpy(&registry, SIGNAL(compositorAnnounced(quint32, quint32)));
141     QSignalSpy shmSpy(&registry, SIGNAL(shmAnnounced(quint32, quint32)));
142     QSignalSpy allAnnounced(&registry, SIGNAL(interfacesAnnounced()));
143     QVERIFY(allAnnounced.isValid());
144     QVERIFY(shmSpy.isValid());
145     registry.create(m_connection->display());
146     QVERIFY(registry.isValid());
147     registry.setup();
148     QVERIFY(allAnnounced.wait());
149     QVERIFY(!compositorSpy.isEmpty());
150     QVERIFY(!shmSpy.isEmpty());
151 
152     m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(),
153                                              compositorSpy.first().last().value<quint32>(),
154                                              this);
155     QVERIFY(m_compositor->isValid());
156     m_shm = registry.createShmPool(
157         shmSpy.first().first().value<quint32>(), shmSpy.first().last().value<quint32>(), this);
158     QVERIFY(m_shm->isValid());
159 
160     m_idleInhibitManager = registry.createIdleInhibitManager(
161         registry.interface(Wrapland::Client::Registry::Interface::IdleInhibitManagerUnstableV1)
162             .name,
163         registry.interface(Wrapland::Client::Registry::Interface::IdleInhibitManagerUnstableV1)
164             .version,
165         this);
166     QVERIFY(m_idleInhibitManager->isValid());
167 }
168 
cleanup()169 void TestSurface::cleanup()
170 {
171     if (m_compositor) {
172         delete m_compositor;
173         m_compositor = nullptr;
174     }
175     if (m_idleInhibitManager) {
176         delete m_idleInhibitManager;
177         m_idleInhibitManager = nullptr;
178     }
179     if (m_shm) {
180         delete m_shm;
181         m_shm = nullptr;
182     }
183     if (m_queue) {
184         delete m_queue;
185         m_queue = nullptr;
186     }
187     if (m_thread) {
188         m_thread->quit();
189         m_thread->wait();
190         delete m_thread;
191         m_thread = nullptr;
192     }
193     delete m_connection;
194     m_connection = nullptr;
195 
196     delete m_serverCompositor;
197     m_serverCompositor = nullptr;
198 
199     delete m_idleInhibitInterface;
200     m_idleInhibitInterface = nullptr;
201 
202     delete m_display;
203     m_display = nullptr;
204 }
205 
testStaticAccessor()206 void TestSurface::testStaticAccessor()
207 {
208     // TODO: Does this test still make sense with the remodel? If yes, needs porting.
209 #if 0
210     QSignalSpy serverSurfaceCreated(m_serverCompositor,
211                                     SIGNAL(surfaceCreated(Wrapland::Server::Surface*)));
212     QVERIFY(serverSurfaceCreated.isValid());
213 
214     QVERIFY(!Wrapland::Server::Surface::get(nullptr));
215     QVERIFY(!Wrapland::Server::Surface::get(1, nullptr));
216     QVERIFY(Wrapland::Client::Surface::all().isEmpty());
217     auto s1 = m_compositor->createSurface();
218     QVERIFY(s1->isValid());
219     QCOMPARE(Wrapland::Client::Surface::all().count(), 1);
220     QCOMPARE(Wrapland::Client::Surface::all().first(), s1);
221     QCOMPARE(Wrapland::Client::Surface::get(*s1), s1);
222     QVERIFY(serverSurfaceCreated.wait());
223     auto serverSurface1 = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
224     QVERIFY(serverSurface1);
225     QCOMPARE(Wrapland::Server::Surface::get(serverSurface1->resource()), serverSurface1);
226     QCOMPARE(Wrapland::Server::Surface::get(serverSurface1->id(), serverSurface1->client()),
227              serverSurface1);
228 
229     QVERIFY(!s1->size().isValid());
230     QSignalSpy sizeChangedSpy(s1, SIGNAL(sizeChanged(QSize)));
231     QVERIFY(sizeChangedSpy.isValid());
232     const QSize testSize(200, 300);
233     s1->setSize(testSize);
234     QCOMPARE(s1->size(), testSize);
235     QCOMPARE(sizeChangedSpy.count(), 1);
236     QCOMPARE(sizeChangedSpy.first().first().toSize(), testSize);
237 
238     // add another surface
239     auto s2 = m_compositor->createSurface();
240     QVERIFY(s2->isValid());
241     QCOMPARE(Wrapland::Client::Surface::all().count(), 2);
242     QCOMPARE(Wrapland::Client::Surface::all().first(), s1);
243     QCOMPARE(Wrapland::Client::Surface::all().last(), s2);
244     QCOMPARE(Wrapland::Client::Surface::get(*s1), s1);
245     QCOMPARE(Wrapland::Client::Surface::get(*s2), s2);
246     serverSurfaceCreated.clear();
247     QVERIFY(serverSurfaceCreated.wait());
248     auto serverSurface2 = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
249     QVERIFY(serverSurface2);
250     QCOMPARE(Wrapland::Server::Surface::get(serverSurface1->resource()), serverSurface1);
251     QCOMPARE(Wrapland::Server::Surface::get(serverSurface1->id(), serverSurface1->client()),
252              serverSurface1);
253     QCOMPARE(Wrapland::Server::Surface::get(serverSurface2->resource()), serverSurface2);
254     QCOMPARE(Wrapland::Server::Surface::get(serverSurface2->id(), serverSurface2->client()),
255              serverSurface2);
256 
257     // delete s2 again
258     delete s2;
259     QCOMPARE(Wrapland::Client::Surface::all().count(), 1);
260     QCOMPARE(Wrapland::Client::Surface::all().first(), s1);
261     QCOMPARE(Wrapland::Client::Surface::get(*s1), s1);
262 
263     // and finally delete the last one
264     delete s1;
265     QVERIFY(Wrapland::Client::Surface::all().isEmpty());
266     QVERIFY(!Wrapland::Client::Surface::get(nullptr));
267     QSignalSpy unboundSpy(serverSurface1, &Wrapland::Server::Resource::unbound);
268     QVERIFY(unboundSpy.isValid());
269     QVERIFY(unboundSpy.wait());
270     QVERIFY(!Wrapland::Server::Surface::get(nullptr));
271     QVERIFY(!Wrapland::Server::Surface::get(1, nullptr));
272 #endif
273 }
274 
testDamage()275 void TestSurface::testDamage()
276 {
277     QSignalSpy serverSurfaceCreated(m_serverCompositor,
278                                     SIGNAL(surfaceCreated(Wrapland::Server::Surface*)));
279     QVERIFY(serverSurfaceCreated.isValid());
280     std::unique_ptr<Wrapland::Client::Surface> s{m_compositor->createSurface()};
281     s->setScale(2);
282     QVERIFY(serverSurfaceCreated.wait());
283     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
284     QVERIFY(serverSurface);
285     QCOMPARE(serverSurface->state().damage, QRegion());
286     QVERIFY(!serverSurface->isMapped());
287 
288     QSignalSpy committedSpy(serverSurface, SIGNAL(committed()));
289 
290     // send damage without a buffer
291     s->damage(QRect(0, 0, 100, 100));
292     s->commit(Wrapland::Client::Surface::CommitFlag::None);
293     wl_display_flush(m_connection->display());
294     QCoreApplication::processEvents();
295     QCoreApplication::processEvents();
296     QVERIFY(!serverSurface->isMapped());
297     QCOMPARE(committedSpy.count(), 1);
298     QVERIFY(serverSurface->state().damage.isEmpty());
299 
300     QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
301     img.fill(Qt::black);
302     auto b = m_shm->createBuffer(img);
303     s->attachBuffer(b, QPoint(55, 55));
304     s->damage(QRect(0, 0, 10, 10));
305     s->commit(Wrapland::Client::Surface::CommitFlag::None);
306     QVERIFY(committedSpy.wait());
307     QCOMPARE(serverSurface->state().offset,
308              QPoint(55, 55)); // offset is surface local so scale doesn't change this
309     QCOMPARE(serverSurface->state().damage, QRegion(0, 0, 5, 5)); // scale is 2
310     QVERIFY(serverSurface->isMapped());
311     QCOMPARE(committedSpy.count(), 2);
312 
313     // damage multiple times
314     QRegion testRegion(5, 8, 3, 6);
315     testRegion = testRegion.united(QRect(10, 11, 6, 1));
316     img = QImage(QSize(40, 35), QImage::Format_ARGB32_Premultiplied);
317     img.fill(Qt::black);
318     b = m_shm->createBuffer(img);
319     s->attachBuffer(b);
320     s->damage(testRegion);
321     s->commit(Wrapland::Client::Surface::CommitFlag::None);
322     QVERIFY(committedSpy.wait());
323     QCOMPARE(serverSurface->state().damage, testRegion);
324     QVERIFY(serverSurface->isMapped());
325     QCOMPARE(committedSpy.count(), 3);
326 
327     // damage buffer
328     const QRegion testRegion2(30, 40, 22, 4);
329     const QRegion cmpRegion2(15, 20, 11, 2); // divided by scale factor
330     img = QImage(QSize(80, 70), QImage::Format_ARGB32_Premultiplied);
331     img.fill(Qt::black);
332     b = m_shm->createBuffer(img);
333     s->attachBuffer(b);
334     s->damageBuffer(testRegion2);
335     s->commit(Wrapland::Client::Surface::CommitFlag::None);
336     QVERIFY(committedSpy.wait());
337     QCOMPARE(serverSurface->state().damage, cmpRegion2);
338     QVERIFY(serverSurface->isMapped());
339 
340     // combined regular damage and damaged buffer
341     const QRegion testRegion3 = testRegion.united(cmpRegion2);
342     img = QImage(QSize(80, 70), QImage::Format_ARGB32_Premultiplied);
343     img.fill(Qt::black);
344     b = m_shm->createBuffer(img);
345     s->attachBuffer(b);
346     s->damage(testRegion);
347     s->damageBuffer(testRegion2);
348     s->commit(Wrapland::Client::Surface::CommitFlag::None);
349     QVERIFY(committedSpy.wait());
350     QVERIFY(serverSurface->state().damage != testRegion);
351     QVERIFY(serverSurface->state().damage != testRegion2);
352     QVERIFY(serverSurface->state().damage != cmpRegion2);
353     QCOMPARE(serverSurface->state().damage, testRegion3);
354     QVERIFY(serverSurface->isMapped());
355 }
356 
testFrameCallback()357 void TestSurface::testFrameCallback()
358 {
359     QSignalSpy serverSurfaceCreated(m_serverCompositor,
360                                     SIGNAL(surfaceCreated(Wrapland::Server::Surface*)));
361     QVERIFY(serverSurfaceCreated.isValid());
362     std::unique_ptr<Wrapland::Client::Surface> s{m_compositor->createSurface()};
363     QVERIFY(serverSurfaceCreated.wait());
364     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
365     QVERIFY(serverSurface);
366 
367     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
368     QVERIFY(commit_spy.isValid());
369 
370     QSignalSpy frameRenderedSpy(s.get(), SIGNAL(frameRendered()));
371     QVERIFY(frameRenderedSpy.isValid());
372     QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
373     img.fill(Qt::black);
374     auto b = m_shm->createBuffer(img);
375     s->attachBuffer(b);
376     s->damage(QRect(0, 0, 10, 10));
377     s->commit();
378     QVERIFY(commit_spy.wait());
379     serverSurface->frameRendered(10);
380     QVERIFY(frameRenderedSpy.isEmpty());
381     QVERIFY(frameRenderedSpy.wait());
382     QVERIFY(!frameRenderedSpy.isEmpty());
383 }
384 
testAttachBuffer()385 void TestSurface::testAttachBuffer()
386 {
387     // create the surface
388     QSignalSpy serverSurfaceCreated(m_serverCompositor,
389                                     SIGNAL(surfaceCreated(Wrapland::Server::Surface*)));
390     QVERIFY(serverSurfaceCreated.isValid());
391     std::unique_ptr<Wrapland::Client::Surface> s{m_compositor->createSurface()};
392     QVERIFY(serverSurfaceCreated.wait());
393     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
394     QVERIFY(serverSurface);
395 
396     // create three images
397     QImage black(24, 24, QImage::Format_RGB32);
398     black.fill(Qt::black);
399     QImage red(24, 24, QImage::Format_ARGB32); // Note - deliberately not premultiplied
400     red.fill(QColor(255, 0, 0, 128));
401     QImage blue(24, 24, QImage::Format_ARGB32_Premultiplied);
402     blue.fill(QColor(0, 0, 255, 128));
403 
404     std::shared_ptr<Wrapland::Client::Buffer> blackBufferPtr = m_shm->createBuffer(black).lock();
405     QVERIFY(blackBufferPtr);
406     wl_buffer* blackBuffer = *(blackBufferPtr.get());
407     std::shared_ptr<Wrapland::Client::Buffer> redBuffer = m_shm->createBuffer(red).lock();
408     QVERIFY(redBuffer);
409     std::shared_ptr<Wrapland::Client::Buffer> blueBuffer = m_shm->createBuffer(blue).lock();
410     QVERIFY(blueBuffer);
411 
412     QCOMPARE(blueBuffer->format(), Wrapland::Client::Buffer::Format::ARGB32);
413     QCOMPARE(blueBuffer->size(), blue.size());
414     QVERIFY(!blueBuffer->isReleased());
415     QVERIFY(!blueBuffer->isUsed());
416     QCOMPARE(blueBuffer->stride(), blue.bytesPerLine());
417 
418     s->attachBuffer(redBuffer);
419     s->attachBuffer(blackBuffer);
420     s->damage(QRect(0, 0, 24, 24));
421     s->commit(Wrapland::Client::Surface::CommitFlag::None);
422     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
423     QVERIFY(commit_spy.isValid());
424     QVERIFY(commit_spy.wait());
425     QVERIFY(serverSurface->isMapped());
426 
427     // now the ServerSurface should have the black image attached as a buffer
428     auto buffer1 = serverSurface->state().buffer;
429     QVERIFY(buffer1->shmBuffer());
430     QCOMPARE(buffer1->shmImage()->createQImage(), black);
431     QCOMPARE(buffer1->shmImage()->format(), Wrapland::Server::ShmImage::Format::xrgb8888);
432     QCOMPARE(buffer1->shmImage()->createQImage().format(), QImage::Format_RGB32);
433     buffer1.reset();
434 
435     // render another frame
436     s->attachBuffer(redBuffer);
437     s->damage(QRect(0, 0, 24, 24));
438     s->commit(Wrapland::Client::Surface::CommitFlag::None);
439     QVERIFY(!redBuffer->isReleased());
440     QVERIFY(commit_spy.wait());
441     QVERIFY(serverSurface->isMapped());
442 
443     auto buffer2 = serverSurface->state().buffer;
444     QVERIFY(buffer2->shmBuffer());
445     QCOMPARE(buffer2->shmImage()->createQImage().format(), QImage::Format_ARGB32_Premultiplied);
446     QCOMPARE(buffer2->shmImage()->createQImage().width(), 24);
447     QCOMPARE(buffer2->shmImage()->createQImage().height(), 24);
448     for (int i = 0; i < 24; ++i) {
449         for (int j = 0; j < 24; ++j) {
450             // it's premultiplied in the format
451             QCOMPARE(buffer2->shmImage()->createQImage().pixel(i, j), qRgba(128, 0, 0, 128));
452         }
453     }
454     QVERIFY(!redBuffer->isReleased());
455     buffer2.reset();
456     QVERIFY(!redBuffer->isReleased());
457 
458     // render another frame
459     blueBuffer->setUsed(true);
460     QVERIFY(blueBuffer->isUsed());
461     s->attachBuffer(blueBuffer.get());
462     s->damage(QRect(0, 0, 24, 24));
463     QSignalSpy frameRenderedSpy(s.get(), SIGNAL(frameRendered()));
464     QVERIFY(frameRenderedSpy.isValid());
465     s->commit();
466     QVERIFY(commit_spy.wait());
467     QVERIFY(serverSurface->isMapped());
468     buffer2.reset();
469 
470     // TODO: we should have a signal on when the Buffer gets released
471     QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
472     if (!redBuffer->isReleased()) {
473         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
474     }
475     QVERIFY(redBuffer->isReleased());
476 
477     auto buffer3 = serverSurface->state().buffer.get();
478     QVERIFY(buffer3->shmBuffer());
479     QCOMPARE(buffer3->shmImage()->createQImage().format(), QImage::Format_ARGB32_Premultiplied);
480     QCOMPARE(buffer3->shmImage()->createQImage().width(), 24);
481     QCOMPARE(buffer3->shmImage()->createQImage().height(), 24);
482     for (int i = 0; i < 24; ++i) {
483         for (int j = 0; j < 24; ++j) {
484             // it's premultiplied in the format
485             QCOMPARE(buffer3->shmImage()->createQImage().pixel(i, j), qRgba(0, 0, 128, 128));
486         }
487     }
488 
489     serverSurface->frameRendered(1);
490     QVERIFY(frameRenderedSpy.wait());
491 
492     // commit a different value shouldn't change our buffer
493     QCOMPARE(serverSurface->state().buffer.get(), buffer3);
494     QVERIFY(serverSurface->state().input.isNull());
495     s->setInputRegion(m_compositor->createRegion(QRegion(0, 0, 24, 24)).get());
496     s->commit(Wrapland::Client::Surface::CommitFlag::None);
497     wl_display_flush(m_connection->display());
498     QCoreApplication::processEvents();
499     QCoreApplication::processEvents();
500     QCOMPARE(serverSurface->state().input, QRegion(0, 0, 24, 24));
501     QCOMPARE(serverSurface->state().buffer.get(), buffer3);
502     QVERIFY(serverSurface->state().damage.isEmpty());
503     QVERIFY(serverSurface->isMapped());
504 
505     // clear the surface
506     s->attachBuffer(blackBuffer);
507     s->damage(QRect(0, 0, 1, 1));
508     // TODO: better method
509     s->attachBuffer((wl_buffer*)nullptr);
510     s->damage(QRect(0, 0, 10, 10));
511     s->commit(Wrapland::Client::Surface::CommitFlag::None);
512     QVERIFY(commit_spy.wait());
513     QVERIFY(serverSurface->state().damage.isEmpty());
514     QVERIFY(!serverSurface->isMapped());
515 }
516 
testMultipleSurfaces()517 void TestSurface::testMultipleSurfaces()
518 {
519     using namespace Wrapland::Client;
520     using namespace Wrapland::Server;
521     Registry registry;
522     registry.setEventQueue(m_queue);
523     QSignalSpy shmSpy(&registry, SIGNAL(shmAnnounced(quint32, quint32)));
524     registry.create(m_connection->display());
525     QVERIFY(registry.isValid());
526     registry.setup();
527     QVERIFY(shmSpy.wait());
528 
529     ShmPool pool1;
530     ShmPool pool2;
531     pool1.setup(registry.bindShm(shmSpy.first().first().value<quint32>(),
532                                  shmSpy.first().last().value<quint32>()));
533     pool2.setup(registry.bindShm(shmSpy.first().first().value<quint32>(),
534                                  shmSpy.first().last().value<quint32>()));
535     QVERIFY(pool1.isValid());
536     QVERIFY(pool2.isValid());
537 
538     // create the surfaces
539     QSignalSpy serverSurfaceCreated(m_serverCompositor,
540                                     SIGNAL(surfaceCreated(Wrapland::Server::Surface*)));
541     QVERIFY(serverSurfaceCreated.isValid());
542     std::unique_ptr<Wrapland::Client::Surface> s1(m_compositor->createSurface());
543     QVERIFY(serverSurfaceCreated.wait());
544     auto serverSurface1 = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
545     QVERIFY(serverSurface1);
546     // second surface
547     std::unique_ptr<Wrapland::Client::Surface> s2(m_compositor->createSurface());
548     QVERIFY(serverSurfaceCreated.wait());
549     auto serverSurface2 = serverSurfaceCreated.last().first().value<Wrapland::Server::Surface*>();
550     QVERIFY(serverSurface2);
551     //    QVERIFY(serverSurface1->resource() != serverSurface2->resource());
552 
553     // create two images
554     QImage black(24, 24, QImage::Format_RGB32);
555     black.fill(Qt::black);
556     QImage red(24, 24, QImage::Format_ARGB32_Premultiplied);
557     red.fill(QColor(255, 0, 0, 128));
558 
559     auto blackBuffer = pool1.createBuffer(black);
560     auto redBuffer = pool2.createBuffer(red);
561 
562     s1->attachBuffer(blackBuffer);
563     s1->damage(QRect(0, 0, 24, 24));
564     s1->commit(Wrapland::Client::Surface::CommitFlag::None);
565     QSignalSpy commit_spy1(serverSurface1, &Wrapland::Server::Surface::committed);
566     QVERIFY(commit_spy1.isValid());
567     QVERIFY(commit_spy1.wait());
568 
569     // now the ServerSurface should have the black image attached as a buffer
570     auto buffer1 = serverSurface1->state().buffer;
571     QVERIFY(buffer1);
572     QImage buffer1Data = buffer1->shmImage()->createQImage();
573     QCOMPARE(buffer1Data, black);
574     // accessing the same buffer is OK
575     QImage buffer1Data2 = buffer1->shmImage()->createQImage();
576     QCOMPARE(buffer1Data2, buffer1Data);
577     buffer1Data = QImage();
578     QVERIFY(buffer1Data.isNull());
579     buffer1Data2 = QImage();
580     QVERIFY(buffer1Data2.isNull());
581 
582     // attach a buffer for the other surface
583     s2->attachBuffer(redBuffer);
584     s2->damage(QRect(0, 0, 24, 24));
585     s2->commit(Wrapland::Client::Surface::CommitFlag::None);
586     QSignalSpy commit_spy2(serverSurface2, &Wrapland::Server::Surface::committed);
587     QVERIFY(commit_spy2.isValid());
588     QVERIFY(commit_spy2.wait());
589 
590     auto buffer2 = serverSurface2->state().buffer;
591     QVERIFY(buffer2);
592     QImage buffer2Data = buffer2->shmImage()->createQImage();
593     QCOMPARE(buffer2Data, red);
594 
595     // while buffer2 is accessed we cannot access buffer1
596     auto buffer1ShmImage = buffer1->shmImage();
597     QVERIFY(!buffer1ShmImage);
598 
599     // a deep copy can be kept around
600     QImage deepCopy = buffer2Data.copy();
601     QCOMPARE(deepCopy, red);
602     buffer2Data = QImage();
603     QVERIFY(buffer2Data.isNull());
604     QCOMPARE(deepCopy, red);
605 
606     // now that buffer2Data is destroyed we can access buffer1 again
607     buffer1ShmImage = buffer1->shmImage();
608     QVERIFY(buffer1ShmImage);
609     buffer1Data = buffer1ShmImage->createQImage();
610     QVERIFY(!buffer1Data.isNull());
611     QCOMPARE(buffer1Data, black);
612 }
613 
testOpaque()614 void TestSurface::testOpaque()
615 {
616     using namespace Wrapland::Client;
617     using namespace Wrapland::Server;
618     QSignalSpy serverSurfaceCreated(m_serverCompositor,
619                                     SIGNAL(surfaceCreated(Wrapland::Server::Surface*)));
620     QVERIFY(serverSurfaceCreated.isValid());
621     std::unique_ptr<Wrapland::Client::Surface> s{m_compositor->createSurface()};
622     QVERIFY(serverSurfaceCreated.wait());
623     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
624     QVERIFY(serverSurface);
625     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
626     QVERIFY(commit_spy.isValid());
627 
628     // by default there should be an empty opaque region
629     QCOMPARE(serverSurface->state().opaque, QRegion());
630 
631     // let's install an opaque region
632     s->setOpaqueRegion(m_compositor->createRegion(QRegion(0, 10, 20, 30)).get());
633     // the region should only be applied after the surface got committed
634     wl_display_flush(m_connection->display());
635     QCoreApplication::processEvents();
636     QCOMPARE(serverSurface->state().opaque, QRegion());
637     QCOMPARE(commit_spy.count(), 0);
638 
639     // so let's commit to get the new region
640     s->commit(Wrapland::Client::Surface::CommitFlag::None);
641     QVERIFY(commit_spy.wait());
642     QCOMPARE(commit_spy.count(), 1);
643     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::opaque);
644     QCOMPARE(serverSurface->state().opaque, QRegion(0, 10, 20, 30));
645 
646     // committing without setting a new region shouldn't change
647     s->commit(Wrapland::Client::Surface::CommitFlag::None);
648     wl_display_flush(m_connection->display());
649     QCoreApplication::processEvents();
650     QCOMPARE(commit_spy.count(), 2);
651     QCOMPARE(serverSurface->state().updates & Wrapland::Server::surface_change::opaque, false);
652     QCOMPARE(serverSurface->state().opaque, QRegion(0, 10, 20, 30));
653 
654     // let's change the opaque region
655     s->setOpaqueRegion(m_compositor->createRegion(QRegion(10, 20, 30, 40)).get());
656     s->commit(Wrapland::Client::Surface::CommitFlag::None);
657     QVERIFY(commit_spy.wait());
658     QCOMPARE(commit_spy.count(), 3);
659     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::opaque);
660     QCOMPARE(serverSurface->state().opaque, QRegion(10, 20, 30, 40));
661 
662     // and let's go back to an empty region
663     s->setOpaqueRegion();
664     s->commit(Wrapland::Client::Surface::CommitFlag::None);
665     QVERIFY(commit_spy.wait());
666     QCOMPARE(commit_spy.count(), 4);
667     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::opaque);
668     QCOMPARE(serverSurface->state().opaque, QRegion());
669 }
670 
testInput()671 void TestSurface::testInput()
672 {
673     using namespace Wrapland::Client;
674     using namespace Wrapland::Server;
675     QSignalSpy serverSurfaceCreated(m_serverCompositor,
676                                     SIGNAL(surfaceCreated(Wrapland::Server::Surface*)));
677     QVERIFY(serverSurfaceCreated.isValid());
678     std::unique_ptr<Wrapland::Client::Surface> s{m_compositor->createSurface()};
679     QVERIFY(serverSurfaceCreated.wait());
680     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
681     QVERIFY(serverSurface);
682     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
683     QVERIFY(commit_spy.isValid());
684 
685     // by default there should be an empty == infinite input region
686     QCOMPARE(serverSurface->state().input, QRegion());
687     QCOMPARE(serverSurface->state().input_is_infinite, true);
688 
689     // let's install an input region
690     s->setInputRegion(m_compositor->createRegion(QRegion(0, 10, 20, 30)).get());
691     // the region should only be applied after the surface got committed
692     wl_display_flush(m_connection->display());
693     QCoreApplication::processEvents();
694     QCOMPARE(serverSurface->state().input, QRegion());
695     QCOMPARE(serverSurface->state().input_is_infinite, true);
696     QCOMPARE(commit_spy.count(), 0);
697 
698     // so let's commit to get the new region
699     s->commit(Wrapland::Client::Surface::CommitFlag::None);
700     QVERIFY(commit_spy.wait());
701     QCOMPARE(commit_spy.count(), 1);
702     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::input);
703     QCOMPARE(serverSurface->state().input, QRegion(0, 10, 20, 30));
704     QCOMPARE(serverSurface->state().input_is_infinite, false);
705 
706     // committing without setting a new region shouldn't change
707     s->commit(Wrapland::Client::Surface::CommitFlag::None);
708     wl_display_flush(m_connection->display());
709     QCoreApplication::processEvents();
710     QCOMPARE(commit_spy.count(), 2);
711     QCOMPARE(serverSurface->state().updates & Wrapland::Server::surface_change::input, false);
712     QCOMPARE(serverSurface->state().input, QRegion(0, 10, 20, 30));
713     QCOMPARE(serverSurface->state().input_is_infinite, false);
714 
715     // let's change the input region
716     s->setInputRegion(m_compositor->createRegion(QRegion(10, 20, 30, 40)).get());
717     s->commit(Wrapland::Client::Surface::CommitFlag::None);
718     QVERIFY(commit_spy.wait());
719     QCOMPARE(commit_spy.count(), 3);
720     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::input);
721     QCOMPARE(serverSurface->state().input, QRegion(10, 20, 30, 40));
722     QCOMPARE(serverSurface->state().input_is_infinite, false);
723 
724     // and let's go back to an empty region
725     s->setInputRegion();
726     s->commit(Wrapland::Client::Surface::CommitFlag::None);
727     QVERIFY(commit_spy.wait());
728     QCOMPARE(commit_spy.count(), 4);
729     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::input);
730     QCOMPARE(serverSurface->state().input, QRegion());
731     QCOMPARE(serverSurface->state().input_is_infinite, true);
732 }
733 
testScale()734 void TestSurface::testScale()
735 {
736     // This test verifies that updating the scale factor is correctly passed to the Wayland server.
737 
738     // Create surface.
739     QSignalSpy serverSurfaceCreated(m_serverCompositor,
740                                     &Wrapland::Server::Compositor::surfaceCreated);
741     QVERIFY(serverSurfaceCreated.isValid());
742 
743     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
744     QCOMPARE(s->scale(), 1);
745     QVERIFY(serverSurfaceCreated.wait());
746     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
747     QVERIFY(serverSurface);
748     QCOMPARE(serverSurface->state().scale, 1);
749 
750     // let's change the scale factor
751     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
752 
753     QVERIFY(commit_spy.isValid());
754     s->setScale(2);
755     QCOMPARE(s->scale(), 2);
756     // needs a commit
757     QVERIFY(!commit_spy.wait(100));
758     QCOMPARE(serverSurface->state().updates & Wrapland::Server::surface_change::scale, false);
759 
760     s->commit(Wrapland::Client::Surface::CommitFlag::None);
761     QVERIFY(commit_spy.wait());
762     QCOMPARE(commit_spy.count(), 1);
763     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::scale);
764     QCOMPARE(serverSurface->state().scale, 2);
765 
766     // even though we've changed the scale, if we don't have a buffer we
767     // don't have a size. If we don't have a size it can't have changed
768     QCOMPARE(serverSurface->state().updates & Wrapland::Server::surface_change::size, false);
769     QVERIFY(!serverSurface->size().isValid());
770 
771     // let's try changing to same factor, should not emit changed on server
772     s->setScale(2);
773     s->commit(Wrapland::Client::Surface::CommitFlag::None);
774     QVERIFY(commit_spy.wait());
775     QEXPECT_FAIL("", "Scale update set also when factor stays same. Change behavior?", Continue);
776     QCOMPARE(serverSurface->state().updates & Wrapland::Server::surface_change::scale, false);
777 
778     // but changing to a different value should still work
779     s->setScale(4);
780     s->commit(Wrapland::Client::Surface::CommitFlag::None);
781     QVERIFY(commit_spy.wait());
782     QCOMPARE(commit_spy.count(), 3);
783     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::scale);
784     QCOMPARE(serverSurface->state().scale, 4);
785     commit_spy.clear();
786 
787     // attach a buffer of 100x100, our scale is 4, so this should be a size of 25x25
788     QImage red(100, 100, QImage::Format_ARGB32_Premultiplied);
789     red.fill(QColor(255, 0, 0, 128));
790     std::shared_ptr<Wrapland::Client::Buffer> redBuffer = m_shm->createBuffer(red).lock();
791     QVERIFY(redBuffer);
792     s->attachBuffer(redBuffer.get());
793     s->damage(QRect(0, 0, 25, 25));
794     s->commit(Wrapland::Client::Surface::CommitFlag::None);
795     QVERIFY(commit_spy.wait());
796     QCOMPARE(commit_spy.count(), 1);
797     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::size);
798     QCOMPARE(serverSurface->state().updates & Wrapland::Server::surface_change::scale, false);
799     QCOMPARE(serverSurface->size(), QSize(25, 25));
800     commit_spy.clear();
801 
802     // set the scale to 1, buffer is still 100x100 so size should change to 100x100
803     s->setScale(1);
804     s->commit(Wrapland::Client::Surface::CommitFlag::None);
805     QVERIFY(commit_spy.wait());
806     QCOMPARE(commit_spy.count(), 1);
807     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::size);
808     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::scale);
809     QCOMPARE(serverSurface->state().scale, 1);
810     QCOMPARE(serverSurface->size(), QSize(100, 100));
811     commit_spy.clear();
812 
813     // set scale and size in one commit, buffer is 50x50 at scale 2 so size should be 25x25
814     QImage blue(50, 50, QImage::Format_ARGB32_Premultiplied);
815     red.fill(QColor(255, 0, 0, 128));
816     std::shared_ptr<Wrapland::Client::Buffer> blueBuffer = m_shm->createBuffer(blue).lock();
817     QVERIFY(blueBuffer);
818     s->attachBuffer(blueBuffer.get());
819     s->setScale(2);
820     s->commit(Wrapland::Client::Surface::CommitFlag::None);
821     QVERIFY(commit_spy.wait());
822     QCOMPARE(commit_spy.count(), 1);
823     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::size);
824     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::scale);
825     QCOMPARE(serverSurface->state().scale, 2);
826     QCOMPARE(serverSurface->size(), QSize(25, 25));
827 }
828 
testDestroy()829 void TestSurface::testDestroy()
830 {
831     using namespace Wrapland::Client;
832     std::unique_ptr<Wrapland::Client::Surface> s{m_compositor->createSurface()};
833 
834     connect(m_connection, &ConnectionThread::establishedChanged, s.get(), &Surface::release);
835     connect(
836         m_connection, &ConnectionThread::establishedChanged, m_compositor, &Compositor::release);
837     connect(m_connection, &ConnectionThread::establishedChanged, m_shm, &ShmPool::release);
838     connect(m_connection, &ConnectionThread::establishedChanged, m_queue, &EventQueue::release);
839     connect(m_connection,
840             &ConnectionThread::establishedChanged,
841             m_idleInhibitManager,
842             &IdleInhibitManager::release);
843     QVERIFY(s->isValid());
844 
845     delete m_serverCompositor;
846     m_serverCompositor = nullptr;
847     delete m_idleInhibitInterface;
848     m_idleInhibitInterface = nullptr;
849     delete m_display;
850     m_display = nullptr;
851     QTRY_VERIFY(!m_connection->established());
852 
853     // Now the Surface should be destroyed.
854     QTRY_VERIFY(!s->isValid());
855 
856     // Calling destroy again should not fail.
857     s->release();
858 }
859 
testUnmapOfNotMappedSurface()860 void TestSurface::testUnmapOfNotMappedSurface()
861 {
862     // this test verifies that a surface which doesn't have a buffer attached doesn't trigger the
863     // unmapped signal
864 
865     // create surface
866     QSignalSpy serverSurfaceCreated(m_serverCompositor,
867                                     &Wrapland::Server::Compositor::surfaceCreated);
868     QVERIFY(serverSurfaceCreated.isValid());
869     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
870     QVERIFY(serverSurfaceCreated.wait());
871     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
872 
873     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
874 
875     // let's map a null buffer and change scale to trigger a signal we can wait for
876     s->attachBuffer(Wrapland::Client::Buffer::Ptr());
877     s->setScale(2);
878     s->commit(Wrapland::Client::Surface::CommitFlag::None);
879 
880     QVERIFY(commit_spy.wait());
881     QVERIFY(serverSurface->state().updates & Wrapland::Server::surface_change::scale);
882     QCOMPARE(serverSurface->state().updates & Wrapland::Server::surface_change::mapped, false);
883 }
884 
testDamageTracking()885 void TestSurface::testDamageTracking()
886 {
887     // this tests the damage tracking feature
888     using namespace Wrapland::Client;
889     using namespace Wrapland::Server;
890     // create surface
891     QSignalSpy serverSurfaceCreated(m_serverCompositor,
892                                     &Wrapland::Server::Compositor::surfaceCreated);
893     QVERIFY(serverSurfaceCreated.isValid());
894     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
895     QVERIFY(serverSurfaceCreated.wait());
896     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
897 
898     // before first commit, the tracked damage should be empty
899     QVERIFY(serverSurface->trackedDamage().isEmpty());
900 
901     // Now let's damage the surface
902     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
903     QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
904     image.fill(Qt::red);
905     s->attachBuffer(m_shm->createBuffer(image));
906     s->damage(QRect(0, 0, 100, 100));
907     s->commit(Wrapland::Client::Surface::CommitFlag::None);
908     QVERIFY(commit_spy.wait());
909     QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 100, 100));
910     QCOMPARE(serverSurface->state().damage, QRegion(0, 0, 100, 100));
911 
912     // resetting the tracked damage should empty it
913     serverSurface->resetTrackedDamage();
914     QVERIFY(serverSurface->trackedDamage().isEmpty());
915     // but not affect the actual damage
916     QCOMPARE(serverSurface->state().damage, QRegion(0, 0, 100, 100));
917 
918     // let's damage some parts of the surface
919     QPainter p;
920     p.begin(&image);
921     p.fillRect(QRect(0, 0, 10, 10), Qt::blue);
922     p.end();
923     s->attachBuffer(m_shm->createBuffer(image));
924     s->damage(QRect(0, 0, 10, 10));
925     s->commit(Wrapland::Client::Surface::CommitFlag::None);
926     QVERIFY(commit_spy.wait());
927     QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 10, 10));
928     QCOMPARE(serverSurface->state().damage, QRegion(0, 0, 10, 10));
929 
930     // and damage some part completely not bounding to the current damage region
931     p.begin(&image);
932     p.fillRect(QRect(50, 40, 20, 30), Qt::blue);
933     p.end();
934     s->attachBuffer(m_shm->createBuffer(image));
935     s->damage(QRect(50, 40, 20, 30));
936     s->commit(Wrapland::Client::Surface::CommitFlag::None);
937     QVERIFY(commit_spy.wait());
938     QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 10, 10).united(QRegion(50, 40, 20, 30)));
939     QCOMPARE(serverSurface->trackedDamage().rectCount(), 2);
940     QCOMPARE(serverSurface->state().damage, QRegion(50, 40, 20, 30));
941 
942     // now let's reset the tracked damage again
943     serverSurface->resetTrackedDamage();
944     QVERIFY(serverSurface->trackedDamage().isEmpty());
945     // but not affect the actual damage
946     QCOMPARE(serverSurface->state().damage, QRegion(50, 40, 20, 30));
947 }
948 
testSurfaceAt()949 void TestSurface::testSurfaceAt()
950 {
951     // this test verifies that surfaceAt(const QPointF&) works as expected for the case of no
952     // children
953     using namespace Wrapland::Client;
954     using namespace Wrapland::Server;
955     // create surface
956     QSignalSpy serverSurfaceCreated(m_serverCompositor,
957                                     &Wrapland::Server::Compositor::surfaceCreated);
958     QVERIFY(serverSurfaceCreated.isValid());
959     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
960     QVERIFY(serverSurfaceCreated.wait());
961     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
962 
963     // a newly created surface should not be mapped and not provide a surface at a position
964     QVERIFY(!serverSurface->isMapped());
965     QVERIFY(!Wrapland::Server::Test::surface_at(serverSurface, QPointF(0, 0)));
966 
967     // let's damage this surface
968     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
969     QVERIFY(commit_spy.isValid());
970     QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
971     image.fill(Qt::red);
972     s->attachBuffer(m_shm->createBuffer(image));
973     s->damage(QRect(0, 0, 100, 100));
974     s->commit(Wrapland::Client::Surface::CommitFlag::None);
975     QVERIFY(commit_spy.wait());
976 
977     // now the surface is mapped and surfaceAt should give the surface
978     QVERIFY(serverSurface->isMapped());
979     QCOMPARE(Wrapland::Server::Test::surface_at(serverSurface, QPointF(0, 0)), serverSurface);
980     QCOMPARE(Wrapland::Server::Test::surface_at(serverSurface, QPointF(100, 100)), serverSurface);
981     // outside the geometry it should not give a surface
982     QVERIFY(!Wrapland::Server::Test::surface_at(serverSurface, QPointF(101, 101)));
983     QVERIFY(!Wrapland::Server::Test::surface_at(serverSurface, QPointF(-1, -1)));
984 }
985 
testDestroyAttachedBuffer()986 void TestSurface::testDestroyAttachedBuffer()
987 {
988     // this test verifies that destroying of a buffer attached to a surface works
989 
990     // create surface
991     QSignalSpy serverSurfaceCreated(m_serverCompositor,
992                                     &Wrapland::Server::Compositor::surfaceCreated);
993     QVERIFY(serverSurfaceCreated.isValid());
994     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
995     QVERIFY(serverSurfaceCreated.wait());
996     auto serverSurface = serverSurfaceCreated.first().first().value<Wrapland::Server::Surface*>();
997 
998     // let's damage this surface
999     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
1000     QVERIFY(commit_spy.isValid());
1001     QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
1002     image.fill(Qt::red);
1003     s->attachBuffer(m_shm->createBuffer(image));
1004     s->damage(QRect(0, 0, 100, 100));
1005     s->commit(Wrapland::Client::Surface::CommitFlag::None);
1006     QVERIFY(commit_spy.wait());
1007     QVERIFY(serverSurface->state().buffer);
1008 
1009     // attach another buffer
1010     image.fill(Qt::blue);
1011     s->attachBuffer(m_shm->createBuffer(image));
1012     m_connection->flush();
1013 
1014     // Let's try to destroy it
1015     auto currentBuffer = serverSurface->state().buffer;
1016     QSignalSpy destroySpy(currentBuffer.get(), &Wrapland::Server::Buffer::resourceDestroyed);
1017     QVERIFY(destroySpy.isValid());
1018     delete m_shm;
1019     m_shm = nullptr;
1020     QVERIFY(destroySpy.count() || destroySpy.wait());
1021     QVERIFY(!serverSurface->state().buffer);
1022 }
1023 
testDestroyWithPendingCallback()1024 void TestSurface::testDestroyWithPendingCallback()
1025 {
1026     // this test tries to verify that destroying a surface with a pending callback works correctly
1027     // first create surface
1028     using namespace Wrapland::Client;
1029     using namespace Wrapland::Server;
1030     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
1031     QVERIFY(s != nullptr);
1032     QVERIFY(s->isValid());
1033     QSignalSpy surfaceCreatedSpy(m_serverCompositor, &Wrapland::Server::Compositor::surfaceCreated);
1034     QVERIFY(surfaceCreatedSpy.isValid());
1035     QVERIFY(surfaceCreatedSpy.wait());
1036     auto serverSurface = surfaceCreatedSpy.first().first().value<Wrapland::Server::Surface*>();
1037     QVERIFY(serverSurface);
1038 
1039     // now render to it
1040     QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
1041     img.fill(Qt::black);
1042     auto b = m_shm->createBuffer(img);
1043     s->attachBuffer(b);
1044     s->damage(QRect(0, 0, 10, 10));
1045     // add some frame callbacks
1046     wl_callback* callbacks[1000];
1047     for (int i = 0; i < 1000; i++) {
1048         callbacks[i] = wl_surface_frame(*s);
1049     }
1050     s->commit(Wrapland::Client::Surface::CommitFlag::FrameCallback);
1051     QSignalSpy commit_spy(serverSurface, &Wrapland::Server::Surface::committed);
1052     QVERIFY(commit_spy.isValid());
1053     QVERIFY(commit_spy.wait());
1054 
1055     // now try to destroy the Surface again
1056     QSignalSpy destroyedSpy(serverSurface, &QObject::destroyed);
1057     QVERIFY(destroyedSpy.isValid());
1058     s.reset();
1059     QVERIFY(destroyedSpy.wait());
1060 
1061     for (int i = 0; i < 1000; i++) {
1062         wl_callback_destroy(callbacks[i]);
1063     }
1064 }
1065 
testDisconnect()1066 void TestSurface::testDisconnect()
1067 {
1068     // this test verifies that the server side correctly tears down the resources when the client
1069     // disconnects
1070     using namespace Wrapland::Client;
1071     using namespace Wrapland::Server;
1072     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
1073     QVERIFY(s != nullptr);
1074     QVERIFY(s->isValid());
1075     QSignalSpy surfaceCreatedSpy(m_serverCompositor, &Wrapland::Server::Compositor::surfaceCreated);
1076     QVERIFY(surfaceCreatedSpy.isValid());
1077     QVERIFY(surfaceCreatedSpy.wait());
1078     auto serverSurface = surfaceCreatedSpy.first().first().value<Wrapland::Server::Surface*>();
1079     QVERIFY(serverSurface);
1080 
1081     // destroy client
1082     QSignalSpy clientDisconnectedSpy(serverSurface->client(),
1083                                      &Wrapland::Server::Client::disconnected);
1084     QVERIFY(clientDisconnectedSpy.isValid());
1085     QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed);
1086     QVERIFY(surfaceDestroyedSpy.isValid());
1087 
1088     s->release();
1089     m_shm->release();
1090     m_compositor->release();
1091     m_queue->release();
1092     m_idleInhibitManager->release();
1093 
1094     QCOMPARE(surfaceDestroyedSpy.count(), 0);
1095 
1096     QVERIFY(m_connection);
1097     m_connection->deleteLater();
1098     m_connection = nullptr;
1099 
1100     QVERIFY(clientDisconnectedSpy.wait());
1101     QCOMPARE(clientDisconnectedSpy.count(), 1);
1102     QTRY_COMPARE(surfaceDestroyedSpy.count(), 1);
1103 }
1104 
testOutput()1105 void TestSurface::testOutput()
1106 {
1107     // This test verifies that the enter/leave are sent correctly to the Client
1108     qRegisterMetaType<Wrapland::Client::Output*>();
1109 
1110     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
1111     QVERIFY(s != nullptr);
1112     QVERIFY(s->isValid());
1113     QVERIFY(s->outputs().isEmpty());
1114     QSignalSpy enteredSpy(s.get(), &Wrapland::Client::Surface::outputEntered);
1115     QVERIFY(enteredSpy.isValid());
1116     QSignalSpy leftSpy(s.get(), &Wrapland::Client::Surface::outputLeft);
1117     QVERIFY(leftSpy.isValid());
1118     // wait for the surface on the Server side
1119     QSignalSpy surfaceCreatedSpy(m_serverCompositor, &Wrapland::Server::Compositor::surfaceCreated);
1120     QVERIFY(surfaceCreatedSpy.isValid());
1121     QVERIFY(surfaceCreatedSpy.wait());
1122     auto serverSurface = surfaceCreatedSpy.first().first().value<Wrapland::Server::Surface*>();
1123     QVERIFY(serverSurface);
1124     QCOMPARE(serverSurface->outputs(), std::vector<Wrapland::Server::WlOutput*>());
1125 
1126     // Create another registry to get notified about added outputs.
1127     Wrapland::Client::Registry registry;
1128     registry.setEventQueue(m_queue);
1129     QSignalSpy allAnnounced(&registry, &Wrapland::Client::Registry::interfacesAnnounced);
1130     QVERIFY(allAnnounced.isValid());
1131     registry.create(m_connection);
1132     QVERIFY(registry.isValid());
1133     registry.setup();
1134     QVERIFY(allAnnounced.wait());
1135     QSignalSpy outputAnnouncedSpy(&registry, &Wrapland::Client::Registry::outputAnnounced);
1136     QVERIFY(outputAnnouncedSpy.isValid());
1137 
1138     auto serverOutput = new Wrapland::Server::Output(m_display, m_display);
1139     serverOutput->set_enabled(true);
1140     serverOutput->done();
1141 
1142     QVERIFY(outputAnnouncedSpy.wait());
1143     std::unique_ptr<Wrapland::Client::Output> clientOutput(
1144         registry.createOutput(outputAnnouncedSpy.first().first().value<quint32>(),
1145                               outputAnnouncedSpy.first().last().value<quint32>()));
1146     QVERIFY(clientOutput->isValid());
1147     m_connection->flush();
1148     m_display->dispatchEvents();
1149 
1150     // Now enter it.
1151     std::vector<Wrapland::Server::WlOutput*> outputs;
1152     outputs.push_back(serverOutput->wayland_output());
1153     serverSurface->setOutputs(outputs);
1154     QCOMPARE(serverSurface->outputs(), outputs);
1155     QVERIFY(enteredSpy.wait());
1156     QCOMPARE(enteredSpy.count(), 1);
1157     QCOMPARE(enteredSpy.first().first().value<Wrapland::Client::Output*>(), clientOutput.get());
1158     QCOMPARE(s->outputs(), QVector<Wrapland::Client::Output*>{clientOutput.get()});
1159 
1160     // Adding to same should not trigger.
1161     serverSurface->setOutputs(std::vector<Wrapland::Server::Output*>{serverOutput});
1162 
1163     // leave again
1164     serverSurface->setOutputs(std::vector<Wrapland::Server::Output*>());
1165     QCOMPARE(serverSurface->outputs(), std::vector<Wrapland::Server::WlOutput*>());
1166     QVERIFY(leftSpy.wait());
1167     QCOMPARE(enteredSpy.count(), 1);
1168     QCOMPARE(leftSpy.count(), 1);
1169     QCOMPARE(leftSpy.first().first().value<Wrapland::Client::Output*>(), clientOutput.get());
1170     QCOMPARE(s->outputs(), QVector<Wrapland::Client::Output*>());
1171 
1172     // Leave again should not trigger.
1173     serverSurface->setOutputs(std::vector<Wrapland::Server::Output*>());
1174 
1175     // and enter again, just to verify
1176     serverSurface->setOutputs(std::vector<Wrapland::Server::Output*>{serverOutput});
1177     QCOMPARE(serverSurface->outputs(), {serverOutput->wayland_output()});
1178     QVERIFY(enteredSpy.wait());
1179     QCOMPARE(enteredSpy.count(), 2);
1180     QCOMPARE(leftSpy.count(), 1);
1181 
1182     // Delete output client is on. Client should get an exit and be left on no outputs (which is
1183     // allowed).
1184     serverOutput->deleteLater();
1185     QVERIFY(leftSpy.wait());
1186     QCOMPARE(serverSurface->outputs().size(), 0);
1187 }
1188 
testInhibit()1189 void TestSurface::testInhibit()
1190 {
1191     std::unique_ptr<Wrapland::Client::Surface> s(m_compositor->createSurface());
1192     // wait for the surface on the Server side
1193     QSignalSpy surfaceCreatedSpy(m_serverCompositor, &Wrapland::Server::Compositor::surfaceCreated);
1194     QVERIFY(surfaceCreatedSpy.isValid());
1195     QVERIFY(surfaceCreatedSpy.wait());
1196     auto serverSurface = surfaceCreatedSpy.first().first().value<Wrapland::Server::Surface*>();
1197     QVERIFY(serverSurface);
1198     QCOMPARE(serverSurface->inhibitsIdle(), false);
1199 
1200     QSignalSpy inhibitsChangedSpy(serverSurface, &Wrapland::Server::Surface::inhibitsIdleChanged);
1201     QVERIFY(inhibitsChangedSpy.isValid());
1202 
1203     // now create an idle inhibition
1204     std::unique_ptr<Wrapland::Client::IdleInhibitor> inhibitor1(
1205         m_idleInhibitManager->createInhibitor(s.get()));
1206     QVERIFY(inhibitsChangedSpy.wait());
1207     QCOMPARE(serverSurface->inhibitsIdle(), true);
1208 
1209     // creating a second idle inhibition should not trigger the signal
1210     std::unique_ptr<Wrapland::Client::IdleInhibitor> inhibitor2(
1211         m_idleInhibitManager->createInhibitor(s.get()));
1212     QVERIFY(!inhibitsChangedSpy.wait(500));
1213     QCOMPARE(serverSurface->inhibitsIdle(), true);
1214 
1215     // and also deleting the first inhibitor should not yet change the inhibition
1216     inhibitor1.reset();
1217     QVERIFY(!inhibitsChangedSpy.wait(500));
1218     QCOMPARE(serverSurface->inhibitsIdle(), true);
1219 
1220     // but deleting also the second inhibitor should trigger
1221     inhibitor2.reset();
1222     QVERIFY(inhibitsChangedSpy.wait());
1223     QCOMPARE(serverSurface->inhibitsIdle(), false);
1224     QCOMPARE(inhibitsChangedSpy.count(), 2);
1225 
1226     // recreate inhibitor1 should inhibit again
1227     inhibitor1.reset(m_idleInhibitManager->createInhibitor(s.get()));
1228     QVERIFY(inhibitsChangedSpy.wait());
1229     QCOMPARE(serverSurface->inhibitsIdle(), true);
1230     // and destroying should uninhibit
1231     inhibitor1.reset();
1232     QVERIFY(inhibitsChangedSpy.wait());
1233     QCOMPARE(serverSurface->inhibitsIdle(), false);
1234     QCOMPARE(inhibitsChangedSpy.count(), 4);
1235 }
1236 
1237 QTEST_GUILESS_MAIN(TestSurface)
1238 #include "surface.moc"
1239