1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module 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 
40 #include "qeventdispatcher_winrt_p.h"
41 
42 #include <QtCore/QCoreApplication>
43 #include <QtCore/QThread>
44 #include <QtCore/QHash>
45 #include <QtCore/QMutex>
46 #include <QtCore/QSemaphore>
47 #include <QtCore/qfunctions_winrt.h>
48 #include <private/qabstracteventdispatcher_p.h>
49 #include <private/qcoreapplication_p.h>
50 
51 #include <functional>
52 #include <memory>
53 
54 #include <wrl.h>
55 #include <windows.foundation.h>
56 #include <windows.system.threading.h>
57 #include <windows.ui.core.h>
58 #include <windows.applicationmodel.core.h>
59 using namespace Microsoft::WRL;
60 using namespace Microsoft::WRL::Wrappers;
61 using namespace ABI::Windows::System::Threading;
62 using namespace ABI::Windows::Foundation;
63 using namespace ABI::Windows::Foundation::Collections;
64 using namespace ABI::Windows::UI::Core;
65 using namespace ABI::Windows::ApplicationModel::Core;
66 
67 QT_BEGIN_NAMESPACE
68 
69 #define INTERRUPT_HANDLE 0
70 #define INVALID_TIMER_ID -1
71 
72 struct WinRTTimerInfo : public QAbstractEventDispatcher::TimerInfo {
WinRTTimerInfoWinRTTimerInfo73     WinRTTimerInfo(int timerId = INVALID_TIMER_ID, int interval = 0, Qt::TimerType timerType = Qt::CoarseTimer,
74                    QObject *obj = 0, quint64 tt = 0) :
75         QAbstractEventDispatcher::TimerInfo(timerId, interval, timerType),
76         inEvent(false), object(obj), targetTime(tt)
77     {
78     }
79 
80     bool inEvent;
81     QObject *object;
82     quint64 targetTime;
83 };
84 
85 class AgileDispatchedHandler : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IDispatchedHandler, IAgileObject>
86 {
87 public:
AgileDispatchedHandler(const std::function<HRESULT ()> & delegate)88     AgileDispatchedHandler(const std::function<HRESULT()> &delegate)
89         : delegate(delegate)
90     {
91     }
92 
Invoke()93     HRESULT __stdcall Invoke()
94     {
95         return delegate();
96     }
97 
98 private:
99     std::function<HRESULT()> delegate;
100 };
101 
102 class QWorkHandler : public IWorkItemHandler
103 {
104 public:
QWorkHandler(const std::function<HRESULT ()> & delegate)105     QWorkHandler(const std::function<HRESULT()> &delegate)
106         : m_delegate(delegate)
107     {
108     }
109 
Invoke(ABI::Windows::Foundation::IAsyncAction * operation)110     STDMETHODIMP Invoke(ABI::Windows::Foundation::IAsyncAction *operation)
111     {
112         HRESULT res = m_delegate();
113         Q_UNUSED(operation);
114         return res;
115     }
116 
QueryInterface(REFIID riid,void FAR * FAR * ppvObj)117     STDMETHODIMP QueryInterface(REFIID riid, void FAR* FAR* ppvObj)
118     {
119         if (riid == IID_IUnknown || riid == IID_IWorkItemHandler) {
120             *ppvObj = this;
121             AddRef();
122             return NOERROR;
123         }
124         *ppvObj = NULL;
125         return ResultFromScode(E_NOINTERFACE);
126     }
127 
AddRef(void)128     STDMETHODIMP_(ULONG) AddRef(void)
129     {
130         return ++m_refs;
131     }
132 
Release(void)133     STDMETHODIMP_(ULONG) Release(void)
134     {
135         if (--m_refs == 0) {
136             delete this;
137             return 0;
138         }
139         return m_refs;
140     }
141 
142 private:
143     std::function<HRESULT()> m_delegate;
144     ULONG m_refs{0};
145 };
146 
147 class QEventDispatcherWinRTPrivate : public QAbstractEventDispatcherPrivate
148 {
149     Q_DECLARE_PUBLIC(QEventDispatcherWinRT)
150 
151 public:
152     QEventDispatcherWinRTPrivate();
153     ~QEventDispatcherWinRTPrivate();
154 
155 private:
156     QHash<int, QObject *> timerIdToObject;
157     QVector<WinRTTimerInfo> timerInfos;
158     mutable QMutex timerInfoLock;
159     QHash<HANDLE, int> timerHandleToId;
160     QHash<int, HANDLE> timerIdToHandle;
161     QHash<int, HANDLE> timerIdToCancelHandle;
162 
addTimer(int id,int interval,Qt::TimerType type,QObject * obj,HANDLE handle,HANDLE cancelHandle)163     void addTimer(int id, int interval, Qt::TimerType type, QObject *obj,
164                      HANDLE handle, HANDLE cancelHandle)
165     {
166         // Zero timer events do not need these handles.
167         if (interval > 0) {
168             timerHandleToId.insert(handle, id);
169             timerIdToHandle.insert(id, handle);
170             timerIdToCancelHandle.insert(id, cancelHandle);
171         }
172 
173         const quint64 targetTime = qt_msectime() + interval;
174         const WinRTTimerInfo info(id, interval, type, obj, targetTime);
175         QMutexLocker locker(&timerInfoLock);
176         if (id >= timerInfos.size())
177             timerInfos.resize(id + 1);
178         timerInfos[id] = info;
179         timerIdToObject.insert(id, obj);
180     }
181 
removeTimer(int id)182     bool removeTimer(int id)
183     {
184         QMutexLocker locker(&timerInfoLock);
185         if (id >= timerInfos.size())
186             return false;
187 
188         WinRTTimerInfo &info = timerInfos[id];
189         if (info.timerId == INVALID_TIMER_ID)
190             return false;
191 
192         if (info.interval > 0 && (!timerIdToHandle.contains(id) || !timerIdToCancelHandle.contains(id)))
193             return false;
194 
195         info.timerId = INVALID_TIMER_ID;
196 
197         // Remove invalid timerinfos from the vector's end, if the timer with the highest id was removed
198         int lastTimer = timerInfos.size() - 1;
199         while (lastTimer >= 0 && timerInfos.at(lastTimer).timerId == INVALID_TIMER_ID)
200             --lastTimer;
201         if (lastTimer >= 0 && lastTimer != timerInfos.size() - 1)
202             timerInfos.resize(lastTimer + 1);
203         timerIdToObject.remove(id);
204         // ... remove handle from all lists
205         if (info.interval > 0) {
206             HANDLE handle = timerIdToHandle.take(id);
207             timerHandleToId.remove(handle);
208             SetEvent(timerIdToCancelHandle.take(id));
209         }
210         return true;
211     }
212 };
213 
QEventDispatcherWinRT(QObject * parent)214 QEventDispatcherWinRT::QEventDispatcherWinRT(QObject *parent)
215     : QAbstractEventDispatcher(*new QEventDispatcherWinRTPrivate, parent)
216 {
217 }
218 
QEventDispatcherWinRT(QEventDispatcherWinRTPrivate & dd,QObject * parent)219 QEventDispatcherWinRT::QEventDispatcherWinRT(QEventDispatcherWinRTPrivate &dd, QObject *parent)
220     : QAbstractEventDispatcher(dd, parent)
221 { }
222 
~QEventDispatcherWinRT()223 QEventDispatcherWinRT::~QEventDispatcherWinRT()
224 {
225 }
226 
runOnXamlThread(const std::function<HRESULT ()> & delegate,bool waitForRun)227 HRESULT QEventDispatcherWinRT::runOnXamlThread(const std::function<HRESULT ()> &delegate, bool waitForRun)
228 {
229     static __declspec(thread) ICoreDispatcher *dispatcher = nullptr;
230     HRESULT hr;
231     if (!dispatcher) {
232         ComPtr<ICoreImmersiveApplication> application;
233         hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication).Get(),
234                                     IID_PPV_ARGS(&application));
235         ComPtr<ICoreApplicationView> view;
236         hr = application->get_MainView(&view);
237         if (SUCCEEDED(hr) && view) {
238             ComPtr<ICoreWindow> window;
239             hr = view->get_CoreWindow(&window);
240             Q_ASSERT_SUCCEEDED(hr);
241             if (!window) {
242                 // In case the application is launched via activation
243                 // there might not be a main view (eg ShareTarget).
244                 // Hence iterate through the available views and try to find
245                 // a dispatcher in there
246                 ComPtr<IVectorView<CoreApplicationView*>> appViews;
247                 hr = application->get_Views(&appViews);
248                 Q_ASSERT_SUCCEEDED(hr);
249                 quint32 count;
250                 hr = appViews->get_Size(&count);
251                 Q_ASSERT_SUCCEEDED(hr);
252                 for (quint32 i = 0; i < count; ++i) {
253                     hr = appViews->GetAt(i, &view);
254                     Q_ASSERT_SUCCEEDED(hr);
255                     hr = view->get_CoreWindow(&window);
256                     Q_ASSERT_SUCCEEDED(hr);
257                     if (window) {
258                         hr = window->get_Dispatcher(&dispatcher);
259                         Q_ASSERT_SUCCEEDED(hr);
260                         if (dispatcher)
261                             break;
262                     }
263                 }
264             } else {
265                 hr = window->get_Dispatcher(&dispatcher);
266                 Q_ASSERT_SUCCEEDED(hr);
267             }
268         }
269     }
270 
271     if (Q_UNLIKELY(!dispatcher)) {
272         // In case the application is launched in a way that has no UI and
273         // also does not allow to create one, e.g. as a background task.
274         // Features like network operations do still work, others might cause
275         // errors in that case.
276         ComPtr<IThreadPoolStatics> tpStatics;
277         hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(),
278                                     IID_PPV_ARGS(&tpStatics));
279         ComPtr<IAsyncAction> op;
280         hr = tpStatics.Get()->RunAsync(new QWorkHandler(delegate), &op);
281         if (FAILED(hr) || !waitForRun)
282             return hr;
283         return QWinRTFunctions::await(op);
284     }
285 
286     boolean onXamlThread;
287     hr = dispatcher->get_HasThreadAccess(&onXamlThread);
288     Q_ASSERT_SUCCEEDED(hr);
289     if (onXamlThread) // Already there
290         return delegate();
291 
292     ComPtr<IAsyncAction> op;
293     hr = dispatcher->RunAsync(CoreDispatcherPriority_Normal, Make<AgileDispatchedHandler>(delegate).Get(), &op);
294     if (FAILED(hr) || !waitForRun)
295         return hr;
296     return QWinRTFunctions::await(op);
297 }
298 
runOnMainThread(const std::function<HRESULT ()> & delegate,int timeout)299 HRESULT QEventDispatcherWinRT::runOnMainThread(const std::function<HRESULT()> &delegate, int timeout)
300 {
301     if (QThread::currentThread() == QCoreApplication::instance()->thread())
302         return delegate();
303 
304     struct State {
305         QSemaphore semaphore;
306         HRESULT result;
307     };
308 
309     const auto state = std::make_shared<State>();
310 
311     QMetaObject::invokeMethod(QCoreApplication::instance(), [delegate, state]() {
312         const QSemaphoreReleaser releaser{state->semaphore};
313         state->result = delegate();
314     }, nullptr);
315 
316     return state->semaphore.tryAcquire(1, timeout) ? state->result : E_FAIL;
317 }
318 
processEvents(QEventLoop::ProcessEventsFlags flags)319 bool QEventDispatcherWinRT::processEvents(QEventLoop::ProcessEventsFlags flags)
320 {
321     Q_D(QEventDispatcherWinRT);
322 
323     DWORD waitTime = 0;
324     do {
325         // Additional user events have to be handled before timer events, but the function may not
326         // return yet.
327         const bool userEventsSent = sendPostedEvents(flags);
328 
329         const QVector<HANDLE> timerHandles = d->timerIdToHandle.values().toVector();
330         if (waitTime)
331             emit aboutToBlock();
332         bool timerEventsSent = false;
333         DWORD waitResult = WaitForMultipleObjectsEx(timerHandles.count(), timerHandles.constData(), FALSE, waitTime, TRUE);
334         while (waitResult >= WAIT_OBJECT_0 && waitResult < WAIT_OBJECT_0 + timerHandles.count()) {
335             timerEventsSent = true;
336             const HANDLE handle = timerHandles.value(waitResult - WAIT_OBJECT_0);
337             ResetEvent(handle);
338             const int timerId = d->timerHandleToId.value(handle);
339             if (timerId == INTERRUPT_HANDLE)
340                 break;
341 
342             {
343                 QMutexLocker locker(&d->timerInfoLock);
344 
345                 WinRTTimerInfo &info = d->timerInfos[timerId];
346                 Q_ASSERT(info.timerId != INVALID_TIMER_ID);
347 
348                 QCoreApplication::postEvent(this, new QTimerEvent(timerId));
349 
350                 // Update timer's targetTime
351                 const quint64 targetTime = qt_msectime() + info.interval;
352                 info.targetTime = targetTime;
353             }
354             waitResult = WaitForMultipleObjectsEx(timerHandles.count(), timerHandles.constData(), FALSE, 0, TRUE);
355         }
356         emit awake();
357         if (timerEventsSent || userEventsSent)
358             return true;
359 
360         // We cannot wait infinitely like on other platforms, as
361         // WaitForMultipleObjectsEx might not return.
362         // For instance win32 uses MsgWaitForMultipleObjects to hook
363         // into the native event loop, while WinRT handles those
364         // via callbacks.
365         waitTime = 1;
366     } while (flags & QEventLoop::WaitForMoreEvents);
367     return false;
368 }
369 
sendPostedEvents(QEventLoop::ProcessEventsFlags flags)370 bool QEventDispatcherWinRT::sendPostedEvents(QEventLoop::ProcessEventsFlags flags)
371 {
372     Q_UNUSED(flags);
373     if (hasPendingEvents()) {
374         QCoreApplication::sendPostedEvents();
375         return true;
376     }
377     return false;
378 }
379 
hasPendingEvents()380 bool QEventDispatcherWinRT::hasPendingEvents()
381 {
382     return qGlobalPostedEventsCount();
383 }
384 
registerSocketNotifier(QSocketNotifier * notifier)385 void QEventDispatcherWinRT::registerSocketNotifier(QSocketNotifier *notifier)
386 {
387     Q_UNUSED(notifier);
388     Q_UNIMPLEMENTED();
389 }
unregisterSocketNotifier(QSocketNotifier * notifier)390 void QEventDispatcherWinRT::unregisterSocketNotifier(QSocketNotifier *notifier)
391 {
392     Q_UNUSED(notifier);
393     Q_UNIMPLEMENTED();
394 }
395 
registerTimer(int timerId,int interval,Qt::TimerType timerType,QObject * object)396 void QEventDispatcherWinRT::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
397 {
398     Q_UNUSED(timerType);
399 
400     if (timerId < 1 || interval < 0 || !object) {
401 #ifndef QT_NO_DEBUG
402         qWarning("QEventDispatcherWinRT::registerTimer: invalid arguments");
403 #endif
404         return;
405     } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
406 #ifndef QT_NO_DEBUG
407         qWarning("QEventDispatcherWinRT::registerTimer: timers cannot be started from another thread");
408 #endif
409         return;
410     }
411 
412     Q_D(QEventDispatcherWinRT);
413     // Don't use timer factory for zero-delay timers
414     if (interval == 0u) {
415         d->addTimer(timerId, interval, timerType, object, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE);
416         QCoreApplication::postEvent(this, new QTimerEvent(timerId));
417         return;
418     }
419 
420     TimeSpan period;
421     // TimeSpan is based on 100-nanosecond units
422     period.Duration = qMax(qint64(1), qint64(interval) * 10000);
423     const HANDLE handle = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE | EVENT_MODIFY_STATE);
424     const HANDLE cancelHandle = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE|EVENT_MODIFY_STATE);
425     HRESULT hr = runOnXamlThread([cancelHandle, handle, period]() {
426         static ComPtr<IThreadPoolTimerStatics> timerFactory;
427         HRESULT hr;
428         if (!timerFactory) {
429             hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(),
430                                       &timerFactory);
431             Q_ASSERT_SUCCEEDED(hr);
432         }
433         IThreadPoolTimer *timer;
434         hr = timerFactory->CreatePeriodicTimerWithCompletion(
435         Callback<ITimerElapsedHandler>([handle, cancelHandle](IThreadPoolTimer *timer) {
436             DWORD cancelResult = WaitForSingleObjectEx(cancelHandle, 0, TRUE);
437             if (cancelResult == WAIT_OBJECT_0) {
438                 timer->Cancel();
439                 return S_OK;
440             }
441             if (!SetEvent(handle)) {
442                 Q_ASSERT_X(false, "QEventDispatcherWinRT::registerTimer",
443                            "SetEvent should never fail here");
444                 return S_OK;
445             }
446             return S_OK;
447         }).Get(), period,
448         Callback<ITimerDestroyedHandler>([handle, cancelHandle](IThreadPoolTimer *) {
449             CloseHandle(handle);
450             CloseHandle(cancelHandle);
451             return S_OK;
452         }).Get(), &timer);
453         RETURN_HR_IF_FAILED("Failed to create periodic timer");
454         return hr;
455     }, false);
456     if (FAILED(hr)) {
457         CloseHandle(handle);
458         CloseHandle(cancelHandle);
459         return;
460     }
461     d->addTimer(timerId, interval, timerType, object, handle, cancelHandle);
462 }
463 
unregisterTimer(int timerId)464 bool QEventDispatcherWinRT::unregisterTimer(int timerId)
465 {
466     if (timerId < 1) {
467 #ifndef QT_NO_DEBUG
468         qWarning("QEventDispatcherWinRT::unregisterTimer: invalid argument");
469 #endif
470         return false;
471     }
472     if (thread() != QThread::currentThread()) {
473 #ifndef QT_NO_DEBUG
474         qWarning("QEventDispatcherWinRT::unregisterTimer: timers cannot be stopped from another thread");
475 #endif
476         return false;
477     }
478 
479     // As we post all timer events internally, they have to pe removed to prevent stray events
480     QCoreApplicationPrivate::removePostedTimerEvent(this, timerId);
481     Q_D(QEventDispatcherWinRT);
482     return d->removeTimer(timerId);
483 }
484 
unregisterTimers(QObject * object)485 bool QEventDispatcherWinRT::unregisterTimers(QObject *object)
486 {
487     if (!object) {
488 #ifndef QT_NO_DEBUG
489         qWarning("QEventDispatcherWinRT::unregisterTimers: invalid argument");
490 #endif
491         return false;
492     }
493     QThread *currentThread = QThread::currentThread();
494     if (object->thread() != thread() || thread() != currentThread) {
495 #ifndef QT_NO_DEBUG
496         qWarning("QEventDispatcherWinRT::unregisterTimers: timers cannot be stopped from another thread");
497 #endif
498         return false;
499     }
500 
501     Q_D(QEventDispatcherWinRT);
502     const auto timerIds = d->timerIdToObject.keys(); // ### FIXME: iterate over hash directly? But unregisterTimer() modifies the hash!
503     for (int id : timerIds) {
504         if (d->timerIdToObject.value(id) == object)
505             unregisterTimer(id);
506     }
507 
508     return true;
509 }
510 
registeredTimers(QObject * object) const511 QList<QAbstractEventDispatcher::TimerInfo> QEventDispatcherWinRT::registeredTimers(QObject *object) const
512 {
513     if (!object) {
514 #ifndef QT_NO_DEBUG
515         qWarning("QEventDispatcherWinRT:registeredTimers: invalid argument");
516 #endif
517         return QList<TimerInfo>();
518     }
519 
520     Q_D(const QEventDispatcherWinRT);
521     QMutexLocker locker(&d->timerInfoLock);
522     QList<TimerInfo> timerInfos;
523     for (const WinRTTimerInfo &info : d->timerInfos) {
524         if (info.object == object && info.timerId != INVALID_TIMER_ID)
525             timerInfos.append(info);
526     }
527     return timerInfos;
528 }
529 
registerEventNotifier(QWinEventNotifier * notifier)530 bool QEventDispatcherWinRT::registerEventNotifier(QWinEventNotifier *notifier)
531 {
532     Q_UNUSED(notifier);
533     Q_UNIMPLEMENTED();
534     return false;
535 }
536 
unregisterEventNotifier(QWinEventNotifier * notifier)537 void QEventDispatcherWinRT::unregisterEventNotifier(QWinEventNotifier *notifier)
538 {
539     Q_UNUSED(notifier);
540     Q_UNIMPLEMENTED();
541 }
542 
remainingTime(int timerId)543 int QEventDispatcherWinRT::remainingTime(int timerId)
544 {
545     if (timerId < 1) {
546 #ifndef QT_NO_DEBUG
547         qWarning("QEventDispatcherWinRT::remainingTime: invalid argument");
548 #endif
549         return -1;
550     }
551 
552     Q_D(QEventDispatcherWinRT);
553     QMutexLocker locker(&d->timerInfoLock);
554     const WinRTTimerInfo timerInfo = d->timerInfos.at(timerId);
555     if (timerInfo.timerId == INVALID_TIMER_ID) {
556 #ifndef QT_NO_DEBUG
557         qWarning("QEventDispatcherWinRT::remainingTime: timer id %d not found", timerId);
558 #endif
559         return -1;
560     }
561 
562     const quint64 currentTime = qt_msectime();
563     if (currentTime < timerInfo.targetTime) {
564         // time to wait
565         return timerInfo.targetTime - currentTime;
566     } else {
567         return 0;
568     }
569 
570     return -1;
571 }
572 
wakeUp()573 void QEventDispatcherWinRT::wakeUp()
574 {
575 }
576 
interrupt()577 void QEventDispatcherWinRT::interrupt()
578 {
579     Q_D(QEventDispatcherWinRT);
580     SetEvent(d->timerIdToHandle.value(INTERRUPT_HANDLE));
581 }
582 
flush()583 void QEventDispatcherWinRT::flush()
584 {
585 }
586 
startingUp()587 void QEventDispatcherWinRT::startingUp()
588 {
589 }
590 
closingDown()591 void QEventDispatcherWinRT::closingDown()
592 {
593 }
594 
event(QEvent * e)595 bool QEventDispatcherWinRT::event(QEvent *e)
596 {
597     Q_D(QEventDispatcherWinRT);
598     switch (e->type()) {
599     case QEvent::Timer: {
600         QTimerEvent *timerEvent = static_cast<QTimerEvent *>(e);
601         const int id = timerEvent->timerId();
602 
603         QMutexLocker locker(&d->timerInfoLock);
604 
605         Q_ASSERT(id < d->timerInfos.size());
606         WinRTTimerInfo &info = d->timerInfos[id];
607         Q_ASSERT(info.timerId != INVALID_TIMER_ID);
608 
609         if (info.inEvent) // but don't allow event to recurse
610             break;
611         info.inEvent = true;
612 
613         QObject *timerObj = d->timerIdToObject.value(id);
614         locker.unlock();
615 
616         QTimerEvent te(id);
617         QCoreApplication::sendEvent(timerObj, &te);
618 
619         locker.relock();
620 
621         // The timer might have been removed in the meanwhile. If the timer was
622         // the last one in the list, id is bigger than the list's size.
623         // Otherwise, the id will just be set to INVALID_TIMER_ID.
624         if (id >= d->timerInfos.size() || info.timerId == INVALID_TIMER_ID)
625             break;
626 
627         if (info.interval == 0 && info.inEvent) {
628             // post the next zero timer event as long as the timer was not restarted
629             QCoreApplication::postEvent(this, new QTimerEvent(id));
630         }
631         info.inEvent = false;
632     }
633     default:
634         break;
635     }
636     return QAbstractEventDispatcher::event(e);
637 }
638 
QEventDispatcherWinRTPrivate()639 QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate()
640 {
641     const bool isGuiThread = QCoreApplication::instance() &&
642             QThread::currentThread() == QCoreApplication::instance()->thread();
643     CoInitializeEx(NULL, isGuiThread ? COINIT_APARTMENTTHREADED : COINIT_MULTITHREADED);
644     HANDLE interruptHandle = CreateEventEx(NULL, NULL, NULL, SYNCHRONIZE|EVENT_MODIFY_STATE);
645     timerIdToHandle.insert(INTERRUPT_HANDLE, interruptHandle);
646     timerHandleToId.insert(interruptHandle, INTERRUPT_HANDLE);
647     timerInfos.reserve(256);
648 }
649 
~QEventDispatcherWinRTPrivate()650 QEventDispatcherWinRTPrivate::~QEventDispatcherWinRTPrivate()
651 {
652     CloseHandle(timerIdToHandle.value(INTERRUPT_HANDLE));
653     CoUninitialize();
654 }
655 
656 QT_END_NAMESPACE
657