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