1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 #include "qwinrtdrag.h"
40 
41 #include <QtCore/qglobal.h>
42 #include <QtCore/QMimeData>
43 #include <QtCore/QStringList>
44 #include <QtGui/QGuiApplication>
45 #include <qpa/qwindowsysteminterface.h>
46 
47 #include <qfunctions_winrt.h>
48 #include <private/qeventdispatcher_winrt_p.h>
49 
50 #include <Windows.ApplicationModel.datatransfer.h>
51 #include <windows.ui.xaml.h>
52 #include <windows.foundation.collections.h>
53 #include <windows.graphics.imaging.h>
54 #include <windows.storage.streams.h>
55 #include <functional>
56 #include <robuffer.h>
57 
58 using namespace ABI::Windows::ApplicationModel::DataTransfer;
59 using namespace ABI::Windows::ApplicationModel::DataTransfer::DragDrop;
60 using namespace ABI::Windows::Foundation;
61 using namespace ABI::Windows::Foundation::Collections;
62 using namespace ABI::Windows::Graphics::Imaging;
63 using namespace ABI::Windows::Storage;
64 using namespace ABI::Windows::Storage::Streams;
65 using namespace ABI::Windows::UI::Xaml;
66 using namespace Microsoft::WRL;
67 using namespace Microsoft::WRL::Wrappers;
68 
69 QT_BEGIN_NAMESPACE
70 
71 Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime")
72 
createIBufferFromData(const char * data,qint32 size)73 ComPtr<IBuffer> createIBufferFromData(const char *data, qint32 size)
74 {
75     static ComPtr<IBufferFactory> bufferFactory;
76     HRESULT hr;
77     if (!bufferFactory) {
78         hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
79                                     IID_PPV_ARGS(&bufferFactory));
80         Q_ASSERT_SUCCEEDED(hr);
81     }
82 
83     ComPtr<IBuffer> buffer;
84     const UINT32 length = UINT32(size);
85     hr = bufferFactory->Create(length, &buffer);
86     Q_ASSERT_SUCCEEDED(hr);
87     hr = buffer->put_Length(length);
88     Q_ASSERT_SUCCEEDED(hr);
89 
90     ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
91     hr = buffer.As(&byteArrayAccess);
92     Q_ASSERT_SUCCEEDED(hr);
93 
94     byte *bytes;
95     hr = byteArrayAccess->Buffer(&bytes);
96     Q_ASSERT_SUCCEEDED(hr);
97     memcpy(bytes, data, length);
98     return buffer;
99 }
100 
101 class DragThreadTransferData : public QObject
102 {
103     Q_OBJECT
104 public slots:
105     void handleDrag();
106 public:
107     explicit DragThreadTransferData(QObject *parent = nullptr);
108     QWindow *window;
109     QWinRTInternalMimeData *mime;
110     QPoint point;
111     Qt::DropActions actions;
112     bool dropAction;
113     ComPtr<IDragEventArgs> nativeArgs;
114     ComPtr<IDragOperationDeferral> deferral;
115 };
116 
hStringToQString(const HString & hString)117 inline QString hStringToQString(const HString &hString)
118 {
119     quint32 l;
120     const wchar_t *raw = hString.GetRawBuffer(&l);
121     return (QString::fromWCharArray(raw, int(l)));
122 }
123 
qStringToHString(const QString & qString)124 inline HString qStringToHString(const QString &qString)
125 {
126     HString h;
127     h.Set(reinterpret_cast<const wchar_t*>(qString.utf16()), uint(qString.size()));
128     return h;
129 }
130 
131 namespace NativeFormatStrings {
132     static ComPtr<IStandardDataFormatsStatics> dataStatics;
133     static HSTRING text; // text/plain
134     static HSTRING html; // text/html
135     static HSTRING storage; // text/uri-list
136 }
137 
translateFromQDragDropActions(const Qt::DropAction action)138 static inline DataPackageOperation translateFromQDragDropActions(const Qt::DropAction action)
139 {
140     switch (action) {
141     case Qt::CopyAction:
142         return DataPackageOperation_Copy;
143     case Qt::MoveAction:
144         return DataPackageOperation_Move;
145     case Qt::LinkAction:
146         return DataPackageOperation_Link;
147     case Qt::IgnoreAction:
148     default:
149         return DataPackageOperation_None;
150     }
151 }
152 
translateToQDragDropActions(const DataPackageOperation op)153 static inline Qt::DropActions translateToQDragDropActions(const DataPackageOperation op)
154 {
155     Qt::DropActions actions = Qt::IgnoreAction;
156     // None needs to be interpreted as the sender being able to handle
157     // anything and let the receiver decide
158     if (op == DataPackageOperation_None)
159         actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction;
160     if (op & DataPackageOperation_Link)
161         actions |= Qt::LinkAction;
162     if (op & DataPackageOperation_Copy)
163         actions |= Qt::CopyAction;
164     if (op & DataPackageOperation_Move)
165         actions |= Qt::MoveAction;
166     return actions;
167 }
168 
QWinRTInternalMimeData()169 QWinRTInternalMimeData::QWinRTInternalMimeData()
170     : QInternalMimeData()
171 {
172     qCDebug(lcQpaMime) << __FUNCTION__;
173     if (!NativeFormatStrings::dataStatics) {
174         HRESULT hr;
175         hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_StandardDataFormats).Get(),
176                                     IID_PPV_ARGS(&NativeFormatStrings::dataStatics));
177         Q_ASSERT_SUCCEEDED(hr);
178         hr = NativeFormatStrings::dataStatics->get_Text(&NativeFormatStrings::text);
179         Q_ASSERT_SUCCEEDED(hr);
180         hr = NativeFormatStrings::dataStatics->get_Html(&NativeFormatStrings::html);
181         Q_ASSERT_SUCCEEDED(hr);
182         hr = NativeFormatStrings::dataStatics->get_StorageItems(&NativeFormatStrings::storage);
183         Q_ASSERT_SUCCEEDED(hr);
184     }
185 }
186 
hasFormat_sys(const QString & mimetype) const187 bool QWinRTInternalMimeData::hasFormat_sys(const QString &mimetype) const
188 {
189     qCDebug(lcQpaMime) << __FUNCTION__ << mimetype;
190 
191     if (!dataView)
192         return false;
193 
194     return formats_sys().contains(mimetype);
195 }
196 
formats_sys() const197 QStringList QWinRTInternalMimeData::formats_sys() const
198 {
199     qCDebug(lcQpaMime) << __FUNCTION__;
200 
201     if (!dataView)
202         return QStringList();
203 
204     if (!formats.isEmpty())
205         return formats;
206 
207     HRESULT hr;
208     hr = QEventDispatcherWinRT::runOnXamlThread([this]() {
209         boolean contains;
210         HRESULT hr;
211         hr = dataView->Contains(NativeFormatStrings::text, &contains);
212         if (SUCCEEDED(hr) && contains)
213             formats.append(QLatin1String("text/plain"));
214 
215         hr = dataView->Contains(NativeFormatStrings::html, &contains);
216         if (SUCCEEDED(hr) && contains)
217             formats.append(QLatin1String("text/html"));
218 
219         hr = dataView->Contains(NativeFormatStrings::storage, &contains);
220         if (SUCCEEDED(hr) && contains)
221             formats.append(QLatin1String("text/uri-list"));
222 
223         // We need to add any additional format as well, for legacy windows
224         // reasons, but also in case someone adds custom formats.
225         ComPtr<IVectorView<HSTRING>> availableFormats;
226         hr = dataView->get_AvailableFormats(&availableFormats);
227         RETURN_OK_IF_FAILED("Could not query available formats.");
228 
229         quint32 size;
230         hr = availableFormats->get_Size(&size);
231         RETURN_OK_IF_FAILED("Could not query format vector size.");
232         for (quint32 i = 0; i < size; ++i) {
233             HString str;
234             hr = availableFormats->GetAt(i, str.GetAddressOf());
235             if (FAILED(hr))
236                 continue;
237             formats.append(hStringToQString(str));
238         }
239         return S_OK;
240     });
241     Q_ASSERT_SUCCEEDED(hr);
242 
243     return formats;
244 }
245 
retrieveData_sys(const QString & mimetype,QVariant::Type preferredType) const246 QVariant QWinRTInternalMimeData::retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const
247 {
248     qCDebug(lcQpaMime) << __FUNCTION__ << mimetype << preferredType;
249 
250     if (!dataView || !formats.contains(mimetype))
251         return QVariant();
252 
253     QVariant result;
254     HRESULT hr;
255     if (mimetype == QLatin1String("text/plain")) {
256         hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() {
257             HRESULT hr;
258             ComPtr<IAsyncOperation<HSTRING>> op;
259             HString res;
260             hr = dataView->GetTextAsync(&op);
261             Q_ASSERT_SUCCEEDED(hr);
262             hr = QWinRTFunctions::await(op, res.GetAddressOf());
263             Q_ASSERT_SUCCEEDED(hr);
264             result.setValue(hStringToQString(res));
265             return S_OK;
266         });
267     } else if (mimetype == QLatin1String("text/uri-list")) {
268         hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() {
269             HRESULT hr;
270             ComPtr<IAsyncOperation<IVectorView<IStorageItem*>*>> op;
271             hr = dataView->GetStorageItemsAsync(&op);
272             Q_ASSERT_SUCCEEDED(hr);
273             ComPtr<IVectorView<IStorageItem*>> nativeItems;
274             hr = QWinRTFunctions::await(op, nativeItems.GetAddressOf());
275             Q_ASSERT_SUCCEEDED(hr);
276             QList<QVariant> items;
277             quint32 count;
278             hr = nativeItems->get_Size(&count);
279             for (quint32 i = 0; i < count; ++i) {
280                 ComPtr<IStorageItem> item;
281                 hr = nativeItems->GetAt(i, &item);
282                 Q_ASSERT_SUCCEEDED(hr);
283                 HString path;
284                 hr = item->get_Path(path.GetAddressOf());
285                 Q_ASSERT_SUCCEEDED(hr);
286                 items.append(QUrl::fromLocalFile(hStringToQString(path)));
287             }
288             result.setValue(items);
289             return S_OK;
290         });
291     } else if (mimetype == QLatin1String("text/html")) {
292         hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() {
293             HRESULT hr;
294             ComPtr<IAsyncOperation<HSTRING>> op;
295             HString res;
296             hr = dataView->GetHtmlFormatAsync(&op);
297             Q_ASSERT_SUCCEEDED(hr);
298             hr = QWinRTFunctions::await(op, res.GetAddressOf());
299             Q_ASSERT_SUCCEEDED(hr);
300             result.setValue(hStringToQString(res));
301             return S_OK;
302         });
303     } else {
304         // Asking for custom data
305         hr = QEventDispatcherWinRT::runOnXamlThread([this, &result, mimetype]() {
306             HRESULT hr;
307             ComPtr<IAsyncOperation<IInspectable*>> op;
308             ComPtr<IInspectable> res;
309             HString type;
310             type.Set(reinterpret_cast<const wchar_t*>(mimetype.utf16()), uint(mimetype.size()));
311             hr = dataView->GetDataAsync(type.Get(), &op);
312             RETURN_OK_IF_FAILED("Could not query custom drag data.");
313             hr = QWinRTFunctions::await(op, res.GetAddressOf());
314             if (FAILED(hr) || !res) {
315                 qCDebug(lcQpaMime) << "Custom drop data operation returned no results or failed.";
316                 return S_OK;
317             }
318 
319             // Test for properties
320             ComPtr<IPropertyValue> propertyValue;
321             hr = res.As(&propertyValue);
322             if (SUCCEEDED(hr)) {
323                 // We need to check which type of custom data we are receiving
324                 PropertyType propertyType;
325                 propertyValue->get_Type(&propertyType);
326                 switch (propertyType) {
327                 case PropertyType_UInt8: {
328                     quint8 v;
329                     hr = propertyValue->GetUInt8(&v);
330                     Q_ASSERT_SUCCEEDED(hr);
331                     result.setValue(v);
332                     return S_OK;
333                 }
334                 case PropertyType_Int16: {
335                     qint16 v;
336                     hr = propertyValue->GetInt16(&v);
337                     Q_ASSERT_SUCCEEDED(hr);
338                     result.setValue(v);
339                     return S_OK;
340                 }
341                 case PropertyType_UInt16: {
342                     quint16 v;
343                     hr = propertyValue->GetUInt16(&v);
344                     Q_ASSERT_SUCCEEDED(hr);
345                     result.setValue(v);
346                     return S_OK;
347                 }
348                 case PropertyType_Int32: {
349                     qint32 v;
350                     hr = propertyValue->GetInt32(&v);
351                     Q_ASSERT_SUCCEEDED(hr);
352                     result.setValue(v);
353                     return S_OK;
354                 }
355                 case PropertyType_UInt32: {
356                     quint32 v;
357                     hr = propertyValue->GetUInt32(&v);
358                     Q_ASSERT_SUCCEEDED(hr);
359                     result.setValue(v);
360                     return S_OK;
361                 }
362                 case PropertyType_Int64: {
363                     qint64 v;
364                     hr = propertyValue->GetInt64(&v);
365                     Q_ASSERT_SUCCEEDED(hr);
366                     result.setValue(v);
367                     return S_OK;
368                 }
369                 case PropertyType_UInt64: {
370                     quint64 v;
371                     hr = propertyValue->GetUInt64(&v);
372                     Q_ASSERT_SUCCEEDED(hr);
373                     result.setValue(v);
374                     return S_OK;
375                 }
376                 case PropertyType_Single: {
377                     float v;
378                     hr = propertyValue->GetSingle(&v);
379                     Q_ASSERT_SUCCEEDED(hr);
380                     result.setValue(v);
381                     return S_OK;
382                 }
383                 case PropertyType_Double: {
384                     double v;
385                     hr = propertyValue->GetDouble(&v);
386                     Q_ASSERT_SUCCEEDED(hr);
387                     result.setValue(v);
388                     return S_OK;
389                 }
390                 case PropertyType_Char16: {
391                     wchar_t v;
392                     hr = propertyValue->GetChar16(&v);
393                     Q_ASSERT_SUCCEEDED(hr);
394                     result.setValue(QString::fromWCharArray(&v, 1));
395                     return S_OK;
396                 }
397                 case PropertyType_Boolean: {
398                     boolean v;
399                     hr = propertyValue->GetBoolean(&v);
400                     Q_ASSERT_SUCCEEDED(hr);
401                     result.setValue(v);
402                     return S_OK;
403                 }
404                 case PropertyType_String: {
405                     HString stringProperty;
406                     hr = propertyValue->GetString(stringProperty.GetAddressOf());
407                     Q_ASSERT_SUCCEEDED(hr);
408                     result.setValue(hStringToQString(stringProperty));
409                     return S_OK;
410                 }
411                 default:
412                     qCDebug(lcQpaMime) << "Unknown property type dropped:" << propertyType;
413                 }
414                 return S_OK;
415             }
416 
417             // Custom data can be read via input streams
418             ComPtr<IRandomAccessStream> randomAccessStream;
419             hr = res.As(&randomAccessStream);
420             if (SUCCEEDED(hr)) {
421                 UINT64 size;
422                 hr = randomAccessStream->get_Size(&size);
423                 Q_ASSERT_SUCCEEDED(hr);
424                 ComPtr<IInputStream> stream;
425                 hr = randomAccessStream.As(&stream);
426                 Q_ASSERT_SUCCEEDED(hr);
427 
428                 ComPtr<IBufferFactory> bufferFactory;
429                 hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
430                                             IID_PPV_ARGS(&bufferFactory));
431                 Q_ASSERT_SUCCEEDED(hr);
432 
433                 UINT32 length = UINT32(qBound(quint64(0), quint64(size), quint64(UINT_MAX)));
434                 ComPtr<IBuffer> buffer;
435                 hr = bufferFactory->Create(length, &buffer);
436                 Q_ASSERT_SUCCEEDED(hr);
437 
438                 ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> readOp;
439                 hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &readOp);
440 
441                 ComPtr<IBuffer> effectiveBuffer;
442                 hr = QWinRTFunctions::await(readOp, effectiveBuffer.GetAddressOf());
443 
444                 hr = effectiveBuffer->get_Length(&length);
445 
446                 ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
447                 hr = effectiveBuffer.As(&byteArrayAccess);
448 
449                 byte *bytes;
450                 hr = byteArrayAccess->Buffer(&bytes);
451                 QByteArray array((char *)bytes, int(length));
452                 result.setValue(array);
453                 return S_OK;
454             }
455 
456             HSTRING runtimeClass;
457             hr = res->GetRuntimeClassName(&runtimeClass);
458             Q_ASSERT_SUCCEEDED(hr);
459             HString converted;
460             converted.Set(runtimeClass);
461             qCDebug(lcQpaMime) << "Unknown drop data type received (" << hStringToQString(converted)
462                                << "). Ignoring...";
463             return S_OK;
464         });
465     }
466     return result;
467 }
468 
setDataView(const Microsoft::WRL::ComPtr<IDataPackageView> & d)469 void QWinRTInternalMimeData::setDataView(const Microsoft::WRL::ComPtr<IDataPackageView> &d)
470 {
471     dataView = d;
472     formats.clear();
473 }
474 
qt_drag_enter(IInspectable * sender,ABI::Windows::UI::Xaml::IDragEventArgs * e)475 static HRESULT qt_drag_enter(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e)
476 {
477     QWinRTDrag::instance()->handleNativeDragEvent(sender, e);
478     return S_OK;
479 }
480 
qt_drag_over(IInspectable * sender,ABI::Windows::UI::Xaml::IDragEventArgs * e)481 static HRESULT qt_drag_over(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e)
482 {
483     QWinRTDrag::instance()->handleNativeDragEvent(sender, e);
484     return S_OK;
485 }
486 
qt_drag_leave(IInspectable * sender,ABI::Windows::UI::Xaml::IDragEventArgs * e)487 static HRESULT qt_drag_leave(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e)
488 {
489     // Qt internally checks for new drags and auto sends leave events
490     // Also there is no QPA function for handling leave
491     Q_UNUSED(sender);
492     Q_UNUSED(e);
493     return S_OK;
494 }
495 
qt_drop(IInspectable * sender,ABI::Windows::UI::Xaml::IDragEventArgs * e)496 static HRESULT qt_drop(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e)
497 {
498     QWinRTDrag::instance()->handleNativeDragEvent(sender, e, true);
499     return S_OK;
500 }
501 
502 #define Q_DECLARE_DRAGHANDLER(name,func) \
503 class QtDragEventHandler##name : public IDragEventHandler \
504 { \
505 public: \
506     virtual ~QtDragEventHandler##name() {\
507     }\
508     STDMETHODIMP Invoke(IInspectable *sender, \
509                         ABI::Windows::UI::Xaml::IDragEventArgs *e) \
510     { \
511         return qt_##func(sender, e);\
512     } \
513  \
514     STDMETHODIMP \
515     QueryInterface(REFIID riid, void FAR* FAR* ppvObj) \
516     { \
517         if (riid == IID_IUnknown || riid == IID_IDragEventHandler) { \
518             *ppvObj = this; \
519             AddRef(); \
520             return NOERROR; \
521         } \
522         *ppvObj = NULL; \
523         return ResultFromScode(E_NOINTERFACE); \
524     } \
525  \
526     STDMETHODIMP_(ULONG) \
527     AddRef(void) \
528     { \
529         return ++m_refs; \
530     } \
531  \
532     STDMETHODIMP_(ULONG) \
533     Release(void) \
534     { \
535         if (--m_refs == 0) { \
536             delete this; \
537             return 0; \
538         } \
539         return m_refs; \
540     } \
541 private: \
542 ULONG m_refs{0}; \
543 };
544 
545 Q_DECLARE_DRAGHANDLER(Enter, drag_enter)
546 Q_DECLARE_DRAGHANDLER(Over, drag_over)
547 Q_DECLARE_DRAGHANDLER(Leave, drag_leave)
548 Q_DECLARE_DRAGHANDLER(Drop, drop)
549 
550 #define Q_INST_DRAGHANDLER(name) QtDragEventHandler##name()
551 
552 Q_GLOBAL_STATIC(QWinRTDrag, gDrag);
553 
554 extern ComPtr<ABI::Windows::UI::Input::IPointerPoint> qt_winrt_lastPointerPoint; // qwinrtscreen.cpp
555 
QWinRTDrag()556 QWinRTDrag::QWinRTDrag()
557     : QPlatformDrag()
558     , m_dragTarget(nullptr)
559 {
560     qCDebug(lcQpaMime) << __FUNCTION__;
561     m_enter = new Q_INST_DRAGHANDLER(Enter);
562     m_over = new Q_INST_DRAGHANDLER(Over);
563     m_leave = new Q_INST_DRAGHANDLER(Leave);
564     m_drop = new Q_INST_DRAGHANDLER(Drop);
565     m_mimeData = new QWinRTInternalMimeData;
566 }
567 
~QWinRTDrag()568 QWinRTDrag::~QWinRTDrag()
569 {
570     qCDebug(lcQpaMime) << __FUNCTION__;
571     delete m_enter;
572     delete m_over;
573     delete m_leave;
574     delete m_drop;
575     delete m_mimeData;
576 }
577 
instance()578 QWinRTDrag *QWinRTDrag::instance()
579 {
580     return gDrag;
581 }
582 
resetUiElementDrag(ComPtr<IUIElement3> & elem3,EventRegistrationToken startingToken)583 inline HRESULT resetUiElementDrag(ComPtr<IUIElement3> &elem3, EventRegistrationToken startingToken)
584 {
585     return QEventDispatcherWinRT::runOnXamlThread([elem3, startingToken]() {
586         HRESULT hr;
587         hr = elem3->put_CanDrag(false);
588         Q_ASSERT_SUCCEEDED(hr);
589         hr = elem3->remove_DragStarting(startingToken);
590         Q_ASSERT_SUCCEEDED(hr);
591         return S_OK;
592     });
593 }
594 
drag(QDrag * drag)595 Qt::DropAction QWinRTDrag::drag(QDrag *drag)
596 {
597     qCDebug(lcQpaMime) << __FUNCTION__ << drag;
598 
599     if (!qt_winrt_lastPointerPoint) {
600         Q_ASSERT_X(qt_winrt_lastPointerPoint, Q_FUNC_INFO, "No pointerpoint known");
601         return Qt::IgnoreAction;
602     }
603 
604     ComPtr<IUIElement3> elem3;
605     HRESULT hr = m_ui.As(&elem3);
606     Q_ASSERT_SUCCEEDED(hr);
607 
608     ComPtr<IAsyncOperation<ABI::Windows::ApplicationModel::DataTransfer::DataPackageOperation>> op;
609     EventRegistrationToken startingToken;
610 
611     hr = QEventDispatcherWinRT::runOnXamlThread([drag, &op, &hr, elem3, &startingToken]() {
612 
613         hr = elem3->put_CanDrag(true);
614         Q_ASSERT_SUCCEEDED(hr);
615 
616         auto startingCallback = Callback<ITypedEventHandler<UIElement*, DragStartingEventArgs*>> ([drag](IInspectable *, IDragStartingEventArgs *args) {
617             qCDebug(lcQpaMime) << "Drag starting" << args;
618 
619             ComPtr<IDataPackage> dataPackage;
620             HRESULT hr;
621             hr = args->get_Data(dataPackage.GetAddressOf());
622             Q_ASSERT_SUCCEEDED(hr);
623             Qt::DropAction action = drag->defaultAction();
624             hr = dataPackage->put_RequestedOperation(translateFromQDragDropActions(action));
625             Q_ASSERT_SUCCEEDED(hr);
626 
627 #ifndef QT_WINRT_LIMITED_DRAGANDDROP
628             ComPtr<IDragStartingEventArgs2> args2;
629             hr = args->QueryInterface(IID_PPV_ARGS(&args2));
630             Q_ASSERT_SUCCEEDED(hr);
631 
632             Qt::DropActions actions = drag->supportedActions();
633             DataPackageOperation allowedOperations = DataPackageOperation_None;
634             if (actions & Qt::CopyAction)
635                 allowedOperations |= DataPackageOperation_Copy;
636             if (actions & Qt::MoveAction)
637                 allowedOperations |= DataPackageOperation_Move;
638             if (actions & Qt::LinkAction)
639                 allowedOperations |= DataPackageOperation_Link;
640             hr = args2->put_AllowedOperations(allowedOperations);
641             Q_ASSERT_SUCCEEDED(hr);
642 #endif // QT_WINRT_LIMITED_DRAGANDDROP
643             QMimeData *mimeData = drag->mimeData();
644             if (mimeData->hasText()) {
645                 hr = dataPackage->SetText(qStringToHString(mimeData->text()).Get());
646                 Q_ASSERT_SUCCEEDED(hr);
647             }
648             if (mimeData->hasHtml()) {
649                 hr = dataPackage->SetHtmlFormat(qStringToHString(mimeData->html()).Get());
650                 Q_ASSERT_SUCCEEDED(hr);
651             }
652             // ### TODO: Missing: weblink, image
653 
654             if (!drag->pixmap().isNull()) {
655                 const QImage image2 = drag->pixmap().toImage();
656                 const QImage image = image2.convertToFormat(QImage::Format_ARGB32);
657                 if (!image.isNull()) {
658                     // Create IBuffer containing image
659                     ComPtr<IBuffer> imageBuffer
660                             = createIBufferFromData(reinterpret_cast<const char*>(image.bits()), int(image.sizeInBytes()));
661 
662                     ComPtr<ISoftwareBitmapFactory> bitmapFactory;
663                     hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_SoftwareBitmap).Get(),
664                                                 IID_PPV_ARGS(&bitmapFactory));
665                     Q_ASSERT_SUCCEEDED(hr);
666 
667                     ComPtr<ISoftwareBitmap> bitmap;
668                     hr = bitmapFactory->Create(BitmapPixelFormat_Rgba8, image.width(), image.height(), &bitmap);
669                     Q_ASSERT_SUCCEEDED(hr);
670 
671                     hr = bitmap->CopyFromBuffer(imageBuffer.Get());
672                     Q_ASSERT_SUCCEEDED(hr);
673 
674                     ComPtr<IDragUI> dragUi;
675                     hr = args->get_DragUI(dragUi.GetAddressOf());
676                     Q_ASSERT_SUCCEEDED(hr);
677 
678                     hr = dragUi->SetContentFromSoftwareBitmap(bitmap.Get());
679                     Q_ASSERT_SUCCEEDED(hr);
680                 }
681             }
682 
683             const QStringList formats = mimeData->formats();
684             for (auto item : formats) {
685                 QByteArray data = mimeData->data(item);
686 
687                 ComPtr<IBuffer> buffer = createIBufferFromData(data.constData(), data.size());
688 
689                 // We cannot push the buffer to the data package as the result on
690                 // recipient side is different from native events. It still sends a
691                 // buffer, but that potentially cannot be parsed. Hence we need to create
692                 // a IRandomAccessStream which gets forwarded as is to the drop side.
693                 ComPtr<IRandomAccessStream> ras;
694                 hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), &ras);
695                 Q_ASSERT_SUCCEEDED(hr);
696 
697                 hr = ras->put_Size(UINT64(data.size()));
698                 ComPtr<IOutputStream> outputStream;
699                 hr = ras->GetOutputStreamAt(0, &outputStream);
700                 Q_ASSERT_SUCCEEDED(hr);
701 
702                 ComPtr<IAsyncOperationWithProgress<UINT32,UINT32>> writeOp;
703                 hr = outputStream->WriteAsync(buffer.Get(), &writeOp);
704                 Q_ASSERT_SUCCEEDED(hr);
705 
706                 UINT32 result;
707                 hr = QWinRTFunctions::await(writeOp, &result);
708                 Q_ASSERT_SUCCEEDED(hr);
709 
710                 unsigned char flushResult;
711                 ComPtr<IAsyncOperation<bool>> flushOp;
712                 hr = outputStream->FlushAsync(&flushOp);
713                 Q_ASSERT_SUCCEEDED(hr);
714 
715                 hr = QWinRTFunctions::await(flushOp, &flushResult);
716                 Q_ASSERT_SUCCEEDED(hr);
717 
718                 hr = dataPackage->SetData(qStringToHString(item).Get(), ras.Get());
719                 Q_ASSERT_SUCCEEDED(hr);
720             }
721             return S_OK;
722         });
723 
724         hr = elem3->add_DragStarting(startingCallback.Get(), &startingToken);
725         Q_ASSERT_SUCCEEDED(hr);
726 
727         hr = elem3->StartDragAsync(qt_winrt_lastPointerPoint.Get(), &op);
728         Q_ASSERT_SUCCEEDED(hr);
729 
730         return hr;
731     });
732     if (!op || FAILED(hr)) {
733         qCDebug(lcQpaMime) << "Drag failed:" << hr;
734         hr = resetUiElementDrag(elem3, startingToken);
735         Q_ASSERT_SUCCEEDED(hr);
736         return Qt::IgnoreAction;
737     }
738 
739     DataPackageOperation nativeOperationType;
740     // Do not yield, as that can cause deadlocks when dropping inside the same app
741     hr = QWinRTFunctions::await(op, &nativeOperationType, QWinRTFunctions::ProcessThreadEvents);
742     Q_ASSERT_SUCCEEDED(hr);
743 
744     hr = resetUiElementDrag(elem3, startingToken);
745     Q_ASSERT_SUCCEEDED(hr);
746 
747     Qt::DropAction resultAction;
748     switch (nativeOperationType) {
749     case DataPackageOperation_Link:
750         resultAction = Qt::LinkAction;
751         break;
752     case DataPackageOperation_Copy:
753         resultAction = Qt::CopyAction;
754         break;
755     case DataPackageOperation_Move:
756         resultAction = Qt::MoveAction;
757         break;
758     case DataPackageOperation_None:
759     default:
760         resultAction = Qt::IgnoreAction;
761         break;
762     }
763 
764     return resultAction;
765 }
766 
setDropTarget(QWindow * target)767 void QWinRTDrag::setDropTarget(QWindow *target)
768 {
769     qCDebug(lcQpaMime) << __FUNCTION__ << target;
770     m_dragTarget = target;
771 }
772 
setUiElement(ComPtr<ABI::Windows::UI::Xaml::IUIElement> & element)773 void QWinRTDrag::setUiElement(ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element)
774 {
775     qCDebug(lcQpaMime) << __FUNCTION__;
776     m_ui = element;
777     // We set the element to always accept drops and then evaluate during
778     // runtime
779     HRESULT hr = element->put_AllowDrop(TRUE);
780     EventRegistrationToken tok;
781     hr = element->add_DragEnter(m_enter, &tok);
782     RETURN_VOID_IF_FAILED("Failed to add DragEnter handler.");
783     hr = element->add_DragOver(m_over, &tok);
784     RETURN_VOID_IF_FAILED("Failed to add DragOver handler.");
785     hr = element->add_DragLeave(m_leave, &tok);
786     RETURN_VOID_IF_FAILED("Failed to add DragLeave handler.");
787     hr = element->add_Drop(m_drop, &tok);
788     RETURN_VOID_IF_FAILED("Failed to add Drop handler.");
789 }
790 
handleNativeDragEvent(IInspectable * sender,ABI::Windows::UI::Xaml::IDragEventArgs * e,bool drop)791 void QWinRTDrag::handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop)
792 {
793     Q_UNUSED(sender);
794 
795     if (!m_dragTarget)
796         return;
797 
798     HRESULT hr;
799     Point relativePoint;
800     hr = e->GetPosition(m_ui.Get(), &relativePoint);
801     RETURN_VOID_IF_FAILED("Could not query drag position.");
802     const QPoint p(int(relativePoint.X), int(relativePoint.Y));
803 
804     ComPtr<IDragEventArgs2> e2;
805     hr = e->QueryInterface(IID_PPV_ARGS(&e2));
806     RETURN_VOID_IF_FAILED("Could not convert drag event args");
807 
808     DragDropModifiers modifiers;
809     hr = e2->get_Modifiers(&modifiers);
810 
811 #ifndef QT_WINRT_LIMITED_DRAGANDDROP
812     ComPtr<IDragEventArgs3> e3;
813     hr = e->QueryInterface(IID_PPV_ARGS(&e3));
814     Q_ASSERT_SUCCEEDED(hr);
815 
816     DataPackageOperation dataOp;
817     hr = e3->get_AllowedOperations(&dataOp);
818     if (FAILED(hr))
819         qCDebug(lcQpaMime) << __FUNCTION__ << "Could not query drag operations";
820 
821     const Qt::DropActions actions = translateToQDragDropActions(dataOp);
822 #else // !QT_WINRT_LIMITED_DRAGANDDROP
823     const Qt::DropActions actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction;;
824 #endif // !QT_WINRT_LIMITED_DRAGANDDROP
825 
826     ComPtr<IDataPackageView> dataView;
827     hr = e2->get_DataView(&dataView);
828     Q_ASSERT_SUCCEEDED(hr);
829 
830     m_mimeData->setDataView(dataView);
831 
832     // We use deferral as we need to jump to the Qt thread to handle
833     // the drag event
834     ComPtr<IDragOperationDeferral> deferral;
835     hr = e2->GetDeferral(&deferral);
836     Q_ASSERT_SUCCEEDED(hr);
837 
838     DragThreadTransferData *transferData = new DragThreadTransferData;
839     transferData->moveToThread(qGuiApp->thread());
840     transferData->window = m_dragTarget;
841     transferData->point = p;
842     transferData->mime = m_mimeData;
843     transferData->actions = actions;
844     transferData->dropAction = drop;
845     transferData->nativeArgs = e;
846     transferData->deferral = deferral;
847     QMetaObject::invokeMethod(transferData, "handleDrag", Qt::QueuedConnection);
848 }
849 
DragThreadTransferData(QObject * parent)850 DragThreadTransferData::DragThreadTransferData(QObject *parent)
851     : QObject(parent)
852     , dropAction(false)
853 {
854 }
855 
handleDrag()856 void DragThreadTransferData::handleDrag()
857 {
858     bool accepted = false;
859     Qt::DropAction acceptedAction;
860     if (dropAction) {
861         QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(window, mime, point, actions);
862         accepted = response.isAccepted();
863         acceptedAction = response.acceptedAction();
864     } else {
865         QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(window, mime, point, actions);
866         accepted = response.isAccepted();
867         acceptedAction = response.acceptedAction();
868     }
869 
870     HRESULT hr;
871     hr = QEventDispatcherWinRT::runOnXamlThread([accepted, acceptedAction, this]() {
872         HRESULT hr;
873         hr = nativeArgs->put_Handled(accepted);
874         if (acceptedAction != Qt::IgnoreAction) {
875             ComPtr<IDragEventArgs2> e2;
876             hr = nativeArgs.As(&e2);
877             if (SUCCEEDED(hr))
878                 hr = e2->put_AcceptedOperation(translateFromQDragDropActions(acceptedAction));
879         }
880         deferral->Complete();
881         return S_OK;
882     });
883     Q_ASSERT_SUCCEEDED(hr);
884     deleteLater();
885 }
886 
887 QT_END_NAMESPACE
888 
889 #include "qwinrtdrag.moc"
890