diff options
author | Morten Sorvig <morten.sorvig@nokia.com> | 2011-06-21 13:40:57 +0200 |
---|---|---|
committer | Morten Sorvig <morten.sorvig@nokia.com> | 2011-06-22 09:47:49 +0200 |
commit | 4efaf305f51d09e214645a18a7bfa62bc36e2c61 (patch) | |
tree | 6454f785f42e1e2be1d1422f81e3bbe1ad8b1158 /src/plugins | |
parent | 18c1d671371ef8d94506a9a6dd6ee3680ef80fd3 (diff) |
Add lighthouse event dispatcher API.
Platform plugin creation is now moved forward in
order to have a platform plugin instance at event
dispatcher creation time.
Plugins are now responsible for implementing
PlatformIntegration::createEventDispatcher and returning
an QAbstractEventDispatcher subclass.
Diffstat (limited to 'src/plugins')
28 files changed, 1453 insertions, 14 deletions
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index 489e633674..5ff494aba6 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -2,7 +2,7 @@ TARGET = qcocoa load(qt_plugin) DESTDIR = $$QT.gui.plugins/platforms -OBJECTIVE_SOURCES = main.mm \ +OBJECTIVE_SOURCES += main.mm \ qcocoaintegration.mm \ qcocoabackingstore.mm \ qcocoawindow.mm \ @@ -10,23 +10,24 @@ OBJECTIVE_SOURCES = main.mm \ qcocoaautoreleasepool.mm \ qnswindowdelegate.mm \ qcocoaglcontext.mm \ - qcocoanativeinterface.mm + qcocoanativeinterface.mm \ + qcocoaeventdispatcher.mm - -OBJECTIVE_HEADERS = qcocoaintegration.h \ +HEADERS += qcocoaintegration.h \ qcocoabackingstore.h \ qcocoawindow.h \ qnsview.h \ qcocoaautoreleasepool.h \ qnswindowdelegate.h \ qcocoaglcontext.h \ - qcocoanativeinterface.h + qcocoanativeinterface.h \ + qcocoaeventdispatcher.h DEFINES += QT_BUILD_COCOA_LIB #add libz for freetype. LIBS += -lz -LIBS += -framework cocoa +LIBS += -framework cocoa -framework Carbon QT += core-private gui-private platformsupport-private diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h new file mode 100644 index 0000000000..ae2182ef65 --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $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_MAC_P_H +#define QEVENTDISPATCHER_MAC_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 <QtCore/qhash.h> +#include <QtCore/qstack.h> +#include <QtGui/qwindowdefs.h> +#include <qeventdispatcher_qpa.h> + +#include <CoreFoundation/CoreFoundation.h> + +QT_BEGIN_NAMESPACE + +typedef struct _NSModalSession *NSModalSession; +typedef struct _QCocoaModalSessionInfo { + QPointer<QWindow> window; + NSModalSession session; + void *nswindow; +} QCocoaModalSessionInfo; + +class Q_GUI_EXPORT QMacCocoaAutoReleasePool +{ +private: + void *pool; +public: + QMacCocoaAutoReleasePool(); + ~QMacCocoaAutoReleasePool(); + + inline void *handle() const { return pool; } +}; + +class QCocoaEventDispatcherPrivate; +class QCocoaEventDispatcher : public QEventDispatcherQPA +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QCocoaEventDispatcher) + +public: + QCocoaEventDispatcher(QAbstractEventDispatcherPrivate &priv, QObject *parent = 0); + explicit QCocoaEventDispatcher(QObject *parent = 0); + ~QCocoaEventDispatcher(); + + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + + void registerTimer(int timerId, int interval, QObject *object); + bool unregisterTimer(int timerId); + bool unregisterTimers(QObject *object); + QList<TimerInfo> registeredTimers(QObject *object) const; + + void wakeUp(); + void interrupt(); + +private: + //friend void qt_mac_select_timer_callbk(__EventLoopTimer*, void*); + friend class QApplicationPrivate; +}; + +struct MacTimerInfo { + int id; + int interval; + QObject *obj; + bool pending; + CFRunLoopTimerRef runLoopTimer; + bool operator==(const MacTimerInfo &other) + { + return (id == other.id); + } +}; +typedef QHash<int, MacTimerInfo *> MacTimerHash; + +struct MacSocketInfo { + MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {} + CFSocketRef socket; + CFRunLoopSourceRef runloop; + QObject *readNotifier; + QObject *writeNotifier; +}; +typedef QHash<int, MacSocketInfo *> MacSocketHash; + +class QCocoaEventDispatcherPrivate : public QEventDispatcherQPAPrivate +{ + Q_DECLARE_PUBLIC(QCocoaEventDispatcher) + +public: + QCocoaEventDispatcherPrivate(); + + static MacTimerHash macTimerHash; + // Set 'blockSendPostedEvents' to true if you _really_ need + // to make sure that qt events are not posted while calling + // low-level cocoa functions (like beginModalForWindow). And + // use a QBoolBlocker to be safe: + static bool blockSendPostedEvents; + // The following variables help organizing modal sessions: + static QStack<QCocoaModalSessionInfo> cocoaModalSessionStack; + static bool currentExecIsNSAppRun; + static bool nsAppRunCalledByQt; + static bool cleanupModalSessionsNeeded; + static NSModalSession currentModalSessionCached; + static NSModalSession currentModalSession(); + static void updateChildrenWorksWhenModal(); + static void temporarilyStopAllModalSessions(); + static void beginModalSession(QWindow *widget); + static void endModalSession(QWindow *widget); + static void cancelWaitForMoreEvents(); + static void cleanupModalSessions(); + static void ensureNSAppInitialized(); + + MacSocketHash macSockets; + QList<void *> queuedUserInputEvents; // NSEvent * + CFRunLoopSourceRef postedEventsSource; + CFRunLoopObserverRef waitingObserver; + CFRunLoopObserverRef firstTimeObserver; + QAtomicInt serialNumber; + int lastSerial; + static bool interrupt; +private: + static Boolean postedEventSourceEqualCallback(const void *info1, const void *info2); + static void postedEventsSourcePerformCallback(void *info); + static void activateTimer(CFRunLoopTimerRef, void *info); + static void waitingObserverCallback(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void *info); + static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info); + friend void processPostedEvents(QCocoaEventDispatcherPrivate *const d, const bool blockSendPostedEvents); +}; + +class QtCocoaInterruptDispatcher : public QObject +{ + static QtCocoaInterruptDispatcher *instance; + bool cancelled; + + QtCocoaInterruptDispatcher(); + ~QtCocoaInterruptDispatcher(); + + public: + static void interruptLater(); + static void cancelInterruptLater(); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_MAC_P_H diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm new file mode 100644 index 0000000000..b5cbc83029 --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -0,0 +1,1135 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $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. +** +****************************************************************************/ + +#include "qcocoaeventdispatcher.h" +#include "qguiapplication.h" +#include "qevent.h" +#include "qhash.h" +#include "qmutex.h" +#include "qsocketnotifier.h" +#include <qplatformwindow_qpa.h> +#include "private/qthread_p.h" +#include "private/qguiapplication_p.h" +#include <qdebug.h> + +//#include <private/qcocoaapplication_mac_p.h> +//#include "private/qt_cocoa_helpers_mac_p.h" +#include <Cocoa/Cocoa.h> +#include <Carbon/Carbon.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +enum { + QtCocoaEventSubTypeWakeup = SHRT_MAX, + QtCocoaEventSubTypePostMessage = SHRT_MAX-1 +}; + +static inline CFRunLoopRef mainRunLoop() +{ + return CFRunLoopGetMain(); +} + +QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool() +{ + pool = (void*)[[NSAutoreleasePool alloc] init]; +} + +QMacCocoaAutoReleasePool::~QMacCocoaAutoReleasePool() +{ + [(NSAutoreleasePool*)pool release]; +} + + +/***************************************************************************** + Timers stuff + *****************************************************************************/ + +/* timer call back */ +void QCocoaEventDispatcherPrivate::activateTimer(CFRunLoopTimerRef, void *info) +{ + int timerID = +#ifdef Q_OS_MAC64 + qint64(info); +#else + int(info); +#endif + + MacTimerInfo *tmr; + tmr = macTimerHash.value(timerID); + if (tmr == 0 || tmr->pending == true) + return; // Can't send another timer event if it's pending. + + + if (blockSendPostedEvents) { + QCoreApplication::postEvent(tmr->obj, new QTimerEvent(tmr->id)); + } else { + tmr->pending = true; + QTimerEvent e(tmr->id); + + QCoreApplication::sendSpontaneousEvent(tmr->obj, &e); + // Get the value again in case the timer gets unregistered during the sendEvent. + tmr = macTimerHash.value(timerID); + if (tmr != 0) + tmr->pending = false; + } + +} + +void QCocoaEventDispatcher::registerTimer(int timerId, int interval, QObject *obj) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1 || interval < 0 || !obj) { + qWarning("QEventDispatcherMac::registerTimer: invalid arguments"); + return; + } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::startTimer: timers cannot be started from another thread"); + return; + } +#endif + + MacTimerInfo *t = new MacTimerInfo(); + t->id = timerId; + t->interval = interval; + t->obj = obj; + t->runLoopTimer = 0; + t->pending = false; + + CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent(); + CFTimeInterval cfinterval = qMax(CFTimeInterval(interval) / 1000, 0.0000001); + fireDate += cfinterval; + QCocoaEventDispatcherPrivate::macTimerHash.insert(timerId, t); + CFRunLoopTimerContext info = { 0, (void *)timerId, 0, 0, 0 }; + t->runLoopTimer = CFRunLoopTimerCreate(0, fireDate, cfinterval, 0, 0, + QCocoaEventDispatcherPrivate::activateTimer, &info); + if (t->runLoopTimer == 0) { + qFatal("QEventDispatcherMac::registerTimer: Cannot create timer"); + } + CFRunLoopAddTimer(mainRunLoop(), t->runLoopTimer, kCFRunLoopCommonModes); +} + +bool QCocoaEventDispatcher::unregisterTimer(int identifier) +{ +#ifndef QT_NO_DEBUG + if (identifier < 1) { + qWarning("QEventDispatcherMac::unregisterTimer: invalid argument"); + return false; + } else if (thread() != QThread::currentThread()) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } +#endif + if (identifier <= 0) + return false; // not init'd or invalid timer + + MacTimerInfo *timerInfo = QCocoaEventDispatcherPrivate::macTimerHash.take(identifier); + if (timerInfo == 0) + return false; + + if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent) + QAbstractEventDispatcherPrivate::releaseTimerId(identifier); + CFRunLoopTimerInvalidate(timerInfo->runLoopTimer); + CFRelease(timerInfo->runLoopTimer); + delete timerInfo; + + return true; +} + +bool QCocoaEventDispatcher::unregisterTimers(QObject *obj) +{ +#ifndef QT_NO_DEBUG + if (!obj) { + qWarning("QEventDispatcherMac::unregisterTimers: invalid argument"); + return false; + } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } +#endif + + MacTimerHash::iterator it = QCocoaEventDispatcherPrivate::macTimerHash.begin(); + while (it != QCocoaEventDispatcherPrivate::macTimerHash.end()) { + MacTimerInfo *timerInfo = it.value(); + if (timerInfo->obj != obj) { + ++it; + } else { + if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent) + QAbstractEventDispatcherPrivate::releaseTimerId(timerInfo->id); + CFRunLoopTimerInvalidate(timerInfo->runLoopTimer); + CFRelease(timerInfo->runLoopTimer); + delete timerInfo; + it = QCocoaEventDispatcherPrivate::macTimerHash.erase(it); + } + } + return true; +} + +QList<QCocoaEventDispatcher::TimerInfo> +QCocoaEventDispatcher::registeredTimers(QObject *object) const +{ + if (!object) { + qWarning("QEventDispatcherMac:registeredTimers: invalid argument"); + return QList<TimerInfo>(); + } + + QList<TimerInfo> list; + + MacTimerHash::const_iterator it = QCocoaEventDispatcherPrivate::macTimerHash.constBegin(); + while (it != QCocoaEventDispatcherPrivate::macTimerHash.constEnd()) { + MacTimerInfo *t = it.value(); + if (t->obj == object) + list << TimerInfo(t->id, t->interval); + ++it; + } + return list; +} + +/************************************************************************** + 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); + } +} + +/* + 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. + + Qt has separate socket notifiers for reading and writing, but on the mac there is + a limitation of one CFSocket object for each native socket. +*/ +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); + } +} + +/* + 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); + } +} + +bool QCocoaEventDispatcher::hasPendingEvents() +{ + extern uint qGlobalPostedEventsCount(); + extern bool qt_is_gui_used; //qapplication.cpp + return qGlobalPostedEventsCount() || (qt_is_gui_used && GetNumEventsInQueue(GetMainEventQueue())); +} + +static bool IsMouseOrKeyEvent( NSEvent* event ) +{ + bool result = false; + + switch( [event type] ) + { + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: + case NSMouseMoved: // ?? + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSMouseEntered: + case NSMouseExited: + case NSKeyDown: + case NSKeyUp: + case NSFlagsChanged: // key modifiers changed? + case NSCursorUpdate: // ?? + case NSScrollWheel: + case NSTabletPoint: + case NSTabletProximity: + case NSOtherMouseDown: + case NSOtherMouseUp: + case NSOtherMouseDragged: +#ifndef QT_NO_GESTURES +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + case NSEventTypeGesture: // touch events + case NSEventTypeMagnify: + case NSEventTypeSwipe: + case NSEventTypeRotate: + case NSEventTypeBeginGesture: + case NSEventTypeEndGesture: +#endif +#endif // QT_NO_GESTURES + result = true; + break; + + default: + break; + } + return result; +} + +static inline void qt_mac_waitForMoreEvents() +{ + // If no event exist in the cocoa event que, wait + // (and free up cpu time) until at least one event occur. + // This implementation is a bit on the edge, but seems to + // work fine: + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + [NSApp postEvent:event atStart:YES]; +} + +static inline void qt_mac_waitForMoreModalSessionEvents() +{ + // If no event exist in the cocoa event que, wait + // (and free up cpu time) until at least one event occur. + // This implementation is a bit on the edge, but seems to + // work fine: + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSModalPanelRunLoopMode + dequeue:YES]; + if (event) + [NSApp postEvent:event atStart:YES]; +} + +bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QCocoaEventDispatcher); + d->interrupt = false; + + bool interruptLater = false; + QtCocoaInterruptDispatcher::cancelInterruptLater(); + + // In case we end up recursing while we now process events, make sure + // that we send remaining posted Qt events before this call returns: + wakeUp(); + emit awake(); + + bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; + bool retVal = false; + forever { + if (d->interrupt) + break; + + QMacCocoaAutoReleasePool pool; + NSEvent* event = 0; + + // First, send all previously excluded input events, if any: + if (!excludeUserEvents) { + while (!d->queuedUserInputEvents.isEmpty()) { + event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); + if (!filterEvent(event)) { + [NSApp sendEvent:event]; + retVal = true; + } + [event release]; + } + } + + // If Qt is used as a plugin, or as an extension in a native cocoa + // application, we should not run or stop NSApplication; This will be + // done from the application itself. And if processEvents is called + // manually (rather than from a QEventLoop), we cannot enter a tight + // loop and block this call, but instead we need to return after one flush. + // Finally, if we are to exclude user input events, we cannot call [NSApp run] + // as we then loose control over which events gets dispatched: + const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; + const bool canExec_Qt = !excludeUserEvents && + (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) ; + + if (canExec_Qt && canExec_3rdParty) { + // We can use exec-mode, meaning that we can stay in a tight loop until + // interrupted. This is mostly an optimization, but it allow us to use + // [NSApp run], which is the normal code path for cocoa applications. + if (NSModalSession session = d->currentModalSession()) { + QBoolBlocker execGuard(d->currentExecIsNSAppRun, false); + while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt) + qt_mac_waitForMoreModalSessionEvents(); + + if (!d->interrupt && session == d->currentModalSessionCached) { + // Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + } else { + d->nsAppRunCalledByQt = true; + QBoolBlocker execGuard(d->currentExecIsNSAppRun, true); + [NSApp run]; + } + retVal = true; + } else { + // We cannot block the thread (and run in a tight loop). + // Instead we will process all current pending events and return. + d->ensureNSAppInitialized(); + if (NSModalSession session = d->currentModalSession()) { + // INVARIANT: a modal window is executing. + if (!excludeUserEvents) { + // Since we can dispatch all kinds of events, we choose + // to use cocoa's native way of running modal sessions: + if (flags & QEventLoop::WaitForMoreEvents) + qt_mac_waitForMoreModalSessionEvents(); + NSInteger status = [NSApp runModalSession:session]; + if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { + // INVARIANT: Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + retVal = true; + } else do { + // Dispatch all non-user events (but que non-user events up for later). In + // this case, we need more control over which events gets dispatched, and + // cannot use [NSApp runModalSession:session]: + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSModalPanelRunLoopMode + dequeue: YES]; + + if (event) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } + if (!filterEvent(event)) { + [NSApp sendEvent:event]; + retVal = true; + } + } + } while (!d->interrupt && event != nil); + } else do { + // INVARIANT: No modal window is executing. + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSDefaultRunLoopMode + dequeue: YES]; + + if (event) { + if (flags & QEventLoop::ExcludeUserInputEvents) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } + } + if (!filterEvent(event)) { + [NSApp sendEvent:event]; + retVal = true; + } + } + } while (!d->interrupt && event != nil); + + // Be sure to flush the Qt posted events when not using exec mode + // (exec mode will always do this call from the event loop source): + if (!d->interrupt) + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + + // Since the window that holds modality might have changed while processing + // events, we we need to interrupt when we return back the previous process + // event recursion to ensure that we spin the correct modal session. + // We do the interruptLater at the end of the function to ensure that we don't + // disturb the 'wait for more events' below (as deleteLater will post an event): + interruptLater = true; + } + bool canWait = (d->threadData->canWait + && !retVal + && !d->interrupt + && (flags & QEventLoop::WaitForMoreEvents)); + if (canWait) { + // INVARIANT: We haven't processed any events yet. And we're told + // to stay inside this function until at least one event is processed. + qt_mac_waitForMoreEvents(); + flags &= ~QEventLoop::WaitForMoreEvents; + } else { + // Done with event processing for now. + // Leave the function: + break; + } + } + + // If we're interrupted, we need to interrupt the _current_ + // recursion as well to check if it is still supposed to be + // executing. This way we wind down the stack until we land + // on a recursion that again calls processEvents (typically + // from QEventLoop), and set interrupt to false: + if (d->interrupt) + interrupt(); + + if (interruptLater) + QtCocoaInterruptDispatcher::interruptLater(); + + return retVal; +} + +void QCocoaEventDispatcher::wakeUp() +{ + Q_D(QCocoaEventDispatcher); + d->serialNumber.ref(); + CFRunLoopSourceSignal(d->postedEventsSource); + CFRunLoopWakeUp(mainRunLoop()); +} + +/***************************************************************************** + QEventDispatcherMac Implementation + *****************************************************************************/ +MacTimerHash QCocoaEventDispatcherPrivate::macTimerHash; +bool QCocoaEventDispatcherPrivate::blockSendPostedEvents = false; +bool QCocoaEventDispatcherPrivate::interrupt = false; + + +QStack<QCocoaModalSessionInfo> QCocoaEventDispatcherPrivate::cocoaModalSessionStack; +bool QCocoaEventDispatcherPrivate::currentExecIsNSAppRun = false; +bool QCocoaEventDispatcherPrivate::nsAppRunCalledByQt = false; +bool QCocoaEventDispatcherPrivate::cleanupModalSessionsNeeded = false; +NSModalSession QCocoaEventDispatcherPrivate::currentModalSessionCached = 0; + +void QCocoaEventDispatcherPrivate::ensureNSAppInitialized() +{ + // Some elements in Cocoa require NSApplication to be running before + // they get fully initialized, in particular the menu bar. This + // function is intended for cases where a dialog is told to execute before + // QGuiApplication::exec is called, or the application spins the events loop + // manually rather than calling QGuiApplication:exec. + // The function makes sure that NSApplication starts running, but stops + // it again as soon as the send posted events callback is called. That way + // we let Cocoa finish the initialization it seems to need. We'll only + // apply this trick at most once for any application, and we avoid doing it + // for the common case where main just starts QGuiApplication::exec. + if (nsAppRunCalledByQt || [NSApp isRunning]) + return; + nsAppRunCalledByQt = true; + QBoolBlocker block1(interrupt, true); + QBoolBlocker block2(currentExecIsNSAppRun, true); + [NSApp run]; +} + +void QCocoaEventDispatcherPrivate::temporarilyStopAllModalSessions() +{ + // Flush, and Stop, all created modal session, and as + // such, make them pending again. The next call to + // currentModalSession will recreate them again. The + // reason to stop all session like this is that otherwise + // a call [NSApp stop] would not stop NSApp, but rather + // the current modal session. So if we need to stop NSApp + // we need to stop all the modal session first. To avoid changing + // the stacking order of the windows while doing so, we put + // up a block that is used in QCocoaWindow and QCocoaPanel: + int stackSize = cocoaModalSessionStack.size(); + for (int i=0; i<stackSize; ++i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.session) { + [NSApp endModalSession:info.session]; + info.session = 0; + } + } + currentModalSessionCached = 0; +} + +NSModalSession QCocoaEventDispatcherPrivate::currentModalSession() +{ + // If we have one or more modal windows, this function will create + // a session for each of those, and return the one for the top. + if (currentModalSessionCached) + return currentModalSessionCached; + + if (cocoaModalSessionStack.isEmpty()) + return 0; + + int sessionCount = cocoaModalSessionStack.size(); + for (int i=0; i<sessionCount; ++i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (!info.window) + continue; +// ### port +// if (info.window->testAttribute(Qt::WA_DontShowOnScreen)) +// continue; + + if (!info.session) { + QMacCocoaAutoReleasePool pool; + NSWindow *window = reinterpret_cast<NSWindow *>(info.window->handle()->winId()); + if (!window) + continue; + + ensureNSAppInitialized(); + QBoolBlocker block1(blockSendPostedEvents, true); + info.nswindow = window; + [(NSWindow*) info.nswindow retain]; + int levelBeforeEnterModal = [window level]; + info.session = [NSApp beginModalSessionForWindow:window]; + // Make sure we don't stack the window lower that it was before + // entering modal, in case it e.g. had the stays-on-top flag set: + if (levelBeforeEnterModal > [window level]) + [window setLevel:levelBeforeEnterModal]; + } + currentModalSessionCached = info.session; + cleanupModalSessionsNeeded = false; + } + return currentModalSessionCached; +} + +static void setChildrenWorksWhenModal(QWindow *window, bool worksWhenModal) +{ + // For NSPanels (but not NSWindows, sadly), we can set the flag + // worksWhenModal, so that they are active even when they are not modal. +/* + ### not ported + QList<QDialog *> dialogs = window->findChildren<QDialog *>(); + for (int i=0; i<dialogs.size(); ++i){ + NSWindow *window = qt_mac_window_for(dialogs[i]); + if (window && [window isKindOfClass:[NSPanel class]]) { + [static_cast<NSPanel *>(window) setWorksWhenModal:worksWhenModal]; + if (worksWhenModal && [window isVisible]){ + [window orderFront:window]; + } + } + } +*/ +} + +void QCocoaEventDispatcherPrivate::updateChildrenWorksWhenModal() +{ + // Make the dialog children of the window + // active. And make the dialog children of + // the previous modal dialog unactive again: + QMacCocoaAutoReleasePool pool; + int size = cocoaModalSessionStack.size(); + if (size > 0){ + if (QWindow *prevModal = cocoaModalSessionStack[size-1].window) + setChildrenWorksWhenModal(prevModal, true); + if (size > 1){ + if (QWindow *prevModal = cocoaModalSessionStack[size-2].window) + setChildrenWorksWhenModal(prevModal, false); + } + } +} + +void QCocoaEventDispatcherPrivate::cleanupModalSessions() +{ + // Go through the list of modal sessions, and end those + // that no longer has a window assosiated; no window means + // the the session has logically ended. The reason we wait like + // this to actually end the sessions for real (rather than at the + // point they were marked as stopped), is that ending a session + // when no other session runs below it on the stack will make cocoa + // drop some events on the floor. + QMacCocoaAutoReleasePool pool; + int stackSize = cocoaModalSessionStack.size(); + + for (int i=stackSize-1; i>=0; --i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.window) { + // This session has a window, and is therefore not marked + // as stopped. So just make it current. There might still be other + // stopped sessions on the stack, but those will be stopped on + // a later "cleanup" call. + currentModalSessionCached = info.session; + break; + } + cocoaModalSessionStack.remove(i); + currentModalSessionCached = 0; + if (info.session) { + [NSApp endModalSession:info.session]; + [(NSWindow *)info.nswindow release]; + } + } + + updateChildrenWorksWhenModal(); + cleanupModalSessionsNeeded = false; +} + +void QCocoaEventDispatcherPrivate::beginModalSession(QWindow *window) +{ + // Add a new, empty (null), NSModalSession to the stack. + // It will become active the next time QEventDispatcher::processEvents is called. + // A QCocoaModalSessionInfo is considered pending to become active if the window pointer + // is non-zero, and the session pointer is zero (it will become active upon a call to + // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if + // the window pointer is zero, and the session pointer is non-zero (it will be fully + // stopped in cleanupModalSessions()). + QCocoaModalSessionInfo info = {window, 0, 0}; + cocoaModalSessionStack.push(info); + updateChildrenWorksWhenModal(); + currentModalSessionCached = 0; +} + +void QCocoaEventDispatcherPrivate::endModalSession(QWindow *window) +{ + // Mark all sessions attached to window as pending to be stopped. We do this + // by setting the window pointer to zero, but leave the session pointer. + // We don't tell cocoa to stop any sessions just yet, because cocoa only understands + // when we stop the _current_ modal session (which is the session on top of + // the stack, and might not belong to 'window'). + int stackSize = cocoaModalSessionStack.size(); + for (int i=stackSize-1; i>=0; --i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.window == window) { + info.window = 0; + if (i == stackSize-1) { + // The top sessions ended. Interrupt the event dispatcher + // to start spinning the correct session immidiatly: + currentModalSessionCached = 0; + cleanupModalSessionsNeeded = true; + QCocoaEventDispatcher::instance()->interrupt(); + } + } + } +} + +QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate() +{ +} + +QCocoaEventDispatcher::QCocoaEventDispatcher(QObject *parent) + : QEventDispatcherQPA(*new QCocoaEventDispatcherPrivate, parent) +{ + Q_D(QCocoaEventDispatcher); + CFRunLoopSourceContext context; + bzero(&context, sizeof(CFRunLoopSourceContext)); + context.info = d; + context.equal = QCocoaEventDispatcherPrivate::postedEventSourceEqualCallback; + context.perform = QCocoaEventDispatcherPrivate::postedEventsSourcePerformCallback; + d->postedEventsSource = CFRunLoopSourceCreate(0, 0, &context); + Q_ASSERT(d->postedEventsSource); + CFRunLoopAddSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes); + + CFRunLoopObserverContext observerContext; + bzero(&observerContext, sizeof(CFRunLoopObserverContext)); + observerContext.info = this; + d->waitingObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, + kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting, + true, 0, + QCocoaEventDispatcherPrivate::waitingObserverCallback, + &observerContext); + CFRunLoopAddObserver(mainRunLoop(), d->waitingObserver, kCFRunLoopCommonModes); + + /* The first cycle in the loop adds the source and the events of the source + are not processed. + We use an observer to process the posted events for the first + execution of the loop. */ + CFRunLoopObserverContext firstTimeObserverContext; + bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext)); + firstTimeObserverContext.info = d; + d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, + kCFRunLoopEntry, + /* repeats = */ false, + 0, + QCocoaEventDispatcherPrivate::firstLoopEntry, + &firstTimeObserverContext); + CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, kCFRunLoopCommonModes); +} + +void QCocoaEventDispatcherPrivate::waitingObserverCallback(CFRunLoopObserverRef, + CFRunLoopActivity activity, void *info) +{ + if (activity == kCFRunLoopBeforeWaiting) + emit static_cast<QCocoaEventDispatcher*>(info)->aboutToBlock(); + else + emit static_cast<QCocoaEventDispatcher*>(info)->awake(); +} + +Boolean QCocoaEventDispatcherPrivate::postedEventSourceEqualCallback(const void *info1, const void *info2) +{ + return info1 == info2; +} + +void processPostedEvents(QCocoaEventDispatcherPrivate *const d, const bool blockSendPostedEvents) +{ + if (blockSendPostedEvents) { + // We're told to not send posted events (because the event dispatcher + // is currently working on setting up the correct session to run). But + // we still need to make sure that we don't fall asleep until pending events + // are sendt, so we just signal this need, and return: + CFRunLoopSourceSignal(d->postedEventsSource); + return; + } + + if (d->cleanupModalSessionsNeeded) + d->cleanupModalSessions(); + + if (d->interrupt) { + if (d->currentExecIsNSAppRun) { + // The event dispatcher has been interrupted. But since + // [NSApplication run] is running the event loop, we + // delayed stopping it until now (to let cocoa process + // pending cocoa events first). + if (d->currentModalSessionCached) + d->temporarilyStopAllModalSessions(); + [NSApp stop:NSApp]; + d->cancelWaitForMoreEvents(); + } + return; + } + + if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) { + d->lastSerial = d->serialNumber; + // Call down to the base class event handler, which will send + // the window system events. + d->q_func()->QEventDispatcherQPA::processEvents(QEventLoop::AllEvents); + } +} + +void QCocoaEventDispatcherPrivate::firstLoopEntry(CFRunLoopObserverRef ref, + CFRunLoopActivity activity, + void *info) +{ + Q_UNUSED(ref); + Q_UNUSED(activity); +/* + // This function is called when NSApplication has finished initialization, + // which appears to be just after [NSApplication run] has started to execute. + // By setting up our apple events handlers this late, we override the ones + // set up by NSApplication. + + // If Qt is used as a plugin, we let the 3rd party application handle events + // like quit and open file events. Otherwise, if we install our own handlers, we + // easily end up breaking functionallity the 3rd party application depend on: + if (QGuiApplication::testAttribute(Qt::AA_MacPluginApplication)) + return; + + QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate]; + NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager]; + [eventManager setEventHandler:newDelegate andSelector:@selector(appleEventQuit:withReplyEvent:) + forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; + [eventManager setEventHandler:newDelegate andSelector:@selector(getUrl:withReplyEvent:) + forEventClass:kInternetEventClass andEventID:kAEGetURL]; +*/ + + processPostedEvents(static_cast<QCocoaEventDispatcherPrivate *>(info), blockSendPostedEvents); +} + +void QCocoaEventDispatcherPrivate::postedEventsSourcePerformCallback(void *info) +{ + processPostedEvents(static_cast<QCocoaEventDispatcherPrivate *>(info), blockSendPostedEvents); +} + +void QCocoaEventDispatcherPrivate::cancelWaitForMoreEvents() +{ + // In case the event dispatcher is waiting for more + // events somewhere, we post a dummy event to wake it up: + QMacCocoaAutoReleasePool pool; + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint + modifierFlags:0 timestamp:0. windowNumber:0 context:0 + subtype:QtCocoaEventSubTypeWakeup data1:0 data2:0] atStart:NO]; +} + +void QCocoaEventDispatcher::interrupt() +{ + Q_D(QCocoaEventDispatcher); + d->interrupt = true; + wakeUp(); + + // We do nothing more here than setting d->interrupt = true, and + // poke the event loop if it is sleeping. Actually stopping + // NSApp, or the current modal session, is done inside the send + // posted events callback. We do this to ensure that all current pending + // cocoa events gets delivered before we stop. Otherwise, if we now stop + // the last event loop recursion, cocoa will just drop pending posted + // events on the floor before we get a chance to reestablish a new session. + d->cancelWaitForMoreEvents(); +} + +QCocoaEventDispatcher::~QCocoaEventDispatcher() +{ + Q_D(QCocoaEventDispatcher); + //timer cleanup + MacTimerHash::iterator it = QCocoaEventDispatcherPrivate::macTimerHash.begin(); + while (it != QCocoaEventDispatcherPrivate::macTimerHash.end()) { + MacTimerInfo *t = it.value(); + if (t->runLoopTimer) { + CFRunLoopTimerInvalidate(t->runLoopTimer); + CFRelease(t->runLoopTimer); + } + delete t; + ++it; + } + QCocoaEventDispatcherPrivate::macTimerHash.clear(); + + // 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); + } + } + CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes); + CFRelease(d->postedEventsSource); + + CFRunLoopObserverInvalidate(d->waitingObserver); + CFRelease(d->waitingObserver); + + CFRunLoopObserverInvalidate(d->firstTimeObserver); + CFRelease(d->firstTimeObserver); +} + +QtCocoaInterruptDispatcher* QtCocoaInterruptDispatcher::instance = 0; + +QtCocoaInterruptDispatcher::QtCocoaInterruptDispatcher() : cancelled(false) +{ + // The whole point of this class is that we enable a way to interrupt + // the event dispatcher when returning back to a lower recursion level + // than where interruptLater was called. This is needed to detect if + // [NSApp run] should still be running at the recursion level it is at. + // Since the interrupt is canceled if processEvents is called before + // this object gets deleted, we also avoid interrupting unnecessary. + deleteLater(); +} + +QtCocoaInterruptDispatcher::~QtCocoaInterruptDispatcher() +{ + if (cancelled) + return; + instance = 0; + QCocoaEventDispatcher::instance()->interrupt(); +} + +void QtCocoaInterruptDispatcher::cancelInterruptLater() +{ + if (!instance) + return; + instance->cancelled = true; + delete instance; + instance = 0; +} + +void QtCocoaInterruptDispatcher::interruptLater() +{ + cancelInterruptLater(); + instance = new QtCocoaInterruptDispatcher; +} + +QT_END_NAMESPACE + diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 56864a4d57..031d4056fb 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -80,6 +80,7 @@ public: QPlatformWindow *createPlatformWindow(QWindow *window) const; QPlatformGLContext *createPlatformGLContext(const QSurfaceFormat &glFormat, QPlatformGLContext *share) const; QPlatformBackingStore *createPlatformBackingStore(QWindow *widget) const; + QAbstractEventDispatcher *createEventDispatcher() const; QList<QPlatformScreen *> screens() const { return mScreens; } diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 474f3a3d5c..4d01aa0a80 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -45,6 +45,7 @@ #include "qcocoabackingstore.h" #include "qcocoanativeinterface.h" +#include "qcocoaeventdispatcher.h" #include <QtPlatformSupport/private/qbasicunixfontdatabase_p.h> #include <private/qpixmap_raster_p.h> @@ -122,6 +123,11 @@ QPlatformBackingStore *QCocoaIntegration::createPlatformBackingStore(QWindow *wi return new QCocoaBackingStore(window); } +QAbstractEventDispatcher *QCocoaIntegration::createEventDispatcher() const +{ + return new QCocoaEventDispatcher(); +} + QPlatformFontDatabase *QCocoaIntegration::fontDatabase() const { return mFontDb; diff --git a/src/plugins/platforms/directfb/directfb.pro b/src/plugins/platforms/directfb/directfb.pro index f830177dcb..fb446f6166 100644 --- a/src/plugins/platforms/directfb/directfb.pro +++ b/src/plugins/platforms/directfb/directfb.pro @@ -2,6 +2,8 @@ TARGET = qdirectfb load(qt_plugin) DESTDIR = $$QT.gui.plugins/platforms +QT += core-private gui-private platformsupport-private + isEmpty(DIRECTFB_LIBS) { DIRECTFB_LIBS = -ldirectfb -lfusion -ldirect -lpthread } diff --git a/src/plugins/platforms/directfb/qdirectfbintegration.cpp b/src/plugins/platforms/directfb/qdirectfbintegration.cpp index 61f1d2513b..018a5d9e2d 100644 --- a/src/plugins/platforms/directfb/qdirectfbintegration.cpp +++ b/src/plugins/platforms/directfb/qdirectfbintegration.cpp @@ -46,7 +46,8 @@ #include "qdirectfbcursor.h" #include "qdirectfbwindow.h" -#include "qgenericunixfontdatabase.h" +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> #include <private/qwindowsurface_raster_p.h> #include <private/qpixmap_raster_p.h> @@ -55,6 +56,7 @@ #include <QtGui/private/qpixmapdata_p.h> #include <QtCore/QCoreApplication> #include <QtCore/QThread> +#include <QtCore/QAbstractEventDispatcher> QT_BEGIN_NAMESPACE @@ -131,6 +133,11 @@ QPlatformWindow *QDirectFbIntegration::createPlatformWindow(QWidget *widget, WId return new QDirectFbWindow(widget,input); } +QAbstractEventDispatcher *QDirectFbIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + QWindowSurface *QDirectFbIntegration::createWindowSurface(QWidget *widget, WId winId) const { return new QDirectFbWindowSurface(widget,winId); diff --git a/src/plugins/platforms/directfb/qdirectfbintegration.h b/src/plugins/platforms/directfb/qdirectfbintegration.h index 0e8337a5fb..8195ebd4b5 100644 --- a/src/plugins/platforms/directfb/qdirectfbintegration.h +++ b/src/plugins/platforms/directfb/qdirectfbintegration.h @@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE class QThread; +class QAbstractEventDispatcher; class QDirectFBCursor; class QDirectFbScreen : public QPlatformScreen @@ -87,6 +88,7 @@ public: QPixmapData *createPixmapData(QPixmapData::PixelType type) const; QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId = 0) const; QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const; + QAbstractEventDispatcher *createEventDispatcher() const; QList<QPlatformScreen *> screens() const { return mScreens; } diff --git a/src/plugins/platforms/linuxfb/linuxfb.pro b/src/plugins/platforms/linuxfb/linuxfb.pro index ce6814ecc1..9375486d8b 100644 --- a/src/plugins/platforms/linuxfb/linuxfb.pro +++ b/src/plugins/platforms/linuxfb/linuxfb.pro @@ -3,6 +3,8 @@ load(qt_plugin) DESTDIR = $$QT.gui.plugins/platforms +QT += core-private gui-private platformsupport-private + SOURCES = main.cpp qlinuxfbintegration.cpp HEADERS = qlinuxfbintegration.h diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp index 4a24d6614a..c2388ceb85 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp @@ -42,6 +42,7 @@ #include "qlinuxfbintegration.h" #include "../fb_base/fb_base.h" #include "qgenericunixfontdatabase.h" +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> #include <QtGui/private/qpixmap_raster_p.h> #include <private/qcore_unix_p.h> // overrides QT_OPEN #include <qimage.h> @@ -810,6 +811,11 @@ QPlatformWindow *QLinuxFbIntegration::createPlatformWindow(QWidget *widget, WId return w; } +QAbstractEventDispatcher *QMinimalIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + QPlatformFontDatabase *QLinuxFbIntegration::fontDatabase() const { return fontDb; diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h index b96749ff03..62c2a59187 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h +++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h @@ -70,6 +70,7 @@ class QLinuxFbIntegrationPrivate; struct fb_cmap; struct fb_var_screeninfo; struct fb_fix_screeninfo; +class QAbstractEventDispatcher; class QLinuxFbIntegration : public QPlatformIntegration { @@ -82,6 +83,7 @@ public: QPixmapData *createPixmapData(QPixmapData::PixelType type) const; QPlatformWindow *createPlatformWindow(QWidget *widget, WId WinId) const; QWindowSurface *createWindowSurface(QWidget *widget, WId WinId) const; + QAbstractEventDispatcher *createEventDispatcher() const; QList<QPlatformScreen *> screens() const { return mScreens; } diff --git a/src/plugins/platforms/minimal/minimal.pro b/src/plugins/platforms/minimal/minimal.pro index 53ae1b1319..392d12d19a 100644 --- a/src/plugins/platforms/minimal/minimal.pro +++ b/src/plugins/platforms/minimal/minimal.pro @@ -1,7 +1,7 @@ TARGET = qminimal load(qt_plugin) -QT = core-private gui-private +QT += core-private gui-private platformsupport-private DESTDIR = $$QT.gui.plugins/platforms SOURCES = main.cpp \ diff --git a/src/plugins/platforms/minimal/qminimalintegration.cpp b/src/plugins/platforms/minimal/qminimalintegration.cpp index c3300ce3b8..a12ac3657a 100644 --- a/src/plugins/platforms/minimal/qminimalintegration.cpp +++ b/src/plugins/platforms/minimal/qminimalintegration.cpp @@ -41,6 +41,7 @@ #include "qminimalintegration.h" #include "qminimalbackingstore.h" +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> #include <QtGui/private/qpixmap_raster_p.h> #include <QtGui/QPlatformWindow> @@ -79,3 +80,9 @@ QPlatformBackingStore *QMinimalIntegration::createPlatformBackingStore(QWindow * { return new QMinimalBackingStore(window); } + +QAbstractEventDispatcher *QMinimalIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + diff --git a/src/plugins/platforms/minimal/qminimalintegration.h b/src/plugins/platforms/minimal/qminimalintegration.h index efb0f352d4..dc111757db 100644 --- a/src/plugins/platforms/minimal/qminimalintegration.h +++ b/src/plugins/platforms/minimal/qminimalintegration.h @@ -73,7 +73,8 @@ public: QPixmapData *createPixmapData(QPixmapData::PixelType type) const; QPlatformWindow *createPlatformWindow(QWindow *window) const; - QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QAbstractEventDispatcher *createEventDispatcher() const; QList<QPlatformScreen *> screens() const { return mScreens; } diff --git a/src/plugins/platforms/qvfb/qvfb.pro b/src/plugins/platforms/qvfb/qvfb.pro index 5db8533264..a95b13efc7 100644 --- a/src/plugins/platforms/qvfb/qvfb.pro +++ b/src/plugins/platforms/qvfb/qvfb.pro @@ -3,6 +3,7 @@ load(qt_plugin) DESTDIR = $$QT.gui.plugins/platforms +QT += core-private gui-private platformsupport-private SOURCES = main.cpp qvfbintegration.cpp qvfbwindowsurface.cpp HEADERS = qvfbintegration.h qvfbwindowsurface.h diff --git a/src/plugins/platforms/qvfb/qvfbintegration.cpp b/src/plugins/platforms/qvfb/qvfbintegration.cpp index 6b54420402..214f6a8368 100644 --- a/src/plugins/platforms/qvfb/qvfbintegration.cpp +++ b/src/plugins/platforms/qvfb/qvfbintegration.cpp @@ -63,6 +63,7 @@ #include <QWindowSystemInterface> #include "qgenericunixfontdatabase.h" +#include "qgenericunixeventdispatcher.h" QT_BEGIN_NAMESPACE @@ -438,6 +439,11 @@ QPlatformWindow *QVFbIntegration::createPlatformWindow(QWidget *widget, WId) con return new QVFbWindow(mPrimaryScreen, widget); } +QAbstractEventDispatcher *QVFbIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + QPlatformFontDatabase *QVFbIntegration::fontDatabase() const { return mFontDb; diff --git a/src/plugins/platforms/qvfb/qvfbintegration.h b/src/plugins/platforms/qvfb/qvfbintegration.h index ae3ba7bcc3..aaf20a37ec 100644 --- a/src/plugins/platforms/qvfb/qvfbintegration.h +++ b/src/plugins/platforms/qvfb/qvfbintegration.h @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE class QVFbScreenPrivate; +class QAbstractEventDispatcher; class QVFbScreen : public QPlatformScreen { @@ -81,6 +82,7 @@ public: QPixmapData *createPixmapData(QPixmapData::PixelType type) const; QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId) const; QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const; + QAbstractEventDispatcher *createEventDispatcher() const; QList<QPlatformScreen *> screens() const { return mScreens; } diff --git a/src/plugins/platforms/vnc/qvncintegration.cpp b/src/plugins/platforms/vnc/qvncintegration.cpp index 1e8c6c1bc5..459eefd599 100644 --- a/src/plugins/platforms/vnc/qvncintegration.cpp +++ b/src/plugins/platforms/vnc/qvncintegration.cpp @@ -173,6 +173,10 @@ QWindowSurface *QVNCIntegration::createWindowSurface(QWidget *widget, WId) const return surface; } +QAbstractEventDispatcher *QVFbIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} QPlatformWindow *QVNCIntegration::createPlatformWindow(QWidget *widget, WId /*winId*/) const { diff --git a/src/plugins/platforms/vnc/qvncintegration.h b/src/plugins/platforms/vnc/qvncintegration.h index 3e13bc3b8d..dbdac51c73 100644 --- a/src/plugins/platforms/vnc/qvncintegration.h +++ b/src/plugins/platforms/vnc/qvncintegration.h @@ -74,7 +74,7 @@ private: }; class QVNCIntegrationPrivate; - +class QAbstractEventDispatcher; class QVNCIntegration : public QPlatformIntegration { @@ -85,6 +85,7 @@ public: QPixmapData *createPixmapData(QPixmapData::PixelType type) const; QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId) const; QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const; + QAbstractEventDispatcher createEventDispatcher() const; QPixmap grabWindow(WId window, int x, int y, int width, int height) const; diff --git a/src/plugins/platforms/vnc/vnc.pro b/src/plugins/platforms/vnc/vnc.pro index 85bffb0637..321cee4790 100644 --- a/src/plugins/platforms/vnc/vnc.pro +++ b/src/plugins/platforms/vnc/vnc.pro @@ -1,7 +1,7 @@ TARGET = qvncgraphicssystem load(qt_plugin) -QT += network +QT += network core-private gui-private platformsupport-private DESTDIR = $$QT.gui.plugins/platforms diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index dc59d37f40..bdd8235d5b 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -48,6 +48,7 @@ #include "qwaylandclipboard.h" #include "QtPlatformSupport/private/qgenericunixfontdatabase_p.h" +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> #include <QtGui/QWindowSystemInterface> #include <QtGui/QPlatformCursor> @@ -122,6 +123,11 @@ QPlatformBackingStore *QWaylandIntegration::createPlatformBackingStore(QWindow * return new QWaylandShmBackingStore(window); } +QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + QPlatformFontDatabase *QWaylandIntegration::fontDatabase() const { return mFontDb; diff --git a/src/plugins/platforms/wayland/qwaylandintegration.h b/src/plugins/platforms/wayland/qwaylandintegration.h index adb8b81ca6..6a5e4d3b28 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.h +++ b/src/plugins/platforms/wayland/qwaylandintegration.h @@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE class QWaylandBuffer; class QWaylandDisplay; +class QAbstractEventDispatcher; class QWaylandIntegration : public QPlatformIntegration { @@ -59,6 +60,7 @@ public: QPlatformWindow *createPlatformWindow(QWindow *window) const; QPlatformGLContext *createPlatformGLContext(const QSurfaceFormat &glFormat, QPlatformGLContext *share) const; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QAbstractEventDispatcher *createEventDispatcher() const; QList<QPlatformScreen *> screens() const; diff --git a/src/plugins/platforms/wayland/wayland.pro b/src/plugins/platforms/wayland/wayland.pro index 9010439939..dc4485ca33 100644 --- a/src/plugins/platforms/wayland/wayland.pro +++ b/src/plugins/platforms/wayland/wayland.pro @@ -42,3 +42,4 @@ INSTALLS += target include ($$PWD/gl_integration/gl_integration.pri) include ($$PWD/windowmanager_integration/windowmanager_integration.pri) +load(qpa/eventdispatchers/eventdispatchers) diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index d5f7c70ad2..2513075f45 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -55,6 +55,7 @@ #include <private/qpixmap_raster_p.h> +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> #include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> #include <stdio.h> @@ -136,6 +137,11 @@ QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *wind return new QXcbBackingStore(window); } +QAbstractEventDispatcher *QXcbIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + QList<QPlatformScreen *> QXcbIntegration::screens() const { return m_screens; diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index a839b78caa..1f426ab4e7 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE class QXcbConnection; +class QAbstractEventDispatcher; class QXcbIntegration : public QPlatformIntegration { @@ -60,6 +61,7 @@ public: QPlatformWindow *createPlatformWindow(QWindow *window) const; QPlatformGLContext *createPlatformGLContext(const QSurfaceFormat &glFormat, QPlatformGLContext *share) const; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QAbstractEventDispatcher *createEventDispatcher() const; QList<QPlatformScreen *> screens() const; void moveToScreen(QWindow *window, int screen); diff --git a/src/plugins/platforms/xlib/qxlibintegration.cpp b/src/plugins/platforms/xlib/qxlibintegration.cpp index 02104d9f74..f659d439ec 100644 --- a/src/plugins/platforms/xlib/qxlibintegration.cpp +++ b/src/plugins/platforms/xlib/qxlibintegration.cpp @@ -45,7 +45,8 @@ #include <QtCore/qdebug.h> #include "qxlibwindow.h" -#include "qgenericunixfontdatabase.h" +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> #include "qxlibscreen.h" #include "qxlibclipboard.h" #include "qxlibdisplay.h" @@ -106,7 +107,10 @@ QPlatformWindow *QXlibIntegration::createPlatformWindow(QWidget *widget, WId /*w return new QXlibWindow(widget); } - +QAbstractEventDispatcher *QXlibIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} QPixmap QXlibIntegration::grabWindow(WId window, int x, int y, int width, int height) const { diff --git a/src/plugins/platforms/xlib/qxlibintegration.h b/src/plugins/platforms/xlib/qxlibintegration.h index 9c814ead69..ce5f35a41d 100644 --- a/src/plugins/platforms/xlib/qxlibintegration.h +++ b/src/plugins/platforms/xlib/qxlibintegration.h @@ -64,6 +64,7 @@ public: QPixmapData *createPixmapData(QPixmapData::PixelType type) const; QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId) const; QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const; + QAbstractEventDispatcher *createEventDispatcher() const; QPixmap grabWindow(WId window, int x, int y, int width, int height) const; diff --git a/src/plugins/platforms/xlib/xlib.pro b/src/plugins/platforms/xlib/xlib.pro index b6d0e0bf27..0c97d31f11 100644 --- a/src/plugins/platforms/xlib/xlib.pro +++ b/src/plugins/platforms/xlib/xlib.pro @@ -3,7 +3,7 @@ TARGET = qxlib load(qpa/plugin) DESTDIR = $$QT.gui.plugins/platforms -QT += core-private gui-private opengl-private +QT += core-private gui-private opengl-private platformsupport-private SOURCES = \ main.cpp \ |