From 92c46fdcd3263398aa4be354533b4c2520a54dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 4 Nov 2014 15:10:22 +0100 Subject: [PATCH] Add DataDeviceManager and DataSource in client and server Basic implementation of the DataDeviceManager and the DataSource it creates. Still needs support for DataDevice and DataOffer to complement the API. --- src/wayland/CMakeLists.txt | 4 + src/wayland/autotests/client/CMakeLists.txt | 11 + .../autotests/client/test_datasource.cpp | 294 ++++++++++++++++++ .../client/test_wayland_registry.cpp | 11 + src/wayland/datadevicemanager_interface.cpp | 155 +++++++++ src/wayland/datadevicemanager_interface.h | 59 ++++ src/wayland/datasource_interface.cpp | 150 +++++++++ src/wayland/datasource_interface.h | 68 ++++ src/wayland/display.cpp | 8 + src/wayland/display.h | 2 + 10 files changed, 762 insertions(+) create mode 100644 src/wayland/autotests/client/test_datasource.cpp create mode 100644 src/wayland/datadevicemanager_interface.cpp create mode 100644 src/wayland/datadevicemanager_interface.h create mode 100644 src/wayland/datasource_interface.cpp create mode 100644 src/wayland/datasource_interface.h diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index d565f3eca6..8757623a17 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -1,6 +1,8 @@ set(SERVER_LIB_SRCS buffer_interface.cpp compositor_interface.cpp + datadevicemanager_interface.cpp + datasource_interface.cpp display.cpp output_interface.cpp region_interface.cpp @@ -45,6 +47,8 @@ set_target_properties(KF5WaylandServer PROPERTIES VERSION ${KWAYLAND_VERSION_S # ${CMAKE_CURRENT_BINARY_DIR}/KWayland/Server/kwaylandserver_export.h # buffer_interface.h # compositor_interface.h +# datadevicemanager_interface.h +# datasource_interface.h # display.h # output_interface.h # region_interface.h diff --git a/src/wayland/autotests/client/CMakeLists.txt b/src/wayland/autotests/client/CMakeLists.txt index e90e4dd54b..b224b51237 100644 --- a/src/wayland/autotests/client/CMakeLists.txt +++ b/src/wayland/autotests/client/CMakeLists.txt @@ -134,3 +134,14 @@ add_executable(testRegion ${testRegion_SRCS}) target_link_libraries( testRegion Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(kwayland-testRegion testRegion) ecm_mark_as_test(testRegion) + +######################################################## +# Test DataSource +######################################################## +set( testDataSource_SRCS + test_datasource.cpp + ) +add_executable(testDataSource ${testDataSource_SRCS}) +target_link_libraries( testDataSource Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client) +add_test(kwayland-testDataSource testDataSource) +ecm_mark_as_test(testDataSource) diff --git a/src/wayland/autotests/client/test_datasource.cpp b/src/wayland/autotests/client/test_datasource.cpp new file mode 100644 index 0000000000..9fe5033c38 --- /dev/null +++ b/src/wayland/autotests/client/test_datasource.cpp @@ -0,0 +1,294 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +// Qt +#include +#include +// KWayland +#include "../../src/client/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/datadevicemanager.h" +#include "../../src/client/datasource.h" +#include "../../src/client/registry.h" +#include "../../src/server/display.h" +#include "../../src/server/datadevicemanager_interface.h" +#include "../../src/server/datasource_interface.h" +// Wayland +#include + +class TestDataSource : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void init(); + void cleanup(); + + void testOffer(); + void testTargetAccepts_data(); + void testTargetAccepts(); + void testRequestSend(); + void testCancel(); + void testDestroy(); + +private: + KWayland::Server::Display *m_display = nullptr; + KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; + KWayland::Client::ConnectionThread *m_connection = nullptr; + KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr; + KWayland::Client::EventQueue *m_queue = nullptr; + QThread *m_thread = nullptr; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datasource-0"); + +void TestDataSource::init() +{ + using namespace KWayland::Server; + delete m_display; + m_display = new Display(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); + m_connection->setSocketName(s_socketName); + + m_thread = new QThread(this); + m_connection->moveToThread(m_thread); + m_thread->start(); + + m_connection->initConnection(); + QVERIFY(connectedSpy.wait()); + + m_queue = new KWayland::Client::EventQueue(this); + QVERIFY(!m_queue->isValid()); + m_queue->setup(m_connection); + QVERIFY(m_queue->isValid()); + + KWayland::Client::Registry registry; + QSignalSpy dataDeviceManagerSpy(®istry, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32))); + QVERIFY(dataDeviceManagerSpy.isValid()); + QVERIFY(!registry.eventQueue()); + registry.setEventQueue(m_queue); + QCOMPARE(registry.eventQueue(), m_queue); + registry.create(m_connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + + m_dataDeviceManagerInterface = m_display->createDataDeviceManager(m_display); + m_dataDeviceManagerInterface->create(); + QVERIFY(m_dataDeviceManagerInterface->isValid()); + + QVERIFY(dataDeviceManagerSpy.wait()); + m_dataDeviceManager = registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), + dataDeviceManagerSpy.first().last().value(), this); +} + +void TestDataSource::cleanup() +{ + if (m_dataDeviceManager) { + delete m_dataDeviceManager; + m_dataDeviceManager = nullptr; + } + if (m_queue) { + delete m_queue; + m_queue = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + delete m_connection; + m_connection = nullptr; + + delete m_display; + m_display = nullptr; +} + +void TestDataSource::testOffer() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + + QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(DataSourceInterface*))); + QVERIFY(dataSourceCreatedSpy.isValid()); + + QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); + QVERIFY(dataSource->isValid()); + + QVERIFY(dataSourceCreatedSpy.wait()); + QCOMPARE(dataSourceCreatedSpy.count(), 1); + + QPointer serverDataSource = dataSourceCreatedSpy.first().first().value(); + QVERIFY(!serverDataSource.isNull()); + QCOMPARE(serverDataSource->mimeTypes().count(), 0); + + QSignalSpy offeredSpy(serverDataSource.data(), SIGNAL(mimeTypeOffered(const QString&))); + QVERIFY(offeredSpy.isValid()); + + const QString plain = QStringLiteral("text/plain"); + QMimeDatabase db; + dataSource->offer(db.mimeTypeForName(plain)); + + QVERIFY(offeredSpy.wait()); + QCOMPARE(offeredSpy.count(), 1); + QCOMPARE(offeredSpy.last().first().toString(), plain); + QCOMPARE(serverDataSource->mimeTypes().count(), 1); + QCOMPARE(serverDataSource->mimeTypes().first(), plain); + + const QString html = QStringLiteral("text/html"); + dataSource->offer(db.mimeTypeForName(html)); + + QVERIFY(offeredSpy.wait()); + QCOMPARE(offeredSpy.count(), 2); + QCOMPARE(offeredSpy.first().first().toString(), plain); + QCOMPARE(offeredSpy.last().first().toString(), html); + QCOMPARE(serverDataSource->mimeTypes().count(), 2); + QCOMPARE(serverDataSource->mimeTypes().first(), plain); + QCOMPARE(serverDataSource->mimeTypes().last(), html); + + // try destroying the client side, should trigger a destroy of server side + dataSource.reset(); + QVERIFY(!serverDataSource.isNull()); + wl_display_flush(m_connection->display()); + // after running the event loop the Wayland event should be delivered, but it uses delete later + QCoreApplication::processEvents(); + QVERIFY(!serverDataSource.isNull()); + // so once more event loop + QCoreApplication::processEvents(); + QVERIFY(serverDataSource.isNull()); +} + +void TestDataSource::testTargetAccepts_data() +{ + QTest::addColumn("mimeType"); + + QTest::newRow("empty") << QString(); + QTest::newRow("text/plain") << QStringLiteral("text/plain"); +} + +void TestDataSource::testTargetAccepts() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + + QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(DataSourceInterface*))); + QVERIFY(dataSourceCreatedSpy.isValid()); + + QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); + QVERIFY(dataSource->isValid()); + + QSignalSpy targetAcceptsSpy(dataSource.data(), SIGNAL(targetAccepts(QString))); + QVERIFY(targetAcceptsSpy.isValid()); + + QVERIFY(dataSourceCreatedSpy.wait()); + QCOMPARE(dataSourceCreatedSpy.count(), 1); + + QFETCH(QString, mimeType); + dataSourceCreatedSpy.first().first().value()->accept(mimeType); + + QVERIFY(targetAcceptsSpy.wait()); + QCOMPARE(targetAcceptsSpy.count(), 1); + QCOMPARE(targetAcceptsSpy.first().first().toString(), mimeType); +} + +void TestDataSource::testRequestSend() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + + QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(DataSourceInterface*))); + QVERIFY(dataSourceCreatedSpy.isValid()); + + QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); + QVERIFY(dataSource->isValid()); + + QSignalSpy sendRequestedSpy(dataSource.data(), SIGNAL(sendDataRequested(QString,qint32))); + QVERIFY(sendRequestedSpy.isValid()); + + const QString plain = QStringLiteral("text/plain"); + QVERIFY(dataSourceCreatedSpy.wait()); + QCOMPARE(dataSourceCreatedSpy.count(), 1); + QTemporaryFile file; + QVERIFY(file.open()); + dataSourceCreatedSpy.first().first().value()->requestData(plain, file.handle()); + + QVERIFY(sendRequestedSpy.wait()); + QCOMPARE(sendRequestedSpy.count(), 1); + QCOMPARE(sendRequestedSpy.first().first().toString(), plain); + QVERIFY(sendRequestedSpy.first().last().value() != file.handle()); + QVERIFY(sendRequestedSpy.first().last().value() != -1); + + QFile writeFile; + QVERIFY(writeFile.open(sendRequestedSpy.first().last().value(), QFile::WriteOnly, QFileDevice::AutoCloseHandle)); + writeFile.close(); +} + +void TestDataSource::testCancel() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + + QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(DataSourceInterface*))); + QVERIFY(dataSourceCreatedSpy.isValid()); + + QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); + QVERIFY(dataSource->isValid()); + QSignalSpy cancelledSpy(dataSource.data(), SIGNAL(cancelled())); + QVERIFY(cancelledSpy.isValid()); + + QVERIFY(dataSourceCreatedSpy.wait()); + + QCOMPARE(cancelledSpy.count(), 0); + dataSourceCreatedSpy.first().first().value()->cancel(); + + QVERIFY(cancelledSpy.wait()); + QCOMPARE(cancelledSpy.count(), 1); +} + +void TestDataSource::testDestroy() +{ + using namespace KWayland::Client; + + QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); + QVERIFY(dataSource->isValid()); + + connect(m_connection, &ConnectionThread::connectionDied, m_dataDeviceManager, &DataDeviceManager::destroy); + connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); + connect(m_connection, &ConnectionThread::connectionDied, dataSource.data(), &DataSource::destroy); + + QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); + QVERIFY(connectionDiedSpy.isValid()); + delete m_display; + m_display = nullptr; + QVERIFY(connectionDiedSpy.wait()); + + // now the pool should be destroyed; + QVERIFY(!dataSource->isValid()); + + // calling destroy again should not fail + dataSource->destroy(); +} + +QTEST_GUILESS_MAIN(TestDataSource) +#include "test_datasource.moc" diff --git a/src/wayland/autotests/client/test_wayland_registry.cpp b/src/wayland/autotests/client/test_wayland_registry.cpp index 1bc07b82a1..d3f6863f41 100644 --- a/src/wayland/autotests/client/test_wayland_registry.cpp +++ b/src/wayland/autotests/client/test_wayland_registry.cpp @@ -24,6 +24,7 @@ License along with this library. If not, see . #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/server/compositor_interface.h" +#include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/display.h" #include "../../src/server/output_interface.h" #include "../../src/server/seat_interface.h" @@ -48,6 +49,7 @@ private Q_SLOTS: void testBindShm(); void testBindSeat(); void testBindSubCompositor(); + void testBindDataDeviceManager(); void testGlobalSync(); void testGlobalSyncThreaded(); void testRemoval(); @@ -60,6 +62,7 @@ private: KWayland::Server::SeatInterface *m_seat; KWayland::Server::ShellInterface *m_shell; KWayland::Server::SubCompositorInterface *m_subcompositor; + KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManager; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0"); @@ -72,6 +75,7 @@ TestWaylandRegistry::TestWaylandRegistry(QObject *parent) , m_seat(nullptr) , m_shell(nullptr) , m_subcompositor(nullptr) + , m_dataDeviceManager(nullptr) { } @@ -91,6 +95,8 @@ void TestWaylandRegistry::init() m_shell->create(); m_subcompositor = m_display->createSubCompositor(); m_subcompositor->create(); + m_dataDeviceManager = m_display->createDataDeviceManager(); + m_dataDeviceManager->create(); } void TestWaylandRegistry::cleanup() @@ -179,6 +185,11 @@ void TestWaylandRegistry::testBindSubCompositor() TEST_BIND(KWayland::Client::Registry::Interface::SubCompositor, SIGNAL(subCompositorAnnounced(quint32,quint32)), bindSubCompositor, wl_subcompositor_destroy) } +void TestWaylandRegistry::testBindDataDeviceManager() +{ + TEST_BIND(KWayland::Client::Registry::Interface::DataDeviceManager, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32)), bindDataDeviceManager, wl_data_device_manager_destroy) +} + #undef TEST_BIND void TestWaylandRegistry::testRemoval() diff --git a/src/wayland/datadevicemanager_interface.cpp b/src/wayland/datadevicemanager_interface.cpp new file mode 100644 index 0000000000..0328138fc1 --- /dev/null +++ b/src/wayland/datadevicemanager_interface.cpp @@ -0,0 +1,155 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +#include "datadevicemanager_interface.h" +#include "display.h" +// Wayland +#include + +namespace KWayland +{ +namespace Server +{ + +static const quint32 s_version = 1; + +class DataDeviceManagerInterface::Private +{ +public: + Private(DataDeviceManagerInterface *q, Display *d); + void create(); + + Display *display; + wl_global *manager = nullptr; + +private: + void bind(wl_client *client, uint32_t version, uint32_t id); + void createDataSource(wl_client *client, wl_resource *resource, uint32_t id); + + static void bind(wl_client *client, void *data, uint32_t version, uint32_t id); + static void unbind(wl_resource *resource); + static void createDataSourceCallback(wl_client *client, wl_resource *resource, uint32_t id); + static void getDataDeviceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + DataDeviceManagerInterface *q; + static const struct wl_data_device_manager_interface s_interface; +}; + +const struct wl_data_device_manager_interface DataDeviceManagerInterface::Private::s_interface = { + createDataSourceCallback, + getDataDeviceCallback +}; + +DataDeviceManagerInterface::Private::Private(DataDeviceManagerInterface *q, Display *d) + : display(d) + , q(q) +{ +} + +void DataDeviceManagerInterface::Private::bind(wl_client *client, void *data, uint32_t version, uint32_t id) +{ + auto m = reinterpret_cast(data); + m->bind(client, version, id); +} + +void DataDeviceManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + wl_resource *resource = wl_resource_create(client, &wl_data_device_manager_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + // TODO: should we track? +} + +void DataDeviceManagerInterface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) +} + +void DataDeviceManagerInterface::Private::createDataSourceCallback(wl_client *client, wl_resource *resource, uint32_t id) +{ + cast(resource)->createDataSource(client, resource, id); +} + +void DataDeviceManagerInterface::Private::createDataSource(wl_client *client, wl_resource *resource, uint32_t id) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + Q_UNUSED(id) + DataSourceInterface *dataSource = new DataSourceInterface(q); + dataSource->create(client, wl_resource_get_version(resource), id); + if (!dataSource->resource()) { + wl_resource_post_no_memory(resource); + delete dataSource; + return; + } + emit q->dataSourceCreated(dataSource); +} + +void DataDeviceManagerInterface::Private::getDataDeviceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + Q_UNUSED(id) + Q_UNUSED(seat) +} + +void DataDeviceManagerInterface::Private::create() +{ + Q_ASSERT(!manager); + manager = wl_global_create(*display, &wl_data_device_manager_interface, s_version, this, bind); +} + +DataDeviceManagerInterface::DataDeviceManagerInterface(Display *display, QObject *parent) + : QObject(parent) + , d(new Private(this, display)) +{ +} + +DataDeviceManagerInterface::~DataDeviceManagerInterface() +{ + destroy(); +} + +void DataDeviceManagerInterface::create() +{ + d->create(); +} + +void DataDeviceManagerInterface::destroy() +{ + if (!d->manager) { + return; + } + wl_global_destroy(d->manager); + d->manager = nullptr; +} + +bool DataDeviceManagerInterface::isValid() const +{ + return d->manager != nullptr; +} + +} +} diff --git a/src/wayland/datadevicemanager_interface.h b/src/wayland/datadevicemanager_interface.h new file mode 100644 index 0000000000..95246af4b8 --- /dev/null +++ b/src/wayland/datadevicemanager_interface.h @@ -0,0 +1,59 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +#ifndef WAYLAND_SERVER_DATA_DEVICE_MANAGER_INTERFACE_H +#define WAYLAND_SERVER_DATA_DEVICE_MANAGER_INTERFACE_H + +#include + +#include +#include "datasource_interface.h" + +namespace KWayland +{ +namespace Server +{ + +class Display; +class DataSourceInterface; + +class KWAYLANDSERVER_EXPORT DataDeviceManagerInterface : public QObject +{ + Q_OBJECT +public: + virtual ~DataDeviceManagerInterface(); + + void create(); + void destroy(); + bool isValid() const; + +Q_SIGNALS: + void dataSourceCreated(DataSourceInterface*); + +private: + explicit DataDeviceManagerInterface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; + QScopedPointer d; +}; + +} +} + +#endif diff --git a/src/wayland/datasource_interface.cpp b/src/wayland/datasource_interface.cpp new file mode 100644 index 0000000000..4945d0e4b8 --- /dev/null +++ b/src/wayland/datasource_interface.cpp @@ -0,0 +1,150 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +#include "datasource_interface.h" +// Qt +#include +// Wayland +#include + +namespace KWayland +{ +namespace Server +{ + +class DataSourceInterface::Private +{ +public: + Private(DataSourceInterface *q); + ~Private(); + void create(wl_client *client, quint32 version, quint32 id); + + wl_resource *dataSource = nullptr; + QStringList mimeTypes; + +private: + void offer(const QString &mimeType); + + static void offerCallback(wl_client *client, wl_resource *resource, const char *mimeType); + static void destroyCallack(wl_client *client, wl_resource *resource); + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + const static struct wl_data_source_interface s_interface; + DataSourceInterface *q; +}; + +const struct wl_data_source_interface DataSourceInterface::Private::s_interface = { + offerCallback, + destroyCallack +}; + +DataSourceInterface::Private::Private(DataSourceInterface *q) + : q(q) +{ +} + +DataSourceInterface::Private::~Private() +{ + if (dataSource) { + wl_resource_destroy(dataSource); + } +} + +void DataSourceInterface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + auto s = cast(resource); + s->dataSource = nullptr; + s->q->deleteLater(); +} + +void DataSourceInterface::Private::destroyCallack(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + cast(resource)->q->deleteLater(); +} + +void DataSourceInterface::Private::offerCallback(wl_client *client, wl_resource *resource, const char *mimeType) +{ + Q_UNUSED(client) + cast(resource)->offer(QString::fromUtf8(mimeType)); +} + +void DataSourceInterface::Private::offer(const QString &mimeType) +{ + mimeTypes << mimeType; + emit q->mimeTypeOffered(mimeType); +} + +void DataSourceInterface::Private::create(wl_client *client, quint32 version, quint32 id) +{ + Q_ASSERT(!dataSource); + dataSource = wl_resource_create(client, &wl_data_source_interface, version, id); + if (!dataSource) { + return; + } + wl_resource_set_implementation(dataSource, &s_interface, this, unbind); +} + +DataSourceInterface::DataSourceInterface(DataDeviceManagerInterface *parent) + : QObject(/*parent*/) + , d(new Private(this)) +{ + Q_UNUSED(parent) +} + +DataSourceInterface::~DataSourceInterface() = default; + +void DataSourceInterface::create(wl_client *client, quint32 version, quint32 id) +{ + d->create(client, version, id); +} + +void DataSourceInterface::accept(const QString &mimeType) +{ + // TODO: does this require a sanity check on the possible mimeType? + wl_data_source_send_target(d->dataSource, mimeType.isEmpty() ? nullptr : mimeType.toUtf8().constData()); +} + +void DataSourceInterface::requestData(const QString &mimeType, qint32 fd) +{ + // TODO: does this require a sanity check on the possible mimeType? + wl_data_source_send_send(d->dataSource, mimeType.toUtf8().constData(), int32_t(fd)); +} + +void DataSourceInterface::cancel() +{ + wl_data_source_send_cancelled(d->dataSource); +} + +wl_resource *DataSourceInterface::resource() const +{ + return d->dataSource; +} + +QStringList DataSourceInterface::mimeTypes() const +{ + return d->mimeTypes; +} + +} +} \ No newline at end of file diff --git a/src/wayland/datasource_interface.h b/src/wayland/datasource_interface.h new file mode 100644 index 0000000000..295beb9b16 --- /dev/null +++ b/src/wayland/datasource_interface.h @@ -0,0 +1,68 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +#ifndef WAYLAND_SERVER_DATA_SOURCE_INTERFACE_H +#define WAYLAND_SERVER_DATA_SOURCE_INTERFACE_H + +#include + +#include + +struct wl_client; +struct wl_resource; + +namespace KWayland +{ +namespace Server +{ +class DataDeviceManagerInterface; + +class KWAYLANDSERVER_EXPORT DataSourceInterface : public QObject +{ + Q_OBJECT +public: + virtual ~DataSourceInterface(); + + void create(wl_client *client, quint32 version, quint32 id); + + void accept(const QString &mimeType); + void requestData(const QString &mimeType, qint32 fd); + void cancel(); + + QStringList mimeTypes() const; + + wl_resource *resource() const; + +Q_SIGNALS: + void mimeTypeOffered(const QString&); + +private: + friend class DataDeviceManagerInterface; + explicit DataSourceInterface(DataDeviceManagerInterface *parent); + + class Private; + QScopedPointer d; +}; + +} +} + +Q_DECLARE_METATYPE(KWayland::Server::DataSourceInterface*) + +#endif diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp index fe71df50b9..6332a562d7 100644 --- a/src/wayland/display.cpp +++ b/src/wayland/display.cpp @@ -19,6 +19,7 @@ License along with this library. If not, see . *********************************************************************/ #include "display.h" #include "compositor_interface.h" +#include "datadevicemanager_interface.h" #include "output_interface.h" #include "seat_interface.h" #include "shell_interface.h" @@ -172,6 +173,13 @@ SubCompositorInterface *Display::createSubCompositor(QObject *parent) return c; } +DataDeviceManagerInterface *Display::createDataDeviceManager(QObject *parent) +{ + auto m = new DataDeviceManagerInterface(this, parent); + connect(this, &Display::aboutToTerminate, m, [this,m] { delete m; }); + return m; +} + void Display::createShm() { Q_ASSERT(d->running); diff --git a/src/wayland/display.h b/src/wayland/display.h index 553ed14c22..7873ed92d1 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -34,6 +34,7 @@ namespace Server { class CompositorInterface; +class DataDeviceManagerInterface; class OutputInterface; class SeatInterface; class ShellInterface; @@ -70,6 +71,7 @@ public: ShellInterface *createShell(QObject *parent = nullptr); SeatInterface *createSeat(QObject *parent = nullptr); SubCompositorInterface *createSubCompositor(QObject *parent = nullptr); + DataDeviceManagerInterface *createDataDeviceManager(QObject *parent = nullptr); Q_SIGNALS: void socketNameChanged(const QString&);