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