|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
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 |
|
94 QT_BEGIN_NAMESPACE |
|
95 |
|
96 QT_USE_NAMESPACE |
|
97 #endif |
|
98 |
|
99 /***************************************************************************** |
|
100 Externals |
|
101 *****************************************************************************/ |
|
102 extern void qt_event_request_timer(MacTimerInfo *); //qapplication_mac.cpp |
|
103 extern MacTimerInfo *qt_event_get_timer(EventRef); //qapplication_mac.cpp |
|
104 extern void qt_event_request_select(QEventDispatcherMac *); //qapplication_mac.cpp |
|
105 extern void qt_event_request_updates(); //qapplication_mac.cpp |
|
106 extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp |
|
107 extern bool qt_is_gui_used; //qapplication.cpp |
|
108 extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp |
|
109 extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp |
|
110 |
|
111 static 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 */ |
|
125 void 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 |
|
154 void 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 |
|
186 bool 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 |
|
213 bool 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 |
|
242 QList<QEventDispatcherMac::TimerInfo> |
|
243 QEventDispatcherMac::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 *************************************************************************/ |
|
265 void 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 */ |
|
290 CFRunLoopSourceRef 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 */ |
|
303 void 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 */ |
|
319 void 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 */ |
|
396 void 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 |
|
447 bool QEventDispatcherMac::hasPendingEvents() |
|
448 { |
|
449 extern uint qGlobalPostedEventsCount(); |
|
450 return qGlobalPostedEventsCount() || (qt_is_gui_used && GetNumEventsInQueue(GetMainEventQueue())); |
|
451 } |
|
452 |
|
453 |
|
454 static 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 |
|
470 static 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 result = true; |
|
496 break; |
|
497 |
|
498 default: |
|
499 break; |
|
500 } |
|
501 return result; |
|
502 } |
|
503 #endif |
|
504 |
|
505 static inline void qt_mac_waitForMoreEvents() |
|
506 { |
|
507 #ifndef QT_MAC_USE_COCOA |
|
508 while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut) ; |
|
509 #else |
|
510 // If no event exist in the cocoa event que, wait |
|
511 // (and free up cpu time) until at least one event occur. |
|
512 // This implementation is a bit on the edge, but seems to |
|
513 // work fine: |
|
514 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask |
|
515 untilDate:[NSDate distantFuture] |
|
516 inMode:NSDefaultRunLoopMode |
|
517 dequeue:YES]; |
|
518 if (event) |
|
519 [NSApp postEvent:event atStart:YES]; |
|
520 #endif |
|
521 } |
|
522 |
|
523 #ifdef QT_MAC_USE_COCOA |
|
524 static inline void qt_mac_waitForMoreModalSessionEvents() |
|
525 { |
|
526 // If no event exist in the cocoa event que, wait |
|
527 // (and free up cpu time) until at least one event occur. |
|
528 // This implementation is a bit on the edge, but seems to |
|
529 // work fine: |
|
530 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask |
|
531 untilDate:[NSDate distantFuture] |
|
532 inMode:NSModalPanelRunLoopMode |
|
533 dequeue:YES]; |
|
534 if (event) |
|
535 [NSApp postEvent:event atStart:YES]; |
|
536 } |
|
537 #endif |
|
538 |
|
539 bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) |
|
540 { |
|
541 Q_D(QEventDispatcherMac); |
|
542 d->interrupt = false; |
|
543 // In case we end up recursing while we now process events, make sure |
|
544 // that we send remaining posted Qt events before this call returns: |
|
545 wakeUp(); |
|
546 emit awake(); |
|
547 |
|
548 bool retVal = false; |
|
549 forever { |
|
550 if (d->interrupt) |
|
551 break; |
|
552 |
|
553 #ifdef QT_MAC_USE_COCOA |
|
554 QMacCocoaAutoReleasePool pool; |
|
555 NSEvent* event = 0; |
|
556 |
|
557 // If Qt is used as a plugin, or just added into a native cocoa |
|
558 // application, we should not run or stop NSApplication; |
|
559 // This will be done from outside Qt. |
|
560 // And if processEvents is called manually (rather than from QEventLoop), we |
|
561 // cannot enter a tight loop and block the call, but instead return after one flush: |
|
562 bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; |
|
563 bool canExec_Qt = flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec; |
|
564 |
|
565 if (canExec_Qt && canExec_3rdParty) { |
|
566 // We can use exec-mode, meaning that we can stay in a tight loop until |
|
567 // interrupted. This is mostly an optimization, but it also allow us |
|
568 // to use [NSApp run], which is the recommended way of running applications |
|
569 // in cocoa. [NSApp run] should be called at least once for any cocoa app. |
|
570 if (NSModalSession session = d->currentModalSession()) { |
|
571 QBoolBlocker execGuard(d->currentExecIsNSAppRun, false); |
|
572 while (!d->interrupt && [NSApp runModalSession:session] == NSRunContinuesResponse) |
|
573 qt_mac_waitForMoreModalSessionEvents(); |
|
574 } else { |
|
575 d->nsAppRunCalledByQt = true; |
|
576 QBoolBlocker execGuard(d->currentExecIsNSAppRun, true); |
|
577 [NSApp run]; |
|
578 } |
|
579 retVal = true; |
|
580 } else do { |
|
581 // INVARIANT: We cannot block the thread (and run in a tight loop). |
|
582 // Instead we will process all current pending events and return. |
|
583 bool mustRelease = false; |
|
584 |
|
585 if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { |
|
586 // Process a pending user input event |
|
587 mustRelease = true; |
|
588 event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); |
|
589 } else { |
|
590 if (NSModalSession session = d->currentModalSession()) { |
|
591 if (flags & QEventLoop::WaitForMoreEvents) |
|
592 qt_mac_waitForMoreModalSessionEvents(); |
|
593 [NSApp runModalSession:session]; |
|
594 retVal = true; |
|
595 break; |
|
596 } else { |
|
597 event = [NSApp nextEventMatchingMask:NSAnyEventMask |
|
598 untilDate:nil |
|
599 inMode:NSDefaultRunLoopMode |
|
600 dequeue: YES]; |
|
601 |
|
602 if (event != nil) { |
|
603 if (flags & QEventLoop::ExcludeUserInputEvents) { |
|
604 if (IsMouseOrKeyEvent(event)) { |
|
605 // retain event here? |
|
606 [event retain]; |
|
607 d->queuedUserInputEvents.append(event); |
|
608 continue; |
|
609 } |
|
610 } |
|
611 } |
|
612 } |
|
613 } |
|
614 if (event) { |
|
615 if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) |
|
616 retVal = true; |
|
617 if (mustRelease) |
|
618 [event release]; |
|
619 } |
|
620 } while(!d->interrupt && event != nil); |
|
621 |
|
622 #else |
|
623 do { |
|
624 EventRef event; |
|
625 if (!(flags & QEventLoop::ExcludeUserInputEvents) |
|
626 && !d->queuedUserInputEvents.isEmpty()) { |
|
627 // process a pending user input event |
|
628 event = static_cast<EventRef>(d->queuedUserInputEvents.takeFirst()); |
|
629 } else { |
|
630 OSStatus err = ReceiveNextEvent(0,0, kEventDurationNoWait, true, &event); |
|
631 if(err != noErr) |
|
632 continue; |
|
633 // else |
|
634 if (flags & QEventLoop::ExcludeUserInputEvents) { |
|
635 UInt32 ekind = GetEventKind(event), |
|
636 eclass = GetEventClass(event); |
|
637 switch(eclass) { |
|
638 case kEventClassQt: |
|
639 if(ekind != kEventQtRequestContext) |
|
640 break; |
|
641 // fall through |
|
642 case kEventClassMouse: |
|
643 case kEventClassKeyboard: |
|
644 d->queuedUserInputEvents.append(event); |
|
645 continue; |
|
646 } |
|
647 } |
|
648 } |
|
649 |
|
650 if (!filterEvent(&event) && qt_mac_send_event(flags, event, 0)) |
|
651 retVal = true; |
|
652 ReleaseEvent(event); |
|
653 } while(!d->interrupt && GetNumEventsInQueue(GetMainEventQueue()) > 0); |
|
654 |
|
655 #endif |
|
656 |
|
657 bool canWait = (d->threadData->canWait |
|
658 && !retVal |
|
659 && !d->interrupt |
|
660 && (flags & QEventLoop::WaitForMoreEvents)); |
|
661 if (canWait) { |
|
662 // INVARIANT: We haven't processed any events yet. And we're told |
|
663 // to stay inside this function until at least one event is processed. |
|
664 qt_mac_waitForMoreEvents(); |
|
665 flags &= ~QEventLoop::WaitForMoreEvents; |
|
666 } else { |
|
667 // Done with event processing for now. |
|
668 // Leave the function: |
|
669 break; |
|
670 } |
|
671 } |
|
672 |
|
673 #ifdef QT_MAC_USE_COCOA |
|
674 // In case we _now_ process events using [NSApp run], we need to stop it to |
|
675 // ensure that: |
|
676 // 1. the QEventLoop that called us is still executing, or |
|
677 // 2. we have a modal session that needs to be spun instead. |
|
678 // In case this is a plain call to processEvents (perhaps from a loop) |
|
679 // from the application (rather than from a QEventLoop), we delay the |
|
680 // interrupting until we/ actually enter a lower loop level (hence the |
|
681 // deffered delete of the object below): |
|
682 QtMacInterruptDispatcherHelp::interruptLater(); |
|
683 #endif |
|
684 |
|
685 if (d->interrupt) { |
|
686 // We should continue to leave all recursion to processEvents until |
|
687 // processEvents is called again (e.g. from a QEventLoop that |
|
688 // was not yet told to quit: |
|
689 interrupt(); |
|
690 } |
|
691 |
|
692 return retVal; |
|
693 } |
|
694 |
|
695 void QEventDispatcherMac::wakeUp() |
|
696 { |
|
697 Q_D(QEventDispatcherMac); |
|
698 d->serialNumber.ref(); |
|
699 CFRunLoopSourceSignal(d->postedEventsSource); |
|
700 CFRunLoopWakeUp(mainRunLoop()); |
|
701 } |
|
702 |
|
703 void QEventDispatcherMac::flush() |
|
704 { |
|
705 if(qApp) { |
|
706 QWidgetList tlws = QApplication::topLevelWidgets(); |
|
707 for(int i = 0; i < tlws.size(); i++) { |
|
708 QWidget *tlw = tlws.at(i); |
|
709 if(tlw->isVisible()) |
|
710 macWindowFlush(qt_mac_window_for(tlw)); |
|
711 } |
|
712 } |
|
713 } |
|
714 |
|
715 /***************************************************************************** |
|
716 QEventDispatcherMac Implementation |
|
717 *****************************************************************************/ |
|
718 MacTimerHash QEventDispatcherMacPrivate::macTimerHash; |
|
719 bool QEventDispatcherMacPrivate::blockSendPostedEvents = false; |
|
720 |
|
721 #ifdef QT_MAC_USE_COCOA |
|
722 QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack; |
|
723 bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false; |
|
724 bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false; |
|
725 NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0; |
|
726 |
|
727 int QEventDispatcherMacPrivate::activeModalSessionCount() |
|
728 { |
|
729 // Returns the number of modal sessions created |
|
730 // (and not just pushed onto the stack, pending to be created) |
|
731 int count = 0; |
|
732 for (int i=cocoaModalSessionStack.size()-1; i>=0; --i) { |
|
733 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; |
|
734 if (info.session) |
|
735 ++count; |
|
736 } |
|
737 return count; |
|
738 } |
|
739 |
|
740 void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions() |
|
741 { |
|
742 // Stop all created modal session, and as such, make then |
|
743 // pending again. The next call to currentModalSession will |
|
744 // recreate the session on top again: |
|
745 int stackSize = cocoaModalSessionStack.size(); |
|
746 for (int i=stackSize-1; i>=0; --i) { |
|
747 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; |
|
748 if (info.session) { |
|
749 [NSApp endModalSession:info.session]; |
|
750 info.session = 0; |
|
751 } |
|
752 } |
|
753 currentModalSessionCached = 0; |
|
754 } |
|
755 |
|
756 NSModalSession QEventDispatcherMacPrivate::currentModalSession() |
|
757 { |
|
758 // If we have one or more modal windows, this function will create |
|
759 // a session for each of those, and return the one for the top. |
|
760 if (currentModalSessionCached) |
|
761 return currentModalSessionCached; |
|
762 |
|
763 if (cocoaModalSessionStack.isEmpty()) |
|
764 return 0; |
|
765 |
|
766 // Since this code will end up calling our Qt event handler |
|
767 // (also from beginModalSessionForWindow), we need to block |
|
768 // that to avoid side effects of events beeing delivered: |
|
769 QBoolBlocker block(blockSendPostedEvents, true); |
|
770 |
|
771 if (![NSApp isRunning]) { |
|
772 // Sadly, we need to introduce this little event flush |
|
773 // to stop dialogs from blinking/poping in front if a |
|
774 // modal session restart was needed: |
|
775 while (NSEvent *event = [NSApp nextEventMatchingMask:0 |
|
776 untilDate:nil |
|
777 inMode:NSDefaultRunLoopMode |
|
778 dequeue: YES]) { |
|
779 qt_mac_send_event(0, event, 0); |
|
780 } |
|
781 } |
|
782 |
|
783 int sessionCount = cocoaModalSessionStack.size(); |
|
784 for (int i=0; i<sessionCount; ++i) { |
|
785 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; |
|
786 if (!info.widget) |
|
787 continue; |
|
788 if (info.widget->testAttribute(Qt::WA_DontShowOnScreen)) |
|
789 continue; |
|
790 if (!info.session) { |
|
791 QMacCocoaAutoReleasePool pool; |
|
792 NSWindow *window = qt_mac_window_for(info.widget); |
|
793 if (!window) |
|
794 continue; |
|
795 info.session = [NSApp beginModalSessionForWindow:window]; |
|
796 } |
|
797 currentModalSessionCached = info.session; |
|
798 } |
|
799 |
|
800 return currentModalSessionCached; |
|
801 } |
|
802 |
|
803 static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal) |
|
804 { |
|
805 // For NSPanels (but not NSWindows, sadly), we can set the flag |
|
806 // worksWhenModal, so that they are active even when they are not modal. |
|
807 QList<QDialog *> dialogs = widget->findChildren<QDialog *>(); |
|
808 for (int i=0; i<dialogs.size(); ++i){ |
|
809 NSWindow *window = qt_mac_window_for(dialogs[i]); |
|
810 if (window && [window isKindOfClass:[NSPanel class]]) { |
|
811 [static_cast<NSPanel *>(window) setWorksWhenModal:worksWhenModal]; |
|
812 if (worksWhenModal && dialogs[i]->isVisible()){ |
|
813 [window orderFront:window]; |
|
814 } |
|
815 } |
|
816 } |
|
817 } |
|
818 |
|
819 void QEventDispatcherMacPrivate::updateChildrenWorksWhenModal() |
|
820 { |
|
821 // Make the dialog children of the widget |
|
822 // active. And make the dialog children of |
|
823 // the previous modal dialog unactive again: |
|
824 int size = cocoaModalSessionStack.size(); |
|
825 if (size > 0){ |
|
826 if (QWidget *prevModal = cocoaModalSessionStack[size-1].widget) |
|
827 setChildrenWorksWhenModal(prevModal, true); |
|
828 if (size > 1){ |
|
829 if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget) |
|
830 setChildrenWorksWhenModal(prevModal, false); |
|
831 } |
|
832 } |
|
833 } |
|
834 |
|
835 void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget) |
|
836 { |
|
837 // Add a new, empty (null), NSModalSession to the stack. |
|
838 // It will become active the next time QEventDispatcher::processEvents is called. |
|
839 // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer |
|
840 // is non-zero, and the session pointer is zero (it will become active upon a call to |
|
841 // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if |
|
842 // the widget pointer is zero, and the session pointer is non-zero (it will be fully |
|
843 // stopped in endModalSession(). |
|
844 QCocoaModalSessionInfo info = {widget, 0}; |
|
845 cocoaModalSessionStack.push(info); |
|
846 updateChildrenWorksWhenModal(); |
|
847 currentModalSessionCached = 0; |
|
848 } |
|
849 |
|
850 void QEventDispatcherMacPrivate::endModalSession(QWidget *widget) |
|
851 { |
|
852 // Mark all sessions attached to widget as pending to be stopped. We do this |
|
853 // by setting the widget pointer to zero, but leave the session pointer. |
|
854 // We don't tell cocoa to stop any sessions just yet, because cocoa only understands |
|
855 // when we stop the _current_ modal session (which is the session on top of |
|
856 // the stack, and might not belong to 'widget'). |
|
857 int stackSize = cocoaModalSessionStack.size(); |
|
858 for (int i=stackSize-1; i>=0; --i) { |
|
859 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; |
|
860 if (info.widget == widget) |
|
861 info.widget = 0; |
|
862 } |
|
863 |
|
864 // Now we stop, and remove, all sessions marked as pending |
|
865 // to be stopped on _top_ of the stack, if any: |
|
866 bool needToInterruptEventDispatcher = false; |
|
867 bool needToUpdateChildrenWorksWhenModal = false; |
|
868 |
|
869 for (int i=stackSize-1; i>=0; --i) { |
|
870 QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; |
|
871 if (info.widget) |
|
872 break; |
|
873 cocoaModalSessionStack.remove(i); |
|
874 needToUpdateChildrenWorksWhenModal = true; |
|
875 currentModalSessionCached = 0; |
|
876 if (info.session) { |
|
877 [NSApp endModalSession:info.session]; |
|
878 needToInterruptEventDispatcher = true; |
|
879 } |
|
880 } |
|
881 |
|
882 if (needToUpdateChildrenWorksWhenModal) |
|
883 updateChildrenWorksWhenModal(); |
|
884 if (needToInterruptEventDispatcher) |
|
885 QEventDispatcherMac::instance()->interrupt(); |
|
886 } |
|
887 |
|
888 #endif |
|
889 |
|
890 QEventDispatcherMacPrivate::QEventDispatcherMacPrivate() |
|
891 : interrupt(false) |
|
892 { |
|
893 } |
|
894 |
|
895 QEventDispatcherMac::QEventDispatcherMac(QObject *parent) |
|
896 : QAbstractEventDispatcher(*new QEventDispatcherMacPrivate, parent) |
|
897 { |
|
898 Q_D(QEventDispatcherMac); |
|
899 CFRunLoopSourceContext context; |
|
900 bzero(&context, sizeof(CFRunLoopSourceContext)); |
|
901 context.info = d; |
|
902 context.equal = QEventDispatcherMacPrivate::postedEventSourceEqualCallback; |
|
903 context.perform = QEventDispatcherMacPrivate::postedEventsSourcePerformCallback; |
|
904 d->postedEventsSource = CFRunLoopSourceCreate(0, 0, &context); |
|
905 Q_ASSERT(d->postedEventsSource); |
|
906 CFRunLoopAddSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes); |
|
907 |
|
908 CFRunLoopObserverContext observerContext; |
|
909 bzero(&observerContext, sizeof(CFRunLoopObserverContext)); |
|
910 observerContext.info = this; |
|
911 d->waitingObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, |
|
912 kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting, |
|
913 true, 0, |
|
914 QEventDispatcherMacPrivate::waitingObserverCallback, |
|
915 &observerContext); |
|
916 CFRunLoopAddObserver(mainRunLoop(), d->waitingObserver, kCFRunLoopCommonModes); |
|
917 |
|
918 /* The first cycle in the loop adds the source and the events of the source |
|
919 are not processed. |
|
920 We use an observer to process the posted events for the first |
|
921 execution of the loop. */ |
|
922 CFRunLoopObserverContext firstTimeObserverContext; |
|
923 bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext)); |
|
924 firstTimeObserverContext.info = d; |
|
925 d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, |
|
926 kCFRunLoopEntry, |
|
927 /* repeats = */ false, |
|
928 0, |
|
929 QEventDispatcherMacPrivate::firstLoopEntry, |
|
930 &firstTimeObserverContext); |
|
931 CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, kCFRunLoopCommonModes); |
|
932 } |
|
933 |
|
934 void QEventDispatcherMacPrivate::waitingObserverCallback(CFRunLoopObserverRef, |
|
935 CFRunLoopActivity activity, void *info) |
|
936 { |
|
937 if (activity == kCFRunLoopBeforeWaiting) |
|
938 emit static_cast<QEventDispatcherMac*>(info)->aboutToBlock(); |
|
939 else |
|
940 emit static_cast<QEventDispatcherMac*>(info)->awake(); |
|
941 } |
|
942 |
|
943 Boolean QEventDispatcherMacPrivate::postedEventSourceEqualCallback(const void *info1, const void *info2) |
|
944 { |
|
945 return info1 == info2; |
|
946 } |
|
947 |
|
948 inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents) |
|
949 { |
|
950 if (blockSendPostedEvents || d->interrupt) { |
|
951 CFRunLoopSourceSignal(d->postedEventsSource); |
|
952 } else { |
|
953 if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) { |
|
954 d->lastSerial = d->serialNumber; |
|
955 QApplicationPrivate::sendPostedEvents(0, 0, d->threadData); |
|
956 } |
|
957 } |
|
958 } |
|
959 |
|
960 void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref, |
|
961 CFRunLoopActivity activity, |
|
962 void *info) |
|
963 { |
|
964 Q_UNUSED(ref); |
|
965 Q_UNUSED(activity); |
|
966 processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); |
|
967 } |
|
968 |
|
969 void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info) |
|
970 { |
|
971 processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); |
|
972 } |
|
973 |
|
974 void QEventDispatcherMac::interrupt() |
|
975 { |
|
976 Q_D(QEventDispatcherMac); |
|
977 d->interrupt = true; |
|
978 wakeUp(); |
|
979 |
|
980 #ifndef QT_MAC_USE_COCOA |
|
981 CFRunLoopStop(mainRunLoop()); |
|
982 #else |
|
983 QMacCocoaAutoReleasePool pool; |
|
984 // In case we wait for more events inside |
|
985 // processEvents (or NSApp run), post a dummy to wake it up: |
|
986 static const short NSAppShouldStopForQt = SHRT_MAX; |
|
987 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint |
|
988 modifierFlags:0 timestamp:0. windowNumber:0 context:0 |
|
989 subtype:NSAppShouldStopForQt data1:0 data2:0] atStart:NO]; |
|
990 |
|
991 if (d->activeModalSessionCount() == 0) { |
|
992 // We should only stop NSApp if we actually started it (and |
|
993 // not some 3rd party application, e.g. if we are a plugin). |
|
994 if (d->nsAppRunCalledByQt) |
|
995 [NSApp stop:NSApp]; |
|
996 } |
|
997 #endif |
|
998 } |
|
999 |
|
1000 QEventDispatcherMac::~QEventDispatcherMac() |
|
1001 { |
|
1002 Q_D(QEventDispatcherMac); |
|
1003 //timer cleanup |
|
1004 MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin(); |
|
1005 while (it != QEventDispatcherMacPrivate::macTimerHash.end()) { |
|
1006 MacTimerInfo *t = it.value(); |
|
1007 if (t->runLoopTimer) { |
|
1008 CFRunLoopTimerInvalidate(t->runLoopTimer); |
|
1009 CFRelease(t->runLoopTimer); |
|
1010 } |
|
1011 delete t; |
|
1012 ++it; |
|
1013 } |
|
1014 QEventDispatcherMacPrivate::macTimerHash.clear(); |
|
1015 |
|
1016 // Remove CFSockets from the runloop. |
|
1017 for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) { |
|
1018 MacSocketInfo *socketInfo = (*it); |
|
1019 if (CFSocketIsValid(socketInfo->socket)) { |
|
1020 qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop); |
|
1021 CFRunLoopSourceInvalidate(socketInfo->runloop); |
|
1022 CFRelease(socketInfo->runloop); |
|
1023 CFSocketInvalidate(socketInfo->socket); |
|
1024 CFRelease(socketInfo->socket); |
|
1025 } |
|
1026 } |
|
1027 CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes); |
|
1028 CFRelease(d->postedEventsSource); |
|
1029 |
|
1030 CFRunLoopObserverInvalidate(d->waitingObserver); |
|
1031 CFRelease(d->waitingObserver); |
|
1032 |
|
1033 CFRunLoopObserverInvalidate(d->firstTimeObserver); |
|
1034 CFRelease(d->firstTimeObserver); |
|
1035 } |
|
1036 |
|
1037 ///////////////////////////////////////////////////////////////////////////// |
|
1038 |
|
1039 #ifdef QT_MAC_USE_COCOA |
|
1040 |
|
1041 QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0; |
|
1042 |
|
1043 QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false) |
|
1044 { |
|
1045 // This is the whole point of encapsulation this code |
|
1046 // inside a class; we can make the code (inside destructor) |
|
1047 // execute on lower loop level: |
|
1048 deleteLater(); |
|
1049 } |
|
1050 |
|
1051 QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp() |
|
1052 { |
|
1053 if (cancelled) |
|
1054 return; |
|
1055 |
|
1056 instance = 0; |
|
1057 |
|
1058 if (QEventDispatcherMacPrivate::currentExecIsNSAppRun) { |
|
1059 int activeCount = QEventDispatcherMacPrivate::activeModalSessionCount(); |
|
1060 if (activeCount > 0) { |
|
1061 // The problem we now have hit: [NSApp stop] will not stop NSApp |
|
1062 // if a session is active; it will stop the session instead. |
|
1063 // So to stop NSApp, we need to temporarily stop all the |
|
1064 // sessions, then stop NSApp, then restart the session on top again. |
|
1065 // We need to do this to ensure that we're not stuck inside |
|
1066 // [NSApp run] when we really should be running a modal session: |
|
1067 QEventDispatcherMacPrivate::temporarilyStopAllModalSessions(); |
|
1068 } |
|
1069 } |
|
1070 // Always interrupt once more in case the modal session stack changed |
|
1071 // while processEvents was called manually from within the application: |
|
1072 QEventDispatcherMac::instance()->interrupt(); |
|
1073 } |
|
1074 |
|
1075 void QtMacInterruptDispatcherHelp::interruptLater() { |
|
1076 if (instance) { |
|
1077 instance->cancelled = true; |
|
1078 delete instance; |
|
1079 } |
|
1080 instance = new QtMacInterruptDispatcherHelp; |
|
1081 } |
|
1082 |
|
1083 #endif |
|
1084 |
|
1085 QT_END_NAMESPACE |