Index: autotests/server/CMakeLists.txt =================================================================== --- autotests/server/CMakeLists.txt +++ autotests/server/CMakeLists.txt @@ -57,3 +57,15 @@ target_link_libraries( testTabletInterface Qt5::Test Plasma::KWaylandServer KF5::WaylandClient Wayland::Client) add_test(NAME kwayland-testTabletInterface COMMAND testTabletInterface) ecm_mark_as_test(testTabletInterface) + +######################################################## +# Test DataControlInterface +######################################################## +ecm_add_qtwayland_client_protocol(DATACONTROL_SRCS + PROTOCOL ${PROJECT_SOURCE_DIR}/src/protocols/wlr-data-control-unstable-v1.xml + BASENAME wlr-data-control-unstable-v1 +) +add_executable(testDataControlInterface test_datacontrol_interface.cpp ${DATACONTROL_SRCS}) +target_link_libraries( testDataControlInterface Qt5::Test Plasma::KWaylandServer KF5::WaylandClient Wayland::Client) +add_test(NAME kwayland-testDataControlInterface COMMAND testDataControlInterface) +ecm_mark_as_test(testDataControlInterface) Index: autotests/server/test_datacontrol_interface.cpp =================================================================== --- /dev/null +++ autotests/server/test_datacontrol_interface.cpp @@ -0,0 +1,274 @@ +/******************************************************************** +Copyright 2020 David Edmundson + +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 +#include + +// WaylandServer +#include "../../src/server/compositor_interface.h" +#include "../../src/server/display.h" +#include "../../src/server/seat_interface.h" +#include "../../src/server/datacontroldevice_interface.h" +#include "../../src/server/datacontroldevicemanager_interface.h" +#include "../../src/server/datacontrolsource_interface.h" + +#include +#include +#include +#include +#include + +#include "qwayland-wlr-data-control-unstable-v1.h" + +using namespace KWaylandServer; + +// Faux-client API for tests + +Q_DECLARE_OPAQUE_POINTER(::zwlr_data_control_offer_v1*); +Q_DECLARE_METATYPE(::zwlr_data_control_offer_v1*); + +class DataControlDeviceManager : public QObject, public QtWayland::zwlr_data_control_manager_v1 +{ + Q_OBJECT +}; + +class DataControlOffer: public QObject, public QtWayland::zwlr_data_control_offer_v1 +{ + Q_OBJECT +public: + QStringList receivedOffers() { + return m_receivedOffers; + } +protected: + virtual void zwlr_data_control_offer_v1_offer(const QString &mime_type) override { + m_receivedOffers << mime_type; + } +private: + QStringList m_receivedOffers; +}; + +class DataControlDevice : public QObject, public QtWayland::zwlr_data_control_device_v1 +{ + Q_OBJECT +Q_SIGNALS: + void dataControlOffer(DataControlOffer *offer); //our event receives a new ID, so we make a new object + void selection(struct ::zwlr_data_control_offer_v1 *id); +protected: + void zwlr_data_control_device_v1_data_offer(struct ::zwlr_data_control_offer_v1 *id) override { + auto offer = new DataControlOffer; + offer->init(id); + Q_EMIT dataControlOffer(offer); + } + + void zwlr_data_control_device_v1_selection(struct ::zwlr_data_control_offer_v1 *id) override { + Q_EMIT selection(id); + } +}; + +class DataControlSource: public QObject, public QtWayland::zwlr_data_control_source_v1 +{ + Q_OBJECT +}; + + +class TestDataSource : public AbstractDataSource +{ + Q_OBJECT +public: + TestDataSource() : + AbstractDataSource(nullptr) + {} + void requestData(const QString &mimeType, qint32 fd) { + Q_UNUSED(mimeType); + Q_UNUSED(fd); + }; + void cancel() {}; + QStringList mimeTypes() const { + return {"text/test1", "text/test2"}; + } +}; + + +// The test itself + +class DataControlInterfaceTest : public QObject +{ + Q_OBJECT +public: + DataControlInterfaceTest() + { + } + ~DataControlInterfaceTest() override; + +private Q_SLOTS: + void initTestCase(); + void testCopyToControl(); + void testCopyFromControl(); + +private: + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::EventQueue *m_queue; + KWayland::Client::Compositor *m_clientCompositor; + KWayland::Client::Seat *m_clientSeat = nullptr; + + QThread *m_thread; + Display m_display; + SeatInterface *m_seat; + CompositorInterface *m_serverCompositor; + + DataControlDeviceManagerInterface *m_dataControlDeviceManagerInterface; + + DataControlDeviceManager *m_dataControlDeviceManager; + + QVector m_surfaces; +}; + +DataControlInterfaceTest::~DataControlInterfaceTest() +{ + 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_seat; + m_connection->deleteLater(); + m_connection = nullptr; +} + + +static const QString s_socketName = QStringLiteral("kwin-wayland-server-tablet-test-0"); + +void DataControlInterfaceTest::initTestCase() +{ + m_display.setSocketName(s_socketName); + m_display.start(); + QVERIFY(m_display.isRunning()); + + m_seat = m_display.createSeat(this); + m_seat->create(); + m_serverCompositor = m_display.createCompositor(this); + m_serverCompositor->create(); + m_dataControlDeviceManagerInterface = m_display.createDataControlDeviceManager(this); + m_dataControlDeviceManagerInterface->create(); + QVERIFY(m_serverCompositor->isValid()); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::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()); + QVERIFY(!m_connection->connections().isEmpty()); + + m_queue = new KWayland::Client::EventQueue(this); + QVERIFY(!m_queue->isValid()); + m_queue->setup(m_connection); + QVERIFY(m_queue->isValid()); + + auto registry = new KWayland::Client::Registry(this); + connect(registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, registry](const QByteArray &interface, quint32 name, quint32 version) { + if (interface == "zwlr_data_control_manager_v1") { + m_dataControlDeviceManager = new DataControlDeviceManager; + m_dataControlDeviceManager->init(registry->registry(), name, version); + m_dataControlDeviceManager->setParent(registry); + } + }); + connect(registry, &KWayland::Client::Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { + m_clientSeat = registry->createSeat(name, version); + }); + registry->setEventQueue(m_queue); + QSignalSpy compositorSpy(registry, &KWayland::Client::Registry::compositorAnnounced); + registry->create(m_connection->display()); + QVERIFY(registry->isValid()); + registry->setup(); + wl_display_flush(m_connection->display()); + + QVERIFY(compositorSpy.wait()); + m_clientCompositor = registry->createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); + QVERIFY(m_clientCompositor->isValid()); + + QVERIFY(m_dataControlDeviceManager); +} + + +void DataControlInterfaceTest::testCopyToControl() +{ + // we set a dummy data source on the seat using abstract client directly + // then confirm we receive the offer despite not having a surface + + QScopedPointer dataControlDevice(new DataControlDevice); + dataControlDevice->init(m_dataControlDeviceManager->get_data_device(*m_clientSeat)); + + QSignalSpy newOfferSpy(dataControlDevice.data(), &DataControlDevice::dataControlOffer); + QSignalSpy selectionSpy(dataControlDevice.data(), &DataControlDevice::selection); + + auto testSelection = new TestDataSource; + m_seat->setSelection(testSelection); + + // selection will be sent after we've been sent a new offer object and the mimes have been sent to that object + selectionSpy.wait(); + + QCOMPARE(newOfferSpy.count(), 1); + QScopedPointer offer(newOfferSpy.first().first().value()); + QCOMPARE(selectionSpy.first().first().value(), offer->object()); + + QCOMPARE(offer->receivedOffers().count(), 2); + QCOMPARE(offer->receivedOffers()[0], "text/test1"); + QCOMPARE(offer->receivedOffers()[1], "text/test2"); +} + +void DataControlInterfaceTest::testCopyFromControl() +{ + // we create a data device and set a selection + // then confirm the server sees the new selection + QSignalSpy serverSelectionChangedSpy(m_seat, &SeatInterface::selectionChanged); + + QScopedPointer dataControlDevice(new DataControlDevice); + dataControlDevice->init(m_dataControlDeviceManager->get_data_device(*m_clientSeat)); + + QScopedPointer source(new DataControlSource); + source->init(m_dataControlDeviceManager->create_data_source()); + source->offer("cheese/test1"); + source->offer("cheese/test2"); + + dataControlDevice->set_selection(source->object()); + + serverSelectionChangedSpy.wait(); + QVERIFY(m_seat->selection()); + QCOMPARE(m_seat->selection()->mimeTypes(), QStringList({"cheese/test1", "cheese/test2"})); + source->destroy(); +} + + +QTEST_GUILESS_MAIN(DataControlInterfaceTest) + +#include "test_datacontrol_interface.moc" Index: src/protocols/README.md =================================================================== --- /dev/null +++ src/protocols/README.md @@ -0,0 +1 @@ +Folder for storing external protocols that aren't in released wayland-protocols or Plasma specific. Index: src/protocols/wlr-data-control-unstable-v1.xml =================================================================== --- /dev/null +++ src/protocols/wlr-data-control-unstable-v1.xml @@ -0,0 +1,278 @@ + + + + Copyright © 2018 Simon Ser + Copyright © 2019 Ivan Molodetskikh + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol allows a privileged client to control data devices. In + particular, the client will be able to manage the current selection and take + the role of a clipboard manager. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows creating per-seat data device + controls. + + + + + Create a new data source. + + + + + + + Create a data device that can be used to manage a seat's selection. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This interface allows a client to manage a seat's selection. + + When the seat is destroyed, this object becomes inert. + + + + + This request asks the compositor to set the selection to the data from + the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the selection, set the source to NULL. + + + + + + + Destroys the data device object. + + + + + + The data_offer event introduces a new wlr_data_control_offer object, + which will subsequently be used in either the + wlr_data_control_device.selection event (for the regular clipboard + selections) or the wlr_data_control_device.primary_selection event (for + the primary clipboard selections). Immediately following the + wlr_data_control_device.data_offer event, the new data_offer object + will send out wlr_data_control_offer.offer events to describe the MIME + types it offers. + + + + + + + The selection event is sent out to notify the client of a new + wlr_data_control_offer for the selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The selection event is sent to a client when a new + selection is set. The wlr_data_control_offer is valid until a new + wlr_data_control_offer or NULL is received. The client must destroy the + previous selection wlr_data_control_offer, if any, upon receiving this + event. + + The first selection event is sent upon binding the + wlr_data_control_device object. + + + + + + + This data control object is no longer valid and should be destroyed by + the client. + + + + + + + + The primary_selection event is sent out to notify the client of a new + wlr_data_control_offer for the primary selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The primary_selection event is sent to a client when a + new primary selection is set. The wlr_data_control_offer is valid until + a new wlr_data_control_offer or NULL is received. The client must + destroy the previous primary selection wlr_data_control_offer, if any, + upon receiving this event. + + If the compositor supports primary selection, the first + primary_selection event is sent upon binding the + wlr_data_control_device object. + + + + + + + This request asks the compositor to set the primary selection to the + data from the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the primary selection, set the source to NULL. + + The compositor will ignore this request if it does not support primary + selection. + + + + + + + + + + + + The wlr_data_control_source object is the source side of a + wlr_data_control_offer. It is created by the source client in a data + transfer and provides a way to describe the offered data and a way to + respond to requests to transfer the data. + + + + + + + + + This request adds a MIME type to the set of MIME types advertised to + targets. Can be called several times to offer multiple types. + + Calling this after wlr_data_control_device.set_selection is a protocol + error. + + + + + + + Destroys the data source object. + + + + + + Request for data from the client. Send the data as the specified MIME + type over the passed file descriptor, then close it. + + + + + + + + This data source is no longer valid. The data source has been replaced + by another data source. + + The client should clean up and destroy this data source. + + + + + + + A wlr_data_control_offer represents a piece of data offered for transfer + by another client (the source client). The offer describes the different + MIME types that the data can be converted to and provides the mechanism + for transferring the data directly from the source client. + + + + + To transfer the offered data, the client issues this request and + indicates the MIME type it wants to receive. The transfer happens + through the passed file descriptor (typically created with the pipe + system call). The source client writes the data in the MIME type + representation requested and then closes the file descriptor. + + The receiving client reads from the read end of the pipe until EOF and + then closes its end, at which point the transfer is complete. + + This request may happen multiple times for different MIME types. + + + + + + + + Destroys the data offer object. + + + + + + Sent immediately after creating the wlr_data_control_offer object. + One event per offered MIME type. + + + + + Index: src/server/CMakeLists.txt =================================================================== --- src/server/CMakeLists.txt +++ src/server/CMakeLists.txt @@ -7,6 +7,10 @@ clientconnection.cpp compositor_interface.cpp contrast_interface.cpp + datacontroldevice_interface.cpp + datacontroldevicemanager_interface.cpp + datacontroloffer_interface.cpp + datacontrolsource_interface.cpp datadevice_interface.cpp datadevicemanager_interface.cpp dataoffer_interface.cpp @@ -235,6 +239,11 @@ BASENAME tablet-unstable-v2 ) +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${PROJECT_SOURCE_DIR}/src/protocols/wlr-data-control-unstable-v1.xml + BASENAME data-control-v1 +) + set(SERVER_GENERATED_SRCS ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h @@ -339,6 +348,10 @@ clientconnection.h compositor_interface.h contrast_interface.h + datacontroldevice_interface.h + datacontroldevicemanager_interface.h + datacontroloffer_interface.h + datacontrolsource_interface.h datadevice_interface.h datadevicemanager_interface.h dataoffer_interface.h Index: src/server/datacontroldevice_interface.h =================================================================== --- /dev/null +++ src/server/datacontroldevice_interface.h @@ -0,0 +1,75 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2019 David Edmundson + +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_CONTROL_DEVICE_INTERFACE_H +#define WAYLAND_SERVER_DATA_CONTROL_DEVICE_INTERFACE_H + +#include + +#include + +#include "resource.h" + +namespace KWaylandServer +{ + +class AbstractDataSource; +class DataControlDeviceManagerInterface; +class DataControlOfferInterface; +class DataControlSourceInterface; +class SeatInterface; +class SurfaceInterface; + +/** + * @brief Represents the Resource for the wl_data_device interface. + * + * @see SeatInterface + * @see DataControlSourceInterface + **/ +class KWAYLANDSERVER_EXPORT DataControlDeviceInterface : public Resource +{ + Q_OBJECT +public: + virtual ~DataControlDeviceInterface(); + + SeatInterface *seat() const; + + DataControlSourceInterface *selection() const; + + void sendSelection(AbstractDataSource *other); + void sendClearSelection(); + +Q_SIGNALS: + void selectionChanged(KWaylandServer::DataControlSourceInterface*); + void selectionCleared(); + +private: + friend class DataControlDeviceManagerInterface; + explicit DataControlDeviceInterface(SeatInterface *seat, DataControlDeviceManagerInterface *parent, wl_resource *parentResource); + + class Private; + Private *d_func() const; +}; + +} + +Q_DECLARE_METATYPE(KWaylandServer::DataControlDeviceInterface*) + +#endif Index: src/server/datacontroldevice_interface.cpp =================================================================== --- /dev/null +++ src/server/datacontroldevice_interface.cpp @@ -0,0 +1,181 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2019 David Edmundson + +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 "datacontroldevice_interface.h" +#include "datacontroldevicemanager_interface.h" +#include "datacontroloffer_interface_p.h" +#include "datacontrolsource_interface.h" +#include "display.h" +#include "resource_p.h" +#include "pointer_interface.h" +#include "seat_interface.h" +#include "surface_interface.h" +// Wayland +#include + +namespace KWaylandServer +{ + +class DataControlDeviceInterface::Private : public Resource::Private +{ +public: + Private(SeatInterface *seat, DataControlDeviceInterface *q, DataControlDeviceManagerInterface *manager, wl_resource *parentResource); + ~Private(); + + DataControlOfferInterface *createDataOffer(AbstractDataSource *source); + + SeatInterface *seat; + DataControlSourceInterface *source = nullptr; + + DataControlSourceInterface *selection = nullptr; + QMetaObject::Connection selectionUnboundConnection; + QMetaObject::Connection selectionDestroyedConnection; + +private: + DataControlDeviceInterface *q_func() { + return reinterpret_cast(q); + } + void setSelection(DataControlSourceInterface *dataSource); + static void setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source); + + static const struct zwlr_data_control_device_v1_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwlr_data_control_device_v1_interface DataControlDeviceInterface::Private::s_interface = { + setSelectionCallback, + resourceDestroyedCallback +}; +#endif + +DataControlDeviceInterface::Private::Private(SeatInterface *seat, DataControlDeviceInterface *q, DataControlDeviceManagerInterface *manager, wl_resource *parentResource) + : Resource::Private(q, manager, parentResource, &zwlr_data_control_device_v1_interface, &s_interface) + , seat(seat) +{ +} + +DataControlDeviceInterface::Private::~Private() = default; + +void DataControlDeviceInterface::Private::setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source) +{ + Q_UNUSED(client) + cast(resource)->setSelection(DataControlSourceInterface::get(source)); +} + +void DataControlDeviceInterface::Private::setSelection(DataControlSourceInterface *dataSource) +{ + if (selection == dataSource) { + return; + } + Q_Q(DataControlDeviceInterface); + QObject::disconnect(selectionUnboundConnection); + QObject::disconnect(selectionDestroyedConnection); + if (selection) { + selection->cancel(); + } + selection = dataSource; + if (selection) { + auto clearSelection = [this] { + setSelection(nullptr); + }; + selectionUnboundConnection = QObject::connect(selection, &Resource::unbound, q, clearSelection); + selectionDestroyedConnection = QObject::connect(selection, &QObject::destroyed, q, clearSelection); + emit q->selectionChanged(selection); + } else { + selectionUnboundConnection = QMetaObject::Connection(); + selectionDestroyedConnection = QMetaObject::Connection(); + emit q->selectionCleared(); + } +} + +DataControlOfferInterface *DataControlDeviceInterface::Private::createDataOffer(AbstractDataSource *source) +{ + if (!resource) { + return nullptr; + } + if (!source) { + // a data offer can only exist together with a source + return nullptr; + } + Q_Q(DataControlDeviceInterface); + DataControlOfferInterface *offer = new DataControlOfferInterface(source, q, resource); + auto c = q->global()->display()->getConnection(wl_resource_get_client(resource)); + offer->create(c, wl_resource_get_version(resource), 0); + if (!offer->resource()) { + // TODO: send error? + delete offer; + return nullptr; + } + zwlr_data_control_device_v1_send_data_offer(resource, offer->resource()); + offer->sendAllOffers(); + return offer; +} + +DataControlDeviceInterface::DataControlDeviceInterface(SeatInterface *seat, DataControlDeviceManagerInterface *parent, wl_resource *parentResource) + : Resource(new Private(seat, this, parent, parentResource)) +{ +} + +DataControlDeviceInterface::~DataControlDeviceInterface() = default; + +SeatInterface *DataControlDeviceInterface::seat() const +{ + Q_D(); + return d->seat; +} + +DataControlSourceInterface *DataControlDeviceInterface::selection() const +{ + Q_D(); + return d->selection; +} + +void DataControlDeviceInterface::sendSelection(AbstractDataSource *other) +{ + Q_D(); + if (!other) { + sendClearSelection(); + return; + } + auto r = d->createDataOffer(other); + if (!r) { + return; + } + if (!d->resource) { + return; + } + zwlr_data_control_device_v1_send_selection(d->resource, r->resource()); +} + +void DataControlDeviceInterface::sendClearSelection() +{ + Q_D(); + if (!d->resource) { + return; + } + zwlr_data_control_device_v1_send_selection(d->resource, nullptr); +} + +DataControlDeviceInterface::Private *DataControlDeviceInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +} Index: src/server/datacontroldevicemanager_interface.h =================================================================== --- /dev/null +++ src/server/datacontroldevicemanager_interface.h @@ -0,0 +1,59 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2019 David Edmundson + +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_CONTROL_DEVICE_MANAGER_INTERFACE_H +#define WAYLAND_SERVER_DATA_CONTROL_DEVICE_MANAGER_INTERFACE_H + +#include + +#include + +#include "global.h" +#include "datacontroldevice_interface.h" + +namespace KWaylandServer +{ + +class Display; +class DataControlSourceInterface; + +/** + * @brief Represents the Global for zwlr_data_control_manager_v1 interface. + * + **/ +class KWAYLANDSERVER_EXPORT DataControlDeviceManagerInterface : public Global +{ + Q_OBJECT +public: + virtual ~DataControlDeviceManagerInterface(); + +Q_SIGNALS: + void dataSourceCreated(KWaylandServer::DataControlSourceInterface*); + void dataDeviceCreated(KWaylandServer::DataControlDeviceInterface*); + +private: + explicit DataControlDeviceManagerInterface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; +}; + +} + +#endif Index: src/server/datacontroldevicemanager_interface.cpp =================================================================== --- /dev/null +++ src/server/datacontroldevicemanager_interface.cpp @@ -0,0 +1,133 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2019 David Edmundson + +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 "datacontroldevicemanager_interface.h" +#include "datacontrolsource_interface.h" +#include "global_p.h" +#include "display.h" +#include "seat_interface_p.h" +// Wayland +#include + +namespace KWaylandServer +{ + +class DataControlDeviceManagerInterface::Private : public Global::Private +{ +public: + Private(DataControlDeviceManagerInterface *q, Display *d); + +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + void createDataSource(wl_client *client, wl_resource *resource, uint32_t id); + void getDataDevice(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat); + + 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)); + } + + DataControlDeviceManagerInterface *q; + static const struct zwlr_data_control_manager_v1_interface s_interface; + static const quint32 s_version; + static const qint32 s_dataDeviceVersion; + static const qint32 s_dataSourceVersion; +}; + +const quint32 DataControlDeviceManagerInterface::Private::s_version = 1; +const qint32 DataControlDeviceManagerInterface::Private::s_dataDeviceVersion = 1; +const qint32 DataControlDeviceManagerInterface::Private::s_dataSourceVersion = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwlr_data_control_manager_v1_interface DataControlDeviceManagerInterface::Private::s_interface = { + createDataSourceCallback, + getDataDeviceCallback +}; +#endif + +DataControlDeviceManagerInterface::Private::Private(DataControlDeviceManagerInterface *q, Display *d) + : Global::Private(d, &zwlr_data_control_manager_v1_interface, s_version) + , q(q) +{ +} + +void DataControlDeviceManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zwlr_data_control_manager_v1_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 DataControlDeviceManagerInterface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) +} + +void DataControlDeviceManagerInterface::Private::createDataSourceCallback(wl_client *client, wl_resource *resource, uint32_t id) +{ + cast(resource)->createDataSource(client, resource, id); +} + +void DataControlDeviceManagerInterface::Private::createDataSource(wl_client *client, wl_resource *resource, uint32_t id) +{ + DataControlSourceInterface *dataSource = new DataControlSourceInterface(q, resource); + dataSource->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_dataSourceVersion) , id); + if (!dataSource->resource()) { + wl_resource_post_no_memory(resource); + delete dataSource; + return; + } + emit q->dataSourceCreated(dataSource); +} + +void DataControlDeviceManagerInterface::Private::getDataDeviceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat) +{ + cast(resource)->getDataDevice(client, resource, id, seat); +} + +void DataControlDeviceManagerInterface::Private::getDataDevice(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat) +{ + SeatInterface *s = SeatInterface::get(seat); + Q_ASSERT(s); + DataControlDeviceInterface *dataDevice = new DataControlDeviceInterface(s, q, resource); + dataDevice->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_dataDeviceVersion), id); + if (!dataDevice->resource()) { + wl_resource_post_no_memory(resource); + return; + } + s->d_func()->registerDataControlDevice(dataDevice); + emit q->dataDeviceCreated(dataDevice); +} + +DataControlDeviceManagerInterface::DataControlDeviceManagerInterface(Display *display, QObject *parent) + : Global(new Private(this, display), parent) +{ +} + +DataControlDeviceManagerInterface::~DataControlDeviceManagerInterface() = default; + +} Index: src/server/datacontroloffer_interface.h =================================================================== --- /dev/null +++ src/server/datacontroloffer_interface.h @@ -0,0 +1,62 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2019 David Edmundson + +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_CONTROL_OFFER_INTERFACE_H +#define WAYLAND_SERVER_DATA_CONTROL_OFFER_INTERFACE_H + +#include + +#include + +#include "resource.h" +#include "datacontroldevicemanager_interface.h" + +namespace KWaylandServer +{ + +class AbstractDataSource; +class DataControlDeviceInterface; +class DataControlSourceInterface; + +/** + * @brief Represents the Resource for the wl_data_offer interface. + * + **/ +class KWAYLANDSERVER_EXPORT DataControlOfferInterface : public Resource +{ + Q_OBJECT +public: + virtual ~DataControlOfferInterface(); + + void sendAllOffers(); + +private: + friend class DataControlDeviceInterface; + explicit DataControlOfferInterface(AbstractDataSource *source, DataControlDeviceInterface *parentInterface, wl_resource *parentResource); + + class Private; + Private *d_func() const; +}; + +} + +Q_DECLARE_METATYPE(KWaylandServer::DataControlOfferInterface*) + +#endif Index: src/server/datacontroloffer_interface.cpp =================================================================== --- /dev/null +++ src/server/datacontroloffer_interface.cpp @@ -0,0 +1,102 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2019 David Edmundson + +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 "datacontroloffer_interface_p.h" +#include "datacontroldevice_interface.h" +#include "datacontrolsource_interface.h" +// Qt +#include +// Wayland +#include +// system +#include + +namespace KWaylandServer +{ + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwlr_data_control_offer_v1_interface DataControlOfferInterface::Private::s_interface = { + receiveCallback, + resourceDestroyedCallback, +}; +#endif + +DataControlOfferInterface::Private::Private(AbstractDataSource *source, DataControlDeviceInterface *parentInterface, DataControlOfferInterface *q, wl_resource *parentResource) + : Resource::Private(q, nullptr, parentResource, &zwlr_data_control_offer_v1_interface, &s_interface) + , source(source) + , dataDevice(parentInterface) +{ + // TODO: connect to new selections +} + +DataControlOfferInterface::Private::~Private() = default; + +void DataControlOfferInterface::Private::receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd) +{ + Q_UNUSED(client) + cast(resource)->receive(QString::fromUtf8(mimeType), fd); +} + +void DataControlOfferInterface::Private::receive(const QString &mimeType, qint32 fd) +{ + if (!source) { + close(fd); + return; + } + source->requestData(mimeType, fd); +} + +DataControlOfferInterface::DataControlOfferInterface(AbstractDataSource *source, DataControlDeviceInterface *parentInterface, wl_resource *parentResource) + : Resource(new Private(source, parentInterface, this, parentResource)) +{ + Q_ASSERT(source); + connect(source, &DataControlSourceInterface::mimeTypeOffered, this, + [this](const QString &mimeType) { + Q_D(); + if (!d->resource) { + return; + } + zwlr_data_control_offer_v1_send_offer(d->resource, mimeType.toUtf8().constData()); + } + ); + QObject::connect(source, &QObject::destroyed, this, + [this] { + Q_D(); + d->source = nullptr; + } + ); +} + +DataControlOfferInterface::~DataControlOfferInterface() = default; + +void DataControlOfferInterface::sendAllOffers() +{ + Q_D(); + for (const QString &mimeType : d->source->mimeTypes()) { + zwlr_data_control_offer_v1_send_offer(d->resource, mimeType.toUtf8().constData()); + } +} + +DataControlOfferInterface::Private *DataControlOfferInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +} Index: src/server/datacontroloffer_interface_p.h =================================================================== --- /dev/null +++ src/server/datacontroloffer_interface_p.h @@ -0,0 +1,51 @@ +/******************************************************************** +Copyright 2017 Martin Flöser +Copyright 2019 David Edmundson + +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 KWAYLAND_SERVER_DATA_CONTROL_OFFERINTERFACE_P_H +#define KWAYLAND_SERVER_DATA_CONTROL_OFFERINTERFACE_P_H +#include "datacontroloffer_interface.h" +#include "datacontrolsource_interface.h" +#include "resource_p.h" +#include + +namespace KWaylandServer +{ + +class Q_DECL_HIDDEN DataControlOfferInterface::Private : public Resource::Private +{ +public: + Private(AbstractDataSource *source, DataControlDeviceInterface *parentInterface, DataControlOfferInterface *q, wl_resource *parentResource); + ~Private(); + AbstractDataSource *source; + DataControlDeviceInterface *dataDevice; + +private: + DataControlOfferInterface *q_func() { + return reinterpret_cast(q); + } + void receive(const QString &mimeType, qint32 fd); + static void receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd); + + static const struct zwlr_data_control_offer_v1_interface s_interface; +}; + +} + +#endif Index: src/server/datacontrolsource_interface.h =================================================================== --- /dev/null +++ src/server/datacontrolsource_interface.h @@ -0,0 +1,61 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2019 David Edmundson + +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_CONTROL_SOURCE_INTERFACE_H +#define WAYLAND_SERVER_DATA_CONTROL_SOURCE_INTERFACE_H + +#include "abstract_data_source.h" + +#include + +#include "datacontroldevicemanager_interface.h" + +namespace KWaylandServer +{ + +/** + * @brief Represents the Resource for the zwlr_data_control_source_v1 interface. + **/ +class KWAYLANDSERVER_EXPORT DataControlSourceInterface : public AbstractDataSource +{ + Q_OBJECT +public: + virtual ~DataControlSourceInterface(); + + void requestData(const QString &mimeType, qint32 fd) override; + void cancel() override; + + QStringList mimeTypes() const override; + + static DataControlSourceInterface *get(wl_resource *native); + +private: + friend class DataControlDeviceManagerInterface; + explicit DataControlSourceInterface(DataControlDeviceManagerInterface *parent, wl_resource *parentResource); + + class Private; + Private *d_func() const; +}; + +} + +Q_DECLARE_METATYPE(KWaylandServer::DataControlSourceInterface*) + +#endif Index: src/server/datacontrolsource_interface.cpp =================================================================== --- /dev/null +++ src/server/datacontrolsource_interface.cpp @@ -0,0 +1,125 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin +Copyright 2019 David Edmundson + +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 "datacontrolsource_interface.h" +#include "datacontroldevicemanager_interface.h" +#include "clientconnection.h" +#include "resource_p.h" +// Qt +#include +// Wayland +#include +// system +#include + +namespace KWaylandServer +{ + +class DataControlSourceInterface::Private : public Resource::Private +{ +public: + Private(DataControlSourceInterface *q, DataControlDeviceManagerInterface *parent, wl_resource *parentResource); + ~Private(); + + QStringList mimeTypes; + +private: + DataControlSourceInterface *q_func() { + return reinterpret_cast(q); + } + void offer(const QString &mimeType); + + static void offerCallback(wl_client *client, wl_resource *resource, const char *mimeType); + static void setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions); + + const static struct zwlr_data_control_source_v1_interface s_interface; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zwlr_data_control_source_v1_interface DataControlSourceInterface::Private::s_interface = { + offerCallback, + resourceDestroyedCallback, +}; +#endif + +DataControlSourceInterface::Private::Private(DataControlSourceInterface *q, DataControlDeviceManagerInterface *parent, wl_resource *parentResource) + : Resource::Private(q, parent, parentResource, &zwlr_data_control_source_v1_interface, &s_interface) +{ +} + +DataControlSourceInterface::Private::~Private() = default; + +void DataControlSourceInterface::Private::offerCallback(wl_client *client, wl_resource *resource, const char *mimeType) +{ + Q_UNUSED(client) + cast(resource)->offer(QString::fromUtf8(mimeType)); +} + +void DataControlSourceInterface::Private::offer(const QString &mimeType) +{ + mimeTypes << mimeType; + Q_Q(DataControlSourceInterface); + emit q->mimeTypeOffered(mimeType); +} + +DataControlSourceInterface::DataControlSourceInterface(DataControlDeviceManagerInterface *parent, wl_resource *parentResource) + : AbstractDataSource(new Private(this, parent, parentResource)) +{ +} + +DataControlSourceInterface::~DataControlSourceInterface() = default; + +void DataControlSourceInterface::requestData(const QString &mimeType, qint32 fd) +{ + Q_D(); + // TODO: does this require a sanity check on the possible mimeType? + if (d->resource) { + zwlr_data_control_source_v1_send_send(d->resource, mimeType.toUtf8().constData(), int32_t(fd)); + } + close(fd); +} + +void DataControlSourceInterface::cancel() +{ + Q_D(); + if (!d->resource) { + return; + } + zwlr_data_control_source_v1_send_cancelled(d->resource); + client()->flush(); +} + +QStringList DataControlSourceInterface::mimeTypes() const +{ + Q_D(); + return d->mimeTypes; +} + +DataControlSourceInterface *DataControlSourceInterface::get(wl_resource *native) +{ + return Private::get(native); +} + +DataControlSourceInterface::Private *DataControlSourceInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +} Index: src/server/display.h =================================================================== --- src/server/display.h +++ src/server/display.h @@ -78,6 +78,7 @@ class KeyStateInterface; class LinuxDmabufUnstableV1Interface; class TabletManagerInterface; +class DataControlDeviceManagerInterface; /** * @brief Class holding the Wayland server display loop. @@ -309,6 +310,12 @@ */ EglStreamControllerInterface *createEglStreamControllerInterface(QObject *parent = nullptr); + /** + * Creates the DataControlDeviceManager + * + */ + DataControlDeviceManagerInterface *createDataControlDeviceManager(QObject *parent = nullptr); + /** * Creates the entry point to support wacom-like tablets and pens. * Index: src/server/display.cpp =================================================================== --- src/server/display.cpp +++ src/server/display.cpp @@ -45,6 +45,10 @@ #include "xdgshell_stable_interface_p.h" #include "xdgshell_v5_interface_p.h" #include "xdgshell_v6_interface_p.h" +#include "xdgdecoration_interface.h" +#include "eglstream_controller_interface.h" +#include "keystate_interface.h" +#include "datacontroldevicemanager_interface.h" #include #include @@ -529,6 +533,13 @@ return d; } +DataControlDeviceManagerInterface *Display::createDataControlDeviceManager(QObject *parent) +{ + auto m = new DataControlDeviceManagerInterface(this, parent); + connect(this, &Display::aboutToTerminate, m, [this, m] { delete m; }); + return m; +} + void Display::createShm() { Q_ASSERT(d->display); Index: src/server/seat_interface.h =================================================================== --- src/server/seat_interface.h +++ src/server/seat_interface.h @@ -768,6 +768,7 @@ private: friend class Display; + friend class DataControlDeviceManagerInterface; friend class DataDeviceManagerInterface; friend class TextInputManagerUnstableV0Interface; friend class TextInputManagerUnstableV2Interface; Index: src/server/seat_interface.cpp =================================================================== --- src/server/seat_interface.cpp +++ src/server/seat_interface.cpp @@ -9,6 +9,8 @@ #include "display.h" #include "datadevice_interface.h" #include "datasource_interface.h" +#include "datacontroldevice_interface.h" +#include "datacontrolsource_interface.h" #include "keyboard_interface.h" #include "keyboard_interface_p.h" #include "pointer_interface.h" @@ -332,6 +334,36 @@ } } +void SeatInterface::Private::registerDataControlDevice(DataControlDeviceInterface *dataDevice) +{ + Q_ASSERT(dataDevice->seat() == q); + dataControlDevices << dataDevice; + auto dataDeviceCleanup = [this, dataDevice] { + dataControlDevices.removeOne(dataDevice); + }; + QObject::connect(dataDevice, &QObject::destroyed, q, dataDeviceCleanup); + QObject::connect(dataDevice, &Resource::unbound, q, dataDeviceCleanup); + + QObject::connect(dataDevice, &DataControlDeviceInterface::selectionChanged, q, + [this, dataDevice] { + q->setSelection(dataDevice->selection()); + } + ); + + QObject::connect(dataDevice, &DataControlDeviceInterface::selectionCleared, q, + [this, dataDevice] { + // only clear if this is the owner of the current selection + if (currentSelection && currentSelection->client() == dataDevice->client()) { + q->setSelection(nullptr); + } + } + ); + if (currentSelection) { + dataDevice->sendSelection(currentSelection); + } +} + + void SeatInterface::Private::registerTextInput(TextInputInterface *ti) { // text input version 0 might call this multiple times @@ -1584,6 +1616,14 @@ } } + for (auto control: qAsConst(d->dataControlDevices)) { + if (selection) { + control->sendSelection(selection); + } else { + control->sendClearSelection(); + } + } + emit selectionChanged(selection); } Index: src/server/seat_interface_p.h =================================================================== --- src/server/seat_interface_p.h +++ src/server/seat_interface_p.h @@ -21,6 +21,8 @@ class AbstractDataSource; class DataDeviceInterface; +class DataSourceInterface; +class DataControlDeviceInterface; class TextInputInterface; class SeatInterface::Private : public Global::Private @@ -36,6 +38,7 @@ DataDeviceInterface *dataDeviceForSurface(SurfaceInterface *surface) const; TextInputInterface *textInputForSurface(SurfaceInterface *surface) const; void registerDataDevice(DataDeviceInterface *dataDevice); + void registerDataControlDevice(DataControlDeviceInterface *dataDevice); void registerTextInput(TextInputInterface *textInput); void endDrag(quint32 serial); @@ -49,6 +52,7 @@ QVector keyboards; QVector touchs; QVector dataDevices; + QVector dataControlDevices; QVector textInputs;