KWin
Loading...
Searching...
No Matches
datadevice.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
4 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7*/
8
9#include "datadevice.h"
10#include "datadevice_p.h"
11#include "datadevicemanager.h"
12#include "dataoffer.h"
13#include "datasource.h"
14#include "display.h"
15#include "pointer.h"
16#include "seat.h"
17#include "seat_p.h"
18#include "surface.h"
19
20namespace KWin
21{
23{
24public:
26
27 QPointer<SurfaceInterface> surface;
28 QPoint position;
29};
30
35
36DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface)
37 : QObject(surface)
38 , d(new DragAndDropIconPrivate(surface))
39{
40 connect(surface, &SurfaceInterface::committed, this, &DragAndDropIcon::commit);
41}
42
46
48{
49 static SurfaceRole role(QByteArrayLiteral("dnd_icon"));
50 return &role;
51}
52
53void DragAndDropIcon::commit()
54{
55 d->position += d->surface->offset();
56 Q_EMIT changed();
57}
58
60{
61 return d->position;
62}
63
65{
66 return d->surface;
67}
68
73
75 : QtWaylandServer::wl_data_device(resource)
76 , seat(seat)
77 , q(_q)
78{
79}
80
82 wl_resource *sourceResource,
83 wl_resource *originResource,
84 wl_resource *iconResource,
85 uint32_t serial)
86{
87 SurfaceInterface *focusSurface = SurfaceInterface::get(originResource);
88 DataSourceInterface *dataSource = nullptr;
89 if (sourceResource) {
90 dataSource = DataSourceInterface::get(sourceResource);
91 }
92
93 const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface;
94 if (!pointerGrab) {
95 // Client doesn't have pointer grab.
96 const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface;
97 if (!touchGrab) {
98 // Client neither has pointer nor touch grab. No drag start allowed.
99 return;
100 }
101 }
102
103 DragAndDropIcon *dragIcon = nullptr;
104 if (SurfaceInterface *iconSurface = SurfaceInterface::get(iconResource)) {
105 if (const SurfaceRole *role = iconSurface->role()) {
106 if (role != DragAndDropIcon::role()) {
107 wl_resource_post_error(resource->handle, 0,
108 "the wl_surface already has a role assigned %s", role->name().constData());
109 return;
110 }
111 } else {
112 iconSurface->setRole(DragAndDropIcon::role());
113 }
114
115 // drag icon lifespan is mapped to surface lifespan
116 dragIcon = new DragAndDropIcon(iconSurface);
117 }
118 drag.serial = serial;
119 Q_EMIT q->dragStarted(dataSource, focusSurface, serial, dragIcon);
120}
121
122void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial)
123{
124 DataSourceInterface *dataSource = DataSourceInterface::get(source);
125
126 if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
127 wl_resource_post_error(dataSource->resource(), QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
128 return;
129 }
130
131 if (dataSource && dataSource->xdgToplevelDrag()) {
132 wl_resource_post_error(resource->handle, QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
133 return;
134 }
135
136 if (selection == dataSource) {
137 return;
138 }
139 if (selection) {
140 selection->cancel();
141 }
142 selection = dataSource;
144}
145
146void DataDeviceInterfacePrivate::data_device_release(QtWaylandServer::wl_data_device::Resource *resource)
147{
148 wl_resource_destroy(resource->handle);
149}
150
152{
153 if (!source) {
154 // a data offer can only exist together with a source
155 return nullptr;
156 }
157
158 wl_resource *data_offer_resource = wl_resource_create(resource()->client(), &wl_data_offer_interface, resource()->version(), 0);
159 if (!data_offer_resource) {
160 wl_resource_post_no_memory(resource()->handle);
161 return nullptr;
162 }
163
164 DataOfferInterface *offer = new DataOfferInterface(source, data_offer_resource);
165 send_data_offer(offer->resource());
166 offer->sendAllOffers();
167 offer->sendSourceActions();
168 return offer;
169}
170
171void DataDeviceInterfacePrivate::data_device_destroy_resource(QtWaylandServer::wl_data_device::Resource *resource)
172{
173 Q_EMIT q->aboutToBeDestroyed();
174 delete q;
175}
176
177DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, wl_resource *resource)
178 : AbstractDropHandler(nullptr)
179 , d(new DataDeviceInterfacePrivate(seat, this, resource))
180{
182 seatPrivate->registerDataDevice(this);
183}
184
186
188{
189 return d->seat;
190}
191
193{
194 return d->selection;
195}
196
198{
199 auto r = other ? d->createDataOffer(other) : nullptr;
200 d->send_selection(r ? r->resource() : nullptr);
201}
202
204{
205 d->send_drop();
206 d->drag.surface = nullptr; // prevent sending wl_data_device.leave event
207
208 disconnect(d->drag.posConnection);
209 d->drag.posConnection = QMetaObject::Connection();
210 disconnect(d->drag.destroyConnection);
211 d->drag.destroyConnection = QMetaObject::Connection();
212
213 if (d->seat->dragSource()->selectedDndAction() != DataDeviceManagerInterface::DnDAction::Ask) {
214 disconnect(d->drag.sourceActionConnection);
215 d->drag.sourceActionConnection = QMetaObject::Connection();
216 disconnect(d->drag.targetActionConnection);
217 d->drag.targetActionConnection = QMetaObject::Connection();
218 }
219}
220
222{
223 if (offer->preferredDragAndDropAction().has_value()) {
224 if (source->supportedDragAndDropActions().testFlag(*offer->preferredDragAndDropAction())) {
225 return *offer->preferredDragAndDropAction();
226 }
227 }
228
229 if (offer->supportedDragAndDropActions().has_value()) {
230 for (const auto &action : {DataDeviceManagerInterface::DnDAction::Copy, DataDeviceManagerInterface::DnDAction::Move, DataDeviceManagerInterface::DnDAction::Ask}) {
231 if (source->supportedDragAndDropActions().testFlag(action) && offer->supportedDragAndDropActions()->testFlag(action)) {
232 return action;
233 }
234 }
235 }
236
237 return DataDeviceManagerInterface::DnDAction::None;
238}
239
241{
242 if (d->drag.surface == surface) {
243 return;
244 }
245
246 if (d->drag.surface) {
247 if (d->drag.surface->resource()) {
248 d->send_leave();
249 }
250 if (d->drag.posConnection) {
251 disconnect(d->drag.posConnection);
252 d->drag.posConnection = QMetaObject::Connection();
253 }
254 disconnect(d->drag.destroyConnection);
255 d->drag.destroyConnection = QMetaObject::Connection();
256 d->drag.surface = nullptr;
257 if (d->drag.sourceActionConnection) {
258 disconnect(d->drag.sourceActionConnection);
259 d->drag.sourceActionConnection = QMetaObject::Connection();
260 }
261 if (d->drag.targetActionConnection) {
262 disconnect(d->drag.targetActionConnection);
263 d->drag.targetActionConnection = QMetaObject::Connection();
264 }
265 // don't update serial, we need it
266 }
267 auto dragSource = d->seat->dragSource();
268 if (!surface || !dragSource) {
269 if (auto s = dragSource) {
270 s->dndAction(DataDeviceManagerInterface::DnDAction::None);
271 }
272 return;
273 }
274
275 if (dragSource) {
276 dragSource->accept(QString());
277 }
278 DataOfferInterface *offer = d->createDataOffer(dragSource);
279 d->drag.surface = surface;
280 if (d->seat->isDragPointer()) {
281 d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] {
282 const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
283 d->send_motion(d->seat->timestamp().count(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
284 });
285 } else if (d->seat->isDragTouch()) {
286 // When dragging from one window to another, we may end up in a data_device
287 // that didn't get "data_device_start_drag". In that case, the internal
288 // touch point serial will be incorrect and we need to update it to the
289 // serial from the seat.
291 if (seatPrivate->drag.dragImplicitGrabSerial != d->drag.serial) {
292 d->drag.serial = seatPrivate->drag.dragImplicitGrabSerial.value();
293 }
294
295 d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this, [this](qint32 id, quint32 serial, const QPointF &globalPosition) {
296 if (serial != d->drag.serial) {
297 // different touch down has been moved
298 return;
299 }
300 const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition);
301 d->send_motion(d->seat->timestamp().count(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
302 });
303 }
304 d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this, [this] {
305 d->send_leave();
306 if (d->drag.posConnection) {
307 disconnect(d->drag.posConnection);
308 }
310 });
311
312 QPointF pos;
313 if (d->seat->isDragPointer()) {
314 pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
315 } else if (d->seat->isDragTouch()) {
316 pos = d->seat->dragSurfaceTransformation().map(d->seat->firstTouchPointPosition());
317 }
318 d->send_enter(serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
319 if (offer) {
320 auto matchOffers = [dragSource, offer] {
321 const DataDeviceManagerInterface::DnDAction action = chooseDndAction(dragSource, offer);
322 offer->dndAction(action);
323 dragSource->dndAction(action);
324 };
325 d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, dragSource, matchOffers);
326 d->drag.sourceActionConnection = connect(dragSource, &AbstractDataSource::supportedDragAndDropActionsChanged, dragSource, matchOffers);
327 }
328}
329
330wl_client *DataDeviceInterface::client()
331{
332 return d->resource()->client();
333}
334
335}
336
337#include "moc_datadevice.cpp"
The AbstractDataSource class abstracts the data that can be transferred to another client.
virtual DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const
DataDeviceInterface allows clients to share data by copy-and-paste and drag-and-drop.
Definition datadevice.h:80
void selectionChanged(KWin::DataSourceInterface *)
void dragStarted(AbstractDataSource *source, SurfaceInterface *originSurface, quint32 serial, DragAndDropIcon *dragIcon)
SeatInterface * seat() const
DataSourceInterface * selection() const
void sendSelection(KWin::AbstractDataSource *other)
void updateDragTarget(SurfaceInterface *surface, quint32 serial) override
static DataDeviceInterfacePrivate * get(DataDeviceInterface *device)
void data_device_start_drag(Resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial) override
void data_device_release(Resource *resource) override
void data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial) override
QPointer< DataSourceInterface > selection
void data_device_destroy_resource(Resource *resource) override
DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource)
DataOfferInterface * createDataOffer(AbstractDataSource *source)
Represents the Resource for the wl_data_offer interface.
Definition dataoffer.h:28
std::optional< DataDeviceManagerInterface::DnDAction > preferredDragAndDropAction() const
std::optional< DataDeviceManagerInterface::DnDActions > supportedDragAndDropActions() const
void dndAction(DataDeviceManagerInterface::DnDAction action)
wl_resource * resource() const
Represents the Resource for the wl_data_source interface.
Definition datasource.h:23
wl_resource * resource() const
static DataSourceInterface * get(wl_resource *native)
DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const override
XdgToplevelDragV1Interface * xdgToplevelDrag() const
SurfaceInterface * surface() const
~DragAndDropIcon() override
QPoint position() const
static SurfaceRole * role()
DragAndDropIconPrivate(SurfaceInterface *surface)
QPointer< SurfaceInterface > surface
Represents a Seat on the Wayland Display.
Definition seat.h:134
SurfaceInterface * focusedTouchSurface() const
Definition seat.cpp:998
bool hasImplicitTouchGrab(quint32 serial) const
Definition seat.cpp:1173
void touchMoved(qint32 id, quint32 serial, const QPointF &globalPosition)
SurfaceInterface * focusedPointerSurface() const
Definition seat.cpp:550
bool hasImplicitPointerGrab(quint32 serial) const
Definition seat.cpp:1197
void pointerPosChanged(const QPointF &pos)
static KWIN_EXPORT SeatInterfacePrivate * get(SeatInterface *seat)
Definition seat.cpp:44
void registerDataDevice(DataDeviceInterface *dataDevice)
Definition seat.cpp:144
Resource representing a wl_surface.
Definition surface.h:80
static SurfaceInterface * get(wl_resource *native)
Definition surface.cpp:819
constexpr int version
std::optional< quint32 > dragImplicitGrabSerial
Definition seat_p.h:141