15#include <private/qdnd_p.h>
18#include <qguiapplication.h>
22#include <qpa/qwindowsysteminterface.h>
24#include <private/qguiapplication_p.h>
25#include <private/qshapedpixmapdndwindow_p.h>
26#include <private/qsimpledrag_p.h>
27#include <private/qhighdpiscaling_p.h>
31using namespace Qt::StringLiterals;
37 return static_cast<QXcbWindow *>(w)->xcb_window();
42 return static_cast<QXcbWindow *>(w->handle())->xcb_window();
45static xcb_window_t
xdndProxy(QXcbConnection *c, xcb_window_t w)
47 xcb_window_t proxy = XCB_NONE;
49 auto reply =
Q_XCB_REPLY(xcb_get_property, c->xcb_connection(),
52 if (reply && reply->type == XCB_ATOM_WINDOW)
53 proxy = *((xcb_window_t *)xcb_get_property_value(reply.get()));
55 if (proxy == XCB_NONE)
59 reply =
Q_XCB_REPLY(xcb_get_property, c->xcb_connection(),
62 if (reply && reply->type == XCB_ATOM_WINDOW) {
63 xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply.get()));
84 QVariant
xdndObtainData(
const QByteArray &format, QMetaType requestedType)
const;
104 currentWindow.clear();
106 accepted_drop_action = Qt::IgnoreAction;
108 xdnd_dragsource = XCB_NONE;
110 waiting_for_status =
false;
111 current_target = XCB_NONE;
112 current_proxy_target = XCB_NONE;
114 source_time = XCB_CURRENT_TIME;
115 target_time = XCB_CURRENT_TIME;
117 QXcbCursor::queryPointer(connection(), ¤t_virtual_desktop,
nullptr);
125 source_sameanswer = QRect();
131
132
133
134 if (initiatorWindow && o == shapedPixmapWindow())
135 o = initiatorWindow.data();
136 return QBasicDrag::eventFilter(o, e);
143 qCDebug(lcQpaXDnd) <<
"starting drag where source:" << connection()->qtSelectionOwner();
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));
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());
161 setUseCompositing(current_virtual_desktop->compositingActive());
162 setScreen(current_virtual_desktop->screens().constFirst()->screen());
163 initiatorWindow = QGuiApplicationPrivate::currentMouseWindow;
164 QBasicDrag::startDrag();
166 shapedPixmapWindow()->setMouseGrabEnabled(
true);
168 auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), initiatorWindow.data());
169 move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
174 QBasicDrag::endDrag();
175 if (!dropped && !canceled && canDrop()) {
177 setExecutedDropAction(accepted_drop_action);
179 initiatorWindow.clear();
184 if (currentDrag() || drop_actions.isEmpty())
185 return QBasicDrag::defaultAction(possibleActions, modifiers);
187 return toDropAction(drop_actions.first());
201 bool interacts =
false;
202 auto reply =
Q_XCB_REPLY(xcb_shape_get_rectangles, connection, w, shapeType);
204 xcb_rectangle_t *rectangles = xcb_shape_get_rectangles_rectangles(reply.get());
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);
216xcb_window_t
QXcbDrag::findRealWindow(
const QPoint & pos, xcb_window_t w,
int md,
bool ignoreNonXdndAwareWindows)
218 if (w == shapedPixmapWindow()->handle()->winId())
226 if (reply->map_state != XCB_MAP_STATE_VIEWABLE)
233 QRect windowRect(greply->x, greply->y, greply->width, greply->height);
234 if (windowRect.contains(pos)) {
235 bool windowContainsMouse = !ignoreNonXdndAwareWindows;
238 false, w, connection()->atom(QXcbAtom::AtomXdndAware
),
239 XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
240 bool isAware = reply && reply->type != XCB_NONE;
242 const QPoint relPos = pos - windowRect.topLeft();
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);
250 windowContainsMouse =
true;
251 if (windowContainsMouse)
259 int nc = xcb_query_tree_children_length(reply.get());
260 xcb_window_t *c = xcb_query_tree_children(reply.get());
263 for (uint i = nc; !r && i--;)
264 r = findRealWindow(pos - windowRect.topLeft(), c[i], md-1, ignoreNonXdndAwareWindows);
273 if (!windowContainsMouse)
284 xcb_window_t rootwin = current_virtual_desktop->root();
286 rootwin, rootwin, globalPos.x(), globalPos.y());
290 xcb_window_t target = translate->child;
291 int lx = translate->dst_x;
292 int ly = translate->dst_y;
294 if (target && target != rootwin) {
295 xcb_window_t src = rootwin;
296 while (target != 0) {
297 qCDebug(lcQpaXDnd) <<
"checking target for XdndAware" << target;
300 src, target, lx, ly);
305 lx = translate->dst_x;
306 ly = translate->dst_y;
308 xcb_window_t child = translate->child;
312 bool aware = reply && reply->type != XCB_NONE;
314 qCDebug(lcQpaXDnd) <<
"found XdndAware on" << target;
321 if (!target || target == shapedPixmapWindow()->handle()->winId()) {
322 qCDebug(lcQpaXDnd) <<
"need to find real window";
323 target = findRealWindow(globalPos, rootwin, 6,
true);
325 target = findRealWindow(globalPos, rootwin, 6,
false);
326 qCDebug(lcQpaXDnd) <<
"real window found" << target;
330 *target_out = target;
337 if (!currentDrag()) {
342 if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
348 QXcbScreen *screen = virtualDesktop->screenAt(cursorPos);
349 QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen);
351 if (virtualDesktop != current_virtual_desktop) {
353 recreateShapedPixmapWindow(
static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos);
355 shapedPixmapWindow()->setMouseGrabEnabled(
true);
357 current_virtual_desktop = virtualDesktop;
359 QBasicDrag::moveShapedPixmapWindow(deviceIndependentPos);
366 QXcbWindow *w =
nullptr;
368 w =
connection()->platformWindowFromId(target);
371 target = current_virtual_desktop->root();
374 xcb_window_t proxy_target = xdndProxy(
connection(), target);
376 proxy_target = target;
377 int target_version = 1;
383 if (!reply || reply->type == XCB_NONE) {
386 target_version = *(uint32_t *)xcb_get_property_value(reply.get());
387 target_version = qMin(
xdnd_version, target_version ? target_version : 1);
391 if (target != current_target) {
395 current_target = target;
396 current_proxy_target = proxy_target;
398 int flags = target_version << 24;
399 if (drag_types.size() > 3)
402 xcb_client_message_event_t enter;
403 enter.response_type = XCB_CLIENT_MESSAGE;
405 enter.window = target;
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;
414 source_sameanswer = QRect(globalPos.x() - 2, globalPos.y() - 2 , 5, 5);
416 qCDebug(lcQpaXDnd) <<
"sending XdndEnter to target:" << target;
421 xcb_send_event(
xcb_connection(),
false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (
const char *)&enter);
422 waiting_for_status =
false;
426 if (waiting_for_status)
430 waiting_for_status =
true;
433 xcb_client_message_event_t move;
434 move.response_type = XCB_CLIENT_MESSAGE;
436 move.window = target;
439 move.data.data32[0] =
connection()->qtSelectionOwner();
440 move.data.data32[1] = 0;
441 move.data.data32[2] = (globalPos.x() << 16) + globalPos.y();
443 const auto supportedActions = currentDrag()->supportedActions();
444 const auto requestedAction = defaultAction(supportedActions, mods);
445 move.data.data32[4] = toXdndAction(requestedAction);
447 qCDebug(lcQpaXDnd) <<
"sending XdndPosition to target:" << target;
452 handle_xdnd_position(w, &move, b, mods);
454 setActionList(requestedAction, supportedActions);
455 xcb_send_event(
xcb_connection(),
false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (
const char *)&move);
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;
465 if (target == xdndCollectionWindow) {
467 updateCursor(Qt::IgnoreAction);
474 QBasicDrag::drop(globalPos, b, mods);
479 xcb_client_message_event_t drop;
480 drop.response_type = XCB_CLIENT_MESSAGE;
482 drop.window = current_target;
485 drop.data.data32[0] =
connection()->qtSelectionOwner();
486 drop.data.data32[1] = 0;
489 drop.data.data32[3] = 0;
490 drop.data.data32[4] = currentDrag()->supportedActions();
492 QXcbWindow *w =
connection()->platformWindowFromId(current_proxy_target);
495 connection()->time(),
497 current_proxy_target,
503 transactions.append(t);
506 if (!t.targetWindow && !cleanup_timer.isActive())
507 cleanup_timer.start(XdndDropTransactionTimeout,
this);
509 qCDebug(lcQpaXDnd) <<
"sending drop to target:" << current_target;
512 handleDrop(w, &drop, b, mods);
514 xcb_send_event(
xcb_connection(),
false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (
const char *)&drop);
518Qt::DropAction
QXcbDrag::toDropAction(xcb_atom_t a)
const
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;
529Qt::DropActions
QXcbDrag::toDropActions(
const QList<xcb_atom_t> &atoms)
const
531 Qt::DropActions actions;
532 for (
const auto actionAtom : atoms) {
533 if (actionAtom != atom(QXcbAtom::AtomXdndActionAsk))
534 actions |= toDropAction(actionAtom);
539xcb_atom_t
QXcbDrag::toXdndAction(Qt::DropAction a)
const
547 case Qt::TargetMoveAction:
549 case Qt::IgnoreAction:
558 drop_actions.clear();
560 atom(QXcbAtom::AtomXdndActionList), XCB_ATOM_ATOM,
562 if (reply && reply->type != XCB_NONE && reply->format == 32) {
563 int length = xcb_get_property_value_length(reply.get()) / 4;
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]);
571void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions)
573#ifndef QT_NO_CLIPBOARD
574 QList<xcb_atom_t> actions;
575 if (requestedAction != Qt::IgnoreAction)
576 actions.append(toXdndAction(requestedAction));
578 auto checkAppend = [
this, requestedAction, supportedActions, &actions](Qt::DropAction action) {
579 if (requestedAction != action && supportedActions & action)
580 actions.append(toXdndAction(action));
583 checkAppend(Qt::CopyAction);
584 checkAppend(Qt::MoveAction);
585 checkAppend(Qt::LinkAction);
587 if (current_actions != actions) {
590 XCB_ATOM_ATOM, 32, actions.size(), actions.constData());
591 current_actions = actions;
594 Q_UNUSED(requestedAction)
595 Q_UNUSED(supportedActions)
599void QXcbDrag::startListeningForActionListChanges()
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);
606void QXcbDrag::stopListeningForActionListChanges()
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);
613int QXcbDrag::findTransactionByWindow(xcb_window_t window)
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) {
626int QXcbDrag::findTransactionByTime(xcb_timestamp_t timestamp)
629 for (
int i = 0; i < transactions.size(); ++i) {
630 const Transaction &t = transactions.at(i);
631 if (t.timestamp == timestamp) {
641static QWidget* current_embedding_widget =
nullptr;
642static xcb_client_message_event_t last_enter_event;
645static bool checkEmbedded(QWidget* w,
const XEvent* xe)
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();
655 current_proxy_target = 0;
656 current_embedding_widget = 0;
659 QWExtra* extra = ((QExtraWidget*)w)->extraData();
660 if (extra && extra->xDndProxy != 0) {
662 if (current_embedding_widget != w) {
664 last_enter_event.xany.window = extra->xDndProxy;
665 XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event);
666 current_embedding_widget = w;
669 ((XEvent*)xe)->xany.window = extra->xDndProxy;
670 XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe);
671 if (currentWindow != w) {
676 current_embedding_widget = 0;
684 qCDebug(lcQpaXDnd) <<
"target:" << event->window <<
"received XdndEnter";
688 int version = (
int)(event->data.data32[1] >> 24);
692 xdnd_dragsource = event->data.data32[0];
693 startListeningForActionListChanges();
697 proxy = xdndProxy(
connection(), xdnd_dragsource);
698 current_proxy_target = proxy ? proxy : xdnd_dragsource;
700 if (event->data.data32[1] & 1) {
703 atom(QXcbAtom::AtomXdndTypelist
), XCB_ATOM_ATOM,
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;
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]);
717 for(
int i = 2; i < 5; i++) {
718 if (event->data.data32[i])
719 xdnd_types.append(event->data.data32[i]);
722 for(
int i = 0; i < xdnd_types.size(); ++i)
723 qCDebug(lcQpaXDnd) <<
" " << connection()->atomName(xdnd_types.at(i));
726void QXcbDrag::handle_xdnd_position(QPlatformWindow *w,
const xcb_client_message_event_t *e,
727 Qt::MouseButtons b, Qt::KeyboardModifiers mods)
731 qCDebug(lcQpaXDnd) <<
"target:" << e->window <<
"received XdndPosition";
733 QPoint p
((e->data.data32[2] & 0xffff0000) >> 16
, e->data.data32[2] & 0x0000ffff
);
735 QRect geometry = w->geometry();
736 p -= w->isEmbedded() ? w->mapToGlobal(geometry.topLeft()) : geometry.topLeft();
738 if (!w || !w->window())
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);
748 currentWindow = w->window();
751 if (e->data.data32[3] != XCB_NONE) {
752 target_time = e->data.data32[3];
755 QMimeData *dropData =
nullptr;
756 Qt::DropActions supported_actions = Qt::IgnoreAction;
758 dropData = currentDrag()->mimeData();
759 supported_actions = currentDrag()->supportedActions();
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]));
767 auto buttons = currentDrag() ? b :
connection()->queryMouseButtons();
768 auto modifiers = currentDrag() ? mods :
connection()->keyboard()->queryKeyboardModifiers();
770 QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(
771 w->window(), dropData, p, supported_actions, buttons, modifiers);
774 QRect answerRect(p + geometry.topLeft(), QSize(1,1));
775 answerRect = qt_response.answerRect().translated(geometry.topLeft()).intersected(geometry);
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;
787 response.data.data32[1] = qt_response.isAccepted();
788 response.data.data32[2] = 0;
789 response.data.data32[3] = 0;
790 response.data.data32[4] = toXdndAction(qt_response.acceptedAction());
792 accepted_drop_action = qt_response.acceptedAction();
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);
808 target_time = XCB_CURRENT_TIME;
810 qCDebug(lcQpaXDnd) <<
"sending XdndStatus to source:" << xdnd_dragsource;
812 if (xdnd_dragsource ==
connection()->qtSelectionOwner())
813 handle_xdnd_status(&response);
816 XCB_EVENT_MASK_NO_EVENT, (
const char *)&response);
821 class ClientMessageScanner {
823 ClientMessageScanner(xcb_atom_t a) : atom(a) {}
825 bool operator() (xcb_generic_event_t *event,
int type)
const {
826 if (type != XCB_CLIENT_MESSAGE)
828 auto clientMessage =
reinterpret_cast<xcb_client_message_event_t *>(event);
829 return clientMessage->type == atom;
836 xcb_client_message_event_t *lastEvent =
const_cast<xcb_client_message_event_t *>(event);
838 while (
auto nextEvent = connection()->eventQueue()->peek(scanner)) {
839 if (lastEvent != event)
841 lastEvent =
reinterpret_cast<xcb_client_message_event_t *>(nextEvent);
844 handle_xdnd_position(w, lastEvent);
845 if (lastEvent != event)
849void QXcbDrag::handle_xdnd_status(
const xcb_client_message_event_t *event)
853 qCDebug(lcQpaXDnd) <<
"source:" << event->window <<
"received XdndStatus";
854 waiting_for_status =
false;
856 if (event->data.data32[0] && event->data.data32[0] != current_target)
859 const bool dropPossible = event->data.data32[1];
860 setCanDrop(dropPossible);
863 accepted_drop_action = toDropAction(event->data.data32[4]);
864 updateCursor(accepted_drop_action);
866 updateCursor(Qt::IgnoreAction);
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);
874 source_sameanswer = QRect();
880 if (event->window !=
connection()->qtSelectionOwner() || !drag())
883 xcb_client_message_event_t *lastEvent =
const_cast<xcb_client_message_event_t *>(event);
884 xcb_generic_event_t *nextEvent;
886 while ((nextEvent =
connection()->eventQueue()->peek(scanner))) {
887 if (lastEvent != event)
889 lastEvent = (xcb_client_message_event_t *)nextEvent;
892 handle_xdnd_status(lastEvent);
893 if (lastEvent != event)
900 qCDebug(lcQpaXDnd) <<
"target:" << event->window <<
"received XdndLeave";
902 if (!currentWindow || w != currentWindow.data()->handle()) {
903 stopListeningForActionListChanges();
914 if (event->data.data32[0] != xdnd_dragsource) {
916 qCDebug(lcQpaXDnd,
"xdnd drag leave from unexpected source (%x not %x",
917 event->data.data32[0], xdnd_dragsource);
920 stopListeningForActionListChanges();
922 QWindowSystemInterface::handleDrag(w->window(),
nullptr, QPoint(), Qt::IgnoreAction, { }, { });
931 xcb_client_message_event_t leave;
932 leave.response_type = XCB_CLIENT_MESSAGE;
934 leave.window = current_target;
937 leave.data.data32[0] =
connection()->qtSelectionOwner();
938 leave.data.data32[1] = 0;
939 leave.data.data32[2] = 0;
940 leave.data.data32[3] = 0;
941 leave.data.data32[4] = 0;
943 QXcbWindow *w =
connection()->platformWindowFromId(current_proxy_target);
945 qCDebug(lcQpaXDnd) <<
"sending XdndLeave to target:" << current_target;
948 handleLeave(w, (
const xcb_client_message_event_t *)&leave);
951 XCB_EVENT_MASK_NO_EVENT, (
const char *)&leave);
955 Qt::MouseButtons b, Qt::KeyboardModifiers mods)
958 qCDebug(lcQpaXDnd) <<
"target:" << event->window <<
"received XdndDrop";
960 if (!currentWindow) {
961 stopListeningForActionListChanges();
966 const uint32_t *l = event->data.data32;
968 if (l[0] != xdnd_dragsource) {
969 qCDebug(lcQpaXDnd,
"xdnd drop from unexpected source (%x not %x", l[0], xdnd_dragsource);
977 Qt::DropActions supported_drop_actions;
978 QMimeData *dropData =
nullptr;
982 int at = findTransactionByTime(target_time);
984 qCDebug(lcQpaXDnd) <<
"found one transaction via findTransactionByTime()";
985 dropData = transactions.at(at).drag->mimeData();
987 if (dropData && dropData->hasImage())
991 const QDrag *currentDragObject = currentDrag();
993 if (currentDragObject) {
995 dropData = currentDragObject->mimeData();
996 supported_drop_actions = Qt::DropActions(l[4]);
999 dropData = m_dropData;
1000 supported_drop_actions = accepted_drop_action | toDropActions(drop_actions);
1006 auto buttons = currentDragObject ? b :
connection()->queryMouseButtons();
1007 auto modifiers = currentDragObject ? mods :
connection()->keyboard()->queryKeyboardModifiers();
1009 QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(
1010 currentWindow.data(), dropData, currentPosition, supported_drop_actions,
1011 buttons, modifiers);
1013 Qt::DropAction acceptedAaction = response.acceptedAction();
1014 if (!response.isAccepted()) {
1016 acceptedAaction = Qt::IgnoreAction;
1018 setExecutedDropAction(acceptedAaction);
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;
1026 finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE;
1027 finished.data.data32[1] = response.isAccepted();
1028 finished.data.data32[2] = toXdndAction(acceptedAaction);
1030 qCDebug(lcQpaXDnd) <<
"sending XdndFinished to source:" << xdnd_dragsource;
1033 XCB_EVENT_MASK_NO_EVENT, (
char *)&finished);
1035 stopListeningForActionListChanges();
1043 qCDebug(lcQpaXDnd) <<
"source:" << event->window <<
"received XdndFinished";
1045 if (event->window !=
connection()->qtSelectionOwner())
1048 if (xcb_window_t w = event->data.data32[0]) {
1049 int at = findTransactionByWindow(w);
1052 Transaction t = transactions.takeAt(at);
1054 t.drag->deleteLater();
1079 qWarning(
"QXcbDrag::handleFinished - drop data has expired");
1082 waiting_for_status =
false;
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) {
1096 QTime currentTime = QTime::currentTime();
1097 std::chrono::milliseconds delta{t.time.msecsTo(currentTime)};
1098 if (delta > XdndDropTransactionTimeout) {
1100
1101
1102
1103
1104
1106 t.drag->deleteLater();
1107 transactions.removeAt(i--);
1114 cleanup_timer.stop();
1120 qCDebug(lcQpaXDnd) <<
"dnd was canceled";
1122 QBasicDrag::cancel();
1128 currentDrag()->deleteLater();
1135 xcb_window_t target = 0;
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;
1150 xcb_window_t root = qtReply->root;
1151 xcb_window_t parent = qtReply->parent;
1161 qCDebug(lcQpaXDnd) <<
"handle selection request from target:" << event->requestor;
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;
1174 if (currentDrag() && event->time == source_time) {
1180 at = findTransactionByTime(event->time);
1184 at = findTransactionByWindow(event->requestor);
1188 xcb_window_t target = findXdndAwareParent(
connection(), event->requestor);
1190 if (event->time == XCB_CURRENT_TIME && current_target == target)
1193 at = findTransactionByWindow(target);
1198 QDrag *transactionDrag =
nullptr;
1200 transactionDrag = transactions.at(at).drag;
1201 }
else if (at == -2) {
1202 transactionDrag = currentDrag();
1205 if (transactionDrag) {
1206 xcb_atom_t atomFormat = event->target;
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;
1219 xcb_window_t proxy_target = xdndProxy(
connection(), event->requestor);
1221 proxy_target = event->requestor;
1223 xcb_send_event(
xcb_connection(),
false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (
const char *)¬ify);
1229 qCDebug(lcQpaXDnd) <<
"dndEnable" <<
static_cast<QPlatformWindow *>(window) << on;
1233 qCDebug(lcQpaXDnd) <<
"setting XdndAware for" << window->xcb_window();
1235 xcb_change_property(
xcb_connection(), XCB_PROP_MODE_REPLACE, window->xcb_window(),
1243 qCDebug(lcQpaXDnd) <<
"not deleting XDndAware";
1264 QByteArray mime = mimetype.toLatin1();
1265 QVariant data = xdndObtainData(mime, requestedType);
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));
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);
1286#ifndef QT_NO_CLIPBOARD
1291 const std::optional<QByteArray> result = c->clipboard()->getSelection(xdnd_selection, a, xdnd_selection,
drag->targetTime());
1292 if (!result.has_value())
1294 return mimeConvertToFormat(c, a, result.value(), QLatin1StringView(format), requestedType, hasUtf8);
1302 return formats().contains(format);
1307 QStringList formats;
1308 for (
int i = 0; i <
drag->xdnd_types.size(); ++i) {
1310 if (!formats.contains(f))
\inmodule QtCore\reentrant
constexpr QPoint(int xpos, int ypos) noexcept
Constructs a point with the given coordinates (xpos, ypos).
\inmodule QtCore \reentrant
void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override
Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override
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)
xcb_timestamp_t targetTime()
void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy=0)
void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override
void handleStatus(const xcb_client_message_event_t *event)
void handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event, Qt::MouseButtons b={ }, Qt::KeyboardModifiers mods={ })
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.
void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event)
void startDrag() override
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)
QXcbDrag(QXcbConnection *c)
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
QVariant retrieveData_sys(const QString &mimeType, QMetaType type) const override
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
xcb_connection_t * xcb_connection() const
xcb_atom_t atom(QXcbAtom::Atom atom) const
QXcbObject(QXcbConnection *connection=nullptr)
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)
static xcb_window_t xcb_window(QWindow *w)
static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window)
static xcb_window_t xcb_window(QPlatformWindow *w)
static bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint &pos, xcb_window_t w, xcb_shape_sk_t shapeType)