2020-03-15 15:19:28 +00:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
|
2016-07-05 08:56:22 +00:00
|
|
|
|
2020-03-15 15:19:28 +00:00
|
|
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
|
|
*/
|
2016-07-05 08:56:22 +00:00
|
|
|
// Qt
|
2023-07-03 19:28:19 +00:00
|
|
|
#include <QSignalSpy>
|
|
|
|
#include <QTest>
|
2016-07-05 08:56:22 +00:00
|
|
|
// client
|
2020-04-29 13:59:23 +00:00
|
|
|
#include "KWayland/Client/compositor.h"
|
2021-08-29 05:11:06 +00:00
|
|
|
#include "KWayland/Client/connection_thread.h"
|
2020-04-29 13:59:23 +00:00
|
|
|
#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"
|
2016-07-05 08:56:22 +00:00
|
|
|
// server
|
2023-09-13 05:52:59 +00:00
|
|
|
#include "wayland/compositor.h"
|
|
|
|
#include "wayland/datadevicemanager.h"
|
2022-04-22 09:27:33 +00:00
|
|
|
#include "wayland/display.h"
|
2023-09-13 05:52:59 +00:00
|
|
|
#include "wayland/seat.h"
|
2016-07-05 08:56:22 +00:00
|
|
|
|
2023-09-13 17:59:29 +00:00
|
|
|
using namespace KWin;
|
2016-07-05 08:56:22 +00:00
|
|
|
|
|
|
|
class SelectionTest : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
private Q_SLOTS:
|
|
|
|
void init();
|
|
|
|
void cleanup();
|
|
|
|
void testClearOnEnter();
|
|
|
|
|
|
|
|
private:
|
2023-09-13 17:59:29 +00:00
|
|
|
KWin::Display *m_display = nullptr;
|
2016-07-05 08:56:22 +00:00
|
|
|
CompositorInterface *m_compositorInterface = nullptr;
|
|
|
|
SeatInterface *m_seatInterface = nullptr;
|
|
|
|
DataDeviceManagerInterface *m_ddmInterface = nullptr;
|
|
|
|
|
2022-04-19 10:14:26 +00:00
|
|
|
struct Connection
|
|
|
|
{
|
2022-11-08 21:15:17 +00:00
|
|
|
KWayland::Client::ConnectionThread *connection = nullptr;
|
2016-07-05 08:56:22 +00:00
|
|
|
QThread *thread = nullptr;
|
2022-11-08 21:15:17 +00:00
|
|
|
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;
|
2016-07-05 08:56:22 +00:00
|
|
|
};
|
|
|
|
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;
|
2023-09-13 17:59:29 +00:00
|
|
|
m_display = new KWin::Display(this);
|
2020-10-19 15:52:56 +00:00
|
|
|
m_display->addSocketName(s_socketName);
|
2016-07-05 08:56:22 +00:00
|
|
|
m_display->start();
|
|
|
|
QVERIFY(m_display->isRunning());
|
|
|
|
m_display->createShm();
|
2020-12-09 20:13:19 +00:00
|
|
|
m_compositorInterface = new CompositorInterface(m_display, m_display);
|
|
|
|
m_seatInterface = new SeatInterface(m_display, m_display);
|
2016-07-05 08:56:22 +00:00
|
|
|
m_seatInterface->setHasKeyboard(true);
|
2020-12-09 20:13:19 +00:00
|
|
|
m_ddmInterface = new DataDeviceManagerInterface(m_display, m_display);
|
2016-07-05 08:56:22 +00:00
|
|
|
|
|
|
|
// setup connection
|
|
|
|
setupConnection(&m_client1);
|
|
|
|
setupConnection(&m_client2);
|
|
|
|
}
|
|
|
|
|
2021-08-29 05:11:06 +00:00
|
|
|
bool SelectionTest::setupConnection(Connection *c)
|
2016-07-05 08:56:22 +00:00
|
|
|
{
|
2022-11-08 21:15:17 +00:00
|
|
|
c->connection = new KWayland::Client::ConnectionThread;
|
|
|
|
QSignalSpy connectedSpy(c->connection, &KWayland::Client::ConnectionThread::connected);
|
2016-07-05 08:56:22 +00:00
|
|
|
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();
|
2018-07-17 09:30:16 +00:00
|
|
|
if (!connectedSpy.wait(500)) {
|
2016-07-05 08:56:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-11-08 21:15:17 +00:00
|
|
|
c->queue = new KWayland::Client::EventQueue(this);
|
2016-07-05 08:56:22 +00:00
|
|
|
c->queue->setup(c->connection);
|
|
|
|
|
2022-11-08 21:15:17 +00:00
|
|
|
KWayland::Client::Registry registry;
|
|
|
|
QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced);
|
2016-07-05 08:56:22 +00:00
|
|
|
if (!interfacesAnnouncedSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
registry.setEventQueue(c->queue);
|
|
|
|
registry.create(c->connection);
|
|
|
|
if (!registry.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
registry.setup();
|
2018-07-17 09:30:16 +00:00
|
|
|
if (!interfacesAnnouncedSpy.wait(500)) {
|
2016-07-05 08:56:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-08-29 05:11:06 +00:00
|
|
|
c->compositor =
|
2022-11-08 21:15:17 +00:00
|
|
|
registry.createCompositor(registry.interface(KWayland::Client::Registry::Interface::Compositor).name, registry.interface(KWayland::Client::Registry::Interface::Compositor).version, this);
|
2016-07-05 08:56:22 +00:00
|
|
|
if (!c->compositor->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-11-08 21:15:17 +00:00
|
|
|
c->ddm = registry.createDataDeviceManager(registry.interface(KWayland::Client::Registry::Interface::DataDeviceManager).name,
|
|
|
|
registry.interface(KWayland::Client::Registry::Interface::DataDeviceManager).version,
|
2016-07-05 08:56:22 +00:00
|
|
|
this);
|
|
|
|
if (!c->ddm->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-11-08 21:15:17 +00:00
|
|
|
c->seat = registry.createSeat(registry.interface(KWayland::Client::Registry::Interface::Seat).name, registry.interface(KWayland::Client::Registry::Interface::Seat).version, this);
|
2016-07-05 08:56:22 +00:00
|
|
|
if (!c->seat->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-11-08 21:15:17 +00:00
|
|
|
QSignalSpy keyboardSpy(c->seat, &KWayland::Client::Seat::hasKeyboardChanged);
|
2016-07-05 08:56:22 +00:00
|
|
|
if (!keyboardSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-17 09:30:16 +00:00
|
|
|
if (!keyboardSpy.wait(500)) {
|
2016-07-05 08:56:22 +00:00
|
|
|
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);
|
2022-04-19 10:14:26 +00:00
|
|
|
#define CLEANUP(variable) \
|
|
|
|
delete variable; \
|
2021-08-29 05:11:06 +00:00
|
|
|
variable = nullptr;
|
2016-07-05 08:56:22 +00:00
|
|
|
|
|
|
|
CLEANUP(m_display)
|
|
|
|
#undef CLEANUP
|
2020-10-30 13:36:24 +00:00
|
|
|
// these are the children of the display
|
|
|
|
m_ddmInterface = nullptr;
|
|
|
|
m_seatInterface = nullptr;
|
|
|
|
m_compositorInterface = nullptr;
|
2016-07-05 08:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2022-11-08 21:15:17 +00:00
|
|
|
QSignalSpy selectionClearedClient1Spy(m_client1.dataDevice, &KWayland::Client::DataDevice::selectionCleared);
|
|
|
|
QSignalSpy keyboardEnteredClient1Spy(m_client1.keyboard, &KWayland::Client::Keyboard::entered);
|
2016-07-05 08:56:22 +00:00
|
|
|
|
|
|
|
// now create a Surface
|
|
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
2022-11-08 21:15:17 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> s1(m_client1.compositor->createSurface());
|
2016-07-05 08:56:22 +00:00
|
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
2021-08-29 05:11:06 +00:00
|
|
|
auto serverSurface1 = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
2016-07-05 08:56:22 +00:00
|
|
|
QVERIFY(serverSurface1);
|
|
|
|
|
|
|
|
// pass this surface keyboard focus
|
|
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface1);
|
|
|
|
// should get a clear
|
|
|
|
QVERIFY(selectionClearedClient1Spy.wait());
|
|
|
|
|
|
|
|
// let's set a selection
|
2022-11-08 21:15:17 +00:00
|
|
|
std::unique_ptr<KWayland::Client::DataSource> dataSource(m_client1.ddm->createDataSource());
|
2016-07-05 08:56:22 +00:00
|
|
|
dataSource->offer(QStringLiteral("text/plain"));
|
2022-08-01 21:29:02 +00:00
|
|
|
m_client1.dataDevice->setSelection(keyboardEnteredClient1Spy.first().first().value<quint32>(), dataSource.get());
|
2016-07-05 08:56:22 +00:00
|
|
|
|
|
|
|
// now let's bring in client 2
|
2022-11-08 21:15:17 +00:00
|
|
|
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<KWayland::Client::Surface> s2(m_client2.compositor->createSurface());
|
2016-07-05 08:56:22 +00:00
|
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
2021-08-29 05:11:06 +00:00
|
|
|
auto serverSurface2 = surfaceCreatedSpy.last().first().value<SurfaceInterface *>();
|
2016-07-05 08:56:22 +00:00
|
|
|
QVERIFY(serverSurface2);
|
|
|
|
|
2018-07-17 10:38:57 +00:00
|
|
|
// entering that surface should give a selection offer
|
2016-07-05 08:56:22 +00:00
|
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface2);
|
|
|
|
QVERIFY(selectionOfferedClient2Spy.wait());
|
|
|
|
QVERIFY(selectionClearedClient2Spy.isEmpty());
|
2018-07-17 10:38:57 +00:00
|
|
|
|
2016-07-05 08:56:22 +00:00
|
|
|
// set a data source but without offers
|
2022-11-08 21:15:17 +00:00
|
|
|
std::unique_ptr<KWayland::Client::DataSource> dataSource2(m_client2.ddm->createDataSource());
|
2022-08-01 21:29:02 +00:00
|
|
|
m_client2.dataDevice->setSelection(keyboardEnteredClient2Spy.first().first().value<quint32>(), dataSource2.get());
|
2016-07-05 08:56:22 +00:00
|
|
|
QVERIFY(selectionOfferedClient2Spy.wait());
|
2018-07-17 10:38:57 +00:00
|
|
|
// and clear
|
2024-08-02 09:41:20 +00:00
|
|
|
m_client2.dataDevice->clearSelection(keyboardEnteredClient2Spy.first().first().value<quint32>() + 1);
|
2016-07-05 08:56:22 +00:00
|
|
|
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"
|