From 81dea57593b5b28990bb7f012aae1387c8d2de33 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 20 Sep 2013 14:06:33 +0200 Subject: add WinRT event dispatcher Change-Id: I40b3f896b89b99e271e1a5ca625a5193f4a7f59e Done-with: Kamil Trzcinski Reviewed-by: Lars Knoll --- src/corelib/kernel/kernel.pri | 11 +- src/corelib/kernel/qcoreapplication.cpp | 6 + src/corelib/kernel/qeventdispatcher_winrt.cpp | 394 ++++++++++++++++++++++++++ src/corelib/kernel/qeventdispatcher_winrt_p.h | 168 +++++++++++ src/corelib/kernel/qwineventnotifier.cpp | 4 + 5 files changed, 580 insertions(+), 3 deletions(-) create mode 100644 src/corelib/kernel/qeventdispatcher_winrt.cpp create mode 100644 src/corelib/kernel/qeventdispatcher_winrt_p.h (limited to 'src/corelib/kernel') diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 26ad9f488c..9118c7cbc6 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -68,16 +68,21 @@ SOURCES += \ win32 { SOURCES += \ - kernel/qeventdispatcher_win.cpp \ kernel/qcoreapplication_win.cpp \ kernel/qwineventnotifier.cpp \ kernel/qsharedmemory_win.cpp \ kernel/qsystemsemaphore_win.cpp HEADERS += \ - kernel/qeventdispatcher_win_p.h \ kernel/qwineventnotifier.h -} + winrt { + SOURCES += kernel/qeventdispatcher_winrt.cpp + HEADERS += kernel/qeventdispatcher_winrt_p.h + } else { + SOURCES += kernel/qeventdispatcher_win.cpp + HEADERS += kernel/qeventdispatcher_win_p.h + } +} wince*: { SOURCES += \ diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index f3caa1d918..1799cf6a24 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -84,7 +84,11 @@ # endif #endif #ifdef Q_OS_WIN +# ifdef Q_OS_WINRT +# include "qeventdispatcher_winrt_p.h" +# else # include "qeventdispatcher_win_p.h" +# endif #endif #endif // QT_NO_QOBJECT @@ -466,6 +470,8 @@ void QCoreApplicationPrivate::createEventDispatcher() # endif eventDispatcher = new QEventDispatcherUNIX(q); # endif +#elif defined(Q_OS_WINRT) + eventDispatcher = new QEventDispatcherWinRT(q); #elif defined(Q_OS_WIN) eventDispatcher = new QEventDispatcherWin32(q); #else diff --git a/src/corelib/kernel/qeventdispatcher_winrt.cpp b/src/corelib/kernel/qeventdispatcher_winrt.cpp new file mode 100644 index 0000000000..c94c69b90f --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_winrt.cpp @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventdispatcher_winrt_p.h" + +#include "qelapsedtimer.h" +#include "qcoreapplication.h" +#include "qthread.h" + +#include +#include + +#include +#include +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::System::Threading; +using namespace ABI::Windows::Foundation; + +QT_BEGIN_NAMESPACE + +QEventDispatcherWinRT::QEventDispatcherWinRT(QObject *parent) + : QAbstractEventDispatcher(*new QEventDispatcherWinRTPrivate, parent) +{ +} + +QEventDispatcherWinRT::QEventDispatcherWinRT(QEventDispatcherWinRTPrivate &dd, QObject *parent) + : QAbstractEventDispatcher(dd, parent) +{ } + +QEventDispatcherWinRT::~QEventDispatcherWinRT() +{ +} + +bool QEventDispatcherWinRT::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_UNUSED(flags); + + // we are awake, broadcast it + emit awake(); + QCoreApplicationPrivate::sendPostedEvents(0, 0, QThreadData::current()); + + return false; +} + +bool QEventDispatcherWinRT::hasPendingEvents() +{ + return qGlobalPostedEventsCount(); +} + +void QEventDispatcherWinRT::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_UNUSED(notifier); + Q_UNIMPLEMENTED(); +} +void QEventDispatcherWinRT::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_UNUSED(notifier); + Q_UNIMPLEMENTED(); +} + +void QEventDispatcherWinRT::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) +{ + Q_UNUSED(timerType); + + if (timerId < 1 || interval < 0 || !object) { + qWarning("QEventDispatcherWinRT::registerTimer: invalid arguments"); + return; + } else if (object->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::startTimer: timers cannot be started from another thread"); + return; + } + + Q_D(QEventDispatcherWinRT); + + WinRTTimerInfo *t = new WinRTTimerInfo(); + t->dispatcher = this; + t->timerId = timerId; + t->interval = interval; + t->timeout = interval; + t->timerType = timerType; + t->obj = object; + t->inTimerEvent = false; + + d->registerTimer(t); + d->timerVec.append(t); // store in timer vector + d->timerDict.insert(t->timerId, t); // store timers in dict +} + +bool QEventDispatcherWinRT::unregisterTimer(int timerId) +{ + if (timerId < 1) { + qWarning("QEventDispatcherWinRT::unregisterTimer: invalid argument"); + return false; + } + if (thread() != QThread::currentThread()) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } + + Q_D(QEventDispatcherWinRT); + if (d->timerVec.isEmpty() || timerId <= 0) + return false; + + WinRTTimerInfo *t = d->timerDict.value(timerId); + if (!t) + return false; + + if (t->timer) + d->threadPoolTimerDict.remove(t->timer); + d->timerDict.remove(t->timerId); + d->timerVec.removeAll(t); + d->unregisterTimer(t); + return true; +} + +bool QEventDispatcherWinRT::unregisterTimers(QObject *object) +{ + if (!object) { + qWarning("QEventDispatcherWinRT::unregisterTimers: invalid argument"); + return false; + } + QThread *currentThread = QThread::currentThread(); + if (object->thread() != thread() || thread() != currentThread) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } + + Q_D(QEventDispatcherWinRT); + if (d->timerVec.isEmpty()) + return false; + register WinRTTimerInfo *t; + for (int i = 0; i < d->timerVec.size(); i++) { + t = d->timerVec.at(i); + if (t && t->obj == object) { // object found + if (t->timer) + d->threadPoolTimerDict.remove(t->timer); + d->timerDict.remove(t->timerId); + d->timerVec.removeAt(i); + d->unregisterTimer(t); + --i; + } + } + return true; +} + +QList QEventDispatcherWinRT::registeredTimers(QObject *object) const +{ + if (!object) { + qWarning("QEventDispatcherWinRT:registeredTimers: invalid argument"); + return QList(); + } + + Q_D(const QEventDispatcherWinRT); + QList list; + for (int i = 0; i < d->timerVec.size(); ++i) { + const WinRTTimerInfo *t = d->timerVec.at(i); + if (t && t->obj == object) + list << TimerInfo(t->timerId, t->interval, t->timerType); + } + return list; +} + +bool QEventDispatcherWinRT::registerEventNotifier(QWinEventNotifier *notifier) +{ + Q_UNUSED(notifier); + Q_UNIMPLEMENTED(); + return false; +} + +void QEventDispatcherWinRT::unregisterEventNotifier(QWinEventNotifier *notifier) +{ + Q_UNUSED(notifier); + Q_UNIMPLEMENTED(); +} + +int QEventDispatcherWinRT::remainingTime(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QEventDispatcherWinRT::remainingTime: invalid argument"); + return -1; + } +#endif + + Q_D(QEventDispatcherWinRT); + + if (d->timerVec.isEmpty()) + return -1; + + quint64 currentTime = qt_msectime(); + + register WinRTTimerInfo *t; + for (int i = 0; i < d->timerVec.size(); i++) { + t = d->timerVec.at(i); + if (t && t->timerId == timerId) { // timer found + if (currentTime < t->timeout) { + // time to wait + return t->timeout - currentTime; + } else { + return 0; + } + } + } + +#ifndef QT_NO_DEBUG + qWarning("QEventDispatcherWinRT::remainingTime: timer id %d not found", timerId); +#endif + + return -1; +} + +void QEventDispatcherWinRT::wakeUp() +{ + Q_D(QEventDispatcherWinRT); + if (d->wakeUps.testAndSetAcquire(0, 1)) { + // ###TODO: is there any thing to wake up? + } +} + +void QEventDispatcherWinRT::interrupt() +{ + Q_D(QEventDispatcherWinRT); + d->interrupt = true; + wakeUp(); +} + +void QEventDispatcherWinRT::flush() +{ +} + +void QEventDispatcherWinRT::startingUp() +{ +} + +void QEventDispatcherWinRT::closingDown() +{ + Q_D(QEventDispatcherWinRT); + + // clean up any timers + for (int i = 0; i < d->timerVec.count(); ++i) + d->unregisterTimer(d->timerVec.at(i)); + d->timerVec.clear(); + d->timerDict.clear(); + d->threadPoolTimerDict.clear(); +} + +bool QEventDispatcherWinRT::event(QEvent *e) +{ + Q_D(QEventDispatcherWinRT); + if (e->type() == QEvent::ZeroTimerEvent) { + QZeroTimerEvent *zte = static_cast(e); + WinRTTimerInfo *t = d->timerDict.value(zte->timerId()); + if (t) { + t->inTimerEvent = true; + + QTimerEvent te(zte->timerId()); + QCoreApplication::sendEvent(t->obj, &te); + + t = d->timerDict.value(zte->timerId()); + if (t) { + if (t->interval == 0 && t->inTimerEvent) { + // post the next zero timer event as long as the timer was not restarted + QCoreApplication::postEvent(this, new QZeroTimerEvent(zte->timerId())); + } + + t->inTimerEvent = false; + } + } + return true; + } else if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast(e); + d->sendTimerEvent(te->timerId()); + } + return QAbstractEventDispatcher::event(e); +} + +QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate() + : interrupt(false) + , timerFactory(0) +{ + CoInitializeEx(NULL, COINIT_MULTITHREADED); + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory); + if (FAILED(hr)) + qWarning("QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate: Could not obtain timer factory: %lx", hr); +} + +QEventDispatcherWinRTPrivate::~QEventDispatcherWinRTPrivate() +{ + if (timerFactory) + timerFactory->Release(); + CoUninitialize(); +} + +void QEventDispatcherWinRTPrivate::registerTimer(WinRTTimerInfo *t) +{ + Q_Q(QEventDispatcherWinRT); + + int ok = 0; + uint interval = t->interval; + if (interval == 0u) { + // optimization for single-shot-zero-timer + QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId)); + ok = 1; + } else { + TimeSpan period; + period.Duration = interval * 10000; // TimeSpan is based on 100-nanosecond units + ok = SUCCEEDED(timerFactory->CreatePeriodicTimer( + Callback(this, &QEventDispatcherWinRTPrivate::timerExpiredCallback).Get(), period, &t->timer)); + if (ok) + threadPoolTimerDict.insert(t->timer, t); + } + t->timeout = qt_msectime() + interval; + if (ok == 0) + qErrnoWarning("QEventDispatcherWinRT::registerTimer: Failed to create a timer"); +} + +void QEventDispatcherWinRTPrivate::unregisterTimer(WinRTTimerInfo *t) +{ + if (t->timer) { + t->timer->Cancel(); + t->timer->Release(); + } + delete t; + t = 0; +} + +void QEventDispatcherWinRTPrivate::sendTimerEvent(int timerId) +{ + WinRTTimerInfo *t = timerDict.value(timerId); + if (t && !t->inTimerEvent) { + // send event, but don't allow it to recurse + t->inTimerEvent = true; + + QTimerEvent e(t->timerId); + QCoreApplication::sendEvent(t->obj, &e); + + // timer could have been removed + t = timerDict.value(timerId); + if (t) { + t->inTimerEvent = false; + } + } +} + +HRESULT QEventDispatcherWinRTPrivate::timerExpiredCallback(IThreadPoolTimer *source) +{ + register WinRTTimerInfo *t = threadPoolTimerDict.value(source); + if (t) + QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId)); + else + qWarning("QEventDispatcherWinRT::timerExpiredCallback: Could not find timer %d in timer list", source); + return S_OK; +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventdispatcher_winrt_p.h b/src/corelib/kernel/qeventdispatcher_winrt_p.h new file mode 100644 index 0000000000..35b3faa771 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_winrt_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QEVENTDISPATCHER_WINRT_P_H +#define QEVENTDISPATCHER_WINRT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qabstracteventdispatcher.h" +#include "private/qabstracteventdispatcher_p.h" + +#include +#include + +namespace ABI { + namespace Windows { + namespace System { + namespace Threading { + struct IThreadPoolTimer; + struct IThreadPoolTimerStatics; + } + } + } +} + +QT_BEGIN_NAMESPACE + +int qt_msectime(); + +class QEventDispatcherWinRTPrivate; + +class Q_CORE_EXPORT QEventDispatcherWinRT : public QAbstractEventDispatcher +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherWinRT) + +public: + explicit QEventDispatcherWinRT(QObject *parent = 0); + ~QEventDispatcherWinRT(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + + void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object); + bool unregisterTimer(int timerId); + bool unregisterTimers(QObject *object); + QList registeredTimers(QObject *object) const; + + int remainingTime(int timerId); + + bool registerEventNotifier(QWinEventNotifier *notifier); + void unregisterEventNotifier(QWinEventNotifier *notifier); + + void wakeUp(); + void interrupt(); + void flush(); + + void startingUp(); + void closingDown(); + +protected: + QEventDispatcherWinRT(QEventDispatcherWinRTPrivate &dd, QObject *parent = 0); + + + bool event(QEvent *); + int activateTimers(); + int activateSocketNotifiers(); +}; + +struct WinRTTimerInfo // internal timer info +{ + WinRTTimerInfo() : timer(0) {} + + QObject *dispatcher; + int timerId; + int interval; + Qt::TimerType timerType; + quint64 timeout; // - when to actually fire + QObject *obj; // - object to receive events + bool inTimerEvent; + ABI::Windows::System::Threading::IThreadPoolTimer *timer; +}; + +class QZeroTimerEvent : public QTimerEvent +{ +public: + explicit inline QZeroTimerEvent(int timerId) + : QTimerEvent(timerId) + { t = QEvent::ZeroTimerEvent; } +}; + +class Q_CORE_EXPORT QEventDispatcherWinRTPrivate : public QAbstractEventDispatcherPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherWinRT) + +public: + QEventDispatcherWinRTPrivate(); + ~QEventDispatcherWinRTPrivate(); + + QList timerVec; + QHash timerDict; + QHash threadPoolTimerDict; + + void registerTimer(WinRTTimerInfo *t); + void unregisterTimer(WinRTTimerInfo *t); + void sendTimerEvent(int timerId); + HRESULT timerExpiredCallback(ABI::Windows::System::Threading::IThreadPoolTimer *source); + + QAtomicInt wakeUps; + bool interrupt; + + ABI::Windows::System::Threading::IThreadPoolTimerStatics *timerFactory; +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_WINRT_P_H diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp index 242702b304..5eb34e635e 100644 --- a/src/corelib/kernel/qwineventnotifier.cpp +++ b/src/corelib/kernel/qwineventnotifier.cpp @@ -41,7 +41,11 @@ #include "qwineventnotifier.h" +#ifdef Q_OS_WINRT +#include "qeventdispatcher_winrt_p.h" +#else #include "qeventdispatcher_win_p.h" +#endif #include "qcoreapplication.h" #include -- cgit v1.2.3