File indexing completed on 2024-06-02 05:36:40

0001 /*
0002     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 // Qt
0007 #include <QSignalSpy>
0008 #include <QTest>
0009 // client
0010 #include "KWayland/Client/compositor.h"
0011 #include "KWayland/Client/connection_thread.h"
0012 #include "KWayland/Client/datadevice.h"
0013 #include "KWayland/Client/datadevicemanager.h"
0014 #include "KWayland/Client/datasource.h"
0015 #include "KWayland/Client/event_queue.h"
0016 #include "KWayland/Client/keyboard.h"
0017 #include "KWayland/Client/registry.h"
0018 #include "KWayland/Client/seat.h"
0019 #include "KWayland/Client/surface.h"
0020 // server
0021 #include "wayland/compositor.h"
0022 #include "wayland/datadevicemanager.h"
0023 #include "wayland/display.h"
0024 #include "wayland/seat.h"
0025 
0026 using namespace KWin;
0027 
0028 class SelectionTest : public QObject
0029 {
0030     Q_OBJECT
0031 private Q_SLOTS:
0032     void init();
0033     void cleanup();
0034     void testClearOnEnter();
0035 
0036 private:
0037     KWin::Display *m_display = nullptr;
0038     CompositorInterface *m_compositorInterface = nullptr;
0039     SeatInterface *m_seatInterface = nullptr;
0040     DataDeviceManagerInterface *m_ddmInterface = nullptr;
0041 
0042     struct Connection
0043     {
0044         KWayland::Client::ConnectionThread *connection = nullptr;
0045         QThread *thread = nullptr;
0046         KWayland::Client::EventQueue *queue = nullptr;
0047         KWayland::Client::Compositor *compositor = nullptr;
0048         KWayland::Client::Seat *seat = nullptr;
0049         KWayland::Client::DataDeviceManager *ddm = nullptr;
0050         KWayland::Client::Keyboard *keyboard = nullptr;
0051         KWayland::Client::DataDevice *dataDevice = nullptr;
0052     };
0053     bool setupConnection(Connection *c);
0054     void cleanupConnection(Connection *c);
0055 
0056     Connection m_client1;
0057     Connection m_client2;
0058 };
0059 
0060 static const QString s_socketName = QStringLiteral("kwayland-test-selection-0");
0061 
0062 void SelectionTest::init()
0063 {
0064     delete m_display;
0065     m_display = new KWin::Display(this);
0066     m_display->addSocketName(s_socketName);
0067     m_display->start();
0068     QVERIFY(m_display->isRunning());
0069     m_display->createShm();
0070     m_compositorInterface = new CompositorInterface(m_display, m_display);
0071     m_seatInterface = new SeatInterface(m_display, m_display);
0072     m_seatInterface->setHasKeyboard(true);
0073     m_ddmInterface = new DataDeviceManagerInterface(m_display, m_display);
0074 
0075     // setup connection
0076     setupConnection(&m_client1);
0077     setupConnection(&m_client2);
0078 }
0079 
0080 bool SelectionTest::setupConnection(Connection *c)
0081 {
0082     c->connection = new KWayland::Client::ConnectionThread;
0083     QSignalSpy connectedSpy(c->connection, &KWayland::Client::ConnectionThread::connected);
0084     if (!connectedSpy.isValid()) {
0085         return false;
0086     }
0087     c->connection->setSocketName(s_socketName);
0088 
0089     c->thread = new QThread(this);
0090     c->connection->moveToThread(c->thread);
0091     c->thread->start();
0092 
0093     c->connection->initConnection();
0094     if (!connectedSpy.wait(500)) {
0095         return false;
0096     }
0097 
0098     c->queue = new KWayland::Client::EventQueue(this);
0099     c->queue->setup(c->connection);
0100 
0101     KWayland::Client::Registry registry;
0102     QSignalSpy interfacesAnnouncedSpy(&registry, &KWayland::Client::Registry::interfacesAnnounced);
0103     if (!interfacesAnnouncedSpy.isValid()) {
0104         return false;
0105     }
0106     registry.setEventQueue(c->queue);
0107     registry.create(c->connection);
0108     if (!registry.isValid()) {
0109         return false;
0110     }
0111     registry.setup();
0112     if (!interfacesAnnouncedSpy.wait(500)) {
0113         return false;
0114     }
0115 
0116     c->compositor =
0117         registry.createCompositor(registry.interface(KWayland::Client::Registry::Interface::Compositor).name, registry.interface(KWayland::Client::Registry::Interface::Compositor).version, this);
0118     if (!c->compositor->isValid()) {
0119         return false;
0120     }
0121     c->ddm = registry.createDataDeviceManager(registry.interface(KWayland::Client::Registry::Interface::DataDeviceManager).name,
0122                                               registry.interface(KWayland::Client::Registry::Interface::DataDeviceManager).version,
0123                                               this);
0124     if (!c->ddm->isValid()) {
0125         return false;
0126     }
0127     c->seat = registry.createSeat(registry.interface(KWayland::Client::Registry::Interface::Seat).name, registry.interface(KWayland::Client::Registry::Interface::Seat).version, this);
0128     if (!c->seat->isValid()) {
0129         return false;
0130     }
0131     QSignalSpy keyboardSpy(c->seat, &KWayland::Client::Seat::hasKeyboardChanged);
0132     if (!keyboardSpy.isValid()) {
0133         return false;
0134     }
0135     if (!keyboardSpy.wait(500)) {
0136         return false;
0137     }
0138     if (!c->seat->hasKeyboard()) {
0139         return false;
0140     }
0141     c->keyboard = c->seat->createKeyboard(c->seat);
0142     if (!c->keyboard->isValid()) {
0143         return false;
0144     }
0145     c->dataDevice = c->ddm->getDataDevice(c->seat, this);
0146     if (!c->dataDevice->isValid()) {
0147         return false;
0148     }
0149 
0150     return true;
0151 }
0152 
0153 void SelectionTest::cleanup()
0154 {
0155     cleanupConnection(&m_client1);
0156     cleanupConnection(&m_client2);
0157 #define CLEANUP(variable) \
0158     delete variable;      \
0159     variable = nullptr;
0160 
0161     CLEANUP(m_display)
0162 #undef CLEANUP
0163     // these are the children of the display
0164     m_ddmInterface = nullptr;
0165     m_seatInterface = nullptr;
0166     m_compositorInterface = nullptr;
0167 }
0168 
0169 void SelectionTest::cleanupConnection(Connection *c)
0170 {
0171     delete c->dataDevice;
0172     c->dataDevice = nullptr;
0173     delete c->keyboard;
0174     c->keyboard = nullptr;
0175     delete c->ddm;
0176     c->ddm = nullptr;
0177     delete c->seat;
0178     c->seat = nullptr;
0179     delete c->compositor;
0180     c->compositor = nullptr;
0181     delete c->queue;
0182     c->queue = nullptr;
0183     if (c->connection) {
0184         c->connection->deleteLater();
0185         c->connection = nullptr;
0186     }
0187     if (c->thread) {
0188         c->thread->quit();
0189         c->thread->wait();
0190         delete c->thread;
0191         c->thread = nullptr;
0192     }
0193 }
0194 
0195 void SelectionTest::testClearOnEnter()
0196 {
0197     // this test verifies that the selection is cleared prior to keyboard enter if there is no current selection
0198     QSignalSpy selectionClearedClient1Spy(m_client1.dataDevice, &KWayland::Client::DataDevice::selectionCleared);
0199     QSignalSpy keyboardEnteredClient1Spy(m_client1.keyboard, &KWayland::Client::Keyboard::entered);
0200 
0201     // now create a Surface
0202     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
0203     std::unique_ptr<KWayland::Client::Surface> s1(m_client1.compositor->createSurface());
0204     QVERIFY(surfaceCreatedSpy.wait());
0205     auto serverSurface1 = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
0206     QVERIFY(serverSurface1);
0207 
0208     // pass this surface keyboard focus
0209     m_seatInterface->setFocusedKeyboardSurface(serverSurface1);
0210     // should get a clear
0211     QVERIFY(selectionClearedClient1Spy.wait());
0212 
0213     // let's set a selection
0214     std::unique_ptr<KWayland::Client::DataSource> dataSource(m_client1.ddm->createDataSource());
0215     dataSource->offer(QStringLiteral("text/plain"));
0216     m_client1.dataDevice->setSelection(keyboardEnteredClient1Spy.first().first().value<quint32>(), dataSource.get());
0217 
0218     // now let's bring in client 2
0219     QSignalSpy selectionOfferedClient2Spy(m_client2.dataDevice, &KWayland::Client::DataDevice::selectionOffered);
0220     QSignalSpy selectionClearedClient2Spy(m_client2.dataDevice, &KWayland::Client::DataDevice::selectionCleared);
0221     QSignalSpy keyboardEnteredClient2Spy(m_client2.keyboard, &KWayland::Client::Keyboard::entered);
0222     std::unique_ptr<KWayland::Client::Surface> s2(m_client2.compositor->createSurface());
0223     QVERIFY(surfaceCreatedSpy.wait());
0224     auto serverSurface2 = surfaceCreatedSpy.last().first().value<SurfaceInterface *>();
0225     QVERIFY(serverSurface2);
0226 
0227     // entering that surface should give a selection offer
0228     m_seatInterface->setFocusedKeyboardSurface(serverSurface2);
0229     QVERIFY(selectionOfferedClient2Spy.wait());
0230     QVERIFY(selectionClearedClient2Spy.isEmpty());
0231 
0232     // set a data source but without offers
0233     std::unique_ptr<KWayland::Client::DataSource> dataSource2(m_client2.ddm->createDataSource());
0234     m_client2.dataDevice->setSelection(keyboardEnteredClient2Spy.first().first().value<quint32>(), dataSource2.get());
0235     QVERIFY(selectionOfferedClient2Spy.wait());
0236     // and clear
0237     m_client2.dataDevice->clearSelection(keyboardEnteredClient2Spy.first().first().value<quint32>());
0238     QVERIFY(selectionClearedClient2Spy.wait());
0239 
0240     // now pass focus to first surface
0241     m_seatInterface->setFocusedKeyboardSurface(serverSurface1);
0242     // we should get a clear
0243     QVERIFY(selectionClearedClient1Spy.wait());
0244 }
0245 
0246 QTEST_GUILESS_MAIN(SelectionTest)
0247 #include "test_selection.moc"