diff options
Diffstat (limited to 'src/corelib/kernel/qeventdispatcher_winrt.cpp')
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_winrt.cpp | 132 |
1 files changed, 76 insertions, 56 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_winrt.cpp b/src/corelib/kernel/qeventdispatcher_winrt.cpp index eceba8d002..df070dd1ae 100644 --- a/src/corelib/kernel/qeventdispatcher_winrt.cpp +++ b/src/corelib/kernel/qeventdispatcher_winrt.cpp @@ -40,6 +40,7 @@ #include <private/qabstracteventdispatcher_p.h> #include <private/qcoreapplication_p.h> +#include <functional> #include <wrl.h> #include <windows.foundation.h> #include <windows.system.threading.h> @@ -70,6 +71,23 @@ struct WinRTTimerInfo : public QAbstractEventDispatcher::TimerInfo { quint64 targetTime; }; +class AgileDispatchedHandler : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IDispatchedHandler, IAgileObject> +{ +public: + AgileDispatchedHandler(const std::function<HRESULT()> &delegate) + : delegate(delegate) + { + } + + HRESULT __stdcall Invoke() + { + return delegate(); + } + +private: + std::function<HRESULT()> delegate; +}; + class QEventDispatcherWinRTPrivate : public QAbstractEventDispatcherPrivate { Q_DECLARE_PUBLIC(QEventDispatcherWinRT) @@ -79,10 +97,6 @@ public: ~QEventDispatcherWinRTPrivate(); private: - ComPtr<IThreadPoolTimerStatics> timerFactory; - ComPtr<ICoreDispatcher> coreDispatcher; - QPointer<QThread> thread; - QHash<int, QObject *> timerIdToObject; QVector<WinRTTimerInfo> timerInfos; QHash<HANDLE, int> timerHandleToId; @@ -136,40 +150,11 @@ private: } return true; } - - void fetchCoreDispatcher() - { - ComPtr<ICoreImmersiveApplication> application; - HRESULT hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication).Get(), - IID_PPV_ARGS(&application)); - RETURN_VOID_IF_FAILED("Failed to get the application factory"); - - static ComPtr<ICoreApplicationView> view; - if (view) - return; - - hr = application->get_MainView(&view); - RETURN_VOID_IF_FAILED("Failed to get the main view"); - - ComPtr<ICoreApplicationView2> view2; - hr = view.As(&view2); - RETURN_VOID_IF_FAILED("Failed to cast the main view"); - - hr = view2->get_Dispatcher(&coreDispatcher); - if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) // expected in thread pool cases - return; - RETURN_VOID_IF_FAILED("Failed to get core dispatcher"); - - thread = QThread::currentThread(); - } }; QEventDispatcherWinRT::QEventDispatcherWinRT(QObject *parent) : QAbstractEventDispatcher(*new QEventDispatcherWinRTPrivate, parent) { - Q_D(QEventDispatcherWinRT); - - d->fetchCoreDispatcher(); } QEventDispatcherWinRT::QEventDispatcherWinRT(QEventDispatcherWinRTPrivate &dd, QObject *parent) @@ -180,32 +165,52 @@ QEventDispatcherWinRT::~QEventDispatcherWinRT() { } +HRESULT QEventDispatcherWinRT::runOnXamlThread(const std::function<HRESULT ()> &delegate, bool waitForRun) +{ + static __declspec(thread) ICoreDispatcher *dispatcher = nullptr; + if (!dispatcher) { + HRESULT hr; + ComPtr<ICoreImmersiveApplication> application; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication).Get(), + IID_PPV_ARGS(&application)); + ComPtr<ICoreApplicationView> view; + hr = application->get_MainView(&view); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<ICoreWindow> window; + hr = view->get_CoreWindow(&window); + Q_ASSERT_SUCCEEDED(hr); + hr = window->get_Dispatcher(&dispatcher); + Q_ASSERT_SUCCEEDED(hr); + } + + HRESULT hr; + boolean onXamlThread; + hr = dispatcher->get_HasThreadAccess(&onXamlThread); + Q_ASSERT_SUCCEEDED(hr); + if (onXamlThread) // Already there + return delegate(); + + ComPtr<IAsyncAction> op; + hr = dispatcher->RunAsync(CoreDispatcherPriority_Normal, Make<AgileDispatchedHandler>(delegate).Get(), &op); + if (FAILED(hr) || !waitForRun) + return hr; + return QWinRTFunctions::await(op); +} + bool QEventDispatcherWinRT::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherWinRT); - if (d->thread && d->thread != QThread::currentThread()) - d->fetchCoreDispatcher(); - + DWORD waitTime = 0; do { - // Process native events - if (d->coreDispatcher) { - boolean hasThreadAccess; - HRESULT hr = d->coreDispatcher->get_HasThreadAccess(&hasThreadAccess); - if (SUCCEEDED(hr) && hasThreadAccess) { - hr = d->coreDispatcher->ProcessEvents(CoreProcessEventsOption_ProcessAllIfPresent); - if (FAILED(hr)) - qErrnoWarning(hr, "Failed to process events"); - } - } - // Additional user events have to be handled before timer events, but the function may not // return yet. const bool userEventsSent = sendPostedEvents(flags); - emit aboutToBlock(); const QVector<HANDLE> timerHandles = d->timerIdToHandle.values().toVector(); - DWORD waitResult = WaitForMultipleObjectsEx(timerHandles.count(), timerHandles.constData(), FALSE, 1, TRUE); + if (waitTime) + emit aboutToBlock(); + DWORD waitResult = WaitForMultipleObjectsEx(timerHandles.count(), timerHandles.constData(), FALSE, waitTime, TRUE); if (waitResult >= WAIT_OBJECT_0 && waitResult < WAIT_OBJECT_0 + timerHandles.count()) { const HANDLE handle = timerHandles.value(waitResult - WAIT_OBJECT_0); ResetEvent(handle); @@ -228,6 +233,13 @@ bool QEventDispatcherWinRT::processEvents(QEventLoop::ProcessEventsFlags flags) if (userEventsSent) return true; + + // We cannot wait infinitely like on other platforms, as + // WaitForMultipleObjectsEx might not return. + // For instance win32 uses MsgWaitForMultipleObjects to hook + // into the native event loop, while WinRT handles those + // via callbacks. + waitTime = 1; } while (flags & QEventLoop::WaitForMoreEvents); return false; } @@ -283,11 +295,20 @@ void QEventDispatcherWinRT::registerTimer(int timerId, int interval, Qt::TimerTy } TimeSpan period; - period.Duration = interval ? (interval * 10000) : 1; // TimeSpan is based on 100-nanosecond units - IThreadPoolTimer *timer; + // TimeSpan is based on 100-nanosecond units + period.Duration = qMax(qint64(1), qint64(interval) * 10000); const HANDLE handle = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE | EVENT_MODIFY_STATE); const HANDLE cancelHandle = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE|EVENT_MODIFY_STATE); - HRESULT hr = d->timerFactory->CreatePeriodicTimerWithCompletion( + HRESULT hr = runOnXamlThread([cancelHandle, handle, period]() { + static ComPtr<IThreadPoolTimerStatics> timerFactory; + HRESULT hr; + if (!timerFactory) { + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), + &timerFactory); + Q_ASSERT_SUCCEEDED(hr); + } + IThreadPoolTimer *timer; + hr = timerFactory->CreatePeriodicTimerWithCompletion( Callback<ITimerElapsedHandler>([handle, cancelHandle](IThreadPoolTimer *timer) { DWORD cancelResult = WaitForSingleObjectEx(cancelHandle, 0, TRUE); if (cancelResult == WAIT_OBJECT_0) { @@ -306,8 +327,10 @@ void QEventDispatcherWinRT::registerTimer(int timerId, int interval, Qt::TimerTy CloseHandle(cancelHandle); return S_OK; }).Get(), &timer); + RETURN_HR_IF_FAILED("Failed to create periodic timer"); + return hr; + }, false); if (FAILED(hr)) { - qErrnoWarning(hr, "Failed to create periodic timer"); CloseHandle(handle); CloseHandle(cancelHandle); return; @@ -486,9 +509,6 @@ QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate() const bool isGuiThread = QCoreApplication::instance() && QThread::currentThread() == QCoreApplication::instance()->thread(); CoInitializeEx(NULL, isGuiThread ? COINIT_APARTMENTTHREADED : COINIT_MULTITHREADED); - HRESULT hr; - hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory); - Q_ASSERT_SUCCEEDED(hr); HANDLE interruptHandle = CreateEventEx(NULL, NULL, NULL, SYNCHRONIZE|EVENT_MODIFY_STATE); timerIdToHandle.insert(INTERRUPT_HANDLE, interruptHandle); timerHandleToId.insert(interruptHandle, INTERRUPT_HANDLE); |