From c751cef8d6fe3e6bee1ec9a0466094b3255ca037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 30 Jun 2015 13:28:31 +0200 Subject: Move QEventDispatcherCoreFoundation to QtCore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export it for use by the iOS platform plugin. Also move QCFSocketNotifier, and export for use by the Cocoa platform plugin. This is a pure code move with no intended behavior changes, in anticipation of using the Core Foundation event dispatcher as the default Qt Core event dispatcher on OS X. Change-Id: I43677d2f6f3c1d0ed0415c964225aa97d2f13078 Reviewed-by: Tor Arne Vestbø --- src/corelib/kernel/kernel.pri | 13 +- src/corelib/kernel/qcfsocketnotifier.cpp | 303 ++++++++++ src/corelib/kernel/qcfsocketnotifier_p.h | 105 ++++ src/corelib/kernel/qeventdispatcher_cf.mm | 624 +++++++++++++++++++++ src/corelib/kernel/qeventdispatcher_cf_p.h | 280 +++++++++ .../cfsocketnotifier/cfsocketnotifier.pri | 4 - .../cfsocketnotifier/qcfsocketnotifier.cpp | 303 ---------- .../cfsocketnotifier/qcfsocketnotifier_p.h | 105 ---- .../eventdispatchers/eventdispatchers.pri | 8 - .../eventdispatchers/qeventdispatcher_cf.mm | 624 --------------------- .../eventdispatchers/qeventdispatcher_cf_p.h | 280 --------- src/platformsupport/platformsupport.pro | 1 - .../platforms/cocoa/qcocoaeventdispatcher.h | 2 +- src/plugins/platforms/ios/qioseventdispatcher.h | 2 +- 14 files changed, 1325 insertions(+), 1329 deletions(-) create mode 100644 src/corelib/kernel/qcfsocketnotifier.cpp create mode 100644 src/corelib/kernel/qcfsocketnotifier_p.h create mode 100644 src/corelib/kernel/qeventdispatcher_cf.mm create mode 100644 src/corelib/kernel/qeventdispatcher_cf_p.h delete mode 100644 src/platformsupport/cfsocketnotifier/cfsocketnotifier.pri delete mode 100644 src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp delete mode 100644 src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h delete mode 100644 src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm delete mode 100644 src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 65dc44def2..600c28b5d7 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -100,9 +100,11 @@ winrt { mac { HEADERS += \ + kernel/qcfsocketnotifier_p.h \ kernel/qcore_mac_p.h SOURCES += \ + kernel/qcfsocketnotifier.cpp \ kernel/qcoreapplication_mac.cpp \ kernel/qcore_mac.cpp @@ -113,8 +115,15 @@ mac { osx: LIBS_PRIVATE += -framework CoreServices - # We need UIKit for UIDevice - ios: LIBS_PRIVATE += -framework UIKit + ios { + OBJECTIVE_SOURCES += \ + kernel/qeventdispatcher_cf.mm + HEADERS += \ + kernel/qeventdispatcher_cf_p.h + + # We need UIKit for UIDevice + LIBS_PRIVATE += -framework UIKit + } } nacl { diff --git a/src/corelib/kernel/qcfsocketnotifier.cpp b/src/corelib/kernel/qcfsocketnotifier.cpp new file mode 100644 index 0000000000..19f9e744b8 --- /dev/null +++ b/src/corelib/kernel/qcfsocketnotifier.cpp @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcfsocketnotifier_p.h" +#include +#include +#include + + +/************************************************************************** + Socket Notifiers + *************************************************************************/ +void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef, + const void *, void *info) +{ + + QCFSocketNotifier *cfSocketNotifier = static_cast(info); + int nativeSocket = CFSocketGetNative(s); + MacSocketInfo *socketInfo = cfSocketNotifier->macSockets.value(nativeSocket); + QEvent notifierEvent(QEvent::SockAct); + + // There is a race condition that happen where we disable the notifier and + // the kernel still has a notification to pass on. We then get this + // notification after we've successfully disabled the CFSocket, but our Qt + // notifier is now gone. The upshot is we have to check the notifier + // every time. + if (callbackType == kCFSocketReadCallBack) { + if (socketInfo->readNotifier && socketInfo->readEnabled) { + socketInfo->readEnabled = false; + QCoreApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); + } + } else if (callbackType == kCFSocketWriteCallBack) { + if (socketInfo->writeNotifier && socketInfo->writeEnabled) { + socketInfo->writeEnabled = false; + QCoreApplication::sendEvent(socketInfo->writeNotifier, ¬ifierEvent); + } + } + + if (cfSocketNotifier->maybeCancelWaitForMoreEvents) + cfSocketNotifier->maybeCancelWaitForMoreEvents(cfSocketNotifier->eventDispatcher); +} + +/* + Adds a loop source for the given socket to the current run loop. +*/ +CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket) +{ + CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0); + if (!loopSource) + return 0; + + CFRunLoopAddSource(CFRunLoopGetMain(), loopSource, kCFRunLoopCommonModes); + return loopSource; +} + +/* + Removes the loop source for the given socket from the current run loop. +*/ +void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop) +{ + Q_ASSERT(runloop); + CFRunLoopRemoveSource(CFRunLoopGetMain(), runloop, kCFRunLoopCommonModes); + CFSocketDisableCallBacks(socket, kCFSocketReadCallBack); + CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack); +} + +QCFSocketNotifier::QCFSocketNotifier() + : eventDispatcher(0) + , maybeCancelWaitForMoreEvents(0) + , enableNotifiersObserver(0) +{ + +} + +QCFSocketNotifier::~QCFSocketNotifier() +{ + +} + +void QCFSocketNotifier::setHostEventDispatcher(QAbstractEventDispatcher *hostEventDispacher) +{ + eventDispatcher = hostEventDispacher; +} + +void QCFSocketNotifier::setMaybeCancelWaitForMoreEventsCallback(MaybeCancelWaitForMoreEventsFn callBack) +{ + maybeCancelWaitForMoreEvents = callBack; +} + +void QCFSocketNotifier::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int nativeSocket = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != eventDispatcher->thread() + || eventDispatcher->thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); + return; + } +#endif + + if (type == QSocketNotifier::Exception) { + qWarning("QSocketNotifier::Exception is not supported on iOS"); + return; + } + + // Check if we have a CFSocket for the native socket, create one if not. + MacSocketInfo *socketInfo = macSockets.value(nativeSocket); + if (!socketInfo) { + socketInfo = new MacSocketInfo(); + + // Create CFSocket, specify that we want both read and write callbacks (the callbacks + // are enabled/disabled later on). + const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack; + CFSocketContext context = {0, this, 0, 0, 0}; + socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context); + if (CFSocketIsValid(socketInfo->socket) == false) { + qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket"); + return; + } + + CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket); + // QSocketNotifier doesn't close the socket upon destruction/invalidation + flags &= ~kCFSocketCloseOnInvalidate; + // Expicitly disable automatic re-enable, as we do that manually on each runloop pass + flags &= ~(kCFSocketAutomaticallyReenableWriteCallBack | kCFSocketAutomaticallyReenableReadCallBack); + CFSocketSetSocketFlags(socketInfo->socket, flags); + + macSockets.insert(nativeSocket, socketInfo); + } + + if (type == QSocketNotifier::Read) { + Q_ASSERT(socketInfo->readNotifier == 0); + socketInfo->readNotifier = notifier; + socketInfo->readEnabled = false; + } else if (type == QSocketNotifier::Write) { + Q_ASSERT(socketInfo->writeNotifier == 0); + socketInfo->writeNotifier = notifier; + socketInfo->writeEnabled = false; + } + + if (!enableNotifiersObserver) { + // Create a run loop observer which enables the socket notifiers on each + // pass of the run loop, before any sources are processed. + CFRunLoopObserverContext context = {}; + context.info = this; + enableNotifiersObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeSources, + true, 0, enableSocketNotifiers, &context); + Q_ASSERT(enableNotifiersObserver); + CFRunLoopAddObserver(CFRunLoopGetMain(), enableNotifiersObserver, kCFRunLoopCommonModes); + } +} + +void QCFSocketNotifier::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int nativeSocket = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != eventDispatcher->thread() || eventDispatcher->thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); + return; + } +#endif + + if (type == QSocketNotifier::Exception) { + qWarning("QSocketNotifier::Exception is not supported on iOS"); + return; + } + MacSocketInfo *socketInfo = macSockets.value(nativeSocket); + if (!socketInfo) { + qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier"); + return; + } + + // Decrement read/write counters and disable callbacks if necessary. + if (type == QSocketNotifier::Read) { + Q_ASSERT(notifier == socketInfo->readNotifier); + socketInfo->readNotifier = 0; + socketInfo->readEnabled = false; + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + } else if (type == QSocketNotifier::Write) { + Q_ASSERT(notifier == socketInfo->writeNotifier); + socketInfo->writeNotifier = 0; + socketInfo->writeEnabled = false; + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + } + + // Remove CFSocket from runloop if this was the last QSocketNotifier. + if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) { + unregisterSocketInfo(socketInfo); + delete socketInfo; + macSockets.remove(nativeSocket); + } +} + +void QCFSocketNotifier::removeSocketNotifiers() +{ + // Remove CFSockets from the runloop. + foreach (MacSocketInfo *socketInfo, macSockets) { + unregisterSocketInfo(socketInfo); + delete socketInfo; + } + + macSockets.clear(); + + destroyRunLoopObserver(); +} + +void QCFSocketNotifier::destroyRunLoopObserver() +{ + if (!enableNotifiersObserver) + return; + + CFRunLoopObserverInvalidate(enableNotifiersObserver); + CFRelease(enableNotifiersObserver); + enableNotifiersObserver = 0; +} + +void QCFSocketNotifier::unregisterSocketInfo(MacSocketInfo *socketInfo) +{ + if (socketInfo->runloop) { + if (CFSocketIsValid(socketInfo->socket)) + qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop); + CFRunLoopSourceInvalidate(socketInfo->runloop); + CFRelease(socketInfo->runloop); + } + CFSocketInvalidate(socketInfo->socket); + CFRelease(socketInfo->socket); +} + +void QCFSocketNotifier::enableSocketNotifiers(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info) +{ + Q_UNUSED(ref); + Q_UNUSED(activity); + + QCFSocketNotifier *that = static_cast(info); + + foreach (MacSocketInfo *socketInfo, that->macSockets) { + if (!CFSocketIsValid(socketInfo->socket)) + continue; + + if (!socketInfo->runloop) { + // Add CFSocket to runloop. + if (!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) { + qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop"); + CFSocketInvalidate(socketInfo->socket); + continue; + } + + if (!socketInfo->readNotifier) + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + if (!socketInfo->writeNotifier) + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + } + + if (socketInfo->readNotifier && !socketInfo->readEnabled) { + socketInfo->readEnabled = true; + CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + } + if (socketInfo->writeNotifier && !socketInfo->writeEnabled) { + socketInfo->writeEnabled = true; + CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + } + } +} diff --git a/src/corelib/kernel/qcfsocketnotifier_p.h b/src/corelib/kernel/qcfsocketnotifier_p.h new file mode 100644 index 0000000000..947efecca3 --- /dev/null +++ b/src/corelib/kernel/qcfsocketnotifier_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCFSOCKETNOTIFIER_P_H +#define QCFSOCKETNOTIFIER_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 +#include + +#include + +QT_BEGIN_NAMESPACE + +struct MacSocketInfo { + MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0), + readEnabled(false), writeEnabled(false) {} + CFSocketRef socket; + CFRunLoopSourceRef runloop; + QObject *readNotifier; + QObject *writeNotifier; + bool readEnabled; + bool writeEnabled; +}; +typedef QHash MacSocketHash; + +typedef void (*MaybeCancelWaitForMoreEventsFn)(QAbstractEventDispatcher *hostEventDispacher); + +// The CoreFoundationSocketNotifier class implements socket notifiers support using +// CFSocket for event dispatchers running on top of the Core Foundation run loop system. +// (currently Mac and iOS) +// +// The principal functions are registerSocketNotifier() and unregisterSocketNotifier(). +// +// setHostEventDispatcher() should be called at startup. +// removeSocketNotifiers() should be called at shutdown. +// +class Q_CORE_EXPORT QCFSocketNotifier +{ +public: + QCFSocketNotifier(); + ~QCFSocketNotifier(); + void setHostEventDispatcher(QAbstractEventDispatcher *hostEventDispacher); + void setMaybeCancelWaitForMoreEventsCallback(MaybeCancelWaitForMoreEventsFn callBack); + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + void removeSocketNotifiers(); + +private: + void destroyRunLoopObserver(); + + static void unregisterSocketInfo(MacSocketInfo *socketInfo); + static void enableSocketNotifiers(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info); + + MacSocketHash macSockets; + QAbstractEventDispatcher *eventDispatcher; + MaybeCancelWaitForMoreEventsFn maybeCancelWaitForMoreEvents; + CFRunLoopObserverRef enableNotifiersObserver; + + friend void qt_mac_socket_callback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/kernel/qeventdispatcher_cf.mm b/src/corelib/kernel/qeventdispatcher_cf.mm new file mode 100644 index 0000000000..5b9ad38b28 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_cf.mm @@ -0,0 +1,624 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventdispatcher_cf_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +@interface RunLoopModeTracker : NSObject { + QStack m_runLoopModes; +} +@end + +@implementation RunLoopModeTracker + +- (id) init +{ + if (self = [super init]) { + m_runLoopModes.push(kCFRunLoopDefaultMode); + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receivedNotification:) + name:nil + object:[UIApplication sharedApplication]]; + } + + return self; +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [super dealloc]; +} + +static CFStringRef runLoopMode(NSDictionary *dictionary) +{ + for (NSString *key in dictionary) { + if (CFStringHasSuffix((CFStringRef)key, CFSTR("RunLoopMode"))) + return (CFStringRef)[dictionary objectForKey: key]; + } + + return nil; +} + +- (void) receivedNotification:(NSNotification *) notification +{ + if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR("RunLoopModePushNotification"))) { + if (CFStringRef mode = runLoopMode(notification.userInfo)) + m_runLoopModes.push(mode); + else + qWarning("Encountered run loop push notification without run loop mode!"); + + } else if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR("RunLoopModePopNotification"))) { + CFStringRef mode = runLoopMode(notification.userInfo); + if (CFStringCompare(mode, [self currentMode], 0) == kCFCompareEqualTo) + m_runLoopModes.pop(); + else + qWarning("Tried to pop run loop mode '%s' that was never pushed!", qPrintable(QCFString::toQString(mode))); + + Q_ASSERT(m_runLoopModes.size() >= 1); + } +} + +- (CFStringRef) currentMode +{ + return m_runLoopModes.top(); +} + +@end + +QT_BEGIN_NAMESPACE +QT_USE_NAMESPACE + +class RunLoopDebugger : public QObject +{ + Q_OBJECT + + Q_ENUMS(Activity) + Q_ENUMS(Result) + +public: + + #define Q_MIRROR_ENUM(name) name = name + + enum Activity { + Q_MIRROR_ENUM(kCFRunLoopEntry), + Q_MIRROR_ENUM(kCFRunLoopBeforeTimers), + Q_MIRROR_ENUM(kCFRunLoopBeforeSources), + Q_MIRROR_ENUM(kCFRunLoopBeforeWaiting), + Q_MIRROR_ENUM(kCFRunLoopAfterWaiting), + Q_MIRROR_ENUM(kCFRunLoopExit) + }; + + enum Result { + Q_MIRROR_ENUM(kCFRunLoopRunFinished), + Q_MIRROR_ENUM(kCFRunLoopRunStopped), + Q_MIRROR_ENUM(kCFRunLoopRunTimedOut), + Q_MIRROR_ENUM(kCFRunLoopRunHandledSource) + }; +}; + +#define Q_ENUM_PRINTER(enumName) \ + static const char* qPrintable##enumName(int value) \ + { \ + return RunLoopDebugger::staticMetaObject.enumerator(RunLoopDebugger::staticMetaObject.indexOfEnumerator(#enumName)).valueToKey(value); \ + } + +Q_ENUM_PRINTER(Activity); +Q_ENUM_PRINTER(Result); + +QDebug operator<<(QDebug s, timespec tv) +{ + s << tv.tv_sec << "." << qSetFieldWidth(9) << qSetPadChar(QChar(48)) << tv.tv_nsec << reset; + return s; +} + +#if DEBUG_EVENT_DISPATCHER +uint g_eventDispatcherIndentationLevel = 0; +#endif + +static const CFTimeInterval kCFTimeIntervalMinimum = 0; +static const CFTimeInterval kCFTimeIntervalDistantFuture = std::numeric_limits::max(); + +#pragma mark - Class definition + +QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(QObject *parent) + : QAbstractEventDispatcher(parent) + , m_processEvents(QEventLoop::EventLoopExec) + , m_postedEventsRunLoopSource(this, &QEventDispatcherCoreFoundation::processPostedEvents) + , m_runLoopActivityObserver(this, &QEventDispatcherCoreFoundation::handleRunLoopActivity, +#if DEBUG_EVENT_DISPATCHER + kCFRunLoopAllActivities +#else + kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting +#endif + ) + , m_runLoopModeTracker([[RunLoopModeTracker alloc] init]) + , m_runLoopTimer(0) + , m_blockedRunLoopTimer(0) + , m_overdueTimerScheduled(false) +{ + m_cfSocketNotifier.setHostEventDispatcher(this); + + m_postedEventsRunLoopSource.addToMode(kCFRunLoopCommonModes); + m_runLoopActivityObserver.addToMode(kCFRunLoopCommonModes); +} + +QEventDispatcherCoreFoundation::~QEventDispatcherCoreFoundation() +{ + invalidateTimer(); + qDeleteAll(m_timerInfoList); + + m_cfSocketNotifier.removeSocketNotifiers(); +} + +/*! + Processes all pending events that match \a flags until there are no + more events to process. Returns \c true if pending events were handled; + otherwise returns \c false. + + Note: + + - All events are considered equal. This function should process + both system/native events (that we may or may not care about), + as well as Qt-events (posted events and timers). + + - The function should not return until all queued/available events + have been processed. If the WaitForMoreEvents is set, the + function should wait only if there were no events ready, + and _then_ process all newly queued/available events. + + These notes apply to other function in this class as well, such as + hasPendingEvents(). +*/ +bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + bool eventsProcessed = false; + + if (flags & (QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers)) + qWarning() << "processEvents() flags" << flags << "not supported on iOS"; + + qEventDispatcherDebug() << "Entering with " << flags; qIndent(); + + if (m_blockedRunLoopTimer) { + Q_ASSERT(m_blockedRunLoopTimer == m_runLoopTimer); + + qEventDispatcherDebug() << "Recursing from blocked timer " << m_blockedRunLoopTimer; + m_runLoopTimer = 0; // Unset current timer to force creation of new timer + updateTimers(); + } + + if (m_processEvents.deferredWakeUp) { + // We may be processing events recursivly as a result of processing a posted event, + // in which case we need to signal the run-loop source so that this iteration of + // processEvents will take care of the newly posted events. + m_postedEventsRunLoopSource.signal(); + m_processEvents.deferredWakeUp = false; + + qEventDispatcherDebug() << "Processed deferred wake-up"; + } + + // The documentation states that this signal is emitted after the event + // loop returns from a function that could block, which is not the case + // here, but all the other event dispatchers emit awake at the start of + // processEvents, and the QEventLoop auto-test has an explicit check for + // this behavior, so we assume it's for a good reason and do it as well. + emit awake(); + + ProcessEventsState previousState = m_processEvents; + m_processEvents = ProcessEventsState(flags); + + bool returnAfterSingleSourceHandled = !(m_processEvents.flags & QEventLoop::EventLoopExec); + + Q_FOREVER { + CFStringRef mode = [m_runLoopModeTracker currentMode]; + + CFTimeInterval duration = (m_processEvents.flags & QEventLoop::WaitForMoreEvents) ? + kCFTimeIntervalDistantFuture : kCFTimeIntervalMinimum; + + qEventDispatcherDebug() << "Calling CFRunLoopRunInMode = " << qPrintable(QCFString::toQString(mode)) + << " for " << duration << " ms, processing single source = " << returnAfterSingleSourceHandled; qIndent(); + + SInt32 result = CFRunLoopRunInMode(mode, duration, returnAfterSingleSourceHandled); + + qUnIndent(); qEventDispatcherDebug() << "result = " << qPrintableResult(result); + + eventsProcessed |= (result == kCFRunLoopRunHandledSource + || m_processEvents.processedPostedEvents + || m_processEvents.processedTimers); + + if (result == kCFRunLoopRunFinished) { + // This should only happen at application shutdown, as the main runloop + // will presumably always have sources registered. + break; + } else if (m_processEvents.wasInterrupted) { + + if (m_processEvents.flags & QEventLoop::EventLoopExec) { + Q_ASSERT(result == kCFRunLoopRunStopped); + + // The runloop was potentially stopped (interrupted) by us, as a response to + // a Qt event loop being asked to exit. We check that the topmost eventloop + // is still supposed to keep going and return if not. Note that the runloop + // might get stopped as a result of a non-top eventloop being asked to exit, + // in which case we continue running the top event loop until that is asked + // to exit, and then unwind back to the previous event loop which will break + // immediately, since it has already been exited. + + QEventLoop *currentEventLoop = QThreadData::current()->eventLoops.top(); + Q_ASSERT(currentEventLoop); + + if (!currentEventLoop->isRunning()) { + qEventDispatcherDebug() << "Top level event loop was exited"; + break; + } else { + qEventDispatcherDebug() << "Top level event loop still running, making another pass"; + } + } else { + // We were called manually, through processEvents(), and should stop processing + // events, even if we didn't finish processing all the queued events. + qEventDispatcherDebug() << "Top level processEvents was interrupted"; + break; + } + } + + if (m_processEvents.flags & QEventLoop::EventLoopExec) { + // We were called from QEventLoop's exec(), which blocks until the event + // loop is asked to exit by calling processEvents repeatedly. Instead of + // re-entering this method again and again from QEventLoop, we can block + // here, one lever closer to CFRunLoopRunInMode, by running the native + // event loop again and again until we're interrupted by QEventLoop. + continue; + } else { + // We were called 'manually', through processEvents() + + if (result == kCFRunLoopRunHandledSource) { + // We processed one or more sources, but there might still be other + // sources that did not get a chance to process events, so we need + // to do another pass. + + // But we should only wait for more events the first time + m_processEvents.flags &= ~QEventLoop::WaitForMoreEvents; + continue; + + } else if (m_overdueTimerScheduled && !m_processEvents.processedTimers) { + // CFRunLoopRunInMode does not guarantee that a scheduled timer with a fire + // date in the past (overdue) will fire on the next run loop pass. The Qt + // APIs on the other hand document eg. zero-interval timers to always be + // handled after processing all available window-system events. + qEventDispatcherDebug() << "Manually processing timers due to overdue timer"; + processTimers(0); + eventsProcessed = true; + } + } + + break; + } + + if (m_blockedRunLoopTimer) { + invalidateTimer(); + m_runLoopTimer = m_blockedRunLoopTimer; + } + + if (m_processEvents.deferredUpdateTimers) + updateTimers(); + + if (m_processEvents.deferredWakeUp) { + m_postedEventsRunLoopSource.signal(); + qEventDispatcherDebug() << "Processed deferred wake-up"; + } + + bool wasInterrupted = m_processEvents.wasInterrupted; + + // Restore state of previous processEvents() call + m_processEvents = previousState; + + if (wasInterrupted) { + // The current processEvents run has been interrupted, but there may still be + // others below it (eg, in the case of nested event loops). We need to trigger + // another interrupt so that the parent processEvents call has a chance to check + // if it should continue. + qEventDispatcherDebug() << "Forwarding interrupt in case of nested processEvents"; + interrupt(); + } + + qEventDispatcherDebug() << "Returning with eventsProcessed = " << eventsProcessed; qUnIndent(); + + return eventsProcessed; +} + +bool QEventDispatcherCoreFoundation::processPostedEvents() +{ + if (m_processEvents.processedPostedEvents && !(m_processEvents.flags & QEventLoop::EventLoopExec)) { + qEventDispatcherDebug() << "Already processed events this pass"; + return false; + } + + m_processEvents.processedPostedEvents = true; + + qEventDispatcherDebug() << "Sending posted events for " << m_processEvents.flags; qIndent(); + QCoreApplication::sendPostedEvents(); + qUnIndent(); + + return true; +} + +void QEventDispatcherCoreFoundation::processTimers(CFRunLoopTimerRef timer) +{ + if (m_processEvents.processedTimers && !(m_processEvents.flags & QEventLoop::EventLoopExec)) { + qEventDispatcherDebug() << "Already processed timers this pass"; + m_processEvents.deferredUpdateTimers = true; + return; + } + + qEventDispatcherDebug() << "CFRunLoopTimer " << timer << " fired, activating Qt timers"; qIndent(); + + // Activating Qt timers might recurse into processEvents() if a timer-callback + // brings up a new event-loop or tries to processes events manually. Although + // a CFRunLoop can recurse inside its callbacks, a single CFRunLoopTimer can + // not. So, for each recursion into processEvents() from a timer-callback we + // need to set up a new timer-source. Instead of doing it preemtivly each + // time we activate Qt timers, we set a flag here, and let processEvents() + // decide whether or not it needs to bring up a new timer source. + + // We may have multiple recused timers, so keep track of the previous blocked timer + CFRunLoopTimerRef previouslyBlockedRunLoopTimer = m_blockedRunLoopTimer; + + m_blockedRunLoopTimer = timer; + m_timerInfoList.activateTimers(); + m_blockedRunLoopTimer = previouslyBlockedRunLoopTimer; + m_processEvents.processedTimers = true; + + qUnIndent(); + + // Now that the timer source is unblocked we may need to schedule it again + updateTimers(); +} + +void QEventDispatcherCoreFoundation::handleRunLoopActivity(CFRunLoopActivity activity) +{ + qEventDispatcherDebug() << qPrintableActivity(activity); + + switch (activity) { + case kCFRunLoopBeforeWaiting: + if (m_processEvents.processedTimers + && !(m_processEvents.flags & QEventLoop::EventLoopExec) + && m_processEvents.flags & QEventLoop::WaitForMoreEvents) { + // CoreFoundation does not treat a timer as a reason to exit CFRunLoopRunInMode + // when asked to only process a single source, so we risk waiting a long time for + // a 'proper' source to fire (typically a system source that we don't control). + // To fix this we do an explicit interrupt after processing our timer, so that + // processEvents() gets a chance to re-evaluate the state of things. + interrupt(); + } + emit aboutToBlock(); + break; + case kCFRunLoopAfterWaiting: + emit awake(); + break; +#if DEBUG_EVENT_DISPATCHER + case kCFRunLoopEntry: + case kCFRunLoopBeforeTimers: + case kCFRunLoopBeforeSources: + case kCFRunLoopExit: + break; +#endif + default: + Q_UNREACHABLE(); + } +} + +bool QEventDispatcherCoreFoundation::hasPendingEvents() +{ + // There doesn't seem to be any API on iOS to peek into the other sources + // to figure out if there are pending non-Qt events. As a workaround, we + // assume that if the run-loop is currently blocking and waiting for a + // source to signal then there are no system-events pending. If this + // function is called from the main thread then the second clause + // of the condition will always be true, as the run loop is + // never waiting in that case. The function would be more aptly named + // 'maybeHasPendingEvents' in our case. + + extern uint qGlobalPostedEventsCount(); + return qGlobalPostedEventsCount() || !CFRunLoopIsWaiting(CFRunLoopGetMain()); +} + +void QEventDispatcherCoreFoundation::wakeUp() +{ + if (m_processEvents.processedPostedEvents && !(m_processEvents.flags & QEventLoop::EventLoopExec)) { + // A manual processEvents call should only result in processing the events posted + // up until then. Any newly posted events as result of processing existing posted + // events should be handled in the next call to processEvents(). Since we're using + // a run-loop source to process our posted events we need to prevent it from being + // signaled as a result of posting new events, otherwise we end up in an infinite + // loop. We do however need to signal the source at some point, so that the newly + // posted event gets processed on the next processEvents() call, so we flag the + // need to do a deferred wake-up. + m_processEvents.deferredWakeUp = true; + qEventDispatcherDebug() << "Already processed posted events, deferring wakeUp"; + return; + } + + m_postedEventsRunLoopSource.signal(); + CFRunLoopWakeUp(CFRunLoopGetMain()); + + qEventDispatcherDebug() << "Signaled posted event run-loop source"; +} + +void QEventDispatcherCoreFoundation::interrupt() +{ + qEventDispatcherDebug() << "Marking current processEvent as interrupted"; + m_processEvents.wasInterrupted = true; + CFRunLoopStop(CFRunLoopGetMain()); +} + +void QEventDispatcherCoreFoundation::flush() +{ + // X11 only. +} + +#pragma mark - Socket notifiers + +void QEventDispatcherCoreFoundation::registerSocketNotifier(QSocketNotifier *notifier) +{ + m_cfSocketNotifier.registerSocketNotifier(notifier); +} + +void QEventDispatcherCoreFoundation::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + m_cfSocketNotifier.unregisterSocketNotifier(notifier); +} + +#pragma mark - Timers + +void QEventDispatcherCoreFoundation::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) +{ + qEventDispatcherDebug() << "id = " << timerId << ", interval = " << interval + << ", type = " << timerType << ", object = " << object; + + Q_ASSERT(timerId > 0 && interval >= 0 && object); + Q_ASSERT(object->thread() == thread() && thread() == QThread::currentThread()); + + m_timerInfoList.registerTimer(timerId, interval, timerType, object); + updateTimers(); +} + +bool QEventDispatcherCoreFoundation::unregisterTimer(int timerId) +{ + Q_ASSERT(timerId > 0); + Q_ASSERT(thread() == QThread::currentThread()); + + bool returnValue = m_timerInfoList.unregisterTimer(timerId); + + qEventDispatcherDebug() << "id = " << timerId << ", timers left: " << m_timerInfoList.size(); + + updateTimers(); + return returnValue; +} + +bool QEventDispatcherCoreFoundation::unregisterTimers(QObject *object) +{ + Q_ASSERT(object && object->thread() == thread() && thread() == QThread::currentThread()); + + bool returnValue = m_timerInfoList.unregisterTimers(object); + + qEventDispatcherDebug() << "object = " << object << ", timers left: " << m_timerInfoList.size(); + + updateTimers(); + return returnValue; +} + +QList QEventDispatcherCoreFoundation::registeredTimers(QObject *object) const +{ + Q_ASSERT(object); + return m_timerInfoList.registeredTimers(object); +} + +int QEventDispatcherCoreFoundation::remainingTime(int timerId) +{ + Q_ASSERT(timerId > 0); + return m_timerInfoList.timerRemainingTime(timerId); +} + +static double timespecToSeconds(const timespec &spec) +{ + static double nanosecondsPerSecond = 1.0 * 1000 * 1000 * 1000; + return spec.tv_sec + (spec.tv_nsec / nanosecondsPerSecond); +} + +void QEventDispatcherCoreFoundation::updateTimers() +{ + if (m_timerInfoList.size() > 0) { + // We have Qt timers registered, so create or reschedule CF timer to match + + timespec tv = { -1, -1 }; + CFAbsoluteTime timeToFire = m_timerInfoList.timerWait(tv) ? + // We have a timer ready to fire right now, or some time in the future + CFAbsoluteTimeGetCurrent() + timespecToSeconds(tv) + // We have timers, but they are all currently blocked by callbacks + : kCFTimeIntervalDistantFuture; + + if (!m_runLoopTimer) { + m_runLoopTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, + timeToFire, kCFTimeIntervalDistantFuture, 0, 0, ^(CFRunLoopTimerRef timer) { + processTimers(timer); + }); + + CFRunLoopAddTimer(CFRunLoopGetMain(), m_runLoopTimer, kCFRunLoopCommonModes); + qEventDispatcherDebug() << "Created new CFRunLoopTimer " << m_runLoopTimer; + + } else { + CFRunLoopTimerSetNextFireDate(m_runLoopTimer, timeToFire); + qEventDispatcherDebug() << "Re-scheduled CFRunLoopTimer " << m_runLoopTimer; + } + + m_overdueTimerScheduled = !timespecToSeconds(tv); + + qEventDispatcherDebug() << "Next timeout in " << tv << " seconds"; + + } else { + // No Qt timers are registered, so make sure we're not running any CF timers + invalidateTimer(); + + m_overdueTimerScheduled = false; + } +} + +void QEventDispatcherCoreFoundation::invalidateTimer() +{ + if (!m_runLoopTimer || (m_runLoopTimer == m_blockedRunLoopTimer)) + return; + + CFRunLoopTimerInvalidate(m_runLoopTimer); + qEventDispatcherDebug() << "Invalidated CFRunLoopTimer " << m_runLoopTimer; + + CFRelease(m_runLoopTimer); + m_runLoopTimer = 0; +} + +#include "qeventdispatcher_cf.moc" +#include "moc_qeventdispatcher_cf_p.cpp" + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventdispatcher_cf_p.h b/src/corelib/kernel/qeventdispatcher_cf_p.h new file mode 100644 index 0000000000..5e8d2f0c85 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_cf_p.h @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $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 + +// +// 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. +// + +#define DEBUG_EVENT_DISPATCHER 0 + +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +@class RunLoopModeTracker; +#else +typedef struct objc_object RunLoopModeTracker; +#endif + +QT_BEGIN_NAMESPACE + +class QEventDispatcherCoreFoundation; + +template +class RunLoopSource +{ +public: + typedef bool (T::*CallbackFunction)(); + + enum { kHighestPriority = 0 } RunLoopSourcePriority; + + RunLoopSource(T *delegate, CallbackFunction callback) + : m_delegate(delegate), m_callback(callback) + { + CFRunLoopSourceContext context = {}; + context.info = this; + context.perform = RunLoopSource::process; + + m_source = CFRunLoopSourceCreate(kCFAllocatorDefault, kHighestPriority, &context); + Q_ASSERT(m_source); + } + + ~RunLoopSource() + { + CFRunLoopSourceInvalidate(m_source); + CFRelease(m_source); + } + + void addToMode(CFStringRef mode, CFRunLoopRef runLoop = 0) + { + if (!runLoop) + runLoop = CFRunLoopGetCurrent(); + + CFRunLoopAddSource(runLoop, m_source, mode); + } + + void signal() { CFRunLoopSourceSignal(m_source); } + +private: + static void process(void *info) + { + RunLoopSource *self = static_cast(info); + ((self->m_delegate)->*(self->m_callback))(); + } + + T *m_delegate; + CallbackFunction m_callback; + CFRunLoopSourceRef m_source; +}; + +template +class RunLoopObserver +{ +public: + typedef void (T::*CallbackFunction) (CFRunLoopActivity activity); + + RunLoopObserver(T *delegate, CallbackFunction callback, CFOptionFlags activities) + : m_delegate(delegate), m_callback(callback) + { + CFRunLoopObserverContext context = {}; + context.info = this; + + m_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, activities, true, 0, process, &context); + Q_ASSERT(m_observer); + } + + ~RunLoopObserver() + { + CFRunLoopObserverInvalidate(m_observer); + CFRelease(m_observer); + } + + void addToMode(CFStringRef mode, CFRunLoopRef runLoop = 0) + { + if (!runLoop) + runLoop = CFRunLoopGetCurrent(); + + if (!CFRunLoopContainsObserver(runLoop, m_observer, mode)) + CFRunLoopAddObserver(runLoop, m_observer, mode); + } + + void removeFromMode(CFStringRef mode, CFRunLoopRef runLoop = 0) + { + if (!runLoop) + runLoop = CFRunLoopGetCurrent(); + + if (CFRunLoopContainsObserver(runLoop, m_observer, mode)) + CFRunLoopRemoveObserver(runLoop, m_observer, mode); + } + +private: + static void process(CFRunLoopObserverRef, CFRunLoopActivity activity, void *info) + { + RunLoopObserver *self = static_cast(info); + ((self->m_delegate)->*(self->m_callback))(activity); + } + + T *m_delegate; + CallbackFunction m_callback; + CFRunLoopObserverRef m_observer; +}; + +class Q_CORE_EXPORT 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(); + +protected: + virtual bool processPostedEvents(); + + struct ProcessEventsState + { + ProcessEventsState(QEventLoop::ProcessEventsFlags f) + : flags(f), wasInterrupted(false) + , processedPostedEvents(false), processedTimers(false) + , deferredWakeUp(false), deferredUpdateTimers(false) {} + + QEventLoop::ProcessEventsFlags flags; + bool wasInterrupted; + bool processedPostedEvents; + bool processedTimers; + bool deferredWakeUp; + bool deferredUpdateTimers; + }; + + ProcessEventsState m_processEvents; + +private: + RunLoopSource<> m_postedEventsRunLoopSource; + RunLoopObserver<> m_runLoopActivityObserver; + + RunLoopModeTracker *m_runLoopModeTracker; + + QTimerInfoList m_timerInfoList; + CFRunLoopTimerRef m_runLoopTimer; + CFRunLoopTimerRef m_blockedRunLoopTimer; + bool m_overdueTimerScheduled; + + QCFSocketNotifier m_cfSocketNotifier; + + void processTimers(CFRunLoopTimerRef); + + void handleRunLoopActivity(CFRunLoopActivity activity); + + void updateTimers(); + void invalidateTimer(); +}; + +QT_END_NAMESPACE + +#if DEBUG_EVENT_DISPATCHER +extern uint g_eventDispatcherIndentationLevel; +#define qEventDispatcherDebug() qDebug().nospace() \ + << qPrintable(QString(QLatin1String("| ")).repeated(g_eventDispatcherIndentationLevel)) \ + << __FUNCTION__ << "(): " +#define qIndent() ++g_eventDispatcherIndentationLevel +#define qUnIndent() --g_eventDispatcherIndentationLevel +#else +#define qEventDispatcherDebug() QT_NO_QDEBUG_MACRO() +#define qIndent() +#define qUnIndent() +#endif + +#endif // QEVENTDISPATCHER_CF_P_H diff --git a/src/platformsupport/cfsocketnotifier/cfsocketnotifier.pri b/src/platformsupport/cfsocketnotifier/cfsocketnotifier.pri deleted file mode 100644 index 9a19d3c278..0000000000 --- a/src/platformsupport/cfsocketnotifier/cfsocketnotifier.pri +++ /dev/null @@ -1,4 +0,0 @@ -mac { - HEADERS += $$PWD/qcfsocketnotifier_p.h - SOURCES += $$PWD/qcfsocketnotifier.cpp -} diff --git a/src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp b/src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp deleted file mode 100644 index c58e0ea78d..0000000000 --- a/src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qcfsocketnotifier_p.h" -#include -#include -#include - - -/************************************************************************** - Socket Notifiers - *************************************************************************/ -void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef, - const void *, void *info) -{ - - QCFSocketNotifier *cfSocketNotifier = static_cast(info); - int nativeSocket = CFSocketGetNative(s); - MacSocketInfo *socketInfo = cfSocketNotifier->macSockets.value(nativeSocket); - QEvent notifierEvent(QEvent::SockAct); - - // There is a race condition that happen where we disable the notifier and - // the kernel still has a notification to pass on. We then get this - // notification after we've successfully disabled the CFSocket, but our Qt - // notifier is now gone. The upshot is we have to check the notifier - // every time. - if (callbackType == kCFSocketReadCallBack) { - if (socketInfo->readNotifier && socketInfo->readEnabled) { - socketInfo->readEnabled = false; - QGuiApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); - } - } else if (callbackType == kCFSocketWriteCallBack) { - if (socketInfo->writeNotifier && socketInfo->writeEnabled) { - socketInfo->writeEnabled = false; - QGuiApplication::sendEvent(socketInfo->writeNotifier, ¬ifierEvent); - } - } - - if (cfSocketNotifier->maybeCancelWaitForMoreEvents) - cfSocketNotifier->maybeCancelWaitForMoreEvents(cfSocketNotifier->eventDispatcher); -} - -/* - Adds a loop source for the given socket to the current run loop. -*/ -CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket) -{ - CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0); - if (!loopSource) - return 0; - - CFRunLoopAddSource(CFRunLoopGetMain(), loopSource, kCFRunLoopCommonModes); - return loopSource; -} - -/* - Removes the loop source for the given socket from the current run loop. -*/ -void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop) -{ - Q_ASSERT(runloop); - CFRunLoopRemoveSource(CFRunLoopGetMain(), runloop, kCFRunLoopCommonModes); - CFSocketDisableCallBacks(socket, kCFSocketReadCallBack); - CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack); -} - -QCFSocketNotifier::QCFSocketNotifier() - : eventDispatcher(0) - , maybeCancelWaitForMoreEvents(0) - , enableNotifiersObserver(0) -{ - -} - -QCFSocketNotifier::~QCFSocketNotifier() -{ - -} - -void QCFSocketNotifier::setHostEventDispatcher(QAbstractEventDispatcher *hostEventDispacher) -{ - eventDispatcher = hostEventDispacher; -} - -void QCFSocketNotifier::setMaybeCancelWaitForMoreEventsCallback(MaybeCancelWaitForMoreEventsFn callBack) -{ - maybeCancelWaitForMoreEvents = callBack; -} - -void QCFSocketNotifier::registerSocketNotifier(QSocketNotifier *notifier) -{ - Q_ASSERT(notifier); - int nativeSocket = notifier->socket(); - int type = notifier->type(); -#ifndef QT_NO_DEBUG - if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { - qWarning("QSocketNotifier: Internal error"); - return; - } else if (notifier->thread() != eventDispatcher->thread() - || eventDispatcher->thread() != QThread::currentThread()) { - qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); - return; - } -#endif - - if (type == QSocketNotifier::Exception) { - qWarning("QSocketNotifier::Exception is not supported on iOS"); - return; - } - - // Check if we have a CFSocket for the native socket, create one if not. - MacSocketInfo *socketInfo = macSockets.value(nativeSocket); - if (!socketInfo) { - socketInfo = new MacSocketInfo(); - - // Create CFSocket, specify that we want both read and write callbacks (the callbacks - // are enabled/disabled later on). - const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack; - CFSocketContext context = {0, this, 0, 0, 0}; - socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context); - if (CFSocketIsValid(socketInfo->socket) == false) { - qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket"); - return; - } - - CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket); - // QSocketNotifier doesn't close the socket upon destruction/invalidation - flags &= ~kCFSocketCloseOnInvalidate; - // Expicitly disable automatic re-enable, as we do that manually on each runloop pass - flags &= ~(kCFSocketAutomaticallyReenableWriteCallBack | kCFSocketAutomaticallyReenableReadCallBack); - CFSocketSetSocketFlags(socketInfo->socket, flags); - - macSockets.insert(nativeSocket, socketInfo); - } - - if (type == QSocketNotifier::Read) { - Q_ASSERT(socketInfo->readNotifier == 0); - socketInfo->readNotifier = notifier; - socketInfo->readEnabled = false; - } else if (type == QSocketNotifier::Write) { - Q_ASSERT(socketInfo->writeNotifier == 0); - socketInfo->writeNotifier = notifier; - socketInfo->writeEnabled = false; - } - - if (!enableNotifiersObserver) { - // Create a run loop observer which enables the socket notifiers on each - // pass of the run loop, before any sources are processed. - CFRunLoopObserverContext context = {}; - context.info = this; - enableNotifiersObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeSources, - true, 0, enableSocketNotifiers, &context); - Q_ASSERT(enableNotifiersObserver); - CFRunLoopAddObserver(CFRunLoopGetMain(), enableNotifiersObserver, kCFRunLoopCommonModes); - } -} - -void QCFSocketNotifier::unregisterSocketNotifier(QSocketNotifier *notifier) -{ - Q_ASSERT(notifier); - int nativeSocket = notifier->socket(); - int type = notifier->type(); -#ifndef QT_NO_DEBUG - if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { - qWarning("QSocketNotifier: Internal error"); - return; - } else if (notifier->thread() != eventDispatcher->thread() || eventDispatcher->thread() != QThread::currentThread()) { - qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); - return; - } -#endif - - if (type == QSocketNotifier::Exception) { - qWarning("QSocketNotifier::Exception is not supported on iOS"); - return; - } - MacSocketInfo *socketInfo = macSockets.value(nativeSocket); - if (!socketInfo) { - qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier"); - return; - } - - // Decrement read/write counters and disable callbacks if necessary. - if (type == QSocketNotifier::Read) { - Q_ASSERT(notifier == socketInfo->readNotifier); - socketInfo->readNotifier = 0; - socketInfo->readEnabled = false; - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - } else if (type == QSocketNotifier::Write) { - Q_ASSERT(notifier == socketInfo->writeNotifier); - socketInfo->writeNotifier = 0; - socketInfo->writeEnabled = false; - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - } - - // Remove CFSocket from runloop if this was the last QSocketNotifier. - if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) { - unregisterSocketInfo(socketInfo); - delete socketInfo; - macSockets.remove(nativeSocket); - } -} - -void QCFSocketNotifier::removeSocketNotifiers() -{ - // Remove CFSockets from the runloop. - foreach (MacSocketInfo *socketInfo, macSockets) { - unregisterSocketInfo(socketInfo); - delete socketInfo; - } - - macSockets.clear(); - - destroyRunLoopObserver(); -} - -void QCFSocketNotifier::destroyRunLoopObserver() -{ - if (!enableNotifiersObserver) - return; - - CFRunLoopObserverInvalidate(enableNotifiersObserver); - CFRelease(enableNotifiersObserver); - enableNotifiersObserver = 0; -} - -void QCFSocketNotifier::unregisterSocketInfo(MacSocketInfo *socketInfo) -{ - if (socketInfo->runloop) { - if (CFSocketIsValid(socketInfo->socket)) - qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop); - CFRunLoopSourceInvalidate(socketInfo->runloop); - CFRelease(socketInfo->runloop); - } - CFSocketInvalidate(socketInfo->socket); - CFRelease(socketInfo->socket); -} - -void QCFSocketNotifier::enableSocketNotifiers(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info) -{ - Q_UNUSED(ref); - Q_UNUSED(activity); - - QCFSocketNotifier *that = static_cast(info); - - foreach (MacSocketInfo *socketInfo, that->macSockets) { - if (!CFSocketIsValid(socketInfo->socket)) - continue; - - if (!socketInfo->runloop) { - // Add CFSocket to runloop. - if (!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) { - qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop"); - CFSocketInvalidate(socketInfo->socket); - continue; - } - - if (!socketInfo->readNotifier) - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - if (!socketInfo->writeNotifier) - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - } - - if (socketInfo->readNotifier && !socketInfo->readEnabled) { - socketInfo->readEnabled = true; - CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - } - if (socketInfo->writeNotifier && !socketInfo->writeEnabled) { - socketInfo->writeEnabled = true; - CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - } - } -} diff --git a/src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h b/src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h deleted file mode 100644 index 9bccc1bf98..0000000000 --- a/src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QCFSOCKETNOTIFIER_P_H -#define QCFSOCKETNOTIFIER_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 -#include - -#include - -QT_BEGIN_NAMESPACE - -struct MacSocketInfo { - MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0), - readEnabled(false), writeEnabled(false) {} - CFSocketRef socket; - CFRunLoopSourceRef runloop; - QObject *readNotifier; - QObject *writeNotifier; - bool readEnabled; - bool writeEnabled; -}; -typedef QHash MacSocketHash; - -typedef void (*MaybeCancelWaitForMoreEventsFn)(QAbstractEventDispatcher *hostEventDispacher); - -// The CoreFoundationSocketNotifier class implements socket notifiers support using -// CFSocket for event dispatchers running on top of the Core Foundation run loop system. -// (currently Mac and iOS) -// -// The principal functions are registerSocketNotifier() and unregisterSocketNotifier(). -// -// setHostEventDispatcher() should be called at startup. -// removeSocketNotifiers() should be called at shutdown. -// -class QCFSocketNotifier -{ -public: - QCFSocketNotifier(); - ~QCFSocketNotifier(); - void setHostEventDispatcher(QAbstractEventDispatcher *hostEventDispacher); - void setMaybeCancelWaitForMoreEventsCallback(MaybeCancelWaitForMoreEventsFn callBack); - void registerSocketNotifier(QSocketNotifier *notifier); - void unregisterSocketNotifier(QSocketNotifier *notifier); - void removeSocketNotifiers(); - -private: - void destroyRunLoopObserver(); - - static void unregisterSocketInfo(MacSocketInfo *socketInfo); - static void enableSocketNotifiers(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info); - - MacSocketHash macSockets; - QAbstractEventDispatcher *eventDispatcher; - MaybeCancelWaitForMoreEventsFn maybeCancelWaitForMoreEvents; - CFRunLoopObserverRef enableNotifiersObserver; - - friend void qt_mac_socket_callback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/platformsupport/eventdispatchers/eventdispatchers.pri b/src/platformsupport/eventdispatchers/eventdispatchers.pri index c9bbe1f5b7..6e16a46b34 100644 --- a/src/platformsupport/eventdispatchers/eventdispatchers.pri +++ b/src/platformsupport/eventdispatchers/eventdispatchers.pri @@ -8,14 +8,6 @@ HEADERS +=\ $$PWD/qgenericunixeventdispatcher_p.h\ } -ios { -OBJECTIVE_SOURCES +=\ - $$PWD/qeventdispatcher_cf.mm - -HEADERS +=\ - $$PWD/qeventdispatcher_cf_p.h -} - contains(QT_CONFIG, glib) { SOURCES +=$$PWD/qeventdispatcher_glib.cpp HEADERS +=$$PWD/qeventdispatcher_glib_p.h diff --git a/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm b/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm deleted file mode 100644 index 0273fe5ed4..0000000000 --- a/src/platformsupport/eventdispatchers/qeventdispatcher_cf.mm +++ /dev/null @@ -1,624 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qeventdispatcher_cf_p.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -@interface RunLoopModeTracker : NSObject { - QStack m_runLoopModes; -} -@end - -@implementation RunLoopModeTracker - -- (id) init -{ - if (self = [super init]) { - m_runLoopModes.push(kCFRunLoopDefaultMode); - - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(receivedNotification:) - name:nil - object:[UIApplication sharedApplication]]; - } - - return self; -} - -- (void) dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [super dealloc]; -} - -static CFStringRef runLoopMode(NSDictionary *dictionary) -{ - for (NSString *key in dictionary) { - if (CFStringHasSuffix((CFStringRef)key, CFSTR("RunLoopMode"))) - return (CFStringRef)[dictionary objectForKey: key]; - } - - return nil; -} - -- (void) receivedNotification:(NSNotification *) notification -{ - if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR("RunLoopModePushNotification"))) { - if (CFStringRef mode = runLoopMode(notification.userInfo)) - m_runLoopModes.push(mode); - else - qWarning("Encountered run loop push notification without run loop mode!"); - - } else if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR("RunLoopModePopNotification"))) { - CFStringRef mode = runLoopMode(notification.userInfo); - if (CFStringCompare(mode, [self currentMode], 0) == kCFCompareEqualTo) - m_runLoopModes.pop(); - else - qWarning("Tried to pop run loop mode '%s' that was never pushed!", qPrintable(QCFString::toQString(mode))); - - Q_ASSERT(m_runLoopModes.size() >= 1); - } -} - -- (CFStringRef) currentMode -{ - return m_runLoopModes.top(); -} - -@end - -QT_BEGIN_NAMESPACE -QT_USE_NAMESPACE - -class RunLoopDebugger : public QObject -{ - Q_OBJECT - - Q_ENUMS(Activity) - Q_ENUMS(Result) - -public: - - #define Q_MIRROR_ENUM(name) name = name - - enum Activity { - Q_MIRROR_ENUM(kCFRunLoopEntry), - Q_MIRROR_ENUM(kCFRunLoopBeforeTimers), - Q_MIRROR_ENUM(kCFRunLoopBeforeSources), - Q_MIRROR_ENUM(kCFRunLoopBeforeWaiting), - Q_MIRROR_ENUM(kCFRunLoopAfterWaiting), - Q_MIRROR_ENUM(kCFRunLoopExit) - }; - - enum Result { - Q_MIRROR_ENUM(kCFRunLoopRunFinished), - Q_MIRROR_ENUM(kCFRunLoopRunStopped), - Q_MIRROR_ENUM(kCFRunLoopRunTimedOut), - Q_MIRROR_ENUM(kCFRunLoopRunHandledSource) - }; -}; - -#define Q_ENUM_PRINTER(enumName) \ - static const char* qPrintable##enumName(int value) \ - { \ - return RunLoopDebugger::staticMetaObject.enumerator(RunLoopDebugger::staticMetaObject.indexOfEnumerator(#enumName)).valueToKey(value); \ - } - -Q_ENUM_PRINTER(Activity); -Q_ENUM_PRINTER(Result); - -QDebug operator<<(QDebug s, timespec tv) -{ - s << tv.tv_sec << "." << qSetFieldWidth(9) << qSetPadChar(QChar(48)) << tv.tv_nsec << reset; - return s; -} - -#if DEBUG_EVENT_DISPATCHER -uint g_eventDispatcherIndentationLevel = 0; -#endif - -static const CFTimeInterval kCFTimeIntervalMinimum = 0; -static const CFTimeInterval kCFTimeIntervalDistantFuture = std::numeric_limits::max(); - -#pragma mark - Class definition - -QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(QObject *parent) - : QAbstractEventDispatcher(parent) - , m_processEvents(QEventLoop::EventLoopExec) - , m_postedEventsRunLoopSource(this, &QEventDispatcherCoreFoundation::processPostedEvents) - , m_runLoopActivityObserver(this, &QEventDispatcherCoreFoundation::handleRunLoopActivity, -#if DEBUG_EVENT_DISPATCHER - kCFRunLoopAllActivities -#else - kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting -#endif - ) - , m_runLoopModeTracker([[RunLoopModeTracker alloc] init]) - , m_runLoopTimer(0) - , m_blockedRunLoopTimer(0) - , m_overdueTimerScheduled(false) -{ - m_cfSocketNotifier.setHostEventDispatcher(this); - - m_postedEventsRunLoopSource.addToMode(kCFRunLoopCommonModes); - m_runLoopActivityObserver.addToMode(kCFRunLoopCommonModes); -} - -QEventDispatcherCoreFoundation::~QEventDispatcherCoreFoundation() -{ - invalidateTimer(); - qDeleteAll(m_timerInfoList); - - m_cfSocketNotifier.removeSocketNotifiers(); -} - -/*! - Processes all pending events that match \a flags until there are no - more events to process. Returns \c true if pending events were handled; - otherwise returns \c false. - - Note: - - - All events are considered equal. This function should process - both system/native events (that we may or may not care about), - as well as Qt-events (posted events and timers). - - - The function should not return until all queued/available events - have been processed. If the WaitForMoreEvents is set, the - function should wait only if there were no events ready, - and _then_ process all newly queued/available events. - - These notes apply to other function in this class as well, such as - hasPendingEvents(). -*/ -bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlags flags) -{ - bool eventsProcessed = false; - - if (flags & (QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers)) - qWarning() << "processEvents() flags" << flags << "not supported on iOS"; - - qEventDispatcherDebug() << "Entering with " << flags; qIndent(); - - if (m_blockedRunLoopTimer) { - Q_ASSERT(m_blockedRunLoopTimer == m_runLoopTimer); - - qEventDispatcherDebug() << "Recursing from blocked timer " << m_blockedRunLoopTimer; - m_runLoopTimer = 0; // Unset current timer to force creation of new timer - updateTimers(); - } - - if (m_processEvents.deferredWakeUp) { - // We may be processing events recursivly as a result of processing a posted event, - // in which case we need to signal the run-loop source so that this iteration of - // processEvents will take care of the newly posted events. - m_postedEventsRunLoopSource.signal(); - m_processEvents.deferredWakeUp = false; - - qEventDispatcherDebug() << "Processed deferred wake-up"; - } - - // The documentation states that this signal is emitted after the event - // loop returns from a function that could block, which is not the case - // here, but all the other event dispatchers emit awake at the start of - // processEvents, and the QEventLoop auto-test has an explicit check for - // this behavior, so we assume it's for a good reason and do it as well. - emit awake(); - - ProcessEventsState previousState = m_processEvents; - m_processEvents = ProcessEventsState(flags); - - bool returnAfterSingleSourceHandled = !(m_processEvents.flags & QEventLoop::EventLoopExec); - - Q_FOREVER { - CFStringRef mode = [m_runLoopModeTracker currentMode]; - - CFTimeInterval duration = (m_processEvents.flags & QEventLoop::WaitForMoreEvents) ? - kCFTimeIntervalDistantFuture : kCFTimeIntervalMinimum; - - qEventDispatcherDebug() << "Calling CFRunLoopRunInMode = " << qPrintable(QCFString::toQString(mode)) - << " for " << duration << " ms, processing single source = " << returnAfterSingleSourceHandled; qIndent(); - - SInt32 result = CFRunLoopRunInMode(mode, duration, returnAfterSingleSourceHandled); - - qUnIndent(); qEventDispatcherDebug() << "result = " << qPrintableResult(result); - - eventsProcessed |= (result == kCFRunLoopRunHandledSource - || m_processEvents.processedPostedEvents - || m_processEvents.processedTimers); - - if (result == kCFRunLoopRunFinished) { - // This should only happen at application shutdown, as the main runloop - // will presumably always have sources registered. - break; - } else if (m_processEvents.wasInterrupted) { - - if (m_processEvents.flags & QEventLoop::EventLoopExec) { - Q_ASSERT(result == kCFRunLoopRunStopped); - - // The runloop was potentially stopped (interrupted) by us, as a response to - // a Qt event loop being asked to exit. We check that the topmost eventloop - // is still supposed to keep going and return if not. Note that the runloop - // might get stopped as a result of a non-top eventloop being asked to exit, - // in which case we continue running the top event loop until that is asked - // to exit, and then unwind back to the previous event loop which will break - // immediately, since it has already been exited. - - QEventLoop *currentEventLoop = QThreadData::current()->eventLoops.top(); - Q_ASSERT(currentEventLoop); - - if (!currentEventLoop->isRunning()) { - qEventDispatcherDebug() << "Top level event loop was exited"; - break; - } else { - qEventDispatcherDebug() << "Top level event loop still running, making another pass"; - } - } else { - // We were called manually, through processEvents(), and should stop processing - // events, even if we didn't finish processing all the queued events. - qEventDispatcherDebug() << "Top level processEvents was interrupted"; - break; - } - } - - if (m_processEvents.flags & QEventLoop::EventLoopExec) { - // We were called from QEventLoop's exec(), which blocks until the event - // loop is asked to exit by calling processEvents repeatedly. Instead of - // re-entering this method again and again from QEventLoop, we can block - // here, one lever closer to CFRunLoopRunInMode, by running the native - // event loop again and again until we're interrupted by QEventLoop. - continue; - } else { - // We were called 'manually', through processEvents() - - if (result == kCFRunLoopRunHandledSource) { - // We processed one or more sources, but there might still be other - // sources that did not get a chance to process events, so we need - // to do another pass. - - // But we should only wait for more events the first time - m_processEvents.flags &= ~QEventLoop::WaitForMoreEvents; - continue; - - } else if (m_overdueTimerScheduled && !m_processEvents.processedTimers) { - // CFRunLoopRunInMode does not guarantee that a scheduled timer with a fire - // date in the past (overdue) will fire on the next run loop pass. The Qt - // APIs on the other hand document eg. zero-interval timers to always be - // handled after processing all available window-system events. - qEventDispatcherDebug() << "Manually processing timers due to overdue timer"; - processTimers(0); - eventsProcessed = true; - } - } - - break; - } - - if (m_blockedRunLoopTimer) { - invalidateTimer(); - m_runLoopTimer = m_blockedRunLoopTimer; - } - - if (m_processEvents.deferredUpdateTimers) - updateTimers(); - - if (m_processEvents.deferredWakeUp) { - m_postedEventsRunLoopSource.signal(); - qEventDispatcherDebug() << "Processed deferred wake-up"; - } - - bool wasInterrupted = m_processEvents.wasInterrupted; - - // Restore state of previous processEvents() call - m_processEvents = previousState; - - if (wasInterrupted) { - // The current processEvents run has been interrupted, but there may still be - // others below it (eg, in the case of nested event loops). We need to trigger - // another interrupt so that the parent processEvents call has a chance to check - // if it should continue. - qEventDispatcherDebug() << "Forwarding interrupt in case of nested processEvents"; - interrupt(); - } - - qEventDispatcherDebug() << "Returning with eventsProcessed = " << eventsProcessed; qUnIndent(); - - return eventsProcessed; -} - -bool QEventDispatcherCoreFoundation::processPostedEvents() -{ - if (m_processEvents.processedPostedEvents && !(m_processEvents.flags & QEventLoop::EventLoopExec)) { - qEventDispatcherDebug() << "Already processed events this pass"; - return false; - } - - m_processEvents.processedPostedEvents = true; - - qEventDispatcherDebug() << "Sending posted events for " << m_processEvents.flags; qIndent(); - QCoreApplication::sendPostedEvents(); - qUnIndent(); - - return true; -} - -void QEventDispatcherCoreFoundation::processTimers(CFRunLoopTimerRef timer) -{ - if (m_processEvents.processedTimers && !(m_processEvents.flags & QEventLoop::EventLoopExec)) { - qEventDispatcherDebug() << "Already processed timers this pass"; - m_processEvents.deferredUpdateTimers = true; - return; - } - - qEventDispatcherDebug() << "CFRunLoopTimer " << timer << " fired, activating Qt timers"; qIndent(); - - // Activating Qt timers might recurse into processEvents() if a timer-callback - // brings up a new event-loop or tries to processes events manually. Although - // a CFRunLoop can recurse inside its callbacks, a single CFRunLoopTimer can - // not. So, for each recursion into processEvents() from a timer-callback we - // need to set up a new timer-source. Instead of doing it preemtivly each - // time we activate Qt timers, we set a flag here, and let processEvents() - // decide whether or not it needs to bring up a new timer source. - - // We may have multiple recused timers, so keep track of the previous blocked timer - CFRunLoopTimerRef previouslyBlockedRunLoopTimer = m_blockedRunLoopTimer; - - m_blockedRunLoopTimer = timer; - m_timerInfoList.activateTimers(); - m_blockedRunLoopTimer = previouslyBlockedRunLoopTimer; - m_processEvents.processedTimers = true; - - qUnIndent(); - - // Now that the timer source is unblocked we may need to schedule it again - updateTimers(); -} - -void QEventDispatcherCoreFoundation::handleRunLoopActivity(CFRunLoopActivity activity) -{ - qEventDispatcherDebug() << qPrintableActivity(activity); - - switch (activity) { - case kCFRunLoopBeforeWaiting: - if (m_processEvents.processedTimers - && !(m_processEvents.flags & QEventLoop::EventLoopExec) - && m_processEvents.flags & QEventLoop::WaitForMoreEvents) { - // CoreFoundation does not treat a timer as a reason to exit CFRunLoopRunInMode - // when asked to only process a single source, so we risk waiting a long time for - // a 'proper' source to fire (typically a system source that we don't control). - // To fix this we do an explicit interrupt after processing our timer, so that - // processEvents() gets a chance to re-evaluate the state of things. - interrupt(); - } - emit aboutToBlock(); - break; - case kCFRunLoopAfterWaiting: - emit awake(); - break; -#if DEBUG_EVENT_DISPATCHER - case kCFRunLoopEntry: - case kCFRunLoopBeforeTimers: - case kCFRunLoopBeforeSources: - case kCFRunLoopExit: - break; -#endif - default: - Q_UNREACHABLE(); - } -} - -bool QEventDispatcherCoreFoundation::hasPendingEvents() -{ - // There doesn't seem to be any API on iOS to peek into the other sources - // to figure out if there are pending non-Qt events. As a workaround, we - // assume that if the run-loop is currently blocking and waiting for a - // source to signal then there are no system-events pending. If this - // function is called from the main thread then the second clause - // of the condition will always be true, as the run loop is - // never waiting in that case. The function would be more aptly named - // 'maybeHasPendingEvents' in our case. - - extern uint qGlobalPostedEventsCount(); - return qGlobalPostedEventsCount() || !CFRunLoopIsWaiting(CFRunLoopGetMain()); -} - -void QEventDispatcherCoreFoundation::wakeUp() -{ - if (m_processEvents.processedPostedEvents && !(m_processEvents.flags & QEventLoop::EventLoopExec)) { - // A manual processEvents call should only result in processing the events posted - // up until then. Any newly posted events as result of processing existing posted - // events should be handled in the next call to processEvents(). Since we're using - // a run-loop source to process our posted events we need to prevent it from being - // signaled as a result of posting new events, otherwise we end up in an infinite - // loop. We do however need to signal the source at some point, so that the newly - // posted event gets processed on the next processEvents() call, so we flag the - // need to do a deferred wake-up. - m_processEvents.deferredWakeUp = true; - qEventDispatcherDebug() << "Already processed posted events, deferring wakeUp"; - return; - } - - m_postedEventsRunLoopSource.signal(); - CFRunLoopWakeUp(CFRunLoopGetMain()); - - qEventDispatcherDebug() << "Signaled posted event run-loop source"; -} - -void QEventDispatcherCoreFoundation::interrupt() -{ - qEventDispatcherDebug() << "Marking current processEvent as interrupted"; - m_processEvents.wasInterrupted = true; - CFRunLoopStop(CFRunLoopGetMain()); -} - -void QEventDispatcherCoreFoundation::flush() -{ - // X11 only. -} - -#pragma mark - Socket notifiers - -void QEventDispatcherCoreFoundation::registerSocketNotifier(QSocketNotifier *notifier) -{ - m_cfSocketNotifier.registerSocketNotifier(notifier); -} - -void QEventDispatcherCoreFoundation::unregisterSocketNotifier(QSocketNotifier *notifier) -{ - m_cfSocketNotifier.unregisterSocketNotifier(notifier); -} - -#pragma mark - Timers - -void QEventDispatcherCoreFoundation::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) -{ - qEventDispatcherDebug() << "id = " << timerId << ", interval = " << interval - << ", type = " << timerType << ", object = " << object; - - Q_ASSERT(timerId > 0 && interval >= 0 && object); - Q_ASSERT(object->thread() == thread() && thread() == QThread::currentThread()); - - m_timerInfoList.registerTimer(timerId, interval, timerType, object); - updateTimers(); -} - -bool QEventDispatcherCoreFoundation::unregisterTimer(int timerId) -{ - Q_ASSERT(timerId > 0); - Q_ASSERT(thread() == QThread::currentThread()); - - bool returnValue = m_timerInfoList.unregisterTimer(timerId); - - qEventDispatcherDebug() << "id = " << timerId << ", timers left: " << m_timerInfoList.size(); - - updateTimers(); - return returnValue; -} - -bool QEventDispatcherCoreFoundation::unregisterTimers(QObject *object) -{ - Q_ASSERT(object && object->thread() == thread() && thread() == QThread::currentThread()); - - bool returnValue = m_timerInfoList.unregisterTimers(object); - - qEventDispatcherDebug() << "object = " << object << ", timers left: " << m_timerInfoList.size(); - - updateTimers(); - return returnValue; -} - -QList QEventDispatcherCoreFoundation::registeredTimers(QObject *object) const -{ - Q_ASSERT(object); - return m_timerInfoList.registeredTimers(object); -} - -int QEventDispatcherCoreFoundation::remainingTime(int timerId) -{ - Q_ASSERT(timerId > 0); - return m_timerInfoList.timerRemainingTime(timerId); -} - -static double timespecToSeconds(const timespec &spec) -{ - static double nanosecondsPerSecond = 1.0 * 1000 * 1000 * 1000; - return spec.tv_sec + (spec.tv_nsec / nanosecondsPerSecond); -} - -void QEventDispatcherCoreFoundation::updateTimers() -{ - if (m_timerInfoList.size() > 0) { - // We have Qt timers registered, so create or reschedule CF timer to match - - timespec tv = { -1, -1 }; - CFAbsoluteTime timeToFire = m_timerInfoList.timerWait(tv) ? - // We have a timer ready to fire right now, or some time in the future - CFAbsoluteTimeGetCurrent() + timespecToSeconds(tv) - // We have timers, but they are all currently blocked by callbacks - : kCFTimeIntervalDistantFuture; - - if (!m_runLoopTimer) { - m_runLoopTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, - timeToFire, kCFTimeIntervalDistantFuture, 0, 0, ^(CFRunLoopTimerRef timer) { - processTimers(timer); - }); - - CFRunLoopAddTimer(CFRunLoopGetMain(), m_runLoopTimer, kCFRunLoopCommonModes); - qEventDispatcherDebug() << "Created new CFRunLoopTimer " << m_runLoopTimer; - - } else { - CFRunLoopTimerSetNextFireDate(m_runLoopTimer, timeToFire); - qEventDispatcherDebug() << "Re-scheduled CFRunLoopTimer " << m_runLoopTimer; - } - - m_overdueTimerScheduled = !timespecToSeconds(tv); - - qEventDispatcherDebug() << "Next timeout in " << tv << " seconds"; - - } else { - // No Qt timers are registered, so make sure we're not running any CF timers - invalidateTimer(); - - m_overdueTimerScheduled = false; - } -} - -void QEventDispatcherCoreFoundation::invalidateTimer() -{ - if (!m_runLoopTimer || (m_runLoopTimer == m_blockedRunLoopTimer)) - return; - - CFRunLoopTimerInvalidate(m_runLoopTimer); - qEventDispatcherDebug() << "Invalidated CFRunLoopTimer " << m_runLoopTimer; - - CFRelease(m_runLoopTimer); - m_runLoopTimer = 0; -} - -#include "qeventdispatcher_cf.moc" -#include "moc_qeventdispatcher_cf_p.cpp" - -QT_END_NAMESPACE diff --git a/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h b/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h deleted file mode 100644 index 2fe5dea3d8..0000000000 --- a/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h +++ /dev/null @@ -1,280 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $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 - -// -// 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. -// - -#define DEBUG_EVENT_DISPATCHER 0 - -#include -#include -#include -#include -#include - -#ifdef __OBJC__ -@class RunLoopModeTracker; -#else -typedef struct objc_object RunLoopModeTracker; -#endif - -QT_BEGIN_NAMESPACE - -class QEventDispatcherCoreFoundation; - -template -class RunLoopSource -{ -public: - typedef bool (T::*CallbackFunction)(); - - enum { kHighestPriority = 0 } RunLoopSourcePriority; - - RunLoopSource(T *delegate, CallbackFunction callback) - : m_delegate(delegate), m_callback(callback) - { - CFRunLoopSourceContext context = {}; - context.info = this; - context.perform = RunLoopSource::process; - - m_source = CFRunLoopSourceCreate(kCFAllocatorDefault, kHighestPriority, &context); - Q_ASSERT(m_source); - } - - ~RunLoopSource() - { - CFRunLoopSourceInvalidate(m_source); - CFRelease(m_source); - } - - void addToMode(CFStringRef mode, CFRunLoopRef runLoop = 0) - { - if (!runLoop) - runLoop = CFRunLoopGetCurrent(); - - CFRunLoopAddSource(runLoop, m_source, mode); - } - - void signal() { CFRunLoopSourceSignal(m_source); } - -private: - static void process(void *info) - { - RunLoopSource *self = static_cast(info); - ((self->m_delegate)->*(self->m_callback))(); - } - - T *m_delegate; - CallbackFunction m_callback; - CFRunLoopSourceRef m_source; -}; - -template -class RunLoopObserver -{ -public: - typedef void (T::*CallbackFunction) (CFRunLoopActivity activity); - - RunLoopObserver(T *delegate, CallbackFunction callback, CFOptionFlags activities) - : m_delegate(delegate), m_callback(callback) - { - CFRunLoopObserverContext context = {}; - context.info = this; - - m_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, activities, true, 0, process, &context); - Q_ASSERT(m_observer); - } - - ~RunLoopObserver() - { - CFRunLoopObserverInvalidate(m_observer); - CFRelease(m_observer); - } - - void addToMode(CFStringRef mode, CFRunLoopRef runLoop = 0) - { - if (!runLoop) - runLoop = CFRunLoopGetCurrent(); - - if (!CFRunLoopContainsObserver(runLoop, m_observer, mode)) - CFRunLoopAddObserver(runLoop, m_observer, mode); - } - - void removeFromMode(CFStringRef mode, CFRunLoopRef runLoop = 0) - { - if (!runLoop) - runLoop = CFRunLoopGetCurrent(); - - if (CFRunLoopContainsObserver(runLoop, m_observer, mode)) - CFRunLoopRemoveObserver(runLoop, m_observer, mode); - } - -private: - static void process(CFRunLoopObserverRef, CFRunLoopActivity activity, void *info) - { - RunLoopObserver *self = static_cast(info); - ((self->m_delegate)->*(self->m_callback))(activity); - } - - T *m_delegate; - CallbackFunction m_callback; - CFRunLoopObserverRef m_observer; -}; - -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(); - -protected: - virtual bool processPostedEvents(); - - struct ProcessEventsState - { - ProcessEventsState(QEventLoop::ProcessEventsFlags f) - : flags(f), wasInterrupted(false) - , processedPostedEvents(false), processedTimers(false) - , deferredWakeUp(false), deferredUpdateTimers(false) {} - - QEventLoop::ProcessEventsFlags flags; - bool wasInterrupted; - bool processedPostedEvents; - bool processedTimers; - bool deferredWakeUp; - bool deferredUpdateTimers; - }; - - ProcessEventsState m_processEvents; - -private: - RunLoopSource<> m_postedEventsRunLoopSource; - RunLoopObserver<> m_runLoopActivityObserver; - - RunLoopModeTracker *m_runLoopModeTracker; - - QTimerInfoList m_timerInfoList; - CFRunLoopTimerRef m_runLoopTimer; - CFRunLoopTimerRef m_blockedRunLoopTimer; - bool m_overdueTimerScheduled; - - QCFSocketNotifier m_cfSocketNotifier; - - void processTimers(CFRunLoopTimerRef); - - void handleRunLoopActivity(CFRunLoopActivity activity); - - void updateTimers(); - void invalidateTimer(); -}; - -QT_END_NAMESPACE - -#if DEBUG_EVENT_DISPATCHER -extern uint g_eventDispatcherIndentationLevel; -#define qEventDispatcherDebug() qDebug().nospace() \ - << qPrintable(QString(QLatin1String("| ")).repeated(g_eventDispatcherIndentationLevel)) \ - << __FUNCTION__ << "(): " -#define qIndent() ++g_eventDispatcherIndentationLevel -#define qUnIndent() --g_eventDispatcherIndentationLevel -#else -#define qEventDispatcherDebug() QT_NO_QDEBUG_MACRO() -#define qIndent() -#define qUnIndent() -#endif - -#endif // QEVENTDISPATCHER_CF_P_H diff --git a/src/platformsupport/platformsupport.pro b/src/platformsupport/platformsupport.pro index 34e2ed3c9b..1ea6d0eb69 100644 --- a/src/platformsupport/platformsupport.pro +++ b/src/platformsupport/platformsupport.pro @@ -7,7 +7,6 @@ mac:LIBS_PRIVATE += -lz DEFINES += QT_NO_CAST_FROM_ASCII PRECOMPILED_HEADER = ../corelib/global/qt_pch.h -include(cfsocketnotifier/cfsocketnotifier.pri) include(cglconvenience/cglconvenience.pri) include(eglconvenience/eglconvenience.pri) include(eventdispatchers/eventdispatchers.pri) diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index 4a2cb42f87..8a2a478a72 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -83,8 +83,8 @@ #include #include #include +#include #include -#include #include diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h index e8ea1cc28b..98977eb670 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.h +++ b/src/plugins/platforms/ios/qioseventdispatcher.h @@ -34,7 +34,7 @@ #ifndef QIOSEVENTDISPATCHER_H #define QIOSEVENTDISPATCHER_H -#include +#include QT_BEGIN_NAMESPACE -- cgit v1.2.3