source: trunk/src/gui/kernel/qeventdispatcher_mac.mm@ 808

Last change on this file since 808 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 42.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
94QT_BEGIN_NAMESPACE
95
96QT_USE_NAMESPACE
97#endif
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 [NSApp 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#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
496 case NSEventTypeGesture: // touch events
497 case NSEventTypeMagnify:
498 case NSEventTypeSwipe:
499 case NSEventTypeRotate:
500 case NSEventTypeBeginGesture:
501 case NSEventTypeEndGesture:
502#endif
503 result = true;
504 break;
505
506 default:
507 break;
508 }
509 return result;
510}
511#endif
512
513static inline void qt_mac_waitForMoreEvents()
514{
515#ifndef QT_MAC_USE_COCOA
516 while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut) ;
517#else
518 // If no event exist in the cocoa event que, wait
519 // (and free up cpu time) until at least one event occur.
520 // This implementation is a bit on the edge, but seems to
521 // work fine:
522 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
523 untilDate:[NSDate distantFuture]
524 inMode:NSDefaultRunLoopMode
525 dequeue:YES];
526 if (event)
527 [NSApp postEvent:event atStart:YES];
528#endif
529}
530
531#ifdef QT_MAC_USE_COCOA
532static inline void qt_mac_waitForMoreModalSessionEvents()
533{
534 // If no event exist in the cocoa event que, wait
535 // (and free up cpu time) until at least one event occur.
536 // This implementation is a bit on the edge, but seems to
537 // work fine:
538 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
539 untilDate:[NSDate distantFuture]
540 inMode:NSModalPanelRunLoopMode
541 dequeue:YES];
542 if (event)
543 [NSApp postEvent:event atStart:YES];
544}
545#endif
546
547bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags)
548{
549 Q_D(QEventDispatcherMac);
550 d->interrupt = false;
551 // In case we end up recursing while we now process events, make sure
552 // that we send remaining posted Qt events before this call returns:
553 wakeUp();
554 emit awake();
555
556 bool retVal = false;
557 forever {
558 if (d->interrupt)
559 break;
560
561#ifdef QT_MAC_USE_COCOA
562 QMacCocoaAutoReleasePool pool;
563 NSEvent* event = 0;
564
565 // If Qt is used as a plugin, or just added into a native cocoa
566 // application, we should not run or stop NSApplication;
567 // This will be done from outside Qt.
568 // And if processEvents is called manually (rather than from QEventLoop), we
569 // cannot enter a tight loop and block the call, but instead return after one flush:
570 bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning];
571 bool canExec_Qt = flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec;
572
573 if (canExec_Qt && canExec_3rdParty) {
574 // We can use exec-mode, meaning that we can stay in a tight loop until
575 // interrupted. This is mostly an optimization, but it also allow us
576 // to use [NSApp run], which is the recommended way of running applications
577 // in cocoa. [NSApp run] should be called at least once for any cocoa app.
578 if (NSModalSession session = d->currentModalSession()) {
579 QBoolBlocker execGuard(d->currentExecIsNSAppRun, false);
580 while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
581 qt_mac_waitForMoreModalSessionEvents();
582 if (!d->interrupt && session == d->currentModalSessionCached) {
583 // INVARIANT: Someone called e.g. [NSApp stopModal:] from outside the event
584 // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
585 // 'session' as well. As a result, we need to restart all internal sessions:
586 d->temporarilyStopAllModalSessions();
587 }
588 } else {
589 d->nsAppRunCalledByQt = true;
590 QBoolBlocker execGuard(d->currentExecIsNSAppRun, true);
591 [NSApp run];
592 }
593 retVal = true;
594 } else do {
595 // INVARIANT: We cannot block the thread (and run in a tight loop).
596 // Instead we will process all current pending events and return.
597 bool mustRelease = false;
598
599 if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
600 // Process a pending user input event
601 mustRelease = true;
602 event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
603 } else {
604 if (NSModalSession session = d->currentModalSession()) {
605 if (flags & QEventLoop::WaitForMoreEvents)
606 qt_mac_waitForMoreModalSessionEvents();
607 NSInteger status = [NSApp runModalSession:session];
608 if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) {
609 // INVARIANT: Someone called e.g. [NSApp stopModal:] from outside the event
610 // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
611 // 'session' as well. As a result, we need to restart all internal sessions:
612 d->temporarilyStopAllModalSessions();
613 }
614 retVal = true;
615 break;
616 } else {
617 event = [NSApp nextEventMatchingMask:NSAnyEventMask
618 untilDate:nil
619 inMode:NSDefaultRunLoopMode
620 dequeue: YES];
621
622 if (event != nil) {
623 if (flags & QEventLoop::ExcludeUserInputEvents) {
624 if (IsMouseOrKeyEvent(event)) {
625 // retain event here?
626 [event retain];
627 d->queuedUserInputEvents.append(event);
628 continue;
629 }
630 }
631 }
632 }
633 }
634 if (event) {
635 if (!filterEvent(event) && qt_mac_send_event(flags, event, 0))
636 retVal = true;
637 if (mustRelease)
638 [event release];
639 }
640 } while(!d->interrupt && event != nil);
641
642#else
643 do {
644 EventRef event;
645 if (!(flags & QEventLoop::ExcludeUserInputEvents)
646 && !d->queuedUserInputEvents.isEmpty()) {
647 // process a pending user input event
648 event = static_cast<EventRef>(d->queuedUserInputEvents.takeFirst());
649 } else {
650 OSStatus err = ReceiveNextEvent(0,0, kEventDurationNoWait, true, &event);
651 if(err != noErr)
652 continue;
653 // else
654 if (flags & QEventLoop::ExcludeUserInputEvents) {
655 UInt32 ekind = GetEventKind(event),
656 eclass = GetEventClass(event);
657 switch(eclass) {
658 case kEventClassQt:
659 if(ekind != kEventQtRequestContext)
660 break;
661 // fall through
662 case kEventClassMouse:
663 case kEventClassKeyboard:
664 d->queuedUserInputEvents.append(event);
665 continue;
666 }
667 }
668 }
669
670 if (!filterEvent(&event) && qt_mac_send_event(flags, event, 0))
671 retVal = true;
672 ReleaseEvent(event);
673 } while(!d->interrupt && GetNumEventsInQueue(GetMainEventQueue()) > 0);
674
675#endif
676
677 bool canWait = (d->threadData->canWait
678 && !retVal
679 && !d->interrupt
680 && (flags & QEventLoop::WaitForMoreEvents));
681 if (canWait) {
682 // INVARIANT: We haven't processed any events yet. And we're told
683 // to stay inside this function until at least one event is processed.
684 qt_mac_waitForMoreEvents();
685 flags &= ~QEventLoop::WaitForMoreEvents;
686 } else {
687 // Done with event processing for now.
688 // Leave the function:
689 break;
690 }
691 }
692
693#ifdef QT_MAC_USE_COCOA
694 // In case we _now_ process events using [NSApp run], we need to stop it to
695 // ensure that:
696 // 1. the QEventLoop that called us is still executing, or
697 // 2. we have a modal session that needs to be spun instead.
698 // In case this is a plain call to processEvents (perhaps from a loop)
699 // from the application (rather than from a QEventLoop), we delay the
700 // interrupting until we/ actually enter a lower loop level (hence the
701 // deffered delete of the object below):
702 QtMacInterruptDispatcherHelp::interruptLater();
703#endif
704
705 if (d->interrupt) {
706 // We should continue to leave all recursion to processEvents until
707 // processEvents is called again (e.g. from a QEventLoop that
708 // was not yet told to quit:
709 interrupt();
710 }
711
712 return retVal;
713}
714
715void QEventDispatcherMac::wakeUp()
716{
717 Q_D(QEventDispatcherMac);
718 d->serialNumber.ref();
719 CFRunLoopSourceSignal(d->postedEventsSource);
720 CFRunLoopWakeUp(mainRunLoop());
721}
722
723void QEventDispatcherMac::flush()
724{
725 if(qApp) {
726 QWidgetList tlws = QApplication::topLevelWidgets();
727 for(int i = 0; i < tlws.size(); i++) {
728 QWidget *tlw = tlws.at(i);
729 if(tlw->isVisible())
730 macWindowFlush(qt_mac_window_for(tlw));
731 }
732 }
733}
734
735/*****************************************************************************
736 QEventDispatcherMac Implementation
737 *****************************************************************************/
738MacTimerHash QEventDispatcherMacPrivate::macTimerHash;
739bool QEventDispatcherMacPrivate::blockSendPostedEvents = false;
740