2020-03-15 15:19:28 +00:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
2020-06-25 15:27:52 +00:00
|
|
|
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2020-03-15 15:19:28 +00:00
|
|
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
|
|
*/
|
2014-11-06 09:02:49 +00:00
|
|
|
#include "datadevice_interface.h"
|
2014-11-14 08:45:02 +00:00
|
|
|
#include "datadevicemanager_interface.h"
|
2014-11-06 09:02:49 +00:00
|
|
|
#include "datasource_interface.h"
|
2020-06-25 15:27:52 +00:00
|
|
|
#include "dataoffer_interface.h"
|
2014-11-19 15:53:56 +00:00
|
|
|
#include "display.h"
|
2014-11-14 08:45:02 +00:00
|
|
|
#include "resource_p.h"
|
2014-11-25 12:52:40 +00:00
|
|
|
#include "pointer_interface.h"
|
2014-11-06 09:02:49 +00:00
|
|
|
#include "seat_interface.h"
|
2020-06-25 15:27:52 +00:00
|
|
|
#include "seat_interface_p.h"
|
2014-11-06 09:02:49 +00:00
|
|
|
#include "surface_interface.h"
|
2020-06-25 15:27:52 +00:00
|
|
|
#include <qwayland-server-wayland.h>
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2020-04-29 14:56:38 +00:00
|
|
|
namespace KWaylandServer
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
class DataDeviceInterfacePrivate : public QtWaylandServer::wl_data_device
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
|
|
|
public:
|
2020-06-25 15:27:52 +00:00
|
|
|
DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource);
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2020-05-12 11:26:48 +00:00
|
|
|
DataOfferInterface *createDataOffer(AbstractDataSource *source);
|
2014-11-06 15:56:50 +00:00
|
|
|
|
2014-11-06 09:02:49 +00:00
|
|
|
SeatInterface *seat;
|
|
|
|
DataSourceInterface *source = nullptr;
|
|
|
|
SurfaceInterface *surface = nullptr;
|
|
|
|
SurfaceInterface *icon = nullptr;
|
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
QPointer<DataSourceInterface> selection;
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2016-03-01 06:49:04 +00:00
|
|
|
struct Drag {
|
|
|
|
SurfaceInterface *surface = nullptr;
|
|
|
|
QMetaObject::Connection destroyConnection;
|
2018-09-12 01:33:07 +00:00
|
|
|
QMetaObject::Connection posConnection;
|
2017-11-26 15:31:57 +00:00
|
|
|
QMetaObject::Connection sourceActionConnection;
|
|
|
|
QMetaObject::Connection targetActionConnection;
|
2016-03-01 06:49:04 +00:00
|
|
|
quint32 serial = 0;
|
|
|
|
};
|
|
|
|
Drag drag;
|
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
DataDeviceInterface *q;
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
QPointer<SurfaceInterface> proxyRemoteSurface;
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
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;
|
2014-11-06 09:02:49 +00:00
|
|
|
};
|
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
DataDeviceInterfacePrivate::DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource)
|
|
|
|
: QtWaylandServer::wl_data_device(resource)
|
2014-11-14 08:45:02 +00:00
|
|
|
, seat(seat)
|
2020-06-25 15:27:52 +00:00
|
|
|
, q(_q)
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource, wl_resource *sourceResource, wl_resource *originResource, wl_resource *iconResource, uint32_t serial)
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
2020-06-25 15:27:52 +00:00
|
|
|
Q_UNUSED(resource)
|
|
|
|
SurfaceInterface *focusSurface = SurfaceInterface::get(originResource);
|
|
|
|
SurfaceInterface *i = SurfaceInterface::get(iconResource);
|
|
|
|
DataSourceInterface *dataSource = nullptr;
|
|
|
|
if (sourceResource) {
|
|
|
|
dataSource = DataSourceInterface::get(sourceResource);
|
|
|
|
}
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2019-02-06 08:26:43 +00:00
|
|
|
if (proxyRemoteSurface) {
|
|
|
|
// origin is a proxy surface
|
2019-08-28 17:38:25 +00:00
|
|
|
focusSurface = proxyRemoteSurface.data();
|
2019-02-06 08:26:43 +00:00
|
|
|
}
|
|
|
|
const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface;
|
2018-09-12 01:33:07 +00:00
|
|
|
if (!pointerGrab) {
|
|
|
|
// Client doesn't have pointer grab.
|
2019-02-06 08:26:43 +00:00
|
|
|
const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface;
|
2018-09-12 01:33:07 +00:00
|
|
|
if (!touchGrab) {
|
|
|
|
// Client neither has pointer nor touch grab. No drag start allowed.
|
|
|
|
return;
|
|
|
|
}
|
2014-11-06 09:02:49 +00:00
|
|
|
}
|
2016-03-01 06:49:04 +00:00
|
|
|
// TODO: source is allowed to be null, handled client internally!
|
2014-11-06 09:02:49 +00:00
|
|
|
source = dataSource;
|
2018-01-27 13:12:41 +00:00
|
|
|
if (dataSource) {
|
2020-06-25 15:27:52 +00:00
|
|
|
QObject::connect(dataSource, &AbstractDataSource::aboutToBeDestroyed, q, [this] { source = nullptr; });
|
2018-01-27 13:12:41 +00:00
|
|
|
}
|
2020-06-25 15:27:52 +00:00
|
|
|
surface = focusSurface;
|
2014-11-06 09:02:49 +00:00
|
|
|
icon = i;
|
2016-03-01 06:49:04 +00:00
|
|
|
drag.serial = serial;
|
2014-11-06 09:02:49 +00:00
|
|
|
emit q->dragStarted();
|
|
|
|
}
|
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial)
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
2020-06-25 15:27:52 +00:00
|
|
|
Q_UNUSED(resource)
|
2014-11-06 09:02:49 +00:00
|
|
|
Q_UNUSED(serial)
|
2020-06-25 15:27:52 +00:00
|
|
|
DataSourceInterface *dataSource = DataSourceInterface::get(source);
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2017-12-26 20:55:11 +00:00
|
|
|
if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
|
2020-06-25 15:27:52 +00:00
|
|
|
wl_resource_post_error(dataSource->resource(), QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
|
2017-11-26 15:31:57 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-06-25 15:27:52 +00:00
|
|
|
|
2018-06-14 12:21:41 +00:00
|
|
|
if (selection == dataSource) {
|
|
|
|
return;
|
|
|
|
}
|
2016-09-12 06:57:07 +00:00
|
|
|
if (selection) {
|
|
|
|
selection->cancel();
|
|
|
|
}
|
2014-11-06 09:02:49 +00:00
|
|
|
selection = dataSource;
|
|
|
|
if (selection) {
|
|
|
|
emit q->selectionChanged(selection);
|
|
|
|
} else {
|
|
|
|
emit q->selectionCleared();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
void DataDeviceInterfacePrivate::data_device_release(QtWaylandServer::wl_data_device::Resource *resource)
|
|
|
|
{
|
|
|
|
wl_resource_destroy(resource->handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DataOfferInterface *DataDeviceInterfacePrivate::createDataOffer(AbstractDataSource *source)
|
2014-11-06 15:56:50 +00:00
|
|
|
{
|
2018-08-25 10:58:02 +00:00
|
|
|
if (!source) {
|
|
|
|
// a data offer can only exist together with a source
|
|
|
|
return nullptr;
|
|
|
|
}
|
2020-06-25 15:27:52 +00:00
|
|
|
|
|
|
|
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);
|
2014-11-06 15:56:50 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
2020-06-25 15:27:52 +00:00
|
|
|
|
|
|
|
DataOfferInterface *offer = new DataOfferInterface(source, data_offer_resource);
|
|
|
|
send_data_offer(offer->resource());
|
2014-11-06 15:56:50 +00:00
|
|
|
offer->sendAllOffers();
|
|
|
|
return offer;
|
|
|
|
}
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
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))
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
2020-06-25 15:27:52 +00:00
|
|
|
seat->d_func()->registerDataDevice(this);
|
2014-11-06 09:02:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DataDeviceInterface::~DataDeviceInterface() = default;
|
|
|
|
|
|
|
|
SeatInterface *DataDeviceInterface::seat() const
|
|
|
|
{
|
|
|
|
return d->seat;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataSourceInterface *DataDeviceInterface::dragSource() const
|
|
|
|
{
|
|
|
|
return d->source;
|
|
|
|
}
|
|
|
|
|
|
|
|
SurfaceInterface *DataDeviceInterface::icon() const
|
|
|
|
{
|
|
|
|
return d->icon;
|
|
|
|
}
|
|
|
|
|
|
|
|
SurfaceInterface *DataDeviceInterface::origin() const
|
|
|
|
{
|
2019-08-28 17:38:25 +00:00
|
|
|
return d->proxyRemoteSurface ? d->proxyRemoteSurface.data() : d->surface;
|
2014-11-06 09:02:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DataSourceInterface *DataDeviceInterface::selection() const
|
|
|
|
{
|
|
|
|
return d->selection;
|
|
|
|
}
|
|
|
|
|
2020-05-12 11:26:48 +00:00
|
|
|
void DataDeviceInterface::sendSelection(AbstractDataSource *other)
|
2014-11-06 15:56:50 +00:00
|
|
|
{
|
2020-05-18 13:43:38 +00:00
|
|
|
auto r = d->createDataOffer(other);
|
2014-11-06 15:56:50 +00:00
|
|
|
if (!r) {
|
|
|
|
return;
|
|
|
|
}
|
2020-06-25 15:27:52 +00:00
|
|
|
d->send_selection(r->resource());
|
2014-11-14 08:45:02 +00:00
|
|
|
}
|
|
|
|
|
2014-11-27 12:38:24 +00:00
|
|
|
void DataDeviceInterface::sendClearSelection()
|
|
|
|
{
|
2020-06-25 15:27:52 +00:00
|
|
|
d->send_selection(nullptr);
|
2014-11-27 12:38:24 +00:00
|
|
|
}
|
|
|
|
|
2016-03-01 06:49:04 +00:00
|
|
|
void DataDeviceInterface::drop()
|
|
|
|
{
|
2020-06-25 15:27:52 +00:00
|
|
|
d->send_drop();
|
2018-09-12 01:33:07 +00:00
|
|
|
if (d->drag.posConnection) {
|
|
|
|
disconnect(d->drag.posConnection);
|
|
|
|
d->drag.posConnection = QMetaObject::Connection();
|
2017-11-26 15:31:57 +00:00
|
|
|
}
|
|
|
|
disconnect(d->drag.destroyConnection);
|
|
|
|
d->drag.destroyConnection = QMetaObject::Connection();
|
|
|
|
d->drag.surface = nullptr;
|
2020-06-25 15:27:52 +00:00
|
|
|
wl_client_flush(d->resource()->client());
|
2016-03-01 06:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 serial)
|
|
|
|
{
|
|
|
|
if (d->drag.surface) {
|
2020-06-25 15:27:52 +00:00
|
|
|
if (d->drag.surface->resource()) {
|
|
|
|
d->send_leave();
|
2016-03-01 06:49:04 +00:00
|
|
|
}
|
2018-09-12 01:33:07 +00:00
|
|
|
if (d->drag.posConnection) {
|
|
|
|
disconnect(d->drag.posConnection);
|
|
|
|
d->drag.posConnection = QMetaObject::Connection();
|
2016-03-01 06:49:04 +00:00
|
|
|
}
|
|
|
|
disconnect(d->drag.destroyConnection);
|
|
|
|
d->drag.destroyConnection = QMetaObject::Connection();
|
|
|
|
d->drag.surface = nullptr;
|
2017-11-26 15:31:57 +00:00
|
|
|
if (d->drag.sourceActionConnection) {
|
|
|
|
disconnect(d->drag.sourceActionConnection);
|
|
|
|
d->drag.sourceActionConnection = QMetaObject::Connection();
|
|
|
|
}
|
|
|
|
if (d->drag.targetActionConnection) {
|
|
|
|
disconnect(d->drag.targetActionConnection);
|
|
|
|
d->drag.targetActionConnection = QMetaObject::Connection();
|
|
|
|
}
|
2016-03-01 06:49:04 +00:00
|
|
|
// don't update serial, we need it
|
|
|
|
}
|
|
|
|
if (!surface) {
|
2018-01-27 13:12:41 +00:00
|
|
|
if (auto s = d->seat->dragSource()->dragSource()) {
|
|
|
|
s->dndAction(DataDeviceManagerInterface::DnDAction::None);
|
|
|
|
}
|
2016-03-01 06:49:04 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-02-06 08:26:43 +00:00
|
|
|
if (d->proxyRemoteSurface && d->proxyRemoteSurface == surface) {
|
|
|
|
// A proxy can not have the remote surface as target.
|
|
|
|
// TODO: do this for all client's surfaces?
|
|
|
|
return;
|
|
|
|
}
|
2017-11-26 15:31:57 +00:00
|
|
|
auto *source = d->seat->dragSource()->dragSource();
|
|
|
|
DataOfferInterface *offer = d->createDataOffer(source);
|
2016-03-01 06:49:04 +00:00
|
|
|
d->drag.surface = surface;
|
|
|
|
if (d->seat->isDragPointer()) {
|
2018-09-12 01:33:07 +00:00
|
|
|
d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this,
|
2016-03-01 06:49:04 +00:00
|
|
|
[this] {
|
|
|
|
const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
|
2020-06-25 15:27:52 +00:00
|
|
|
d->send_motion(d->seat->timestamp(),
|
2016-03-01 06:49:04 +00:00
|
|
|
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
|
2020-06-25 15:27:52 +00:00
|
|
|
wl_client_flush(d->resource()->client());
|
2016-03-01 06:49:04 +00:00
|
|
|
}
|
|
|
|
);
|
2018-09-12 01:33:07 +00:00
|
|
|
} else if (d->seat->isDragTouch()) {
|
|
|
|
d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this,
|
|
|
|
[this](qint32 id, quint32 serial, const QPointF &globalPosition) {
|
|
|
|
Q_UNUSED(id);
|
|
|
|
if (serial != d->drag.serial) {
|
|
|
|
// different touch down has been moved
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition);
|
2020-06-25 15:27:52 +00:00
|
|
|
d->send_motion(d->seat->timestamp(),
|
|
|
|
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
|
|
|
|
wl_client_flush(d->resource()->client());
|
2018-09-12 01:33:07 +00:00
|
|
|
}
|
|
|
|
);
|
2016-03-01 06:49:04 +00:00
|
|
|
}
|
|
|
|
d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this,
|
|
|
|
[this] {
|
2020-06-25 15:27:52 +00:00
|
|
|
d->send_leave();
|
2018-09-12 01:33:07 +00:00
|
|
|
if (d->drag.posConnection) {
|
|
|
|
disconnect(d->drag.posConnection);
|
2016-03-01 06:49:04 +00:00
|
|
|
}
|
2020-06-25 15:27:52 +00:00
|
|
|
d->drag = DataDeviceInterfacePrivate::Drag();
|
2016-03-01 06:49:04 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// TODO: handle touch position
|
|
|
|
const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
|
2020-06-25 15:27:52 +00:00
|
|
|
d->send_enter(serial, surface->resource(),
|
|
|
|
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
|
2017-11-26 15:31:57 +00:00
|
|
|
if (offer) {
|
2020-06-25 15:27:52 +00:00
|
|
|
offer->sendSourceActions();
|
2017-11-26 15:31:57 +00:00
|
|
|
auto matchOffers = [source, offer] {
|
|
|
|
DataDeviceManagerInterface::DnDAction action{DataDeviceManagerInterface::DnDAction::None};
|
|
|
|
if (source->supportedDragAndDropActions().testFlag(offer->preferredDragAndDropAction())) {
|
|
|
|
action = offer->preferredDragAndDropAction();
|
|
|
|
} else {
|
|
|
|
if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy) &&
|
|
|
|
offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)) {
|
|
|
|
action = DataDeviceManagerInterface::DnDAction::Copy;
|
|
|
|
} else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move) &&
|
|
|
|
offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)) {
|
|
|
|
action = DataDeviceManagerInterface::DnDAction::Move;
|
|
|
|
} else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask) &&
|
|
|
|
offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)) {
|
|
|
|
action = DataDeviceManagerInterface::DnDAction::Ask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offer->dndAction(action);
|
|
|
|
source->dndAction(action);
|
|
|
|
};
|
2020-06-18 10:00:38 +00:00
|
|
|
d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, source, matchOffers);
|
2017-11-26 15:31:57 +00:00
|
|
|
d->drag.sourceActionConnection = connect(source, &DataSourceInterface::supportedDragAndDropActionsChanged, source, matchOffers);
|
|
|
|
}
|
2020-06-25 15:27:52 +00:00
|
|
|
wl_client_flush(d->resource()->client());
|
2016-03-01 06:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
quint32 DataDeviceInterface::dragImplicitGrabSerial() const
|
|
|
|
{
|
|
|
|
return d->drag.serial;
|
|
|
|
}
|
|
|
|
|
2019-02-06 08:26:43 +00:00
|
|
|
void DataDeviceInterface::updateProxy(SurfaceInterface *remote)
|
|
|
|
{
|
|
|
|
// TODO: connect destroy signal?
|
|
|
|
d->proxyRemoteSurface = remote;
|
|
|
|
}
|
|
|
|
|
2020-06-25 15:27:52 +00:00
|
|
|
wl_client *DataDeviceInterface::client()
|
2014-11-14 08:45:02 +00:00
|
|
|
{
|
2020-06-25 15:27:52 +00:00
|
|
|
return d->resource()->client();
|
2014-11-06 15:56:50 +00:00
|
|
|
}
|
|
|
|
|
2014-11-06 09:02:49 +00:00
|
|
|
}
|