src/gui/kernel/qeventdispatcher_mac.mm
changeset 30 5dc02b23752f
parent 18 2f34d5167611
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
    88 #include <private/qcocoaapplication_mac_p.h>
    88 #include <private/qcocoaapplication_mac_p.h>
    89 #include "private/qt_cocoa_helpers_mac_p.h"
    89 #include "private/qt_cocoa_helpers_mac_p.h"
    90 
    90 
    91 #ifndef QT_NO_THREAD
    91 #ifndef QT_NO_THREAD
    92 #  include "qmutex.h"
    92 #  include "qmutex.h"
       
    93 #endif
    93 
    94 
    94 QT_BEGIN_NAMESPACE
    95 QT_BEGIN_NAMESPACE
    95 
    96 
    96 QT_USE_NAMESPACE
    97 QT_USE_NAMESPACE
    97 #endif
       
    98 
    98 
    99 /*****************************************************************************
    99 /*****************************************************************************
   100   Externals
   100   Externals
   101  *****************************************************************************/
   101  *****************************************************************************/
   102 extern void qt_event_request_timer(MacTimerInfo *); //qapplication_mac.cpp
   102 extern void qt_event_request_timer(MacTimerInfo *); //qapplication_mac.cpp
   546 
   546 
   547 bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags)
   547 bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags)
   548 {
   548 {
   549     Q_D(QEventDispatcherMac);
   549     Q_D(QEventDispatcherMac);
   550     d->interrupt = false;
   550     d->interrupt = false;
       
   551 
       
   552 #ifdef QT_MAC_USE_COCOA
       
   553     bool interruptLater = false;
       
   554     QtMacInterruptDispatcherHelp::cancelInterruptLater();
       
   555 #endif
       
   556 
   551     // In case we end up recursing while we now process events, make sure
   557     // In case we end up recursing while we now process events, make sure
   552     // that we send remaining posted Qt events before this call returns:
   558     // that we send remaining posted Qt events before this call returns:
   553     wakeUp();
   559     wakeUp();
   554     emit awake();
   560     emit awake();
   555 
   561 
   560 
   566 
   561 #ifdef QT_MAC_USE_COCOA
   567 #ifdef QT_MAC_USE_COCOA
   562         QMacCocoaAutoReleasePool pool;
   568         QMacCocoaAutoReleasePool pool;
   563         NSEvent* event = 0;
   569         NSEvent* event = 0;
   564 
   570 
   565         // If Qt is used as a plugin, or just added into a native cocoa
   571         // First, send all previously excluded input events, if any:
   566         // application, we should not run or stop NSApplication;
   572         if (!(flags & QEventLoop::ExcludeUserInputEvents)) {
   567         // This will be done from outside Qt.
   573             while (!d->queuedUserInputEvents.isEmpty()) {
   568         // And if processEvents is called manually (rather than from QEventLoop), we
   574                 event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
   569         // cannot enter a tight loop and block the call, but instead return after one flush:
   575                 if (!filterEvent(event)) {
   570         bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning];
   576                     qt_mac_send_event(flags, event, 0);
   571         bool canExec_Qt = flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec;
   577                     retVal = true;
       
   578                 }
       
   579                 [event release];
       
   580             }
       
   581         }
       
   582 
       
   583         // If Qt is used as a plugin, or as an extension in a native cocoa
       
   584         // application, we should not run or stop NSApplication; This will be
       
   585         // done from the application itself. And if processEvents is called
       
   586         // manually (rather than from a QEventLoop), we cannot enter a tight
       
   587         // loop and block this call, but instead we need to return after one flush:
       
   588         const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning];
       
   589         const bool canExec_Qt = flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec;
   572 
   590 
   573         if (canExec_Qt && canExec_3rdParty) {
   591         if (canExec_Qt && canExec_3rdParty) {
   574             // We can use exec-mode, meaning that we can stay in a tight loop until
   592             // We can use exec-mode, meaning that we can stay in a tight loop until
   575             // interrupted. This is mostly an optimization, but it also allow us
   593             // interrupted. This is mostly an optimization, but it allow us to use
   576             // to use [NSApp run], which is the recommended way of running applications
   594             // [NSApp run], which is the normal code path for cocoa applications.
   577             // in cocoa. [NSApp run] should be called at least once for any cocoa app.
       
   578             if (NSModalSession session = d->currentModalSession()) {
   595             if (NSModalSession session = d->currentModalSession()) {
   579                 QBoolBlocker execGuard(d->currentExecIsNSAppRun, false);
   596                 QBoolBlocker execGuard(d->currentExecIsNSAppRun, false);
   580                 while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
   597                 while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
   581                     qt_mac_waitForMoreModalSessionEvents();
   598                     qt_mac_waitForMoreModalSessionEvents();
       
   599 
   582                 if (!d->interrupt && session == d->currentModalSessionCached) {
   600                 if (!d->interrupt && session == d->currentModalSessionCached) {
   583                     // INVARIANT: Someone called e.g. [NSApp stopModal:] from outside the event
   601                     // Someone called [NSApp stopModal:] from outside the event
   584                     // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
   602                     // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
   585                     // 'session' as well. As a result, we need to restart all internal sessions:
   603                     // 'session' as well. As a result, we need to restart all internal sessions:
   586                     d->temporarilyStopAllModalSessions();
   604                     d->temporarilyStopAllModalSessions();
   587                 }
   605                 }
   588             } else {
   606             } else {
   589                 d->nsAppRunCalledByQt = true;
   607                 d->nsAppRunCalledByQt = true;
   590                 QBoolBlocker execGuard(d->currentExecIsNSAppRun, true);
   608                 QBoolBlocker execGuard(d->currentExecIsNSAppRun, true);
   591                 [NSApp run];
   609                 [NSApp run];
   592             }
   610             }
   593             retVal = true;
   611             retVal = true;
   594         } else do {
   612         } else {
   595             // INVARIANT: We cannot block the thread (and run in a tight loop).
   613             // We cannot block the thread (and run in a tight loop).
   596             // Instead we will process all current pending events and return.
   614             // Instead we will process all current pending events and return.
   597             bool mustRelease = false;
   615             d->ensureNSAppInitialized();
   598 
   616             if (NSModalSession session = d->currentModalSession()) {
   599             if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
   617                 if (flags & QEventLoop::WaitForMoreEvents)
   600                 // Process a pending user input event
   618                     qt_mac_waitForMoreModalSessionEvents();
   601                 mustRelease = true;
   619                 NSInteger status = [NSApp runModalSession:session];
   602                 event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
   620                 if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) {
   603             } else {
   621                     // INVARIANT: Someone called [NSApp stopModal:] from outside the event
   604                 if (NSModalSession session = d->currentModalSession()) {
   622                     // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
   605                     if (flags & QEventLoop::WaitForMoreEvents)
   623                     // 'session' as well. As a result, we need to restart all internal sessions:
   606                         qt_mac_waitForMoreModalSessionEvents();
   624                     d->temporarilyStopAllModalSessions();
   607                     NSInteger status = [NSApp runModalSession:session];
   625                 }
   608                     if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) {
   626                 retVal = true;
   609                         // INVARIANT: Someone called e.g. [NSApp stopModal:] from outside the event
   627             } else do {
   610                         // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
   628                 event = [NSApp nextEventMatchingMask:NSAnyEventMask
   611                         // 'session' as well. As a result, we need to restart all internal sessions:
   629                     untilDate:nil
   612                         d->temporarilyStopAllModalSessions();
   630                     inMode:NSDefaultRunLoopMode
   613                     }
   631                     dequeue: YES];
   614                     retVal = true;
   632 
   615                     break;
   633                 if (event) {
   616                 } else {
   634                     if (flags & QEventLoop::ExcludeUserInputEvents) {
   617                     event = [NSApp nextEventMatchingMask:NSAnyEventMask
   635                         if (IsMouseOrKeyEvent(event)) {
   618                         untilDate:nil
   636                             [event retain];
   619                         inMode:NSDefaultRunLoopMode
   637                             d->queuedUserInputEvents.append(event);
   620                         dequeue: YES];
   638                             continue;
   621 
       
   622                     if (event != nil) {
       
   623                         if (flags & QEventLoop::ExcludeUserInputEvents) {
       
   624                             if (IsMouseOrKeyEvent(event)) {
       
   625                                 // retain event here?
       
   626                                 [event retain];
       
   627                                 d->queuedUserInputEvents.append(event);
       
   628                                 continue;
       
   629                             }
       
   630                         }
   639                         }
   631                     }
   640                     }
       
   641                     if (!filterEvent(event) && qt_mac_send_event(flags, event, 0))
       
   642                         retVal = true;
   632                 }
   643                 }
   633             }
   644             } while (!d->interrupt && event != nil);
   634             if (event) {
   645 
   635                 if (!filterEvent(event) && qt_mac_send_event(flags, event, 0))
   646             // Since the window that holds modality might have changed while processing
   636                     retVal = true;
   647             // events, we we need to interrupt when we return back the previous process
   637                 if (mustRelease)
   648             // event recursion to ensure that we spin the correct modal session.
   638                     [event release];
   649             // We do the interruptLater at the end of the function to ensure that we don't
   639             }
   650             // disturb the 'wait for more events' below (as deleteLater will post an event):
   640         } while(!d->interrupt && event != nil);
   651             interruptLater = true;
   641 
   652         }
   642 #else
   653 #else
   643         do {
   654         do {
   644             EventRef event;
   655             EventRef event;
   645             if (!(flags & QEventLoop::ExcludeUserInputEvents)
   656             if (!(flags & QEventLoop::ExcludeUserInputEvents)
   646                     && !d->queuedUserInputEvents.isEmpty()) {
   657                     && !d->queuedUserInputEvents.isEmpty()) {
   688             // Leave the function:
   699             // Leave the function:
   689             break;
   700             break;
   690         }
   701         }
   691     }
   702     }
   692 
   703 
       
   704     // If we're interrupted, we need to interrupt the _current_
       
   705     // recursion as well to check if it is  still supposed to be
       
   706     // executing. This way we wind down the stack until we land
       
   707     // on a recursion that again calls processEvents (typically
       
   708     // from QEventLoop), and set interrupt to false:
       
   709     if (d->interrupt)
       
   710         interrupt();
       
   711 
   693 #ifdef QT_MAC_USE_COCOA
   712 #ifdef QT_MAC_USE_COCOA
   694     // In case we _now_ process events using [NSApp run], we need to stop it to
   713     if (interruptLater)
   695     // ensure that:
   714         QtMacInterruptDispatcherHelp::interruptLater();
   696     //    1. the QEventLoop that called us is still executing, or
   715 #endif
   697     //    2. we have a modal session that needs to be spun instead.
       
   698     // In case this is a plain call to processEvents (perhaps from a loop) 
       
   699     // from the application (rather than from a QEventLoop), we delay the
       
   700     // interrupting until we/ actually enter a lower loop level (hence the
       
   701     // deffered delete of the object below):
       
   702     QtMacInterruptDispatcherHelp::interruptLater();
       
   703 #endif
       
   704 
       
   705     if (d->interrupt) {
       
   706         // We should continue to leave all recursion to processEvents until
       
   707         // processEvents is called again (e.g. from a QEventLoop that 
       
   708         // was not yet told to quit:
       
   709         interrupt();
       
   710     }
       
   711 
   716 
   712     return retVal;
   717     return retVal;
   713 }
   718 }
   714 
   719 
   715 void QEventDispatcherMac::wakeUp()
   720 void QEventDispatcherMac::wakeUp()
   735 /*****************************************************************************
   740 /*****************************************************************************
   736   QEventDispatcherMac Implementation
   741   QEventDispatcherMac Implementation
   737  *****************************************************************************/
   742  *****************************************************************************/
   738 MacTimerHash QEventDispatcherMacPrivate::macTimerHash;
   743 MacTimerHash QEventDispatcherMacPrivate::macTimerHash;
   739 bool QEventDispatcherMacPrivate::blockSendPostedEvents = false;
   744 bool QEventDispatcherMacPrivate::blockSendPostedEvents = false;
       
   745 bool QEventDispatcherMacPrivate::interrupt = false;
   740 
   746 
   741 #ifdef QT_MAC_USE_COCOA
   747 #ifdef QT_MAC_USE_COCOA
   742 QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack;
   748 QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack;
   743 bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false;
   749 bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false;
   744 bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false;
   750 bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false;
       
   751 bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false;
   745 NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0;
   752 NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0;
   746 
   753 
   747 int QEventDispatcherMacPrivate::activeModalSessionCount()
   754 void QEventDispatcherMacPrivate::ensureNSAppInitialized()
   748 {
   755 {
   749     // Returns the number of modal sessions created
   756     // Some elements in Cocoa require NSApplication to be running before
   750     // (and not just pushed onto the stack, pending to be created)
   757     // they get fully initialized, in particular the menu bar. This
   751     int count = 0;
   758     // function is intended for cases where a dialog is told to execute before
   752     for (int i=cocoaModalSessionStack.size()-1; i>=0; --i) {
   759     // QApplication::exec is called, or the application spins the events loop
   753         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
   760     // manually rather than calling QApplication:exec.
   754         if (info.session)
   761     // The function makes sure that NSApplication starts running, but stops
   755             ++count;
   762     // it again as soon as the send posted events callback is called. That way
   756     }
   763     // we let Cocoa finish the initialization it seems to need. We'll only
   757     return count;
   764     // apply this trick at most once for any application, and we avoid doing it
       
   765     // for the common case where main just starts QApplication::exec.
       
   766     if (nsAppRunCalledByQt || [NSApp isRunning])
       
   767         return;
       
   768     nsAppRunCalledByQt = true;
       
   769     QBoolBlocker block1(interrupt, true);
       
   770     QBoolBlocker block2(currentExecIsNSAppRun, true);
       
   771     [NSApp run];
   758 }
   772 }
   759 
   773 
   760 void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions()
   774 void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions()
   761 {
   775 {
   762     // Stop all created modal session, and as such, make then
   776     // Flush, and Stop, all created modal session, and as
   763     // pending again. The next call to currentModalSession will
   777     // such, make them pending again. The next call to
   764     // recreate the session on top again:
   778     // currentModalSession will recreate them again. The
       
   779     // reason to stop all session like this is that otherwise
       
   780     // a call [NSApp stop] would not stop NSApp, but rather
       
   781     // the current modal session. So if we need to stop NSApp
       
   782     // we need to stop all the modal session first. To avoid changing
       
   783     // the stacking order of the windows while doing so, we put
       
   784     // up a block that is used in QCocoaWindow and QCocoaPanel:
   765     int stackSize = cocoaModalSessionStack.size();
   785     int stackSize = cocoaModalSessionStack.size();
   766     for (int i=stackSize-1; i>=0; --i) {
   786     for (int i=stackSize-1; i>=0; --i) {
   767         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
   787         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
   768         if (info.session) {
   788         if (info.session) {
   769             [NSApp endModalSession:info.session];
   789             [NSApp endModalSession:info.session];
   780     if (currentModalSessionCached)
   800     if (currentModalSessionCached)
   781         return currentModalSessionCached;
   801         return currentModalSessionCached;
   782 
   802 
   783     if (cocoaModalSessionStack.isEmpty())
   803     if (cocoaModalSessionStack.isEmpty())
   784         return 0;
   804         return 0;
   785 
       
   786     // Since this code will end up calling our Qt event handler
       
   787     // (also from beginModalSessionForWindow), we need to block
       
   788     // that to avoid side effects of events beeing delivered:
       
   789     QBoolBlocker block(blockSendPostedEvents, true);
       
   790 
       
   791     if (![NSApp isRunning]) {
       
   792         // Sadly, we need to introduce this little event flush
       
   793         // to stop dialogs from blinking/poping in front if a 
       
   794         // modal session restart was needed:
       
   795         while (NSEvent *event = [NSApp nextEventMatchingMask:0
       
   796                 untilDate:nil
       
   797                 inMode:NSDefaultRunLoopMode
       
   798                 dequeue: YES]) {
       
   799             qt_mac_send_event(0, event, 0);
       
   800         }
       
   801     }
       
   802 
   805 
   803     int sessionCount = cocoaModalSessionStack.size();
   806     int sessionCount = cocoaModalSessionStack.size();
   804     for (int i=0; i<sessionCount; ++i) {
   807     for (int i=0; i<sessionCount; ++i) {
   805         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
   808         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
   806         if (!info.widget)
   809         if (!info.widget)
   810         if (!info.session) {
   813         if (!info.session) {
   811             QMacCocoaAutoReleasePool pool;
   814             QMacCocoaAutoReleasePool pool;
   812             NSWindow *window = qt_mac_window_for(info.widget);
   815             NSWindow *window = qt_mac_window_for(info.widget);
   813             if (!window)
   816             if (!window)
   814                 continue;
   817                 continue;
       
   818 
       
   819             ensureNSAppInitialized();
       
   820             QBoolBlocker block1(blockSendPostedEvents, true);
       
   821             info.nswindow = window;
       
   822             [(NSWindow*) info.nswindow retain];
       
   823             // When creating a modal session cocoa will rearrange the windows.
       
   824             // In order to avoid windows to be put behind another we need to
       
   825             // keep the window level.
       
   826             int level = [window level];
   815             info.session = [NSApp beginModalSessionForWindow:window];
   827             info.session = [NSApp beginModalSessionForWindow:window];
       
   828             [window setLevel:level];
   816         }
   829         }
   817         currentModalSessionCached = info.session;
   830         currentModalSessionCached = info.session;
   818     }
   831     }
   819 
       
   820     return currentModalSessionCached;
   832     return currentModalSessionCached;
   821 }
   833 }
   822 
   834 
   823 static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal)
   835 static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal)
   824 {
   836 {
   850                 setChildrenWorksWhenModal(prevModal, false);
   862                 setChildrenWorksWhenModal(prevModal, false);
   851         }
   863         }
   852     }
   864     }
   853 }
   865 }
   854 
   866 
       
   867 void QEventDispatcherMacPrivate::cleanupModalSessions()
       
   868 {
       
   869     // Go through the list of modal sessions, and end those
       
   870     // that no longer has a widget assosiated; no widget means
       
   871     // the the session has logically ended. The reason we wait like
       
   872     // this to actually end the sessions for real (rather than at the
       
   873     // point they were marked as stopped), is that ending a session
       
   874     // when no other session runs below it on the stack will make cocoa
       
   875     // drop some events on the floor. 
       
   876     QMacCocoaAutoReleasePool pool;
       
   877     int stackSize = cocoaModalSessionStack.size();
       
   878 
       
   879     for (int i=stackSize-1; i>=0; --i) {
       
   880         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
       
   881         if (info.widget) {
       
   882             currentModalSessionCached = info.session;
       
   883             break;
       
   884         }
       
   885         cocoaModalSessionStack.remove(i);
       
   886         currentModalSessionCached = 0;
       
   887         if (info.session) {
       
   888             [NSApp endModalSession:info.session];
       
   889             [(NSWindow *)info.nswindow release];
       
   890         }
       
   891     }
       
   892 
       
   893     updateChildrenWorksWhenModal();
       
   894     cleanupModalSessionsNeeded = false;
       
   895 }
       
   896 
   855 void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget)
   897 void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget)
   856 {
   898 {
   857     // Add a new, empty (null), NSModalSession to the stack.
   899     // Add a new, empty (null), NSModalSession to the stack.
   858     // It will become active the next time QEventDispatcher::processEvents is called.
   900     // It will become active the next time QEventDispatcher::processEvents is called.
   859     // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer 
   901     // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer 
   860     // is non-zero, and the session pointer is zero (it will become active upon a call to
   902     // is non-zero, and the session pointer is zero (it will become active upon a call to
   861     // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if
   903     // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if
   862     // the widget pointer is zero, and the session pointer is non-zero (it will be fully
   904     // the widget pointer is zero, and the session pointer is non-zero (it will be fully
   863     // stopped in endModalSession().
   905     // stopped in cleanupModalSessions()).
   864     QCocoaModalSessionInfo info = {widget, 0};
   906     QCocoaModalSessionInfo info = {widget, 0, 0};
   865     cocoaModalSessionStack.push(info);
   907     cocoaModalSessionStack.push(info);
   866     updateChildrenWorksWhenModal();
   908     updateChildrenWorksWhenModal();
   867     currentModalSessionCached = 0;
   909     currentModalSessionCached = 0;
   868 }
   910 }
   869 
   911 
   875     // when we stop the _current_ modal session (which is the session on top of
   917     // when we stop the _current_ modal session (which is the session on top of
   876     // the stack, and might not belong to 'widget'). 
   918     // the stack, and might not belong to 'widget'). 
   877     int stackSize = cocoaModalSessionStack.size();
   919     int stackSize = cocoaModalSessionStack.size();
   878     for (int i=stackSize-1; i>=0; --i) {
   920     for (int i=stackSize-1; i>=0; --i) {
   879         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
   921         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
   880         if (info.widget == widget)
   922         if (info.widget == widget) {
   881             info.widget = 0;
   923             info.widget = 0;
   882     }
   924             if (i == stackSize-1) {
   883 
   925                 // The top sessions ended. Interrupt the event dispatcher
   884     // Now we stop, and remove, all sessions marked as pending
   926                 // to start spinning the correct session immidiatly: 
   885     // to be stopped on _top_ of the stack, if any:
   927                 cleanupModalSessionsNeeded = true;
   886     bool needToInterruptEventDispatcher = false;
   928                 QEventDispatcherMac::instance()->interrupt();
   887     bool needToUpdateChildrenWorksWhenModal = false;
   929             }
   888 
   930         }
   889     for (int i=stackSize-1; i>=0; --i) {
   931     }
   890         QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
       
   891         if (info.widget)
       
   892             break;
       
   893         cocoaModalSessionStack.remove(i);
       
   894         needToUpdateChildrenWorksWhenModal = true;
       
   895         currentModalSessionCached = 0;
       
   896         if (info.session) {
       
   897             [NSApp endModalSession:info.session];
       
   898             needToInterruptEventDispatcher = true;
       
   899         }
       
   900     }
       
   901 
       
   902     if (needToUpdateChildrenWorksWhenModal)
       
   903         updateChildrenWorksWhenModal();
       
   904     if (needToInterruptEventDispatcher)
       
   905         QEventDispatcherMac::instance()->interrupt();
       
   906 }
   932 }
   907 
   933 
   908 #endif
   934 #endif
   909 
   935 
   910 QEventDispatcherMacPrivate::QEventDispatcherMacPrivate()
   936 QEventDispatcherMacPrivate::QEventDispatcherMacPrivate()
   911     : interrupt(false)
       
   912 {
   937 {
   913 }
   938 }
   914 
   939 
   915 QEventDispatcherMac::QEventDispatcherMac(QObject *parent)
   940 QEventDispatcherMac::QEventDispatcherMac(QObject *parent)
   916     : QAbstractEventDispatcher(*new QEventDispatcherMacPrivate, parent)
   941     : QAbstractEventDispatcher(*new QEventDispatcherMacPrivate, parent)
   965     return info1 == info2;
   990     return info1 == info2;
   966 }
   991 }
   967 
   992 
   968 inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents)
   993 inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents)
   969 {
   994 {
   970     if (blockSendPostedEvents || d->interrupt) {
   995     if (blockSendPostedEvents) {
       
   996         // We're told to not send posted events (because the event dispatcher
       
   997         // is currently working on setting up the correct session to run). But
       
   998         // we still need to make sure that we don't fall asleep until pending events
       
   999         // are sendt, so we just signal this need, and return:
   971         CFRunLoopSourceSignal(d->postedEventsSource);
  1000         CFRunLoopSourceSignal(d->postedEventsSource);
   972     } else {
  1001         return;
   973         if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) {
  1002     }
   974             d->lastSerial = d->serialNumber;
  1003 
   975             QApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
  1004 #ifdef QT_MAC_USE_COCOA
   976         }
  1005     if (d->cleanupModalSessionsNeeded)
       
  1006         d->cleanupModalSessions();
       
  1007 #endif
       
  1008 
       
  1009     if (d->interrupt) {
       
  1010 #ifdef QT_MAC_USE_COCOA
       
  1011         if (d->currentExecIsNSAppRun) {
       
  1012             // The event dispatcher has been interrupted. But since
       
  1013             // [NSApplication run] is running the event loop, we
       
  1014             // delayed stopping it until now (to let cocoa process 
       
  1015             // pending cocoa events first).
       
  1016             if (d->currentModalSessionCached)
       
  1017                 d->temporarilyStopAllModalSessions();
       
  1018             [NSApp stop:NSApp];
       
  1019             d->cancelWaitForMoreEvents();
       
  1020         }
       
  1021 #endif
       
  1022         return;
       
  1023     }
       
  1024 
       
  1025     if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) {
       
  1026         d->lastSerial = d->serialNumber;
       
  1027         QApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
   977     }
  1028     }
   978 }
  1029 }
   979 
  1030 
   980 void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref,
  1031 void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref,
   981                                                 CFRunLoopActivity activity,
  1032                                                 CFRunLoopActivity activity,
   982                                                 void *info)
  1033                                                 void *info)
   983 {
  1034 {
   984     Q_UNUSED(ref);
  1035     Q_UNUSED(ref);
   985     Q_UNUSED(activity);
  1036     Q_UNUSED(activity);
       
  1037 #ifdef QT_MAC_USE_COCOA
       
  1038     QApplicationPrivate::qt_initAfterNSAppStarted();
       
  1039 #endif
   986     processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents);
  1040     processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents);
   987 }
  1041 }
   988 
  1042 
   989 void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info)
  1043 void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info)
   990 {
  1044 {
   991     processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents);
  1045     processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents);
   992 }
  1046 }
       
  1047 
       
  1048 #ifdef QT_MAC_USE_COCOA
       
  1049 void QEventDispatcherMacPrivate::cancelWaitForMoreEvents()
       
  1050 {
       
  1051     // In case the event dispatcher is waiting for more
       
  1052     // events somewhere, we post a dummy event to wake it up:
       
  1053     QMacCocoaAutoReleasePool pool;
       
  1054     [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint
       
  1055         modifierFlags:0 timestamp:0. windowNumber:0 context:0
       
  1056         subtype:QtCocoaEventSubTypeWakeup data1:0 data2:0] atStart:NO];
       
  1057 }
       
  1058 #endif
   993 
  1059 
   994 void QEventDispatcherMac::interrupt()
  1060 void QEventDispatcherMac::interrupt()
   995 {
  1061 {
   996     Q_D(QEventDispatcherMac);
  1062     Q_D(QEventDispatcherMac);
   997     d->interrupt = true;
  1063     d->interrupt = true;
   998     wakeUp();
  1064     wakeUp();
   999 
  1065 
  1000 #ifndef QT_MAC_USE_COCOA
  1066 #ifndef QT_MAC_USE_COCOA
  1001     CFRunLoopStop(mainRunLoop());
  1067     CFRunLoopStop(mainRunLoop());
  1002 #else
  1068 #else
  1003     QMacCocoaAutoReleasePool pool;
  1069     // We do nothing more here than setting d->interrupt = true, and
  1004     // In case we wait for more events inside
  1070     // poke the event loop if it is sleeping. Actually stopping
  1005     // processEvents (or NSApp run), post a dummy to wake it up:
  1071     // NSApp, or the current modal session, is done inside the send
  1006     static const short NSAppShouldStopForQt = SHRT_MAX;
  1072     // posted events callback. We do this to ensure that all current pending
  1007     [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint
  1073     // cocoa events gets delivered before we stop. Otherwise, if we now stop
  1008         modifierFlags:0 timestamp:0. windowNumber:0 context:0
  1074     // the last event loop recursion, cocoa will just drop pending posted
  1009         subtype:NSAppShouldStopForQt data1:0 data2:0] atStart:NO];
  1075     // events on the floor before we get a chance to reestablish a new session.
  1010 
  1076     d->cancelWaitForMoreEvents();
  1011     if (d->activeModalSessionCount() == 0) {
       
  1012         // We should only stop NSApp if we actually started it (and
       
  1013         // not some 3rd party application, e.g. if we are a plugin).
       
  1014         if (d->nsAppRunCalledByQt)
       
  1015             [NSApp stop:NSApp];
       
  1016     }
       
  1017 #endif
  1077 #endif
  1018 }
  1078 }
  1019 
  1079 
  1020 QEventDispatcherMac::~QEventDispatcherMac()
  1080 QEventDispatcherMac::~QEventDispatcherMac()
  1021 {
  1081 {
  1052 
  1112 
  1053     CFRunLoopObserverInvalidate(d->firstTimeObserver);
  1113     CFRunLoopObserverInvalidate(d->firstTimeObserver);
  1054     CFRelease(d->firstTimeObserver);
  1114     CFRelease(d->firstTimeObserver);
  1055 }
  1115 }
  1056 
  1116 
  1057 /////////////////////////////////////////////////////////////////////////////
       
  1058 
       
  1059 #ifdef QT_MAC_USE_COCOA
  1117 #ifdef QT_MAC_USE_COCOA
  1060 
  1118 
  1061 QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0;
  1119 QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0;
  1062 
  1120 
  1063 QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false)
  1121 QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false)
  1064 {
  1122 {
  1065     // This is the whole point of encapsulation this code
  1123     // The whole point of this class is that we enable a way to interrupt
  1066     // inside a class; we can make the code (inside destructor)
  1124     // the event dispatcher when returning back to a lower recursion level
  1067     // execute on lower loop level:
  1125     // than where interruptLater was called. This is needed to detect if
       
  1126     // [NSApp run] should still be running at the recursion level it is at.
       
  1127     // Since the interrupt is canceled if processEvents is called before
       
  1128     // this object gets deleted, we also avoid interrupting unnecessary.
  1068     deleteLater();
  1129     deleteLater();
  1069 }
  1130 }
  1070 
  1131 
  1071 QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp()
  1132 QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp()
  1072 {
  1133 {
  1073     if (cancelled)
  1134     if (cancelled)
  1074         return;
  1135         return;
  1075 
       
  1076     instance = 0;
  1136     instance = 0;
  1077 
       
  1078     if (QEventDispatcherMacPrivate::currentExecIsNSAppRun) {
       
  1079         int activeCount = QEventDispatcherMacPrivate::activeModalSessionCount();
       
  1080         if (activeCount > 0) {
       
  1081             // The problem we now have hit: [NSApp stop] will not stop NSApp
       
  1082             // if a session is active; it will stop the session instead.
       
  1083             // So to stop NSApp, we need to temporarily stop all the
       
  1084             // sessions, then stop NSApp, then restart the session on top again.
       
  1085             // We need to do this to ensure that we're not stuck inside
       
  1086             // [NSApp run] when we really should be running a modal session:
       
  1087             QEventDispatcherMacPrivate::temporarilyStopAllModalSessions();
       
  1088         }
       
  1089     }
       
  1090     // Always interrupt once more in case the modal session stack changed
       
  1091     // while processEvents was called manually from within the application:
       
  1092     QEventDispatcherMac::instance()->interrupt();
  1137     QEventDispatcherMac::instance()->interrupt();
  1093 }
  1138 }
  1094 
  1139 
  1095 void QtMacInterruptDispatcherHelp::interruptLater() {
  1140 void QtMacInterruptDispatcherHelp::cancelInterruptLater()
  1096     if (instance) {
  1141 {
  1097         instance->cancelled = true;
  1142     if (!instance)
  1098         delete instance;
  1143         return;
  1099     }
  1144     instance->cancelled = true;
       
  1145     delete instance;
       
  1146     instance = 0;
       
  1147 }
       
  1148 
       
  1149 void QtMacInterruptDispatcherHelp::interruptLater()
       
  1150 {
       
  1151     cancelInterruptLater();
  1100     instance = new QtMacInterruptDispatcherHelp;
  1152     instance = new QtMacInterruptDispatcherHelp;
  1101 }
  1153 }
  1102 
  1154 
  1103 #endif
  1155 #endif
  1104 
  1156 
  1105 QT_END_NAMESPACE
  1157 QT_END_NAMESPACE
       
  1158