KWayland

datadevice_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "datadevice_interface.h"
7 #include "datadevicemanager_interface.h"
8 #include "dataoffer_interface_p.h"
9 #include "datasource_interface.h"
10 #include "display.h"
11 #include "resource_p.h"
12 #include "pointer_interface.h"
13 #include "seat_interface.h"
14 #include "surface_interface.h"
15 // Wayland
16 #include <wayland-server.h>
17 
18 namespace KWayland
19 {
20 namespace Server
21 {
22 
23 class DataDeviceInterface::Private : public Resource::Private
24 {
25 public:
26  Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource);
27  ~Private();
28 
29  DataOfferInterface *createDataOffer(DataSourceInterface *source);
30 
31  SeatInterface *seat;
32  DataSourceInterface *source = nullptr;
33  SurfaceInterface *surface = nullptr;
34  SurfaceInterface *icon = nullptr;
35 
36  DataSourceInterface *selection = nullptr;
37  QMetaObject::Connection selectionUnboundConnection;
38  QMetaObject::Connection selectionDestroyedConnection;
39 
40  struct Drag {
41  SurfaceInterface *surface = nullptr;
42  QMetaObject::Connection destroyConnection;
43  QMetaObject::Connection posConnection;
44  QMetaObject::Connection sourceActionConnection;
45  QMetaObject::Connection targetActionConnection;
46  quint32 serial = 0;
47  };
48  Drag drag;
49 
50  QPointer<SurfaceInterface> proxyRemoteSurface;
51 
52 private:
53  DataDeviceInterface *q_func() {
54  return reinterpret_cast<DataDeviceInterface*>(q);
55  }
56  void startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *icon, quint32 serial);
57  void setSelection(DataSourceInterface *dataSource);
58  static void startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial);
59  static void setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial);
60 
61  static const struct wl_data_device_interface s_interface;
62 };
63 
64 #ifndef K_DOXYGEN
65 const struct wl_data_device_interface DataDeviceInterface::Private::s_interface = {
66  startDragCallback,
67  setSelectionCallback,
68  resourceDestroyedCallback
69 };
70 #endif
71 
72 DataDeviceInterface::Private::Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource)
73  : Resource::Private(q, manager, parentResource, &wl_data_device_interface, &s_interface)
74  , seat(seat)
75 {
76 }
77 
78 DataDeviceInterface::Private::~Private() = default;
79 
80 void DataDeviceInterface::Private::startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial)
81 {
82  Q_UNUSED(client)
83  Q_UNUSED(serial)
84  // TODO: verify serial
85  cast<Private>(resource)->startDrag(DataSourceInterface::get(source), SurfaceInterface::get(origin), SurfaceInterface::get(icon), serial);
86 }
87 
88 void DataDeviceInterface::Private::startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *i, quint32 serial)
89 {
90  SurfaceInterface *focusSurface = origin;
91  if (proxyRemoteSurface) {
92  // origin is a proxy surface
93  focusSurface = proxyRemoteSurface.data();
94  }
95  const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface;
96  if (!pointerGrab) {
97  // Client doesn't have pointer grab.
98  const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface;
99  if (!touchGrab) {
100  // Client neither has pointer nor touch grab. No drag start allowed.
101  return;
102  }
103  }
104  // TODO: source is allowed to be null, handled client internally!
105  Q_Q(DataDeviceInterface);
106  source = dataSource;
107  if (dataSource) {
108  QObject::connect(dataSource, &Resource::aboutToBeUnbound, q, [this] { source = nullptr; });
109  }
110  surface = origin;
111  icon = i;
112  drag.serial = serial;
113  emit q->dragStarted();
114 }
115 
116 void DataDeviceInterface::Private::setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial)
117 {
118  Q_UNUSED(client)
119  Q_UNUSED(serial)
120  // TODO: verify serial
121  cast<Private>(resource)->setSelection(DataSourceInterface::get(source));
122 }
123 
124 void DataDeviceInterface::Private::setSelection(DataSourceInterface *dataSource)
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(), WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "Data source is for drag and drop");
128  return;
129  }
130  if (selection == dataSource) {
131  return;
132  }
133  Q_Q(DataDeviceInterface);
134  QObject::disconnect(selectionUnboundConnection);
135  QObject::disconnect(selectionDestroyedConnection);
136  if (selection) {
137  selection->cancel();
138  }
139  selection = dataSource;
140  if (selection) {
141  auto clearSelection = [this] {
142  setSelection(nullptr);
143  };
144  selectionUnboundConnection = QObject::connect(selection, &Resource::unbound, q, clearSelection);
145  selectionDestroyedConnection = QObject::connect(selection, &QObject::destroyed, q, clearSelection);
146  emit q->selectionChanged(selection);
147  } else {
148  selectionUnboundConnection = QMetaObject::Connection();
149  selectionDestroyedConnection = QMetaObject::Connection();
150  emit q->selectionCleared();
151  }
152 }
153 
154 DataOfferInterface *DataDeviceInterface::Private::createDataOffer(DataSourceInterface *source)
155 {
156  if (!resource) {
157  return nullptr;
158  }
159  if (!source) {
160  // a data offer can only exist together with a source
161  return nullptr;
162  }
163  Q_Q(DataDeviceInterface);
164  DataOfferInterface *offer = new DataOfferInterface(source, q, resource);
165  auto c = q->global()->display()->getConnection(wl_resource_get_client(resource));
166  offer->create(c, wl_resource_get_version(resource), 0);
167  if (!offer->resource()) {
168  // TODO: send error?
169  delete offer;
170  return nullptr;
171  }
172  wl_data_device_send_data_offer(resource, offer->resource());
173  offer->sendAllOffers();
174  return offer;
175 }
176 
177 DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, DataDeviceManagerInterface *parent, wl_resource *parentResource)
178  : Resource(new Private(seat, this, parent, parentResource))
179 {
180 }
181 
182 DataDeviceInterface::~DataDeviceInterface() = default;
183 
184 SeatInterface *DataDeviceInterface::seat() const
185 {
186  Q_D();
187  return d->seat;
188 }
189 
190 DataSourceInterface *DataDeviceInterface::dragSource() const
191 {
192  Q_D();
193  return d->source;
194 }
195 
196 SurfaceInterface *DataDeviceInterface::icon() const
197 {
198  Q_D();
199  return d->icon;
200 }
201 
202 SurfaceInterface *DataDeviceInterface::origin() const
203 {
204  Q_D();
205  return d->proxyRemoteSurface ? d->proxyRemoteSurface.data() : d->surface;
206 }
207 
208 DataSourceInterface *DataDeviceInterface::selection() const
209 {
210  Q_D();
211  return d->selection;
212 }
213 
214 void DataDeviceInterface::sendSelection(DataDeviceInterface *other)
215 {
216  Q_D();
217  auto otherSelection = other->selection();
218  if (!otherSelection) {
219  sendClearSelection();
220  return;
221  }
222  auto r = d->createDataOffer(otherSelection);
223  if (!r) {
224  return;
225  }
226  if (!d->resource) {
227  return;
228  }
229  wl_data_device_send_selection(d->resource, r->resource());
230 }
231 
232 void DataDeviceInterface::sendClearSelection()
233 {
234  Q_D();
235  if (!d->resource) {
236  return;
237  }
238  wl_data_device_send_selection(d->resource, nullptr);
239 }
240 
241 void DataDeviceInterface::drop()
242 {
243  Q_D();
244  if (!d->resource) {
245  return;
246  }
247  wl_data_device_send_drop(d->resource);
248  if (d->drag.posConnection) {
249  disconnect(d->drag.posConnection);
250  d->drag.posConnection = QMetaObject::Connection();
251  }
252  disconnect(d->drag.destroyConnection);
253  d->drag.destroyConnection = QMetaObject::Connection();
254  d->drag.surface = nullptr;
255  client()->flush();
256 }
257 
258 void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 serial)
259 {
260  Q_D();
261  if (d->drag.surface) {
262  if (d->resource && d->drag.surface->resource()) {
263  wl_data_device_send_leave(d->resource);
264  }
265  if (d->drag.posConnection) {
266  disconnect(d->drag.posConnection);
267  d->drag.posConnection = QMetaObject::Connection();
268  }
269  disconnect(d->drag.destroyConnection);
270  d->drag.destroyConnection = QMetaObject::Connection();
271  d->drag.surface = nullptr;
272  if (d->drag.sourceActionConnection) {
273  disconnect(d->drag.sourceActionConnection);
274  d->drag.sourceActionConnection = QMetaObject::Connection();
275  }
276  if (d->drag.targetActionConnection) {
277  disconnect(d->drag.targetActionConnection);
278  d->drag.targetActionConnection = QMetaObject::Connection();
279  }
280  // don't update serial, we need it
281  }
282  if (!surface) {
283  if (auto s = d->seat->dragSource()->dragSource()) {
284  s->dndAction(DataDeviceManagerInterface::DnDAction::None);
285  }
286  return;
287  }
288  if (d->proxyRemoteSurface && d->proxyRemoteSurface == surface) {
289  // A proxy can not have the remote surface as target.
290  // TODO: do this for all client's surfaces?
291  return;
292  }
293  auto *source = d->seat->dragSource()->dragSource();
294  DataOfferInterface *offer = d->createDataOffer(source);
295  d->drag.surface = surface;
296  if (d->seat->isDragPointer()) {
297  d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this,
298  [this] {
299  Q_D();
300  const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
301  wl_data_device_send_motion(d->resource, d->seat->timestamp(),
302  wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
303  client()->flush();
304  }
305  );
306  } else if (d->seat->isDragTouch()) {
307  d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this,
308  [this](qint32 id, quint32 serial, const QPointF &globalPosition) {
309  Q_D();
310  Q_UNUSED(id);
311  if (serial != d->drag.serial) {
312  // different touch down has been moved
313  return;
314  }
315  const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition);
316  wl_data_device_send_motion(d->resource, d->seat->timestamp(),
317  wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
318  client()->flush();
319  }
320  );
321  }
322  d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this,
323  [this] {
324  Q_D();
325  if (d->resource) {
326  wl_data_device_send_leave(d->resource);
327  }
328  if (d->drag.posConnection) {
329  disconnect(d->drag.posConnection);
330  }
331  d->drag = Private::Drag();
332  }
333  );
334 
335  // TODO: handle touch position
336  const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
337  wl_data_device_send_enter(d->resource, serial, surface->resource(),
338  wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
339  if (offer) {
340  offer->d_func()->sendSourceActions();
341  auto matchOffers = [source, offer] {
342  DataDeviceManagerInterface::DnDAction action{DataDeviceManagerInterface::DnDAction::None};
343  if (source->supportedDragAndDropActions().testFlag(offer->preferredDragAndDropAction())) {
344  action = offer->preferredDragAndDropAction();
345  } else {
346  if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy) &&
347  offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)) {
348  action = DataDeviceManagerInterface::DnDAction::Copy;
349  } else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move) &&
350  offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)) {
351  action = DataDeviceManagerInterface::DnDAction::Move;
352  } else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask) &&
353  offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)) {
354  action = DataDeviceManagerInterface::DnDAction::Ask;
355  }
356  }
357  offer->dndAction(action);
358  source->dndAction(action);
359  };
360  d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, offer, matchOffers);
361  d->drag.sourceActionConnection = connect(source, &DataSourceInterface::supportedDragAndDropActionsChanged, source, matchOffers);
362  }
363  d->client->flush();
364 }
365 
366 quint32 DataDeviceInterface::dragImplicitGrabSerial() const
367 {
368  Q_D();
369  return d->drag.serial;
370 }
371 
372 void DataDeviceInterface::updateProxy(SurfaceInterface *remote)
373 {
374  Q_D();
375  // TODO: connect destroy signal?
376  d->proxyRemoteSurface = remote;
377 }
378 
379 DataDeviceInterface::Private *DataDeviceInterface::d_func() const
380 {
381  return reinterpret_cast<DataDeviceInterface::Private*>(d.data());
382 }
383 
384 }
385 }
Represents the Resource for the wl_data_offer interface.
wl_resource * parentResource() const
Definition: resource.cpp:93
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
wl_resource * resource()
Definition: resource.cpp:88
Resource representing a wl_surface.
T * data() const const
if(recurs()&&!first)
DnDAction
Drag and Drop actions supported by the DataSourceInterface.
ClientConnection * client()
Definition: resource.cpp:78
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
void destroyed(QObject *obj)
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Aug 7 2020 22:48:17 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.