src/gui/kernel/qeventdispatcher_mac.mm
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /****************************************************************************
       
    43 **
       
    44 ** Copyright (c) 2007-2008, Apple, Inc.
       
    45 **
       
    46 ** All rights reserved.
       
    47 **
       
    48 ** Redistribution and use in source and binary forms, with or without
       
    49 ** modification, are permitted provided that the following conditions are met:
       
    50 **
       
    51 **   * Redistributions of source code must retain the above copyright notice,
       
    52 **     this list of conditions and the following disclaimer.
       
    53 **
       
    54 **   * Redistributions in binary form must reproduce the above copyright notice,
       
    55 **     this list of conditions and the following disclaimer in the documentation
       
    56 **     and/or other materials provided with the distribution.
       
    57 **
       
    58 **   * Neither the name of Apple, Inc. nor the names of its contributors
       
    59 **     may be used to endorse or promote products derived from this software
       
    60 **     without specific prior written permission.
       
    61 **
       
    62 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
    63 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
    64 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
    65 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
       
    66 ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    67 ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    68 ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    69 ** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       
    70 ** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       
    71 ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       
    72 ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    73 **
       
    74 ****************************************************************************/
       
    75 
       
    76 #include "qplatformdefs.h"
       
    77 #include "private/qt_mac_p.h"
       
    78 #include "qeventdispatcher_mac_p.h"
       
    79 #include "qapplication.h"
       
    80 #include "qevent.h"
       
    81 #include "qdialog.h"
       
    82 #include "qhash.h"
       
    83 #include "qsocketnotifier.h"
       
    84 #include "private/qwidget_p.h"
       
    85 #include "private/qthread_p.h"
       
    86 #include "private/qapplication_p.h"
       
    87 
       
    88 #include <private/qcocoaapplication_mac_p.h>
       
    89 #include "private/qt_cocoa_helpers_mac_p.h"
       
    90 
       
    91 #ifndef QT_NO_THREAD
       
    92 #  include "qmutex.h"
       
    93 
       
    94 QT_BEGIN_NAMESPACE
       
    95 
       
    96 QT_USE_NAMESPACE
       
    97 #endif
       
    98 
       
    99 /*****************************************************************************
       
   100   Externals
       
   101  *****************************************************************************/
       
   102 extern void qt_event_request_timer(MacTimerInfo *); //qapplication_mac.cpp
       
   103 extern MacTimerInfo *qt_event_get_timer(EventRef); //qapplication_mac.cpp
       
   104 extern void qt_event_request_select(QEventDispatcherMac *); //qapplication_mac.cpp
       
   105 extern void qt_event_request_updates(); //qapplication_mac.cpp
       
   106 extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
       
   107 extern bool qt_is_gui_used; //qapplication.cpp
       
   108 extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp
       
   109 extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp
       
   110 
       
   111 static inline CFRunLoopRef mainRunLoop()
       
   112 {
       
   113 #ifndef QT_MAC_USE_COCOA
       
   114     return reinterpret_cast<CFRunLoopRef>(const_cast<void *>(GetCFRunLoopFromEventLoop(GetMainEventLoop())));
       
   115 #else
       
   116     return CFRunLoopGetMain();
       
   117 #endif
       
   118 }
       
   119 
       
   120 /*****************************************************************************
       
   121   Timers stuff
       
   122  *****************************************************************************/
       
   123 
       
   124 /* timer call back */
       
   125 void QEventDispatcherMacPrivate::activateTimer(CFRunLoopTimerRef, void *info)
       
   126 {
       
   127     int timerID =
       
   128 #ifdef Q_OS_MAC64
       
   129     qint64(info);
       
   130 #else
       
   131     int(info);
       
   132 #endif
       
   133 
       
   134     MacTimerInfo *tmr;
       
   135     tmr = macTimerHash.value(timerID);
       
   136     if (tmr == 0 || tmr->pending == true)
       
   137         return; // Can't send another timer event if it's pending.
       
   138 
       
   139 
       
   140     if (blockSendPostedEvents) {
       
   141         QCoreApplication::postEvent(tmr->obj, new QTimerEvent(tmr->id));
       
   142     } else {
       
   143         tmr->pending = true;
       
   144         QTimerEvent e(tmr->id);
       
   145         qt_sendSpontaneousEvent(tmr->obj, &e);
       
   146         // Get the value again in case the timer gets unregistered during the sendEvent.
       
   147         tmr = macTimerHash.value(timerID);
       
   148         if (tmr != 0)
       
   149             tmr->pending = false;
       
   150     }
       
   151 
       
   152 }
       
   153 
       
   154 void QEventDispatcherMac::registerTimer(int timerId, int interval, QObject *obj)
       
   155 {
       
   156 #ifndef QT_NO_DEBUG
       
   157     if (timerId < 1 || interval < 0 || !obj) {
       
   158         qWarning("QEventDispatcherMac::registerTimer: invalid arguments");
       
   159         return;
       
   160     } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
       
   161         qWarning("QObject::startTimer: timers cannot be started from another thread");
       
   162         return;
       
   163     }
       
   164 #endif
       
   165 
       
   166     MacTimerInfo *t = new MacTimerInfo();
       
   167     t->id = timerId;
       
   168     t->interval = interval;
       
   169     t->obj = obj;
       
   170     t->runLoopTimer = 0;
       
   171     t->pending = false;
       
   172 
       
   173     CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent();
       
   174     CFTimeInterval cfinterval = qMax(CFTimeInterval(interval) / 1000, 0.0000001);
       
   175     fireDate += cfinterval;
       
   176     QEventDispatcherMacPrivate::macTimerHash.insert(timerId, t);
       
   177     CFRunLoopTimerContext info = { 0, (void *)timerId, 0, 0, 0 };
       
   178     t->runLoopTimer = CFRunLoopTimerCreate(0, fireDate, cfinterval, 0, 0,
       
   179                                            QEventDispatcherMacPrivate::activateTimer, &info);
       
   180     if (t->runLoopTimer == 0) {
       
   181         qFatal("QEventDispatcherMac::registerTimer: Cannot create timer");
       
   182     }
       
   183     CFRunLoopAddTimer(mainRunLoop(), t->runLoopTimer, kCFRunLoopCommonModes);
       
   184 }
       
   185 
       
   186 bool QEventDispatcherMac::unregisterTimer(int identifier)
       
   187 {
       
   188 #ifndef QT_NO_DEBUG
       
   189     if (identifier < 1) {
       
   190         qWarning("QEventDispatcherMac::unregisterTimer: invalid argument");
       
   191         return false;
       
   192     } else if (thread() != QThread::currentThread()) {
       
   193         qWarning("QObject::killTimer: timers cannot be stopped from another thread");
       
   194         return false;
       
   195     }
       
   196 #endif
       
   197     if (identifier <= 0)
       
   198         return false;                                // not init'd or invalid timer
       
   199 
       
   200     MacTimerInfo *timerInfo = QEventDispatcherMacPrivate::macTimerHash.take(identifier);
       
   201     if (timerInfo == 0)
       
   202         return false;
       
   203 
       
   204     if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent)
       
   205         QAbstractEventDispatcherPrivate::releaseTimerId(identifier);
       
   206     CFRunLoopTimerInvalidate(timerInfo->runLoopTimer);
       
   207     CFRelease(timerInfo->runLoopTimer);
       
   208     delete timerInfo;
       
   209 
       
   210     return true;
       
   211 }
       
   212 
       
   213 bool QEventDispatcherMac::unregisterTimers(QObject *obj)
       
   214 {
       
   215 #ifndef QT_NO_DEBUG
       
   216     if (!obj) {
       
   217         qWarning("QEventDispatcherMac::unregisterTimers: invalid argument");
       
   218         return false;
       
   219     } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
       
   220         qWarning("QObject::killTimers: timers cannot be stopped from another thread");
       
   221         return false;
       
   222     }
       
   223 #endif
       
   224 
       
   225     MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin();
       
   226     while (it != QEventDispatcherMacPrivate::macTimerHash.end()) {
       
   227         MacTimerInfo *timerInfo = it.value();
       
   228         if (timerInfo->obj != obj) {
       
   229             ++it;
       
   230         } else {
       
   231             if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent)
       
   232                 QAbstractEventDispatcherPrivate::releaseTimerId(timerInfo->id);
       
   233             CFRunLoopTimerInvalidate(timerInfo->runLoopTimer);
       
   234             CFRelease(timerInfo->runLoopTimer);
       
   235             delete timerInfo;
       
   236             it = QEventDispatcherMacPrivate::macTimerHash.erase(it);
       
   237         }
       
   238     }
       
   239     return true;
       
   240 }
       
   241 
       
   242 QList<QEventDispatcherMac::TimerInfo>
       
   243 QEventDispatcherMac::registeredTimers(QObject *object) const
       
   244 {
       
   245     if (!object) {
       
   246         qWarning("QEventDispatcherMac:registeredTimers: invalid argument");
       
   247         return QList<TimerInfo>();
       
   248     }
       
   249 
       
   250     QList<TimerInfo> list;
       
   251 
       
   252     MacTimerHash::const_iterator it = QEventDispatcherMacPrivate::macTimerHash.constBegin();
       
   253     while (it != QEventDispatcherMacPrivate::macTimerHash.constEnd()) {
       
   254         MacTimerInfo *t = it.value();
       
   255         if (t->obj == object)
       
   256             list << TimerInfo(t->id, t->interval);
       
   257         ++it;
       
   258     }
       
   259     return list;
       
   260 }
       
   261 
       
   262 /**************************************************************************
       
   263     Socket Notifiers
       
   264  *************************************************************************/
       
   265 void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef,
       
   266                             const void *,  void *info) {
       
   267     QEventDispatcherMacPrivate *const eventDispatcher
       
   268                                     = static_cast<QEventDispatcherMacPrivate *>(info);
       
   269     int nativeSocket = CFSocketGetNative(s);
       
   270     MacSocketInfo *socketInfo = eventDispatcher->macSockets.value(nativeSocket);
       
   271     QEvent notifierEvent(QEvent::SockAct);
       
   272 
       
   273     // There is a race condition that happen where we disable the notifier and
       
   274     // the kernel still has a notification to pass on. We then get this
       
   275     // notification after we've successfully disabled the CFSocket, but our Qt
       
   276     // notifier is now gone. The upshot is we have to check the notifier
       
   277     // everytime.
       
   278     if (callbackType == kCFSocketReadCallBack) {
       
   279         if (socketInfo->readNotifier)
       
   280             QApplication::sendEvent(socketInfo->readNotifier, &notifierEvent);
       
   281     } else if (callbackType == kCFSocketWriteCallBack) {
       
   282         if (socketInfo->writeNotifier)
       
   283             QApplication::sendEvent(socketInfo->writeNotifier, &notifierEvent);
       
   284     }
       
   285 }
       
   286 
       
   287 /*
       
   288     Adds a loop source for the given socket to the current run loop.
       
   289 */
       
   290 CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket)
       
   291 {
       
   292     CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
       
   293     if (!loopSource)
       
   294         return 0;
       
   295 
       
   296     CFRunLoopAddSource(mainRunLoop(), loopSource, kCFRunLoopCommonModes);
       
   297     return loopSource;
       
   298 }
       
   299 
       
   300 /*
       
   301     Removes the loop source for the given socket from the current run loop.
       
   302 */
       
   303 void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop)
       
   304 {
       
   305     Q_ASSERT(runloop);
       
   306     CFRunLoopRemoveSource(mainRunLoop(), runloop, kCFRunLoopCommonModes);
       
   307     CFSocketDisableCallBacks(socket, kCFSocketReadCallBack);
       
   308     CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack);
       
   309     CFRunLoopSourceInvalidate(runloop);
       
   310 }
       
   311 
       
   312 /*
       
   313     Register a QSocketNotifier with the mac event system by creating a CFSocket with
       
   314     with a read/write callback.
       
   315 
       
   316     Qt has separate socket notifiers for reading and writing, but on the mac there is
       
   317     a limitation of one CFSocket object for each native socket.
       
   318 */
       
   319 void QEventDispatcherMac::registerSocketNotifier(QSocketNotifier *notifier)
       
   320 {
       
   321     Q_ASSERT(notifier);
       
   322     int nativeSocket = notifier->socket();
       
   323     int type = notifier->type();
       
   324 #ifndef QT_NO_DEBUG
       
   325     if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
       
   326         qWarning("QSocketNotifier: Internal error");
       
   327         return;
       
   328     } else if (notifier->thread() != thread()
       
   329                || thread() != QThread::currentThread()) {
       
   330         qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
       
   331         return;
       
   332     }
       
   333 #endif
       
   334 
       
   335     Q_D(QEventDispatcherMac);
       
   336 
       
   337     if (type == QSocketNotifier::Exception) {
       
   338         qWarning("QSocketNotifier::Exception is not supported on Mac OS X");
       
   339         return;
       
   340     }
       
   341 
       
   342     // Check if we have a CFSocket for the native socket, create one if not.
       
   343     MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket);
       
   344     if (!socketInfo) {
       
   345         socketInfo = new MacSocketInfo();
       
   346 
       
   347         // Create CFSocket, specify that we want both read and write callbacks (the callbacks
       
   348         // are enabled/disabled later on).
       
   349         const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack;
       
   350         CFSocketContext context = {0, d, 0, 0, 0};
       
   351         socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context);
       
   352         if (CFSocketIsValid(socketInfo->socket) == false) {
       
   353             qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket");
       
   354             return;
       
   355         }
       
   356 
       
   357         CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket);
       
   358         flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write
       
   359         flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation
       
   360         CFSocketSetSocketFlags(socketInfo->socket, flags);
       
   361 
       
   362         // Add CFSocket to runloop.
       
   363         if(!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) {
       
   364             qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop");
       
   365             CFSocketInvalidate(socketInfo->socket);
       
   366             CFRelease(socketInfo->socket);
       
   367             return;
       
   368         }
       
   369 
       
   370         // Disable both callback types by default. This must be done after
       
   371         // we add the CFSocket to the runloop, or else these calls will have
       
   372         // no effect.
       
   373         CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
       
   374         CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
       
   375 
       
   376         d->macSockets.insert(nativeSocket, socketInfo);
       
   377     }
       
   378 
       
   379     // Increment read/write counters and select enable callbacks if necessary.
       
   380     if (type == QSocketNotifier::Read) {
       
   381         Q_ASSERT(socketInfo->readNotifier == 0);
       
   382         socketInfo->readNotifier = notifier;
       
   383         CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
       
   384     } else if (type == QSocketNotifier::Write) {
       
   385         Q_ASSERT(socketInfo->writeNotifier == 0);
       
   386         socketInfo->writeNotifier = notifier;
       
   387         CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
       
   388     }
       
   389 }
       
   390 
       
   391 /*
       
   392     Unregister QSocketNotifer. The CFSocket correspoding to this notifier is
       
   393     removed from the runloop of this is the last notifier that users
       
   394     that CFSocket.
       
   395 */
       
   396 void QEventDispatcherMac::unregisterSocketNotifier(QSocketNotifier *notifier)
       
   397 {
       
   398     Q_ASSERT(notifier);
       
   399     int nativeSocket = notifier->socket();
       
   400     int type = notifier->type();
       
   401 #ifndef QT_NO_DEBUG
       
   402     if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
       
   403         qWarning("QSocketNotifier: Internal error");
       
   404         return;
       
   405     } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
       
   406         qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
       
   407         return;
       
   408     }
       
   409 #endif
       
   410 
       
   411     Q_D(QEventDispatcherMac);
       
   412 
       
   413     if (type == QSocketNotifier::Exception) {
       
   414         qWarning("QSocketNotifier::Exception is not supported on Mac OS X");
       
   415         return;
       
   416     }
       
   417     MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket);
       
   418     if (!socketInfo) {
       
   419         qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier");
       
   420         return;
       
   421     }
       
   422 
       
   423     // Decrement read/write counters and disable callbacks if necessary.
       
   424     if (type == QSocketNotifier::Read) {
       
   425         Q_ASSERT(notifier == socketInfo->readNotifier);
       
   426         socketInfo->readNotifier = 0;
       
   427         CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
       
   428     } else if (type == QSocketNotifier::Write) {
       
   429         Q_ASSERT(notifier == socketInfo->writeNotifier);
       
   430         socketInfo->writeNotifier = 0;
       
   431         CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
       
   432     }
       
   433 
       
   434     // Remove CFSocket from runloop if this was the last QSocketNotifier.
       
   435     if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) {
       
   436         if (CFSocketIsValid(socketInfo->socket))
       
   437             qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
       
   438         CFRunLoopSourceInvalidate(socketInfo->runloop);
       
   439         CFRelease(socketInfo->runloop);
       
   440         CFSocketInvalidate(socketInfo->socket);
       
   441         CFRelease(socketInfo->socket);
       
   442         delete socketInfo;
       
   443         d->macSockets.remove(nativeSocket);
       
   444     }
       
   445 }
       
   446 
       
   447 bool QEventDispatcherMac::hasPendingEvents()
       
   448 {
       
   449     extern uint qGlobalPostedEventsCount();
       
   450     return qGlobalPostedEventsCount() || (qt_is_gui_used && GetNumEventsInQueue(GetMainEventQueue()));
       
   451 }
       
   452 
       
   453 
       
   454 static bool qt_mac_send_event(QEventLoop::ProcessEventsFlags, OSEventRef event, OSWindowRef pt)
       
   455 {
       
   456 #ifndef QT_MAC_USE_COCOA
       
   457     if(pt && SendEventToWindow(event, pt) != eventNotHandledErr)
       
   458         return true;
       
   459     return !SendEventToEventTarget(event, GetEventDispatcherTarget());
       
   460 #else // QT_MAC_USE_COCOA
       
   461     if (pt)
       
   462         [pt sendEvent:event];
       
   463     else
       
   464         [NSApp sendEvent:event];
       
   465     return true;
       
   466 #endif
       
   467 }
       
   468 
       
   469 #ifdef QT_MAC_USE_COCOA
       
   470 static bool IsMouseOrKeyEvent( NSEvent* event )
       
   471 {
       
   472     bool    result    = false;
       
   473     
       
   474     switch( [event type] )
       
   475     {
       
   476         case NSLeftMouseDown:              
       
   477         case NSLeftMouseUp:      
       
   478         case NSRightMouseDown:   
       
   479         case NSRightMouseUp:     
       
   480         case NSMouseMoved:                // ??
       
   481         case NSLeftMouseDragged: 
       
   482         case NSRightMouseDragged:
       
   483         case NSMouseEntered:     
       
   484         case NSMouseExited:      
       
   485         case NSKeyDown:          
       
   486         case NSKeyUp:            
       
   487         case NSFlagsChanged:            // key modifiers changed?
       
   488         case NSCursorUpdate:            // ??
       
   489         case NSScrollWheel:      
       
   490         case NSTabletPoint:      
       
   491         case NSTabletProximity:  
       
   492         case NSOtherMouseDown:   
       
   493         case NSOtherMouseUp:     
       
   494         case NSOtherMouseDragged:
       
   495             result    = true;
       
   496         break;
       
   497 
       
   498         default:
       
   499         break;
       
   500     }
       
   501     return result;
       
   502 }
       
   503 #endif
       
   504 
       
   505 static inline void qt_mac_waitForMoreEvents()
       
   506 {
       
   507 #ifndef QT_MAC_USE_COCOA
       
   508     while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut) ;
       
   509 #else
       
   510     // If no event exist in the cocoa event que, wait
       
   511     // (and free up cpu time) until at least one event occur.
       
   512     // This implementation is a bit on the edge, but seems to
       
   513     // work fine:
       
   514     NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
       
   515         untilDate:[NSDate distantFuture]
       
   516         inMode:NSDefaultRunLoopMode
       
   517         dequeue:YES];
       
   518     if (event)
       
   519         [NSApp postEvent:event atStart:YES];
       
   520 #endif
       
   521 }
       
   522 
       
   523 #ifdef QT_MAC_USE_COCOA
       
   524 static inline void qt_mac_waitForMoreModalSessionEvents()
       
   525 {
       
   526     // If no event exist in the cocoa event que, wait
       
   527     // (and free up cpu time) until at least one event occur.
       
   528     // This implementation is a bit on the edge, but seems to
       
   529     // work fine:
       
   530     NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
       
   531         untilDate:[NSDate distantFuture]
       
   532         inMode:NSModalPanelRunLoopMode
       
   533         dequeue:YES];
       
   534     if (event)
       
   535         [NSApp postEvent:event atStart:YES];
       
   536 }
       
   537 #endif
       
   538 
       
   539 bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags)
       
   540 {
       
   541     Q_D(QEventDispatcherMac);
       
   542     d->interrupt = false;
       
   543     // In case we end up recursing while we now process events, make sure
       
   544     // that we send remaining posted Qt events before this call returns:
       
   545     wakeUp();
       
   546     emit awake();
       
   547 
       
   548     bool retVal = false;
       
   549     forever {
       
   550         if (d->interrupt)
       
   551             break;
       
   552 
       
   553 #ifdef QT_MAC_USE_COCOA
       
   554         QMacCocoaAutoReleasePool pool;
       
   555         NSEvent* event = 0;
       
   556 
       
   557         // If Qt is used as a plugin, or just added into a native cocoa
       
   558         // application, we should not run or stop NSApplication;
       
   559         // This will be done from outside Qt.
       
   560         // And if processEvents is called manually (rather than from QEventLoop), we
       
   561         // cannot enter a tight loop and block the call, but instead return after one flush:
       
   562         bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning];
       
   563         bool canExec_Qt = flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec;
       
   564 
       
   565         if (canExec_Qt && canExec_3rdParty) {
       
   566             // We can use exec-mode, meaning that we can stay in a tight loop until
       
   567             // interrupted. This is mostly an optimization, but it also allow us
       
   568             // to use [NSApp run], which is the recommended way of running applications
       
   569             // in cocoa. [NSApp run] should be called at least once for any cocoa app.
       
   570             if (NSModalSession session = d->currentModalSession()) {
       
   571                 QBoolBlocker execGuard(d->currentExecIsNSAppRun, false);
       
   572                 while (!d->interrupt && [NSApp runModalSession:session] == NSRunContinuesResponse)
       
   573                     qt_mac_waitForMoreModalSessionEvents();
       
   574             } else {
       
   575                 d->nsAppRunCalledByQt = true;
       
   576                 QBoolBlocker execGuard(d->currentExecIsNSAppRun, true);
       
   577                 [NSApp run];
       
   578             }
       
   579             retVal = true;
       
   580         } else do {
       
   581             // INVARIANT: We cannot block the thread (and run in a tight loop).
       
   582             // Instead we will process all current pending events and return.
       
   583             bool mustRelease = false;
       
   584 
       
   585             if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
       
   586                 // Process a pending user input event
       
   587                 mustRelease = true;
       
   588                 event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
       
   589             } else {
       
   590                 if (NSModalSession session = d->currentModalSession()) {
       
   591                     if (flags & QEventLoop::WaitForMoreEvents)
       
   592                         qt_mac_waitForMoreModalSessionEvents();
       
   593                     [NSApp runModalSession:session];
       
   594                     retVal = true;
       
   595                     break;
       
   596                 } else {
       
   597                     event = [NSApp nextEventMatchingMask:NSAnyEventMask
       
   598                         untilDate:nil
       
   599                         inMode:NSDefaultRunLoopMode
       
   600                         dequeue: YES];
       
   601 
       
   602                     if (event != nil) {
       
   603                         if (flags & QEventLoop::ExcludeUserInputEvents) {
       
   604                             if (IsMouseOrKeyEvent(event)) {
       
   605                                 // retain event here?
       
   606                                 [event retain];
       
   607                                 d->queuedUserInputEvents.append(event);
       
   608                                 continue;
       
   609                             }
       
   610                         }
       
   611                     }
       
   612                 }
       
   613             }
       
   614             if (event) {
       
   615                 if (!filterEvent(event) && qt_mac_send_event(flags, event, 0))
       
   616                     retVal = true;
       
   617                 if (mustRelease)
       
   618                     [event release];
       
   619             }
       
   620         } while(!d->interrupt && event != nil);
       
   621 
       
   622 #else
       
   623         do {
       
   624             EventRef event;
       
   625             if (!(flags & QEventLoop::ExcludeUserInputEvents)
       
   626                     && !d->queuedUserInputEvents.isEmpty()) {
       
   627                 // process a pending user input event
       
   628                 event = static_cast<EventRef>(d->queuedUserInputEvents.takeFirst());
       
   629             } else {
       
   630                 OSStatus err = ReceiveNextEvent(0,0, kEventDurationNoWait, true, &event);
       
   631                 if(err != noErr)
       
   632                     continue;
       
   633                 // else
       
   634                 if (flags & QEventLoop::ExcludeUserInputEvents) {
       
   635                     UInt32 ekind = GetEventKind(event),
       
   636                            eclass = GetEventClass(event);
       
   637                     switch(eclass) {
       
   638                         case kEventClassQt:
       
   639                             if(ekind != kEventQtRequestContext)
       
   640                                 break;
       
   641                             // fall through
       
   642                         case kEventClassMouse:
       
   643                         case kEventClassKeyboard:
       
   644                             d->queuedUserInputEvents.append(event);
       
   645                             continue;
       
   646                     }
       
   647                 }
       
   648             }
       
   649 
       
   650             if (!filterEvent(&event) && qt_mac_send_event(flags, event, 0))
       
   651                 retVal = true;
       
   652             ReleaseEvent(event);
       
   653         } while(!d->interrupt && GetNumEventsInQueue(GetMainEventQueue()) > 0);
       
   654 
       
   655 #endif
       
   656 
       
   657         bool canWait = (d->threadData->canWait
       
   658                 && !retVal
       
   659                 && !d->interrupt
       
   660                 && (flags & QEventLoop::WaitForMoreEvents));
       
   661         if (canWait) {
       
   662             // INVARIANT: We haven't processed any events yet. And we're told
       
   663             // to stay inside this function until at least one event is processed.
       
   664             qt_mac_waitForMoreEvents();
       
   665             flags &= ~QEventLoop::WaitForMoreEvents;
       
   666         } else {
       
   667             // Done with event processing for now.
       
   668             // Leave the function:
       
   669             break;
       
   670         }
       
   671     }
       
   672 
       
   673 #ifdef QT_MAC_USE_COCOA
       
   674     // In case we _now_ process events using [NSApp run], we need to stop it to
       
   675     // ensure that:
       
   676     //    1. the QEventLoop that called us is still executing, or
       
   677     //    2. we have a modal session that needs to be spun instead.
       
   678     // In case this is a plain call to processEvents (perhaps from a loop) 
       
   679     // from the application (rather than from a QEventLoop), we delay the
       
   680     // interrupting until we/ actually enter a lower loop level (hence the
       
   681     // deffered delete of the object below):
       
   682     QtMacInterruptDispatcherHelp::interruptLater();
       
   683 #endif
       
   684 
       
   685     if (d->interrupt) {
       
   686         // We should continue to leave all recursion to processEvents until
       
   687         // processEvents is called again (e.g. from a QEventLoop that 
       
   688         // was not yet told to quit:
       
   689         interrupt();
       
   690     }
       
   691 
       
   692     return retVal;
       
   693 }
       
   694 
       
   695 void QEventDispatcherMac::wakeUp()
       
   696 {
       
   697     Q_D(QEventDispatcherMac);
       
   698     d->serialNumber.ref();
       
   699     CFRunLoopSourceSignal(d->postedEventsSource);
       
   700     CFRunLoopWakeUp(mainRunLoop());
       
   701 }
       
   702 
       
   703 void QEventDispatcherMac::flush()
       
   704 {
       
   705     if(qApp) {
       
   706         QWidgetList tlws = QApplication::topLevelWidgets();
       
   707         for(int i = 0; i < tlws.size(); i++) {
       
   708             QWidget *tlw = tlws.at(i);
       
   709             if(tlw->isVisible())
       
   710                 macWindowFlush(qt_mac_window_for(tlw));
       
   711         }
       
   712     }
       
   713 }
       
   714 
       
   715 /*****************************************************************************
       
   716   QEventDispatcherMac Implementation
       
   717  *****************************************************************************/
       
   718 MacTimerHash QEventDispatcherMacPrivate::macTimerHash;
       
   719 bool QEventDispatcherMacPrivate::blockSendPostedEvents = false;
       
   720 
       
   721 #ifdef QT_MAC_USE_COCOA
       
   722 QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack;
       
   723 bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false;
       
   724 bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false;
       
   725 NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0;
       
   726 
       
   727 int QEventDispatcherMacPrivate::activeModalSessionCount()
       
   728 {
       
   729     // Returns the number of modal sessions created
       
   730     // (and not just pushed onto the stack, pending to be created)
       
   731     int count = 0;
       
   732     for (int i=cocoaModalSessionStack.size()-1; i>=0; --i) {
       
   733         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
       
   734         if (info.session)
       
   735             ++count;
       
   736     }
       
   737     return count;
       
   738 }
       
   739 
       
   740 void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions()
       
   741 {
       
   742     // Stop all created modal session, and as such, make then
       
   743     // pending again. The next call to currentModalSession will
       
   744     // recreate the session on top again:
       
   745     int stackSize = cocoaModalSessionStack.size();
       
   746     for (int i=stackSize-1; i>=0; --i) {
       
   747         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
       
   748         if (info.session) {
       
   749             [NSApp endModalSession:info.session];
       
   750             info.session = 0;
       
   751         }
       
   752     }
       
   753     currentModalSessionCached = 0;
       
   754 }
       
   755 
       
   756 NSModalSession QEventDispatcherMacPrivate::currentModalSession()
       
   757 {
       
   758     // If we have one or more modal windows, this function will create
       
   759     // a session for each of those, and return the one for the top.
       
   760     if (currentModalSessionCached)
       
   761         return currentModalSessionCached;
       
   762 
       
   763     if (cocoaModalSessionStack.isEmpty())
       
   764         return 0;
       
   765 
       
   766     // Since this code will end up calling our Qt event handler
       
   767     // (also from beginModalSessionForWindow), we need to block
       
   768     // that to avoid side effects of events beeing delivered:
       
   769     QBoolBlocker block(blockSendPostedEvents, true);
       
   770 
       
   771     if (![NSApp isRunning]) {
       
   772         // Sadly, we need to introduce this little event flush
       
   773         // to stop dialogs from blinking/poping in front if a 
       
   774         // modal session restart was needed:
       
   775         while (NSEvent *event = [NSApp nextEventMatchingMask:0
       
   776                 untilDate:nil
       
   777                 inMode:NSDefaultRunLoopMode
       
   778                 dequeue: YES]) {
       
   779             qt_mac_send_event(0, event, 0);
       
   780         }
       
   781     }
       
   782 
       
   783     int sessionCount = cocoaModalSessionStack.size();
       
   784     for (int i=0; i<sessionCount; ++i) {
       
   785         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
       
   786         if (!info.widget)
       
   787             continue;
       
   788         if (info.widget->testAttribute(Qt::WA_DontShowOnScreen))
       
   789             continue;
       
   790         if (!info.session) {
       
   791             QMacCocoaAutoReleasePool pool;
       
   792             NSWindow *window = qt_mac_window_for(info.widget);
       
   793             if (!window)
       
   794                 continue;
       
   795             info.session = [NSApp beginModalSessionForWindow:window];
       
   796         }
       
   797         currentModalSessionCached = info.session;
       
   798     }
       
   799 
       
   800     return currentModalSessionCached;
       
   801 }
       
   802 
       
   803 static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal)
       
   804 {
       
   805     // For NSPanels (but not NSWindows, sadly), we can set the flag
       
   806     // worksWhenModal, so that they are active even when they are not modal. 
       
   807     QList<QDialog *> dialogs = widget->findChildren<QDialog *>();
       
   808     for (int i=0; i<dialogs.size(); ++i){
       
   809         NSWindow *window = qt_mac_window_for(dialogs[i]);
       
   810         if (window && [window isKindOfClass:[NSPanel class]]) {
       
   811             [static_cast<NSPanel *>(window) setWorksWhenModal:worksWhenModal];
       
   812             if (worksWhenModal && dialogs[i]->isVisible()){
       
   813                 [window orderFront:window];
       
   814             }
       
   815         }
       
   816     }
       
   817 }
       
   818 
       
   819 void QEventDispatcherMacPrivate::updateChildrenWorksWhenModal()
       
   820 {
       
   821     // Make the dialog children of the widget
       
   822     // active. And make the dialog children of
       
   823     // the previous modal dialog unactive again:
       
   824     int size = cocoaModalSessionStack.size();
       
   825     if (size > 0){
       
   826         if (QWidget *prevModal = cocoaModalSessionStack[size-1].widget)
       
   827             setChildrenWorksWhenModal(prevModal, true);
       
   828         if (size > 1){
       
   829             if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget)
       
   830                 setChildrenWorksWhenModal(prevModal, false);
       
   831         }
       
   832     }
       
   833 }
       
   834 
       
   835 void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget)
       
   836 {
       
   837     // Add a new, empty (null), NSModalSession to the stack.
       
   838     // It will become active the next time QEventDispatcher::processEvents is called.
       
   839     // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer 
       
   840     // is non-zero, and the session pointer is zero (it will become active upon a call to
       
   841     // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if
       
   842     // the widget pointer is zero, and the session pointer is non-zero (it will be fully
       
   843     // stopped in endModalSession().
       
   844     QCocoaModalSessionInfo info = {widget, 0};
       
   845     cocoaModalSessionStack.push(info);
       
   846     updateChildrenWorksWhenModal();
       
   847     currentModalSessionCached = 0;
       
   848 }
       
   849 
       
   850 void QEventDispatcherMacPrivate::endModalSession(QWidget *widget)
       
   851 {
       
   852     // Mark all sessions attached to widget as pending to be stopped. We do this
       
   853     // by setting the widget pointer to zero, but leave the session pointer.
       
   854     // We don't tell cocoa to stop any sessions just yet, because cocoa only understands
       
   855     // when we stop the _current_ modal session (which is the session on top of
       
   856     // the stack, and might not belong to 'widget'). 
       
   857     int stackSize = cocoaModalSessionStack.size();
       
   858     for (int i=stackSize-1; i>=0; --i) {
       
   859         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
       
   860         if (info.widget == widget)
       
   861             info.widget = 0;
       
   862     }
       
   863 
       
   864     // Now we stop, and remove, all sessions marked as pending
       
   865     // to be stopped on _top_ of the stack, if any:
       
   866     bool needToInterruptEventDispatcher = false;
       
   867     bool needToUpdateChildrenWorksWhenModal = false;
       
   868 
       
   869     for (int i=stackSize-1; i>=0; --i) {
       
   870         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
       
   871         if (info.widget)
       
   872             break;
       
   873         cocoaModalSessionStack.remove(i);
       
   874         needToUpdateChildrenWorksWhenModal = true;
       
   875         currentModalSessionCached = 0;
       
   876         if (info.session) {
       
   877             [NSApp endModalSession:info.session];
       
   878             needToInterruptEventDispatcher = true;
       
   879         }
       
   880     }
       
   881 
       
   882     if (needToUpdateChildrenWorksWhenModal)
       
   883         updateChildrenWorksWhenModal();
       
   884     if (needToInterruptEventDispatcher)
       
   885         QEventDispatcherMac::instance()->interrupt();
       
   886 }
       
   887 
       
   888 #endif
       
   889 
       
   890 QEventDispatcherMacPrivate::QEventDispatcherMacPrivate()
       
   891     : interrupt(false)
       
   892 {
       
   893 }
       
   894 
       
   895 QEventDispatcherMac::QEventDispatcherMac(QObject *parent)
       
   896     : QAbstractEventDispatcher(*new QEventDispatcherMacPrivate, parent)
       
   897 {
       
   898     Q_D(QEventDispatcherMac);
       
   899     CFRunLoopSourceContext context;
       
   900     bzero(&context, sizeof(CFRunLoopSourceContext));
       
   901     context.info = d;
       
   902     context.equal = QEventDispatcherMacPrivate::postedEventSourceEqualCallback;
       
   903     context.perform = QEventDispatcherMacPrivate::postedEventsSourcePerformCallback;
       
   904     d->postedEventsSource = CFRunLoopSourceCreate(0, 0, &context);
       
   905     Q_ASSERT(d->postedEventsSource);
       
   906     CFRunLoopAddSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes);
       
   907 
       
   908     CFRunLoopObserverContext observerContext;
       
   909     bzero(&observerContext, sizeof(CFRunLoopObserverContext));
       
   910     observerContext.info = this;
       
   911     d->waitingObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
       
   912                                                  kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting,
       
   913                                                  true, 0,
       
   914                                                  QEventDispatcherMacPrivate::waitingObserverCallback,
       
   915                                                  &observerContext);
       
   916     CFRunLoopAddObserver(mainRunLoop(), d->waitingObserver, kCFRunLoopCommonModes);
       
   917 
       
   918     /* The first cycle in the loop adds the source and the events of the source
       
   919        are not processed.
       
   920        We use an observer to process the posted events for the first
       
   921        execution of the loop. */
       
   922     CFRunLoopObserverContext firstTimeObserverContext;
       
   923     bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext));
       
   924     firstTimeObserverContext.info = d;
       
   925     d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
       
   926                                                    kCFRunLoopEntry,
       
   927                                                    /* repeats = */ false,
       
   928                                                    0,
       
   929                                                    QEventDispatcherMacPrivate::firstLoopEntry,
       
   930                                                    &firstTimeObserverContext);
       
   931     CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, kCFRunLoopCommonModes);
       
   932 }
       
   933 
       
   934 void QEventDispatcherMacPrivate::waitingObserverCallback(CFRunLoopObserverRef,
       
   935                                                           CFRunLoopActivity activity, void *info)
       
   936 {
       
   937     if (activity == kCFRunLoopBeforeWaiting)
       
   938         emit static_cast<QEventDispatcherMac*>(info)->aboutToBlock();
       
   939     else
       
   940         emit static_cast<QEventDispatcherMac*>(info)->awake();
       
   941 }
       
   942 
       
   943 Boolean QEventDispatcherMacPrivate::postedEventSourceEqualCallback(const void *info1, const void *info2)
       
   944 {
       
   945     return info1 == info2;
       
   946 }
       
   947 
       
   948 inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents)
       
   949 {
       
   950     if (blockSendPostedEvents || d->interrupt) {
       
   951         CFRunLoopSourceSignal(d->postedEventsSource);
       
   952     } else {
       
   953         if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) {
       
   954             d->lastSerial = d->serialNumber;
       
   955             QApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
       
   956         }
       
   957     }
       
   958 }
       
   959 
       
   960 void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref,
       
   961                                                 CFRunLoopActivity activity,
       
   962                                                 void *info)
       
   963 {
       
   964     Q_UNUSED(ref);
       
   965     Q_UNUSED(activity);
       
   966     processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents);
       
   967 }
       
   968 
       
   969 void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info)
       
   970 {
       
   971     processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents);
       
   972 }
       
   973 
       
   974 void QEventDispatcherMac::interrupt()
       
   975 {
       
   976     Q_D(QEventDispatcherMac);
       
   977     d->interrupt = true;
       
   978     wakeUp();
       
   979 
       
   980 #ifndef QT_MAC_USE_COCOA
       
   981     CFRunLoopStop(mainRunLoop());
       
   982 #else
       
   983     QMacCocoaAutoReleasePool pool;
       
   984     // In case we wait for more events inside
       
   985     // processEvents (or NSApp run), post a dummy to wake it up:
       
   986     static const short NSAppShouldStopForQt = SHRT_MAX;
       
   987     [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint
       
   988         modifierFlags:0 timestamp:0. windowNumber:0 context:0
       
   989         subtype:NSAppShouldStopForQt data1:0 data2:0] atStart:NO];
       
   990 
       
   991     if (d->activeModalSessionCount() == 0) {
       
   992         // We should only stop NSApp if we actually started it (and
       
   993         // not some 3rd party application, e.g. if we are a plugin).
       
   994         if (d->nsAppRunCalledByQt)
       
   995             [NSApp stop:NSApp];
       
   996     }
       
   997 #endif
       
   998 }
       
   999 
       
  1000 QEventDispatcherMac::~QEventDispatcherMac()
       
  1001 {
       
  1002     Q_D(QEventDispatcherMac);
       
  1003     //timer cleanup
       
  1004     MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin();
       
  1005     while (it != QEventDispatcherMacPrivate::macTimerHash.end()) {
       
  1006         MacTimerInfo *t = it.value();
       
  1007         if (t->runLoopTimer) {
       
  1008             CFRunLoopTimerInvalidate(t->runLoopTimer);
       
  1009             CFRelease(t->runLoopTimer);
       
  1010         }
       
  1011         delete t;
       
  1012         ++it;
       
  1013     }
       
  1014     QEventDispatcherMacPrivate::macTimerHash.clear();
       
  1015 
       
  1016     // Remove CFSockets from the runloop.
       
  1017     for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) {
       
  1018         MacSocketInfo *socketInfo = (*it);
       
  1019         if (CFSocketIsValid(socketInfo->socket)) {
       
  1020             qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
       
  1021             CFRunLoopSourceInvalidate(socketInfo->runloop);
       
  1022             CFRelease(socketInfo->runloop);
       
  1023             CFSocketInvalidate(socketInfo->socket);
       
  1024             CFRelease(socketInfo->socket);
       
  1025         }
       
  1026     }
       
  1027     CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes);
       
  1028     CFRelease(d->postedEventsSource);
       
  1029 
       
  1030     CFRunLoopObserverInvalidate(d->waitingObserver);
       
  1031     CFRelease(d->waitingObserver);
       
  1032 
       
  1033     CFRunLoopObserverInvalidate(d->firstTimeObserver);
       
  1034     CFRelease(d->firstTimeObserver);
       
  1035 }
       
  1036 
       
  1037 /////////////////////////////////////////////////////////////////////////////
       
  1038 
       
  1039 #ifdef QT_MAC_USE_COCOA
       
  1040 
       
  1041 QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0;
       
  1042 
       
  1043 QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false)
       
  1044 {
       
  1045     // This is the whole point of encapsulation this code
       
  1046     // inside a class; we can make the code (inside destructor)
       
  1047     // execute on lower loop level:
       
  1048     deleteLater();
       
  1049 }
       
  1050 
       
  1051 QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp()
       
  1052 {
       
  1053     if (cancelled)
       
  1054         return;
       
  1055 
       
  1056     instance = 0;
       
  1057 
       
  1058     if (QEventDispatcherMacPrivate::currentExecIsNSAppRun) {
       
  1059         int activeCount = QEventDispatcherMacPrivate::activeModalSessionCount();
       
  1060         if (activeCount > 0) {
       
  1061             // The problem we now have hit: [NSApp stop] will not stop NSApp
       
  1062             // if a session is active; it will stop the session instead.
       
  1063             // So to stop NSApp, we need to temporarily stop all the
       
  1064             // sessions, then stop NSApp, then restart the session on top again.
       
  1065             // We need to do this to ensure that we're not stuck inside
       
  1066             // [NSApp run] when we really should be running a modal session:
       
  1067             QEventDispatcherMacPrivate::temporarilyStopAllModalSessions();
       
  1068         }
       
  1069     }
       
  1070     // Always interrupt once more in case the modal session stack changed
       
  1071     // while processEvents was called manually from within the application:
       
  1072     QEventDispatcherMac::instance()->interrupt();
       
  1073 }
       
  1074 
       
  1075 void QtMacInterruptDispatcherHelp::interruptLater() {
       
  1076     if (instance) {
       
  1077         instance->cancelled = true;
       
  1078         delete instance;
       
  1079     }
       
  1080     instance = new QtMacInterruptDispatcherHelp;
       
  1081 }
       
  1082 
       
  1083 #endif
       
  1084 
       
  1085 QT_END_NAMESPACE