File indexing completed on 2024-11-10 04:56:16
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(®istry, &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"