diff options
Diffstat (limited to 'src/plugins/platforms/ios')
18 files changed, 407 insertions, 582 deletions
diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro index 842ff17f1c..5a2129eb08 100644 --- a/src/plugins/platforms/ios/ios.pro +++ b/src/plugins/platforms/ios/ios.pro @@ -1,3 +1,39 @@ -TEMPLATE = subdirs +TARGET = qios -SUBDIRS += plugin.pro qtmain.pro +PLUGIN_TYPE = platforms +PLUGIN_CLASS_NAME = QIOSIntegrationPlugin +load(qt_plugin) + +QT += core-private gui-private platformsupport-private +LIBS += -framework Foundation -framework UIKit -framework QuartzCore + +OBJECTIVE_SOURCES = \ + plugin.mm \ + qiosmain_wrapper.mm \ + qiosmain_dummy.mm \ + qiosintegration.mm \ + qioswindow.mm \ + qiosscreen.mm \ + qiosbackingstore.mm \ + qiosapplicationdelegate.mm \ + qiosapplicationstate.mm \ + qiosviewcontroller.mm \ + qioscontext.mm \ + qiosinputcontext.mm \ + qiostheme.mm \ + qiosglobal.mm + +HEADERS = \ + qiosintegration.h \ + qioswindow.h \ + qiosscreen.h \ + qiosbackingstore.h \ + qiosapplicationdelegate.h \ + qiosapplicationstate.h \ + qiosviewcontroller.h \ + qioscontext.h \ + qiosinputcontext.h \ + qiostheme.h \ + qiosglobal.h + +#HEADERS = qiossoftwareinputhandler.h diff --git a/src/plugins/platforms/ios/plugin.mm b/src/plugins/platforms/ios/plugin.mm index a93b6037ad..efb1ad8d74 100644 --- a/src/plugins/platforms/ios/plugin.mm +++ b/src/plugins/platforms/ios/plugin.mm @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE class QIOSIntegrationPlugin : public QPlatformIntegrationPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "ios.json") + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2" FILE "ios.json") public: QPlatformIntegration *create(const QString&, const QStringList&); }; @@ -66,4 +66,8 @@ QT_END_NAMESPACE #include "plugin.moc" +// Dummy function that we explicitly tell the linker to look for, +// so that the plugin's static initializer is included and run. +extern "C" void qt_registerPlatformPlugin() {} + Q_IMPORT_PLUGIN(QIOSIntegrationPlugin) diff --git a/src/plugins/platforms/ios/plugin.pro b/src/plugins/platforms/ios/plugin.pro deleted file mode 100644 index 591a0a67ed..0000000000 --- a/src/plugins/platforms/ios/plugin.pro +++ /dev/null @@ -1,36 +0,0 @@ -TARGET = qios - -PLUGIN_TYPE = platforms -load(qt_plugin) - -QT += core-private gui-private platformsupport-private -LIBS += -framework UIKit -framework QuartzCore - -OBJECTIVE_SOURCES = \ - plugin.mm \ - qiosintegration.mm \ - qioswindow.mm \ - qiosscreen.mm \ - qioseventdispatcher.mm \ - qiosbackingstore.mm \ - qiosapplicationdelegate.mm \ - qiosviewcontroller.mm \ - qioscontext.mm \ - qiosinputcontext.mm \ - qiostheme.mm \ - qiosglobal.mm - -HEADERS = \ - qiosintegration.h \ - qioswindow.h \ - qiosscreen.h \ - qioseventdispatcher.h \ - qiosbackingstore.h \ - qiosapplicationdelegate.h \ - qiosviewcontroller.h \ - qioscontext.h \ - qiosinputcontext.h \ - qiostheme.h \ - qiosglobal.h - -#HEADERS = qiossoftwareinputhandler.h diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.h b/src/plugins/platforms/ios/qiosapplicationdelegate.h index 3d3ba58049..bfe31af198 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.h +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.h @@ -50,7 +50,3 @@ @property (strong, nonatomic) QIOSViewController *qiosViewController; @end - -@interface QIOSMainWrapperApplicationDelegate : QIOSApplicationDelegate -@end - diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm index 10cbe529c4..52d94f38fb 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm @@ -39,10 +39,15 @@ ** ****************************************************************************/ -#import "qiosapplicationdelegate.h" +#include "qiosapplicationdelegate.h" + +#include "qiosviewcontroller.h" #include "qioswindow.h" + #include <QtCore/QtCore> +extern int qt_user_main(int argc, char *argv[]); + @implementation QIOSApplicationDelegate @synthesize window; @@ -50,40 +55,43 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - Q_UNUSED(application) - Q_UNUSED(launchOptions) + Q_UNUSED(application); + Q_UNUSED(launchOptions); - return YES; -} + self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; + self.qiosViewController = [[[QIOSViewController alloc] init] autorelease]; + self.window.rootViewController = self.qiosViewController; -- (void)applicationWillResignActive:(UIApplication *)application -{ - Q_UNUSED(application) -} +#ifdef QT_DEBUG + self.window.backgroundColor = [UIColor cyanColor]; +#endif -- (void)applicationDidEnterBackground:(UIApplication *)application -{ - Q_UNUSED(application) -} + [self.window makeKeyAndVisible]; -- (void)applicationWillEnterForeground:(UIApplication *)application -{ - Q_UNUSED(application) - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} + // 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]; -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - Q_UNUSED(application) - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + return YES; } -- (void)applicationWillTerminate:(UIApplication *)application +- (void)runUserMain { - Q_UNUSED(application) - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + 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]]); + } + + qt_user_main(argc, argv); + delete[] argv; } + - (void)dealloc { [qiosViewController release]; @@ -93,4 +101,3 @@ @end - diff --git a/src/plugins/platforms/ios/qiosapplicationstate.h b/src/plugins/platforms/ios/qiosapplicationstate.h new file mode 100644 index 0000000000..e726ad895e --- /dev/null +++ b/src/plugins/platforms/ios/qiosapplicationstate.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QIOSAPPLICATIONSTATE_H +#define QIOSAPPLICATIONSTATE_H + +QT_BEGIN_NAMESPACE + +@class QIOSApplicationStateListener; + +class QIOSApplicationState +{ +public: + QIOSApplicationState(); + ~QIOSApplicationState(); +private: + QIOSApplicationStateListener *m_listener; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/ios/qiosapplicationstate.mm b/src/plugins/platforms/ios/qiosapplicationstate.mm new file mode 100644 index 0000000000..df64edf465 --- /dev/null +++ b/src/plugins/platforms/ios/qiosapplicationstate.mm @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#import <UIKit/UIKit.h> + +#include <qpa/qwindowsysteminterface.h> +#include "qiosapplicationstate.h" + +@interface QIOSApplicationStateListener : NSObject +@end + +@implementation QIOSApplicationStateListener + +- (id) init +{ + self = [super init]; + if (self) { + // Listen for application state changes. + // Note: We use notifications rather than application delegate callbacks to + // also support hybrid applications were QIOSApplicationDelegate is not in use. + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(applicationDidBecomeActive) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(applicationWillResignActive) + name:UIApplicationWillResignActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(applicationDidEnterBackground) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + } + return self; +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:UIApplicationDidBecomeActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:UIApplicationWillResignActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + [super dealloc]; +} + +- (void) applicationDidBecomeActive +{ + [self handleApplicationStateChanged:UIApplicationStateActive]; +} + +- (void) applicationWillResignActive +{ + // Note that UIApplication is still UIApplicationStateActive at this + // point, but since there is no separate notification for the inactive + // state, we report UIApplicationStateInactive now: + [self handleApplicationStateChanged:UIApplicationStateInactive]; +} + +- (void) applicationDidEnterBackground +{ + [self handleApplicationStateChanged:UIApplicationStateBackground]; +} + +- (void) handleApplicationStateChanged:(UIApplicationState) uiApplicationState +{ + Qt::ApplicationState state; + switch (uiApplicationState) { + case UIApplicationStateActive: + // The application is visible in front, and receiving events: + state = Qt::ApplicationActive; + break; + case UIApplicationStateInactive: + // The app is running in the foreground but is not receiving events. This + // typically happens while transitioning to/from active/background, like + // upon app launch or when receiving incoming calls: + state = Qt::ApplicationInactive; + break; + case UIApplicationStateBackground: + // Normally the app would enter this state briefly before it gets + // suspeded (you have five seconds, according to Apple). + // You can request more time and start a background task, which would + // normally map closer to Qt::ApplicationHidden. But since we have no + // API for doing that yet, we handle this state as "about to be suspended". + // Note: A screen-shot for the SpringBoard will also be taken after this + // call returns. + state = Qt::ApplicationSuspended; + break; + } + QWindowSystemInterface::handleApplicationStateChanged(state); +} + +@end + +QT_BEGIN_NAMESPACE + +QIOSApplicationState::QIOSApplicationState() + : m_listener([[QIOSApplicationStateListener alloc] init]) +{ + // Update the current state now, since we have missed all the updates + // posted from AppKit so far. But let QPA finish initialization first: + dispatch_async(dispatch_get_main_queue(), ^{ + UIApplicationState state = [UIApplication sharedApplication].applicationState; + [m_listener handleApplicationStateChanged:state]; + }); +} + +QIOSApplicationState::~QIOSApplicationState() +{ + [m_listener release]; +} + +QT_END_NAMESPACE + diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index 87bcc01d04..85f560a722 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -200,7 +200,7 @@ void QIOSContext::windowDestroyed(QObject *object) QFunctionPointer QIOSContext::getProcAddress(const QByteArray& functionName) { - return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_NEXT, functionName.constData())); + return QFunctionPointer(dlsym(RTLD_DEFAULT, functionName.constData())); } #include "moc_qioscontext.cpp" diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h deleted file mode 100644 index 53a75618ce..0000000000 --- a/src/plugins/platforms/ios/qioseventdispatcher.h +++ /dev/null @@ -1,133 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -/**************************************************************************** -** -** Copyright (c) 2007-2008, Apple, Inc. -** -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** -** * Redistributions of source code must retain the above copyright notice, -** this list of conditions and the following disclaimer. -** -** * Redistributions in binary form must reproduce the above copyright notice, -** this list of conditions and the following disclaimer in the documentation -** and/or other materials provided with the distribution. -** -** * Neither the name of Apple, Inc. nor the names of its contributors -** may be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** -****************************************************************************/ - -#ifndef QEVENTDISPATCHER_IOS_P_H -#define QEVENTDISPATCHER_IOS_P_H - -#include <QtCore/qabstracteventdispatcher.h> -#include <QtCore/private/qtimerinfo_unix_p.h> -#include <QtPlatformSupport/private/qcfsocketnotifier_p.h> -#include <CoreFoundation/CoreFoundation.h> - -QT_BEGIN_NAMESPACE - -class QIOSEventDispatcher : public QAbstractEventDispatcher -{ - Q_OBJECT - -public: - explicit QIOSEventDispatcher(QObject *parent = 0); - ~QIOSEventDispatcher(); - - bool processEvents(QEventLoop::ProcessEventsFlags flags); - bool hasPendingEvents(); - - void registerSocketNotifier(QSocketNotifier *notifier); - void unregisterSocketNotifier(QSocketNotifier *notifier); - - void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object); - bool unregisterTimer(int timerId); - bool unregisterTimers(QObject *object); - QList<QAbstractEventDispatcher::TimerInfo> registeredTimers(QObject *object) const; - - int remainingTime(int timerId); - - void wakeUp(); - void interrupt(); - void flush(); - -private: - bool m_interrupted; - - CFRunLoopSourceRef m_postedEventsRunLoopSource; - CFRunLoopSourceRef m_blockingTimerRunLoopSource; - - QTimerInfoList m_timerInfoList; - CFRunLoopTimerRef m_runLoopTimerRef; - - QCFSocketNotifier m_cfSocketNotifier; - - void processPostedEvents(); - void maybeStartCFRunLoopTimer(); - void maybeStopCFRunLoopTimer(); - - static void postedEventsRunLoopCallback(void *info); - static void nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info); - static void blockingTimerRunLoopCallback(void *info); -}; - -QT_END_NAMESPACE - -#endif // QEVENTDISPATCHER_IOS_P_H diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm deleted file mode 100644 index e9bf039047..0000000000 --- a/src/plugins/platforms/ios/qioseventdispatcher.mm +++ /dev/null @@ -1,322 +0,0 @@ -/**************************************************************************** -** -** 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" -#import "qiosapplicationdelegate.h" -#include <qdebug.h> -#include <qpa/qwindowsysteminterface.h> -#include <QtCore/QThread> -#include <QtCore/private/qcoreapplication_p.h> -#include <UIKit/UIApplication.h> - -QT_BEGIN_NAMESPACE -QT_USE_NAMESPACE - -static Boolean runLoopSourceEqualCallback(const void *info1, const void *info2) -{ - return info1 == info2; -} - -void QIOSEventDispatcher::postedEventsRunLoopCallback(void *info) -{ - QIOSEventDispatcher *self = static_cast<QIOSEventDispatcher *>(info); - self->processPostedEvents(); -} - -void QIOSEventDispatcher::nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info) -{ - // The (one and only) CFRunLoopTimer has fired, which means that at least - // one QTimer should now fire as well. Note that CFRunLoopTimer's callback will - // never recurse. So if the app starts a new QEventLoop within this callback, other - // timers will stop working. The work-around is to forward the callback to a - // dedicated CFRunLoopSource that can recurse: - QIOSEventDispatcher *self = static_cast<QIOSEventDispatcher *>(info); - CFRunLoopSourceSignal(self->m_blockingTimerRunLoopSource); -} - -void QIOSEventDispatcher::blockingTimerRunLoopCallback(void *info) -{ - // TODO: - // We also need to block this new timer source - // along with the posted event source when calling processEvents() - // "manually" to prevent livelock deep in CFRunLoop. - - QIOSEventDispatcher *self = static_cast<QIOSEventDispatcher *>(info); - self->m_timerInfoList.activateTimers(); - self->maybeStartCFRunLoopTimer(); -} - -void QIOSEventDispatcher::maybeStartCFRunLoopTimer() -{ - // Find out when the next registered timer should fire, and schedule - // runLoopTimer accordingly. If the runLoopTimer does not yet exist, and - // at least one timer is registered, start by creating the timer: - if (m_timerInfoList.isEmpty()) { - Q_ASSERT(m_runLoopTimerRef == 0); - return; - } - - CFAbsoluteTime ttf = CFAbsoluteTimeGetCurrent(); - CFTimeInterval interval; - - if (m_runLoopTimerRef == 0) { - // start the CFRunLoopTimer - CFTimeInterval oneyear = CFTimeInterval(3600. * 24. * 365.); - - // calculate when the next timer should fire: - struct timespec tv; - if (m_timerInfoList.timerWait(tv)) { - interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); - } else { - // this shouldn't really happen, but in case it does, set the timer - // to fire a some point in the distant future: - interval = oneyear; - } - - ttf += interval; - CFRunLoopTimerContext info = { 0, this, 0, 0, 0 }; - // create the timer with a large interval, as recommended by the CFRunLoopTimerSetNextFireDate() - // documentation, since we will adjust the timer's time-to-fire as needed to keep Qt timers working - m_runLoopTimerRef = CFRunLoopTimerCreate(0, ttf, oneyear, 0, 0, QIOSEventDispatcher::nonBlockingTimerRunLoopCallback, &info); - Q_ASSERT(m_runLoopTimerRef != 0); - - CFRunLoopAddTimer(CFRunLoopGetMain(), m_runLoopTimerRef, kCFRunLoopCommonModes); - } else { - struct timespec tv; - // Calculate when the next timer should fire: - if (m_timerInfoList.timerWait(tv)) { - interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); - } else { - // no timers can fire, but we cannot stop the CFRunLoopTimer, set the timer to fire at some - // point in the distant future (the timer interval is one year) - interval = CFRunLoopTimerGetInterval(m_runLoopTimerRef); - } - - ttf += interval; - CFRunLoopTimerSetNextFireDate(m_runLoopTimerRef, ttf); - } -} - -void QIOSEventDispatcher::maybeStopCFRunLoopTimer() -{ - if (m_runLoopTimerRef == 0) - return; - - CFRunLoopTimerInvalidate(m_runLoopTimerRef); - CFRelease(m_runLoopTimerRef); - m_runLoopTimerRef = 0; -} - -void QIOSEventDispatcher::processPostedEvents() -{ - QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents); -} - -QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent) - : QAbstractEventDispatcher(parent) - , m_interrupted(false) - , m_runLoopTimerRef(0) -{ - m_cfSocketNotifier.setHostEventDispatcher(this); - - CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); - CFRunLoopSourceContext context; - bzero(&context, sizeof(CFRunLoopSourceContext)); - context.equal = runLoopSourceEqualCallback; - context.info = this; - - // source used to handle timers: - context.perform = QIOSEventDispatcher::blockingTimerRunLoopCallback; - m_blockingTimerRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); - Q_ASSERT(m_blockingTimerRunLoopSource); - CFRunLoopAddSource(mainRunLoop, m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); - - // source used to handle posted events: - context.perform = QIOSEventDispatcher::postedEventsRunLoopCallback; - m_postedEventsRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); - Q_ASSERT(m_postedEventsRunLoopSource); - CFRunLoopAddSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); -} - -QIOSEventDispatcher::~QIOSEventDispatcher() -{ - CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); - CFRunLoopRemoveSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); - CFRelease(m_postedEventsRunLoopSource); - - qDeleteAll(m_timerInfoList); - maybeStopCFRunLoopTimer(); - CFRunLoopRemoveSource(CFRunLoopGetMain(), m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); - CFRelease(m_blockingTimerRunLoopSource); - - m_cfSocketNotifier.removeSocketNotifiers(); -} - -bool QIOSEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) -{ - m_interrupted = false; - bool eventsProcessed = false; - - bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; - bool execFlagSet = (flags & QEventLoop::DialogExec) || (flags & QEventLoop::EventLoopExec); - bool useExecMode = execFlagSet && !excludeUserEvents; - - if (useExecMode) { - NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; - while ([runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]] && !m_interrupted); - eventsProcessed = true; - } else { - if (!(flags & QEventLoop::WaitForMoreEvents)) - wakeUp(); - eventsProcessed = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; - } - return eventsProcessed; -} - -bool QIOSEventDispatcher::hasPendingEvents() -{ - qDebug() << __FUNCTION__ << "not implemented"; - return false; -} - -void QIOSEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier) -{ - m_cfSocketNotifier.registerSocketNotifier(notifier); -} - -void QIOSEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier) -{ - m_cfSocketNotifier.unregisterSocketNotifier(notifier); -} - -void QIOSEventDispatcher::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *obj) -{ -#ifndef QT_NO_DEBUG - if (timerId < 1 || interval < 0 || !obj) { - qWarning("QIOSEventDispatcher::registerTimer: invalid arguments"); - return; - } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { - qWarning("QIOSEventDispatcher: timers cannot be started from another thread"); - return; - } -#endif - - m_timerInfoList.registerTimer(timerId, interval, timerType, obj); - maybeStartCFRunLoopTimer(); -} - -bool QIOSEventDispatcher::unregisterTimer(int timerId) -{ -#ifndef QT_NO_DEBUG - if (timerId < 1) { - qWarning("QIOSEventDispatcher::unregisterTimer: invalid argument"); - return false; - } else if (thread() != QThread::currentThread()) { - qWarning("QObject::killTimer: timers cannot be stopped from another thread"); - return false; - } -#endif - - bool returnValue = m_timerInfoList.unregisterTimer(timerId); - m_timerInfoList.isEmpty() ? maybeStopCFRunLoopTimer() : maybeStartCFRunLoopTimer(); - return returnValue; -} - -bool QIOSEventDispatcher::unregisterTimers(QObject *object) -{ -#ifndef QT_NO_DEBUG - if (!object) { - qWarning("QIOSEventDispatcher::unregisterTimers: invalid argument"); - return false; - } else if (object->thread() != thread() || thread() != QThread::currentThread()) { - qWarning("QObject::killTimers: timers cannot be stopped from another thread"); - return false; - } -#endif - - bool returnValue = m_timerInfoList.unregisterTimers(object); - m_timerInfoList.isEmpty() ? maybeStopCFRunLoopTimer() : maybeStartCFRunLoopTimer(); - return returnValue; -} - -QList<QAbstractEventDispatcher::TimerInfo> QIOSEventDispatcher::registeredTimers(QObject *object) const -{ -#ifndef QT_NO_DEBUG - if (!object) { - qWarning("QIOSEventDispatcher:registeredTimers: invalid argument"); - return QList<TimerInfo>(); - } -#endif - - return m_timerInfoList.registeredTimers(object); -} - -int QIOSEventDispatcher::remainingTime(int timerId) -{ -#ifndef QT_NO_DEBUG - if (timerId < 1) { - qWarning("QIOSEventDispatcher::remainingTime: invalid argument"); - return -1; - } -#endif - - return m_timerInfoList.timerRemainingTime(timerId); -} - -void QIOSEventDispatcher::wakeUp() -{ - CFRunLoopSourceSignal(m_postedEventsRunLoopSource); - CFRunLoopWakeUp(CFRunLoopGetMain()); -} - -void QIOSEventDispatcher::interrupt() -{ - wakeUp(); - m_interrupted = true; -} - -void QIOSEventDispatcher::flush() -{ - // X11 only. -} - -QT_END_NAMESPACE - diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h index 176ad05733..78c1b260e6 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.h +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -66,6 +66,7 @@ public: private: QIOSKeyboardListener *m_keyboardListener; UIView *m_focusView; + bool m_hasPendingHideRequest; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index 1d3ab12de9..d430589037 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -99,6 +99,7 @@ QIOSInputContext::QIOSInputContext() : QPlatformInputContext() , m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) , m_focusView(0) + , m_hasPendingHideRequest(false) { } @@ -120,12 +121,20 @@ void QIOSInputContext::showInputPanel() // responder. Rather than searching for it from the top, we let the active QIOSWindow tell us which view to use. // Note that Qt will forward keyevents to whichever QObject that needs it, regardless of which UIView the input // actually came from. So in this respect, we're undermining iOS' responder chain. + m_hasPendingHideRequest = false; [m_focusView becomeFirstResponder]; } void QIOSInputContext::hideInputPanel() { - [m_focusView resignFirstResponder]; + // Delay hiding the keyboard for cases where the user is transferring focus between + // 'line edits'. In that case the 'line edit' that lost focus will close the input + // panel, just to see that the new 'line edit' will open it again: + m_hasPendingHideRequest = true; + dispatch_async(dispatch_get_main_queue(), ^{ + if (m_hasPendingHideRequest) + [m_focusView resignFirstResponder]; + }); } bool QIOSInputContext::isInputPanelVisible() const diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index c352e0f2d2..4aaf98f839 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -46,6 +46,8 @@ #include <qpa/qplatformnativeinterface.h> #include <qpa/qwindowsysteminterface.h> +#include "qiosapplicationstate.h" + QT_BEGIN_NAMESPACE class QIOSIntegration : public QPlatformIntegration, public QPlatformNativeInterface @@ -79,6 +81,7 @@ private: QPlatformInputContext *m_inputContext; QPlatformScreen *m_screen; QTouchDevice *m_touchDevice; + QIOSApplicationState m_applicationState; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 61fd1c3d60..e31804f428 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -43,12 +43,13 @@ #include "qioswindow.h" #include "qiosbackingstore.h" #include "qiosscreen.h" -#include "qioseventdispatcher.h" #include "qioscontext.h" #include "qiosinputcontext.h" #include "qiostheme.h" +#include <QtPlatformSupport/private/qioseventdispatcher_p.h> #include <QtPlatformSupport/private/qcoretextfontdatabase_p.h> +#include <QDir> #include <QtDebug> @@ -71,6 +72,9 @@ QIOSIntegration::QIOSIntegration() exit(-1); } + // Set current directory to app bundle folder + QDir::setCurrent(QString::fromUtf8([[[NSBundle mainBundle] bundlePath] UTF8String])); + screenAdded(m_screen); m_touchDevice = new QTouchDevice; @@ -86,6 +90,8 @@ bool QIOSIntegration::hasCapability(Capability cap) const return true; case MultipleWindows: return true; + case ApplicationState: + return true; default: return QPlatformIntegration::hasCapability(cap); } @@ -129,6 +135,8 @@ QVariant QIOSIntegration::styleHint(StyleHint hint) const switch (hint) { case ShowIsFullScreen: return true; + case SetFocusOnTouchRelease: + return true; default: return QPlatformIntegration::styleHint(hint); } diff --git a/src/plugins/platforms/ios/qiosmain_dummy.mm b/src/plugins/platforms/ios/qiosmain_dummy.mm new file mode 100644 index 0000000000..28d7e59381 --- /dev/null +++ b/src/plugins/platforms/ios/qiosmain_dummy.mm @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** 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 <QtCore/qglobal.h> + +/* + This file provides a dummy implementation of qt_user_main, so that + we don't get an undefined symbol in the hybrid use-case, where we + don't rename main() to qt_user_main(). As long as the linker is not + passed -all_load, this translation unit is only picked up and used + if qt_user_main is not defined by the user's code. +*/ + +int qt_user_main(int, char **) +{ + qFatal("Hit dummy qt_user_main, this should never happen!"); + return 0; +} diff --git a/src/plugins/platforms/ios/qtmain.mm b/src/plugins/platforms/ios/qiosmain_wrapper.mm index 19c98f2c59..d9b8c7311e 100644 --- a/src/plugins/platforms/ios/qtmain.mm +++ b/src/plugins/platforms/ios/qiosmain_wrapper.mm @@ -40,55 +40,21 @@ ****************************************************************************/ #include "qiosapplicationdelegate.h" -#include "qiosviewcontroller.h" -int main(int argc, char *argv[]) -{ - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSMainWrapperApplicationDelegate class])); - } -} - -extern int qt_main(int argc, char *argv[]); - -@implementation QIOSMainWrapperApplicationDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; - self.qiosViewController = [[[QIOSViewController alloc] init] autorelease]; - self.window.rootViewController = self.qiosViewController; +/* + This file provides a wrapper implementation of main() for the non- + hybrid use-case. The user's main is renamed to qt_user_main by the + build rules, and we'll call out to that main at the appropriate time. -#ifdef QT_DEBUG - self.window.backgroundColor = [UIColor cyanColor]; -#endif + This file purposly only exports a single symbol, _main, so that + when the linker considers the translation unit for inclusion it + will discard it when main has already been defined in the user's + application for the hybrid use-case. +*/ - [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]; - - if ([QIOSApplicationDelegate instancesRespondToSelector:_cmd]) - return [super application:application didFinishLaunchingWithOptions:launchOptions]; - else - return YES; -} - -- (void)runUserMain +int main(int argc, char *argv[]) { - 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]]); + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class])); } - - qt_main(argc, argv); - delete[] argv; } - -@end diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 0c3ae8e834..4c8a16b0bc 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -247,6 +247,23 @@ return YES; } +- (BOOL)becomeFirstResponder +{ + // On iOS, a QWindow should only have input focus when the input panel is + // open. This is to stop cursors and focus rects from being drawn when the + // user cannot type. And since the keyboard will open when a view becomes + // the first responder, it's now a good time to inform QPA that the QWindow + // this view backs became active: + QWindowSystemInterface::handleWindowActivated(m_qioswindow->window()); + return [super becomeFirstResponder]; +} + +- (BOOL)resignFirstResponder +{ + QWindowSystemInterface::handleWindowActivated(0); + return [super resignFirstResponder]; +} + - (BOOL)hasText { return YES; @@ -416,7 +433,6 @@ void QIOSWindow::requestActivateWindow() raise(); QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); static_cast<QIOSInputContext *>(context)->focusViewChanged(m_view); - QPlatformWindow::requestActivateWindow(); } void QIOSWindow::raiseOrLower(bool raise) diff --git a/src/plugins/platforms/ios/qtmain.pro b/src/plugins/platforms/ios/qtmain.pro deleted file mode 100644 index cbcb272217..0000000000 --- a/src/plugins/platforms/ios/qtmain.pro +++ /dev/null @@ -1,8 +0,0 @@ -TARGET = qiosmain - -PLUGIN_TYPE = platforms -load(qt_plugin) - -QT += gui-private - -OBJECTIVE_SOURCES = qtmain.mm |