iOS: Generalize jumping event-dispatcher to handle QEventLoop:exec()
We already supported re-entering QApplication::exec(), so adding support for handling a generalized QEventLoop::exec() was nothing more than removing the qApplication->in_exec condition in processEvents() and the QThreadData::current()->quitNow condition when interrupting the event loop. Everything else is just renaming and rewording, now that the feature is not specific to QApplication::exec(). This means dialogs such as QFileDialog opened in the main() function will show something on screen, as we then fall back to the iOS root run-loop handling, while at the same time supporting QApplication exec once the dialog closes. We still don't hadle recursive QEventLoop:exec() at the root level, as that would require multiple stacks and detailed application knowledge about when to create them. Change-Id: I334a362d85796341a343ce82f3104ff5866bdc3f Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
This commit is contained in:
parent
54c5a79fd0
commit
2043adf97c
@ -58,11 +58,11 @@ public:
|
||||
|
||||
void handleRunLoopExit(CFRunLoopActivity activity);
|
||||
|
||||
void checkIfApplicationShouldQuit();
|
||||
void interruptQApplicationExec();
|
||||
void checkIfEventLoopShouldExit();
|
||||
void interruptEventLoopExec();
|
||||
|
||||
private:
|
||||
uint m_processEventCallsAfterAppExec;
|
||||
uint m_processEventCallsAfterExec;
|
||||
RunLoopObserver<QIOSEventDispatcher> m_runLoopExitObserver;
|
||||
};
|
||||
|
||||
|
@ -244,7 +244,7 @@ enum SetJumpResult
|
||||
{
|
||||
kJumpPointSetSuccessfully = 0,
|
||||
kJumpedFromEventDispatcherProcessEvents,
|
||||
kJumpedFromQApplicationExecInterrupt,
|
||||
kJumpedFromEventLoopExecInterrupt,
|
||||
kJumpedFromUserMainTrampoline,
|
||||
};
|
||||
|
||||
@ -395,9 +395,9 @@ static const char kApplicationWillTerminateExitCode = SIGTERM | 0x80;
|
||||
|
||||
// The runloop will not exit when the application is about to terminate,
|
||||
// so we'll never see the exit activity and have a chance to return from
|
||||
// QApplication::exec(). We initiate the return manually as a workaround.
|
||||
qEventDispatcherDebug() << "Manually triggering return from QApp exec";
|
||||
static_cast<QIOSEventDispatcher *>(qApp->eventDispatcher())->interruptQApplicationExec();
|
||||
// QEventLoop::exec(). We initiate the return manually as a workaround.
|
||||
qEventDispatcherDebug() << "Manually triggering return from event loop exec";
|
||||
static_cast<QIOSEventDispatcher *>(qApp->eventDispatcher())->interruptEventLoopExec();
|
||||
break;
|
||||
case kJumpedFromUserMainTrampoline:
|
||||
// The user's main has returned, so we're ready to let iOS terminate the application
|
||||
@ -415,7 +415,7 @@ QT_USE_NAMESPACE
|
||||
|
||||
QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent)
|
||||
: QEventDispatcherCoreFoundation(parent)
|
||||
, m_processEventCallsAfterAppExec(0)
|
||||
, m_processEventCallsAfterExec(0)
|
||||
, m_runLoopExitObserver(this, &QIOSEventDispatcher::handleRunLoopExit, kCFRunLoopExit)
|
||||
{
|
||||
}
|
||||
@ -432,27 +432,22 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo
|
||||
return false;
|
||||
}
|
||||
|
||||
QCoreApplicationPrivate *qApplication = static_cast<QCoreApplicationPrivate *>(QObjectPrivate::get(qApp));
|
||||
if (!m_processEventCallsAfterAppExec && qApplication->in_exec) {
|
||||
Q_ASSERT(flags & QEventLoop::EventLoopExec);
|
||||
if (!m_processEventCallsAfterExec && (flags & QEventLoop::EventLoopExec)) {
|
||||
++m_processEventCallsAfterExec;
|
||||
|
||||
// We know that app->in_exec is set just before executing the main event loop,
|
||||
// so the first processEvents call after that will be the main event loop.
|
||||
++m_processEventCallsAfterAppExec;
|
||||
|
||||
// We set a new jump point here that we can return to when the Qt application
|
||||
// is asked to exit, so that we can return from QCoreApplication::exec().
|
||||
// We set a new jump point here that we can return to when the event loop
|
||||
// is asked to exit, so that we can return from QEventLoop::exec().
|
||||
switch (setjmp(processEventExitJumpPoint)) {
|
||||
case kJumpPointSetSuccessfully:
|
||||
qEventDispatcherDebug() << "QApplication exec detected, jumping back to native runloop";
|
||||
qEventDispatcherDebug() << "QEventLoop exec detected, jumping back to native runloop";
|
||||
longjmp(processEventEnterJumpPoint, kJumpedFromEventDispatcherProcessEvents);
|
||||
break;
|
||||
case kJumpedFromQApplicationExecInterrupt:
|
||||
// QCoreApplication has quit (either by the hand of the user, or the iOS termination
|
||||
case kJumpedFromEventLoopExecInterrupt:
|
||||
// The event loop has exited (either by the hand of the user, or the iOS termination
|
||||
// signal), and we jumped back though processEventExitJumpPoint. We return from processEvents,
|
||||
// which will emit aboutToQuit and then return to the user's main, which can do
|
||||
// whatever it wants, including calling exec() on the application again.
|
||||
qEventDispatcherDebug() << "kJumpedFromQApplicationExecInterrupt, returning with eventsProcessed = true";
|
||||
// which will emit aboutToQuit if it's QApplication's event loop, and then return to the user's
|
||||
// main, which can do whatever it wants, including calling exec() on the application again.
|
||||
qEventDispatcherDebug() << "kJumpedFromEventLoopExecInterrupt, returning with eventsProcessed = true";
|
||||
return true;
|
||||
default:
|
||||
qFatal("Unexpected jump result in event loop integration");
|
||||
@ -461,19 +456,19 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (m_processEventCallsAfterAppExec)
|
||||
++m_processEventCallsAfterAppExec;
|
||||
if (m_processEventCallsAfterExec)
|
||||
++m_processEventCallsAfterExec;
|
||||
|
||||
bool processedEvents = QEventDispatcherCoreFoundation::processEvents(flags);
|
||||
|
||||
if (m_processEventCallsAfterAppExec)
|
||||
--m_processEventCallsAfterAppExec;
|
||||
if (m_processEventCallsAfterExec)
|
||||
--m_processEventCallsAfterExec;
|
||||
|
||||
// If we're running with nested event loops and the application is quit,
|
||||
// then the forwarded interrupt call will happen while our processEvent
|
||||
// counter is still 2, and we won't detect that we're about to fall down
|
||||
// to the root iOS run-loop. We do an extra check here to catch that case.
|
||||
checkIfApplicationShouldQuit();
|
||||
checkIfEventLoopShouldExit();
|
||||
|
||||
return processedEvents;
|
||||
}
|
||||
@ -488,12 +483,12 @@ void QIOSEventDispatcher::interrupt()
|
||||
// If an interrupt happens as part of a non-nested event loop, that is,
|
||||
// by processing an event or timer in the root iOS run-loop, we'll be
|
||||
// able to detect it here.
|
||||
checkIfApplicationShouldQuit();
|
||||
checkIfEventLoopShouldExit();
|
||||
}
|
||||
|
||||
void QIOSEventDispatcher::checkIfApplicationShouldQuit()
|
||||
void QIOSEventDispatcher::checkIfEventLoopShouldExit()
|
||||
{
|
||||
if (QThreadData::current()->quitNow && m_processEventCallsAfterAppExec == 1) {
|
||||
if (m_processEventCallsAfterExec == 1) {
|
||||
qEventDispatcherDebug() << "Hit root runloop level, watching for runloop exit";
|
||||
m_runLoopExitObserver.addToMode(kCFRunLoopCommonModes);
|
||||
}
|
||||
@ -505,26 +500,25 @@ void QIOSEventDispatcher::handleRunLoopExit(CFRunLoopActivity activity)
|
||||
|
||||
m_runLoopExitObserver.removeFromMode(kCFRunLoopCommonModes);
|
||||
|
||||
interruptQApplicationExec();
|
||||
interruptEventLoopExec();
|
||||
}
|
||||
|
||||
void QIOSEventDispatcher::interruptQApplicationExec()
|
||||
void QIOSEventDispatcher::interruptEventLoopExec()
|
||||
{
|
||||
Q_ASSERT(QThreadData::current()->quitNow);
|
||||
Q_ASSERT(m_processEventCallsAfterAppExec == 1);
|
||||
Q_ASSERT(m_processEventCallsAfterExec == 1);
|
||||
|
||||
--m_processEventCallsAfterAppExec;
|
||||
--m_processEventCallsAfterExec;
|
||||
|
||||
// We re-set applicationProcessEventsReturnPoint here so that future
|
||||
// calls to QApplication::exec() will end up back here after entering
|
||||
// calls to QEventLoop::exec() will end up back here after entering
|
||||
// processEvents, instead of back in didFinishLaunchingWithOptions.
|
||||
switch (setjmp(processEventEnterJumpPoint)) {
|
||||
case kJumpPointSetSuccessfully:
|
||||
qEventDispatcherDebug() << "Jumping back to processEvents";
|
||||
longjmp(processEventExitJumpPoint, kJumpedFromQApplicationExecInterrupt);
|
||||
longjmp(processEventExitJumpPoint, kJumpedFromEventLoopExecInterrupt);
|
||||
break;
|
||||
case kJumpedFromEventDispatcherProcessEvents:
|
||||
// QCoreApplication was re-executed
|
||||
// QEventLoop was re-executed
|
||||
qEventDispatcherDebug() << "kJumpedFromEventDispatcherProcessEvents";
|
||||
break;
|
||||
default:
|
||||
|
Loading…
Reference in New Issue
Block a user