summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h12
-rw-r--r--src/plugins/platforms/ios/ios.pro3
-rw-r--r--src/plugins/platforms/ios/qiosapplicationdelegate.mm23
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.h (renamed from src/plugins/platforms/ios/qiosmain_wrapper.mm)34
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.mm522
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm8
6 files changed, 569 insertions, 33 deletions
diff --git a/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h b/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h
index 0b9dda2b7f..1742a9b939 100644
--- a/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h
+++ b/src/platformsupport/eventdispatchers/qeventdispatcher_cf_p.h
@@ -168,7 +168,17 @@ public:
if (!runLoop)
runLoop = CFRunLoopGetCurrent();
- CFRunLoopAddObserver(runLoop, m_observer, mode);
+ if (!CFRunLoopContainsObserver(runLoop, m_observer, mode))
+ CFRunLoopAddObserver(runLoop, m_observer, mode);
+ }
+
+ void removeFromMode(CFStringRef mode, CFRunLoopRef runLoop = 0)
+ {
+ if (!runLoop)
+ runLoop = CFRunLoopGetCurrent();
+
+ if (CFRunLoopContainsObserver(runLoop, m_observer, mode))
+ CFRunLoopRemoveObserver(runLoop, m_observer, mode);
}
private:
diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro
index 263577d43f..9b53974998 100644
--- a/src/plugins/platforms/ios/ios.pro
+++ b/src/plugins/platforms/ios/ios.pro
@@ -9,8 +9,8 @@ LIBS += -framework Foundation -framework UIKit -framework QuartzCore
OBJECTIVE_SOURCES = \
plugin.mm \
- qiosmain_wrapper.mm \
qiosintegration.mm \
+ qioseventdispatcher.mm \
qioswindow.mm \
qiosscreen.mm \
qiosbackingstore.mm \
@@ -24,6 +24,7 @@ OBJECTIVE_SOURCES = \
HEADERS = \
qiosintegration.h \
+ qioseventdispatcher.h \
qioswindow.h \
qiosscreen.h \
qiosbackingstore.h \
diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm
index d4fd613ae3..e06d2b8840 100644
--- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm
+++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm
@@ -46,8 +46,6 @@
#include <QtCore/QtCore>
-extern "C" int main(int argc, char *argv[]);
-
@implementation QIOSApplicationDelegate
@synthesize window;
@@ -68,30 +66,9 @@ extern "C" int main(int argc, char *argv[]);
[self.window makeKeyAndVisible];
- // We schedule the main-redirection for the next eventloop pass so that we
- // can return from this function and let UIApplicationMain finish its job.
- [NSTimer scheduledTimerWithTimeInterval:.01f target:self
- selector:@selector(runUserMain) userInfo:nil repeats:NO];
-
return YES;
}
-- (void)runUserMain
-{
- NSArray *arguments = [[NSProcessInfo processInfo] arguments];
- int argc = arguments.count;
- char **argv = new char*[argc];
- for (int i = 0; i < argc; ++i) {
- NSString *arg = [arguments objectAtIndex:i];
- argv[i] = reinterpret_cast<char *>(malloc([arg lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]]));
- strcpy(argv[i], [arg cStringUsingEncoding:[NSString defaultCStringEncoding]]);
- }
-
- main(argc, argv);
- delete[] argv;
-}
-
-
- (void)dealloc
{
[qiosViewController release];
diff --git a/src/plugins/platforms/ios/qiosmain_wrapper.mm b/src/plugins/platforms/ios/qioseventdispatcher.h
index cb9a2c161e..83267e80ea 100644
--- a/src/plugins/platforms/ios/qiosmain_wrapper.mm
+++ b/src/plugins/platforms/ios/qioseventdispatcher.h
@@ -39,11 +39,33 @@
**
****************************************************************************/
-#include "qiosapplicationdelegate.h"
+#ifndef QIOSEVENTDISPATCHER_H
+#define QIOSEVENTDISPATCHER_H
-extern "C" int qtmn(int argc, char *argv[])
+#include <QtPlatformSupport/private/qeventdispatcher_cf_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIOSEventDispatcher : public QEventDispatcherCoreFoundation
{
- @autoreleasepool {
- return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class]));
- }
-}
+ Q_OBJECT
+
+public:
+ explicit QIOSEventDispatcher(QObject *parent = 0);
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) Q_DECL_OVERRIDE;
+ void interrupt() Q_DECL_OVERRIDE;
+
+ void handleRunLoopExit(CFRunLoopActivity activity);
+
+ void checkIfApplicationShouldQuit();
+ void interruptQApplicationExec();
+
+private:
+ uint m_processEventCallsAfterAppExec;
+ RunLoopObserver<QIOSEventDispatcher> m_runLoopExitObserver;
+};
+
+QT_END_NAMESPACE
+
+#endif // QIOSEVENTDISPATCHER_H \ No newline at end of file
diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm
new file mode 100644
index 0000000000..6148338000
--- /dev/null
+++ b/src/plugins/platforms/ios/qioseventdispatcher.mm
@@ -0,0 +1,522 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qioseventdispatcher.h"
+#include "qiosapplicationdelegate.h"
+#include "qiosglobal.h"
+
+#include <QtCore/qprocessordetection.h>
+#include <QtCore/private/qcoreapplication_p.h>
+#include <QtCore/private/qthread_p.h>
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSString.h>
+#import <Foundation/NSProcessInfo.h>
+#import <Foundation/NSThread.h>
+#import <Foundation/NSNotification.h>
+
+#import <UIKit/UIApplication.h>
+
+#include <setjmp.h> // Here be dragons
+
+#include <sys/mman.h>
+
+#define qAlignDown(val, align) val & ~(align - 1)
+#define qAlignUp(val, align) qAlignDown(val + (align - 1), align)
+
+static const size_t kBytesPerKiloByte = 1024;
+static const long kPageSize = sysconf(_SC_PAGESIZE);
+
+/*
+ The following diagram shows the layout of the reserved
+ stack in relation to the regular stack, and the basic
+ flow of the initial startup sequence. Note how we end
+ up back in applicationDidLaunch after the user's main
+ recurses into qApp-exec(), which allows us to return
+ from applicationDidLaunch and spin the run-loop at the
+ same level (UIApplicationMain) as iOS nativly does.
+
+ +-----------------------------+
+ | qtmn() |
+ | +--------------------+ <-- base
+ | +----> main() | |
+ | | +--------------------+ |
+ | | | ... | |
+ | | +--------------------+ |
+ | | | qApp->exec() | |
+ | | +--------------------+ |
+ | | | processEvents() | |
+ | | | | |
+ | | +--+ longjmp(a) | |
+ | | | | | |
+ | | | +--------------------+ |
+ | | | | | |
+ | | | | | |
+ | | | | unused | |
+ | | | | | |
+ | | | | | |
+ | | | +--------------------+ <-- limit
+ | | | | memory guard | |
+ | | | +--------------------+ <-- reservedStack
+ +-|-|-------------------------+
+ | | | UIApplicationMain() |
+ +-|-|-------------------------+
+ | | | applicationDidLaunch() |
+ | | | |
+ | | +--> setjmp(a) |
+ | +----+ trampoline() |
+ | |
+ +-----------------------------+
+
+ Note: the diagram does not reflect alignment issues.
+*/
+
+namespace
+{
+ struct Stack
+ {
+ uintptr_t base;
+ uintptr_t limit;
+
+ static size_t computeSize(size_t requestedSize)
+ {
+ if (!requestedSize)
+ return 0;
+
+ // The stack size must be a multiple of 4 KB
+ size_t stackSize = qAlignUp(requestedSize, 4 * kBytesPerKiloByte);
+
+ // Be at least 16 KB
+ stackSize = qMax(16 * kBytesPerKiloByte, stackSize);
+
+ // Have enough extra space for our (aligned) memory guard
+ stackSize += (2 * kPageSize);
+
+ // But not exceed the 1MB maximum (adjusted to account for current stack usage)
+ stackSize = qMin(stackSize, ((1024 - 64) * kBytesPerKiloByte));
+
+ // Which we verify, just in case
+ struct rlimit stackLimit = {0, 0};
+ if (getrlimit(RLIMIT_STACK, &stackLimit) == 0 && stackSize > stackLimit.rlim_cur)
+ qFatal("Unexpectedly exceeded stack limit");
+
+ return stackSize;
+ }
+
+ void adopt(void* memory, size_t size)
+ {
+ uintptr_t memoryStart = uintptr_t(memory);
+
+ // Add memory guard at the end of the reserved stack, so that any stack
+ // overflow during the user's main will trigger an exception at that point,
+ // and not when we return and find that the current stack has been smashed.
+ uintptr_t memoryGuardStart = qAlignUp(memoryStart, kPageSize);
+ if (mprotect((void*)memoryGuardStart, kPageSize, PROT_NONE))
+ qWarning() << "Failed to add memory guard:" << strerror(errno);
+
+ // We don't consider the memory guard part of the usable stack space
+ limit = memoryGuardStart + kPageSize;
+
+ // The stack grows downwards in memory, so the stack base is actually
+ // at the end of the reserved stack space. And, as the memory guard
+ // was page aligned, we need to align down the base as well, to
+ // keep the requirement that the stack size is a multiple of 4K.
+ base = qAlignDown(memoryStart + size, kPageSize);
+ }
+
+ bool isValid()
+ {
+ return base && limit;
+ }
+
+ size_t size()
+ {
+ return base - limit;
+ }
+
+ static const int kScribblePattern;
+
+ void scribble()
+ {
+ memset_pattern4((void*)limit, &kScribblePattern, size());
+ }
+
+ void printUsage()
+ {
+ uintptr_t highWaterMark = limit;
+ for (; highWaterMark < base; highWaterMark += 4) {
+ if (memcmp((void*)highWaterMark, &kScribblePattern, 4))
+ break;
+ }
+
+ qDebug("main() used roughly %lu bytes of stack space", (base - highWaterMark));
+ }
+ };
+
+ const int Stack::kScribblePattern = 0xfafafafa;
+
+ Stack userMainStack;
+
+ jmp_buf processEventEnterJumpPoint;
+ jmp_buf processEventExitJumpPoint;
+
+ bool applicationAboutToTerminate = false;
+ jmp_buf applicationWillTerminateJumpPoint;
+
+ bool debugStackUsage = false;
+}
+
+static int infoPlistValue(NSString* key, int defaultValue)
+{
+ static NSBundle *bundle = [NSBundle mainBundle];
+ NSNumber* value = [bundle objectForInfoDictionaryKey:key];
+ return value ? [value intValue] : defaultValue;
+}
+
+extern "C" int qtmn(int argc, char *argv[])
+{
+ @autoreleasepool {
+ size_t defaultStackSize = 512 * kBytesPerKiloByte; // Same as secondary threads
+
+ uint requestedStackSize = qMax(0, infoPlistValue(@"QtRunLoopIntegrationStackSize", defaultStackSize));
+
+ if (infoPlistValue(@"QtRunLoopIntegrationDisableSeparateStack", false))
+ requestedStackSize = 0;
+
+ char reservedStack[Stack::computeSize(requestedStackSize)];
+
+ if (sizeof(reservedStack) > 0) {
+ userMainStack.adopt(reservedStack, sizeof(reservedStack));
+
+ if (infoPlistValue(@"QtRunLoopIntegrationDebugStackUsage", false)) {
+ debugStackUsage = true;
+ userMainStack.scribble();
+ qDebug("Effective stack size is %lu bytes", userMainStack.size());
+ }
+ }
+
+ qEventDispatcherDebug() << "Running UIApplicationMain"; qIndent();
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class]));
+ }
+}
+
+enum SetJumpResult
+{
+ kJumpPointSetSuccessfully = 0,
+ kJumpedFromEventDispatcherProcessEvents,
+ kJumpedFromQApplicationExecInterrupt,
+ kJumpedFromUserMainTrampoline,
+};
+
+extern "C" int main(int argc, char *argv[]);
+
+static void __attribute__((noinline, noreturn)) user_main_trampoline()
+{
+ NSArray *arguments = [[NSProcessInfo processInfo] arguments];
+ int argc = arguments.count;
+ char **argv = new char*[argc];
+ for (int i = 0; i < argc; ++i) {
+ NSString *arg = [arguments objectAtIndex:i];
+ argv[i] = reinterpret_cast<char *>(malloc([arg lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]]));
+ strcpy(argv[i], [arg cStringUsingEncoding:[NSString defaultCStringEncoding]]);
+ }
+
+ int exitCode = main(argc, argv);
+ delete[] argv;
+
+ qEventDispatcherDebug() << "Returned from main with exit code " << exitCode;
+
+ if (Q_UNLIKELY(debugStackUsage))
+ userMainStack.printUsage();
+
+ if (applicationAboutToTerminate)
+ longjmp(applicationWillTerminateJumpPoint, kJumpedFromUserMainTrampoline);
+
+ // We end up here if the user's main() never calls QApplication::exec(),
+ // or if we return from exec() after quitting the application. If so we
+ // follow the expected behavior from the point of the user's main(), which
+ // is to exit with the given exit code.
+ exit(exitCode);
+}
+
+// If we don't have a stack set up, we're not running inside
+// iOS' native/root level run-loop in UIApplicationMain.
+static bool rootLevelRunLoopIntegration()
+{
+ return userMainStack.isValid();
+}
+
+@interface QIOSApplicationStateTracker : NSObject
+@end
+
+@implementation QIOSApplicationStateTracker
+
++ (void) load
+{
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(applicationDidFinishLaunching)
+ name:UIApplicationDidFinishLaunchingNotification
+ object:nil];
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(applicationWillTerminate)
+ name:UIApplicationWillTerminateNotification
+ object:nil];
+}
+
+#if defined(Q_PROCESSOR_X86)
+# define SET_STACK_POINTER "mov %0, %%esp"
+# define FUNCTION_CALL_ALIGNMENT 16
+#elif defined(Q_PROCESSOR_ARM)
+# define SET_STACK_POINTER "mov sp, %0"
+# define FUNCTION_CALL_ALIGNMENT 4
+#else
+# error "Unknown processor family"
+#endif
+
++ (void) applicationDidFinishLaunching
+{
+ if (!isQtApplication())
+ return;
+
+ if (!rootLevelRunLoopIntegration()) {
+ // We schedule the main-redirection for the next run-loop pass, so that we
+ // can return from this function and let UIApplicationMain finish its job.
+ // This results in running Qt's application eventloop as a nested runloop.
+ qEventDispatcherDebug() << "Scheduling main() on next run-loop pass";
+ CFRunLoopTimerRef userMainTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault,
+ CFAbsoluteTimeGetCurrent(), 0, 0, 0, ^(CFRunLoopTimerRef) { user_main_trampoline(); });
+ CFRunLoopAddTimer(CFRunLoopGetMain(), userMainTimer, kCFRunLoopCommonModes);
+ CFRelease(userMainTimer);
+ return;
+ }
+
+ switch (setjmp(processEventEnterJumpPoint)) {
+ case kJumpPointSetSuccessfully:
+ qEventDispatcherDebug() << "Running main() on separate stack"; qIndent();
+
+ // Redirect the stack pointer to the start of the reserved stack. This ensures
+ // that when we longjmp out of the event dispatcher and continue execution, the
+ // 'Qt main' call-stack will not be smashed, as it lives in a part of the stack
+ // that was allocated back in main().
+ __asm__ __volatile__(
+ SET_STACK_POINTER
+ : /* no outputs */
+ : "r" (qAlignDown(userMainStack.base, FUNCTION_CALL_ALIGNMENT))
+ );
+
+ user_main_trampoline();
+
+ Q_UNREACHABLE();
+ break;
+ case kJumpedFromEventDispatcherProcessEvents:
+ // We've returned from the longjmp in the event dispatcher,
+ // and the stack has been restored to its old self.
+ qUnIndent(); qEventDispatcherDebug() << "Returned from processEvents";
+
+ if (Q_UNLIKELY(debugStackUsage))
+ userMainStack.printUsage();
+
+ break;
+ default:
+ qFatal("Unexpected jump result in event loop integration");
+ }
+}
+
++ (void) applicationWillTerminate
+{
+ if (!isQtApplication())
+ return;
+
+ if (!rootLevelRunLoopIntegration())
+ return;
+
+ // Normally iOS just sends SIGKILL to quit applications, in which case there's
+ // no chance for us to clean up anything, but in some rare cases iOS will tell
+ // us that the application is about to be terminated.
+
+ // We try to play nice with Qt by ending the main event loop, which will result
+ // in QCoreApplication::aboutToQuit() being emitted, and main() returning to the
+ // trampoline. The trampoline then redirects us back here, so that we can return
+ // to UIApplicationMain instead of calling exit().
+
+ applicationAboutToTerminate = true;
+ switch (setjmp(applicationWillTerminateJumpPoint)) {
+ case kJumpPointSetSuccessfully:
+ qEventDispatcherDebug() << "Exiting qApp with SIGTERM exit code"; qIndent();
+ // We treat applicationWillTerminate as SIGTERM, even if it can't be ignored
+ qApp->exit(128 + SIGTERM);
+
+ // 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();
+ break;
+ case kJumpedFromUserMainTrampoline:
+ // The user's main has returned, so we're ready to let iOS terminate the application
+ qUnIndent(); qEventDispatcherDebug() << "kJumpedFromUserMainTrampoline, allowing iOS to terminate";
+ break;
+ default:
+ qFatal("Unexpected jump result in event loop integration");
+ }
+}
+
+@end
+
+QT_BEGIN_NAMESPACE
+QT_USE_NAMESPACE
+
+QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent)
+ : QEventDispatcherCoreFoundation(parent)
+ , m_processEventCallsAfterAppExec(0)
+ , m_runLoopExitObserver(this, &QIOSEventDispatcher::handleRunLoopExit, kCFRunLoopExit)
+{
+}
+
+bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ if (!rootLevelRunLoopIntegration())
+ return QEventDispatcherCoreFoundation::processEvents(flags);
+
+ QCoreApplicationPrivate *qApplication = static_cast<QCoreApplicationPrivate *>(QObjectPrivate::get(qApp));
+ if (!m_processEventCallsAfterAppExec && qApplication->in_exec) {
+ Q_ASSERT(flags & QEventLoop::EventLoopExec);
+
+ // 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().
+ switch (setjmp(processEventExitJumpPoint)) {
+ case kJumpPointSetSuccessfully:
+ qEventDispatcherDebug() << "QApplication 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
+ // 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";
+ return true;
+ default:
+ qFatal("Unexpected jump result in event loop integration");
+ }
+
+ Q_UNREACHABLE();
+ }
+
+ if (m_processEventCallsAfterAppExec)
+ ++m_processEventCallsAfterAppExec;
+
+ bool processedEvents = QEventDispatcherCoreFoundation::processEvents(flags);
+
+ if (m_processEventCallsAfterAppExec)
+ --m_processEventCallsAfterAppExec;
+
+ // 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();
+
+ return processedEvents;
+}
+
+void QIOSEventDispatcher::interrupt()
+{
+ QEventDispatcherCoreFoundation::interrupt();
+
+ if (!rootLevelRunLoopIntegration())
+ return;
+
+ // 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();
+}
+
+void QIOSEventDispatcher::checkIfApplicationShouldQuit()
+{
+ if (QThreadData::current()->quitNow && m_processEventCallsAfterAppExec == 1) {
+ qEventDispatcherDebug() << "Hit root runloop level, watching for runloop exit";
+ m_runLoopExitObserver.addToMode(kCFRunLoopCommonModes);
+ }
+}
+
+void QIOSEventDispatcher::handleRunLoopExit(CFRunLoopActivity activity)
+{
+ Q_ASSERT(activity == kCFRunLoopExit);
+
+ m_runLoopExitObserver.removeFromMode(kCFRunLoopCommonModes);
+
+ interruptQApplicationExec();
+}
+
+void QIOSEventDispatcher::interruptQApplicationExec()
+{
+ Q_ASSERT(QThreadData::current()->quitNow);
+ Q_ASSERT(m_processEventCallsAfterAppExec == 1);
+
+ --m_processEventCallsAfterAppExec;
+
+ // We re-set applicationProcessEventsReturnPoint here so that future
+ // calls to QApplication::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);
+ break;
+ case kJumpedFromEventDispatcherProcessEvents:
+ // QCoreApplication was re-executed
+ qEventDispatcherDebug() << "kJumpedFromEventDispatcherProcessEvents";
+ break;
+ default:
+ qFatal("Unexpected jump result in event loop integration");
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm
index cbf9dba862..d854bf7723 100644
--- a/src/plugins/platforms/ios/qiosintegration.mm
+++ b/src/plugins/platforms/ios/qiosintegration.mm
@@ -40,6 +40,8 @@
****************************************************************************/
#include "qiosintegration.h"
+#include "qioseventdispatcher.h"
+#include "qiosglobal.h"
#include "qioswindow.h"
#include "qiosbackingstore.h"
#include "qiosscreen.h"
@@ -47,7 +49,6 @@
#include "qiosinputcontext.h"
#include "qiostheme.h"
-#include <QtPlatformSupport/private/qeventdispatcher_cf_p.h>
#include <QtPlatformSupport/private/qcoretextfontdatabase_p.h>
#include <QDir>
@@ -114,7 +115,10 @@ QPlatformOpenGLContext *QIOSIntegration::createPlatformOpenGLContext(QOpenGLCont
QAbstractEventDispatcher *QIOSIntegration::guiThreadEventDispatcher() const
{
- return new QEventDispatcherCoreFoundation;
+ if (isQtApplication())
+ return new QIOSEventDispatcher;
+ else
+ return new QEventDispatcherCoreFoundation;
}
QPlatformFontDatabase * QIOSIntegration::fontDatabase() const