datadevice_interface.cpp 11.7 KB
Newer Older
1
2
/*
    SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3
    SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
4

5
6
    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
7
#include "datadevice_interface.h"
8
#include "datadevicemanager_interface.h"
9
#include "datasource_interface.h"
10
#include "dataoffer_interface.h"
11
#include "display.h"
12
#include "resource_p.h"
13
#include "pointer_interface.h"
14
#include "seat_interface.h"
15
#include "seat_interface_p.h"
16
#include "surface_interface.h"
17
#include <qwayland-server-wayland.h>
18

19
namespace KWaylandServer
20
21
{

22
class DataDeviceInterfacePrivate : public QtWaylandServer::wl_data_device
23
24
{
public:
25
    DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource);
26

27
    DataOfferInterface *createDataOffer(AbstractDataSource *source);
28

29
30
31
    SeatInterface *seat;
    DataSourceInterface *source = nullptr;
    SurfaceInterface *surface = nullptr;
32
    QPointer<SurfaceInterface> icon;
33
    QPointer<DataSourceInterface> selection;
34

35
36
37
    struct Drag {
        SurfaceInterface *surface = nullptr;
        QMetaObject::Connection destroyConnection;
Roman Gilg's avatar
Roman Gilg committed
38
        QMetaObject::Connection posConnection;
39
40
        QMetaObject::Connection sourceActionConnection;
        QMetaObject::Connection targetActionConnection;
41
42
43
44
        quint32 serial = 0;
    };
    Drag drag;

45
    DataDeviceInterface *q;
46

47
    QPointer<SurfaceInterface> proxyRemoteSurface;
48

49
50
51
52
53
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;
54
55
};

56
57
DataDeviceInterfacePrivate::DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource)
    : QtWaylandServer::wl_data_device(resource)
58
    , seat(seat)
59
    , q(_q)
60
61
62
{
}

63
void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource, wl_resource *sourceResource, wl_resource *originResource, wl_resource *iconResource, uint32_t serial)
64
{
65
66
67
68
69
70
71
    Q_UNUSED(resource)
    SurfaceInterface *focusSurface = SurfaceInterface::get(originResource);
    SurfaceInterface *i = SurfaceInterface::get(iconResource);
    DataSourceInterface *dataSource = nullptr;
    if (sourceResource) {
        dataSource = DataSourceInterface::get(sourceResource);
    }
72

73
74
    if (proxyRemoteSurface) {
        // origin is a proxy surface
75
        focusSurface = proxyRemoteSurface.data();
76
77
    }
    const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface;
Roman Gilg's avatar
Roman Gilg committed
78
79
    if (!pointerGrab) {
        // Client doesn't have pointer grab.
80
        const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface;
Roman Gilg's avatar
Roman Gilg committed
81
82
83
84
        if (!touchGrab) {
            // Client neither has pointer nor touch grab. No drag start allowed.
            return;
        }
85
    }
86
    // TODO: source is allowed to be null, handled client internally!
87
    source = dataSource;
88
    if (dataSource) {
89
        QObject::connect(dataSource, &AbstractDataSource::aboutToBeDestroyed, q, [this] { source = nullptr; });
90
    }
91
    surface = focusSurface;
92
    icon = i;
93
    drag.serial = serial;
94
95
96
    emit q->dragStarted();
}

97
void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial)
98
{
99
    Q_UNUSED(resource)
100
    Q_UNUSED(serial)
101
    DataSourceInterface *dataSource = DataSourceInterface::get(source);
102

103
    if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
104
        wl_resource_post_error(dataSource->resource(), QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
105
106
        return;
    }
107

108
109
110
    if (selection == dataSource) {
        return;
    }
111
112
113
    if (selection) {
        selection->cancel();
    }
114
115
116
117
118
119
120
121
    selection = dataSource;
    if (selection) {
        emit q->selectionChanged(selection);
    } else {
        emit q->selectionCleared();
    }
}

122
123
124
125
126
127
128
void DataDeviceInterfacePrivate::data_device_release(QtWaylandServer::wl_data_device::Resource *resource)
{
    wl_resource_destroy(resource->handle);
}


DataOfferInterface *DataDeviceInterfacePrivate::createDataOffer(AbstractDataSource *source)
129
{
130
131
132
133
    if (!source) {
        // a data offer can only exist together with a source
        return nullptr;
    }
134
135
136
137

    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);
138
139
        return nullptr;
    }
140
141
142

    DataOfferInterface *offer = new DataOfferInterface(source, data_offer_resource);
    send_data_offer(offer->resource());
143
144
145
    offer->sendAllOffers();
    return offer;
}
146

147
148
149
150
151
152
153
154
155
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))
156
{
157
    seat->d_func()->registerDataDevice(this);
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
}

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
{
179
    return d->proxyRemoteSurface ? d->proxyRemoteSurface.data() : d->surface;
180
181
182
183
184
185
186
}

DataSourceInterface *DataDeviceInterface::selection() const
{
    return d->selection;
}

187
void DataDeviceInterface::sendSelection(AbstractDataSource *other)
188
{
189
    auto r = d->createDataOffer(other);
190
191
192
    if (!r) {
        return;
    }
193
    d->send_selection(r->resource());
194
195
}

196
197
void DataDeviceInterface::sendClearSelection()
{
198
    d->send_selection(nullptr);
199
200
}

201
202
void DataDeviceInterface::drop()
{
203
    d->send_drop();
Roman Gilg's avatar
Roman Gilg committed
204
205
206
    if (d->drag.posConnection) {
        disconnect(d->drag.posConnection);
        d->drag.posConnection = QMetaObject::Connection();
207
208
209
210
    }
    disconnect(d->drag.destroyConnection);
    d->drag.destroyConnection = QMetaObject::Connection();
    d->drag.surface = nullptr;
211
    wl_client_flush(d->resource()->client());
212
213
214
215
216
}

void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 serial)
{
    if (d->drag.surface) {
217
218
        if (d->drag.surface->resource()) {
            d->send_leave();
219
        }
Roman Gilg's avatar
Roman Gilg committed
220
221
222
        if (d->drag.posConnection) {
            disconnect(d->drag.posConnection);
            d->drag.posConnection = QMetaObject::Connection();
223
224
225
226
        }
        disconnect(d->drag.destroyConnection);
        d->drag.destroyConnection = QMetaObject::Connection();
        d->drag.surface = nullptr;
227
228
229
230
231
232
233
234
        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();
        }
235
236
237
        // don't update serial, we need it
    }
    if (!surface) {
238
239
240
        if (auto s = d->seat->dragSource()->dragSource()) {
            s->dndAction(DataDeviceManagerInterface::DnDAction::None);
        }
241
242
        return;
    }
243
244
245
246
247
    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;
    }
248
    auto *source = d->seat->dragSource()->dragSource();
249
250
251
    if (source) {
        source->setAccepted(false);
    }
252
    DataOfferInterface *offer = d->createDataOffer(source);
253
254
    d->drag.surface = surface;
    if (d->seat->isDragPointer()) {
Roman Gilg's avatar
Roman Gilg committed
255
        d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this,
256
257
            [this] {
                const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
258
                d->send_motion(d->seat->timestamp(),
259
                                        wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
260
                wl_client_flush(d->resource()->client());
261
262
            }
        );
Roman Gilg's avatar
Roman Gilg committed
263
264
265
266
267
268
269
270
271
    } 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);
272
273
274
                d->send_motion(d->seat->timestamp(),
                               wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
                wl_client_flush(d->resource()->client());
Roman Gilg's avatar
Roman Gilg committed
275
276
            }
        );
277
278
279
    }
    d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this,
        [this] {
280
            d->send_leave();
Roman Gilg's avatar
Roman Gilg committed
281
282
            if (d->drag.posConnection) {
                disconnect(d->drag.posConnection);
283
            }
284
            d->drag = DataDeviceInterfacePrivate::Drag();
285
286
287
288
289
        }
    );

    // TODO: handle touch position
    const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
290
291
    d->send_enter(serial, surface->resource(),
                  wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
292
    if (offer) {
293
        offer->sendSourceActions();
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
        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);
        };
313
        d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, source, matchOffers);
314
315
        d->drag.sourceActionConnection = connect(source, &DataSourceInterface::supportedDragAndDropActionsChanged, source, matchOffers);
    }
316
    wl_client_flush(d->resource()->client());
317
318
319
320
321
322
323
}

quint32 DataDeviceInterface::dragImplicitGrabSerial() const
{
    return d->drag.serial;
}

324
325
326
327
328
329
void DataDeviceInterface::updateProxy(SurfaceInterface *remote)
{
    // TODO: connect destroy signal?
    d->proxyRemoteSurface = remote;
}

330
wl_client *DataDeviceInterface::client()
331
{
332
    return d->resource()->client();
333
334
}

335
}