Port DataDevice to the new inheritance approach

This was done mostly because I wanted to get rid of the Resource
dependency in AbstractDataSource so I can make our xwl bridge direct,
but this also fixes up some issues with object lifespan present in the
previous version and keeps all our clipboard code in-line.
This commit is contained in:
David Edmundson 2020-06-25 16:27:52 +01:00
parent 85d6888504
commit 096deea9ec
24 changed files with 345 additions and 499 deletions

View file

@ -8,6 +8,6 @@
using namespace KWaylandServer;
AbstractDataSource::AbstractDataSource(Resource::Private *d, QObject *parent)
: Resource(d, parent)
AbstractDataSource::AbstractDataSource(QObject *parent)
: QObject(parent)
{}

View file

@ -27,10 +27,7 @@ namespace KWaylandServer {
// Anything related to selections are pure virtual, content relating
// to drag and drop has a default implementation
// TODO ideally this shouldn't inherit from resource as it provides some misleading public methods
// This can be resolved once DataSource is ported to the new system
class KWAYLANDSERVER_EXPORT AbstractDataSource : public Resource
class KWAYLANDSERVER_EXPORT AbstractDataSource : public QObject
{
Q_OBJECT
public:
@ -64,16 +61,18 @@ public:
Q_UNUSED(action);
};
virtual wl_client* client() {
return Resource::client()->client();
}
virtual wl_client* client() const {
return nullptr;
};
Q_SIGNALS:
void aboutToBeDestroyed();
void mimeTypeOffered(const QString&);
void supportedDragAndDropActionsChanged();
protected:
explicit AbstractDataSource(Resource::Private *d, QObject *parent = nullptr);
explicit AbstractDataSource(QObject *parent = nullptr);
};
}

View file

@ -27,6 +27,8 @@
// Wayland
#include <wayland-client.h>
#include <unistd.h>
class TestDataDevice : public QObject
{
Q_OBJECT
@ -101,8 +103,6 @@ void TestDataDevice::init()
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<quint32>(),
@ -179,8 +179,6 @@ void TestDataDevice::testCreate()
QVERIFY(!deviceInterface->origin());
QVERIFY(!deviceInterface->icon());
QVERIFY(!deviceInterface->selection());
QVERIFY(deviceInterface->parentResource());
// this will probably fail, we need to make a selection client side
QVERIFY(!m_seatInterface->selection());
@ -433,12 +431,10 @@ void TestDataDevice::testSetSelection()
dataDevice->setSelection(2, dataSource.data());
QVERIFY(selectionChangedSpy.wait());
// now unbind the dataDevice
QSignalSpy unboundSpy(deviceInterface, &DataDeviceInterface::unbound);
QSignalSpy unboundSpy(deviceInterface, &QObject::destroyed);
QVERIFY(unboundSpy.isValid());
dataDevice.reset();
QVERIFY(unboundSpy.wait());
// send a selection to the unbound data device
deviceInterface->sendSelection(deviceInterface->selection());
}
void TestDataDevice::testSendSelectionOnSeat()
@ -491,7 +487,7 @@ void TestDataDevice::testSendSelectionOnSeat()
// now let's try to destroy the data device and set a focused keyboard just while the data device is being destroyedd
m_seatInterface->setFocusedKeyboardSurface(nullptr);
QSignalSpy unboundSpy(serverDataDevice, &Resource::unbound);
QSignalSpy unboundSpy(serverDataDevice, &QObject::destroyed);
QVERIFY(unboundSpy.isValid());
dataDevice.reset();
QVERIFY(unboundSpy.wait());
@ -575,6 +571,21 @@ void TestDataDevice::testReplaceSource()
dataSource3.reset();
dataDevice2->setSelection(1, dataSource4.data());
QVERIFY(selectionOfferedSpy.wait());
auto dataOffer = selectionOfferedSpy.last()[0].value<DataOffer*>();
// try to crash by destroying the data source, then requesting data
dataSource4.reset();
int pipeFds[2];
Q_ASSERT(pipe(pipeFds) == 0);
dataOffer->receive(QStringLiteral("text/plain"), pipeFds[1]);
close(pipeFds[1]);
//spin the event loop, nothing should explode
QTest::qWait(10);
close(pipeFds[0]);
}
void TestDataDevice::testDestroy()

View file

@ -29,7 +29,6 @@ private Q_SLOTS:
void testTargetAccepts_data();
void testTargetAccepts();
void testRequestSend();
void testRequestSendOnUnbound();
void testCancel();
void testServerGet();
void testDestroy();
@ -82,8 +81,6 @@ void TestDataSource::init()
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<quint32>(),
@ -131,7 +128,6 @@ void TestDataSource::testOffer()
QPointer<DataSourceInterface> serverDataSource = dataSourceCreatedSpy.first().first().value<DataSourceInterface*>();
QVERIFY(!serverDataSource.isNull());
QCOMPARE(serverDataSource->mimeTypes().count(), 0);
QVERIFY(serverDataSource->parentResource());
QSignalSpy offeredSpy(serverDataSource.data(), SIGNAL(mimeTypeOffered(QString)));
QVERIFY(offeredSpy.isValid());
@ -161,10 +157,6 @@ void TestDataSource::testOffer()
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());
}
@ -212,7 +204,6 @@ void TestDataSource::testRequestSend()
QScopedPointer<DataSource> dataSource(m_dataDeviceManager->createDataSource());
QVERIFY(dataSource->isValid());
QSignalSpy sendRequestedSpy(dataSource.data(), SIGNAL(sendDataRequested(QString,qint32)));
QVERIFY(sendRequestedSpy.isValid());
@ -234,28 +225,6 @@ void TestDataSource::testRequestSend()
writeFile.close();
}
void TestDataSource::testRequestSendOnUnbound()
{
// this test verifies that the server doesn't crash when requesting a send on an unbound DataSource
using namespace KWayland::Client;
using namespace KWaylandServer;
QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataSourceCreated);
QVERIFY(dataSourceCreatedSpy.isValid());
QScopedPointer<DataSource> dataSource(m_dataDeviceManager->createDataSource());
QVERIFY(dataSource->isValid());
QVERIFY(dataSourceCreatedSpy.wait());
QCOMPARE(dataSourceCreatedSpy.count(), 1);
auto sds = dataSourceCreatedSpy.first().first().value<DataSourceInterface*>();
QVERIFY(sds);
QSignalSpy unboundSpy(sds, &Resource::unbound);
QVERIFY(unboundSpy.isValid());
dataSource.reset();
QVERIFY(unboundSpy.wait());
sds->requestData(QStringLiteral("text/plain"), -1);
}
void TestDataSource::testCancel()
{
using namespace KWayland::Client;

View file

@ -84,8 +84,6 @@ void TestDragAndDrop::init()
m_seatInterface->create();
QVERIFY(m_seatInterface->isValid());
m_dataDeviceManagerInterface = m_display->createDataDeviceManager(m_display);
m_dataDeviceManagerInterface->create();
QVERIFY(m_dataDeviceManagerInterface->isValid());
m_display->createShm();
m_thread = new QThread(this);

View file

@ -72,7 +72,6 @@ void SelectionTest::init()
m_seatInterface->setHasKeyboard(true);
m_seatInterface->create();
m_ddmInterface = m_display->createDataDeviceManager(m_display);
m_ddmInterface->create();
// setup connection
setupConnection(&m_client1);

View file

@ -1814,7 +1814,6 @@ void TestWaylandSeat::testSelection()
using namespace KWayland::Client;
using namespace KWaylandServer;
QScopedPointer<DataDeviceManagerInterface> ddmi(m_display->createDataDeviceManager());
ddmi->create();
Registry registry;
QSignalSpy dataDeviceManagerSpy(&registry, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32)));
QVERIFY(dataDeviceManagerSpy.isValid());
@ -1927,7 +1926,6 @@ void TestWaylandSeat::testDataDeviceForKeyboardSurface()
using namespace KWaylandServer;
// create the DataDeviceManager
QScopedPointer<DataDeviceManagerInterface> ddmi(m_display->createDataDeviceManager());
ddmi->create();
QSignalSpy ddiCreatedSpy(ddmi.data(), &DataDeviceManagerInterface::dataDeviceCreated);
QVERIFY(ddiCreatedSpy.isValid());

View file

@ -110,7 +110,7 @@ public:
AbstractDataSource(nullptr)
{}
~TestDataSource() {
emit unbound();
emit aboutToBeDestroyed();
}
void requestData(const QString &mimeType, qint32 fd) override {
Q_UNUSED(mimeType);

View file

@ -40,7 +40,7 @@ DataControlSourceV1InterfacePrivate::DataControlSourceV1InterfacePrivate(DataCon
void DataControlSourceV1InterfacePrivate::zwlr_data_control_source_v1_destroy_resource(QtWaylandServer::zwlr_data_control_source_v1::Resource *resource)
{
Q_UNUSED(resource)
emit q->unbound();
emit q->aboutToBeDestroyed();
delete q;
}
@ -56,7 +56,7 @@ void DataControlSourceV1InterfacePrivate::zwlr_data_control_source_v1_destroy(Qt
}
DataControlSourceV1Interface::DataControlSourceV1Interface(DataControlDeviceManagerV1Interface *parent, ::wl_resource *resource)
: AbstractDataSource(nullptr, parent)
: AbstractDataSource(parent)
, d(new DataControlSourceV1InterfacePrivate(this, resource))
{
}
@ -79,7 +79,7 @@ QStringList DataControlSourceV1Interface::mimeTypes() const
return d->mimeTypes;
}
wl_client *DataControlSourceV1Interface::client()
wl_client *DataControlSourceV1Interface::client() const
{
return d->resource()->client();
}

View file

@ -33,7 +33,7 @@ public:
void cancel() override;
QStringList mimeTypes() const override;
wl_client *client() override;
wl_client *client() const override;
static DataControlSourceV1Interface *get(wl_resource *native);

View file

@ -1,28 +1,28 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "datadevice_interface.h"
#include "datadevicemanager_interface.h"
#include "dataoffer_interface_p.h"
#include "datasource_interface.h"
#include "dataoffer_interface.h"
#include "display.h"
#include "resource_p.h"
#include "pointer_interface.h"
#include "seat_interface.h"
#include "seat_interface_p.h"
#include "surface_interface.h"
// Wayland
#include <wayland-server.h>
#include <qwayland-server-wayland.h>
namespace KWaylandServer
{
class DataDeviceInterface::Private : public Resource::Private
class DataDeviceInterfacePrivate : public QtWaylandServer::wl_data_device
{
public:
Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource);
~Private();
DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource);
DataOfferInterface *createDataOffer(AbstractDataSource *source);
@ -31,9 +31,7 @@ public:
SurfaceInterface *surface = nullptr;
SurfaceInterface *icon = nullptr;
DataSourceInterface *selection = nullptr;
QMetaObject::Connection selectionUnboundConnection;
QMetaObject::Connection selectionDestroyedConnection;
QPointer<DataSourceInterface> selection;
struct Drag {
SurfaceInterface *surface = nullptr;
@ -45,47 +43,34 @@ public:
};
Drag drag;
DataDeviceInterface *q;
QPointer<SurfaceInterface> proxyRemoteSurface;
private:
DataDeviceInterface *q_func() {
return reinterpret_cast<DataDeviceInterface*>(q);
}
void startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *icon, quint32 serial);
void setSelection(DataSourceInterface *dataSource);
static void startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial);
static void setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial);
static const struct wl_data_device_interface s_interface;
protected:
void data_device_destroy_resource(Resource *resource) override;
void data_device_start_drag(Resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial) override;
void data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial) override;
void data_device_release(Resource *resource) override;
};
#ifndef K_DOXYGEN
const struct wl_data_device_interface DataDeviceInterface::Private::s_interface = {
startDragCallback,
setSelectionCallback,
resourceDestroyedCallback
};
#endif
DataDeviceInterface::Private::Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource)
: Resource::Private(q, manager, parentResource, &wl_data_device_interface, &s_interface)
DataDeviceInterfacePrivate::DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource)
: QtWaylandServer::wl_data_device(resource)
, seat(seat)
, q(_q)
{
}
DataDeviceInterface::Private::~Private() = default;
void DataDeviceInterface::Private::startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial)
void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource, wl_resource *sourceResource, wl_resource *originResource, wl_resource *iconResource, uint32_t serial)
{
Q_UNUSED(client)
Q_UNUSED(serial)
// TODO: verify serial
cast<Private>(resource)->startDrag(DataSourceInterface::get(source), SurfaceInterface::get(origin), SurfaceInterface::get(icon), serial);
}
Q_UNUSED(resource)
SurfaceInterface *focusSurface = SurfaceInterface::get(originResource);
SurfaceInterface *i = SurfaceInterface::get(iconResource);
DataSourceInterface *dataSource = nullptr;
if (sourceResource) {
dataSource = DataSourceInterface::get(sourceResource);
}
void DataDeviceInterface::Private::startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *i, quint32 serial)
{
SurfaceInterface *focusSurface = origin;
if (proxyRemoteSurface) {
// origin is a proxy surface
focusSurface = proxyRemoteSurface.data();
@ -100,144 +85,123 @@ void DataDeviceInterface::Private::startDrag(DataSourceInterface *dataSource, Su
}
}
// TODO: source is allowed to be null, handled client internally!
Q_Q(DataDeviceInterface);
source = dataSource;
if (dataSource) {
QObject::connect(dataSource, &Resource::aboutToBeUnbound, q, [this] { source = nullptr; });
QObject::connect(dataSource, &AbstractDataSource::aboutToBeDestroyed, q, [this] { source = nullptr; });
}
surface = origin;
surface = focusSurface;
icon = i;
drag.serial = serial;
emit q->dragStarted();
}
void DataDeviceInterface::Private::setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial)
void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial)
{
Q_UNUSED(client)
Q_UNUSED(resource)
Q_UNUSED(serial)
// TODO: verify serial
cast<Private>(resource)->setSelection(DataSourceInterface::get(source));
}
DataSourceInterface *dataSource = DataSourceInterface::get(source);
void DataDeviceInterface::Private::setSelection(DataSourceInterface *dataSource)
{
if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
wl_resource_post_error(dataSource->resource(), WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "Data source is for drag and drop");
wl_resource_post_error(dataSource->resource(), QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
return;
}
if (selection == dataSource) {
return;
}
Q_Q(DataDeviceInterface);
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();
}
}
DataOfferInterface *DataDeviceInterface::Private::createDataOffer(AbstractDataSource *source)
void DataDeviceInterfacePrivate::data_device_release(QtWaylandServer::wl_data_device::Resource *resource)
{
wl_resource_destroy(resource->handle);
}
DataOfferInterface *DataDeviceInterfacePrivate::createDataOffer(AbstractDataSource *source)
{
if (!resource) {
return nullptr;
}
if (!source) {
// a data offer can only exist together with a source
return nullptr;
}
Q_Q(DataDeviceInterface);
DataOfferInterface *offer = new DataOfferInterface(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;
wl_resource *data_offer_resource = wl_resource_create(resource()->client(), &wl_data_offer_interface, resource()->version(), 0);
if (!data_offer_resource) {
wl_resource_post_no_memory(resource()->handle);
return nullptr;
}
wl_data_device_send_data_offer(resource, offer->resource());
DataOfferInterface *offer = new DataOfferInterface(source, data_offer_resource);
send_data_offer(offer->resource());
offer->sendAllOffers();
return offer;
}
DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, DataDeviceManagerInterface *parent, wl_resource *parentResource)
: Resource(new Private(seat, this, parent, parentResource))
void DataDeviceInterfacePrivate::data_device_destroy_resource(QtWaylandServer::wl_data_device::Resource *resource)
{
Q_UNUSED(resource)
delete q;
}
DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, wl_resource *resource)
: QObject(nullptr)
, d(new DataDeviceInterfacePrivate(seat, this, resource))
{
seat->d_func()->registerDataDevice(this);
}
DataDeviceInterface::~DataDeviceInterface() = default;
SeatInterface *DataDeviceInterface::seat() const
{
Q_D();
return d->seat;
}
DataSourceInterface *DataDeviceInterface::dragSource() const
{
Q_D();
return d->source;
}
SurfaceInterface *DataDeviceInterface::icon() const
{
Q_D();
return d->icon;
}
SurfaceInterface *DataDeviceInterface::origin() const
{
Q_D();
return d->proxyRemoteSurface ? d->proxyRemoteSurface.data() : d->surface;
}
DataSourceInterface *DataDeviceInterface::selection() const
{
Q_D();
return d->selection;
}
void DataDeviceInterface::sendSelection(AbstractDataSource *other)
{
Q_D();
auto r = d->createDataOffer(other);
if (!r) {
return;
}
if (!d->resource) {
return;
}
wl_data_device_send_selection(d->resource, r->resource());
d->send_selection(r->resource());
}
void DataDeviceInterface::sendClearSelection()
{
Q_D();
if (!d->resource) {
return;
}
wl_data_device_send_selection(d->resource, nullptr);
d->send_selection(nullptr);
}
void DataDeviceInterface::drop()
{
Q_D();
if (!d->resource) {
return;
}
wl_data_device_send_drop(d->resource);
d->send_drop();
if (d->drag.posConnection) {
disconnect(d->drag.posConnection);
d->drag.posConnection = QMetaObject::Connection();
@ -245,15 +209,14 @@ void DataDeviceInterface::drop()
disconnect(d->drag.destroyConnection);
d->drag.destroyConnection = QMetaObject::Connection();
d->drag.surface = nullptr;
client()->flush();
wl_client_flush(d->resource()->client());
}
void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 serial)
{
Q_D();
if (d->drag.surface) {
if (d->resource && d->drag.surface->resource()) {
wl_data_device_send_leave(d->resource);
if (d->drag.surface->resource()) {
d->send_leave();
}
if (d->drag.posConnection) {
disconnect(d->drag.posConnection);
@ -289,48 +252,43 @@ void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 se
if (d->seat->isDragPointer()) {
d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this,
[this] {
Q_D();
const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
wl_data_device_send_motion(d->resource, d->seat->timestamp(),
d->send_motion(d->seat->timestamp(),
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
client()->flush();
wl_client_flush(d->resource()->client());
}
);
} else if (d->seat->isDragTouch()) {
d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this,
[this](qint32 id, quint32 serial, const QPointF &globalPosition) {
Q_D();
Q_UNUSED(id);
if (serial != d->drag.serial) {
// different touch down has been moved
return;
}
const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition);
wl_data_device_send_motion(d->resource, d->seat->timestamp(),
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
client()->flush();
d->send_motion(d->seat->timestamp(),
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
wl_client_flush(d->resource()->client());
}
);
}
d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this,
[this] {
Q_D();
if (d->resource) {
wl_data_device_send_leave(d->resource);
}
d->send_leave();
if (d->drag.posConnection) {
disconnect(d->drag.posConnection);
}
d->drag = Private::Drag();
d->drag = DataDeviceInterfacePrivate::Drag();
}
);
// TODO: handle touch position
const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
wl_data_device_send_enter(d->resource, serial, surface->resource(),
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
d->send_enter(serial, surface->resource(),
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
if (offer) {
offer->d_func()->sendSourceActions();
offer->sendSourceActions();
auto matchOffers = [source, offer] {
DataDeviceManagerInterface::DnDAction action{DataDeviceManagerInterface::DnDAction::None};
if (source->supportedDragAndDropActions().testFlag(offer->preferredDragAndDropAction())) {
@ -353,25 +311,23 @@ void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 se
d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, source, matchOffers);
d->drag.sourceActionConnection = connect(source, &DataSourceInterface::supportedDragAndDropActionsChanged, source, matchOffers);
}
d->client->flush();
wl_client_flush(d->resource()->client());
}
quint32 DataDeviceInterface::dragImplicitGrabSerial() const
{
Q_D();
return d->drag.serial;
}
void DataDeviceInterface::updateProxy(SurfaceInterface *remote)
{
Q_D();
// TODO: connect destroy signal?
d->proxyRemoteSurface = remote;
}
DataDeviceInterface::Private *DataDeviceInterface::d_func() const
wl_client *DataDeviceInterface::client()
{
return reinterpret_cast<DataDeviceInterface::Private*>(d.data());
return d->resource()->client();
}
}

View file

@ -1,5 +1,6 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
@ -21,6 +22,7 @@ class DataSourceInterface;
class AbstractDataSource;
class SeatInterface;
class SurfaceInterface;
class DataDeviceInterfacePrivate;
/**
* @brief DataDeviceInterface allows clients to share data by copy-and-paste and drag-and-drop.
@ -33,7 +35,7 @@ class SurfaceInterface;
* @see SeatInterface
* @see DataSourceInterface
**/
class KWAYLANDSERVER_EXPORT DataDeviceInterface : public Resource
class KWAYLANDSERVER_EXPORT DataDeviceInterface : public QObject
{
Q_OBJECT
public:
@ -78,17 +80,17 @@ public:
**/
void updateProxy(SurfaceInterface *remote);
wl_client *client();
Q_SIGNALS:
void dragStarted();
void selectionChanged(KWaylandServer::DataSourceInterface*);
void selectionCleared();
private:
friend class DataDeviceManagerInterface;
explicit DataDeviceInterface(SeatInterface *seat, DataDeviceManagerInterface *parent, wl_resource *parentResource);
class Private;
Private *d_func() const;
friend class DataDeviceManagerInterfacePrivate;
explicit DataDeviceInterface(SeatInterface *seat, wl_resource *resource);
QScopedPointer<DataDeviceInterfacePrivate> d;
};
}

View file

@ -1,5 +1,6 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
@ -9,107 +10,66 @@
#include "display.h"
#include "seat_interface_p.h"
// Wayland
#include <wayland-server.h>
#include <qwayland-server-wayland.h>
namespace KWaylandServer
{
class DataDeviceManagerInterface::Private : public Global::Private
static const quint32 s_version = 3;
class DataDeviceManagerInterfacePrivate : public QtWaylandServer::wl_data_device_manager
{
public:
Private(DataDeviceManagerInterface *q, Display *d);
DataDeviceManagerInterfacePrivate(DataDeviceManagerInterface *q, Display *d);
DataDeviceManagerInterface *q;
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<Private*>(wl_resource_get_user_data(r));
}
DataDeviceManagerInterface *q;
static const struct wl_data_device_manager_interface s_interface;
static const quint32 s_version;
static const qint32 s_dataDeviceVersion;
static const qint32 s_dataSourceVersion;
protected:
void data_device_manager_create_data_source(Resource *resource, uint32_t id) override;
void data_device_manager_get_data_device(Resource *resource, uint32_t id, wl_resource *seat) override;
};
const quint32 DataDeviceManagerInterface::Private::s_version = 3;
const qint32 DataDeviceManagerInterface::Private::s_dataDeviceVersion = 3;
const qint32 DataDeviceManagerInterface::Private::s_dataSourceVersion = 3;
#ifndef K_DOXYGEN
const struct wl_data_device_manager_interface DataDeviceManagerInterface::Private::s_interface = {
createDataSourceCallback,
getDataDeviceCallback
};
#endif
DataDeviceManagerInterface::Private::Private(DataDeviceManagerInterface *q, Display *d)
: Global::Private(d, &wl_data_device_manager_interface, s_version)
DataDeviceManagerInterfacePrivate::DataDeviceManagerInterfacePrivate(DataDeviceManagerInterface *q, Display *d)
: QtWaylandServer::wl_data_device_manager(*d, s_version)
, q(q)
{
}
void DataDeviceManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
void DataDeviceManagerInterfacePrivate::data_device_manager_create_data_source(Resource *resource, uint32_t id)
{
auto c = display->getConnection(client);
wl_resource *resource = c->createResource(&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)
{
DataSourceInterface *dataSource = new DataSourceInterface(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;
wl_resource *data_source_resource = wl_resource_create(resource->client(), &wl_data_source_interface, resource->version(), id);
if (!data_source_resource) {
wl_resource_post_no_memory(resource->handle);
return;
}
DataSourceInterface *dataSource = new DataSourceInterface(q, data_source_resource);
emit q->dataSourceCreated(dataSource);
}
void DataDeviceManagerInterface::Private::getDataDeviceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat)
{
cast(resource)->getDataDevice(client, resource, id, seat);
}
void DataDeviceManagerInterface::Private::getDataDevice(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat)
void DataDeviceManagerInterfacePrivate::data_device_manager_get_data_device(Resource *resource, uint32_t id, wl_resource *seat)
{
SeatInterface *s = SeatInterface::get(seat);
Q_ASSERT(s);
DataDeviceInterface *dataDevice = new DataDeviceInterface(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);
if (!s) {
return;
}
s->d_func()->registerDataDevice(dataDevice);
wl_resource *data_device_resource = wl_resource_create(resource->client(), &wl_data_device_interface, resource->version(), id);
if (!data_device_resource) {
wl_resource_post_no_memory(resource->handle);
return;
}
DataDeviceInterface *dataDevice = new DataDeviceInterface(s, data_device_resource);
emit q->dataDeviceCreated(dataDevice);
}
DataDeviceManagerInterface::DataDeviceManagerInterface(Display *display, QObject *parent)
: Global(new Private(this, display), parent)
: QObject(parent)
, d(new DataDeviceManagerInterfacePrivate(this, display))
{
}

View file

@ -1,5 +1,6 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
@ -9,7 +10,6 @@
#include <QObject>
#include <KWaylandServer/kwaylandserver_export.h>
#include "global.h"
#include "datadevice_interface.h"
namespace KWaylandServer
@ -17,20 +17,20 @@ namespace KWaylandServer
class Display;
class DataSourceInterface;
class DataDeviceManagerInterfacePrivate;
/**
* @brief Represents the Global for wl_data_device_manager interface.
*
**/
class KWAYLANDSERVER_EXPORT DataDeviceManagerInterface : public Global
class KWAYLANDSERVER_EXPORT DataDeviceManagerInterface : public QObject
{
Q_OBJECT
public:
virtual ~DataDeviceManagerInterface();
~DataDeviceManagerInterface() override;
/**
* Drag and Drop actions supported by the DataSourceInterface.
* @since 5.XX
**/
enum class DnDAction {
None = 0,
@ -47,7 +47,7 @@ Q_SIGNALS:
private:
explicit DataDeviceManagerInterface(Display *display, QObject *parent = nullptr);
friend class Display;
class Private;
QScopedPointer<DataDeviceManagerInterfacePrivate> d;
};
}

View file

@ -1,159 +1,160 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "dataoffer_interface_p.h"
#include "dataoffer_interface.h"
#include "datadevice_interface.h"
#include "datasource_interface.h"
// Qt
#include <QStringList>
#include <QPointer>
// Wayland
#include <wayland-server.h>
#include <qwayland-server-wayland.h>
// system
#include <unistd.h>
namespace KWaylandServer
{
#ifndef K_DOXYGEN
const struct wl_data_offer_interface DataOfferInterface::Private::s_interface = {
acceptCallback,
receiveCallback,
resourceDestroyedCallback,
finishCallback,
setActionsCallback
};
#endif
DataOfferInterface::Private::Private(AbstractDataSource *source, DataDeviceInterface *parentInterface, DataOfferInterface *q, wl_resource *parentResource)
: Resource::Private(q, nullptr, parentResource, &wl_data_offer_interface, &s_interface)
, source(source)
, dataDevice(parentInterface)
class DataOfferInterfacePrivate : public QtWaylandServer::wl_data_offer
{
public:
DataOfferInterfacePrivate(AbstractDataSource *source, DataOfferInterface *q, wl_resource *resource);
DataOfferInterface *q;
QPointer<AbstractDataSource> source;
// defaults are set to sensible values for < version 3 interfaces
DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy | DataDeviceManagerInterface::DnDAction::Move;
DataDeviceManagerInterface::DnDAction preferredDnDAction = DataDeviceManagerInterface::DnDAction::Copy;
protected:
void data_offer_destroy_resource(Resource *resource) override;
void data_offer_accept(Resource *resource, uint32_t serial, const QString &mime_type) override;
void data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd) override;
void data_offer_destroy(Resource *resource) override;
void data_offer_finish(Resource *resource) override;
void data_offer_set_actions(Resource *resource, uint32_t dnd_actions, uint32_t preferred_action) override;
};
DataOfferInterfacePrivate::DataOfferInterfacePrivate(AbstractDataSource *_source, DataOfferInterface *_q, wl_resource *resource)
: QtWaylandServer::wl_data_offer(resource)
, q(_q)
, source(_source)
{
// TODO: connect to new selections
}
DataOfferInterface::Private::~Private() = default;
void DataOfferInterface::Private::acceptCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *mimeType)
void DataOfferInterfacePrivate::data_offer_accept(Resource *resource, uint32_t serial, const QString &mime_type)
{
Q_UNUSED(client)
Q_UNUSED(resource)
Q_UNUSED(serial)
auto p = cast<Private>(resource);
if (!p->source) {
if (!source) {
return;
}
p->source->accept(mimeType ? QString::fromUtf8(mimeType) : QString());
source->accept(mime_type);
}
void DataOfferInterface::Private::receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd)
{
Q_UNUSED(client)
cast<Private>(resource)->receive(QString::fromUtf8(mimeType), fd);
}
void DataOfferInterface::Private::receive(const QString &mimeType, qint32 fd)
void DataOfferInterfacePrivate::data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd)
{
Q_UNUSED(resource)
if (!source) {
close(fd);
return;
}
source->requestData(mimeType, fd);
source->requestData(mime_type, fd);
}
void DataOfferInterface::Private::finishCallback(wl_client *client, wl_resource *resource)
void DataOfferInterfacePrivate::data_offer_destroy(QtWaylandServer::wl_data_offer::Resource *resource)
{
Q_UNUSED(client)
auto p = cast<Private>(resource);
if (!p->source) {
return;
}
p->source->dndFinished();
wl_resource_destroy(resource->handle);
}
void DataOfferInterfacePrivate::data_offer_finish(Resource *resource)
{
Q_UNUSED(resource)
source->dndFinished();
// TODO: It is a client error to perform other requests than wl_data_offer.destroy after this one
}
void DataOfferInterface::Private::setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions, uint32_t preferred_action)
void DataOfferInterfacePrivate::data_offer_set_actions(Resource *resource, uint32_t dnd_actions, uint32_t preferred_action)
{
// TODO: check it's drag and drop, otherwise send error
Q_UNUSED(client)
DataDeviceManagerInterface::DnDActions supportedActions;
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) {
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
supportedActions |= DataDeviceManagerInterface::DnDAction::Copy;
}
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) {
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_move) {
supportedActions |= DataDeviceManagerInterface::DnDAction::Move;
}
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) {
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
supportedActions |= DataDeviceManagerInterface::DnDAction::Ask;
}
// verify that the no other actions are sent
if (dnd_actions & ~(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)) {
wl_resource_post_error(resource, WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, "Invalid action mask");
if (dnd_actions & ~(QtWaylandServer::wl_data_device_manager::dnd_action_copy | QtWaylandServer::wl_data_device_manager::dnd_action_move | QtWaylandServer::wl_data_device_manager::dnd_action_ask)) {
wl_resource_post_error(resource->handle, error_invalid_action_mask, "Invalid action mask");
return;
}
if (preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY &&
preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE &&
preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK &&
preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) {
wl_resource_post_error(resource, WL_DATA_OFFER_ERROR_INVALID_ACTION, "Invalid preferred action");
if (preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_copy &&
preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_move &&
preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_ask &&
preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_none) {
wl_resource_post_error(resource->handle, error_invalid_action, "Invalid preferred action");
return;
}
DataDeviceManagerInterface::DnDAction preferredAction = DataDeviceManagerInterface::DnDAction::None;
if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) {
if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
preferredAction = DataDeviceManagerInterface::DnDAction::Copy;
} else if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) {
} else if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_move) {
preferredAction = DataDeviceManagerInterface::DnDAction::Move;
} else if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) {
} else if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
preferredAction = DataDeviceManagerInterface::DnDAction::Ask;
}
auto p = cast<Private>(resource);
p->supportedDnDActions = supportedActions;
p->preferredDnDAction = preferredAction;
emit p->q_func()->dragAndDropActionsChanged();
supportedDnDActions = supportedActions;
preferredDnDAction = preferredAction;
emit q->dragAndDropActionsChanged();
}
void DataOfferInterface::Private::sendSourceActions()
void DataOfferInterface::sendSourceActions()
{
if (!source) {
if (!d->source) {
return;
}
if (wl_resource_get_version(resource) < WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
if (d->resource()->version() < WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
return;
}
uint32_t wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
const auto actions = source->supportedDragAndDropActions();
uint32_t wlActions = QtWaylandServer::wl_data_device_manager::dnd_action_none;
const auto actions = d->source->supportedDragAndDropActions();
if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Copy)) {
wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_copy;
}
if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Move)) {
wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_move;
}
if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Ask)) {
wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_ask;
}
wl_data_offer_send_source_actions(resource, wlActions);
d->send_source_actions(wlActions);
}
DataOfferInterface::DataOfferInterface(AbstractDataSource *source, DataDeviceInterface *parentInterface, wl_resource *parentResource)
: Resource(new Private(source, parentInterface, this, parentResource))
void DataOfferInterfacePrivate::data_offer_destroy_resource(QtWaylandServer::wl_data_offer::Resource *resource)
{
Q_UNUSED(resource)
delete q;
}
DataOfferInterface::DataOfferInterface(AbstractDataSource *source, wl_resource *resource)
: QObject(nullptr)
, d(new DataOfferInterfacePrivate(source, this, resource))
{
Q_ASSERT(source);
connect(source, &DataSourceInterface::mimeTypeOffered, this,
[this](const QString &mimeType) {
Q_D();
if (!d->resource) {
return;
}
wl_data_offer_send_offer(d->resource, mimeType.toUtf8().constData());
}
);
QObject::connect(source, &QObject::destroyed, this,
[this] {
Q_D();
d->source = nullptr;
d->send_offer(mimeType);
}
);
}
@ -162,44 +163,40 @@ DataOfferInterface::~DataOfferInterface() = default;
void DataOfferInterface::sendAllOffers()
{
Q_D();
for (const QString &mimeType : d->source->mimeTypes()) {
wl_data_offer_send_offer(d->resource, mimeType.toUtf8().constData());
d->send_offer(mimeType);
}
}
DataOfferInterface::Private *DataOfferInterface::d_func() const
wl_resource *DataOfferInterface::resource() const
{
return reinterpret_cast<DataOfferInterface::Private*>(d.data());
return d->resource()->handle;
}
DataDeviceManagerInterface::DnDActions DataOfferInterface::supportedDragAndDropActions() const
{
Q_D();
return d->supportedDnDActions;
}
DataDeviceManagerInterface::DnDAction DataOfferInterface::preferredDragAndDropAction() const
{
Q_D();
return d->preferredDnDAction;
}
void DataOfferInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
{
Q_D();
if (wl_resource_get_version(d->resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) {
if (d->resource()->version() < WL_DATA_OFFER_ACTION_SINCE_VERSION) {
return;
}
uint32_t wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
uint32_t wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_none;
if (action == DataDeviceManagerInterface::DnDAction::Copy) {
wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_copy;
} else if (action == DataDeviceManagerInterface::DnDAction::Move ) {
wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_move;
} else if (action == DataDeviceManagerInterface::DnDAction::Ask) {
wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_ask;
}
wl_data_offer_send_action(d->resource, wlAction);
d->send_action(wlAction);
}
}

View file

@ -1,5 +1,6 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
@ -18,18 +19,21 @@ namespace KWaylandServer
class DataDeviceInterface;
class AbstractDataSource;
class DataOfferInterfacePrivate;
/**
* @brief Represents the Resource for the wl_data_offer interface.
*
**/
class KWAYLANDSERVER_EXPORT DataOfferInterface : public Resource
class KWAYLANDSERVER_EXPORT DataOfferInterface : public QObject
{
Q_OBJECT
public:
virtual ~DataOfferInterface();
void sendAllOffers();
void sendSourceActions();
wl_resource *resource() const;
/**
* @returns The Drag and Drop actions supported by this DataOfferInterface.
@ -58,11 +62,10 @@ Q_SIGNALS:
void dragAndDropActionsChanged();
private:
friend class DataDeviceInterface;
explicit DataOfferInterface(AbstractDataSource *source, DataDeviceInterface *parentInterface, wl_resource *parentResource);
friend class DataDeviceInterfacePrivate;
explicit DataOfferInterface(AbstractDataSource *source, wl_resource *resource);
class Private;
Private *d_func() const;
QScopedPointer<DataOfferInterfacePrivate> d;
};
}

View file

@ -1,5 +1,6 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
@ -10,92 +11,91 @@
// Qt
#include <QStringList>
// Wayland
#include <wayland-server.h>
#include <qwayland-server-wayland.h>
// system
#include <unistd.h>
namespace KWaylandServer
{
class DataSourceInterface::Private : public Resource::Private
class DataSourceInterfacePrivate : public QtWaylandServer::wl_data_source
{
public:
Private(DataSourceInterface *q, DataDeviceManagerInterface *parent, wl_resource *parentResource);
~Private();
DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource);
DataSourceInterface *q;
QStringList mimeTypes;
DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::None;
protected:
void data_source_destroy_resource(Resource *resource) override;
void data_source_offer(Resource *resource, const QString &mime_type) override;
void data_source_destroy(Resource *resource) override;
void data_source_set_actions(Resource *resource, uint32_t dnd_actions) override;
private:
DataSourceInterface *q_func() {
return reinterpret_cast<DataSourceInterface *>(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 wl_data_source_interface s_interface;
};
#ifndef K_DOXYGEN
const struct wl_data_source_interface DataSourceInterface::Private::s_interface = {
offerCallback,
resourceDestroyedCallback,
setActionsCallback
};
#endif
DataSourceInterface::Private::Private(DataSourceInterface *q, DataDeviceManagerInterface *parent, wl_resource *parentResource)
: Resource::Private(q, parent, parentResource, &wl_data_source_interface, &s_interface)
DataSourceInterfacePrivate::DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource)
: QtWaylandServer::wl_data_source(resource)
, q(_q)
{
}
DataSourceInterface::Private::~Private() = default;
void DataSourceInterface::Private::offerCallback(wl_client *client, wl_resource *resource, const char *mimeType)
void DataSourceInterfacePrivate::data_source_destroy_resource(Resource *resource)
{
Q_UNUSED(client)
cast<Private>(resource)->offer(QString::fromUtf8(mimeType));
Q_UNUSED(resource)
emit q->aboutToBeDestroyed();
delete q;
}
void DataSourceInterface::Private::offer(const QString &mimeType)
void DataSourceInterfacePrivate::data_source_offer(QtWaylandServer::wl_data_source::Resource *resource, const QString &mime_type)
{
Q_UNUSED(resource)
mimeTypes << mime_type;
emit q->mimeTypeOffered(mime_type);
}
void DataSourceInterfacePrivate::data_source_destroy(QtWaylandServer::wl_data_source::Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void DataSourceInterfacePrivate::offer(const QString &mimeType)
{
mimeTypes << mimeType;
Q_Q(DataSourceInterface);
emit q->mimeTypeOffered(mimeType);
}
void DataSourceInterface::Private::setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions)
void DataSourceInterfacePrivate::data_source_set_actions(Resource *resource, uint32_t dnd_actions)
{
Q_UNUSED(client)
DataDeviceManagerInterface::DnDActions supportedActions;
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) {
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
supportedActions |= DataDeviceManagerInterface::DnDAction::Copy;
}
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) {
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_move) {
supportedActions |= DataDeviceManagerInterface::DnDAction::Move;
}
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) {
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
supportedActions |= DataDeviceManagerInterface::DnDAction::Ask;
}
// verify that the no other actions are sent
if (dnd_actions & ~(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)) {
wl_resource_post_error(resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "Invalid action mask");
if (dnd_actions & ~(QtWaylandServer::wl_data_device_manager::dnd_action_copy | QtWaylandServer::wl_data_device_manager::dnd_action_move | QtWaylandServer::wl_data_device_manager::dnd_action_ask)) {
wl_resource_post_error(resource->handle, QtWaylandServer::wl_data_source::error_invalid_action_mask, "Invalid action mask");
return;
}
auto p = cast<Private>(resource);
if (p->supportedDnDActions!= supportedActions) {
p->supportedDnDActions = supportedActions;
emit p->q_func()->supportedDragAndDropActionsChanged();
if (supportedDnDActions!= supportedActions) {
supportedDnDActions = supportedActions;
emit q->supportedDragAndDropActionsChanged();
}
}
DataSourceInterface::DataSourceInterface(DataDeviceManagerInterface *parent, wl_resource *parentResource)
: AbstractDataSource(new Private(this, parent, parentResource))
DataSourceInterface::DataSourceInterface(DataDeviceManagerInterface *parent, wl_resource *resource)
: AbstractDataSource(parent)
, d(new DataSourceInterfacePrivate(this, resource))
{
if (wl_resource_get_version(parentResource) < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
Q_D();
if (d->resource()->version() < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
d->supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy;
}
}
@ -104,86 +104,79 @@ DataSourceInterface::~DataSourceInterface() = default;
void DataSourceInterface::accept(const QString &mimeType)
{
Q_D();
// TODO: does this require a sanity check on the possible mimeType?
wl_data_source_send_target(d->resource, mimeType.isEmpty() ? nullptr : mimeType.toUtf8().constData());
d->send_target(mimeType);
}
void DataSourceInterface::requestData(const QString &mimeType, qint32 fd)
{
Q_D();
// TODO: does this require a sanity check on the possible mimeType?
if (d->resource) {
wl_data_source_send_send(d->resource, mimeType.toUtf8().constData(), int32_t(fd));
}
d->send_send(mimeType, int32_t(fd));
close(fd);
}
void DataSourceInterface::cancel()
{
Q_D();
if (!d->resource) {
return;
}
wl_data_source_send_cancelled(d->resource);
Resource::client()->flush();
d->send_cancelled();
}
QStringList DataSourceInterface::mimeTypes() const
{
Q_D();
return d->mimeTypes;
}
DataSourceInterface *DataSourceInterface::get(wl_resource *native)
{
return Private::get<DataSourceInterface>(native);
}
DataSourceInterface::Private *DataSourceInterface::d_func() const
{
return reinterpret_cast<DataSourceInterface::Private*>(d.data());
if (!native) {
return nullptr;
}
auto priv = static_cast<DataSourceInterfacePrivate*>(QtWaylandServer::wl_data_source::Resource::fromResource(native)->object());
return priv->q;
}
DataDeviceManagerInterface::DnDActions DataSourceInterface::supportedDragAndDropActions() const
{
Q_D();
return d->supportedDnDActions;
}
void DataSourceInterface::dropPerformed()
{
Q_D();
if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) {
if (d->resource()->version() < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) {
return;
}
wl_data_source_send_dnd_drop_performed(d->resource);
d->send_dnd_drop_performed();
}
void DataSourceInterface::dndFinished()
{
Q_D();
if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
if (d->resource()->version() < WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
return;
}
wl_data_source_send_dnd_finished(d->resource);
d->send_dnd_finished();
}
void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
{
Q_D();
if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
if (d->resource()->version() < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
return;
}
uint32_t wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
uint32_t wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_none;
if (action == DataDeviceManagerInterface::DnDAction::Copy) {
wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_copy;
} else if (action == DataDeviceManagerInterface::DnDAction::Move ) {
wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_move;
} else if (action == DataDeviceManagerInterface::DnDAction::Ask) {
wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_ask;
}
wl_data_source_send_action(d->resource, wlAction);
d->send_action(wlAction);
}
wl_resource *DataSourceInterface::resource() const
{
return d->resource()->handle;
}
wl_client *DataSourceInterface::client() const
{
return d->resource()->client();
}
}

View file

@ -1,5 +1,6 @@
/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
@ -15,6 +16,8 @@
namespace KWaylandServer
{
class DataSourceInterfacePrivate;
/**
* @brief Represents the Resource for the wl_data_source interface.
**/
@ -42,12 +45,15 @@ public:
void dndFinished() override;
void dndAction(DataDeviceManagerInterface::DnDAction action) override;
wl_resource *resource() const;
wl_client *client() const override;
private:
friend class DataDeviceManagerInterface;
friend class DataDeviceManagerInterfacePrivate;
explicit DataSourceInterface(DataDeviceManagerInterface *parent, wl_resource *parentResource);
class Private;
Private *d_func() const;
QScopedPointer<DataSourceInterfacePrivate> d;
};
}

View file

@ -38,8 +38,7 @@ PrimarySelectionSourceV1InterfacePrivate::PrimarySelectionSourceV1InterfacePriva
void PrimarySelectionSourceV1InterfacePrivate::zwp_primary_selection_source_v1_destroy_resource(QtWaylandServer::zwp_primary_selection_source_v1::Resource *resource)
{
Q_UNUSED(resource)
emit q->aboutToBeUnbound();
emit q->unbound();
emit q->aboutToBeDestroyed();
delete q;
}
@ -55,7 +54,7 @@ void PrimarySelectionSourceV1InterfacePrivate::zwp_primary_selection_source_v1_d
}
PrimarySelectionSourceV1Interface::PrimarySelectionSourceV1Interface(PrimarySelectionDeviceManagerV1Interface *parent, ::wl_resource *resource)
: AbstractDataSource(nullptr, parent)
: AbstractDataSource(parent)
, d(new PrimarySelectionSourceV1InterfacePrivate(this, resource))
{
}
@ -78,7 +77,7 @@ QStringList PrimarySelectionSourceV1Interface::mimeTypes() const
return d->mimeTypes;
}
wl_client *PrimarySelectionSourceV1Interface::client()
wl_client *PrimarySelectionSourceV1Interface::client() const
{
return d->resource()->client();
}

View file

@ -33,7 +33,7 @@ public:
QStringList mimeTypes() const override;
static PrimarySelectionSourceV1Interface *get(wl_resource *native);
wl_client *client() override;
wl_client *client() const override;
private:
friend class PrimarySelectionDeviceManagerV1InterfacePrivate;

View file

@ -247,7 +247,13 @@ QVector<TouchInterface *> SeatInterface::Private::touchsForSurface(SurfaceInterf
QVector<DataDeviceInterface *> SeatInterface::Private::dataDevicesForSurface(SurfaceInterface *surface) const
{
return interfacesForSurface(surface, dataDevices);
QVector<DataDeviceInterface *> primarySelectionDevices;
for (auto it = dataDevices.constBegin(); it != dataDevices.constEnd(); ++it) {
if ((*it)->client() == *surface->client()) {
primarySelectionDevices << *it;
}
}
return primarySelectionDevices;
}
void SeatInterface::Private::registerDataDevice(DataDeviceInterface *dataDevice)
@ -259,7 +265,6 @@ void SeatInterface::Private::registerDataDevice(DataDeviceInterface *dataDevice)
keys.focus.selections.removeOne(dataDevice);
};
QObject::connect(dataDevice, &QObject::destroyed, q, dataDeviceCleanup);
QObject::connect(dataDevice, &Resource::unbound, q, dataDeviceCleanup);
QObject::connect(dataDevice, &DataDeviceInterface::selectionChanged, q,
[this, dataDevice] {
updateSelection(dataDevice);
@ -303,7 +308,7 @@ void SeatInterface::Private::registerDataDevice(DataDeviceInterface *dataDevice)
}
);
if (dataDevice->dragSource()) {
drag.dragSourceDestroyConnection = QObject::connect(dataDevice->dragSource(), &Resource::aboutToBeUnbound, q,
drag.dragSourceDestroyConnection = QObject::connect(dataDevice->dragSource(), &AbstractDataSource::aboutToBeDestroyed, q,
[this] {
const auto serial = display->nextSerial();
if (drag.target) {
@ -324,7 +329,7 @@ void SeatInterface::Private::registerDataDevice(DataDeviceInterface *dataDevice)
// is the new DataDevice for the current keyoard focus?
if (keys.focus.surface) {
// same client?
if (keys.focus.surface->client() == dataDevice->client()) {
if (*keys.focus.surface->client() == dataDevice->client()) {
keys.focus.selections.append(dataDevice);
if (currentSelection) {
dataDevice->sendSelection(currentSelection);
@ -419,7 +424,7 @@ void SeatInterface::Private::endDrag(quint32 serial)
void SeatInterface::Private::updateSelection(DataDeviceInterface *dataDevice)
{
// if the update is from the focussed window we should inform the active client
if (!(keys.focus.surface && (keys.focus.surface->client() == dataDevice->client()))) {
if (!(keys.focus.surface && (*keys.focus.surface->client() == dataDevice->client()))) {
return;
}
q->setSelection(dataDevice->selection());
@ -1644,8 +1649,7 @@ void SeatInterface::setSelection(AbstractDataSource *selection)
auto cleanup = [this]() {
setSelection(nullptr);
};
connect(selection, &DataSourceInterface::unbound, this, cleanup);
connect(selection, &QObject::destroyed, this, cleanup);
connect(selection, &DataSourceInterface::aboutToBeDestroyed, this, cleanup);
}
d->currentSelection = selection;
@ -1684,7 +1688,7 @@ void SeatInterface::setPrimarySelection(AbstractDataSource *selection)
auto cleanup = [this]() {
setPrimarySelection(nullptr);
};
connect(selection, &DataSourceInterface::unbound, this, cleanup);
connect(selection, &DataSourceInterface::aboutToBeDestroyed, this, cleanup);
}
d->currentPrimarySelection = selection;

View file

@ -780,8 +780,8 @@ Q_SIGNALS:
private:
friend class Display;
friend class DataControlDeviceV1Interface;
friend class DataDeviceInterface;
friend class PrimarySelectionDeviceV1Interface;
friend class DataDeviceManagerInterface;
friend class TextInputManagerV2InterfacePrivate;
explicit SeatInterface(Display *display, QObject *parent);

View file

@ -1,47 +0,0 @@
/*
SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KWAYLAND_SERVER_DATAOFFERINTERFACE_P_H
#define KWAYLAND_SERVER_DATAOFFERINTERFACE_P_H
#include "dataoffer_interface.h"
#include "datasource_interface.h"
#include "resource_p.h"
#include <QPointer>
#include <wayland-server.h>
namespace KWaylandServer
{
class Q_DECL_HIDDEN DataOfferInterface::Private : public Resource::Private
{
public:
Private(AbstractDataSource *source, DataDeviceInterface *parentInterface, DataOfferInterface *q, wl_resource *parentResource);
~Private();
QPointer<AbstractDataSource> source;
DataDeviceInterface *dataDevice;
// defaults are set to sensible values for < version 3 interfaces
DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy | DataDeviceManagerInterface::DnDAction::Move;
DataDeviceManagerInterface::DnDAction preferredDnDAction = DataDeviceManagerInterface::DnDAction::Copy;
void sendSourceActions();
private:
DataOfferInterface *q_func() {
return reinterpret_cast<DataOfferInterface *>(q);
}
void receive(const QString &mimeType, qint32 fd);
static void acceptCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *mimeType);
static void receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd);
static void finishCallback(wl_client *client, wl_resource *resource);
static void setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions, uint32_t preferred_action);
static const struct wl_data_offer_interface s_interface;
};
}
#endif

View file

@ -240,7 +240,6 @@ int main(int argc, char **argv)
Display display;
display.start();
DataDeviceManagerInterface *ddm = display.createDataDeviceManager();
ddm->create();
display.createCompositor(&display);
XdgShellInterface *shell = display.createXdgShell();
display.createShm();