/* 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 // client #include "KWayland/Client/compositor.h" #include "KWayland/Client/connection_thread.h" #include "KWayland/Client/datadevice.h" #include "KWayland/Client/datadevicemanager.h" #include "KWayland/Client/datasource.h" #include "KWayland/Client/event_queue.h" #include "KWayland/Client/keyboard.h" #include "KWayland/Client/registry.h" #include "KWayland/Client/seat.h" #include "KWayland/Client/surface.h" // server #include "wayland/compositor.h" #include "wayland/datadevicemanager.h" #include "wayland/display.h" #include "wayland/seat.h" using namespace KWin; class SelectionTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testClearOnEnter(); private: KWin::Display *m_display = nullptr; CompositorInterface *m_compositorInterface = nullptr; SeatInterface *m_seatInterface = nullptr; DataDeviceManagerInterface *m_ddmInterface = nullptr; struct Connection { KWayland::Client::ConnectionThread *connection = nullptr; QThread *thread = nullptr; KWayland::Client::EventQueue *queue = nullptr; KWayland::Client::Compositor *compositor = nullptr; KWayland::Client::Seat *seat = nullptr; KWayland::Client::DataDeviceManager *ddm = nullptr; KWayland::Client::Keyboard *keyboard = nullptr; KWayland::Client::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 KWin::Display(this); m_display->addSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_compositorInterface = new CompositorInterface(m_display, m_display); m_seatInterface = new SeatInterface(m_display, QStringLiteral("seat0"), m_display); m_seatInterface->setHasKeyboard(true); m_ddmInterface = new DataDeviceManagerInterface(m_display, m_display); // setup connection setupConnection(&m_client1); setupConnection(&m_client2); } bool SelectionTest::setupConnection(Connection *c) { c->connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(c->connection, &KWayland::Client::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 KWayland::Client::EventQueue(this); c->queue->setup(c->connection); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::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; } c->compositor = registry.createCompositor(registry.interface(KWayland::Client::Registry::Interface::Compositor).name, registry.interface(KWayland::Client::Registry::Interface::Compositor).version, this); if (!c->compositor->isValid()) { return false; } c->ddm = registry.createDataDeviceManager(registry.interface(KWayland::Client::Registry::Interface::DataDeviceManager).name, registry.interface(KWayland::Client::Registry::Interface::DataDeviceManager).version, this); if (!c->ddm->isValid()) { return false; } c->seat = registry.createSeat(registry.interface(KWayland::Client::Registry::Interface::Seat).name, registry.interface(KWayland::Client::Registry::Interface::Seat).version, this); if (!c->seat->isValid()) { return false; } QSignalSpy keyboardSpy(c->seat, &KWayland::Client::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_display) #undef CLEANUP // these are the children of the display m_ddmInterface = nullptr; m_seatInterface = nullptr; m_compositorInterface = nullptr; } 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, &KWayland::Client::DataDevice::selectionCleared); QSignalSpy keyboardEnteredClient1Spy(m_client1.keyboard, &KWayland::Client::Keyboard::entered); // now create a Surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); std::unique_ptr 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 std::unique_ptr dataSource(m_client1.ddm->createDataSource()); dataSource->offer(QStringLiteral("text/plain")); m_client1.dataDevice->setSelection(keyboardEnteredClient1Spy.first().first().value(), dataSource.get()); // now let's bring in client 2 QSignalSpy selectionOfferedClient2Spy(m_client2.dataDevice, &KWayland::Client::DataDevice::selectionOffered); QSignalSpy selectionClearedClient2Spy(m_client2.dataDevice, &KWayland::Client::DataDevice::selectionCleared); QSignalSpy keyboardEnteredClient2Spy(m_client2.keyboard, &KWayland::Client::Keyboard::entered); std::unique_ptr 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 std::unique_ptr dataSource2(m_client2.ddm->createDataSource()); m_client2.dataDevice->setSelection(keyboardEnteredClient2Spy.first().first().value(), dataSource2.get()); QVERIFY(selectionOfferedClient2Spy.wait()); // and clear m_client2.dataDevice->clearSelection(keyboardEnteredClient2Spy.first().first().value() + 1); 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"