KWin
Loading...
Searching...
No Matches
test_selection.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6// Qt
7#include <QSignalSpy>
8#include <QTest>
9// client
10#include "KWayland/Client/compositor.h"
11#include "KWayland/Client/connection_thread.h"
12#include "KWayland/Client/datadevice.h"
13#include "KWayland/Client/datadevicemanager.h"
14#include "KWayland/Client/datasource.h"
15#include "KWayland/Client/event_queue.h"
16#include "KWayland/Client/keyboard.h"
17#include "KWayland/Client/registry.h"
18#include "KWayland/Client/seat.h"
19#include "KWayland/Client/surface.h"
20// server
21#include "wayland/compositor.h"
23#include "wayland/display.h"
24#include "wayland/seat.h"
25
26using namespace KWin;
27
28class SelectionTest : public QObject
29{
30 Q_OBJECT
31private Q_SLOTS:
32 void init();
33 void cleanup();
34 void testClearOnEnter();
35
36private:
37 KWin::Display *m_display = nullptr;
38 CompositorInterface *m_compositorInterface = nullptr;
39 SeatInterface *m_seatInterface = nullptr;
40 DataDeviceManagerInterface *m_ddmInterface = nullptr;
41
42 struct Connection
43 {
44 KWayland::Client::ConnectionThread *connection = nullptr;
45 QThread *thread = nullptr;
46 KWayland::Client::EventQueue *queue = nullptr;
47 KWayland::Client::Compositor *compositor = nullptr;
48 KWayland::Client::Seat *seat = nullptr;
49 KWayland::Client::DataDeviceManager *ddm = nullptr;
50 KWayland::Client::Keyboard *keyboard = nullptr;
51 KWayland::Client::DataDevice *dataDevice = nullptr;
52 };
53 bool setupConnection(Connection *c);
54 void cleanupConnection(Connection *c);
55
56 Connection m_client1;
57 Connection m_client2;
58};
59
60static const QString s_socketName = QStringLiteral("kwayland-test-selection-0");
61
62void SelectionTest::init()
63{
64 delete m_display;
65 m_display = new KWin::Display(this);
66 m_display->addSocketName(s_socketName);
67 m_display->start();
68 QVERIFY(m_display->isRunning());
69 m_display->createShm();
70 m_compositorInterface = new CompositorInterface(m_display, m_display);
71 m_seatInterface = new SeatInterface(m_display, m_display);
72 m_seatInterface->setHasKeyboard(true);
73 m_ddmInterface = new DataDeviceManagerInterface(m_display, m_display);
74
75 // setup connection
76 setupConnection(&m_client1);
77 setupConnection(&m_client2);
78}
79
80bool SelectionTest::setupConnection(Connection *c)
81{
82 c->connection = new KWayland::Client::ConnectionThread;
83 QSignalSpy connectedSpy(c->connection, &KWayland::Client::ConnectionThread::connected);
84 if (!connectedSpy.isValid()) {
85 return false;
86 }
87 c->connection->setSocketName(s_socketName);
88
89 c->thread = new QThread(this);
90 c->connection->moveToThread(c->thread);
91 c->thread->start();
92
93 c->connection->initConnection();
94 if (!connectedSpy.wait(500)) {
95 return false;
96 }
97
98 c->queue = new KWayland::Client::EventQueue(this);
99 c->queue->setup(c->connection);
100
101 KWayland::Client::Registry registry;
102 QSignalSpy interfacesAnnouncedSpy(&registry, &KWayland::Client::Registry::interfacesAnnounced);
103 if (!interfacesAnnouncedSpy.isValid()) {
104 return false;
105 }
106 registry.setEventQueue(c->queue);
107 registry.create(c->connection);
108 if (!registry.isValid()) {
109 return false;
110 }
111 registry.setup();
112 if (!interfacesAnnouncedSpy.wait(500)) {
113 return false;
114 }
115
116 c->compositor =
117 registry.createCompositor(registry.interface(KWayland::Client::Registry::Interface::Compositor).name, registry.interface(KWayland::Client::Registry::Interface::Compositor).version, this);
118 if (!c->compositor->isValid()) {
119 return false;
120 }
121 c->ddm = registry.createDataDeviceManager(registry.interface(KWayland::Client::Registry::Interface::DataDeviceManager).name,
122 registry.interface(KWayland::Client::Registry::Interface::DataDeviceManager).version,
123 this);
124 if (!c->ddm->isValid()) {
125 return false;
126 }
127 c->seat = registry.createSeat(registry.interface(KWayland::Client::Registry::Interface::Seat).name, registry.interface(KWayland::Client::Registry::Interface::Seat).version, this);
128 if (!c->seat->isValid()) {
129 return false;
130 }
131 QSignalSpy keyboardSpy(c->seat, &KWayland::Client::Seat::hasKeyboardChanged);
132 if (!keyboardSpy.isValid()) {
133 return false;
134 }
135 if (!keyboardSpy.wait(500)) {
136 return false;
137 }
138 if (!c->seat->hasKeyboard()) {
139 return false;
140 }
141 c->keyboard = c->seat->createKeyboard(c->seat);
142 if (!c->keyboard->isValid()) {
143 return false;
144 }
145 c->dataDevice = c->ddm->getDataDevice(c->seat, this);
146 if (!c->dataDevice->isValid()) {
147 return false;
148 }
149
150 return true;
151}
152
153void SelectionTest::cleanup()
154{
155 cleanupConnection(&m_client1);
156 cleanupConnection(&m_client2);
157#define CLEANUP(variable) \
158 delete variable; \
159 variable = nullptr;
160
161 CLEANUP(m_display)
162#undef CLEANUP
163 // these are the children of the display
164 m_ddmInterface = nullptr;
165 m_seatInterface = nullptr;
166 m_compositorInterface = nullptr;
167}
168
169void SelectionTest::cleanupConnection(Connection *c)
170{
171 delete c->dataDevice;
172 c->dataDevice = nullptr;
173 delete c->keyboard;
174 c->keyboard = nullptr;
175 delete c->ddm;
176 c->ddm = nullptr;
177 delete c->seat;
178 c->seat = nullptr;
179 delete c->compositor;
180 c->compositor = nullptr;
181 delete c->queue;
182 c->queue = nullptr;
183 if (c->connection) {
184 c->connection->deleteLater();
185 c->connection = nullptr;
186 }
187 if (c->thread) {
188 c->thread->quit();
189 c->thread->wait();
190 delete c->thread;
191 c->thread = nullptr;
192 }
193}
194
195void SelectionTest::testClearOnEnter()
196{
197 // this test verifies that the selection is cleared prior to keyboard enter if there is no current selection
198 QSignalSpy selectionClearedClient1Spy(m_client1.dataDevice, &KWayland::Client::DataDevice::selectionCleared);
199 QSignalSpy keyboardEnteredClient1Spy(m_client1.keyboard, &KWayland::Client::Keyboard::entered);
200
201 // now create a Surface
202 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
203 std::unique_ptr<KWayland::Client::Surface> s1(m_client1.compositor->createSurface());
204 QVERIFY(surfaceCreatedSpy.wait());
205 auto serverSurface1 = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
206 QVERIFY(serverSurface1);
207
208 // pass this surface keyboard focus
209 m_seatInterface->setFocusedKeyboardSurface(serverSurface1);
210 // should get a clear
211 QVERIFY(selectionClearedClient1Spy.wait());
212
213 // let's set a selection
214 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_client1.ddm->createDataSource());
215 dataSource->offer(QStringLiteral("text/plain"));
216 m_client1.dataDevice->setSelection(keyboardEnteredClient1Spy.first().first().value<quint32>(), dataSource.get());
217
218 // now let's bring in client 2
219 QSignalSpy selectionOfferedClient2Spy(m_client2.dataDevice, &KWayland::Client::DataDevice::selectionOffered);
220 QSignalSpy selectionClearedClient2Spy(m_client2.dataDevice, &KWayland::Client::DataDevice::selectionCleared);
221 QSignalSpy keyboardEnteredClient2Spy(m_client2.keyboard, &KWayland::Client::Keyboard::entered);
222 std::unique_ptr<KWayland::Client::Surface> s2(m_client2.compositor->createSurface());
223 QVERIFY(surfaceCreatedSpy.wait());
224 auto serverSurface2 = surfaceCreatedSpy.last().first().value<SurfaceInterface *>();
225 QVERIFY(serverSurface2);
226
227 // entering that surface should give a selection offer
228 m_seatInterface->setFocusedKeyboardSurface(serverSurface2);
229 QVERIFY(selectionOfferedClient2Spy.wait());
230 QVERIFY(selectionClearedClient2Spy.isEmpty());
231
232 // set a data source but without offers
233 std::unique_ptr<KWayland::Client::DataSource> dataSource2(m_client2.ddm->createDataSource());
234 m_client2.dataDevice->setSelection(keyboardEnteredClient2Spy.first().first().value<quint32>(), dataSource2.get());
235 QVERIFY(selectionOfferedClient2Spy.wait());
236 // and clear
237 m_client2.dataDevice->clearSelection(keyboardEnteredClient2Spy.first().first().value<quint32>());
238 QVERIFY(selectionClearedClient2Spy.wait());
239
240 // now pass focus to first surface
241 m_seatInterface->setFocusedKeyboardSurface(serverSurface1);
242 // we should get a clear
243 QVERIFY(selectionClearedClient1Spy.wait());
244}
245
246QTEST_GUILESS_MAIN(SelectionTest)
247#include "test_selection.moc"
void surfaceCreated(KWin::SurfaceInterface *surface)
Represents the Global for wl_data_device_manager interface.
Class holding the Wayland server display loop.
Definition display.h:34
void createShm()
Definition display.cpp:128
bool addSocketName(const QString &name=QString())
Definition display.cpp:68
bool isRunning() const
Definition display.cpp:144
bool start()
Definition display.cpp:92
Represents a Seat on the Wayland Display.
Definition seat.h:134
void setHasKeyboard(bool has)
Definition seat.cpp:342
void setFocusedKeyboardSurface(SurfaceInterface *surface)
Definition seat.cpp:915
Resource representing a wl_surface.
Definition surface.h:80
KWayland::Client::Registry * registry
#define CLEANUP(variable)