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
qwindowsdrag.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 <QtCore/qt_windows.h>
5#include "qwindowsdrag.h"
6#include "qwindowscontext.h"
7#include "qwindowsscreen.h"
8#if QT_CONFIG(clipboard)
9# include "qwindowsclipboard.h"
10#endif
11#include "qwindowsintegration.h"
13#include "qwindowswindow.h"
15#include "qwindowscursor.h"
16#include "qwindowskeymapper.h"
17
18#include <QtGui/qevent.h>
19#include <QtGui/qpixmap.h>
20#include <QtGui/qpainter.h>
21#include <QtGui/qrasterwindow.h>
22#include <QtGui/qguiapplication.h>
23#include <qpa/qwindowsysteminterface_p.h>
24#include <QtGui/private/qdnd_p.h>
25#include <QtGui/private/qguiapplication_p.h>
26#include <QtGui/private/qhighdpiscaling_p.h>
27
28#include <QtCore/qdebug.h>
29#include <QtCore/qbuffer.h>
30#include <QtCore/qpoint.h>
31#include <QtCore/qpointer.h>
32#include <QtCore/private/qcomobject_p.h>
33
34#include <shlobj.h>
35
37
47{
48public:
49 explicit QWindowsDragCursorWindow(QWindow *parent = nullptr);
50
51 void setPixmap(const QPixmap &p);
52
53protected:
54 void paintEvent(QPaintEvent *) override
55 {
56 QPainter painter(this);
57 painter.drawPixmap(0, 0, m_pixmap);
58 }
59
60private:
61 QPixmap m_pixmap;
62};
63
65 : QRasterWindow(parent)
66{
67 QSurfaceFormat windowFormat = format();
68 windowFormat.setAlphaBufferSize(8);
69 setFormat(windowFormat);
70 setObjectName(QStringLiteral("QWindowsDragCursorWindow"));
74}
75
77{
78 if (p.cacheKey() == m_pixmap.cacheKey())
79 return;
80 const QSize oldSize = m_pixmap.size();
81 QSize newSize = p.size();
82 qCDebug(lcQpaMime) << __FUNCTION__ << p.cacheKey() << newSize;
83 m_pixmap = p;
84 if (oldSize != newSize) {
85 const qreal pixDevicePixelRatio = p.devicePixelRatio();
86 if (pixDevicePixelRatio > 1.0 && qFuzzyCompare(pixDevicePixelRatio, devicePixelRatio()))
87 newSize /= qRound(pixDevicePixelRatio);
88 resize(newSize);
89 }
90 if (isVisible())
91 update();
92}
93
106{
107 return QWindowsDrag::instance()->dropDataObject();
108}
109
110static inline Qt::DropActions translateToQDragDropActions(DWORD pdwEffects)
111{
112 Qt::DropActions actions = Qt::IgnoreAction;
113 if (pdwEffects & DROPEFFECT_LINK)
114 actions |= Qt::LinkAction;
115 if (pdwEffects & DROPEFFECT_COPY)
116 actions |= Qt::CopyAction;
117 if (pdwEffects & DROPEFFECT_MOVE)
118 actions |= Qt::MoveAction;
119 return actions;
120}
121
122static inline Qt::DropAction translateToQDragDropAction(DWORD pdwEffect)
123{
124 if (pdwEffect & DROPEFFECT_LINK)
125 return Qt::LinkAction;
126 if (pdwEffect & DROPEFFECT_COPY)
127 return Qt::CopyAction;
128 if (pdwEffect & DROPEFFECT_MOVE)
129 return Qt::MoveAction;
130 return Qt::IgnoreAction;
131}
132
133static inline DWORD translateToWinDragEffects(Qt::DropActions action)
134{
135 DWORD effect = DROPEFFECT_NONE;
136 if (action & Qt::LinkAction)
137 effect |= DROPEFFECT_LINK;
138 if (action & Qt::CopyAction)
139 effect |= DROPEFFECT_COPY;
140 if (action & Qt::MoveAction)
141 effect |= DROPEFFECT_MOVE;
142 return effect;
143}
144
145static inline Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState)
146{
147 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
148
149 if (keyState & MK_SHIFT)
151 if (keyState & MK_CONTROL)
153 if (keyState & MK_ALT)
155
156 return modifiers;
157}
158
159static Qt::KeyboardModifiers lastModifiers = Qt::NoModifier;
160static Qt::MouseButtons lastButtons = Qt::NoButton;
161
172class QWindowsOleDropSource : public QComObject<IDropSource>
173{
174public:
175 enum Mode {
177 TouchDrag // Mouse cursor suppressed, use window as cursor.
178 };
179
180 explicit QWindowsOleDropSource(QWindowsDrag *drag);
181 ~QWindowsOleDropSource() override;
182
183 void createCursors();
184
185 // IDropSource methods
186 STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState) override;
187 STDMETHOD(GiveFeedback)(DWORD dwEffect) override;
188
189private:
190 struct CursorEntry {
191 CursorEntry() : cacheKey(0) {}
192 CursorEntry(const QPixmap &p, qint64 cK, const CursorHandlePtr &c, const QPoint &h) :
193 pixmap(p), cacheKey(cK), cursor(c), hotSpot(h) {}
194
195 QPixmap pixmap;
196 qint64 cacheKey; // Cache key of cursor
197 CursorHandlePtr cursor;
198 QPoint hotSpot;
199 };
200
201 typedef QMap<Qt::DropAction, CursorEntry> ActionCursorMap;
202
203 Mode m_mode;
204 QWindowsDrag *m_drag;
205 QPointer<QWindow> m_windowUnderMouse;
206 Qt::MouseButtons m_currentButtons;
207 ActionCursorMap m_cursors;
208 QWindowsDragCursorWindow *m_touchDragWindow;
209
210#ifndef QT_NO_DEBUG_STREAM
211 friend QDebug operator<<(QDebug, const QWindowsOleDropSource::CursorEntry &);
212#endif
213};
214
216 : m_mode(QWindowsCursor::cursorState() != QWindowsCursor::State::Suppressed ? MouseDrag : TouchDrag)
217 , m_drag(drag)
218 , m_windowUnderMouse(QWindowsContext::instance()->windowUnderMouse())
219 , m_currentButtons(Qt::NoButton)
220 , m_touchDragWindow(nullptr)
221{
222 qCDebug(lcQpaMime) << __FUNCTION__ << m_mode;
223}
224
226{
227 m_cursors.clear();
228 delete m_touchDragWindow;
229 qCDebug(lcQpaMime) << __FUNCTION__;
230}
231
232#ifndef QT_NO_DEBUG_STREAM
233QDebug operator<<(QDebug d, const QWindowsOleDropSource::CursorEntry &e)
234{
235 d << "CursorEntry:" << e.pixmap.size() << '#' << e.cacheKey
236 << "HCURSOR" << e.cursor->handle() << "hotspot:" << e.hotSpot;
237 return d;
238}
239#endif // !QT_NO_DEBUG_STREAM
240
246{
247 const QDrag *drag = m_drag->currentDrag();
248 const QPixmap pixmap = drag->pixmap();
249 const bool hasPixmap = !pixmap.isNull();
250
251 // Find screen for drag. Could be obtained from QDrag::source(), but that might be a QWidget.
252 const QPlatformScreen *platformScreen = QWindowsContext::instance()->screenManager().screenAtDp(QWindowsCursor::mousePosition());
253 if (!platformScreen) {
254 if (const QScreen *primaryScreen = QGuiApplication::primaryScreen())
255 platformScreen = primaryScreen->handle();
256 }
257 Q_ASSERT(platformScreen);
258 QPlatformCursor *platformCursor = platformScreen->cursor();
259
260 if (GetSystemMetrics (SM_REMOTESESSION) != 0) {
261 /* Workaround for RDP issues with large cursors.
262 * Touch drag window seems to work just fine...
263 * 96 pixel is a 'large' mouse cursor, according to RDP spec */
264 const int rdpLargeCursor = qRound(qreal(96) / QHighDpiScaling::factor(platformScreen));
265 if (pixmap.width() > rdpLargeCursor || pixmap.height() > rdpLargeCursor)
266 m_mode = TouchDrag;
267 }
268
269 qreal pixmapScaleFactor = 1;
270 qreal hotSpotScaleFactor = 1;
271 if (m_mode != TouchDrag) { // Touch drag: pixmap is shown in a separate QWindow, which will be scaled.)
272 hotSpotScaleFactor = QHighDpiScaling::factor(platformScreen);
273 pixmapScaleFactor = hotSpotScaleFactor / pixmap.devicePixelRatio();
274 }
275 QPixmap scaledPixmap = qFuzzyCompare(pixmapScaleFactor, 1.0)
276 ? pixmap
277 : pixmap.scaled((QSizeF(pixmap.size()) * pixmapScaleFactor).toSize(),
279 scaledPixmap.setDevicePixelRatio(1);
280
282 int actionCount = int(sizeof(actions) / sizeof(actions[0]));
283 if (!hasPixmap)
284 --actionCount; // No Qt::IgnoreAction unless pixmap
285 const QPoint hotSpot = qFuzzyCompare(hotSpotScaleFactor, 1.0)
286 ? drag->hotSpot()
287 : (QPointF(drag->hotSpot()) * hotSpotScaleFactor).toPoint();
288 for (int cnum = 0; cnum < actionCount; ++cnum) {
289 const Qt::DropAction action = actions[cnum];
290 QPixmap cursorPixmap = drag->dragCursor(action);
291 if (cursorPixmap.isNull() && platformCursor)
292 cursorPixmap = static_cast<QWindowsCursor *>(platformCursor)->dragDefaultCursor(action);
293 const qint64 cacheKey = cursorPixmap.cacheKey();
294 const auto it = m_cursors.find(action);
295 if (it != m_cursors.end() && it.value().cacheKey == cacheKey)
296 continue;
297 if (cursorPixmap.isNull()) {
298 qWarning("%s: Unable to obtain drag cursor for %d.", __FUNCTION__, action);
299 continue;
300 }
301
302 QPoint newHotSpot(0, 0);
303 QPixmap newPixmap = cursorPixmap;
304
305 if (hasPixmap) {
306 const int x1 = qMin(-hotSpot.x(), 0);
307 const int x2 = qMax(scaledPixmap.width() - hotSpot.x(), cursorPixmap.width());
308 const int y1 = qMin(-hotSpot.y(), 0);
309 const int y2 = qMax(scaledPixmap.height() - hotSpot.y(), cursorPixmap.height());
310 QPixmap newCursor(x2 - x1 + 1, y2 - y1 + 1);
311 newCursor.fill(Qt::transparent);
312 QPainter p(&newCursor);
313 const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y()));
314 p.drawPixmap(pmDest, scaledPixmap);
315 p.drawPixmap(qMax(0, hotSpot.x()),qMax(0, hotSpot.y()), cursorPixmap);
316 newPixmap = newCursor;
317 newHotSpot = QPoint(qMax(0, hotSpot.x()), qMax(0, hotSpot.y()));
318 }
319
320 if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newPixmap, newHotSpot)) {
321 const CursorEntry entry(newPixmap, cacheKey, CursorHandlePtr(new CursorHandle(sysCursor)), newHotSpot);
322 if (it == m_cursors.end())
323 m_cursors.insert(action, entry);
324 else
325 it.value() = entry;
326 }
327 }
328#ifndef QT_NO_DEBUG_OUTPUT
329 if (lcQpaMime().isDebugEnabled())
330 qCDebug(lcQpaMime) << __FUNCTION__ << "pixmap" << pixmap.size() << m_cursors.size() << "cursors:\n" << m_cursors;
331#endif // !QT_NO_DEBUG_OUTPUT
332}
333
338QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
339QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
340{
341 // In some rare cases, when a mouse button is released but the mouse is static,
342 // grfKeyState will not be updated with these released buttons until the mouse
343 // is moved. So we use the async key state given by queryMouseButtons() instead.
344 Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons();
345
346 SCODE result = S_OK;
347 if (fEscapePressed || QWindowsDrag::isCanceled()) {
348 result = DRAGDROP_S_CANCEL;
349 buttons = Qt::NoButton;
350 } else {
351 if (buttons && !m_currentButtons) {
352 m_currentButtons = buttons;
353 } else if (m_currentButtons != buttons) { // Button changed: Complete Drop operation.
354 result = DRAGDROP_S_DROP;
355 }
356 }
357
358 switch (result) {
359 case DRAGDROP_S_DROP:
360 case DRAGDROP_S_CANCEL:
361 if (!m_windowUnderMouse.isNull() && m_mode != TouchDrag && fEscapePressed == FALSE
362 && buttons != lastButtons) {
363 // QTBUG 66447: Synthesize a mouse release to the window under mouse at
364 // start of the DnD operation as Windows does not send any.
365 const QPoint globalPos = QWindowsCursor::mousePosition();
366 const QPoint localPos = m_windowUnderMouse->handle()->mapFromGlobal(globalPos);
368 QPointF(localPos), QPointF(globalPos),
371 }
372 m_currentButtons = Qt::NoButton;
373 break;
374
375 default:
377 break;
378 }
379
380 if (QWindowsContext::verbose > 1 || result != S_OK) {
381 qCDebug(lcQpaMime) << __FUNCTION__ << "fEscapePressed=" << fEscapePressed
382 << "grfKeyState=" << grfKeyState << "buttons" << m_currentButtons
383 << "returns 0x" << Qt::hex << int(result) << Qt::dec;
384 }
385 return ResultFromScode(result);
386}
387
392QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
394{
395 const Qt::DropAction action = translateToQDragDropAction(dwEffect);
396 m_drag->updateAction(action);
397
398 const qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey();
399 auto it = m_cursors.constFind(action);
400 // If a custom drag cursor is set, check its cache key to detect changes.
401 if (it == m_cursors.constEnd() || (currentCacheKey && currentCacheKey != it.value().cacheKey)) {
403 it = m_cursors.constFind(action);
404 }
405
406 if (it != m_cursors.constEnd()) {
407 const CursorEntry &e = it.value();
408 switch (m_mode) {
409 case MouseDrag:
410 SetCursor(e.cursor->handle());
411 break;
412 case TouchDrag:
413 // "Touch drag" with an unsuppressed cursor may happen with RDP (see createCursors())
415 SetCursor(nullptr);
416 if (!m_touchDragWindow)
417 m_touchDragWindow = new QWindowsDragCursorWindow;
418 m_touchDragWindow->setPixmap(e.pixmap);
419 m_touchDragWindow->setFramePosition(QCursor::pos() - e.hotSpot);
420 if (!m_touchDragWindow->isVisible())
421 m_touchDragWindow->show();
422 break;
423 }
424 return ResultFromScode(S_OK);
425 }
426
427 return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS);
428}
429
443{
444 qCDebug(lcQpaMime) << __FUNCTION__ << this << w;
445}
446
448{
449 qCDebug(lcQpaMime) << __FUNCTION__ << this;
450}
451
452void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState,
453 const QPoint &point, LPDWORD pdwEffect)
454{
456 m_lastPoint = point;
457 m_lastKeyState = grfKeyState;
458
459 QWindowsDrag *windowsDrag = QWindowsDrag::instance();
460 const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
461
464
465 const QPlatformDragQtResponse response =
466 QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(),
467 m_lastPoint, actions,
469
470 m_answerRect = response.answerRect();
471 const Qt::DropAction action = response.acceptedAction();
472 if (response.isAccepted()) {
473 m_chosenEffect = translateToWinDragEffects(action);
474 } else {
475 m_chosenEffect = DROPEFFECT_NONE;
476 }
477 *pdwEffect = m_chosenEffect;
478 qCDebug(lcQpaMime) << __FUNCTION__ << m_window
479 << windowsDrag->dropData() << " supported actions=" << actions
480 << " mods=" << lastModifiers << " mouse=" << lastButtons
481 << " accepted: " << response.isAccepted() << action
482 << m_answerRect << " effect" << *pdwEffect;
483}
484
485QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
486QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState,
487 POINTL pt, LPDWORD pdwEffect)
488{
489 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
490 dh->DragEnter(reinterpret_cast<HWND>(m_window->winId()), pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect);
491
492 qCDebug(lcQpaMime) << __FUNCTION__ << "widget=" << m_window << " key=" << grfKeyState
493 << "pt=" << pt.x << pt.y;
494
495 QWindowsDrag::instance()->setDropDataObject(pDataObj);
496 pDataObj->AddRef();
497 const QPoint point = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
498 handleDrag(m_window, grfKeyState, point, pdwEffect);
499 return NOERROR;
500}
501
502QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
503QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
504{
505 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
506 dh->DragOver(reinterpret_cast<POINT*>(&pt), *pdwEffect);
507
508 qCDebug(lcQpaMime) << __FUNCTION__ << "m_window" << m_window << "key=" << grfKeyState
509 << "pt=" << pt.x << pt.y;
510 const QPoint tmpPoint = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
511 // see if we should compress this event
512 if ((tmpPoint == m_lastPoint || m_answerRect.contains(tmpPoint))
513 && m_lastKeyState == grfKeyState) {
514 *pdwEffect = m_chosenEffect;
515 qCDebug(lcQpaMime) << __FUNCTION__ << "compressed event";
516 return NOERROR;
517 }
518
519 handleDrag(m_window, grfKeyState, tmpPoint, pdwEffect);
520 return NOERROR;
521}
522
523QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
525{
526 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
527 dh->DragLeave();
528
529 qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window;
530
531 const auto *keyMapper = QWindowsContext::instance()->keyMapper();
532 lastModifiers = keyMapper->queryKeyboardModifiers();
534
535 QWindowSystemInterface::handleDrag(m_window, nullptr, QPoint(), Qt::IgnoreAction,
537
538 if (!QDragManager::self()->source())
539 m_lastKeyState = 0;
540 QWindowsDrag::instance()->releaseDropDataObject();
541
542 return NOERROR;
543}
544
545#define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)
546
547QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
548QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
549 POINTL pt, LPDWORD pdwEffect)
550{
551 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
552 dh->Drop(pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect);
553
554 qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window
555 << "keys=" << grfKeyState << "pt=" << pt.x << ',' << pt.y;
556
557 m_lastPoint = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
558
559 QWindowsDrag *windowsDrag = QWindowsDrag::instance();
560
563
564 const QPlatformDropQtResponse response =
565 QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(),
566 m_lastPoint,
567 translateToQDragDropActions(*pdwEffect),
570
571 m_lastKeyState = grfKeyState;
572
573 if (response.isAccepted()) {
574 const Qt::DropAction action = response.acceptedAction();
575 if (action == Qt::MoveAction || action == Qt::TargetMoveAction) {
576 if (action == Qt::MoveAction)
577 m_chosenEffect = DROPEFFECT_MOVE;
578 else
579 m_chosenEffect = DROPEFFECT_COPY;
580 HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD));
581 if (hData) {
582 auto *moveEffect = reinterpret_cast<DWORD *>(GlobalLock(hData));
583 *moveEffect = DROPEFFECT_MOVE;
584 GlobalUnlock(hData);
585 STGMEDIUM medium;
586 memset(&medium, 0, sizeof(STGMEDIUM));
587 medium.tymed = TYMED_HGLOBAL;
588 medium.hGlobal = hData;
590 format.cfFormat = CLIPFORMAT(RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT));
591 format.tymed = TYMED_HGLOBAL;
592 format.ptd = nullptr;
593 format.dwAspect = 1;
594 format.lindex = -1;
595 windowsDrag->dropDataObject()->SetData(&format, &medium, true);
596 }
597 } else {
598 m_chosenEffect = translateToWinDragEffects(action);
599 }
600 } else {
601 m_chosenEffect = DROPEFFECT_NONE;
602 }
603 *pdwEffect = m_chosenEffect;
604
605 windowsDrag->releaseDropDataObject();
606 return NOERROR;
607}
608
609
616bool QWindowsDrag::m_canceled = false;
617
619
621{
622 if (m_cachedDropTargetHelper)
623 m_cachedDropTargetHelper->Release();
624}
625
631{
632 if (const QDrag *drag = currentDrag())
633 return drag->mimeData();
634 return &m_dropData;
635}
636
640IDropTargetHelper* QWindowsDrag::dropHelper() {
641 if (!m_cachedDropTargetHelper) {
642 CoCreateInstance(CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER,
643 IID_IDropTargetHelper,
644 reinterpret_cast<void**>(&m_cachedDropTargetHelper));
645 }
646 return m_cachedDropTargetHelper;
647}
648
649// Workaround for DoDragDrop() not working with touch/pen input, causing DnD to hang until the mouse is moved.
650// We process pointer messages for touch/pen and generate mouse input through SendInput() to trigger DoDragDrop()
651static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect)
652{
653 QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse();
654 const bool hasMouseCapture = underMouse && static_cast<QWindowsWindow *>(underMouse->handle())->hasMouseCapture();
655 const HWND hwnd = hasMouseCapture ? reinterpret_cast<HWND>(underMouse->winId()) : ::GetFocus();
656 bool starting = false;
657
658 for (;;) {
659 MSG msg{};
660 if (::GetMessage(&msg, hwnd, 0, 0) > 0) {
661
662 if (msg.message == WM_MOUSEMOVE) {
663
664 // Only consider the first simulated event, or actual mouse messages.
665 if (!starting && (msg.wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON | MK_XBUTTON1 | MK_XBUTTON2)) == 0)
666 return E_FAIL;
667
668 return ::DoDragDrop(pDataObj, pDropSource, dwOKEffects, pdwEffect);
669 }
670
671 if (msg.message == WM_POINTERUPDATE) {
672
673 const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
674
675 POINTER_INFO pointerInfo{};
676 if (!GetPointerInfo(pointerId, &pointerInfo))
677 return E_FAIL;
678
679 if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) {
680
681 DWORD flags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MOVE;
682 if (IS_POINTER_FIRSTBUTTON_WPARAM(msg.wParam))
683 flags |= MOUSEEVENTF_LEFTDOWN;
684 if (IS_POINTER_SECONDBUTTON_WPARAM(msg.wParam))
685 flags |= MOUSEEVENTF_RIGHTDOWN;
686 if (IS_POINTER_THIRDBUTTON_WPARAM(msg.wParam))
687 flags |= MOUSEEVENTF_MIDDLEDOWN;
688
689 if (!starting) {
690 POINT pt{};
691 if (::GetCursorPos(&pt)) {
692
693 // Send mouse input that can generate a WM_MOUSEMOVE message.
694 if ((flags & MOUSEEVENTF_LEFTDOWN || flags & MOUSEEVENTF_RIGHTDOWN || flags & MOUSEEVENTF_MIDDLEDOWN)
695 && (pt.x != pointerInfo.ptPixelLocation.x || pt.y != pointerInfo.ptPixelLocation.y)) {
696
697 const int origin_x = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
698 const int origin_y = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
699 const int virt_w = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
700 const int virt_h = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
701 const int virt_x = pointerInfo.ptPixelLocation.x - origin_x;
702 const int virt_y = pointerInfo.ptPixelLocation.y - origin_y;
703
704 INPUT input{};
705 input.type = INPUT_MOUSE;
706 input.mi.dx = static_cast<DWORD>(virt_x * (65535.0 / virt_w));
707 input.mi.dy = static_cast<DWORD>(virt_y * (65535.0 / virt_h));
708 input.mi.dwFlags = flags;
709
710 ::SendInput(1, &input, sizeof(input));
711 starting = true;
712 }
713 }
714 }
715 }
716 } else {
717 // Handle other messages.
718 qWindowsWndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
719
720 if (msg.message == WM_POINTERLEAVE)
721 return E_FAIL;
722 }
723 } else {
724 return E_FAIL;
725 }
726 }
727}
728
730{
731 // TODO: Accessibility handling?
732 QMimeData *dropData = drag->mimeData();
733 Qt::DropAction dragResult = Qt::IgnoreAction;
734
735 DWORD resultEffect;
736 QWindowsDrag::m_canceled = false;
737 auto *windowDropSource = new QWindowsOleDropSource(this);
738 windowDropSource->createCursors();
740 const Qt::DropActions possibleActions = drag->supportedActions();
741 const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
742 qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
743 << Qt::hex << int(possibleActions) << "effects=0x" << allowedEffects << Qt::dec;
744 const HRESULT r = startDoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
745 const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
746 if (r == DRAGDROP_S_DROP) {
747 if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
748 dragResult = Qt::TargetMoveAction;
749 resultEffect = DROPEFFECT_MOVE;
750 } else {
751 dragResult = translateToQDragDropAction(resultEffect);
752 }
753 // Force it to be a copy if an unsupported operation occurred.
754 // This indicates a bug in the drop target.
755 if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects)) {
756 qWarning("%s: Forcing Qt::CopyAction", __FUNCTION__);
757 dragResult = Qt::CopyAction;
758 }
759 }
760 // clean up
761 dropDataObject->releaseQt();
762 dropDataObject->Release(); // Will delete obj if refcount becomes 0
763 windowDropSource->Release(); // Will delete src if refcount becomes 0
764 qCDebug(lcQpaMime) << '<' << __FUNCTION__ << Qt::hex << "allowedEffects=0x" << allowedEffects
765 << "reportedPerformedEffect=0x" << reportedPerformedEffect
766 << " resultEffect=0x" << resultEffect << "hr=0x" << int(r) << Qt::dec << "dropAction=" << dragResult;
767 return dragResult;
768}
769
771{
772 return static_cast<QWindowsDrag *>(QWindowsIntegration::instance()->drag());
773}
774
776{
777 qCDebug(lcQpaMime) << __FUNCTION__ << m_dropDataObject;
778 if (m_dropDataObject) {
779 m_dropDataObject->Release();
780 m_dropDataObject = nullptr;
781 }
782}
783
HCURSOR handle() const
static void processEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Processes some pending events for the calling thread according to the specified flags.
static QPoint pos()
Returns the position of the cursor (hot spot) of the primary screen in global screen coordinates.
Definition qcursor.cpp:188
\inmodule QtCore
static QDragManager * self()
Definition qdnd.cpp:30
\inmodule QtGui
Definition qdrag.h:22
QPixmap pixmap() const
Returns the pixmap used to represent the data in a drag and drop operation.
Definition qdrag.cpp:134
QPixmap dragCursor(Qt::DropAction action) const
Returns the drag cursor for the action.
Definition qdrag.cpp:279
QPoint hotSpot() const
Returns the position of the hot spot relative to the top-left corner of the cursor.
Definition qdrag.cpp:158
@ MouseButtonRelease
Definition qcoreevent.h:61
QScreen * primaryScreen
the primary (or default) screen of the application.
static qreal factor(C *context)
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
const_iterator constFind(const Key &key) const
Definition qmap.h:655
void clear()
Definition qmap.h:289
iterator find(const Key &key)
Definition qmap.h:641
iterator end()
Definition qmap.h:602
size_type size() const
Definition qmap.h:267
const_iterator constEnd() const
Definition qmap.h:604
\inmodule QtCore
Definition qmimedata.h:16
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:127
void update()
Marks the entire window as dirty and schedules a repaint.
qreal devicePixelRatio() const
The QPaintEvent class contains event parameters for paint events.
Definition qevent.h:486
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect)
Draws the rectangular portion source of the given pixmap into the given target in the paint device.
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
int height() const
Returns the height of the pixmap.
Definition qpixmap.cpp:480
QPixmap scaled(int w, int h, Qt::AspectRatioMode aspectMode=Qt::IgnoreAspectRatio, Qt::TransformationMode mode=Qt::FastTransformation) const
Definition qpixmap.h:78
QSize size() const
Returns the size of the pixmap.
Definition qpixmap.cpp:493
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:456
int width() const
Returns the width of the pixmap.
Definition qpixmap.cpp:468
void setDevicePixelRatio(qreal scaleFactor)
Sets the device pixel ratio for the pixmap.
Definition qpixmap.cpp:604
qreal devicePixelRatio() const
Returns the device pixel ratio for the pixmap.
Definition qpixmap.cpp:576
qint64 cacheKey() const
Returns a number that identifies this QPixmap.
Definition qpixmap.cpp:884
The QPlatformCursor class provides information about pointer device events (movement,...
QDrag * currentDrag() const
void updateAction(Qt::DropAction action)
Called to notify QDrag about changes of the current action.
Qt::DropAction acceptedAction() const
The QPlatformScreen class provides an abstraction for visual displays.
virtual QPlatformCursor * cursor() const
Reimplement this function in subclass to return the cursor of the screen.
virtual QPoint mapFromGlobal(const QPoint &pos) const
Translates the global screen coordinate pos to window coordinates using native methods.
\inmodule QtCore\reentrant
Definition qpoint.h:217
\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
T * data() const noexcept
Definition qpointer.h:73
bool isNull() const noexcept
Definition qpointer.h:84
\inmodule QtGui
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
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
static bool handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons state, Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods=Qt::NoModifier, Qt::MouseEventSource source=Qt::MouseEventNotSynthesized)
\inmodule QtGui
Definition qwindow.h:63
void show()
Shows the window.
Definition qwindow.cpp:2254
QSurfaceFormat format() const override
Returns the actual format of this window.
Definition qwindow.cpp:946
Singleton container for all relevant information.
static QWindowsContext * instance()
Platform cursor implementation.
static QPoint mousePosition()
static HCURSOR createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor=1)
static State cursorState()
QPixmap dragDefaultCursor(Qt::DropAction action) const
A toplevel window showing the drag icon in case of touch drag.
void setPixmap(const QPixmap &p)
void paintEvent(QPaintEvent *) override
Handles paint events passed in the event parameter.
QWindowsDragCursorWindow(QWindow *parent=nullptr)
Windows drag implementation.
IDataObject * dropDataObject() const
virtual ~QWindowsDrag()
static bool isCanceled()
QMimeData * dropData()
Return data for a drop in process.
static QWindowsDrag * instance()
void releaseDropDataObject()
IDropTargetHelper * dropHelper()
May be used to handle extended cursors functionality for drags from outside the app.
Qt::DropAction drag(QDrag *drag) override
QWindowsOleDataObject subclass specialized for handling Drag&Drop.
IDataObject * retrieveDataObject() const override
static QWindowsIntegration * instance()
static Qt::MouseButtons queryMouseButtons()
Implementation of IDropSource.
~QWindowsOleDropSource() override
STDMETHOD QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) override
Check for cancel.
void createCursors()
Blend custom pixmap with cursors.
QWindowsOleDropSource(QWindowsDrag *drag)
STDMETHOD GiveFeedback(DWORD dwEffect) override
Give feedback: Change cursor according to action.
friend QDebug operator<<(QDebug, const QWindowsOleDropSource::CursorEntry &)
~QWindowsOleDropTarget() override
LPDATAOBJECT DWORD grfKeyState
LPDATAOBJECT DWORD POINTL pt
STDMETHOD DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) override
QWindowsOleDropTarget(QWindow *w)
LPDATAOBJECT pDataObj
STDMETHOD DragLeave() override
Raster or OpenGL Window.
EGLImageKHR int int EGLuint64KHR * modifiers
widget setFormat(format)
a resize(100000)
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Definition qcompare.h:63
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
@ LeftButton
Definition qnamespace.h:58
@ NoButton
Definition qnamespace.h:57
@ SmoothTransformation
@ KeepAspectRatio
@ transparent
Definition qnamespace.h:47
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
@ ShiftModifier
@ ControlModifier
@ NoModifier
@ AltModifier
DropAction
@ CopyAction
@ IgnoreAction
@ MoveAction
@ TargetMoveAction
@ LinkAction
@ FramelessWindowHint
Definition qnamespace.h:225
@ WindowDoesNotAcceptFocus
Definition qnamespace.h:236
@ Popup
Definition qnamespace.h:211
@ WindowStaysOnTopHint
Definition qnamespace.h:233
@ NoDropShadowWindowHint
Definition qnamespace.h:244
@ WindowTransparentForInput
Definition qnamespace.h:234
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
static QByteArray cacheKey(Args &&...args)
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLfloat GLfloat GLfloat w
[0]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLboolean r
[2]
GLuint GLfloat GLfloat GLfloat x1
GLbitfield flags
GLint GLsizei GLsizei GLenum format
GLfloat GLfloat GLfloat GLfloat h
GLsizei GLsizei GLchar * source
const GLubyte * c
GLfixed GLfixed GLfixed y2
GLuint entry
GLfixed GLfixed x2
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLenum GLenum input
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
#define WM_POINTERUPDATE
#define WM_POINTERLEAVE
unsigned int quint32
Definition qtypes.h:50
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
struct tagMSG MSG
long HRESULT
HICON HCURSOR
LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND, UINT, WPARAM, LPARAM)
QSharedPointer< CursorHandle > CursorHandlePtr
static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect)
static Qt::DropActions translateToQDragDropActions(DWORD pdwEffects)
static Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState)
static Qt::KeyboardModifiers lastModifiers
QDebug operator<<(QDebug d, const QWindowsOleDropSource::CursorEntry &e)
static Qt::DropAction translateToQDragDropAction(DWORD pdwEffect)
static Qt::MouseButtons lastButtons
static DWORD translateToWinDragEffects(Qt::DropActions action)
tagSTGMEDIUM STGMEDIUM
tagFORMATETC FORMATETC
QObject::connect nullptr
QGraphicsOpacityEffect * effect
the effect attached to this item
edit isVisible()
widget render & pixmap
QPainter painter(this)
[7]
aWidget window() -> setWindowTitle("New Window Title")
[2]
static QPoint mapFromGlobal(const HWND hwnd, const QPoint &)