Changeset 561 for trunk/src/gui/kernel/qeventdispatcher_mac.mm
- Timestamp:
- Feb 11, 2010, 11:19:06 PM (15 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk
-
Property svn:mergeinfo
set to (toggle deleted branches)
/branches/vendor/nokia/qt/4.6.1 merged eligible /branches/vendor/nokia/qt/current merged eligible /branches/vendor/trolltech/qt/current 3-149
-
Property svn:mergeinfo
set to (toggle deleted branches)
-
trunk/src/gui/kernel/qeventdispatcher_mac.mm
r2 r561 2 2 ** 3 3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 4 ** Contact: Qt Software Information (qt-info@nokia.com) 4 ** All rights reserved. 5 ** Contact: Nokia Corporation (qt-info@nokia.com) 5 6 ** 6 7 ** This file is part of the QtGui module of the Qt Toolkit. … … 21 22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 22 23 ** 23 ** In addition, as a special exception, Nokia gives you certain 24 ** additional rights. These rights are described in the Nokia Qt LGPL 25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 26 ** package. 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 27 ** 28 28 ** GNU General Public License Usage … … 34 34 ** met: http://www.gnu.org/copyleft/gpl.html. 35 35 ** 36 ** If you are unsure which license is appropriate for your use, please37 ** contact the sales department at qt-sales@nokia.com.36 ** If you have questions regarding the use of this file, please contact 37 ** Nokia at qt-info@nokia.com. 38 38 ** $QT_END_LICENSE$ 39 39 ** … … 137 137 return; // Can't send another timer event if it's pending. 138 138 139 tmr->pending = true; 140 QTimerEvent e(tmr->id); 141 qt_sendSpontaneousEvent(tmr->obj, &e); 142 143 // Get the value again in case the timer gets unregistered during the sendEvent. 144 tmr = macTimerHash.value(timerID); 145 if (tmr != 0) 146 tmr->pending = false; 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 147 152 } 148 153 … … 265 270 MacSocketInfo *socketInfo = eventDispatcher->macSockets.value(nativeSocket); 266 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. 267 278 if (callbackType == kCFSocketReadCallBack) { 268 Q_ASSERT(socketInfo->readNotifier);269 QApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent);279 if (socketInfo->readNotifier) 280 QApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); 270 281 } else if (callbackType == kCFSocketWriteCallBack) { 271 // ### Bug in Apple socket notifiers seems to send write even272 // ### after the notifier has been disabled, need to investigate further.273 282 if (socketInfo->writeNotifier) 274 283 QApplication::sendEvent(socketInfo->writeNotifier, ¬ifierEvent); … … 494 503 #endif 495 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 496 539 bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) 497 540 { … … 503 546 emit awake(); 504 547 505 #ifndef QT_MAC_NO_QUICKDRAW506 if(!qt_mac_safe_pdev) { //create an empty widget and this can be used for a port anytime507 QWidget *tlw = new QWidget;508 tlw->setAttribute(Qt::WA_DeleteOnClose);509 tlw->setObjectName(QLatin1String("empty_widget"));510 tlw->hide();511 qt_mac_safe_pdev = tlw;512 }513 #endif514 515 548 bool retVal = false; 516 549 forever { … … 522 555 NSEvent* event = 0; 523 556 524 if (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) { 525 // The point of the CocoaRequestModal event is to make sure that a 526 // non-execed app modal window recurses into it's own dialog exec 527 // once cocoa is spinning the event loop for us (e.g on top of [NSApp run]). 528 // We expect only one event to notify us about this, regardless of how many 529 // widgets that are waiting to be modal. So we remove all other pending 530 // events, if any. And since cocoa will now take over event processing for us, 531 // we allow new app modal widgets to recurse on top of us, hence the release of 532 // the block: 533 QBoolBlocker block(d->blockCocoaRequestModal, false); 534 QCoreApplication::removePostedEvents(qApp, QEvent::CocoaRequestModal); 535 536 if (NSModalSession session = d->activeModalSession()) 537 while ([NSApp runModalSession:session] == NSRunContinuesResponse) { 538 // runModalSession will not wait for events, so we do it 539 // ourselves (otherwise we would spend 100% CPU inside this loop): 540 event = [NSApp nextEventMatchingMask:NSAnyEventMask 541 untilDate:[NSDate distantFuture] inMode:NSModalPanelRunLoopMode dequeue:YES]; 542 if (event) 543 [NSApp postEvent:event atStart:YES]; 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 if (!d->interrupt && session == d->currentModalSessionCached) { 575 // INVARIANT: Someone called e.g. [NSApp stopModal:] from outside the event 576 // dispatcher (e.g to stop a native dialog). But that call wrongly stopped 577 // 'session' as well. As a result, we need to restart all internal sessions: 578 d->temporarilyStopAllModalSessions(); 544 579 } 545 else 580 } else { 581 d->nsAppRunCalledByQt = true; 582 QBoolBlocker execGuard(d->currentExecIsNSAppRun, true); 546 583 [NSApp run]; 547 548 d->rebuildModalSessionStack(false); 584 } 549 585 retVal = true; 550 586 } else do { 551 // Since we now are going to spin the event loop just _one_ round 552 // we need to block all incoming CocoaRequestModal events to ensure 553 // that we don't recurse into a new exec-ing event loop while doing 554 // so (and as such, 'hang' the thread inside the recursion): 555 QBoolBlocker block(d->blockCocoaRequestModal, true); 587 // INVARIANT: We cannot block the thread (and run in a tight loop). 588 // Instead we will process all current pending events and return. 556 589 bool mustRelease = false; 557 590 558 591 if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { 559 // process a pending user input event592 // Process a pending user input event 560 593 mustRelease = true; 561 594 event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); 562 595 } else { 563 if (NSModalSession session = d-> activeModalSession()) {564 // There's s a modal widget showing, run it's session:565 if (flags & QEventLoop::WaitForMoreEvents) {566 // Wait for at least one event567 // before spinning the session:568 event = [NSApp nextEventMatchingMask:NSAnyEventMask569 untilDate:[NSDate distantFuture] inMode:NSModalPanelRunLoopMode dequeue:YES];570 if (event)571 [NSApp postEvent:event atStart:YES];596 if (NSModalSession session = d->currentModalSession()) { 597 if (flags & QEventLoop::WaitForMoreEvents) 598 qt_mac_waitForMoreModalSessionEvents(); 599 NSInteger status = [NSApp runModalSession:session]; 600 if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { 601 // INVARIANT: Someone called e.g. [NSApp stopModal:] from outside the event 602 // dispatcher (e.g to stop a native dialog). But that call wrongly stopped 603 // 'session' as well. As a result, we need to restart all internal sessions: 604 d->temporarilyStopAllModalSessions(); 572 605 } 573 [NSApp runModalSession:session];574 606 retVal = true; 575 607 break; … … 641 673 if (canWait) { 642 674 // INVARIANT: We haven't processed any events yet. And we're told 643 // to stay inside this function until at least one event is processed 644 // (WaitForMoreEvents). So we wait on the window server: 645 #ifndef QT_MAC_USE_COCOA 646 while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut); 647 #else 648 QMacCocoaAutoReleasePool pool; 649 NSEvent *manualEvent = [NSApp nextEventMatchingMask:NSAnyEventMask 650 untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode 651 dequeue:YES]; 652 if (manualEvent) 653 [NSApp sendEvent:manualEvent]; 654 #endif 675 // to stay inside this function until at least one event is processed. 676 qt_mac_waitForMoreEvents(); 655 677 flags &= ~QEventLoop::WaitForMoreEvents; 656 678 } else { 657 // Done with event processing for now. Leave the function: 679 // Done with event processing for now. 680 // Leave the function: 658 681 break; 659 682 } 660 683 } 661 684 662 // Because pending deffered-delete events are only sendt after 663 // returning from the loop level they were posted in, we schedule 664 // an extra wakup to force the _current_ run loop to process them (in 665 // case the application stands idle waiting for the delete event): 666 wakeUp(); 667 668 if (d->interrupt){ 669 // We restart NSApplication by first stopping it, and then call 'run' 670 // again (NSApplication is actually already stopped, hence the need 671 // for a restart, but calling stop again will also make the call 672 // return from the current recursion). When the call returns to 673 // QEventLoop (mind, not from this recursion, but from the one we're 674 // about to stop), it will just call QEventDispatcherMac::processEvents() 675 // again. 685 #ifdef QT_MAC_USE_COCOA 686 // In case we _now_ process events using [NSApp run], we need to stop it to 687 // ensure that: 688 // 1. the QEventLoop that called us is still executing, or 689 // 2. we have a modal session that needs to be spun instead. 690 // In case this is a plain call to processEvents (perhaps from a loop) 691 // from the application (rather than from a QEventLoop), we delay the 692 // interrupting until we/ actually enter a lower loop level (hence the 693 // deffered delete of the object below): 694 QtMacInterruptDispatcherHelp::interruptLater(); 695 #endif 696 697 if (d->interrupt) { 698 // We should continue to leave all recursion to processEvents until 699 // processEvents is called again (e.g. from a QEventLoop that 700 // was not yet told to quit: 676 701 interrupt(); 677 702 } 703 678 704 return retVal; 679 705 } … … 707 733 #ifdef QT_MAC_USE_COCOA 708 734 QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack; 709 bool QEventDispatcherMacPrivate::blockCocoaRequestModal = false; 710 711 static void qt_mac_setChildDialogsResponsive(QWidget *widget, bool responsive) 712 { 735 bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false; 736 bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false; 737 NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0; 738 739 int QEventDispatcherMacPrivate::activeModalSessionCount() 740 { 741 // Returns the number of modal sessions created 742 // (and not just pushed onto the stack, pending to be created) 743 int count = 0; 744 for (int i=cocoaModalSessionStack.size()-1; i>=0; --i) { 745 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; 746 if (info.session) 747 ++count; 748 } 749 return count; 750 } 751 752 void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions() 753 { 754 // Stop all created modal session, and as such, make then 755 // pending again. The next call to currentModalSession will 756 // recreate the session on top again: 757 int stackSize = cocoaModalSessionStack.size(); 758 for (int i=stackSize-1; i>=0; --i) { 759 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; 760 if (info.session) { 761 [NSApp endModalSession:info.session]; 762 info.session = 0; 763 } 764 } 765 currentModalSessionCached = 0; 766 } 767 768 NSModalSession QEventDispatcherMacPrivate::currentModalSession() 769 { 770 // If we have one or more modal windows, this function will create 771 // a session for each of those, and return the one for the top. 772 if (currentModalSessionCached) 773 return currentModalSessionCached; 774 775 if (cocoaModalSessionStack.isEmpty()) 776 return 0; 777 778 // Since this code will end up calling our Qt event handler 779 // (also from beginModalSessionForWindow), we need to block 780 // that to avoid side effects of events beeing delivered: 781 QBoolBlocker block(blockSendPostedEvents, true); 782 783 if (![NSApp isRunning]) { 784 // Sadly, we need to introduce this little event flush 785 // to stop dialogs from blinking/poping in front if a 786 // modal session restart was needed: 787 while (NSEvent *event = [NSApp nextEventMatchingMask:0 788 untilDate:nil 789 inMode:NSDefaultRunLoopMode 790 dequeue: YES]) { 791 qt_mac_send_event(0, event, 0); 792 } 793 } 794 795 int sessionCount = cocoaModalSessionStack.size(); 796 for (int i=0; i<sessionCount; ++i) { 797 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; 798 if (!info.widget) 799 continue; 800 if (info.widget->testAttribute(Qt::WA_DontShowOnScreen)) 801 continue; 802 if (!info.session) { 803 QMacCocoaAutoReleasePool pool; 804 NSWindow *window = qt_mac_window_for(info.widget); 805 if (!window) 806 continue; 807 info.session = [NSApp beginModalSessionForWindow:window]; 808 } 809 currentModalSessionCached = info.session; 810 } 811 812 return currentModalSessionCached; 813 } 814 815 static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal) 816 { 817 // For NSPanels (but not NSWindows, sadly), we can set the flag 818 // worksWhenModal, so that they are active even when they are not modal. 713 819 QList<QDialog *> dialogs = widget->findChildren<QDialog *>(); 714 820 for (int i=0; i<dialogs.size(); ++i){ 715 821 NSWindow *window = qt_mac_window_for(dialogs[i]); 716 822 if (window && [window isKindOfClass:[NSPanel class]]) { 717 [static_cast<NSPanel *>(window) setWorksWhenModal: responsive];718 if ( responsive&& dialogs[i]->isVisible()){823 [static_cast<NSPanel *>(window) setWorksWhenModal:worksWhenModal]; 824 if (worksWhenModal && dialogs[i]->isVisible()){ 719 825 [window orderFront:window]; 720 826 } … … 723 829 } 724 830 725 NSModalSession QEventDispatcherMacPrivate::activeModalSession() 726 { 727 // Create (if needed) and return the modal session 728 // for the top-most modal dialog, if any: 729 if (cocoaModalSessionStack.isEmpty()) 730 return 0; 731 QCocoaModalSessionInfo &info = cocoaModalSessionStack.last(); 732 if (!info.widget) 733 return 0; 734 if (info.widget->testAttribute(Qt::WA_DontShowOnScreen)){ 735 // INVARIANT: We have a modal widget, but it's not visible on screen. 736 // This will e.g. be true for native dialogs. Make the dialog children 737 // of the previous modal dialog unresponsive, so that the current dialog 738 // (native or not) is the only reponsive dialog on screen: 739 int size = cocoaModalSessionStack.size(); 831 void QEventDispatcherMacPrivate::updateChildrenWorksWhenModal() 832 { 833 // Make the dialog children of the widget 834 // active. And make the dialog children of 835 // the previous modal dialog unactive again: 836 int size = cocoaModalSessionStack.size(); 837 if (size > 0){ 838 if (QWidget *prevModal = cocoaModalSessionStack[size-1].widget) 839 setChildrenWorksWhenModal(prevModal, true); 740 840 if (size > 1){ 741 841 if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget) 742 qt_mac_setChildDialogsResponsive(prevModal, false); 743 } 744 return 0; 745 } 746 747 if (!info.session) { 748 QMacCocoaAutoReleasePool pool; 749 NSWindow *window = qt_mac_window_for(info.widget); 750 if (!window) 751 return 0; 752 // 'beginModalSessionForWindow' will give the event loop a spin, and as 753 // such, deliver Qt events. This might lead to inconsistent behaviour 754 // (especially if CocoaRequestModal is delivered), so we need to block: 755 QBoolBlocker block(blockSendPostedEvents, true); 756 info.session = [NSApp beginModalSessionForWindow:window]; 757 // Make the dialog children of the current modal dialog 758 // responsive. And make the dialog children of 759 // the previous modal dialog unresponsive again: 760 qt_mac_setChildDialogsResponsive(info.widget, true); 761 int size = cocoaModalSessionStack.size(); 762 if (size > 1){ 763 if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget) 764 qt_mac_setChildDialogsResponsive(prevModal, false); 765 } 766 } 767 return info.session; 768 } 769 770 void QEventDispatcherMacPrivate::rebuildModalSessionStack(bool pop) 771 { 772 // Calling [NSApp stopModal], or [NSApp stop], will stop all modal dialogs 773 // in one go. So to to not confuse cocoa, we need to stop all our modal 774 // sessions as well. QMacEventDispatcher will make them modal again 775 // in the correct order as long as they are left on the cocoaModalSessionStack 776 // and a CocoaRequestModal is posted: 777 if (cocoaModalSessionStack.isEmpty()) 778 return; 779 780 QMacCocoaAutoReleasePool pool; 781 [NSApp stopModal]; 782 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint 783 modifierFlags:0 timestamp:0. windowNumber:0 context:0 784 subtype:SHRT_MAX data1:0 data2:0] atStart:NO]; 785 786 for (int i=0; i<cocoaModalSessionStack.size(); ++i){ 842 setChildrenWorksWhenModal(prevModal, false); 843 } 844 } 845 } 846 847 void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget) 848 { 849 // Add a new, empty (null), NSModalSession to the stack. 850 // It will become active the next time QEventDispatcher::processEvents is called. 851 // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer 852 // is non-zero, and the session pointer is zero (it will become active upon a call to 853 // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if 854 // the widget pointer is zero, and the session pointer is non-zero (it will be fully 855 // stopped in endModalSession(). 856 QCocoaModalSessionInfo info = {widget, 0}; 857 cocoaModalSessionStack.push(info); 858 updateChildrenWorksWhenModal(); 859 currentModalSessionCached = 0; 860 } 861 862 void QEventDispatcherMacPrivate::endModalSession(QWidget *widget) 863 { 864 // Mark all sessions attached to widget as pending to be stopped. We do this 865 // by setting the widget pointer to zero, but leave the session pointer. 866 // We don't tell cocoa to stop any sessions just yet, because cocoa only understands 867 // when we stop the _current_ modal session (which is the session on top of 868 // the stack, and might not belong to 'widget'). 869 int stackSize = cocoaModalSessionStack.size(); 870 for (int i=stackSize-1; i>=0; --i) { 787 871 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; 872 if (info.widget == widget) 873 info.widget = 0; 874 } 875 876 // Now we stop, and remove, all sessions marked as pending 877 // to be stopped on _top_ of the stack, if any: 878 bool needToInterruptEventDispatcher = false; 879 bool needToUpdateChildrenWorksWhenModal = false; 880 881 for (int i=stackSize-1; i>=0; --i) { 882 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; 883 if (info.widget) 884 break; 885 cocoaModalSessionStack.remove(i); 886 needToUpdateChildrenWorksWhenModal = true; 887 currentModalSessionCached = 0; 788 888 if (info.session) { 789 889 [NSApp endModalSession:info.session]; 790 info.session = 0; 791 } 792 } 793 794 if (pop) { 795 QCocoaModalSessionInfo info = cocoaModalSessionStack.pop(); 796 if (info.widget) 797 qt_mac_setChildDialogsResponsive(info.widget, false); 798 } 799 800 if (!cocoaModalSessionStack.isEmpty()) { 801 // Since we now have pending modal sessions again, make 802 // sure that we enter modal for the one on the top later: 803 qApp->postEvent(qApp, new QEvent(QEvent::CocoaRequestModal)); 804 } else { 805 QCoreApplication::removePostedEvents(qApp, QEvent::CocoaRequestModal); 806 } 890 needToInterruptEventDispatcher = true; 891 } 892 } 893 894 if (needToUpdateChildrenWorksWhenModal) 895 updateChildrenWorksWhenModal(); 896 if (needToInterruptEventDispatcher) 897 QEventDispatcherMac::instance()->interrupt(); 807 898 } 808 899 … … 836 927 &observerContext); 837 928 CFRunLoopAddObserver(mainRunLoop(), d->waitingObserver, kCFRunLoopCommonModes); 929 930 /* The first cycle in the loop adds the source and the events of the source 931 are not processed. 932 We use an observer to process the posted events for the first 933 execution of the loop. */ 934 CFRunLoopObserverContext firstTimeObserverContext; 935 bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext)); 936 firstTimeObserverContext.info = d; 937 d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, 938 kCFRunLoopEntry, 939 /* repeats = */ false, 940 0, 941 QEventDispatcherMacPrivate::firstLoopEntry, 942 &firstTimeObserverContext); 943 CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, kCFRunLoopCommonModes); 838 944 } 839 945 … … 852 958 } 853 959 854 void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info) 855 { 856 QEventDispatcherMacPrivate *d = static_cast<QEventDispatcherMacPrivate *>(info); 857 if (blockSendPostedEvents) { 960 inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents) 961 { 962 if (blockSendPostedEvents || d->interrupt) { 858 963 CFRunLoopSourceSignal(d->postedEventsSource); 859 964 } else { … … 865 970 } 866 971 867 #ifdef QT_MAC_USE_COCOA 868 static void stopNSApp() 869 { 870 QMacCocoaAutoReleasePool pool; 871 static const short NSAppShouldStopForQt = SHRT_MAX; 872 [NSApp stop:NSApp]; 873 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint 874 modifierFlags:0 timestamp:0. windowNumber:0 context:0 875 subtype:NSAppShouldStopForQt data1:0 data2:0] atStart:NO]; 876 } 877 #endif 972 void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref, 973 CFRunLoopActivity activity, 974 void *info) 975 { 976 Q_UNUSED(ref); 977 Q_UNUSED(activity); 978 processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); 979 } 980 981 void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info) 982 { 983 processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); 984 } 878 985 879 986 void QEventDispatcherMac::interrupt() … … 882 989 d->interrupt = true; 883 990 wakeUp(); 991 884 992 #ifndef QT_MAC_USE_COCOA 885 993 CFRunLoopStop(mainRunLoop()); 886 994 #else 887 stopNSApp(); 995 QMacCocoaAutoReleasePool pool; 996 // In case we wait for more events inside 997 // processEvents (or NSApp run), post a dummy to wake it up: 998 static const short NSAppShouldStopForQt = SHRT_MAX; 999 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint 1000 modifierFlags:0 timestamp:0. windowNumber:0 context:0 1001 subtype:NSAppShouldStopForQt data1:0 data2:0] atStart:NO]; 1002 1003 if (d->activeModalSessionCount() == 0) { 1004 // We should only stop NSApp if we actually started it (and 1005 // not some 3rd party application, e.g. if we are a plugin). 1006 if (d->nsAppRunCalledByQt) 1007 [NSApp stop:NSApp]; 1008 } 888 1009 #endif 889 1010 } … … 921 1042 CFRunLoopObserverInvalidate(d->waitingObserver); 922 1043 CFRelease(d->waitingObserver); 923 } 924 1044 1045 CFRunLoopObserverInvalidate(d->firstTimeObserver); 1046 CFRelease(d->firstTimeObserver); 1047 } 1048 1049 ///////////////////////////////////////////////////////////////////////////// 1050 1051 #ifdef QT_MAC_USE_COCOA 1052 1053 QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0; 1054 1055 QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false) 1056 { 1057 // This is the whole point of encapsulation this code 1058 // inside a class; we can make the code (inside destructor) 1059 // execute on lower loop level: 1060 deleteLater(); 1061 } 1062 1063 QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp() 1064 { 1065 if (cancelled) 1066 return; 1067 1068 instance = 0; 1069 1070 if (QEventDispatcherMacPrivate::currentExecIsNSAppRun) { 1071 int activeCount = QEventDispatcherMacPrivate::activeModalSessionCount(); 1072 if (activeCount > 0) { 1073 // The problem we now have hit: [NSApp stop] will not stop NSApp 1074 // if a session is active; it will stop the session instead. 1075 // So to stop NSApp, we need to temporarily stop all the 1076 // sessions, then stop NSApp, then restart the session on top again. 1077 // We need to do this to ensure that we're not stuck inside 1078 // [NSApp run] when we really should be running a modal session: 1079 QEventDispatcherMacPrivate::temporarilyStopAllModalSessions(); 1080 } 1081 } 1082 // Always interrupt once more in case the modal session stack changed 1083 // while processEvents was called manually from within the application: 1084 QEventDispatcherMac::instance()->interrupt(); 1085 } 1086 1087 void QtMacInterruptDispatcherHelp::interruptLater() { 1088 if (instance) { 1089 instance->cancelled = true; 1090 delete instance; 1091 } 1092 instance = new QtMacInterruptDispatcherHelp; 1093 } 1094 1095 #endif 925 1096 926 1097 QT_END_NAMESPACE
Note:
See TracChangeset
for help on using the changeset viewer.