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(®istry, SIGNAL(compositorAnnounced(quint32, quint32)));
141 QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32, quint32)));
142 QSignalSpy allAnnounced(®istry, 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(®istry, 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(®istry, &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(®istry, &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