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