Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qxcbdrag.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qxcbdrag.h"
6#include <xcb/xcb.h>
9#include "qxcbkeyboard.h"
10#include "qxcbmime.h"
11#include "qxcbwindow.h"
12#include "qxcbscreen.h"
13#include "qwindow.h"
14#include "qxcbcursor.h"
15#include <private/qdnd_p.h>
16#include <qdebug.h>
17#include <qevent.h>
18#include <qguiapplication.h>
19#include <qrect.h>
20#include <qpainter.h>
21
22#include <qpa/qwindowsysteminterface.h>
23
24#include <private/qguiapplication_p.h>
25#include <private/qshapedpixmapdndwindow_p.h>
26#include <private/qsimpledrag_p.h>
27#include <private/qhighdpiscaling_p.h>
28
30
31using namespace Qt::StringLiterals;
32
33const int xdnd_version = 5;
34
35static inline xcb_window_t xcb_window(QPlatformWindow *w)
36{
37 return static_cast<QXcbWindow *>(w)->xcb_window();
38}
39
40static inline xcb_window_t xcb_window(QWindow *w)
41{
42 return static_cast<QXcbWindow *>(w->handle())->xcb_window();
43}
44
45static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w)
46{
47 xcb_window_t proxy = XCB_NONE;
48
49 auto reply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(),
50 false, w, c->atom(QXcbAtom::AtomXdndProxy), XCB_ATOM_WINDOW, 0, 1);
51
52 if (reply && reply->type == XCB_ATOM_WINDOW)
53 proxy = *((xcb_window_t *)xcb_get_property_value(reply.get()));
54
55 if (proxy == XCB_NONE)
56 return proxy;
57
58 // exists and is real?
59 reply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(),
60 false, proxy, c->atom(QXcbAtom::AtomXdndProxy), XCB_ATOM_WINDOW, 0, 1);
61
62 if (reply && reply->type == XCB_ATOM_WINDOW) {
63 xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply.get()));
64 if (proxy != p)
65 proxy = XCB_NONE;
66 } else {
67 proxy = XCB_NONE;
68 }
69
70 return proxy;
71}
72
73class QXcbDropData : public QXcbMime
74{
75public:
78
79protected:
80 bool hasFormat_sys(const QString &mimeType) const override;
81 QStringList formats_sys() const override;
82 QVariant retrieveData_sys(const QString &mimeType, QMetaType type) const override;
83
84 QVariant xdndObtainData(const QByteArray &format, QMetaType requestedType) const;
85
87};
88
89
90QXcbDrag::QXcbDrag(QXcbConnection *c) : QXcbObject(c)
91{
92 m_dropData = new QXcbDropData(this);
93
94 init();
95}
96
98{
99 delete m_dropData;
100}
101
102void QXcbDrag::init()
103{
104 currentWindow.clear();
105
106 accepted_drop_action = Qt::IgnoreAction;
107
108 xdnd_dragsource = XCB_NONE;
109
110 waiting_for_status = false;
111 current_target = XCB_NONE;
112 current_proxy_target = XCB_NONE;
113
114 source_time = XCB_CURRENT_TIME;
115 target_time = XCB_CURRENT_TIME;
116
117 QXcbCursor::queryPointer(connection(), &current_virtual_desktop, nullptr);
118 drag_types.clear();
119
120 //current_embedding_widget = 0;
121
122 dropped = false;
123 canceled = false;
124
125 source_sameanswer = QRect();
126}
127
128bool QXcbDrag::eventFilter(QObject *o, QEvent *e)
129{
130 /* We are setting a mouse grab on the QShapedPixmapWindow in order not to
131 * lose the grab when the virtual desktop changes, but
132 * QBasicDrag::eventFilter() expects the events to be coming from the
133 * window where the drag was started. */
134 if (initiatorWindow && o == shapedPixmapWindow())
135 o = initiatorWindow.data();
136 return QBasicDrag::eventFilter(o, e);
137}
138
140{
141 init();
142
143 qCDebug(lcQpaXDnd) << "starting drag where source:" << connection()->qtSelectionOwner();
144 xcb_set_selection_owner(xcb_connection(), connection()->qtSelectionOwner(),
146
147 QStringList fmts = QXcbMime::formatsHelper(drag()->mimeData());
148 for (int i = 0; i < fmts.size(); ++i) {
149 QList<xcb_atom_t> atoms = QXcbMime::mimeAtomsForFormat(connection(), fmts.at(i));
150 for (int j = 0; j < atoms.size(); ++j) {
151 if (!drag_types.contains(atoms.at(j)))
152 drag_types.append(atoms.at(j));
153 }
154 }
155
156 if (drag_types.size() > 3)
157 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->qtSelectionOwner(),
158 atom(QXcbAtom::AtomXdndTypelist),
159 XCB_ATOM_ATOM, 32, drag_types.size(), (const void *)drag_types.constData());
160
161 setUseCompositing(current_virtual_desktop->compositingActive());
162 setScreen(current_virtual_desktop->screens().constFirst()->screen());
163 initiatorWindow = QGuiApplicationPrivate::currentMouseWindow;
164 QBasicDrag::startDrag();
165 if (connection()->mouseGrabber() == nullptr)
166 shapedPixmapWindow()->setMouseGrabEnabled(true);
167
168 auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), initiatorWindow.data());
169 move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
170}
171
173{
174 QBasicDrag::endDrag();
175 if (!dropped && !canceled && canDrop()) {
176 // Set executed drop action when dropping outside application.
177 setExecutedDropAction(accepted_drop_action);
178 }
179 initiatorWindow.clear();
180}
181
182Qt::DropAction QXcbDrag::defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const
183{
184 if (currentDrag() || drop_actions.isEmpty())
185 return QBasicDrag::defaultAction(possibleActions, modifiers);
186
187 return toDropAction(drop_actions.first());
188}
189
190void QXcbDrag::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
191{
192 if (event->window != xdnd_dragsource || event->atom != atom(QXcbAtom::AtomXdndActionList))
193 return;
194
195 readActionList();
196}
197
198static
199bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint & pos, xcb_window_t w, xcb_shape_sk_t shapeType)
200{
201 bool interacts = false;
202 auto reply = Q_XCB_REPLY(xcb_shape_get_rectangles, connection, w, shapeType);
203 if (reply) {
204 xcb_rectangle_t *rectangles = xcb_shape_get_rectangles_rectangles(reply.get());
205 if (rectangles) {
206 const int nRectangles = xcb_shape_get_rectangles_rectangles_length(reply.get());
207 for (int i = 0; !interacts && i < nRectangles; ++i) {
208 interacts = QRect(rectangles[i].x, rectangles[i].y, rectangles[i].width, rectangles[i].height).contains(pos);
209 }
210 }
211 }
212
213 return interacts;
214}
215
216xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md, bool ignoreNonXdndAwareWindows)
217{
218 if (w == shapedPixmapWindow()->handle()->winId())
219 return 0;
220
221 if (md) {
222 auto reply = Q_XCB_REPLY(xcb_get_window_attributes, xcb_connection(), w);
223 if (!reply)
224 return 0;
225
226 if (reply->map_state != XCB_MAP_STATE_VIEWABLE)
227 return 0;
228
229 auto greply = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), w);
230 if (!greply)
231 return 0;
232
233 QRect windowRect(greply->x, greply->y, greply->width, greply->height);
234 if (windowRect.contains(pos)) {
235 bool windowContainsMouse = !ignoreNonXdndAwareWindows;
236 {
237 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
238 false, w, connection()->atom(QXcbAtom::AtomXdndAware),
239 XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
240 bool isAware = reply && reply->type != XCB_NONE;
241 if (isAware) {
242 const QPoint relPos = pos - windowRect.topLeft();
243 // When ShapeInput and ShapeBounding are not set they return a single rectangle with the geometry of the window, this is why we
244 // need to check both here so that in the case one is set and the other is not we still get the correct result.
245 if (connection()->hasInputShape())
246 windowContainsMouse = windowInteractsWithPosition(xcb_connection(), relPos, w, XCB_SHAPE_SK_INPUT);
247 if (windowContainsMouse && connection()->hasXShape())
248 windowContainsMouse = windowInteractsWithPosition(xcb_connection(), relPos, w, XCB_SHAPE_SK_BOUNDING);
249 if (!connection()->hasInputShape() && !connection()->hasXShape())
250 windowContainsMouse = true;
251 if (windowContainsMouse)
252 return w;
253 }
254 }
255
256 auto reply = Q_XCB_REPLY(xcb_query_tree, xcb_connection(), w);
257 if (!reply)
258 return 0;
259 int nc = xcb_query_tree_children_length(reply.get());
260 xcb_window_t *c = xcb_query_tree_children(reply.get());
261
262 xcb_window_t r = 0;
263 for (uint i = nc; !r && i--;)
264 r = findRealWindow(pos - windowRect.topLeft(), c[i], md-1, ignoreNonXdndAwareWindows);
265
266 if (r)
267 return r;
268
269 // We didn't find a client window! Just use the
270 // innermost window.
271
272 // No children!
273 if (!windowContainsMouse)
274 return 0;
275 else
276 return w;
277 }
278 }
279 return 0;
280}
281
282bool QXcbDrag::findXdndAwareTarget(const QPoint &globalPos, xcb_window_t *target_out)
283{
284 xcb_window_t rootwin = current_virtual_desktop->root();
285 auto translate = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
286 rootwin, rootwin, globalPos.x(), globalPos.y());
287 if (!translate)
288 return false;
289
290 xcb_window_t target = translate->child;
291 int lx = translate->dst_x;
292 int ly = translate->dst_y;
293
294 if (target && target != rootwin) {
295 xcb_window_t src = rootwin;
296 while (target != 0) {
297 qCDebug(lcQpaXDnd) << "checking target for XdndAware" << target;
298
299 auto translate = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
300 src, target, lx, ly);
301 if (!translate) {
302 target = 0;
303 break;
304 }
305 lx = translate->dst_x;
306 ly = translate->dst_y;
307 src = target;
308 xcb_window_t child = translate->child;
309
310 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, target,
311 atom(QXcbAtom::AtomXdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
312 bool aware = reply && reply->type != XCB_NONE;
313 if (aware) {
314 qCDebug(lcQpaXDnd) << "found XdndAware on" << target;
315 break;
316 }
317
318 target = child;
319 }
320
321 if (!target || target == shapedPixmapWindow()->handle()->winId()) {
322 qCDebug(lcQpaXDnd) << "need to find real window";
323 target = findRealWindow(globalPos, rootwin, 6, true);
324 if (target == 0)
325 target = findRealWindow(globalPos, rootwin, 6, false);
326 qCDebug(lcQpaXDnd) << "real window found" << target;
327 }
328 }
329
330 *target_out = target;
331 return true;
332}
333
334void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
335{
336 // currentDrag() might be deleted while 'drag' is progressing
337 if (!currentDrag()) {
338 cancel();
339 return;
340 }
341 // The source sends XdndEnter and XdndPosition to the target.
342 if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
343 return;
344
345 QXcbVirtualDesktop *virtualDesktop = nullptr;
346 QPoint cursorPos;
347 QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos);
348 QXcbScreen *screen = virtualDesktop->screenAt(cursorPos);
349 QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen);
350
351 if (virtualDesktop != current_virtual_desktop) {
352 setUseCompositing(virtualDesktop->compositingActive());
353 recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos);
354 if (connection()->mouseGrabber() == nullptr)
355 shapedPixmapWindow()->setMouseGrabEnabled(true);
356
357 current_virtual_desktop = virtualDesktop;
358 } else {
359 QBasicDrag::moveShapedPixmapWindow(deviceIndependentPos);
360 }
361
362 xcb_window_t target;
363 if (!findXdndAwareTarget(globalPos, &target))
364 return;
365
366 QXcbWindow *w = nullptr;
367 if (target) {
368 w = connection()->platformWindowFromId(target);
369 } else {
370 w = nullptr;
371 target = current_virtual_desktop->root();
372 }
373
374 xcb_window_t proxy_target = xdndProxy(connection(), target);
375 if (!proxy_target)
376 proxy_target = target;
377 int target_version = 1;
378
379 if (proxy_target) {
380 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
381 false, proxy_target,
382 atom(QXcbAtom::AtomXdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
383 if (!reply || reply->type == XCB_NONE) {
384 target = 0;
385 } else {
386 target_version = *(uint32_t *)xcb_get_property_value(reply.get());
387 target_version = qMin(xdnd_version, target_version ? target_version : 1);
388 }
389 }
390
391 if (target != current_target) {
392 if (current_target)
393 send_leave();
394
395 current_target = target;
396 current_proxy_target = proxy_target;
397 if (target) {
398 int flags = target_version << 24;
399 if (drag_types.size() > 3)
400 flags |= 0x0001;
401
402 xcb_client_message_event_t enter;
403 enter.response_type = XCB_CLIENT_MESSAGE;
404 enter.sequence = 0;
405 enter.window = target;
406 enter.format = 32;
407 enter.type = atom(QXcbAtom::AtomXdndEnter);
408 enter.data.data32[0] = connection()->qtSelectionOwner();
409 enter.data.data32[1] = flags;
410 enter.data.data32[2] = drag_types.size() > 0 ? drag_types.at(0) : 0;
411 enter.data.data32[3] = drag_types.size() > 1 ? drag_types.at(1) : 0;
412 enter.data.data32[4] = drag_types.size() > 2 ? drag_types.at(2) : 0;
413 // provisionally set the rectangle to 5x5 pixels...
414 source_sameanswer = QRect(globalPos.x() - 2, globalPos.y() - 2 , 5, 5);
415
416 qCDebug(lcQpaXDnd) << "sending XdndEnter to target:" << target;
417
418 if (w)
419 handleEnter(w, &enter, current_proxy_target);
420 else if (target)
421 xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&enter);
422 waiting_for_status = false;
423 }
424 }
425
426 if (waiting_for_status)
427 return;
428
429 if (target) {
430 waiting_for_status = true;
431 // The source sends a ClientMessage of type XdndPosition. This tells the target the
432 // position of the mouse and the action that the user requested.
433 xcb_client_message_event_t move;
434 move.response_type = XCB_CLIENT_MESSAGE;
435 move.sequence = 0;
436 move.window = target;
437 move.format = 32;
438 move.type = atom(QXcbAtom::AtomXdndPosition);
439 move.data.data32[0] = connection()->qtSelectionOwner();
440 move.data.data32[1] = 0; // flags
441 move.data.data32[2] = (globalPos.x() << 16) + globalPos.y();
442 move.data.data32[3] = connection()->time();
443 const auto supportedActions = currentDrag()->supportedActions();
444 const auto requestedAction = defaultAction(supportedActions, mods);
445 move.data.data32[4] = toXdndAction(requestedAction);
446
447 qCDebug(lcQpaXDnd) << "sending XdndPosition to target:" << target;
448
449 source_time = connection()->time();
450
451 if (w) {
452 handle_xdnd_position(w, &move, b, mods);
453 } else {
454 setActionList(requestedAction, supportedActions);
455 xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move);
456 }
457 }
458
459 static const bool isUnity = qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity";
460 if (isUnity && xdndCollectionWindow == XCB_NONE) {
461 QString name = QXcbWindow::windowTitle(connection(), target);
462 if (name == "XdndCollectionWindowImp"_L1)
463 xdndCollectionWindow = target;
464 }
465 if (target == xdndCollectionWindow) {
466 setCanDrop(false);
467 updateCursor(Qt::IgnoreAction);
468 }
469}
470
471void QXcbDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
472{
473 // XdndDrop is sent from source to target to complete the drop.
474 QBasicDrag::drop(globalPos, b, mods);
475
476 if (!current_target)
477 return;
478
479 xcb_client_message_event_t drop;
480 drop.response_type = XCB_CLIENT_MESSAGE;
481 drop.sequence = 0;
482 drop.window = current_target;
483 drop.format = 32;
484 drop.type = atom(QXcbAtom::AtomXdndDrop);
485 drop.data.data32[0] = connection()->qtSelectionOwner();
486 drop.data.data32[1] = 0; // flags
487 drop.data.data32[2] = connection()->time();
488
489 drop.data.data32[3] = 0;
490 drop.data.data32[4] = currentDrag()->supportedActions();
491
492 QXcbWindow *w = connection()->platformWindowFromId(current_proxy_target);
493
494 Transaction t = {
495 connection()->time(),
496 current_target,
497 current_proxy_target,
498 w,
499// current_embeddig_widget,
500 currentDrag(),
501 QTime::currentTime()
502 };
503 transactions.append(t);
504
505 // timer is needed only for drops that came from other processes.
506 if (!t.targetWindow && !cleanup_timer.isActive())
507 cleanup_timer.start(XdndDropTransactionTimeout, this);
508
509 qCDebug(lcQpaXDnd) << "sending drop to target:" << current_target;
510
511 if (w) {
512 handleDrop(w, &drop, b, mods);
513 } else {
514 xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&drop);
515 }
516}
517
518Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const
519{
520 if (a == atom(QXcbAtom::AtomXdndActionCopy) || a == 0)
521 return Qt::CopyAction;
522 if (a == atom(QXcbAtom::AtomXdndActionLink))
523 return Qt::LinkAction;
524 if (a == atom(QXcbAtom::AtomXdndActionMove))
525 return Qt::MoveAction;
526 return Qt::CopyAction;
527}
528
529Qt::DropActions QXcbDrag::toDropActions(const QList<xcb_atom_t> &atoms) const
530{
531 Qt::DropActions actions;
532 for (const auto actionAtom : atoms) {
533 if (actionAtom != atom(QXcbAtom::AtomXdndActionAsk))
534 actions |= toDropAction(actionAtom);
535 }
536 return actions;
537}
538
539xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const
540{
541 switch (a) {
542 case Qt::CopyAction:
544 case Qt::LinkAction:
546 case Qt::MoveAction:
547 case Qt::TargetMoveAction:
549 case Qt::IgnoreAction:
550 return XCB_NONE;
551 default:
553 }
554}
555
556void QXcbDrag::readActionList()
557{
558 drop_actions.clear();
559 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource,
560 atom(QXcbAtom::AtomXdndActionList), XCB_ATOM_ATOM,
561 0, 1024);
562 if (reply && reply->type != XCB_NONE && reply->format == 32) {
563 int length = xcb_get_property_value_length(reply.get()) / 4;
564
565 xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
566 for (int i = 0; i < length; ++i)
567 drop_actions.append(atoms[i]);
568 }
569}
570
571void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions)
572{
573#ifndef QT_NO_CLIPBOARD
574 QList<xcb_atom_t> actions;
575 if (requestedAction != Qt::IgnoreAction)
576 actions.append(toXdndAction(requestedAction));
577
578 auto checkAppend = [this, requestedAction, supportedActions, &actions](Qt::DropAction action) {
579 if (requestedAction != action && supportedActions & action)
580 actions.append(toXdndAction(action));
581 };
582
583 checkAppend(Qt::CopyAction);
584 checkAppend(Qt::MoveAction);
585 checkAppend(Qt::LinkAction);
586
587 if (current_actions != actions) {
588 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->qtSelectionOwner(),
590 XCB_ATOM_ATOM, 32, actions.size(), actions.constData());
591 current_actions = actions;
592 }
593#else
594 Q_UNUSED(requestedAction)
595 Q_UNUSED(supportedActions)
596#endif
597}
598
599void QXcbDrag::startListeningForActionListChanges()
600{
601 connection()->addWindowEventListener(xdnd_dragsource, this);
602 const uint32_t event_mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
603 xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask);
604}
605
606void QXcbDrag::stopListeningForActionListChanges()
607{
608 const uint32_t event_mask[] = { XCB_EVENT_MASK_NO_EVENT };
609 xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask);
610 connection()->removeWindowEventListener(xdnd_dragsource);
611}
612
613int QXcbDrag::findTransactionByWindow(xcb_window_t window)
614{
615 int at = -1;
616 for (int i = 0; i < transactions.size(); ++i) {
617 const Transaction &t = transactions.at(i);
618 if (t.target == window || t.proxy_target == window) {
619 at = i;
620 break;
621 }
622 }
623 return at;
624}
625
626int QXcbDrag::findTransactionByTime(xcb_timestamp_t timestamp)
627{
628 int at = -1;
629 for (int i = 0; i < transactions.size(); ++i) {
630 const Transaction &t = transactions.at(i);
631 if (t.timestamp == timestamp) {
632 at = i;
633 break;
634 }
635 }
636 return at;
637}
638
639#if 0
640// for embedding only
641static QWidget* current_embedding_widget = nullptr;
642static xcb_client_message_event_t last_enter_event;
643
644
645static bool checkEmbedded(QWidget* w, const XEvent* xe)
646{
647 if (!w)
648 return false;
649
650 if (current_embedding_widget != 0 && current_embedding_widget != w) {
651 current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy;
652 current_proxy_target = current_target;
653 qt_xdnd_send_leave();
654 current_target = 0;
655 current_proxy_target = 0;
656 current_embedding_widget = 0;
657 }
658
659 QWExtra* extra = ((QExtraWidget*)w)->extraData();
660 if (extra && extra->xDndProxy != 0) {
661
662 if (current_embedding_widget != w) {
663
664 last_enter_event.xany.window = extra->xDndProxy;
665 XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event);
666 current_embedding_widget = w;
667 }
668
669 ((XEvent*)xe)->xany.window = extra->xDndProxy;
670 XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe);
671 if (currentWindow != w) {
672 currentWindow = w;
673 }
674 return true;
675 }
676 current_embedding_widget = 0;
677 return false;
678}
679#endif
680
681void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t *event, xcb_window_t proxy)
682{
683 // The target receives XdndEnter.
684 qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndEnter";
685
686 xdnd_types.clear();
687
688 int version = (int)(event->data.data32[1] >> 24);
689 if (version > xdnd_version)
690 return;
691
692 xdnd_dragsource = event->data.data32[0];
693 startListeningForActionListChanges();
694 readActionList();
695
696 if (!proxy)
697 proxy = xdndProxy(connection(), xdnd_dragsource);
698 current_proxy_target = proxy ? proxy : xdnd_dragsource;
699
700 if (event->data.data32[1] & 1) {
701 // get the types from XdndTypeList
702 auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource,
703 atom(QXcbAtom::AtomXdndTypelist), XCB_ATOM_ATOM,
704 0, xdnd_max_type);
705 if (reply && reply->type != XCB_NONE && reply->format == 32) {
706 int length = xcb_get_property_value_length(reply.get()) / 4;
707 if (length > xdnd_max_type)
708 length = xdnd_max_type;
709
710 xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
711 xdnd_types.reserve(length);
712 for (int i = 0; i < length; ++i)
713 xdnd_types.append(atoms[i]);
714 }
715 } else {
716 // get the types from the message
717 for(int i = 2; i < 5; i++) {
718 if (event->data.data32[i])
719 xdnd_types.append(event->data.data32[i]);
720 }
721 }
722 for(int i = 0; i < xdnd_types.size(); ++i)
723 qCDebug(lcQpaXDnd) << " " << connection()->atomName(xdnd_types.at(i));
724}
725
726void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *e,
727 Qt::MouseButtons b, Qt::KeyboardModifiers mods)
728{
729 // The target receives XdndPosition. The target window must determine which widget the mouse
730 // is in and ask it whether or not it will accept the drop.
731 qCDebug(lcQpaXDnd) << "target:" << e->window << "received XdndPosition";
732
733 QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff);
734 Q_ASSERT(w);
735 QRect geometry = w->geometry();
736 p -= w->isEmbedded() ? w->mapToGlobal(geometry.topLeft()) : geometry.topLeft();
737
738 if (!w || !w->window())
739 return;
740
741 if (Q_UNLIKELY(e->data.data32[0] != xdnd_dragsource)) {
742 qCDebug(lcQpaXDnd, "xdnd drag position from unexpected source (%x not %x)",
743 e->data.data32[0], xdnd_dragsource);
744 return;
745 }
746
747 currentPosition = p;
748 currentWindow = w->window();
749
750 // timestamp from the source
751 if (e->data.data32[3] != XCB_NONE) {
752 target_time = e->data.data32[3];
753 }
754
755 QMimeData *dropData = nullptr;
756 Qt::DropActions supported_actions = Qt::IgnoreAction;
757 if (currentDrag()) {
758 dropData = currentDrag()->mimeData();
759 supported_actions = currentDrag()->supportedActions();
760 } else {
761 dropData = m_dropData;
762 supported_actions = toDropActions(drop_actions);
763 if (e->data.data32[4] != atom(QXcbAtom::AtomXdndActionAsk))
764 supported_actions |= Qt::DropActions(toDropAction(e->data.data32[4]));
765 }
766
767 auto buttons = currentDrag() ? b : connection()->queryMouseButtons();
768 auto modifiers = currentDrag() ? mods : connection()->keyboard()->queryKeyboardModifiers();
769
770 QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(
771 w->window(), dropData, p, supported_actions, buttons, modifiers);
772
773 // ### FIXME ? - answerRect appears to be unused.
774 QRect answerRect(p + geometry.topLeft(), QSize(1,1));
775 answerRect = qt_response.answerRect().translated(geometry.topLeft()).intersected(geometry);
776
777 // The target sends a ClientMessage of type XdndStatus. This tells the source whether or not
778 // it will accept the drop, and, if so, what action will be taken. It also includes a rectangle
779 // that means "don't send another XdndPosition message until the mouse moves out of here".
780 xcb_client_message_event_t response;
781 response.response_type = XCB_CLIENT_MESSAGE;
782 response.sequence = 0;
783 response.window = xdnd_dragsource;
784 response.format = 32;
785 response.type = atom(QXcbAtom::AtomXdndStatus);
786 response.data.data32[0] = xcb_window(w);
787 response.data.data32[1] = qt_response.isAccepted(); // flags
788 response.data.data32[2] = 0; // x, y
789 response.data.data32[3] = 0; // w, h
790 response.data.data32[4] = toXdndAction(qt_response.acceptedAction()); // action
791
792 accepted_drop_action = qt_response.acceptedAction();
793
794 if (answerRect.left() < 0)
795 answerRect.setLeft(0);
796 if (answerRect.right() > 4096)
797 answerRect.setRight(4096);
798 if (answerRect.top() < 0)
799 answerRect.setTop(0);
800 if (answerRect.bottom() > 4096)
801 answerRect.setBottom(4096);
802 if (answerRect.width() < 0)
803 answerRect.setWidth(0);
804 if (answerRect.height() < 0)
805 answerRect.setHeight(0);
806
807 // reset
808 target_time = XCB_CURRENT_TIME;
809
810 qCDebug(lcQpaXDnd) << "sending XdndStatus to source:" << xdnd_dragsource;
811
812 if (xdnd_dragsource == connection()->qtSelectionOwner())
813 handle_xdnd_status(&response);
814 else
815 xcb_send_event(xcb_connection(), false, current_proxy_target,
816 XCB_EVENT_MASK_NO_EVENT, (const char *)&response);
817}
818
819namespace
820{
821 class ClientMessageScanner {
822 public:
823 ClientMessageScanner(xcb_atom_t a) : atom(a) {}
824 xcb_atom_t atom;
825 bool operator() (xcb_generic_event_t *event, int type) const {
826 if (type != XCB_CLIENT_MESSAGE)
827 return false;
828 auto clientMessage = reinterpret_cast<xcb_client_message_event_t *>(event);
829 return clientMessage->type == atom;
830 }
831 };
832}
833
834void QXcbDrag::handlePosition(QPlatformWindow * w, const xcb_client_message_event_t *event)
835{
836 xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
837 ClientMessageScanner scanner(atom(QXcbAtom::AtomXdndPosition));
838 while (auto nextEvent = connection()->eventQueue()->peek(scanner)) {
839 if (lastEvent != event)
840 free(lastEvent);
841 lastEvent = reinterpret_cast<xcb_client_message_event_t *>(nextEvent);
842 }
843
844 handle_xdnd_position(w, lastEvent);
845 if (lastEvent != event)
846 free(lastEvent);
847}
848
849void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event)
850{
851 // The source receives XdndStatus. It can use the action to change the cursor to indicate
852 // whether or not the user's requested action will be performed.
853 qCDebug(lcQpaXDnd) << "source:" << event->window << "received XdndStatus";
854 waiting_for_status = false;
855 // ignore late status messages
856 if (event->data.data32[0] && event->data.data32[0] != current_target)
857 return;
858
859 const bool dropPossible = event->data.data32[1];
860 setCanDrop(dropPossible);
861
862 if (dropPossible) {
863 accepted_drop_action = toDropAction(event->data.data32[4]);
864 updateCursor(accepted_drop_action);
865 } else {
866 updateCursor(Qt::IgnoreAction);
867 }
868
869 if ((event->data.data32[1] & 2) == 0) {
870 QPoint p((event->data.data32[2] & 0xffff0000) >> 16, event->data.data32[2] & 0x0000ffff);
871 QSize s((event->data.data32[3] & 0xffff0000) >> 16, event->data.data32[3] & 0x0000ffff);
872 source_sameanswer = QRect(p, s);
873 } else {
874 source_sameanswer = QRect();
875 }
876}
877
878void QXcbDrag::handleStatus(const xcb_client_message_event_t *event)
879{
880 if (event->window != connection()->qtSelectionOwner() || !drag())
881 return;
882
883 xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
884 xcb_generic_event_t *nextEvent;
885 ClientMessageScanner scanner(atom(QXcbAtom::AtomXdndStatus));
886 while ((nextEvent = connection()->eventQueue()->peek(scanner))) {
887 if (lastEvent != event)
888 free(lastEvent);
889 lastEvent = (xcb_client_message_event_t *)nextEvent;
890 }
891
892 handle_xdnd_status(lastEvent);
893 if (lastEvent != event)
894 free(lastEvent);
895}
896
897void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event)
898{
899 // If the target receives XdndLeave, it frees any cached data and forgets the whole incident.
900 qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndLeave";
901
902 if (!currentWindow || w != currentWindow.data()->handle()) {
903 stopListeningForActionListChanges();
904 return; // sanity
905 }
906
907 // ###
908// if (checkEmbedded(current_embedding_widget, event)) {
909// current_embedding_widget = 0;
910// currentWindow.clear();
911// return;
912// }
913
914 if (event->data.data32[0] != xdnd_dragsource) {
915 // This often happens - leave other-process window quickly
916 qCDebug(lcQpaXDnd, "xdnd drag leave from unexpected source (%x not %x",
917 event->data.data32[0], xdnd_dragsource);
918 }
919
920 stopListeningForActionListChanges();
921
922 QWindowSystemInterface::handleDrag(w->window(), nullptr, QPoint(), Qt::IgnoreAction, { }, { });
923}
924
925void QXcbDrag::send_leave()
926{
927 // XdndLeave is sent from the source to the target to cancel the drop.
928 if (!current_target)
929 return;
930
931 xcb_client_message_event_t leave;
932 leave.response_type = XCB_CLIENT_MESSAGE;
933 leave.sequence = 0;
934 leave.window = current_target;
935 leave.format = 32;
936 leave.type = atom(QXcbAtom::AtomXdndLeave);
937 leave.data.data32[0] = connection()->qtSelectionOwner();
938 leave.data.data32[1] = 0; // flags
939 leave.data.data32[2] = 0; // x, y
940 leave.data.data32[3] = 0; // w, h
941 leave.data.data32[4] = 0; // just null
942
943 QXcbWindow *w = connection()->platformWindowFromId(current_proxy_target);
944
945 qCDebug(lcQpaXDnd) << "sending XdndLeave to target:" << current_target;
946
947 if (w)
948 handleLeave(w, (const xcb_client_message_event_t *)&leave);
949 else
950 xcb_send_event(xcb_connection(), false,current_proxy_target,
951 XCB_EVENT_MASK_NO_EVENT, (const char *)&leave);
952}
953
954void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event,
955 Qt::MouseButtons b, Qt::KeyboardModifiers mods)
956{
957 // Target receives XdndDrop. Once it is finished processing the drop, it sends XdndFinished.
958 qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndDrop";
959
960 if (!currentWindow) {
961 stopListeningForActionListChanges();
962 xdnd_dragsource = 0;
963 return; // sanity
964 }
965
966 const uint32_t *l = event->data.data32;
967
968 if (l[0] != xdnd_dragsource) {
969 qCDebug(lcQpaXDnd, "xdnd drop from unexpected source (%x not %x", l[0], xdnd_dragsource);
970 return;
971 }
972
973 // update the "user time" from the timestamp in the event.
974 if (l[2] != 0)
975 target_time = l[2];
976
977 Qt::DropActions supported_drop_actions;
978 QMimeData *dropData = nullptr;
979 // this could be a same-application drop, just proxied due to
980 // some XEMBEDding, so try to find the real QMimeData used
981 // based on the timestamp for this drop.
982 int at = findTransactionByTime(target_time);
983 if (at != -1) {
984 qCDebug(lcQpaXDnd) << "found one transaction via findTransactionByTime()";
985 dropData = transactions.at(at).drag->mimeData();
986 // Can't use the source QMimeData if we need the image conversion code from xdndObtainData
987 if (dropData && dropData->hasImage())
988 dropData = 0;
989 }
990
991 const QDrag *currentDragObject = currentDrag();
992 // if we can't find it, then use the data in the drag manager
993 if (currentDragObject) {
994 if (!dropData)
995 dropData = currentDragObject->mimeData();
996 supported_drop_actions = Qt::DropActions(l[4]);
997 } else {
998 if (!dropData)
999 dropData = m_dropData;
1000 supported_drop_actions = accepted_drop_action | toDropActions(drop_actions);
1001 }
1002
1003 if (!dropData)
1004 return;
1005
1006 auto buttons = currentDragObject ? b : connection()->queryMouseButtons();
1007 auto modifiers = currentDragObject ? mods : connection()->keyboard()->queryKeyboardModifiers();
1008
1009 QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(
1010 currentWindow.data(), dropData, currentPosition, supported_drop_actions,
1011 buttons, modifiers);
1012
1013 Qt::DropAction acceptedAaction = response.acceptedAction();
1014 if (!response.isAccepted()) {
1015 // Ignore a failed drag
1016 acceptedAaction = Qt::IgnoreAction;
1017 }
1018 setExecutedDropAction(acceptedAaction);
1019
1020 xcb_client_message_event_t finished = {};
1021 finished.response_type = XCB_CLIENT_MESSAGE;
1022 finished.sequence = 0;
1023 finished.window = xdnd_dragsource;
1024 finished.format = 32;
1025 finished.type = atom(QXcbAtom::AtomXdndFinished);
1026 finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE;
1027 finished.data.data32[1] = response.isAccepted(); // flags
1028 finished.data.data32[2] = toXdndAction(acceptedAaction);
1029
1030 qCDebug(lcQpaXDnd) << "sending XdndFinished to source:" << xdnd_dragsource;
1031
1032 xcb_send_event(xcb_connection(), false, current_proxy_target,
1033 XCB_EVENT_MASK_NO_EVENT, (char *)&finished);
1034
1035 stopListeningForActionListChanges();
1036
1037 dropped = true;
1038}
1039
1040void QXcbDrag::handleFinished(const xcb_client_message_event_t *event)
1041{
1042 // Source receives XdndFinished when target is done processing the drop data.
1043 qCDebug(lcQpaXDnd) << "source:" << event->window << "received XdndFinished";
1044
1045 if (event->window != connection()->qtSelectionOwner())
1046 return;
1047
1048 if (xcb_window_t w = event->data.data32[0]) {
1049 int at = findTransactionByWindow(w);
1050 if (at != -1) {
1051
1052 Transaction t = transactions.takeAt(at);
1053 if (t.drag)
1054 t.drag->deleteLater();
1055// QDragManager *manager = QDragManager::self();
1056
1057// Window target = current_target;
1058// Window proxy_target = current_proxy_target;
1059// QWidget *embedding_widget = current_embedding_widget;
1060// QDrag *currentObject = manager->object;
1061
1062// current_target = t.target;
1063// current_proxy_target = t.proxy_target;
1064// current_embedding_widget = t.embedding_widget;
1065// manager->object = t.object;
1066
1067// if (!passive)
1068// (void) checkEmbedded(currentWindow, xe);
1069
1070// current_embedding_widget = 0;
1071// current_target = 0;
1072// current_proxy_target = 0;
1073
1074// current_target = target;
1075// current_proxy_target = proxy_target;
1076// current_embedding_widget = embedding_widget;
1077// manager->object = currentObject;
1078 } else {
1079 qWarning("QXcbDrag::handleFinished - drop data has expired");
1080 }
1081 }
1082 waiting_for_status = false;
1083}
1084
1085void QXcbDrag::timerEvent(QTimerEvent* e)
1086{
1087 if (e->id() == cleanup_timer.id()) {
1088 bool stopTimer = true;
1089 for (int i = 0; i < transactions.size(); ++i) {
1090 const Transaction &t = transactions.at(i);
1091 if (t.targetWindow) {
1092 // dnd within the same process, don't delete, these are taken care of
1093 // in handleFinished()
1094 continue;
1095 }
1096 QTime currentTime = QTime::currentTime();
1097 std::chrono::milliseconds delta{t.time.msecsTo(currentTime)};
1098 if (delta > XdndDropTransactionTimeout) {
1099 /* delete transactions which are older than XdndDropTransactionTimeout. It could mean
1100 one of these:
1101 - client has crashed and as a result we have never received XdndFinished
1102 - showing dialog box on drop event where user's response takes more time than XdndDropTransactionTimeout (QTBUG-14493)
1103 - dnd takes unusually long time to process data
1104 */
1105 if (t.drag)
1106 t.drag->deleteLater();
1107 transactions.removeAt(i--);
1108 } else {
1109 stopTimer = false;
1110 }
1111
1112 }
1113 if (stopTimer)
1114 cleanup_timer.stop();
1115 }
1116}
1117
1119{
1120 qCDebug(lcQpaXDnd) << "dnd was canceled";
1121
1122 QBasicDrag::cancel();
1123 if (current_target)
1124 send_leave();
1125
1126 // remove canceled object
1127 if (currentDrag())
1128 currentDrag()->deleteLater();
1129
1130 canceled = true;
1131}
1132
1133static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window)
1134{
1135 xcb_window_t target = 0;
1136 forever {
1137 // check if window has XdndAware
1138 auto gpReply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(), false, window,
1139 c->atom(QXcbAtom::AtomXdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
1140 bool aware = gpReply && gpReply->type != XCB_NONE;
1141 if (aware) {
1142 target = window;
1143 break;
1144 }
1145
1146 // try window's parent
1147 auto qtReply = Q_XCB_REPLY_UNCHECKED(xcb_query_tree, c->xcb_connection(), window);
1148 if (!qtReply)
1149 break;
1150 xcb_window_t root = qtReply->root;
1151 xcb_window_t parent = qtReply->parent;
1152 if (window == root)
1153 break;
1154 window = parent;
1155 }
1156 return target;
1157}
1158
1159void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event)
1160{
1161 qCDebug(lcQpaXDnd) << "handle selection request from target:" << event->requestor;
1162 q_padded_xcb_event<xcb_selection_notify_event_t> notify = {};
1163 notify.response_type = XCB_SELECTION_NOTIFY;
1164 notify.requestor = event->requestor;
1165 notify.selection = event->selection;
1166 notify.target = XCB_NONE;
1167 notify.property = XCB_NONE;
1168 notify.time = event->time;
1169
1170 // which transaction do we use? (note: -2 means use current currentDrag())
1171 int at = -1;
1172
1173 // figure out which data the requestor is really interested in
1174 if (currentDrag() && event->time == source_time) {
1175 // requestor wants the current drag data
1176 at = -2;
1177 } else {
1178 // if someone has requested data in response to XdndDrop, find the corresponding transaction. the
1179 // spec says to call xcb_convert_selection() using the timestamp from the XdndDrop
1180 at = findTransactionByTime(event->time);
1181 if (at == -1) {
1182 // no dice, perhaps the client was nice enough to use the same window id in
1183 // xcb_convert_selection() that we sent the XdndDrop event to.
1184 at = findTransactionByWindow(event->requestor);
1185 }
1186
1187 if (at == -1) {
1188 xcb_window_t target = findXdndAwareParent(connection(), event->requestor);
1189 if (target) {
1190 if (event->time == XCB_CURRENT_TIME && current_target == target)
1191 at = -2;
1192 else
1193 at = findTransactionByWindow(target);
1194 }
1195 }
1196 }
1197
1198 QDrag *transactionDrag = nullptr;
1199 if (at >= 0) {
1200 transactionDrag = transactions.at(at).drag;
1201 } else if (at == -2) {
1202 transactionDrag = currentDrag();
1203 }
1204
1205 if (transactionDrag) {
1206 xcb_atom_t atomFormat = event->target;
1207 int dataFormat = 0;
1208 QByteArray data;
1209 if (QXcbMime::mimeDataForAtom(connection(), event->target, transactionDrag->mimeData(),
1210 &data, &atomFormat, &dataFormat)) {
1211 int dataSize = data.size() / (dataFormat / 8);
1212 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, event->requestor, event->property,
1213 atomFormat, dataFormat, dataSize, (const void *)data.constData());
1214 notify.property = event->property;
1215 notify.target = atomFormat;
1216 }
1217 }
1218
1219 xcb_window_t proxy_target = xdndProxy(connection(), event->requestor);
1220 if (!proxy_target)
1221 proxy_target = event->requestor;
1222
1223 xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&notify);
1224}
1225
1226
1227bool QXcbDrag::dndEnable(QXcbWindow *window, bool on)
1228{
1229 qCDebug(lcQpaXDnd) << "dndEnable" << static_cast<QPlatformWindow *>(window) << on;
1230 // Windows announce that they support the XDND protocol by creating a window property XdndAware.
1231 if (on) {
1232 if (window) {
1233 qCDebug(lcQpaXDnd) << "setting XdndAware for" << window->xcb_window();
1234 xcb_atom_t atm = xdnd_version;
1235 xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window->xcb_window(),
1236 atom(QXcbAtom::AtomXdndAware), XCB_ATOM_ATOM, 32, 1, &atm);
1237 return true;
1238 } else {
1239 return false;
1240 }
1241 }
1242
1243 qCDebug(lcQpaXDnd) << "not deleting XDndAware";
1244 return true;
1245}
1246
1248{
1249 return true;
1250}
1251
1253 : QXcbMime(),
1254 drag(d)
1255{
1256}
1257
1261
1262QVariant QXcbDropData::retrieveData_sys(const QString &mimetype, QMetaType requestedType) const
1263{
1264 QByteArray mime = mimetype.toLatin1();
1265 QVariant data = xdndObtainData(mime, requestedType);
1266 return data;
1267}
1268
1269QVariant QXcbDropData::xdndObtainData(const QByteArray &format, QMetaType requestedType) const
1270{
1271 QXcbConnection *c = drag->connection();
1272 QXcbWindow *xcb_window = c->platformWindowFromId(drag->xdnd_dragsource);
1273 if (xcb_window && drag->currentDrag()) {
1274 QMimeData *data = drag->currentDrag()->mimeData();
1275 if (data->hasFormat(QLatin1StringView(format)))
1276 return data->data(QLatin1StringView(format));
1277 return QVariant();
1278 }
1279
1280 QList<xcb_atom_t> atoms = drag->xdnd_types;
1281 bool hasUtf8 = false;
1282 xcb_atom_t a = mimeAtomForFormat(c, QLatin1StringView(format), requestedType, atoms, &hasUtf8);
1283 if (a == XCB_NONE)
1284 return QVariant();
1285
1286#ifndef QT_NO_CLIPBOARD
1287 if (c->selectionOwner(c->atom(QXcbAtom::AtomXdndSelection)) == XCB_NONE)
1288 return QVariant(); // should never happen?
1289
1290 xcb_atom_t xdnd_selection = c->atom(QXcbAtom::AtomXdndSelection);
1291 const std::optional<QByteArray> result = c->clipboard()->getSelection(xdnd_selection, a, xdnd_selection, drag->targetTime());
1292 if (!result.has_value())
1293 return QVariant();
1294 return mimeConvertToFormat(c, a, result.value(), QLatin1StringView(format), requestedType, hasUtf8);
1295#else
1296 return QVariant();
1297#endif
1298}
1299
1300bool QXcbDropData::hasFormat_sys(const QString &format) const
1301{
1302 return formats().contains(format);
1303}
1304
1306{
1307 QStringList formats;
1308 for (int i = 0; i < drag->xdnd_types.size(); ++i) {
1309 QString f = mimeAtomToString(drag->connection(), drag->xdnd_types.at(i));
1310 if (!formats.contains(f))
1311 formats.append(f);
1312 }
1313 return formats;
1314}
1315
1316QT_END_NAMESPACE
Definition qlist.h:80
\inmodule QtCore\reentrant
Definition qpoint.h:29
constexpr QPoint(int xpos, int ypos) noexcept
Constructs a point with the given coordinates (xpos, ypos).
Definition qpoint.h:136
\inmodule QtCore \reentrant
Definition qdatetime.h:250
@ AtomXdndDrop
Definition qxcbatom.h:142
@ AtomXdndActionMove
Definition qxcbatom.h:154
@ AtomXdndStatus
Definition qxcbatom.h:140
@ AtomXdndActionLink
Definition qxcbatom.h:153
@ AtomXdndFinished
Definition qxcbatom.h:143
@ AtomXdndSelection
Definition qxcbatom.h:147
@ AtomXdndLeave
Definition qxcbatom.h:141
@ AtomXdndEnter
Definition qxcbatom.h:138
@ AtomXdndProxy
Definition qxcbatom.h:150
@ AtomXdndActionCopy
Definition qxcbatom.h:152
@ AtomXdndAware
Definition qxcbatom.h:149
@ AtomXdndTypelist
Definition qxcbatom.h:144
@ AtomXdndPosition
Definition qxcbatom.h:139
@ AtomXdndActionList
Definition qxcbatom.h:145
void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override
Definition qxcbdrag.cpp:334
Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override
Definition qxcbdrag.cpp:182
void timerEvent(QTimerEvent *e) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
bool findXdndAwareTarget(const QPoint &globalPos, xcb_window_t *target_out)
Definition qxcbdrag.cpp:282
xcb_timestamp_t targetTime()
Definition qxcbdrag.h:64
void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy=0)
Definition qxcbdrag.cpp:681
void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override
Definition qxcbdrag.cpp:471
void handleStatus(const xcb_client_message_event_t *event)
Definition qxcbdrag.cpp:878
void handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event, Qt::MouseButtons b={ }, Qt::KeyboardModifiers mods={ })
Definition qxcbdrag.cpp:954
void cancel() override
void handleFinished(const xcb_client_message_event_t *event)
bool eventFilter(QObject *o, QEvent *e) override
Filters events if this object has been installed as an event filter for the watched object.
Definition qxcbdrag.cpp:128
void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event)
Definition qxcbdrag.cpp:834
void startDrag() override
Definition qxcbdrag.cpp:139
bool ownsDragObject() const override
Returns bool indicating whether QPlatformDrag takes ownership and therefore responsibility of deletin...
void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event)
Definition qxcbdrag.cpp:897
QXcbDrag(QXcbConnection *c)
Definition qxcbdrag.cpp:90
void endDrag() override
Definition qxcbdrag.cpp:172
void handleSelectionRequest(const xcb_selection_request_event_t *event)
bool dndEnable(QXcbWindow *win, bool on)
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override
Definition qxcbdrag.cpp:190
QVariant retrieveData_sys(const QString &mimeType, QMetaType type) const override
QXcbDrag * drag
Definition qxcbdrag.cpp:86
bool hasFormat_sys(const QString &mimeType) const override
QVariant xdndObtainData(const QByteArray &format, QMetaType requestedType) const
QXcbDropData(QXcbDrag *d)
QStringList formats_sys() const override
QXcbConnection * connection() const
Definition qxcbobject.h:17
xcb_connection_t * xcb_connection() const
Definition qxcbobject.h:20
xcb_atom_t atom(QXcbAtom::Atom atom) const
Definition qxcbobject.h:19
QXcbObject(QXcbConnection *connection=nullptr)
Definition qxcbobject.h:14
bool compositingActive() const
#define Q_XCB_REPLY(call,...)
#define Q_XCB_REPLY_UNCHECKED(call,...)
static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w)
Definition qxcbdrag.cpp:45
const int xdnd_version
Definition qxcbdrag.cpp:33
static xcb_window_t xcb_window(QWindow *w)
Definition qxcbdrag.cpp:40
static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window)
static xcb_window_t xcb_window(QPlatformWindow *w)
Definition qxcbdrag.cpp:35
static bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint &pos, xcb_window_t w, xcb_shape_sk_t shapeType)
Definition qxcbdrag.cpp:199