iOS: Make the event dispatcher properly emit aboutToBlock() and awake()

This approach follows the same one used by the Cocoa event dispatcher.

Change-Id: I2813b09beae07d90477c9ca506924058ace13f34
Reviewed-by: Ian Dean <ian@mediator-software.com>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
This commit is contained in:
Tor Arne Vestbø 2013-06-17 15:50:08 +02:00 committed by The Qt Project
parent 321cc1f277
commit 56083adbea
2 changed files with 72 additions and 0 deletions

View File

@ -134,6 +134,8 @@ QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(QObject *parent)
, m_interrupted(false)
, m_postedEventsRunLoopSource(this, &QEventDispatcherCoreFoundation::processPostedEvents)
, m_blockingTimerRunLoopSource(this, &QEventDispatcherCoreFoundation::processTimers)
, m_awakeAndBlockObserver(this, &QEventDispatcherCoreFoundation::handleRunLoopActivity,
kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting)
, m_runLoopTimerRef(0)
{
m_cfSocketNotifier.setHostEventDispatcher(this);
@ -142,6 +144,8 @@ QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(QObject *parent)
m_blockingTimerRunLoopSource.addToMode(kCFRunLoopCommonModes);
m_blockingTimerRunLoopSource.addToMode(CFStringRef(UITrackingRunLoopMode));
m_awakeAndBlockObserver.addToMode(kCFRunLoopCommonModes);
}
QEventDispatcherCoreFoundation::~QEventDispatcherCoreFoundation()
@ -168,11 +172,32 @@ void QEventDispatcherCoreFoundation::processTimers()
maybeStartCFRunLoopTimer();
}
void QEventDispatcherCoreFoundation::handleRunLoopActivity(CFRunLoopActivity activity)
{
switch (activity) {
case kCFRunLoopBeforeWaiting:
emit aboutToBlock();
break;
case kCFRunLoopAfterWaiting:
emit awake();
break;
default:
Q_UNREACHABLE();
}
}
bool QEventDispatcherCoreFoundation::processEvents(QEventLoop::ProcessEventsFlags flags)
{
m_interrupted = false;
bool eventsProcessed = false;
// The documentation states that this signal is emitted after the event
// loop returns from a function that could block, which is not the case
// here, but all the other event dispatchers emit awake at the start of
// processEvents, and the QEventLoop auto-test has an explicit check for
// this behavior, so we assume it's for a good reason and do it as well.
emit awake();
bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents;
bool execFlagSet = (flags & QEventLoop::DialogExec) || (flags & QEventLoop::EventLoopExec);
bool useExecMode = execFlagSet && !excludeUserEvents;

View File

@ -132,6 +132,48 @@ private:
CFRunLoopSourceRef m_source;
};
template <class T = QEventDispatcherCoreFoundation>
class RunLoopObserver
{
public:
typedef void (T::*CallbackFunction) (CFRunLoopActivity activity);
RunLoopObserver(T *delegate, CallbackFunction callback, CFOptionFlags activities)
: m_delegate(delegate), m_callback(callback)
{
CFRunLoopObserverContext context = {};
context.info = this;
m_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, activities, true, 0, process, &context);
Q_ASSERT(m_observer);
}
~RunLoopObserver()
{
CFRunLoopObserverInvalidate(m_observer);
CFRelease(m_observer);
}
void addToMode(CFStringRef mode, CFRunLoopRef runLoop = 0)
{
if (!runLoop)
runLoop = CFRunLoopGetCurrent();
CFRunLoopAddObserver(runLoop, m_observer, mode);
}
private:
static void process(CFRunLoopObserverRef, CFRunLoopActivity activity, void *info)
{
RunLoopObserver *self = static_cast<RunLoopObserver *>(info);
((self->m_delegate)->*(self->m_callback))(activity);
}
T *m_delegate;
CallbackFunction m_callback;
CFRunLoopObserverRef m_observer;
};
class QEventDispatcherCoreFoundation : public QAbstractEventDispatcher
{
Q_OBJECT
@ -163,6 +205,8 @@ private:
RunLoopSource<> m_postedEventsRunLoopSource;
RunLoopSource<> m_blockingTimerRunLoopSource;
RunLoopObserver<> m_awakeAndBlockObserver;
QTimerInfoList m_timerInfoList;
CFRunLoopTimerRef m_runLoopTimerRef;
@ -170,9 +214,12 @@ private:
void processPostedEvents();
void processTimers();
void maybeStartCFRunLoopTimer();
void maybeStopCFRunLoopTimer();
void handleRunLoopActivity(CFRunLoopActivity activity);
static void nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info);
};