6#include <QtCore/qdebug.h>
7#include <QtCore/qmetaobject.h>
8#include <QtCore/qthread.h>
9#include <QtCore/private/qcoreapplication_p.h>
10#include <QtCore/private/qcore_unix_p.h>
11#include <QtCore/private/qthread_p.h>
16# include <AppKit/NSApplication.h>
17#elif defined(Q_OS_WATCHOS)
18# include <WatchKit/WatchKit.h>
20# include <UIKit/UIApplication.h>
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51@interface QT_MANGLE_NAMESPACE(RunLoopModeTracker) : NSObject
54QT_NAMESPACE_ALIAS_OBJC_CLASS(RunLoopModeTracker);
56@implementation QT_MANGLE_NAMESPACE(RunLoopModeTracker) {
57 QStack<CFStringRef> m_runLoopModes;
62 if ((self = [super init])) {
63 m_runLoopModes.push(kCFRunLoopDefaultMode);
65#if !defined(Q_OS_WATCHOS)
66 if (!qt_apple_isApplicationExtension()) {
67 [[NSNotificationCenter defaultCenter]
68 addObserver:self selector:@selector(receivedNotification:)
69 name:nil object:qt_apple_sharedApplication()];
79 [NSNotificationCenter.defaultCenter removeObserver:self];
84static CFStringRef runLoopMode(NSDictionary *dictionary)
86 for (NSString *key in dictionary) {
87 if (CFStringHasSuffix((CFStringRef)key, CFSTR(
"RunLoopMode")))
88 return (CFStringRef)dictionary[key];
94- (
void)receivedNotification:(NSNotification *)notification
96 if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR(
"RunLoopModePushNotification"))) {
97 if (CFStringRef mode = runLoopMode(notification.userInfo))
98 m_runLoopModes.push(mode);
100 qCWarning(lcEventDispatcher) <<
"Encountered run loop push notification without run loop mode!";
102 }
else if (CFStringHasSuffix((CFStringRef)notification.name, CFSTR(
"RunLoopModePopNotification"))) {
103 CFStringRef mode = runLoopMode(notification.userInfo);
104 if (CFStringCompare(mode, self.currentMode, 0) == kCFCompareEqualTo)
105 m_runLoopModes.pop();
107 qCWarning(lcEventDispatcher) <<
"Tried to pop run loop mode"
108 << qPrintable(QString::fromCFString(mode)) <<
"that was never pushed!";
110 Q_ASSERT(m_runLoopModes.size() >= 1);
114- (CFStringRef)currentMode
116 return m_runLoopModes.top();
123class RunLoopDebugger :
public QObject
132 #define Q_MIRROR_ENUM(name) name = name
151#define Q_ENUM_PRINTER(enumName)
152 static const char* qPrintable##enumName(int value)
154 return RunLoopDebugger::staticMetaObject.enumerator(RunLoopDebugger::staticMetaObject.indexOfEnumerator(#enumName)).valueToKey(value);
162 s << tv.tv_sec <<
"." << qSetFieldWidth(9) << qSetPadChar(QChar(48)) << tv.tv_nsec << Qt::reset;
169#pragma mark - Class definition
171QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(QObject *parent)
172 : QAbstractEventDispatcherV2(parent)
173 , m_processEvents(QEventLoop::EventLoopExec)
174 , m_postedEventsRunLoopSource(
this, &QEventDispatcherCoreFoundation::processPostedEvents)
175 , m_runLoopActivityObserver(
this, &QEventDispatcherCoreFoundation::handleRunLoopActivity, kCFRunLoopAllActivities)
176 , m_runLoopModeTracker([[RunLoopModeTracker alloc] init])
178 , m_blockedRunLoopTimer(0)
179 , m_overdueTimerScheduled(
false)
183void QEventDispatcherCoreFoundation::startingUp()
187 Q_ASSERT(QThread::currentThread() == thread());
189 m_runLoop = QCFType<CFRunLoopRef>::constructFromGet(CFRunLoopGetCurrent());
190 m_cfSocketNotifier.setHostEventDispatcher(
this);
191 m_postedEventsRunLoopSource.addToMode(kCFRunLoopCommonModes);
192 m_runLoopActivityObserver.addToMode(kCFRunLoopCommonModes);
195QEventDispatcherCoreFoundation::~QEventDispatcherCoreFoundation()
198 m_timerInfoList.clearTimers();
200 m_cfSocketNotifier.removeSocketNotifiers();
203QEventLoop *QEventDispatcherCoreFoundation::currentEventLoop()
const
205 QEventLoop *eventLoop = QThreadData::current()->eventLoops.top();
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlags flags)
230 QT_APPLE_SCOPED_LOG_ACTIVITY(lcEventDispatcher().isDebugEnabled(),
"processEvents");
232 bool eventsProcessed =
false;
234 if (flags & (QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers))
235 qCWarning(lcEventDispatcher) <<
"processEvents() flags" << flags <<
"not supported on iOS";
237 qCDebug(lcEventDispatcher) <<
"Processing events with flags" << flags;
239 if (m_blockedRunLoopTimer) {
240 Q_ASSERT(m_blockedRunLoopTimer == m_runLoopTimer);
242 qCDebug(lcEventDispatcher) <<
"Recursing from blocked timer" << m_blockedRunLoopTimer;
247 if (m_processEvents.deferredWakeUp) {
251 m_postedEventsRunLoopSource.signal();
252 m_processEvents.deferredWakeUp =
false;
254 qCDebug(lcEventDispatcher) <<
"Processed deferred wake-up";
264 ProcessEventsState previousState = m_processEvents;
265 m_processEvents = ProcessEventsState(flags);
267 bool returnAfterSingleSourceHandled = !(m_processEvents.flags & QEventLoop::EventLoopExec);
270 CFStringRef mode = [m_runLoopModeTracker currentMode];
272 CFTimeInterval duration = (m_processEvents.flags & QEventLoop::WaitForMoreEvents) ?
273 kCFTimeIntervalDistantFuture : kCFTimeIntervalMinimum;
275 qCDebug(lcEventDispatcher) <<
"Calling CFRunLoopRunInMode =" << qPrintable(QString::fromCFString(mode))
276 <<
"for" << duration <<
"ms, processing single source =" << returnAfterSingleSourceHandled;
278 SInt32 result = CFRunLoopRunInMode(mode, duration, returnAfterSingleSourceHandled);
280 qCDebug(lcEventDispatcher) <<
"result =" << qPrintableResult(result);
282 eventsProcessed |= (result == kCFRunLoopRunHandledSource
283 || m_processEvents.processedPostedEvents
284 || m_processEvents.processedTimers);
286 if (result == kCFRunLoopRunFinished) {
290 }
else if (m_processEvents.wasInterrupted) {
292 if (m_processEvents.flags & QEventLoop::EventLoopExec) {
293 Q_ASSERT(result == kCFRunLoopRunStopped);
303 if (!currentEventLoop()->isRunning()) {
304 qCDebug(lcEventDispatcher) <<
"Top level event loop was exited";
307 qCDebug(lcEventDispatcher) <<
"Top level event loop still running, making another pass";
312 qCDebug(lcEventDispatcher) <<
"Top level processEvents was interrupted";
317 if (m_processEvents.flags & QEventLoop::EventLoopExec) {
327 if (result == kCFRunLoopRunHandledSource) {
333 m_processEvents.flags &= ~
int(QEventLoop::WaitForMoreEvents);
336 }
else if (m_overdueTimerScheduled && !m_processEvents.processedTimers) {
341 qCDebug(lcEventDispatcher) <<
"Manually processing timers due to overdue timer";
343 eventsProcessed =
true;
350 if (m_blockedRunLoopTimer) {
352 m_runLoopTimer = m_blockedRunLoopTimer;
355 if (m_processEvents.deferredUpdateTimers)
358 if (m_processEvents.deferredWakeUp) {
359 m_postedEventsRunLoopSource.signal();
360 qCDebug(lcEventDispatcher) <<
"Processed deferred wake-up";
363 bool wasInterrupted = m_processEvents.wasInterrupted;
366 m_processEvents = previousState;
368 if (wasInterrupted) {
373 qCDebug(lcEventDispatcher) <<
"Forwarding interrupt in case of nested processEvents";
377 qCDebug(lcEventDispatcher) <<
"Returning with eventsProcessed =" << eventsProcessed;
379 return eventsProcessed;
382bool QEventDispatcherCoreFoundation::processPostedEvents()
384 QT_APPLE_SCOPED_LOG_ACTIVITY(lcEventDispatcher().isDebugEnabled(),
"processPostedEvents");
386 if (m_processEvents.processedPostedEvents && !(m_processEvents.flags & QEventLoop::EventLoopExec)) {
387 qCDebug(lcEventDispatcher) <<
"Already processed events this pass";
391 m_processEvents.processedPostedEvents =
true;
393 qCDebug(lcEventDispatcher) <<
"Sending posted events for"
394 << QEventLoop::ProcessEventsFlags(m_processEvents.flags.loadRelaxed());
395 QCoreApplication::sendPostedEvents();
400void QEventDispatcherCoreFoundation::processTimers(CFRunLoopTimerRef timer)
402 QT_APPLE_SCOPED_LOG_ACTIVITY(lcEventDispatcher().isDebugEnabled(),
"processTimers");
404 if (m_processEvents.processedTimers && !(m_processEvents.flags & QEventLoop::EventLoopExec)) {
405 qCDebug(lcEventDispatcher) <<
"Already processed timers this pass";
406 m_processEvents.deferredUpdateTimers =
true;
410 qCDebug(lcEventDispatcher) <<
"CFRunLoopTimer" << timer <<
"fired, activating Qt timers";
421 CFRunLoopTimerRef previouslyBlockedRunLoopTimer = m_blockedRunLoopTimer;
423 m_blockedRunLoopTimer = timer;
424 m_timerInfoList.activateTimers();
425 m_blockedRunLoopTimer = previouslyBlockedRunLoopTimer;
426 m_processEvents.processedTimers =
true;
434void QEventDispatcherCoreFoundation::handleRunLoopActivity(CFRunLoopActivity activity)
436 qCDebug(lcEventDispatcherActivity) <<
"Runloop entered activity" << qPrintableActivity(activity);
439 case kCFRunLoopBeforeWaiting:
440 if (m_processEvents.processedTimers
441 && !(m_processEvents.flags & QEventLoop::EventLoopExec)
442 && m_processEvents.flags & QEventLoop::WaitForMoreEvents) {
452 case kCFRunLoopAfterWaiting:
455 case kCFRunLoopEntry:
456 case kCFRunLoopBeforeTimers:
457 case kCFRunLoopBeforeSources:
465void QEventDispatcherCoreFoundation::wakeUp()
467 if (m_processEvents.processedPostedEvents && !(m_processEvents.flags & QEventLoop::EventLoopExec)) {
476 m_processEvents.deferredWakeUp =
true;
477 qCDebug(lcEventDispatcher) <<
"Already processed posted events, deferring wakeUp";
481 m_postedEventsRunLoopSource.signal();
483 CFRunLoopWakeUp(m_runLoop);
485 qCDebug(lcEventDispatcher) <<
"Signaled posted event run-loop source";
488void QEventDispatcherCoreFoundation::interrupt()
490 qCDebug(lcEventDispatcher) <<
"Marking current processEvent as interrupted";
491 m_processEvents.wasInterrupted =
true;
492 CFRunLoopStop(m_runLoop);
495#pragma mark - Socket notifiers
497void QEventDispatcherCoreFoundation::registerSocketNotifier(QSocketNotifier *notifier)
499 m_cfSocketNotifier.registerSocketNotifier(notifier);
502void QEventDispatcherCoreFoundation::unregisterSocketNotifier(QSocketNotifier *notifier)
504 m_cfSocketNotifier.unregisterSocketNotifier(notifier);
509void QEventDispatcherCoreFoundation::registerTimer(Qt::TimerId timerId, Duration interval,
510 Qt::TimerType timerType, QObject *object)
512 qCDebug(lcEventDispatcherTimers) <<
"Registering timer with id =" <<
int(timerId) <<
"interval =" << interval
513 <<
"type =" << timerType <<
"object =" << object;
515 Q_ASSERT(qToUnderlying(timerId) > 0 && interval.count() >= 0 && object);
516 Q_ASSERT(object->thread() == thread() && thread() == QThread::currentThread());
518 m_timerInfoList.registerTimer(timerId, interval, timerType, object);
522bool QEventDispatcherCoreFoundation::unregisterTimer(Qt::TimerId timerId)
524 Q_ASSERT(qToUnderlying(timerId) > 0);
525 Q_ASSERT(thread() == QThread::currentThread());
527 bool returnValue = m_timerInfoList.unregisterTimer(timerId);
529 qCDebug(lcEventDispatcherTimers) <<
"Unegistered timer with id =" << qToUnderlying(timerId)
530 <<
"Timers left:" << m_timerInfoList.size();
536bool QEventDispatcherCoreFoundation::unregisterTimers(QObject *object)
538 Q_ASSERT(object && object->thread() == thread() && thread() == QThread::currentThread());
540 bool returnValue = m_timerInfoList.unregisterTimers(object);
542 qCDebug(lcEventDispatcherTimers) <<
"Unegistered timers for object =" << object <<
"Timers left:" << m_timerInfoList.size();
548QList<QAbstractEventDispatcher::TimerInfoV2>
549QEventDispatcherCoreFoundation::timersForObject(QObject *object)
const
552 return m_timerInfoList.registeredTimers(object);
555QEventDispatcherCoreFoundation::Duration
556QEventDispatcherCoreFoundation::remainingTime(Qt::TimerId timerId)
const
558 Q_ASSERT(qToUnderlying(timerId) > 0);
559 return m_timerInfoList.remainingDuration(timerId);
562void QEventDispatcherCoreFoundation::updateTimers()
564 if (m_timerInfoList.size() > 0) {
567 using namespace std::chrono_literals;
568 using DoubleSeconds = std::chrono::duration<
double, std::ratio<1>>;
570 CFAbsoluteTime timeToFire;
571 auto opt = m_timerInfoList.timerWait();
572 DoubleSeconds secs{};
575 secs = DoubleSeconds{*opt};
576 timeToFire = CFAbsoluteTimeGetCurrent() + secs.count();
579 timeToFire = kCFTimeIntervalDistantFuture;
582 if (!m_runLoopTimer) {
583 m_runLoopTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault,
584 timeToFire, kCFTimeIntervalDistantFuture, 0, 0, ^(CFRunLoopTimerRef timer) {
585 processTimers(timer);
588 CFRunLoopAddTimer(m_runLoop, m_runLoopTimer, kCFRunLoopCommonModes);
589 qCDebug(lcEventDispatcherTimers) <<
"Created new CFRunLoopTimer" << m_runLoopTimer;
592 CFRunLoopTimerSetNextFireDate(m_runLoopTimer, timeToFire);
593 qCDebug(lcEventDispatcherTimers) <<
"Re-scheduled CFRunLoopTimer" << m_runLoopTimer;
596 m_overdueTimerScheduled = secs > 0s;
598 qCDebug(lcEventDispatcherTimers) <<
"Next timeout in" << secs;
604 m_overdueTimerScheduled =
false;
608void QEventDispatcherCoreFoundation::invalidateTimer()
610 if (!m_runLoopTimer || (m_runLoopTimer == m_blockedRunLoopTimer))
613 CFRunLoopTimerInvalidate(m_runLoopTimer);
614 qCDebug(lcEventDispatcherTimers) <<
"Invalidated CFRunLoopTimer" << m_runLoopTimer;
616 CFRelease(m_runLoopTimer);
622#include "qeventdispatcher_cf.moc"
623#include "moc_qeventdispatcher_cf_p.cpp"
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
#define Q_ENUM_PRINTER(enumName)
#define Q_MIRROR_ENUM(name)
static const CFTimeInterval kCFTimeIntervalMinimum
static const CFTimeInterval kCFTimeIntervalDistantFuture
QDebug operator<<(QDebug debug, QIODevice::OpenMode modes)