Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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-xdg-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 else
47 wl_data_device_destroy(object());
48}
49
51{
52 return m_selectionOffer.data();
53}
54
56{
57 if (m_selectionOffer.isNull())
58 return;
59
60 m_selectionOffer.reset();
61
62#if QT_CONFIG(clipboard)
64#endif
65}
66
68{
69 return m_selectionSource.data();
70}
71
73{
74 if (source)
75 connect(source, &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::selectionSourceCancelled);
76 set_selection(source ? source->object() : nullptr, m_inputDevice->serial());
77 m_selectionSource.reset(source);
78}
79
80#if QT_CONFIG(draganddrop)
81QWaylandDataOffer *QWaylandDataDevice::dragOffer() const
82{
83 return m_dragOffer.data();
84}
85
86bool QWaylandDataDevice::startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon)
87{
88 auto *seat = m_display->currentInputDevice();
89 auto *origin = seat->pointerFocus();
90 if (!origin)
91 origin = seat->touchFocus();
92
93 if (!origin) {
94 qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found.";
95 return false;
96 }
97
98 // dragging data without mimetypes is a legal operation in Qt terms
99 // but Wayland uses a mimetype to determine if a drag is accepted or not
100 // In this rare case, insert a placeholder
101 if (mimeData->formats().isEmpty())
102 mimeData->setData("application/x-qt-avoid-empty-placeholder"_L1, QByteArray("1"));
103
104 m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
105
106 if (version() >= 3)
107 m_dragSource->set_actions(dropActionsToWl(supportedActions));
108
109 connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
110 connect(m_dragSource.data(), &QWaylandDataSource::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) {
111 auto drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
112 if (!drag->currentDrag()) {
113 return;
114 }
115 // in old versions drop action is not set, so we guess
116 if (m_dragSource->version() < 3) {
117 drag->setResponse(accepted);
118 } else {
119 QPlatformDropQtResponse response(accepted, action);
120 drag->setResponse(response);
121 }
122 });
123 connect(m_dragSource.data(), &QWaylandDataSource::dndDropped, this,
124 [this](bool accepted, Qt::DropAction action) {
125 QPlatformDropQtResponse response(accepted, action);
126 if (m_toplevelDrag) {
127 // If the widget was dropped but the drag not accepted it
128 // should be its own window in the future. To distinguish
129 // from canceling mid-drag the drag is accepted here as the
130 // we know if the widget is over a zone where it can be
131 // incorporated or not
132 response = { accepted, Qt::MoveAction };
133 }
135 ->setDropResponse(response);
136 });
137 connect(m_dragSource.data(), &QWaylandDataSource::finished, this, [this]() {
138 static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag();
139 if (m_toplevelDrag) {
140 m_toplevelDrag->destroy();
141 m_toplevelDrag = nullptr;
142 }
143 });
144
145 if (mimeData->hasFormat("application/x-qt-mainwindowdrag-window"_L1)
146 && m_display->xdgToplevelDragManager()) {
147 qintptr dockWindowPtr;
149 QDataStream windowStream(mimeData->data("application/x-qt-mainwindowdrag-window"_L1));
150 windowStream >> dockWindowPtr;
151 QWindow *dockWindow = reinterpret_cast<QWindow *>(dockWindowPtr);
152 QDataStream offsetStream(mimeData->data("application/x-qt-mainwindowdrag-position"_L1));
153 offsetStream >> offset;
154 if (auto waylandWindow = static_cast<QWaylandWindow *>(dockWindow->handle())) {
155 if (auto toplevel = waylandWindow->surfaceRole<xdg_toplevel>()) {
156 m_toplevelDrag = new QtWayland::xdg_toplevel_drag_v1(
157 m_display->xdgToplevelDragManager()->get_xdg_toplevel_drag(
158 m_dragSource->object()));
159 m_toplevelDrag->attach(toplevel, offset.x(), offset.y());
160 }
161 }
162 }
163
164 start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
165 return true;
166}
167
168void QWaylandDataDevice::cancelDrag()
169{
170 m_dragSource.reset();
171}
172#endif
173
174void QWaylandDataDevice::data_device_data_offer(struct ::wl_data_offer *id)
175{
176 new QWaylandDataOffer(m_display, id);
177}
178
179#if QT_CONFIG(draganddrop)
180void QWaylandDataDevice::data_device_drop()
181{
182 QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
183
184 QMimeData *dragData = nullptr;
185 Qt::DropActions supportedActions;
186 if (drag) {
187 dragData = drag->mimeData();
188 supportedActions = drag->supportedActions();
189 } else if (m_dragOffer) {
190 dragData = m_dragOffer->mimeData();
191 supportedActions = m_dragOffer->supportedActions();
192 } else {
193 return;
194 }
195
196 QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_dragWindow, dragData, m_dragPoint, supportedActions,
199 if (drag) {
200 auto drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
201 drag->setDropResponse(response);
202 drag->finishDrag();
203 } else if (m_dragOffer) {
204 m_dragOffer->finish();
205 }
206}
207
208void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id)
209{
210 auto *dragWaylandWindow = surface ? QWaylandWindow::fromWlSurface(surface) : nullptr;
211 if (!dragWaylandWindow)
212 return; // Ignore foreign surfaces
213
214 m_dragWindow = dragWaylandWindow->window();
215 m_dragPoint = calculateDragPosition(x, y, m_dragWindow);
216 m_enterSerial = serial;
217
218 QMimeData *dragData = nullptr;
219 Qt::DropActions supportedActions;
220
221 m_dragOffer.reset(static_cast<QWaylandDataOffer *>(wl_data_offer_get_user_data(id)));
222 QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
223 if (drag) {
224 dragData = drag->mimeData();
225 supportedActions = drag->supportedActions();
226 } else if (m_dragOffer) {
227 dragData = m_dragOffer->mimeData();
228 supportedActions = m_dragOffer->supportedActions();
229 }
230
231 const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(
232 m_dragWindow, dragData, m_dragPoint, supportedActions, QGuiApplication::mouseButtons(),
234 if (drag) {
235 static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response);
236 }
237
238 sendResponse(supportedActions, response);
239}
240
241void QWaylandDataDevice::data_device_leave()
242{
243 if (m_dragWindow)
244 QWindowSystemInterface::handleDrag(m_dragWindow, nullptr, QPoint(), Qt::IgnoreAction,
247
248 QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
249 if (!drag) {
250 m_dragOffer.reset();
251 }
252}
253
254void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixed_t y)
255{
256 Q_UNUSED(time);
257
258 QDrag *drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->currentDrag();
259
260 if (!drag && !m_dragOffer)
261 return;
262
263 m_dragPoint = calculateDragPosition(x, y, m_dragWindow);
264
265 QMimeData *dragData = nullptr;
266 Qt::DropActions supportedActions;
267 if (drag) {
268 dragData = drag->mimeData();
269 supportedActions = drag->supportedActions();
270 } else {
271 dragData = m_dragOffer->mimeData();
272 supportedActions = m_dragOffer->supportedActions();
273 }
274
275 const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions,
278
279 if (drag) {
280 static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response);
281 }
282
283 sendResponse(supportedActions, response);
284}
285#endif // QT_CONFIG(draganddrop)
286
287void QWaylandDataDevice::data_device_selection(wl_data_offer *id)
288{
289 if (id)
290 m_selectionOffer.reset(static_cast<QWaylandDataOffer *>(wl_data_offer_get_user_data(id)));
291 else
292 m_selectionOffer.reset();
293
294#if QT_CONFIG(clipboard)
296#endif
297}
298
299void QWaylandDataDevice::selectionSourceCancelled()
300{
301 m_selectionSource.reset();
302#if QT_CONFIG(clipboard)
304#endif
305}
306
307#if QT_CONFIG(draganddrop)
308void QWaylandDataDevice::dragSourceCancelled()
309{
310 static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag();
311 m_dragSource.reset();
312 if (m_toplevelDrag) {
313 m_toplevelDrag->destroy();
314 m_toplevelDrag = nullptr;
315 }
316}
317
318QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) const
319{
320 QPoint pnt(wl_fixed_to_int(x), wl_fixed_to_int(y));
321 if (wnd) {
322 QWaylandWindow *wwnd = static_cast<QWaylandWindow*>(m_dragWindow->handle());
323 if (wwnd && wwnd->decoration()) {
324 pnt -= QPoint(wwnd->decoration()->margins().left(),
325 wwnd->decoration()->margins().top());
326 }
327 }
328 return pnt;
329}
330
331void QWaylandDataDevice::sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response)
332{
333 if (response.isAccepted()) {
334 if (version() >= 3)
335 m_dragOffer->set_actions(dropActionsToWl(supportedActions), dropActionsToWl(response.acceptedAction()));
336
337 m_dragOffer->accept(m_enterSerial, m_dragOffer->firstFormat());
338 } else {
339 m_dragOffer->accept(m_enterSerial, QString());
340 }
341}
342
343int QWaylandDataDevice::dropActionsToWl(Qt::DropActions actions)
344{
345
346 int wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
347 if (actions & Qt::CopyAction)
348 wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
349 if (actions & (Qt::MoveAction | Qt::TargetMoveAction))
350 wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
351
352 // wayland does not support LinkAction at the time of writing
353 return wlActions;
354}
355
356
357#endif // QT_CONFIG(draganddrop)
358
359}
360
362
363#include "moc_qwaylanddatadevice_p.cpp"
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\inmodule QtGui
Definition qdrag.h:22
QMimeData * mimeData() const
Returns the MIME data that is encapsulated by the drag object.
Definition qdrag.cpp:114
Qt::DropActions supportedActions() const
Returns the set of possible drop actions for this drag operation.
Definition qdrag.cpp:310
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
void setData(const QString &mimetype, const QByteArray &data)
Sets the data associated with the MIME type given by mimeType to the specified data.
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.
virtual QStringList formats() const
Returns a list of formats supported by the object.
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
Qt::DropAction acceptedAction() const
\inmodule QtCore\reentrant
Definition qpoint.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\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
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#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:166
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