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, &notifierEvent);
281    } else if (callbackType == kCFSocketWriteCallBack) {
282        if (socketInfo->writeNotifier)
283            QApplication::sendEvent(socketInfo->writeNotifier, &notifierEvent);
284    }
285}
286
287/*
288    Adds a loop source for the given socket to the current run loop.
289*/
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