2014-11-06 09:02:49 +00:00
|
|
|
/********************************************************************
|
|
|
|
Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
*********************************************************************/
|
|
|
|
#include "datadevice_interface.h"
|
2014-11-14 08:45:02 +00:00
|
|
|
#include "datadevicemanager_interface.h"
|
2017-11-26 15:31:57 +00:00
|
|
|
#include "dataoffer_interface_p.h"
|
2014-11-06 09:02:49 +00:00
|
|
|
#include "datasource_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"
|
|
|
|
#include "surface_interface.h"
|
|
|
|
// Wayland
|
|
|
|
#include <wayland-server.h>
|
|
|
|
|
|
|
|
namespace KWayland
|
|
|
|
{
|
|
|
|
namespace Server
|
|
|
|
{
|
|
|
|
|
2014-11-14 08:45:02 +00:00
|
|
|
class DataDeviceInterface::Private : public Resource::Private
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
|
|
|
public:
|
2014-11-20 15:40:14 +00:00
|
|
|
Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource);
|
2014-11-06 09:02:49 +00:00
|
|
|
~Private();
|
|
|
|
|
2014-11-06 15:56:50 +00:00
|
|
|
DataOfferInterface *createDataOffer(DataSourceInterface *source);
|
|
|
|
|
2014-11-06 09:02:49 +00:00
|
|
|
SeatInterface *seat;
|
|
|
|
DataSourceInterface *source = nullptr;
|
|
|
|
SurfaceInterface *surface = nullptr;
|
|
|
|
SurfaceInterface *icon = nullptr;
|
|
|
|
|
|
|
|
DataSourceInterface *selection = nullptr;
|
2016-09-12 06:57:07 +00:00
|
|
|
QMetaObject::Connection selectionUnboundConnection;
|
|
|
|
QMetaObject::Connection selectionDestroyedConnection;
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2016-03-01 06:49:04 +00:00
|
|
|
struct Drag {
|
|
|
|
SurfaceInterface *surface = nullptr;
|
|
|
|
QMetaObject::Connection destroyConnection;
|
|
|
|
QMetaObject::Connection pointerPosConnection;
|
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;
|
|
|
|
|
2014-11-06 09:02:49 +00:00
|
|
|
private:
|
2014-11-14 09:55:06 +00:00
|
|
|
DataDeviceInterface *q_func() {
|
|
|
|
return reinterpret_cast<DataDeviceInterface*>(q);
|
|
|
|
}
|
2016-03-01 06:49:04 +00:00
|
|
|
void startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *icon, quint32 serial);
|
2014-11-06 09:02:49 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2015-09-09 14:39:50 +00:00
|
|
|
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
2014-11-06 09:02:49 +00:00
|
|
|
const struct wl_data_device_interface DataDeviceInterface::Private::s_interface = {
|
|
|
|
startDragCallback,
|
2015-09-09 07:34:51 +00:00
|
|
|
setSelectionCallback,
|
2016-05-25 08:04:17 +00:00
|
|
|
resourceDestroyedCallback
|
2014-11-06 09:02:49 +00:00
|
|
|
};
|
2015-09-09 14:39:50 +00:00
|
|
|
#endif
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2014-11-20 15:40:14 +00:00
|
|
|
DataDeviceInterface::Private::Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource)
|
|
|
|
: Resource::Private(q, manager, parentResource, &wl_data_device_interface, &s_interface)
|
2014-11-14 08:45:02 +00:00
|
|
|
, seat(seat)
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-11-14 08:45:02 +00:00
|
|
|
DataDeviceInterface::Private::~Private() = default;
|
2014-11-06 09:02:49 +00:00
|
|
|
|
|
|
|
void DataDeviceInterface::Private::startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial)
|
|
|
|
{
|
|
|
|
Q_UNUSED(client)
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
// TODO: verify serial
|
2016-03-01 06:49:04 +00:00
|
|
|
cast<Private>(resource)->startDrag(DataSourceInterface::get(source), SurfaceInterface::get(origin), SurfaceInterface::get(icon), serial);
|
2014-11-06 09:02:49 +00:00
|
|
|
}
|
|
|
|
|
2016-03-01 06:49:04 +00:00
|
|
|
void DataDeviceInterface::Private::startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *i, quint32 serial)
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
2016-03-01 06:49:04 +00:00
|
|
|
// TODO: allow touch
|
|
|
|
if (seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() != origin) {
|
2014-11-14 08:45:02 +00:00
|
|
|
wl_resource_post_error(resource, 0, "Surface doesn't have pointer grab");
|
2014-11-06 09:02:49 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-03-01 06:49:04 +00:00
|
|
|
// TODO: source is allowed to be null, handled client internally!
|
2018-01-27 13:12:41 +00:00
|
|
|
Q_Q(DataDeviceInterface);
|
2014-11-06 09:02:49 +00:00
|
|
|
source = dataSource;
|
2018-01-27 13:12:41 +00:00
|
|
|
if (dataSource) {
|
|
|
|
QObject::connect(dataSource, &Resource::aboutToBeUnbound, q, [this] { source = nullptr; });
|
|
|
|
}
|
2014-11-06 09:02:49 +00:00
|
|
|
surface = origin;
|
|
|
|
icon = i;
|
2016-03-01 06:49:04 +00:00
|
|
|
drag.serial = serial;
|
2014-11-06 09:02:49 +00:00
|
|
|
emit q->dragStarted();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DataDeviceInterface::Private::setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial)
|
|
|
|
{
|
|
|
|
Q_UNUSED(client)
|
|
|
|
Q_UNUSED(serial)
|
|
|
|
// TODO: verify serial
|
2014-11-14 09:20:43 +00:00
|
|
|
cast<Private>(resource)->setSelection(DataSourceInterface::get(source));
|
2014-11-06 09:02:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DataDeviceInterface::Private::setSelection(DataSourceInterface *dataSource)
|
|
|
|
{
|
2017-12-26 20:55:11 +00:00
|
|
|
if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
|
2017-11-26 15:31:57 +00:00
|
|
|
wl_resource_post_error(dataSource->resource(), WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "Data source is for drag and drop");
|
|
|
|
return;
|
|
|
|
}
|
2018-06-14 12:21:41 +00:00
|
|
|
if (selection == dataSource) {
|
|
|
|
return;
|
|
|
|
}
|
2014-11-14 09:55:06 +00:00
|
|
|
Q_Q(DataDeviceInterface);
|
2016-09-12 06:57:07 +00:00
|
|
|
QObject::disconnect(selectionUnboundConnection);
|
|
|
|
QObject::disconnect(selectionDestroyedConnection);
|
|
|
|
if (selection) {
|
|
|
|
selection->cancel();
|
|
|
|
}
|
2014-11-06 09:02:49 +00:00
|
|
|
selection = dataSource;
|
|
|
|
if (selection) {
|
2016-09-12 06:57:07 +00:00
|
|
|
auto clearSelection = [this] {
|
|
|
|
setSelection(nullptr);
|
|
|
|
};
|
|
|
|
selectionUnboundConnection = QObject::connect(selection, &Resource::unbound, q, clearSelection);
|
|
|
|
selectionDestroyedConnection = QObject::connect(selection, &QObject::destroyed, q, clearSelection);
|
2014-11-06 09:02:49 +00:00
|
|
|
emit q->selectionChanged(selection);
|
|
|
|
} else {
|
2016-09-12 06:57:07 +00:00
|
|
|
selectionUnboundConnection = QMetaObject::Connection();
|
|
|
|
selectionDestroyedConnection = QMetaObject::Connection();
|
2014-11-06 09:02:49 +00:00
|
|
|
emit q->selectionCleared();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-06 15:56:50 +00:00
|
|
|
DataOfferInterface *DataDeviceInterface::Private::createDataOffer(DataSourceInterface *source)
|
|
|
|
{
|
2016-06-14 12:46:39 +00:00
|
|
|
if (!resource) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2014-11-14 09:55:06 +00:00
|
|
|
Q_Q(DataDeviceInterface);
|
2014-11-20 15:40:14 +00:00
|
|
|
DataOfferInterface *offer = new DataOfferInterface(source, q, resource);
|
2014-11-19 15:53:56 +00:00
|
|
|
auto c = q->global()->display()->getConnection(wl_resource_get_client(resource));
|
|
|
|
offer->create(c, wl_resource_get_version(resource), 0);
|
2014-11-06 15:56:50 +00:00
|
|
|
if (!offer->resource()) {
|
|
|
|
// TODO: send error?
|
|
|
|
delete offer;
|
|
|
|
return nullptr;
|
|
|
|
}
|
2014-11-14 08:45:02 +00:00
|
|
|
wl_data_device_send_data_offer(resource, offer->resource());
|
2014-11-06 15:56:50 +00:00
|
|
|
offer->sendAllOffers();
|
|
|
|
return offer;
|
|
|
|
}
|
2014-11-06 09:02:49 +00:00
|
|
|
|
2014-11-20 15:40:14 +00:00
|
|
|
DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, DataDeviceManagerInterface *parent, wl_resource *parentResource)
|
|
|
|
: Resource(new Private(seat, this, parent, parentResource))
|
2014-11-06 09:02:49 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DataDeviceInterface::~DataDeviceInterface() = default;
|
|
|
|
|
|
|
|
SeatInterface *DataDeviceInterface::seat() const
|
|
|
|
{
|
2014-11-14 08:45:02 +00:00
|
|
|
Q_D();
|
2014-11-06 09:02:49 +00:00
|
|
|
return d->seat;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataSourceInterface *DataDeviceInterface::dragSource() const
|
|
|
|
{
|
2014-11-14 08:45:02 +00:00
|
|
|
Q_D();
|
2014-11-06 09:02:49 +00:00
|
|
|
return d->source;
|
|
|
|
}
|
|
|
|
|
|
|
|
SurfaceInterface *DataDeviceInterface::icon() const
|
|
|
|
{
|
2014-11-14 08:45:02 +00:00
|
|
|
Q_D();
|
2014-11-06 09:02:49 +00:00
|
|
|
return d->icon;
|
|
|
|
}
|
|
|
|
|
|
|
|
SurfaceInterface *DataDeviceInterface::origin() const
|
|
|
|
{
|
2014-11-14 08:45:02 +00:00
|
|
|
Q_D();
|
2014-11-06 09:02:49 +00:00
|
|
|
return d->surface;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataSourceInterface *DataDeviceInterface::selection() const
|
|
|
|
{
|
2014-11-14 08:45:02 +00:00
|
|
|
Q_D();
|
2014-11-06 09:02:49 +00:00
|
|
|
return d->selection;
|
|
|
|
}
|
|
|
|
|
2014-11-06 15:56:50 +00:00
|
|
|
void DataDeviceInterface::sendSelection(DataDeviceInterface *other)
|
|
|
|
{
|
2014-11-14 08:45:02 +00:00
|
|
|
Q_D();
|
2017-08-15 23:16:57 +00:00
|
|
|
auto otherSelection = other->selection();
|
|
|
|
if (!otherSelection) {
|
|
|
|
sendClearSelection();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto r = d->createDataOffer(otherSelection);
|
2014-11-06 15:56:50 +00:00
|
|
|
if (!r) {
|
|
|
|
return;
|
|
|
|
}
|
2014-11-27 13:02:54 +00:00
|
|
|
if (!d->resource) {
|
|
|
|
return;
|
|
|
|
}
|
2014-11-14 08:45:02 +00:00
|
|
|
wl_data_device_send_selection(d->resource, r->resource());
|
|
|
|
}
|
|
|
|
|
2014-11-27 12:38:24 +00:00
|
|
|
void DataDeviceInterface::sendClearSelection()
|
|
|
|
{
|
|
|
|
Q_D();
|
2014-11-27 13:02:54 +00:00
|
|
|
if (!d->resource) {
|
|
|
|
return;
|
|
|
|
}
|
2014-11-27 12:38:24 +00:00
|
|
|
wl_data_device_send_selection(d->resource, nullptr);
|
|
|
|
}
|
|
|
|
|
2016-03-01 06:49:04 +00:00
|
|
|
void DataDeviceInterface::drop()
|
|
|
|
{
|
|
|
|
Q_D();
|
|
|
|
if (!d->resource) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
wl_data_device_send_drop(d->resource);
|
2017-11-26 15:31:57 +00:00
|
|
|
if (d->drag.pointerPosConnection) {
|
|
|
|
disconnect(d->drag.pointerPosConnection);
|
|
|
|
d->drag.pointerPosConnection = QMetaObject::Connection();
|
|
|
|
}
|
|
|
|
disconnect(d->drag.destroyConnection);
|
|
|
|
d->drag.destroyConnection = QMetaObject::Connection();
|
|
|
|
d->drag.surface = nullptr;
|
2016-03-01 06:49:04 +00:00
|
|
|
client()->flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
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.pointerPosConnection) {
|
|
|
|
disconnect(d->drag.pointerPosConnection);
|
|
|
|
d->drag.pointerPosConnection = QMetaObject::Connection();
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
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()) {
|
|
|
|
d->drag.pointerPosConnection = 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(),
|
|
|
|
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
|
|
|
|
client()->flush();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// TODO: same for touch
|
|
|
|
d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this,
|
|
|
|
[this] {
|
|
|
|
Q_D();
|
|
|
|
if (d->resource) {
|
|
|
|
wl_data_device_send_leave(d->resource);
|
|
|
|
}
|
|
|
|
if (d->drag.pointerPosConnection) {
|
|
|
|
disconnect(d->drag.pointerPosConnection);
|
|
|
|
}
|
|
|
|
d->drag = Private::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);
|
2017-11-26 15:31:57 +00:00
|
|
|
if (offer) {
|
|
|
|
offer->d_func()->sendSourceActions();
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, offer, matchOffers);
|
|
|
|
d->drag.sourceActionConnection = connect(source, &DataSourceInterface::supportedDragAndDropActionsChanged, source, matchOffers);
|
|
|
|
}
|
2016-03-01 06:49:04 +00:00
|
|
|
d->client->flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
quint32 DataDeviceInterface::dragImplicitGrabSerial() const
|
|
|
|
{
|
|
|
|
Q_D();
|
|
|
|
return d->drag.serial;
|
|
|
|
}
|
|
|
|
|
2014-11-14 08:45:02 +00:00
|
|
|
DataDeviceInterface::Private *DataDeviceInterface::d_func() const
|
|
|
|
{
|
|
|
|
return reinterpret_cast<DataDeviceInterface::Private*>(d.data());
|
2014-11-06 15:56:50 +00:00
|
|
|
}
|
|
|
|
|
2014-11-06 09:02:49 +00:00
|
|
|
}
|
|
|
|
}
|