From c172ae81c93ed8de8e8c080b849a6c7ba66a71d2 Mon Sep 17 00:00:00 2001 From: Ian Dean Date: Wed, 17 Apr 2013 11:50:06 +0200 Subject: Move iOS event dispatcher from platform plugin to platform support. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move iOS event dispatcher from platform plugin to platform support, so that it can be used by multiple iOS platform plugins. Change-Id: I9041b2de5e00e5fe8f30af2dfd922b4f5c594802 Reviewed-by: Tor Arne Vestbø --- .../eventdispatchers/eventdispatchers.pri | 8 + .../eventdispatchers/qioseventdispatcher.mm | 321 ++++++++++++++++++++ .../eventdispatchers/qioseventdispatcher_p.h | 133 +++++++++ src/plugins/platforms/ios/ios.pro | 2 - src/plugins/platforms/ios/qioseventdispatcher.h | 133 --------- src/plugins/platforms/ios/qioseventdispatcher.mm | 322 --------------------- src/plugins/platforms/ios/qiosintegration.mm | 2 +- 7 files changed, 463 insertions(+), 458 deletions(-) create mode 100644 src/platformsupport/eventdispatchers/qioseventdispatcher.mm create mode 100644 src/platformsupport/eventdispatchers/qioseventdispatcher_p.h delete mode 100644 src/plugins/platforms/ios/qioseventdispatcher.h delete mode 100644 src/plugins/platforms/ios/qioseventdispatcher.mm diff --git a/src/platformsupport/eventdispatchers/eventdispatchers.pri b/src/platformsupport/eventdispatchers/eventdispatchers.pri index 6e16a46b34..ec556486b1 100644 --- a/src/platformsupport/eventdispatchers/eventdispatchers.pri +++ b/src/platformsupport/eventdispatchers/eventdispatchers.pri @@ -8,6 +8,14 @@ HEADERS +=\ $$PWD/qgenericunixeventdispatcher_p.h\ } +ios { +OBJECTIVE_SOURCES +=\ + $$PWD/qioseventdispatcher.mm + +HEADERS +=\ + $$PWD/qioseventdispatcher_p.h +} + contains(QT_CONFIG, glib) { SOURCES +=$$PWD/qeventdispatcher_glib.cpp HEADERS +=$$PWD/qeventdispatcher_glib_p.h diff --git a/src/platformsupport/eventdispatchers/qioseventdispatcher.mm b/src/platformsupport/eventdispatchers/qioseventdispatcher.mm new file mode 100644 index 0000000000..f8f8cc15ad --- /dev/null +++ b/src/platformsupport/eventdispatchers/qioseventdispatcher.mm @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins 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 "qioseventdispatcher_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +QT_USE_NAMESPACE + +static Boolean runLoopSourceEqualCallback(const void *info1, const void *info2) +{ + return info1 == info2; +} + +void QIOSEventDispatcher::postedEventsRunLoopCallback(void *info) +{ + QIOSEventDispatcher *self = static_cast(info); + self->processPostedEvents(); +} + +void QIOSEventDispatcher::nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info) +{ + // The (one and only) CFRunLoopTimer has fired, which means that at least + // one QTimer should now fire as well. Note that CFRunLoopTimer's callback will + // never recurse. So if the app starts a new QEventLoop within this callback, other + // timers will stop working. The work-around is to forward the callback to a + // dedicated CFRunLoopSource that can recurse: + QIOSEventDispatcher *self = static_cast(info); + CFRunLoopSourceSignal(self->m_blockingTimerRunLoopSource); +} + +void QIOSEventDispatcher::blockingTimerRunLoopCallback(void *info) +{ + // TODO: + // We also need to block this new timer source + // along with the posted event source when calling processEvents() + // "manually" to prevent livelock deep in CFRunLoop. + + QIOSEventDispatcher *self = static_cast(info); + self->m_timerInfoList.activateTimers(); + self->maybeStartCFRunLoopTimer(); +} + +void QIOSEventDispatcher::maybeStartCFRunLoopTimer() +{ + // Find out when the next registered timer should fire, and schedule + // runLoopTimer accordingly. If the runLoopTimer does not yet exist, and + // at least one timer is registered, start by creating the timer: + if (m_timerInfoList.isEmpty()) { + Q_ASSERT(m_runLoopTimerRef == 0); + return; + } + + CFAbsoluteTime ttf = CFAbsoluteTimeGetCurrent(); + CFTimeInterval interval; + + if (m_runLoopTimerRef == 0) { + // start the CFRunLoopTimer + CFTimeInterval oneyear = CFTimeInterval(3600. * 24. * 365.); + + // calculate when the next timer should fire: + struct timespec tv; + if (m_timerInfoList.timerWait(tv)) { + interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); + } else { + // this shouldn't really happen, but in case it does, set the timer + // to fire a some point in the distant future: + interval = oneyear; + } + + ttf += interval; + CFRunLoopTimerContext info = { 0, this, 0, 0, 0 }; + // create the timer with a large interval, as recommended by the CFRunLoopTimerSetNextFireDate() + // documentation, since we will adjust the timer's time-to-fire as needed to keep Qt timers working + m_runLoopTimerRef = CFRunLoopTimerCreate(0, ttf, oneyear, 0, 0, QIOSEventDispatcher::nonBlockingTimerRunLoopCallback, &info); + Q_ASSERT(m_runLoopTimerRef != 0); + + CFRunLoopAddTimer(CFRunLoopGetMain(), m_runLoopTimerRef, kCFRunLoopCommonModes); + } else { + struct timespec tv; + // Calculate when the next timer should fire: + if (m_timerInfoList.timerWait(tv)) { + interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); + } else { + // no timers can fire, but we cannot stop the CFRunLoopTimer, set the timer to fire at some + // point in the distant future (the timer interval is one year) + interval = CFRunLoopTimerGetInterval(m_runLoopTimerRef); + } + + ttf += interval; + CFRunLoopTimerSetNextFireDate(m_runLoopTimerRef, ttf); + } +} + +void QIOSEventDispatcher::maybeStopCFRunLoopTimer() +{ + if (m_runLoopTimerRef == 0) + return; + + CFRunLoopTimerInvalidate(m_runLoopTimerRef); + CFRelease(m_runLoopTimerRef); + m_runLoopTimerRef = 0; +} + +void QIOSEventDispatcher::processPostedEvents() +{ + QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents); +} + +QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent) + : QAbstractEventDispatcher(parent) + , m_interrupted(false) + , m_runLoopTimerRef(0) +{ + m_cfSocketNotifier.setHostEventDispatcher(this); + + CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); + CFRunLoopSourceContext context; + bzero(&context, sizeof(CFRunLoopSourceContext)); + context.equal = runLoopSourceEqualCallback; + context.info = this; + + // source used to handle timers: + context.perform = QIOSEventDispatcher::blockingTimerRunLoopCallback; + m_blockingTimerRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); + Q_ASSERT(m_blockingTimerRunLoopSource); + CFRunLoopAddSource(mainRunLoop, m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); + + // source used to handle posted events: + context.perform = QIOSEventDispatcher::postedEventsRunLoopCallback; + m_postedEventsRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); + Q_ASSERT(m_postedEventsRunLoopSource); + CFRunLoopAddSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); +} + +QIOSEventDispatcher::~QIOSEventDispatcher() +{ + CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); + CFRunLoopRemoveSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); + CFRelease(m_postedEventsRunLoopSource); + + qDeleteAll(m_timerInfoList); + maybeStopCFRunLoopTimer(); + CFRunLoopRemoveSource(CFRunLoopGetMain(), m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); + CFRelease(m_blockingTimerRunLoopSource); + + m_cfSocketNotifier.removeSocketNotifiers(); +} + +bool QIOSEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + m_interrupted = false; + bool eventsProcessed = false; + + bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; + bool execFlagSet = (flags & QEventLoop::DialogExec) || (flags & QEventLoop::EventLoopExec); + bool useExecMode = execFlagSet && !excludeUserEvents; + + if (useExecMode) { + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + while ([runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]] && !m_interrupted); + eventsProcessed = true; + } else { + if (!(flags & QEventLoop::WaitForMoreEvents)) + wakeUp(); + eventsProcessed = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + return eventsProcessed; +} + +bool QIOSEventDispatcher::hasPendingEvents() +{ + qDebug() << __FUNCTION__ << "not implemented"; + return false; +} + +void QIOSEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier) +{ + m_cfSocketNotifier.registerSocketNotifier(notifier); +} + +void QIOSEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + m_cfSocketNotifier.unregisterSocketNotifier(notifier); +} + +void QIOSEventDispatcher::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *obj) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1 || interval < 0 || !obj) { + qWarning("QIOSEventDispatcher::registerTimer: invalid arguments"); + return; + } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QIOSEventDispatcher: timers cannot be started from another thread"); + return; + } +#endif + + m_timerInfoList.registerTimer(timerId, interval, timerType, obj); + maybeStartCFRunLoopTimer(); +} + +bool QIOSEventDispatcher::unregisterTimer(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QIOSEventDispatcher::unregisterTimer: invalid argument"); + return false; + } else if (thread() != QThread::currentThread()) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } +#endif + + bool returnValue = m_timerInfoList.unregisterTimer(timerId); + m_timerInfoList.isEmpty() ? maybeStopCFRunLoopTimer() : maybeStartCFRunLoopTimer(); + return returnValue; +} + +bool QIOSEventDispatcher::unregisterTimers(QObject *object) +{ +#ifndef QT_NO_DEBUG + if (!object) { + qWarning("QIOSEventDispatcher::unregisterTimers: invalid argument"); + return false; + } else if (object->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } +#endif + + bool returnValue = m_timerInfoList.unregisterTimers(object); + m_timerInfoList.isEmpty() ? maybeStopCFRunLoopTimer() : maybeStartCFRunLoopTimer(); + return returnValue; +} + +QList QIOSEventDispatcher::registeredTimers(QObject *object) const +{ +#ifndef QT_NO_DEBUG + if (!object) { + qWarning("QIOSEventDispatcher:registeredTimers: invalid argument"); + return QList(); + } +#endif + + return m_timerInfoList.registeredTimers(object); +} + +int QIOSEventDispatcher::remainingTime(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QIOSEventDispatcher::remainingTime: invalid argument"); + return -1; + } +#endif + + return m_timerInfoList.timerRemainingTime(timerId); +} + +void QIOSEventDispatcher::wakeUp() +{ + CFRunLoopSourceSignal(m_postedEventsRunLoopSource); + CFRunLoopWakeUp(CFRunLoopGetMain()); +} + +void QIOSEventDispatcher::interrupt() +{ + wakeUp(); + m_interrupted = true; +} + +void QIOSEventDispatcher::flush() +{ + // X11 only. +} + +QT_END_NAMESPACE + diff --git a/src/platformsupport/eventdispatchers/qioseventdispatcher_p.h b/src/platformsupport/eventdispatchers/qioseventdispatcher_p.h new file mode 100644 index 0000000000..53a75618ce --- /dev/null +++ b/src/platformsupport/eventdispatchers/qioseventdispatcher_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (c) 2007-2008, Apple, Inc. +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** +** * Redistributions of source code must retain the above copyright notice, +** this list of conditions and the following disclaimer. +** +** * Redistributions in binary form must reproduce the above copyright notice, +** this list of conditions and the following disclaimer in the documentation +** and/or other materials provided with the distribution. +** +** * Neither the name of Apple, Inc. nor the names of its contributors +** may be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_IOS_P_H +#define QEVENTDISPATCHER_IOS_P_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QIOSEventDispatcher : public QAbstractEventDispatcher +{ + Q_OBJECT + +public: + explicit QIOSEventDispatcher(QObject *parent = 0); + ~QIOSEventDispatcher(); + + 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); + + void wakeUp(); + void interrupt(); + void flush(); + +private: + bool m_interrupted; + + CFRunLoopSourceRef m_postedEventsRunLoopSource; + CFRunLoopSourceRef m_blockingTimerRunLoopSource; + + QTimerInfoList m_timerInfoList; + CFRunLoopTimerRef m_runLoopTimerRef; + + QCFSocketNotifier m_cfSocketNotifier; + + void processPostedEvents(); + void maybeStartCFRunLoopTimer(); + void maybeStopCFRunLoopTimer(); + + static void postedEventsRunLoopCallback(void *info); + static void nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info); + static void blockingTimerRunLoopCallback(void *info); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_IOS_P_H diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro index 6ce0ca748e..f485191c2b 100644 --- a/src/plugins/platforms/ios/ios.pro +++ b/src/plugins/platforms/ios/ios.pro @@ -13,7 +13,6 @@ OBJECTIVE_SOURCES = \ qiosintegration.mm \ qioswindow.mm \ qiosscreen.mm \ - qioseventdispatcher.mm \ qiosbackingstore.mm \ qiosapplicationdelegate.mm \ qiosviewcontroller.mm \ @@ -26,7 +25,6 @@ HEADERS = \ qiosintegration.h \ qioswindow.h \ qiosscreen.h \ - qioseventdispatcher.h \ qiosbackingstore.h \ qiosapplicationdelegate.h \ qiosviewcontroller.h \ diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h deleted file mode 100644 index 53a75618ce..0000000000 --- a/src/plugins/platforms/ios/qioseventdispatcher.h +++ /dev/null @@ -1,133 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the plugins 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$ -** -****************************************************************************/ - -/**************************************************************************** -** -** Copyright (c) 2007-2008, Apple, Inc. -** -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** -** * Redistributions of source code must retain the above copyright notice, -** this list of conditions and the following disclaimer. -** -** * Redistributions in binary form must reproduce the above copyright notice, -** this list of conditions and the following disclaimer in the documentation -** and/or other materials provided with the distribution. -** -** * Neither the name of Apple, Inc. nor the names of its contributors -** may be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** -****************************************************************************/ - -#ifndef QEVENTDISPATCHER_IOS_P_H -#define QEVENTDISPATCHER_IOS_P_H - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QIOSEventDispatcher : public QAbstractEventDispatcher -{ - Q_OBJECT - -public: - explicit QIOSEventDispatcher(QObject *parent = 0); - ~QIOSEventDispatcher(); - - 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); - - void wakeUp(); - void interrupt(); - void flush(); - -private: - bool m_interrupted; - - CFRunLoopSourceRef m_postedEventsRunLoopSource; - CFRunLoopSourceRef m_blockingTimerRunLoopSource; - - QTimerInfoList m_timerInfoList; - CFRunLoopTimerRef m_runLoopTimerRef; - - QCFSocketNotifier m_cfSocketNotifier; - - void processPostedEvents(); - void maybeStartCFRunLoopTimer(); - void maybeStopCFRunLoopTimer(); - - static void postedEventsRunLoopCallback(void *info); - static void nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info); - static void blockingTimerRunLoopCallback(void *info); -}; - -QT_END_NAMESPACE - -#endif // QEVENTDISPATCHER_IOS_P_H diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm deleted file mode 100644 index e9bf039047..0000000000 --- a/src/plugins/platforms/ios/qioseventdispatcher.mm +++ /dev/null @@ -1,322 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the plugins 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 "qioseventdispatcher.h" -#import "qiosapplicationdelegate.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -QT_USE_NAMESPACE - -static Boolean runLoopSourceEqualCallback(const void *info1, const void *info2) -{ - return info1 == info2; -} - -void QIOSEventDispatcher::postedEventsRunLoopCallback(void *info) -{ - QIOSEventDispatcher *self = static_cast(info); - self->processPostedEvents(); -} - -void QIOSEventDispatcher::nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info) -{ - // The (one and only) CFRunLoopTimer has fired, which means that at least - // one QTimer should now fire as well. Note that CFRunLoopTimer's callback will - // never recurse. So if the app starts a new QEventLoop within this callback, other - // timers will stop working. The work-around is to forward the callback to a - // dedicated CFRunLoopSource that can recurse: - QIOSEventDispatcher *self = static_cast(info); - CFRunLoopSourceSignal(self->m_blockingTimerRunLoopSource); -} - -void QIOSEventDispatcher::blockingTimerRunLoopCallback(void *info) -{ - // TODO: - // We also need to block this new timer source - // along with the posted event source when calling processEvents() - // "manually" to prevent livelock deep in CFRunLoop. - - QIOSEventDispatcher *self = static_cast(info); - self->m_timerInfoList.activateTimers(); - self->maybeStartCFRunLoopTimer(); -} - -void QIOSEventDispatcher::maybeStartCFRunLoopTimer() -{ - // Find out when the next registered timer should fire, and schedule - // runLoopTimer accordingly. If the runLoopTimer does not yet exist, and - // at least one timer is registered, start by creating the timer: - if (m_timerInfoList.isEmpty()) { - Q_ASSERT(m_runLoopTimerRef == 0); - return; - } - - CFAbsoluteTime ttf = CFAbsoluteTimeGetCurrent(); - CFTimeInterval interval; - - if (m_runLoopTimerRef == 0) { - // start the CFRunLoopTimer - CFTimeInterval oneyear = CFTimeInterval(3600. * 24. * 365.); - - // calculate when the next timer should fire: - struct timespec tv; - if (m_timerInfoList.timerWait(tv)) { - interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); - } else { - // this shouldn't really happen, but in case it does, set the timer - // to fire a some point in the distant future: - interval = oneyear; - } - - ttf += interval; - CFRunLoopTimerContext info = { 0, this, 0, 0, 0 }; - // create the timer with a large interval, as recommended by the CFRunLoopTimerSetNextFireDate() - // documentation, since we will adjust the timer's time-to-fire as needed to keep Qt timers working - m_runLoopTimerRef = CFRunLoopTimerCreate(0, ttf, oneyear, 0, 0, QIOSEventDispatcher::nonBlockingTimerRunLoopCallback, &info); - Q_ASSERT(m_runLoopTimerRef != 0); - - CFRunLoopAddTimer(CFRunLoopGetMain(), m_runLoopTimerRef, kCFRunLoopCommonModes); - } else { - struct timespec tv; - // Calculate when the next timer should fire: - if (m_timerInfoList.timerWait(tv)) { - interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); - } else { - // no timers can fire, but we cannot stop the CFRunLoopTimer, set the timer to fire at some - // point in the distant future (the timer interval is one year) - interval = CFRunLoopTimerGetInterval(m_runLoopTimerRef); - } - - ttf += interval; - CFRunLoopTimerSetNextFireDate(m_runLoopTimerRef, ttf); - } -} - -void QIOSEventDispatcher::maybeStopCFRunLoopTimer() -{ - if (m_runLoopTimerRef == 0) - return; - - CFRunLoopTimerInvalidate(m_runLoopTimerRef); - CFRelease(m_runLoopTimerRef); - m_runLoopTimerRef = 0; -} - -void QIOSEventDispatcher::processPostedEvents() -{ - QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents); -} - -QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent) - : QAbstractEventDispatcher(parent) - , m_interrupted(false) - , m_runLoopTimerRef(0) -{ - m_cfSocketNotifier.setHostEventDispatcher(this); - - CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); - CFRunLoopSourceContext context; - bzero(&context, sizeof(CFRunLoopSourceContext)); - context.equal = runLoopSourceEqualCallback; - context.info = this; - - // source used to handle timers: - context.perform = QIOSEventDispatcher::blockingTimerRunLoopCallback; - m_blockingTimerRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); - Q_ASSERT(m_blockingTimerRunLoopSource); - CFRunLoopAddSource(mainRunLoop, m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); - - // source used to handle posted events: - context.perform = QIOSEventDispatcher::postedEventsRunLoopCallback; - m_postedEventsRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); - Q_ASSERT(m_postedEventsRunLoopSource); - CFRunLoopAddSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); -} - -QIOSEventDispatcher::~QIOSEventDispatcher() -{ - CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); - CFRunLoopRemoveSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); - CFRelease(m_postedEventsRunLoopSource); - - qDeleteAll(m_timerInfoList); - maybeStopCFRunLoopTimer(); - CFRunLoopRemoveSource(CFRunLoopGetMain(), m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); - CFRelease(m_blockingTimerRunLoopSource); - - m_cfSocketNotifier.removeSocketNotifiers(); -} - -bool QIOSEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) -{ - m_interrupted = false; - bool eventsProcessed = false; - - bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; - bool execFlagSet = (flags & QEventLoop::DialogExec) || (flags & QEventLoop::EventLoopExec); - bool useExecMode = execFlagSet && !excludeUserEvents; - - if (useExecMode) { - NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; - while ([runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]] && !m_interrupted); - eventsProcessed = true; - } else { - if (!(flags & QEventLoop::WaitForMoreEvents)) - wakeUp(); - eventsProcessed = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; - } - return eventsProcessed; -} - -bool QIOSEventDispatcher::hasPendingEvents() -{ - qDebug() << __FUNCTION__ << "not implemented"; - return false; -} - -void QIOSEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier) -{ - m_cfSocketNotifier.registerSocketNotifier(notifier); -} - -void QIOSEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier) -{ - m_cfSocketNotifier.unregisterSocketNotifier(notifier); -} - -void QIOSEventDispatcher::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *obj) -{ -#ifndef QT_NO_DEBUG - if (timerId < 1 || interval < 0 || !obj) { - qWarning("QIOSEventDispatcher::registerTimer: invalid arguments"); - return; - } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { - qWarning("QIOSEventDispatcher: timers cannot be started from another thread"); - return; - } -#endif - - m_timerInfoList.registerTimer(timerId, interval, timerType, obj); - maybeStartCFRunLoopTimer(); -} - -bool QIOSEventDispatcher::unregisterTimer(int timerId) -{ -#ifndef QT_NO_DEBUG - if (timerId < 1) { - qWarning("QIOSEventDispatcher::unregisterTimer: invalid argument"); - return false; - } else if (thread() != QThread::currentThread()) { - qWarning("QObject::killTimer: timers cannot be stopped from another thread"); - return false; - } -#endif - - bool returnValue = m_timerInfoList.unregisterTimer(timerId); - m_timerInfoList.isEmpty() ? maybeStopCFRunLoopTimer() : maybeStartCFRunLoopTimer(); - return returnValue; -} - -bool QIOSEventDispatcher::unregisterTimers(QObject *object) -{ -#ifndef QT_NO_DEBUG - if (!object) { - qWarning("QIOSEventDispatcher::unregisterTimers: invalid argument"); - return false; - } else if (object->thread() != thread() || thread() != QThread::currentThread()) { - qWarning("QObject::killTimers: timers cannot be stopped from another thread"); - return false; - } -#endif - - bool returnValue = m_timerInfoList.unregisterTimers(object); - m_timerInfoList.isEmpty() ? maybeStopCFRunLoopTimer() : maybeStartCFRunLoopTimer(); - return returnValue; -} - -QList QIOSEventDispatcher::registeredTimers(QObject *object) const -{ -#ifndef QT_NO_DEBUG - if (!object) { - qWarning("QIOSEventDispatcher:registeredTimers: invalid argument"); - return QList(); - } -#endif - - return m_timerInfoList.registeredTimers(object); -} - -int QIOSEventDispatcher::remainingTime(int timerId) -{ -#ifndef QT_NO_DEBUG - if (timerId < 1) { - qWarning("QIOSEventDispatcher::remainingTime: invalid argument"); - return -1; - } -#endif - - return m_timerInfoList.timerRemainingTime(timerId); -} - -void QIOSEventDispatcher::wakeUp() -{ - CFRunLoopSourceSignal(m_postedEventsRunLoopSource); - CFRunLoopWakeUp(CFRunLoopGetMain()); -} - -void QIOSEventDispatcher::interrupt() -{ - wakeUp(); - m_interrupted = true; -} - -void QIOSEventDispatcher::flush() -{ - // X11 only. -} - -QT_END_NAMESPACE - diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index d04864d724..c6d189abec 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -43,11 +43,11 @@ #include "qioswindow.h" #include "qiosbackingstore.h" #include "qiosscreen.h" -#include "qioseventdispatcher.h" #include "qioscontext.h" #include "qiosinputcontext.h" #include "qiostheme.h" +#include #include #include -- cgit v1.2.3