Qt 6.x
The Qt SDK
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
32#include <shlobj.h>
33
35
45{
46public:
47 explicit QWindowsDragCursorWindow(QWindow *parent = nullptr);
48
49 void setPixmap(const QPixmap &p);
50
51protected:
52 void paintEvent(QPaintEvent *) override
53 {
54 QPainter painter(this);
55 painter.drawPixmap(0, 0, m_pixmap);
56 }
57
58private:
59 QPixmap m_pixmap;
60};
61
64{
65 QSurfaceFormat windowFormat = format();
66 windowFormat.setAlphaBufferSize(8);
67 setFormat(windowFormat);
68 setObjectName(QStringLiteral("QWindowsDragCursorWindow"));
72}
73
75{
76 if (p.cacheKey() == m_pixmap.cacheKey())
77 return;
78 const QSize oldSize = m_pixmap.size();
79 QSize newSize = p.size();
80 qCDebug(lcQpaMime) << __FUNCTION__ << p.cacheKey() << newSize;
81 m_pixmap = p;
82 if (oldSize != newSize) {
83 const qreal pixDevicePixelRatio = p.devicePixelRatio();
84 if (pixDevicePixelRatio > 1.0 && qFuzzyCompare(pixDevicePixelRatio, devicePixelRatio()))
85 newSize /= qRound(pixDevicePixelRatio);
86 resize(newSize);
87 }
88 if (isVisible())
89 update();
90}
91
104{
106}
107
108static inline Qt::DropActions translateToQDragDropActions(DWORD pdwEffects)
109{
110 Qt::DropActions actions = Qt::IgnoreAction;
111 if (pdwEffects & DROPEFFECT_LINK)
112 actions |= Qt::LinkAction;
113 if (pdwEffects & DROPEFFECT_COPY)
114 actions |= Qt::CopyAction;
115 if (pdwEffects & DROPEFFECT_MOVE)
116 actions |= Qt::MoveAction;
117 return actions;
118}
119
120static inline Qt::DropAction translateToQDragDropAction(DWORD pdwEffect)
121{
122 if (pdwEffect & DROPEFFECT_LINK)
123 return Qt::LinkAction;
124 if (pdwEffect & DROPEFFECT_COPY)
125 return Qt::CopyAction;
126 if (pdwEffect & DROPEFFECT_MOVE)
127 return Qt::MoveAction;
128 return Qt::IgnoreAction;
129}
130
131static inline DWORD translateToWinDragEffects(Qt::DropActions action)
132{
133 DWORD effect = DROPEFFECT_NONE;
134 if (action & Qt::LinkAction)
135 effect |= DROPEFFECT_LINK;
136 if (action & Qt::CopyAction)
137 effect |= DROPEFFECT_COPY;
138 if (action & Qt::MoveAction)
139 effect |= DROPEFFECT_MOVE;
140 return effect;
141}
142
143static inline Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState)
144{
145 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
146
147 if (keyState & MK_SHIFT)
149 if (keyState & MK_CONTROL)
151 if (keyState & MK_ALT)
153
154 return modifiers;
155}
156
157static Qt::KeyboardModifiers lastModifiers = Qt::NoModifier;
158static Qt::MouseButtons lastButtons = Qt::NoButton;
159
170class QWindowsOleDropSource : public QWindowsComBase<IDropSource>
171{
172public:
173 enum Mode {
175 TouchDrag // Mouse cursor suppressed, use window as cursor.
176 };
177
178 explicit QWindowsOleDropSource(QWindowsDrag *drag);
179 ~QWindowsOleDropSource() override;
180
181 void createCursors();
182
183 // IDropSource methods
184 STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState) override;
185 STDMETHOD(GiveFeedback)(DWORD dwEffect) override;
186
187private:
188 struct CursorEntry {
189 CursorEntry() : cacheKey(0) {}
190 CursorEntry(const QPixmap &p, qint64 cK, const CursorHandlePtr &c, const QPoint &h) :
191 pixmap(p), cacheKey(cK), cursor(c), hotSpot(h) {}
192
194 qint64 cacheKey; // Cache key of cursor
196 QPoint hotSpot;
197 };
198
199 typedef QMap<Qt::DropAction, CursorEntry> ActionCursorMap;
200
201 Mode m_mode;
202 QWindowsDrag *m_drag;
203 QPointer<QWindow> m_windowUnderMouse;
204 Qt::MouseButtons m_currentButtons;
205 ActionCursorMap m_cursors;
206 QWindowsDragCursorWindow *m_touchDragWindow;
207
208#ifndef QT_NO_DEBUG_STREAM
209 friend QDebug operator<<(QDebug, const QWindowsOleDropSource::CursorEntry &);
210#endif
211};
212
214 : m_mode(QWindowsCursor::cursorState() != QWindowsCursor::State::Suppressed ? MouseDrag : TouchDrag)
215 , m_drag(drag)
216 , m_windowUnderMouse(QWindowsContext::instance()->windowUnderMouse())
217 , m_currentButtons(Qt::NoButton)
218 , m_touchDragWindow(nullptr)
219{
220 qCDebug(lcQpaMime) << __FUNCTION__ << m_mode;
221}
222
224{
225 m_cursors.clear();
226 delete m_touchDragWindow;
227 qCDebug(lcQpaMime) << __FUNCTION__;
228}
229
230#ifndef QT_NO_DEBUG_STREAM
231QDebug operator<<(QDebug d, const QWindowsOleDropSource::CursorEntry &e)
232{
233 d << "CursorEntry:" << e.pixmap.size() << '#' << e.cacheKey
234 << "HCURSOR" << e.cursor->handle() << "hotspot:" << e.hotSpot;
235 return d;
236}
237#endif // !QT_NO_DEBUG_STREAM
238
244{
245 const QDrag *drag = m_drag->currentDrag();
246 const QPixmap pixmap = drag->pixmap();
247 const bool hasPixmap = !pixmap.isNull();
248
249 // Find screen for drag. Could be obtained from QDrag::source(), but that might be a QWidget.
251 if (!platformScreen) {
252 if (const QScreen *primaryScreen = QGuiApplication::primaryScreen())
253 platformScreen = primaryScreen->handle();
254 }
255 Q_ASSERT(platformScreen);
256 QPlatformCursor *platformCursor = platformScreen->cursor();
257
258 if (GetSystemMetrics (SM_REMOTESESSION) != 0) {
259 /* Workaround for RDP issues with large cursors.
260 * Touch drag window seems to work just fine...
261 * 96 pixel is a 'large' mouse cursor, according to RDP spec */
262 const int rdpLargeCursor = qRound(qreal(96) / QHighDpiScaling::factor(platformScreen));
263 if (pixmap.width() > rdpLargeCursor || pixmap.height() > rdpLargeCursor)
264 m_mode = TouchDrag;
265 }
266
267 qreal pixmapScaleFactor = 1;
268 qreal hotSpotScaleFactor = 1;
269 if (m_mode != TouchDrag) { // Touch drag: pixmap is shown in a separate QWindow, which will be scaled.)
270 hotSpotScaleFactor = QHighDpiScaling::factor(platformScreen);
271 pixmapScaleFactor = hotSpotScaleFactor / pixmap.devicePixelRatio();
272 }
273 QPixmap scaledPixmap = qFuzzyCompare(pixmapScaleFactor, 1.0)
274 ? pixmap
275 : pixmap.scaled((QSizeF(pixmap.size()) * pixmapScaleFactor).toSize(),
277 scaledPixmap.setDevicePixelRatio(1);
278
280 int actionCount = int(sizeof(actions) / sizeof(actions[0]));
281 if (!hasPixmap)
282 --actionCount; // No Qt::IgnoreAction unless pixmap
283 const QPoint hotSpot = qFuzzyCompare(hotSpotScaleFactor, 1.0)
284 ? drag->hotSpot()
285 : (QPointF(drag->hotSpot()) * hotSpotScaleFactor).toPoint();
286 for (int cnum = 0; cnum < actionCount; ++cnum) {
287 const Qt::DropAction action = actions[cnum];
288 QPixmap cursorPixmap = drag->dragCursor(action);
289 if (cursorPixmap.isNull() && platformCursor)
290 cursorPixmap = static_cast<QWindowsCursor *>(platformCursor)->dragDefaultCursor(action);
291 const qint64 cacheKey = cursorPixmap.cacheKey();
292 const auto it = m_cursors.find(action);
293 if (it != m_cursors.end() && it.value().cacheKey == cacheKey)
294 continue;
295 if (cursorPixmap.isNull()) {
296 qWarning("%s: Unable to obtain drag cursor for %d.", __FUNCTION__, action);
297 continue;
298 }
299
300 QPoint newHotSpot(0, 0);
301 QPixmap newPixmap = cursorPixmap;
302
303 if (hasPixmap) {
304 const int x1 = qMin(-hotSpot.x(), 0);
305 const int x2 = qMax(scaledPixmap.width() - hotSpot.x(), cursorPixmap.width());
306 const int y1 = qMin(-hotSpot.y(), 0);
307 const int y2 = qMax(scaledPixmap.height() - hotSpot.y(), cursorPixmap.height());
308 QPixmap newCursor(x2 - x1 + 1, y2 - y1 + 1);
309 newCursor.fill(Qt::transparent);
310 QPainter p(&newCursor);
311 const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y()));
312 p.drawPixmap(pmDest, scaledPixmap);
313 p.drawPixmap(qMax(0, hotSpot.x()),qMax(0, hotSpot.y()), cursorPixmap);
314 newPixmap = newCursor;
315 newHotSpot = QPoint(qMax(0, hotSpot.x()), qMax(0, hotSpot.y()));
316 }
317
318 if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newPixmap, newHotSpot)) {
319 const CursorEntry entry(newPixmap, cacheKey, CursorHandlePtr(new CursorHandle(sysCursor)), newHotSpot);
320 if (it == m_cursors.end())
321 m_cursors.insert(action, entry);
322 else
323 it.value() = entry;
324 }
325 }
326#ifndef QT_NO_DEBUG_OUTPUT
327 if (lcQpaMime().isDebugEnabled())
328 qCDebug(lcQpaMime) << __FUNCTION__ << "pixmap" << pixmap.size() << m_cursors.size() << "cursors:\n" << m_cursors;
329#endif // !QT_NO_DEBUG_OUTPUT
330}
331
336QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
337QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
338{
339 // In some rare cases, when a mouse button is released but the mouse is static,
340 // grfKeyState will not be updated with these released buttons until the mouse
341 // is moved. So we use the async key state given by queryMouseButtons() instead.
342 Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons();
343
344 SCODE result = S_OK;
345 if (fEscapePressed || QWindowsDrag::isCanceled()) {
346 result = DRAGDROP_S_CANCEL;
347 buttons = Qt::NoButton;
348 } else {
349 if (buttons && !m_currentButtons) {
350 m_currentButtons = buttons;
351 } else if (m_currentButtons != buttons) { // Button changed: Complete Drop operation.
352 result = DRAGDROP_S_DROP;
353 }
354 }
355
356 switch (result) {
357 case DRAGDROP_S_DROP:
358 case DRAGDROP_S_CANCEL:
359 if (!m_windowUnderMouse.isNull() && m_mode != TouchDrag && fEscapePressed == FALSE
360 && buttons != lastButtons) {
361 // QTBUG 66447: Synthesize a mouse release to the window under mouse at
362 // start of the DnD operation as Windows does not send any.
363 const QPoint globalPos = QWindowsCursor::mousePosition();
364 const QPoint localPos = m_windowUnderMouse->handle()->mapFromGlobal(globalPos);
366 QPointF(localPos), QPointF(globalPos),
369 }
370 m_currentButtons = Qt::NoButton;
371 break;
372
373 default:
375 break;
376 }
377
378 if (QWindowsContext::verbose > 1 || result != S_OK) {
379 qCDebug(lcQpaMime) << __FUNCTION__ << "fEscapePressed=" << fEscapePressed
380 << "grfKeyState=" << grfKeyState << "buttons" << m_currentButtons
381 << "returns 0x" << Qt::hex << int(result) << Qt::dec;
382 }
383 return ResultFromScode(result);
384}
385
390QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
392{
393 const Qt::DropAction action = translateToQDragDropAction(dwEffect);
394 m_drag->updateAction(action);
395
396 const qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey();
397 auto it = m_cursors.constFind(action);
398 // If a custom drag cursor is set, check its cache key to detect changes.
399 if (it == m_cursors.constEnd() || (currentCacheKey && currentCacheKey != it.value().cacheKey)) {
401 it = m_cursors.constFind(action);
402 }
403
404 if (it != m_cursors.constEnd()) {
405 const CursorEntry &e = it.value();
406 switch (m_mode) {
407 case MouseDrag:
408 SetCursor(e.cursor->handle());
409 break;
410 case TouchDrag:
411 // "Touch drag" with an unsuppressed cursor may happen with RDP (see createCursors())
413 SetCursor(nullptr);
414 if (!m_touchDragWindow)
415 m_touchDragWindow = new QWindowsDragCursorWindow;
416 m_touchDragWindow->setPixmap(e.pixmap);
417 m_touchDragWindow->setFramePosition(QCursor::pos() - e.hotSpot);
418 if (!m_touchDragWindow->isVisible())
419 m_touchDragWindow->show();
420 break;
421 }
422 return ResultFromScode(S_OK);
423 }
424
425 return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS);
426}
427
441{
442 qCDebug(lcQpaMime) << __FUNCTION__ << this << w;
443}
444
446{
447 qCDebug(lcQpaMime) << __FUNCTION__ << this;
448}
449
450void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState,
451 const QPoint &point, LPDWORD pdwEffect)
452{
454 m_lastPoint = point;
455 m_lastKeyState = grfKeyState;
456
457 QWindowsDrag *windowsDrag = QWindowsDrag::instance();
458 const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
459
462
463 const QPlatformDragQtResponse response =
464 QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(),
465 m_lastPoint, actions,
467
468 m_answerRect = response.answerRect();
469 const Qt::DropAction action = response.acceptedAction();
470 if (response.isAccepted()) {
471 m_chosenEffect = translateToWinDragEffects(action);
472 } else {
473 m_chosenEffect = DROPEFFECT_NONE;
474 }
475 *pdwEffect = m_chosenEffect;
476 qCDebug(lcQpaMime) << __FUNCTION__ << m_window
477 << windowsDrag->dropData() << " supported actions=" << actions
478 << " mods=" << lastModifiers << " mouse=" << lastButtons
479 << " accepted: " << response.isAccepted() << action
480 << m_answerRect << " effect" << *pdwEffect;
481}
482
483QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
484QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState,
485 POINTL pt, LPDWORD pdwEffect)
486{
487 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
488 dh->DragEnter(reinterpret_cast<HWND>(m_window->winId()), pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect);
489
490 qCDebug(lcQpaMime) << __FUNCTION__ << "widget=" << m_window << " key=" << grfKeyState
491 << "pt=" << pt.x << pt.y;
492
494 pDataObj->AddRef();
495 const QPoint point = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
496 handleDrag(m_window, grfKeyState, point, pdwEffect);
497 return NOERROR;
498}
499
500QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
501QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
502{
503 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
504 dh->DragOver(reinterpret_cast<POINT*>(&pt), *pdwEffect);
505
506 qCDebug(lcQpaMime) << __FUNCTION__ << "m_window" << m_window << "key=" << grfKeyState
507 << "pt=" << pt.x << pt.y;
508 const QPoint tmpPoint = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
509 // see if we should compress this event
510 if ((tmpPoint == m_lastPoint || m_answerRect.contains(tmpPoint))
511 && m_lastKeyState == grfKeyState) {
512 *pdwEffect = m_chosenEffect;
513 qCDebug(lcQpaMime) << __FUNCTION__ << "compressed event";
514 return NOERROR;
515 }
516
517 handleDrag(m_window, grfKeyState, tmpPoint, pdwEffect);
518 return NOERROR;
519}
520
521QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
523{
524 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
525 dh->DragLeave();
526
527 qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window;
528
531
532 QWindowSystemInterface::handleDrag(m_window, nullptr, QPoint(), Qt::IgnoreAction,
534
535 if (!QDragManager::self()->source())
536 m_lastKeyState = 0;
538
539 return NOERROR;
540}
541
542#define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)
543
544QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
545QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
546 POINTL pt, LPDWORD pdwEffect)
547{
548 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
549 dh->Drop(pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect);
550
551 qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window
552 << "keys=" << grfKeyState << "pt=" << pt.x << ',' << pt.y;
553
554 m_lastPoint = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
555
556 QWindowsDrag *windowsDrag = QWindowsDrag::instance();
557
560
561 const QPlatformDropQtResponse response =
562 QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(),
563 m_lastPoint,
564 translateToQDragDropActions(*pdwEffect),
567
568 m_lastKeyState = grfKeyState;
569
570 if (response.isAccepted()) {
571 const Qt::DropAction action = response.acceptedAction();
572 if (action == Qt::MoveAction || action == Qt::TargetMoveAction) {
573 if (action == Qt::MoveAction)
574 m_chosenEffect = DROPEFFECT_MOVE;
575 else
576 m_chosenEffect = DROPEFFECT_COPY;
577 HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD));
578 if (hData) {
579 auto *moveEffect = reinterpret_cast<DWORD *>(GlobalLock(hData));
580 *moveEffect = DROPEFFECT_MOVE;
581 GlobalUnlock(hData);
582 STGMEDIUM medium;
583 memset(&medium, 0, sizeof(STGMEDIUM));
584 medium.tymed = TYMED_HGLOBAL;
585 medium.hGlobal = hData;
587 format.cfFormat = CLIPFORMAT(RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT));
588 format.tymed = TYMED_HGLOBAL;
589 format.ptd = nullptr;
590 format.dwAspect = 1;
591 format.lindex = -1;
592 windowsDrag->dropDataObject()->SetData(&format, &medium, true);
593 }
594 } else {
595 m_chosenEffect = translateToWinDragEffects(action);
596 }
597 } else {
598 m_chosenEffect = DROPEFFECT_NONE;
599 }
600 *pdwEffect = m_chosenEffect;
601
602 windowsDrag->releaseDropDataObject();
603 return NOERROR;
604}
605
606
613bool QWindowsDrag::m_canceled = false;
614bool QWindowsDrag::m_dragging = false;
615
617
619{
620 if (m_cachedDropTargetHelper)
621 m_cachedDropTargetHelper->Release();
622}
623
629{
630 if (const QDrag *drag = currentDrag())
631 return drag->mimeData();
632 return &m_dropData;
633}
634
638IDropTargetHelper* QWindowsDrag::dropHelper() {
639 if (!m_cachedDropTargetHelper) {
640 CoCreateInstance(CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER,
641 IID_IDropTargetHelper,
642 reinterpret_cast<void**>(&m_cachedDropTargetHelper));
643 }
644 return m_cachedDropTargetHelper;
645}
646
647// Workaround for DoDragDrop() not working with touch/pen input, causing DnD to hang until the mouse is moved.
648// We process pointer messages for touch/pen and generate mouse input through SendInput() to trigger DoDragDrop()
649static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect)
650{
652 const HWND hwnd = underMouse ? reinterpret_cast<HWND>(underMouse->winId()) : ::GetFocus();
653 bool starting = false;
654
655 for (;;) {
656 MSG msg{};
657 if (::GetMessage(&msg, hwnd, 0, 0) > 0) {
658
659 if (msg.message == WM_MOUSEMOVE) {
660
661 // Only consider the first simulated event, or actual mouse messages.
662 if (!starting && (msg.wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON | MK_XBUTTON1 | MK_XBUTTON2)) == 0)
663 return E_FAIL;
664
665 return ::DoDragDrop(pDataObj, pDropSource, dwOKEffects, pdwEffect);
666 }
667
668 if (msg.message == WM_POINTERUPDATE) {
669
670 const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
671
672 POINTER_INFO pointerInfo{};
673 if (!GetPointerInfo(pointerId, &pointerInfo))
674 return E_FAIL;
675
676 if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) {
677
678 DWORD flags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MOVE;
679 if (IS_POINTER_FIRSTBUTTON_WPARAM(msg.wParam))
680 flags |= MOUSEEVENTF_LEFTDOWN;
681 if (IS_POINTER_SECONDBUTTON_WPARAM(msg.wParam))
682 flags |= MOUSEEVENTF_RIGHTDOWN;
683 if (IS_POINTER_THIRDBUTTON_WPARAM(msg.wParam))
684 flags |= MOUSEEVENTF_MIDDLEDOWN;
685
686 if (!starting) {
687 POINT pt{};
688 if (::GetCursorPos(&pt)) {
689
690 // Send mouse input that can generate a WM_MOUSEMOVE message.
691 if ((flags & MOUSEEVENTF_LEFTDOWN || flags & MOUSEEVENTF_RIGHTDOWN || flags & MOUSEEVENTF_MIDDLEDOWN)
692 && (pt.x != pointerInfo.ptPixelLocation.x || pt.y != pointerInfo.ptPixelLocation.y)) {
693
694 const int origin_x = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
695 const int origin_y = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
696 const int virt_w = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
697 const int virt_h = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
698 const int virt_x = pointerInfo.ptPixelLocation.x - origin_x;
699 const int virt_y = pointerInfo.ptPixelLocation.y - origin_y;
700
701 INPUT input{};
702 input.type = INPUT_MOUSE;
703 input.mi.dx = static_cast<DWORD>(virt_x * (65535.0 / virt_w));
704 input.mi.dy = static_cast<DWORD>(virt_y * (65535.0 / virt_h));
705 input.mi.dwFlags = flags;
706
707 ::SendInput(1, &input, sizeof(input));
708 starting = true;
709 }
710 }
711 }
712 }
713 } else {
714 // Handle other messages.
715 qWindowsWndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
716
717 if (msg.message == WM_POINTERLEAVE)
718 return E_FAIL;
719 }
720 } else {
721 return E_FAIL;
722 }
723 }
724}
725
727{
728 // TODO: Accessibility handling?
729 QMimeData *dropData = drag->mimeData();
730 Qt::DropAction dragResult = Qt::IgnoreAction;
731
732 DWORD resultEffect;
733 QWindowsDrag::m_canceled = false;
734 auto *windowDropSource = new QWindowsOleDropSource(this);
735 windowDropSource->createCursors();
737 const Qt::DropActions possibleActions = drag->supportedActions();
738 const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
739 qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
740 << Qt::hex << int(possibleActions) << "effects=0x" << allowedEffects << Qt::dec;
741 // Indicate message handlers we are in DoDragDrop() event loop.
742 QWindowsDrag::m_dragging = true;
743 const HRESULT r = startDoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
744 QWindowsDrag::m_dragging = false;
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
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:132
QPixmap dragCursor(Qt::DropAction action) const
Returns the drag cursor for the action.
Definition qdrag.cpp:277
QPoint hotSpot() const
Returns the position of the hot spot relative to the top-left corner of the cursor.
Definition qdrag.cpp:156
@ MouseButtonRelease
Definition qcoreevent.h:61
QScreen * primaryScreen
the primary (or default) screen of the application.
static qreal factor(C *context)
Definition qmap.h:186
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
const_iterator constFind(const Key &key) const
Definition qmap.h:654
void clear()
Definition qmap.h:288
iterator find(const Key &key)
Definition qmap.h:640
iterator end()
Definition qmap.h:601
size_type size() const
Definition qmap.h:266
const_iterator constEnd() const
Definition qmap.h:603
\inmodule QtCore
Definition qmimedata.h:16
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:114
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:485
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:484
QSize size() const
Returns the size of the pixmap.
Definition qpixmap.cpp:497
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:460
int width() const
Returns the width of the pixmap.
Definition qpixmap.cpp:472
void setDevicePixelRatio(qreal scaleFactor)
Sets the device pixel ratio for the pixmap.
Definition qpixmap.cpp:608
void fill(const QColor &fillColor=Qt::white)
Fills the pixmap with the given color.
Definition qpixmap.cpp:854
qint64 cacheKey() const
Returns a number that identifies this QPixmap.
Definition qpixmap.cpp:888
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:214
\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
\inmodule QtCore
Definition qpointer.h:18
T * data() const
Definition qpointer.h:56
bool isNull() const
Returns true if the referenced object has been destroyed or if there is no referenced object; otherwi...
Definition qpointer.h:67
\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:851
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
\inmodule QtCore
Definition qsize.h:207
\inmodule QtCore
Definition qsize.h:25
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
void setAlphaBufferSize(int size)
Set the desired size in bits of the alpha channel of the color buffer.
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:2181
QSurfaceFormat format() const override
Returns the actual format of this window.
Definition qwindow.cpp:888
Singleton container for all relevant information.
QWindowsScreenManager & screenManager()
QWindow * windowUnderMouse() const
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.
void setDropDataObject(IDataObject *dataObject)
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::KeyboardModifiers queryKeyboardModifiers()
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
const QWindowsScreen * screenAtDp(const QPoint &p) const
EGLImageKHR int int EGLuint64KHR * modifiers
widget setFormat(format)
a resize(100000)
QCursor cursor
double e
QSet< QString >::iterator it
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
@ LeftButton
Definition qnamespace.h:57
@ NoButton
Definition qnamespace.h:56
@ SmoothTransformation
@ KeepAspectRatio
@ transparent
Definition qnamespace.h:46
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:224
@ WindowDoesNotAcceptFocus
Definition qnamespace.h:235
@ Popup
Definition qnamespace.h:210
@ WindowStaysOnTopHint
Definition qnamespace.h:232
@ NoDropShadowWindowHint
Definition qnamespace.h:243
@ WindowTransparentForInput
Definition qnamespace.h:233
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
#define qWarning
Definition qlogging.h:162
#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:45
long long qint64
Definition qtypes.h:55
double qreal
Definition qtypes.h:92
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 &)
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent
virtual HRESULT STDMETHODCALLTYPE GetFocus(__RPC__deref_out_opt IRawElementProviderFragment **pRetVal)=0