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