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

KDE's Doxygen guidelines are available online.