From 29c1567ee4724b02995060035df8d93f7d6d9c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 8 Jul 2013 15:16:32 +0200 Subject: Rename QIOSEventDispatcher to QEventDispatcherCoreFoundation Now that it lives in QPlatformSupport, will be fleshed out more, and might be used on OSX at some point in time. Still iOS specific, as none of the iOS API usages have been ifdef'ed. Change-Id: Ib7fde6403ef2dfef175a6f306a85d58027569a30 Reviewed-by: Ian Dean Reviewed-by: Richard Moe Gustavsen --- .../eventdispatchers/eventdispatchers.pri | 4 +- .../eventdispatchers/qeventdispatcher_cf.mm | 356 +++++++++++++++++++++ .../eventdispatchers/qeventdispatcher_cf_p.h | 133 ++++++++ .../eventdispatchers/qioseventdispatcher.mm | 355 -------------------- .../eventdispatchers/qioseventdispatcher_p.h | 133 -------- 5 files changed, 491 insertions(+), 490 deletions(-) create mode 100644 src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm create mode 100644 src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h delete mode 100644 src/platformsupport/eventdispatchers/qioseventdispatcher.mm delete mode 100644 src/platformsupport/eventdispatchers/qioseventdispatcher_p.h (limited to 'src/platformsupport') diff --git a/src/platformsupport/eventdispatchers/eventdispatchers.pri b/src/platformsupport/eventdispatchers/eventdispatchers.pri index ec556486b1..c9bbe1f5b7 100644 --- a/src/platformsupport/eventdispatchers/eventdispatchers.pri +++ b/src/platformsupport/eventdispatchers/eventdispatchers.pri @@ -10,10 +10,10 @@ HEADERS +=\ ios { OBJECTIVE_SOURCES +=\ - $$PWD/qioseventdispatcher.mm + $$PWD/qeventdispatcher_cf.mm HEADERS +=\ - $$PWD/qioseventdispatcher_p.h + $$PWD/qeventdispatcher_cf_p.h } contains(QT_CONFIG, glib) { diff --git a/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm b/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm new file mode 100644 index 0000000000..55d40937b7 --- /dev/null +++ b/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm @@ -0,0 +1,356 @@ +/**************************************************************************** +** +** 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 "qeventdispatcher_cf_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 QEventDispatcherCoreFoundation::postedEventsRunLoopCallback(void *info) +{ + QEventDispatcherCoreFoundation *self = static_cast(info); + self->processPostedEvents(); +} + +void QEventDispatcherCoreFoundation::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: + QEventDispatcherCoreFoundation *self = static_cast(info); + CFRunLoopSourceSignal(self->m_blockingTimerRunLoopSource); +} + +void QEventDispatcherCoreFoundation::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. + + QEventDispatcherCoreFoundation *self = static_cast(info); + self->m_timerInfoList.activateTimers(); + self->maybeStartCFRunLoopTimer(); +} + +void QEventDispatcherCoreFoundation::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, QEventDispatcherCoreFoundation::nonBlockingTimerRunLoopCallback, &info); + Q_ASSERT(m_runLoopTimerRef != 0); + + CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); + CFRunLoopAddTimer(mainRunLoop, m_runLoopTimerRef, kCFRunLoopCommonModes); + CFRunLoopAddTimer(mainRunLoop, m_runLoopTimerRef, (CFStringRef) UITrackingRunLoopMode); + } 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 QEventDispatcherCoreFoundation::maybeStopCFRunLoopTimer() +{ + if (m_runLoopTimerRef == 0) + return; + + CFRunLoopTimerInvalidate(m_runLoopTimerRef); + CFRelease(m_runLoopTimerRef); + m_runLoopTimerRef = 0; +} + +void QEventDispatcherCoreFoundation::processPostedEvents() +{ + QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents); +} + +QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(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 = QEventDispatcherCoreFoundation::blockingTimerRunLoopCallback; + m_blockingTimerRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); + Q_ASSERT(m_blockingTimerRunLoopSource); + CFRunLoopAddSource(mainRunLoop, m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); + CFRunLoopAddSource(mainRunLoop, m_blockingTimerRunLoopSource, (CFStringRef) UITrackingRunLoopMode); + + // source used to handle posted events: + context.perform = QEventDispatcherCoreFoundation::postedEventsRunLoopCallback; + m_postedEventsRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); + Q_ASSERT(m_postedEventsRunLoopSource); + CFRunLoopAddSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); +} + +QEventDispatcherCoreFoundation::~QEventDispatcherCoreFoundation() +{ + CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); + CFRunLoopRemoveSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); + CFRelease(m_postedEventsRunLoopSource); + + qDeleteAll(m_timerInfoList); + maybeStopCFRunLoopTimer(); + + CFRunLoopRemoveSource(mainRunLoop, m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); + CFRunLoopRemoveSource(mainRunLoop, m_blockingTimerRunLoopSource, (CFStringRef) UITrackingRunLoopMode); + CFRelease(m_blockingTimerRunLoopSource); + + m_cfSocketNotifier.removeSocketNotifiers(); +} + +bool QEventDispatcherCoreFoundation::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; + + CFTimeInterval distantFuture = CFTimeInterval(3600. * 24. * 365. * 10.); + SInt32 result; + + if (useExecMode) { + while (!m_interrupted) { + // Run a single pass on the runloop to unblock it + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); + + // Run the default runloop until interrupted (by Qt or UIKit) + if (result != kCFRunLoopRunFinished) + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, distantFuture, false); + + // App has quit or Qt has interrupted? + if (result == kCFRunLoopRunFinished || m_interrupted) + break; + + // Runloop was interrupted by UIKit? + if (result == kCFRunLoopRunStopped && !m_interrupted) { + // Run runloop in UI tracking mode + if (CFRunLoopRunInMode((CFStringRef) UITrackingRunLoopMode, + distantFuture, false) == kCFRunLoopRunFinished) + break; + } + } + eventsProcessed = true; + } else { + if (!(flags & QEventLoop::WaitForMoreEvents)) + wakeUp(); + + // Run runloop in default mode + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, distantFuture, true); + if (result != kCFRunLoopRunFinished) { + // Run runloop in UI tracking mode + CFRunLoopRunInMode((CFStringRef) UITrackingRunLoopMode, distantFuture, false); + } + eventsProcessed = (result == kCFRunLoopRunHandledSource); + } + return eventsProcessed; +} + +bool QEventDispatcherCoreFoundation::hasPendingEvents() +{ + qDebug() << __FUNCTION__ << "not implemented"; + return false; +} + +void QEventDispatcherCoreFoundation::registerSocketNotifier(QSocketNotifier *notifier) +{ + m_cfSocketNotifier.registerSocketNotifier(notifier); +} + +void QEventDispatcherCoreFoundation::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + m_cfSocketNotifier.unregisterSocketNotifier(notifier); +} + +void QEventDispatcherCoreFoundation::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *obj) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1 || interval < 0 || !obj) { + qWarning("QEventDispatcherCoreFoundation::registerTimer: invalid arguments"); + return; + } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QEventDispatcherCoreFoundation: timers cannot be started from another thread"); + return; + } +#endif + + m_timerInfoList.registerTimer(timerId, interval, timerType, obj); + maybeStartCFRunLoopTimer(); +} + +bool QEventDispatcherCoreFoundation::unregisterTimer(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QEventDispatcherCoreFoundation::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 QEventDispatcherCoreFoundation::unregisterTimers(QObject *object) +{ +#ifndef QT_NO_DEBUG + if (!object) { + qWarning("QEventDispatcherCoreFoundation::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 QEventDispatcherCoreFoundation::registeredTimers(QObject *object) const +{ +#ifndef QT_NO_DEBUG + if (!object) { + qWarning("QEventDispatcherCoreFoundation:registeredTimers: invalid argument"); + return QList(); + } +#endif + + return m_timerInfoList.registeredTimers(object); +} + +int QEventDispatcherCoreFoundation::remainingTime(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QEventDispatcherCoreFoundation::remainingTime: invalid argument"); + return -1; + } +#endif + + return m_timerInfoList.timerRemainingTime(timerId); +} + +void QEventDispatcherCoreFoundation::wakeUp() +{ + CFRunLoopSourceSignal(m_postedEventsRunLoopSource); + CFRunLoopWakeUp(CFRunLoopGetMain()); +} + +void QEventDispatcherCoreFoundation::interrupt() +{ + // Stop the runloop, which will cause processEvents() to exit + m_interrupted = true; + CFRunLoopStop(CFRunLoopGetMain()); +} + +void QEventDispatcherCoreFoundation::flush() +{ + // X11 only. +} + +QT_END_NAMESPACE + diff --git a/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h b/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h new file mode 100644 index 0000000000..6e5ce658a8 --- /dev/null +++ b/src/platformsupport/eventdispatchers/qeventdispatcher_cf_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_CF_P_H +#define QEVENTDISPATCHER_CF_P_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QEventDispatcherCoreFoundation : public QAbstractEventDispatcher +{ + Q_OBJECT + +public: + explicit QEventDispatcherCoreFoundation(QObject *parent = 0); + ~QEventDispatcherCoreFoundation(); + + 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_CF_P_H diff --git a/src/platformsupport/eventdispatchers/qioseventdispatcher.mm b/src/platformsupport/eventdispatchers/qioseventdispatcher.mm deleted file mode 100644 index ab3036143d..0000000000 --- a/src/platformsupport/eventdispatchers/qioseventdispatcher.mm +++ /dev/null @@ -1,355 +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_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); - - CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); - CFRunLoopAddTimer(mainRunLoop, m_runLoopTimerRef, kCFRunLoopCommonModes); - CFRunLoopAddTimer(mainRunLoop, m_runLoopTimerRef, (CFStringRef) UITrackingRunLoopMode); - } 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); - CFRunLoopAddSource(mainRunLoop, m_blockingTimerRunLoopSource, (CFStringRef) UITrackingRunLoopMode); - - // 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(mainRunLoop, m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); - CFRunLoopRemoveSource(mainRunLoop, m_blockingTimerRunLoopSource, (CFStringRef) UITrackingRunLoopMode); - 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; - - CFTimeInterval distantFuture = CFTimeInterval(3600. * 24. * 365. * 10.); - SInt32 result; - - if (useExecMode) { - while (!m_interrupted) { - // Run a single pass on the runloop to unblock it - result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); - - // Run the default runloop until interrupted (by Qt or UIKit) - if (result != kCFRunLoopRunFinished) - result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, distantFuture, false); - - // App has quit or Qt has interrupted? - if (result == kCFRunLoopRunFinished || m_interrupted) - break; - - // Runloop was interrupted by UIKit? - if (result == kCFRunLoopRunStopped && !m_interrupted) { - // Run runloop in UI tracking mode - if (CFRunLoopRunInMode((CFStringRef) UITrackingRunLoopMode, - distantFuture, false) == kCFRunLoopRunFinished) - break; - } - } - eventsProcessed = true; - } else { - if (!(flags & QEventLoop::WaitForMoreEvents)) - wakeUp(); - - // Run runloop in default mode - result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, distantFuture, true); - if (result != kCFRunLoopRunFinished) { - // Run runloop in UI tracking mode - CFRunLoopRunInMode((CFStringRef) UITrackingRunLoopMode, distantFuture, false); - } - eventsProcessed = (result == kCFRunLoopRunHandledSource); - } - 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() -{ - // Stop the runloop, which will cause processEvents() to exit - m_interrupted = true; - CFRunLoopStop(CFRunLoopGetMain()); -} - -void QIOSEventDispatcher::flush() -{ - // X11 only. -} - -QT_END_NAMESPACE - diff --git a/src/platformsupport/eventdispatchers/qioseventdispatcher_p.h b/src/platformsupport/eventdispatchers/qioseventdispatcher_p.h deleted file mode 100644 index 53a75618ce..0000000000 --- a/src/platformsupport/eventdispatchers/qioseventdispatcher_p.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 -- cgit v1.2.3