diff options
8 files changed, 374 insertions, 194 deletions
diff --git a/src/platformsupport/cfsocketnotifier/cfsocketnotifier.pri b/src/platformsupport/cfsocketnotifier/cfsocketnotifier.pri new file mode 100644 index 0000000000..9a19d3c278 --- /dev/null +++ b/src/platformsupport/cfsocketnotifier/cfsocketnotifier.pri @@ -0,0 +1,4 @@ +mac { + HEADERS += $$PWD/qcfsocketnotifier_p.h + SOURCES += $$PWD/qcfsocketnotifier.cpp +} diff --git a/src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp b/src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp new file mode 100644 index 0000000000..5dcd6a4ffd --- /dev/null +++ b/src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcfsocketnotifier_p.h" +#include <QtGui/qguiapplication.h> +#include <QtCore/qsocketnotifier.h> +#include <QtCore/qthread.h> + + +/************************************************************************** + Socket Notifiers + *************************************************************************/ +void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef, + const void *, void *info) +{ + + QCFSocketNotifier *cfSocketNotifier = static_cast<QCFSocketNotifier *>(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) + QGuiApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); + } else if (callbackType == kCFSocketWriteCallBack) { + if (socketInfo->writeNotifier) + 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); + CFRunLoopSourceInvalidate(runloop); +} + +QCFSocketNotifier::QCFSocketNotifier() +:eventDispatcher(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); + flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write + flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation + CFSocketSetSocketFlags(socketInfo->socket, flags); + + // 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); + CFRelease(socketInfo->socket); + return; + } + + // Disable both callback types by default. This must be done after + // we add the CFSocket to the runloop, or else these calls will have + // no effect. + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + + macSockets.insert(nativeSocket, socketInfo); + } + + // Increment read/write counters and select enable callbacks if necessary. + if (type == QSocketNotifier::Read) { + Q_ASSERT(socketInfo->readNotifier == 0); + socketInfo->readNotifier = notifier; + CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + } else if (type == QSocketNotifier::Write) { + Q_ASSERT(socketInfo->writeNotifier == 0); + socketInfo->writeNotifier = notifier; + CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + } +} + +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; + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + } else if (type == QSocketNotifier::Write) { + Q_ASSERT(notifier == socketInfo->writeNotifier); + socketInfo->writeNotifier = 0; + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + } + + // Remove CFSocket from runloop if this was the last QSocketNotifier. + if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) { + 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); + delete socketInfo; + macSockets.remove(nativeSocket); + } +} + +void QCFSocketNotifier::removeSocketNotifiers() +{ + // Remove CFSockets from the runloop. + for (MacSocketHash::ConstIterator it = macSockets.constBegin(); it != macSockets.constEnd(); ++it) { + MacSocketInfo *socketInfo = (*it); + 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); + } + } +} diff --git a/src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h b/src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h new file mode 100644 index 0000000000..cd1eb8e4ca --- /dev/null +++ b/src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCFSOCKETNOTIFIER_P_H +#define QCFSOCKETNOTIFIER_P_H + +#include <QtCore/qabstracteventdispatcher.h> +#include <QtCore/qhash.h> + +#include <CoreFoundation/CoreFoundation.h> + +QT_BEGIN_NAMESPACE + +struct MacSocketInfo { + MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {} + CFSocketRef socket; + CFRunLoopSourceRef runloop; + QObject *readNotifier; + QObject *writeNotifier; +}; +typedef QHash<int, MacSocketInfo *> 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(); + + MacSocketHash macSockets; + QAbstractEventDispatcher *eventDispatcher; + MaybeCancelWaitForMoreEventsFn maybeCancelWaitForMoreEvents; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/platformsupport/platformsupport.pro b/src/platformsupport/platformsupport.pro index 469c76ffae..8e0f396993 100644 --- a/src/platformsupport/platformsupport.pro +++ b/src/platformsupport/platformsupport.pro @@ -9,6 +9,7 @@ load(qt_module) DEFINES += QT_NO_CAST_FROM_ASCII PRECOMPILED_HEADER = ../corelib/global/qt_pch.h +include(cfsocketnotifier/cfsocketnotifier.pri) include(cglconvenience/cglconvenience.pri) include(dnd/dnd.pri) include(eglconvenience/eglconvenience.pri) diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index f63ac0d205..93476ee1b4 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -93,6 +93,7 @@ #include <QtGui/qwindowdefs.h> #include <QtCore/private/qabstracteventdispatcher_p.h> #include <QtCore/private/qtimerinfo_unix_p.h> +#include <QtPlatformSupport/private/qcfsocketnotifier_p.h> #include <CoreFoundation/CoreFoundation.h> @@ -132,16 +133,9 @@ public: void wakeUp(); void interrupt(); void flush(); -}; -struct MacSocketInfo { - MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {} - CFSocketRef socket; - CFRunLoopSourceRef runloop; - QObject *readNotifier; - QObject *writeNotifier; + friend void qt_mac_maybeCancelWaitForMoreEventsForwarder(QAbstractEventDispatcher *eventDispatcher); }; -typedef QHash<int, MacSocketInfo *> MacSocketHash; class QCocoaEventDispatcherPrivate : public QAbstractEventDispatcherPrivate { @@ -183,7 +177,7 @@ public: void maybeCancelWaitForMoreEvents(); void ensureNSAppInitialized(); - MacSocketHash macSockets; + QCFSocketNotifier cfSocketNotifier; QList<void *> queuedUserInputEvents; // NSEvent * CFRunLoopSourceRef postedEventsSource; CFRunLoopObserverRef waitingObserver; diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index 987600c6b4..ed4f8cd1fb 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -270,58 +270,6 @@ QCocoaEventDispatcher::registeredTimers(QObject *object) const return d->timerInfoList.registeredTimers(object); } -/************************************************************************** - Socket Notifiers - *************************************************************************/ -void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef, - const void *, void *info) { - QCocoaEventDispatcherPrivate *const eventDispatcher - = static_cast<QCocoaEventDispatcherPrivate *>(info); - int nativeSocket = CFSocketGetNative(s); - MacSocketInfo *socketInfo = eventDispatcher->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 - // everytime. - if (callbackType == kCFSocketReadCallBack) { - if (socketInfo->readNotifier) - QGuiApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); - } else if (callbackType == kCFSocketWriteCallBack) { - if (socketInfo->writeNotifier) - QGuiApplication::sendEvent(socketInfo->writeNotifier, ¬ifierEvent); - } - - eventDispatcher->maybeCancelWaitForMoreEvents(); -} - -/* - 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(mainRunLoop(), 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(mainRunLoop(), runloop, kCFRunLoopCommonModes); - CFSocketDisableCallBacks(socket, kCFSocketReadCallBack); - CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack); - CFRunLoopSourceInvalidate(runloop); -} - /* Register a QSocketNotifier with the mac event system by creating a CFSocket with with a read/write callback. @@ -331,130 +279,14 @@ void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSource */ void QCocoaEventDispatcher::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() != thread() - || thread() != QThread::currentThread()) { - qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); - return; - } -#endif - Q_D(QCocoaEventDispatcher); - - if (type == QSocketNotifier::Exception) { - qWarning("QSocketNotifier::Exception is not supported on Mac OS X"); - return; - } - - // Check if we have a CFSocket for the native socket, create one if not. - MacSocketInfo *socketInfo = d->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, d, 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); - flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write - flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation - CFSocketSetSocketFlags(socketInfo->socket, flags); - - // 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); - CFRelease(socketInfo->socket); - return; - } - - // Disable both callback types by default. This must be done after - // we add the CFSocket to the runloop, or else these calls will have - // no effect. - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - - d->macSockets.insert(nativeSocket, socketInfo); - } - - // Increment read/write counters and select enable callbacks if necessary. - if (type == QSocketNotifier::Read) { - Q_ASSERT(socketInfo->readNotifier == 0); - socketInfo->readNotifier = notifier; - CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - } else if (type == QSocketNotifier::Write) { - Q_ASSERT(socketInfo->writeNotifier == 0); - socketInfo->writeNotifier = notifier; - CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - } + d->cfSocketNotifier.registerSocketNotifier(notifier); } -/* - Unregister QSocketNotifer. The CFSocket correspoding to this notifier is - removed from the runloop of this is the last notifier that users - that CFSocket. -*/ void QCocoaEventDispatcher::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() != thread() || thread() != QThread::currentThread()) { - qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); - return; - } -#endif - Q_D(QCocoaEventDispatcher); - - if (type == QSocketNotifier::Exception) { - qWarning("QSocketNotifier::Exception is not supported on Mac OS X"); - return; - } - MacSocketInfo *socketInfo = d->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; - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - } else if (type == QSocketNotifier::Write) { - Q_ASSERT(notifier == socketInfo->writeNotifier); - socketInfo->writeNotifier = 0; - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - } - - // Remove CFSocket from runloop if this was the last QSocketNotifier. - if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) { - 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); - delete socketInfo; - d->macSockets.remove(nativeSocket); - } + d->cfSocketNotifier.unregisterSocketNotifier(notifier); } bool QCocoaEventDispatcher::hasPendingEvents() @@ -940,11 +772,19 @@ QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate() { } +void qt_mac_maybeCancelWaitForMoreEventsForwarder(QAbstractEventDispatcher *eventDispatcher) +{ + static_cast<QCocoaEventDispatcher *>(eventDispatcher)->d_func()->maybeCancelWaitForMoreEvents(); +} + QCocoaEventDispatcher::QCocoaEventDispatcher(QObject *parent) : QAbstractEventDispatcher(*new QCocoaEventDispatcherPrivate, parent) { Q_D(QCocoaEventDispatcher); + d->cfSocketNotifier.setHostEventDispatcher(this); + d->cfSocketNotifier.setMaybeCancelWaitForMoreEventsCallback(qt_mac_maybeCancelWaitForMoreEventsForwarder); + // keep our sources running when modal loops are running CFRunLoopAddCommonMode(mainRunLoop(), (CFStringRef) NSModalPanelRunLoopMode); @@ -1127,17 +967,8 @@ QCocoaEventDispatcher::~QCocoaEventDispatcher() [nsevent release]; } - // Remove CFSockets from the runloop. - for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) { - MacSocketInfo *socketInfo = (*it); - 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); - } - } + d->cfSocketNotifier.removeSocketNotifiers(); + CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes); CFRelease(d->postedEventsSource); diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h index 88d5127855..53a75618ce 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.h +++ b/src/plugins/platforms/ios/qioseventdispatcher.h @@ -78,6 +78,7 @@ #include <QtCore/qabstracteventdispatcher.h> #include <QtCore/private/qtimerinfo_unix_p.h> +#include <QtPlatformSupport/private/qcfsocketnotifier_p.h> #include <CoreFoundation/CoreFoundation.h> QT_BEGIN_NAMESPACE @@ -116,6 +117,8 @@ private: QTimerInfoList m_timerInfoList; CFRunLoopTimerRef m_runLoopTimerRef; + QCFSocketNotifier m_cfSocketNotifier; + void processPostedEvents(); void maybeStartCFRunLoopTimer(); void maybeStopCFRunLoopTimer(); diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm index f6f60387d4..e9bf039047 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.mm +++ b/src/plugins/platforms/ios/qioseventdispatcher.mm @@ -155,6 +155,8 @@ QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent) , m_interrupted(false) , m_runLoopTimerRef(0) { + m_cfSocketNotifier.setHostEventDispatcher(this); + CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); CFRunLoopSourceContext context; bzero(&context, sizeof(CFRunLoopSourceContext)); @@ -184,6 +186,8 @@ QIOSEventDispatcher::~QIOSEventDispatcher() maybeStopCFRunLoopTimer(); CFRunLoopRemoveSource(CFRunLoopGetMain(), m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); CFRelease(m_blockingTimerRunLoopSource); + + m_cfSocketNotifier.removeSocketNotifiers(); } bool QIOSEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) @@ -215,14 +219,12 @@ bool QIOSEventDispatcher::hasPendingEvents() void QIOSEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier) { - qDebug() << __FUNCTION__ << "not implemented"; - Q_UNUSED(notifier); + m_cfSocketNotifier.registerSocketNotifier(notifier); } void QIOSEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier) { - qDebug() << __FUNCTION__ << "not implemented"; - Q_UNUSED(notifier); + m_cfSocketNotifier.unregisterSocketNotifier(notifier); } void QIOSEventDispatcher::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *obj) |