KWaylandServer

datadevice_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
3  SPDX-FileCopyrightText: 2020 David Edmundson <[email protected]>
4  SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8 #include "datadevice_interface.h"
9 #include "datadevice_interface_p.h"
10 #include "datadevicemanager_interface.h"
11 #include "dataoffer_interface.h"
12 #include "datasource_interface.h"
13 #include "display.h"
14 #include "pointer_interface.h"
15 #include "seat_interface.h"
16 #include "seat_interface_p.h"
17 #include "surface_interface.h"
18 #include "surfacerole_p.h"
19 
20 namespace KWaylandServer
21 {
22 class DragAndDropIconPrivate : public SurfaceRole
23 {
24 public:
25  explicit DragAndDropIconPrivate(SurfaceInterface *surface);
26 
27  void commit() override;
28 
29  QPoint position;
30 };
31 
32 DragAndDropIconPrivate::DragAndDropIconPrivate(SurfaceInterface *surface)
33  : SurfaceRole(surface, QByteArrayLiteral("dnd_icon"))
34 {
35 }
36 
37 void DragAndDropIconPrivate::commit()
38 {
39  position += surface()->offset();
40 }
41 
42 DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface)
43  : QObject(surface)
44  , d(new DragAndDropIconPrivate(surface))
45 {
46 }
47 
48 DragAndDropIcon::~DragAndDropIcon()
49 {
50 }
51 
53 {
54  return d->position;
55 }
56 
58 {
59  return d->surface();
60 }
61 
62 DataDeviceInterfacePrivate *DataDeviceInterfacePrivate::get(DataDeviceInterface *device)
63 {
64  return device->d.data();
65 }
66 
67 DataDeviceInterfacePrivate::DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource)
68  : QtWaylandServer::wl_data_device(resource)
69  , seat(seat)
70  , q(_q)
71 {
72 }
73 
74 void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource,
75  wl_resource *sourceResource,
76  wl_resource *originResource,
77  wl_resource *iconResource,
78  uint32_t serial)
79 {
80  SurfaceInterface *iconSurface = SurfaceInterface::get(iconResource);
81 
82  const SurfaceRole *surfaceRole = SurfaceRole::get(iconSurface);
83  if (surfaceRole) {
84  wl_resource_post_error(resource->handle, error_role, "the icon surface already has a role assigned %s", surfaceRole->name().constData());
85  return;
86  }
87 
88  SurfaceInterface *focusSurface = SurfaceInterface::get(originResource);
89  DataSourceInterface *dataSource = nullptr;
90  if (sourceResource) {
91  dataSource = DataSourceInterface::get(sourceResource);
92  }
93 
94  const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface;
95  if (!pointerGrab) {
96  // Client doesn't have pointer grab.
97  const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface;
98  if (!touchGrab) {
99  // Client neither has pointer nor touch grab. No drag start allowed.
100  return;
101  }
102  }
103 
104  DragAndDropIcon *dragIcon = nullptr;
105  if (iconSurface) {
106  // drag icon lifespan is mapped to surface lifespan
107  dragIcon = new DragAndDropIcon(iconSurface);
108  }
109  drag.serial = serial;
110  Q_EMIT q->dragStarted(dataSource, focusSurface, serial, dragIcon);
111 }
112 
113 void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial)
114 {
115  Q_UNUSED(resource)
116  Q_UNUSED(serial)
117  DataSourceInterface *dataSource = DataSourceInterface::get(source);
118 
119  if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
120  wl_resource_post_error(dataSource->resource(), QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
121  return;
122  }
123 
124  if (selection == dataSource) {
125  return;
126  }
127  if (selection) {
128  selection->cancel();
129  }
130  selection = dataSource;
131  if (selection) {
132  Q_EMIT q->selectionChanged(selection);
133  } else {
134  Q_EMIT q->selectionCleared();
135  }
136 }
137 
138 void DataDeviceInterfacePrivate::data_device_release(QtWaylandServer::wl_data_device::Resource *resource)
139 {
140  wl_resource_destroy(resource->handle);
141 }
142 
143 DataOfferInterface *DataDeviceInterfacePrivate::createDataOffer(AbstractDataSource *source)
144 {
145  if (!source) {
146  // a data offer can only exist together with a source
147  return nullptr;
148  }
149 
150  wl_resource *data_offer_resource = wl_resource_create(resource()->client(), &wl_data_offer_interface, resource()->version(), 0);
151  if (!data_offer_resource) {
152  wl_resource_post_no_memory(resource()->handle);
153  return nullptr;
154  }
155 
156  DataOfferInterface *offer = new DataOfferInterface(source, data_offer_resource);
157  send_data_offer(offer->resource());
158  offer->sendAllOffers();
159  return offer;
160 }
161 
162 void DataDeviceInterfacePrivate::data_device_destroy_resource(QtWaylandServer::wl_data_device::Resource *resource)
163 {
164  Q_UNUSED(resource)
165  Q_EMIT q->aboutToBeDestroyed();
166  delete q;
167 }
168 
169 DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, wl_resource *resource)
170  : AbstractDropHandler(nullptr)
171  , d(new DataDeviceInterfacePrivate(seat, this, resource))
172 {
173  SeatInterfacePrivate *seatPrivate = SeatInterfacePrivate::get(seat);
174  seatPrivate->registerDataDevice(this);
175 }
176 
177 DataDeviceInterface::~DataDeviceInterface() = default;
178 
179 SeatInterface *DataDeviceInterface::seat() const
180 {
181  return d->seat;
182 }
183 
184 DataSourceInterface *DataDeviceInterface::selection() const
185 {
186  return d->selection;
187 }
188 
189 void DataDeviceInterface::sendSelection(AbstractDataSource *other)
190 {
191  auto r = d->createDataOffer(other);
192  if (!r) {
193  return;
194  }
195  d->send_selection(r->resource());
196 }
197 
198 void DataDeviceInterface::sendClearSelection()
199 {
200  d->send_selection(nullptr);
201 }
202 
204 {
205  d->send_drop();
206  if (d->drag.posConnection) {
207  disconnect(d->drag.posConnection);
208  d->drag.posConnection = QMetaObject::Connection();
209  }
210  disconnect(d->drag.destroyConnection);
211  d->drag.destroyConnection = QMetaObject::Connection();
212  d->drag.surface = nullptr;
213 }
214 
216 {
217  if (d->drag.surface) {
218  if (d->drag.surface->resource()) {
219  d->send_leave();
220  }
221  if (d->drag.posConnection) {
222  disconnect(d->drag.posConnection);
223  d->drag.posConnection = QMetaObject::Connection();
224  }
225  disconnect(d->drag.destroyConnection);
226  d->drag.destroyConnection = QMetaObject::Connection();
227  d->drag.surface = nullptr;
228  if (d->drag.sourceActionConnection) {
229  disconnect(d->drag.sourceActionConnection);
230  d->drag.sourceActionConnection = QMetaObject::Connection();
231  }
232  if (d->drag.targetActionConnection) {
233  disconnect(d->drag.targetActionConnection);
234  d->drag.targetActionConnection = QMetaObject::Connection();
235  }
236  // don't update serial, we need it
237  }
238  auto dragSource = d->seat->dragSource();
239  if (!surface || !dragSource) {
240  if (auto s = dragSource) {
241  s->dndAction(DataDeviceManagerInterface::DnDAction::None);
242  }
243  return;
244  }
245 
246  if (dragSource) {
247  dragSource->accept(QString());
248  }
249  DataOfferInterface *offer = d->createDataOffer(dragSource);
250  d->drag.surface = surface;
251  if (d->seat->isDragPointer()) {
252  d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] {
253  const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
254  d->send_motion(d->seat->timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
255  });
256  } else if (d->seat->isDragTouch()) {
257  d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this, [this](qint32 id, quint32 serial, const QPointF &globalPosition) {
258  Q_UNUSED(id);
259  if (serial != d->drag.serial) {
260  // different touch down has been moved
261  return;
262  }
263  const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition);
264  d->send_motion(d->seat->timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
265  });
266  }
267  d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this, [this] {
268  d->send_leave();
269  if (d->drag.posConnection) {
270  disconnect(d->drag.posConnection);
271  }
272  d->drag = DataDeviceInterfacePrivate::Drag();
273  });
274 
275  // TODO: handle touch position
276  const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
277  d->send_enter(serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
278  if (offer) {
279  offer->sendSourceActions();
280  auto matchOffers = [dragSource, offer] {
281  DataDeviceManagerInterface::DnDAction action{DataDeviceManagerInterface::DnDAction::None};
282  if (dragSource->supportedDragAndDropActions().testFlag(offer->preferredDragAndDropAction())) {
283  action = offer->preferredDragAndDropAction();
284  } else {
285  if (dragSource->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)
286  && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)) {
287  action = DataDeviceManagerInterface::DnDAction::Copy;
288  } else if (dragSource->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)
289  && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)) {
290  action = DataDeviceManagerInterface::DnDAction::Move;
291  } else if (dragSource->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)
292  && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)) {
293  action = DataDeviceManagerInterface::DnDAction::Ask;
294  }
295  }
296  offer->dndAction(action);
297  dragSource->dndAction(action);
298  };
299  d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, dragSource, matchOffers);
300  d->drag.sourceActionConnection = connect(dragSource, &AbstractDataSource::supportedDragAndDropActionsChanged, dragSource, matchOffers);
301  }
302 }
303 
304 void DataDeviceInterface::updateProxy(SurfaceInterface *remote)
305 {
306  // TODO: connect destroy signal?
307  d->proxyRemoteSurface = remote;
308 }
309 
310 wl_client *DataDeviceInterface::client()
311 {
312  return d->resource()->client();
313 }
314 
315 }
The AbstractDataSource class abstracts the data that can be transferred to another client...
Represents a Seat on the Wayland Display.
void drop() override
The event is sent when a drag-and-drop operation is ended because the implicit grab is removed...
DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const override
bool hasImplicitTouchGrab(quint32 serial) const
bool hasImplicitPointerGrab(quint32 serial) const
void dragAndDropActionsChanged()
Emitted whenever the supported or preferred Drag and Drop actions changed.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
Represents the Resource for the wl_data_offer interface.
void updateDragTarget(SurfaceInterface *surface, quint32 serial) override
Updates the SurfaceInterface to which drag motion events are sent.
SurfaceInterface * focusedPointerSurface() const
QPoint position() const
Returns the position of the icon relative to the cursor&#39;s hotspot.
static SurfaceInterface * get(wl_resource *native)
DataDeviceInterface allows clients to share data by copy-and-paste and drag-and-drop.
DnDAction
Drag and Drop actions supported by the DataSourceInterface.
T * data() const const
Represents the Resource for the wl_data_source interface.
SurfaceInterface * surface() const
Returns the underlying icon surface.
Resource representing a wl_surface.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
The DragAndDropIcon class represents a drag-and-drop icon.
void destroyed(QObject *obj)
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 23 2021 23:08:27 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.