/* SPDX-FileCopyrightText: 2016 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ // Qt #include #include #include // client #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/datadevice.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/event_queue.h" #include "../../src/client/keyboard.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/surface.h" // server #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/display.h" #include "../../src/server/seat_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class SelectionTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testClearOnEnter(); private: Display *m_display = nullptr; CompositorInterface *m_compositorInterface = nullptr; SeatInterface *m_seatInterface = nullptr; DataDeviceManagerInterface *m_ddmInterface = nullptr; struct Connection { ConnectionThread *connection = nullptr; QThread *thread = nullptr; EventQueue *queue = nullptr; Compositor *compositor = nullptr; Seat *seat = nullptr; DataDeviceManager *ddm = nullptr; Keyboard *keyboard = nullptr; DataDevice *dataDevice = nullptr; }; bool setupConnection(Connection *c); void cleanupConnection(Connection *c); Connection m_client1; Connection m_client2; }; static const QString s_socketName = QStringLiteral("kwayland-test-selection-0"); void SelectionTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); m_seatInterface = m_display->createSeat(m_display); m_seatInterface->setHasKeyboard(true); m_seatInterface->create(); m_ddmInterface = m_display->createDataDeviceManager(m_display); m_ddmInterface->create(); // setup connection setupConnection(&m_client1); setupConnection(&m_client2); } bool SelectionTest::setupConnection(Connection *c) { c->connection = new ConnectionThread; QSignalSpy connectedSpy(c->connection, &ConnectionThread::connected); if (!connectedSpy.isValid()) { return false; } c->connection->setSocketName(s_socketName); c->thread = new QThread(this); c->connection->moveToThread(c->thread); c->thread->start(); c->connection->initConnection(); if (!connectedSpy.wait(500)) { return false; } c->queue = new EventQueue(this); c->queue->setup(c->connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); if (!interfacesAnnouncedSpy.isValid()) { return false; } registry.setEventQueue(c->queue); registry.create(c->connection); if (!registry.isValid()) { return false; } registry.setup(); if (!interfacesAnnouncedSpy.wait(500)) { return false; } auto compositorInterface = registry.interface(Registry::Interface::Compositor); c->compositor = registry.createCompositor(compositorInterface.name, compositorInterface.version, this); if (!c->compositor->isValid()) { return false; } c->ddm = registry.createDataDeviceManager(registry.interface(Registry::Interface::DataDeviceManager).name, registry.interface(Registry::Interface::DataDeviceManager).version, this); if (!c->ddm->isValid()) { return false; } c->seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); if (!c->seat->isValid()) { return false; } QSignalSpy keyboardSpy(c->seat, &Seat::hasKeyboardChanged); if (!keyboardSpy.isValid()) { return false; } if (!keyboardSpy.wait(500)) { return false; } if (!c->seat->hasKeyboard()) { return false; } c->keyboard = c->seat->createKeyboard(c->seat); if (!c->keyboard->isValid()) { return false; } c->dataDevice = c->ddm->getDataDevice(c->seat, this); if (!c->dataDevice->isValid()) { return false; } return true; } void SelectionTest::cleanup() { cleanupConnection(&m_client1); cleanupConnection(&m_client2); #define CLEANUP(variable) \ delete variable; \ variable = nullptr; CLEANUP(m_ddmInterface) CLEANUP(m_seatInterface) CLEANUP(m_compositorInterface) CLEANUP(m_display) #undef CLEANUP } void SelectionTest::cleanupConnection(Connection *c) { delete c->dataDevice; c->dataDevice = nullptr; delete c->keyboard; c->keyboard = nullptr; delete c->ddm; c->ddm = nullptr; delete c->seat; c->seat = nullptr; delete c->compositor; c->compositor = nullptr; delete c->queue; c->queue = nullptr; if (c->connection) { c->connection->deleteLater(); c->connection = nullptr; } if (c->thread) { c->thread->quit(); c->thread->wait(); delete c->thread; c->thread = nullptr; } } void SelectionTest::testClearOnEnter() { // this test verifies that the selection is cleared prior to keyboard enter if there is no current selection QSignalSpy selectionClearedClient1Spy(m_client1.dataDevice, &DataDevice::selectionCleared); QVERIFY(selectionClearedClient1Spy.isValid()); QSignalSpy keyboardEnteredClient1Spy(m_client1.keyboard, &Keyboard::entered); QVERIFY(keyboardEnteredClient1Spy.isValid()); // now create a Surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer s1(m_client1.compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface1 = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface1); // pass this surface keyboard focus m_seatInterface->setFocusedKeyboardSurface(serverSurface1); // should get a clear QVERIFY(selectionClearedClient1Spy.wait()); // let's set a selection QScopedPointer dataSource(m_client1.ddm->createDataSource()); dataSource->offer(QStringLiteral("text/plain")); m_client1.dataDevice->setSelection(keyboardEnteredClient1Spy.first().first().value(), dataSource.data()); // now let's bring in client 2 QSignalSpy selectionOfferedClient2Spy(m_client2.dataDevice, &DataDevice::selectionOffered); QVERIFY(selectionOfferedClient2Spy.isValid()); QSignalSpy selectionClearedClient2Spy(m_client2.dataDevice, &DataDevice::selectionCleared); QVERIFY(selectionClearedClient2Spy.isValid()); QSignalSpy keyboardEnteredClient2Spy(m_client2.keyboard, &Keyboard::entered); QVERIFY(keyboardEnteredClient2Spy.isValid()); QScopedPointer s2(m_client2.compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface2 = surfaceCreatedSpy.last().first().value(); QVERIFY(serverSurface2); // entering that surface should give a selection offer m_seatInterface->setFocusedKeyboardSurface(serverSurface2); QVERIFY(selectionOfferedClient2Spy.wait()); QVERIFY(selectionClearedClient2Spy.isEmpty()); // set a data source but without offers QScopedPointer dataSource2(m_client2.ddm->createDataSource()); m_client2.dataDevice->setSelection(keyboardEnteredClient2Spy.first().first().value(), dataSource2.data()); QVERIFY(selectionOfferedClient2Spy.wait()); // and clear m_client2.dataDevice->clearSelection(keyboardEnteredClient2Spy.first().first().value()); QVERIFY(selectionClearedClient2Spy.wait()); // now pass focus to first surface m_seatInterface->setFocusedKeyboardSurface(serverSurface1); // we should get a clear QVERIFY(selectionClearedClient1Spy.wait()); } QTEST_GUILESS_MAIN(SelectionTest) #include "test_selection.moc"