Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwaylanddatadevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
6
10#include "qwaylanddnd_p.h"
12#include "qwaylanddisplay_p.h"
14#include "qwaylandsurface_p.h"
15
16#include <QtWaylandClient/private/qwayland-qt-toplevel-drag-v1.h>
17
18#include <QtCore/QMimeData>
19#include <QtGui/QGuiApplication>
20#include <QtGui/private/qguiapplication_p.h>
21
22#if QT_CONFIG(clipboard)
23#include <qpa/qplatformclipboard.h>
24#endif
25#include <qpa/qplatformdrag.h>
26#include <qpa/qwindowsysteminterface.h>
27
29
30namespace QtWaylandClient {
31
32using namespace Qt::StringLiterals;
33
35 : QObject(inputDevice)
36 , QtWayland::wl_data_device(manager->get_data_device(inputDevice->wl_seat()))
37 , m_display(manager->display())
38 , m_inputDevice(inputDevice)
39{
40}
41
43{
44 if (version() >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION)
45 release();
46}
47
49{
50 return m_selectionOffer.data();
51}
52
54{
55 if (m_selectionOffer.isNull())
56 return;
57
58 m_selectionOffer.reset();
59
60#if QT_CONFIG(clipboard)
62#endif
63}
64
66{
67 return m_selectionSource.data();
68}
69
71{
72 if (source)
73 connect(source, &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::selectionSourceCancelled);
74 set_selection(source ? source->object() : nullptr, m_inputDevice->serial());
75 m_selectionSource.reset(source);
76}
77
78#if QT_CONFIG(draganddrop)
79QWaylandDataOffer *QWaylandDataDevice::dragOffer() const
80{
81 return m_dragOffer.data();
82}
83
84bool QWaylandDataDevice::startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon)
85{
86 auto *seat = m_display->currentInputDevice();
87 auto *origin = seat->pointerFocus();
88 if (!origin)
89 origin = seat->touchFocus();
90
91 if (!origin) {
92 qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found.";
93 return false;
94 }
95
96 m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
97
98 if (version() >= 3)
99 m_dragSource->set_actions(dropActionsToWl(supportedActions));
100
101 connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
102 connect(m_dragSource.data(), &QWaylandDataSource::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) {
103 auto drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
104 if (!drag->currentDrag()) {
105 return;
106 }
107 // in old versions drop action is not set, so we guess
108 if (m_dragSource->version() < 3) {
109 drag->setResponse(accepted);
110 } else {
111 QPlatformDropQtResponse response(accepted, action);
112 drag->setResponse(response);
113 }
114 });
115 connect(m_dragSource.data(), &QWaylandDataSource::dndDropped, this,
116 [this](bool accepted, Qt::DropAction action) {
117 QPlatformDropQtResponse response(accepted, action);
118 if (m_toplevelDrag) {
119 // If the widget was dropped but the drag not accepted it
120 // should be its own window in the future. To distinguish
121 // from canceling mid-drag the drag is accepted here as the
122 // we know if the widget is over a zone where it can be
123 // incorporated or not
124 response = { accepted, Qt::MoveAction };
125 }
127 ->setDropResponse(response);
128 });
129 connect(m_dragSource.data(), &QWaylandDataSource::finished, this, [this]() {
130 static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag();
131 if (m_toplevelDrag) {
132 m_toplevelDrag->destroy();
133 m_toplevelDrag = nullptr;
134 }
135 });
136
137 if (mimeData->hasFormat("application/x-qt-mainwindowdrag-window"_L1)
138 && m_display->xdgToplevelDragManager()) {
139 qintptr dockWindowPtr;
141 QDataStream windowStream(mimeData->data("application/x-qt-mainwindowdrag-window"_L1));
142 windowStream >> dockWindowPtr;
143 QWindow *dockWindow = reinterpret_cast<QWindow *>(dockWindowPtr);
144 QDataStream offsetStream(mimeData->data("application/x-qt-mainwindowdrag-position"_L1));
145 offsetStream >> offset;
146 if (auto waylandWindow = static_cast<QWaylandWindow *>(dockWindow->handle())) {
147 if (auto toplevel = waylandWindow->surfaceRole<xdg_toplevel>()) {
148 m_toplevelDrag = new QtWayland::qt_toplevel_drag_v1(
149 m_display->xdgToplevelDragManager()->get_qt_toplevel_drag(
150 m_dragSource->object()));
151 m_toplevelDrag->attach(toplevel, offset.x(), offset.y());
152 }
153 }
154 }
155
156 start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
157 return true;
158}
159
160void QWaylandDataDevice::cancelDrag()
161{
162 m_dragSource.reset();
163}
164#endif
165
166void QWaylandDataDevice::data_device_data_offer(struct ::wl_data_offer *id)
167{
168 new QWaylandDataOffer(m_display, id);
169}
170
171#if QT_CONFIG(draganddrop)
172void QWaylandDataDevice::data_device_drop()
173{
174 QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
175
176 QMimeData *dragData = nullptr;
177 Qt::DropActions supportedActions;
178 if (drag) {
179 dragData = drag->mimeData();
180 supportedActions = drag->supportedActions();
181 } else if (m_dragOffer) {
182 dragData = m_dragOffer->mimeData();
183 supportedActions = m_dragOffer->supportedActions();
184 } else {
185 return;
186 }
187
188 QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_dragWindow, dragData, m_dragPoint, supportedActions,
191 if (drag) {
192 auto drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
193 drag->setDropResponse(response);
194 drag->finishDrag();
195 } else if (m_dragOffer) {
196 m_dragOffer->finish();
197 }
198}
199
200void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id)
201{
202 auto *dragWaylandWindow = surface ? QWaylandWindow::fromWlSurface(surface) : nullptr;
203 if (!dragWaylandWindow)
204 return; // Ignore foreign surfaces
205
206 m_dragWindow = dragWaylandWindow->window();
207 m_dragPoint = calculateDragPosition(x, y, m_dragWindow);
208 m_enterSerial = serial;
209
210 QMimeData *dragData = nullptr;
211 Qt::DropActions supportedActions;
212
213 m_dragOffer.reset(static_cast<QWaylandDataOffer *>(wl_data_offer_get_user_data(id)));
214 QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
215 if (drag) {
216 dragData = drag->mimeData();
217 } else if (m_dragOffer) {
218 dragData = m_dragOffer->mimeData();
219 supportedActions = m_dragOffer->supportedActions();
220 }
221
222 const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(
223 m_dragWindow, dragData, m_dragPoint, supportedActions, QGuiApplication::mouseButtons(),
225 if (drag) {
226 static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response);
227 }
228
229 sendResponse(supportedActions, response);
230}
231
232void QWaylandDataDevice::data_device_leave()
233{
234 if (m_dragWindow)
235 QWindowSystemInterface::handleDrag(m_dragWindow, nullptr, QPoint(), Qt::IgnoreAction,
238
239 QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
240 if (!drag) {
241 m_dragOffer.reset();
242 }
243}
244
245void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixed_t y)
246{
247 Q_UNUSED(time);
248
249 QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
250
251 if (!drag && !m_dragOffer)
252 return;
253
254 m_dragPoint = calculateDragPosition(x, y, m_dragWindow);
255
256 QMimeData *dragData = nullptr;
257 Qt::DropActions supportedActions;
258 if (drag) {
259 dragData = drag->mimeData();
260 supportedActions = drag->supportedActions();
261 } else {
262 dragData = m_dragOffer->mimeData();
263 supportedActions = m_dragOffer->supportedActions();
264 }
265
266 const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions,
269
270 if (drag) {
271 static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response);
272 }
273
274 sendResponse(supportedActions, response);
275}
276#endif // QT_CONFIG(draganddrop)
277
278void QWaylandDataDevice::data_device_selection(wl_data_offer *id)
279{
280 if (id)
281 m_selectionOffer.reset(static_cast<QWaylandDataOffer *>(wl_data_offer_get_user_data(id)));
282 else
283 m_selectionOffer.reset();
284
285#if QT_CONFIG(clipboard)
287#endif
288}
289
290void QWaylandDataDevice::selectionSourceCancelled()
291{
292 m_selectionSource.reset();
293#if QT_CONFIG(clipboard)
295#endif
296}
297
298#if QT_CONFIG(draganddrop)
299void QWaylandDataDevice::dragSourceCancelled()
300{
301 static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag();
302 m_dragSource.reset();
303 if (m_toplevelDrag) {
304 m_toplevelDrag->destroy();
305 m_toplevelDrag = nullptr;
306 }
307}
308
309QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) const
310{
311 QPoint pnt(wl_fixed_to_int(x), wl_fixed_to_int(y));
312 if (wnd) {
313 QWaylandWindow *wwnd = static_cast<QWaylandWindow*>(m_dragWindow->handle());
314 if (wwnd && wwnd->decoration()) {
315 pnt -= QPoint(wwnd->decoration()->margins().left(),
316 wwnd->decoration()->margins().top());
317 }
318 }
319 return pnt;
320}
321
322void QWaylandDataDevice::sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response)
323{
324 if (response.isAccepted()) {
325 if (version() >= 3)
326 m_dragOffer->set_actions(dropActionsToWl(supportedActions), dropActionsToWl(response.acceptedAction()));
327
328 m_dragOffer->accept(m_enterSerial, m_dragOffer->firstFormat());
329 } else {
330 m_dragOffer->accept(m_enterSerial, QString());
331 }
332}
333
334int QWaylandDataDevice::dropActionsToWl(Qt::DropActions actions)
335{
336
337 int wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
338 if (actions & Qt::CopyAction)
339 wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
340 if (actions & (Qt::MoveAction | Qt::TargetMoveAction))
341 wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
342
343 // wayland does not support LinkAction at the time of writing
344 return wlActions;
345}
346
347
348#endif // QT_CONFIG(draganddrop)
349
350}
351
353
354#include "moc_qwaylanddatadevice_p.cpp"
\inmodule QtCore\reentrant
Definition qdatastream.h:30
\inmodule QtGui
Definition qdrag.h:22
QMimeData * mimeData() const
Returns the MIME data that is encapsulated by the drag object.
Definition qdrag.cpp:112
Qt::DropActions supportedActions() const
Returns the set of possible drop actions for this drag operation.
Definition qdrag.cpp:308
static QPlatformIntegration * platformIntegration()
static Qt::KeyboardModifiers keyboardModifiers()
Returns the current state of the modifier keys on the keyboard.
static Qt::MouseButtons mouseButtons()
Returns the current state of the buttons on the mouse.
\inmodule QtCore
Definition qmimedata.h:16
virtual bool hasFormat(const QString &mimetype) const
Returns true if the object can return data for the MIME type specified by mimeType; otherwise returns...
QByteArray data(const QString &mimetype) const
Returns the data stored in the object in the format described by the MIME type specified by mimeType.
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
void emitChanged(QClipboard::Mode mode)
Qt::DropAction acceptedAction() const
virtual QPlatformClipboard * clipboard() const
Accessor for the platform integration's clipboard.
\inmodule QtCore\reentrant
Definition qpoint.h:23
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
\inmodule QtGui
Definition qwindow.h:63
QWaylandDataSource * selectionSource() const
QWaylandDataOffer * selectionOffer() const
QWaylandDataDevice(QWaylandDataDeviceManager *manager, QWaylandInputDevice *inputDevice)
void setSelectionSource(QWaylandDataSource *source)
void dndDropped(bool accepted, Qt::DropAction action)
void dndResponseUpdated(bool accepted, Qt::DropAction action)
QWaylandInputDevice * currentInputDevice() const
struct wl_display * display
Definition linuxdmabuf.h:41
Combined button and popup list for selecting options.
DropAction
@ CopyAction
@ IgnoreAction
@ MoveAction
@ TargetMoveAction
#define qCDebug(category,...)
GLint GLint GLint GLint GLint x
[0]
GLenum GLuint GLintptr offset
GLint y
GLsizei GLsizei GLchar * source
#define Q_UNUSED(x)
ptrdiff_t qintptr
Definition qtypes.h:71
if(qFloatDistance(a, b)<(1<< 7))
[0]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QMimeData * mimeData
QObject::connect nullptr
sem release()
QNetworkAccessManager manager