iOS: Implement socket notifiers.
Create the QCFSocketNotifier class in platform support which contains shared socket notifier support for the Cocoa and iOS plugins. Remove the old code from the Cocoa plugin. The Cocoa code had one QCocoaEventDispatcher-specific call: maybeCancelWaitForMoreEvents. Create a forwarding function that is passed to QCFSocketNotifier. Change-Id: Ibf9bd4745ba4f577a55f13d0cc00f5ae04447405 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@digia.com> Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
This commit is contained in:
parent
0c1ae5f866
commit
aa5528b050
@ -0,0 +1,4 @@
|
||||
mac {
|
||||
HEADERS += $$PWD/qcfsocketnotifier_p.h
|
||||
SOURCES += $$PWD/qcfsocketnotifier.cpp
|
||||
}
|
255
src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp
Normal file
255
src/platformsupport/cfsocketnotifier/qcfsocketnotifier.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
90
src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h
Normal file
90
src/platformsupport/cfsocketnotifier/qcfsocketnotifier_p.h
Normal file
@ -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
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
d->cfSocketNotifier.registerSocketNotifier(notifier);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user