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