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()) { |
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]; |
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 |