summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel de Dietrich <gabriel.dedietrich@digia.com>2013-06-19 12:27:51 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-08-29 12:44:07 +0200
commit1e14762b8d79118540bd09a84dd3e48f4f5e113e (patch)
treef06cb1b7ccb161f3495d2c1abb941c2911a50314
parentee9b9d9fd9a714f97ed8f7e26404c2f113588e9c (diff)
downloadqtbase-1e14762b8d79118540bd09a84dd3e48f4f5e113e.tar.gz
Make QGuiApplication::exec() run within NSApplicationMain()
We follow the same pattern as for iOS and Windows ports, making sure the user's main() runs in a platform friendly environment. In this particular case, it means calling the user's main() during the call of NSApplicationMain(), and calling the user's main() function (renamed to qMain() as in Windows) after receiving NSApplicationDidFinishLaunchingNotification. In practice, this means that NSApp is running when qMain() is called, and therefore when QCoreApplication::exec() is called. For those command-line utilities running on QGuiApplication, or any deriving class, and that do not provide a bundle, we override the main bundle's dictionary and get NSApplicationMain() to run as usual. Added cocoa/cocoamain "subdir" to build libqtcocoamain.a (together with cocoa/cocoaplugin -- plugins/platforms/cocoa is made a subdirs project). This library is linked against all GUI Qt apps and provides the actual main() function. It also catches the launching NSApplication notifications, and calls the user's qMain() function. Note that this will happen in the same cases when the user's application will run with the Cocoa QPA plugin. Launch related code in QCocoaApplicationDelegate is moved to libqtcocoamain, QNSApplication is removed (but sendEvent: redirection still there), and code in QCocoaEventDispatcher dealing with calling [NSApp run] and related has been removed since it's become unreachable. ChangeLog: [Qt for Mac] Make QGuiApplication::exec() run within NSApplicationMain() Change-Id: I790e5138c29aac2e0215a9147d0148fece40ca22 Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
-rw-r--r--mkspecs/features/mac/default_post.prf14
-rw-r--r--src/plugins/platforms/cocoa/cocoa.pro110
-rw-r--r--src/plugins/platforms/cocoa/cocoamain.pro19
-rw-r--r--src/plugins/platforms/cocoa/cocoaplugin.pro109
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplication.h6
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplication.mm46
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h4
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm77
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.h3
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm107
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm5
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintrospection.mm50
-rw-r--r--src/plugins/platforms/cocoa/qcocoamain.mm181
13 files changed, 384 insertions, 347 deletions
diff --git a/mkspecs/features/mac/default_post.prf b/mkspecs/features/mac/default_post.prf
index b183814e63..4de0423f33 100644
--- a/mkspecs/features/mac/default_post.prf
+++ b/mkspecs/features/mac/default_post.prf
@@ -1,6 +1,20 @@
load(default_post)
!no_objective_c:CONFIG += objective_c
+
+equals(TEMPLATE, app):qt: {
+ deps = $$replace(QT, -private$, )
+ deps = $$resolve_depends(deps, "QT.", ".depends" ".private_depends" ".run_depends")
+ contains(deps, gui): {
+ DEFINES += QT_NEEDS_QMAIN
+ CONFIG += link_prl
+ # When CI'ing Qt we still need to link against libqtcocoamain.a, even before deployment.
+ # Hence the /get variable, suffix which looks for the libraries location effective path.
+ QMAKE_LIBDIR += $$[QT_INSTALL_LIBS/get]
+ QMAKE_LIBS += -lqtcocoamain
+ }
+}
+
qt:!isEmpty(QT_CONFIG) {
# Pick a suitable default architecture for qmake-based applications.
# If the Qt package contains one of x86 and x86_64, pick that one. If it
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro
index a2fd8c0613..dd13882f8c 100644
--- a/src/plugins/platforms/cocoa/cocoa.pro
+++ b/src/plugins/platforms/cocoa/cocoa.pro
@@ -1,109 +1,7 @@
-TARGET = qcocoa
+TEMPLATE = subdirs
-PLUGIN_TYPE = platforms
-PLUGIN_CLASS_NAME = QCocoaIntegrationPlugin
-load(qt_plugin)
+cocoamain.file = cocoamain.pro
+cocoaplugin.file = cocoaplugin.pro
-OBJECTIVE_SOURCES += main.mm \
- qcocoaintegration.mm \
- qcocoatheme.mm \
- qcocoabackingstore.mm \
- qcocoawindow.mm \
- qnsview.mm \
- qnsviewaccessibility.mm \
- qcocoaautoreleasepool.mm \
- qnswindowdelegate.mm \
- qcocoaglcontext.mm \
- qcocoanativeinterface.mm \
- qcocoaeventdispatcher.mm \
- qcocoaapplicationdelegate.mm \
- qcocoaapplication.mm \
- qcocoamenu.mm \
- qcocoamenuitem.mm \
- qcocoamenubar.mm \
- qcocoamenuloader.mm \
- qcocoahelpers.mm \
- qmultitouch_mac.mm \
- qcocoaaccessibilityelement.mm \
- qcocoaaccessibility.mm \
- qcocoacolordialoghelper.mm \
- qcocoafiledialoghelper.mm \
- qcocoafontdialoghelper.mm \
- qcocoacursor.mm \
- qcocoaclipboard.mm \
- qcocoadrag.mm \
- qmacclipboard.mm \
- qmacmime.mm \
- qcocoasystemsettings.mm \
- qcocoainputcontext.mm \
- qcocoaservices.mm \
- qcocoasystemtrayicon.mm \
- qcocoaintrospection.mm \
- qcocoakeymapper.mm \
+SUBDIRS = cocoamain cocoaplugin
-SOURCES += messages.cpp
-
-HEADERS += qcocoaintegration.h \
- qcocoatheme.h \
- qcocoabackingstore.h \
- qcocoawindow.h \
- qnsview.h \
- qcocoaautoreleasepool.h \
- qnswindowdelegate.h \
- qcocoaglcontext.h \
- qcocoanativeinterface.h \
- qcocoaeventdispatcher.h \
- qcocoaapplicationdelegate.h \
- qcocoaapplication.h \
- qcocoamenu.h \
- qcocoamenuitem.h \
- qcocoamenubar.h \
- qcocoamenuloader.h \
- qcocoahelpers.h \
- qmultitouch_mac_p.h \
- qcocoaaccessibilityelement.h \
- qcocoaaccessibility.h \
- qcocoacolordialoghelper.h \
- qcocoafiledialoghelper.h \
- qcocoafontdialoghelper.h \
- qcocoacursor.h \
- qcocoaclipboard.h \
- qcocoadrag.h \
- qmacclipboard.h \
- qmacmime.h \
- qcocoasystemsettings.h \
- qcocoainputcontext.h \
- qcocoaservices.h \
- qcocoasystemtrayicon.h \
- qcocoaintrospection.h \
- qcocoakeymapper.h \
- messages.h
-
-RESOURCES += qcocoaresources.qrc
-
-LIBS += -framework Cocoa -framework Carbon -framework IOKit
-
-QT += core-private gui-private platformsupport-private
-
-qtHaveModule(widgets) {
- OBJECTIVE_SOURCES += \
- qpaintengine_mac.mm \
- qprintengine_mac.mm \
- qcocoaprintersupport.mm \
-
- HEADERS += \
- qpaintengine_mac_p.h \
- qprintengine_mac_p.h \
- qcocoaprintersupport.h \
-
- QT += widgets-private printsupport-private
-}
-
-OTHER_FILES += cocoa.json
-
-# Acccessibility debug support
-# DEFINES += QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
-# include ($$PWD/../../../../util/accessibilityinspector/accessibilityinspector.pri)
-
-# Window debug support
-#DEFINES += QT_COCOA_ENABLE_WINDOW_DEBUG
diff --git a/src/plugins/platforms/cocoa/cocoamain.pro b/src/plugins/platforms/cocoa/cocoamain.pro
new file mode 100644
index 0000000000..8065f8b785
--- /dev/null
+++ b/src/plugins/platforms/cocoa/cocoamain.pro
@@ -0,0 +1,19 @@
+# Additional Qt project file for qtmain lib on OS X
+!macx:error("$$_FILE_ is intended only for OS X!")
+
+TEMPLATE = lib
+TARGET = qtcocoamain
+DESTDIR = $$QT.core.libs
+
+CONFIG += staticlib release
+
+QT = core-private gui-private
+LIBS += -framework Cocoa
+
+HEADERS = qcocoaintrospection.h
+
+OBJECTIVE_SOURCES = qcocoamain.mm \
+ qcocoaintrospection.mm
+
+load(qt_installs)
+load(qt_targets)
diff --git a/src/plugins/platforms/cocoa/cocoaplugin.pro b/src/plugins/platforms/cocoa/cocoaplugin.pro
new file mode 100644
index 0000000000..a2fd8c0613
--- /dev/null
+++ b/src/plugins/platforms/cocoa/cocoaplugin.pro
@@ -0,0 +1,109 @@
+TARGET = qcocoa
+
+PLUGIN_TYPE = platforms
+PLUGIN_CLASS_NAME = QCocoaIntegrationPlugin
+load(qt_plugin)
+
+OBJECTIVE_SOURCES += main.mm \
+ qcocoaintegration.mm \
+ qcocoatheme.mm \
+ qcocoabackingstore.mm \
+ qcocoawindow.mm \
+ qnsview.mm \
+ qnsviewaccessibility.mm \
+ qcocoaautoreleasepool.mm \
+ qnswindowdelegate.mm \
+ qcocoaglcontext.mm \
+ qcocoanativeinterface.mm \
+ qcocoaeventdispatcher.mm \
+ qcocoaapplicationdelegate.mm \
+ qcocoaapplication.mm \
+ qcocoamenu.mm \
+ qcocoamenuitem.mm \
+ qcocoamenubar.mm \
+ qcocoamenuloader.mm \
+ qcocoahelpers.mm \
+ qmultitouch_mac.mm \
+ qcocoaaccessibilityelement.mm \
+ qcocoaaccessibility.mm \
+ qcocoacolordialoghelper.mm \
+ qcocoafiledialoghelper.mm \
+ qcocoafontdialoghelper.mm \
+ qcocoacursor.mm \
+ qcocoaclipboard.mm \
+ qcocoadrag.mm \
+ qmacclipboard.mm \
+ qmacmime.mm \
+ qcocoasystemsettings.mm \
+ qcocoainputcontext.mm \
+ qcocoaservices.mm \
+ qcocoasystemtrayicon.mm \
+ qcocoaintrospection.mm \
+ qcocoakeymapper.mm \
+
+SOURCES += messages.cpp
+
+HEADERS += qcocoaintegration.h \
+ qcocoatheme.h \
+ qcocoabackingstore.h \
+ qcocoawindow.h \
+ qnsview.h \
+ qcocoaautoreleasepool.h \
+ qnswindowdelegate.h \
+ qcocoaglcontext.h \
+ qcocoanativeinterface.h \
+ qcocoaeventdispatcher.h \
+ qcocoaapplicationdelegate.h \
+ qcocoaapplication.h \
+ qcocoamenu.h \
+ qcocoamenuitem.h \
+ qcocoamenubar.h \
+ qcocoamenuloader.h \
+ qcocoahelpers.h \
+ qmultitouch_mac_p.h \
+ qcocoaaccessibilityelement.h \
+ qcocoaaccessibility.h \
+ qcocoacolordialoghelper.h \
+ qcocoafiledialoghelper.h \
+ qcocoafontdialoghelper.h \
+ qcocoacursor.h \
+ qcocoaclipboard.h \
+ qcocoadrag.h \
+ qmacclipboard.h \
+ qmacmime.h \
+ qcocoasystemsettings.h \
+ qcocoainputcontext.h \
+ qcocoaservices.h \
+ qcocoasystemtrayicon.h \
+ qcocoaintrospection.h \
+ qcocoakeymapper.h \
+ messages.h
+
+RESOURCES += qcocoaresources.qrc
+
+LIBS += -framework Cocoa -framework Carbon -framework IOKit
+
+QT += core-private gui-private platformsupport-private
+
+qtHaveModule(widgets) {
+ OBJECTIVE_SOURCES += \
+ qpaintengine_mac.mm \
+ qprintengine_mac.mm \
+ qcocoaprintersupport.mm \
+
+ HEADERS += \
+ qpaintengine_mac_p.h \
+ qprintengine_mac_p.h \
+ qcocoaprintersupport.h \
+
+ QT += widgets-private printsupport-private
+}
+
+OTHER_FILES += cocoa.json
+
+# Acccessibility debug support
+# DEFINES += QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
+# include ($$PWD/../../../../util/accessibilityinspector/accessibilityinspector.pri)
+
+# Window debug support
+#DEFINES += QT_COCOA_ENABLE_WINDOW_DEBUG
diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.h b/src/plugins/platforms/cocoa/qcocoaapplication.h
index ffb12ea846..b01f3ca93e 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplication.h
+++ b/src/plugins/platforms/cocoa/qcocoaapplication.h
@@ -102,13 +102,9 @@
- (void)qt_sendPostedMessage:(NSEvent *)event;
- (BOOL)qt_filterEvent:(NSEvent *)event;
+- (BOOL)qt_filterOrSendEvent:(NSEvent *)event;
@end
-@interface QT_MANGLE_NAMESPACE(QNSApplication) : NSApplication {
-}
-@end
-
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSApplication);
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.mm b/src/plugins/platforms/cocoa/qcocoaapplication.mm
index c293f4cd52..cf9644fe9b 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplication.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplication.mm
@@ -83,6 +83,8 @@
QT_USE_NAMESPACE
+static SEL qt_sendEvent_original_SEL = @selector(qt_sendEvent_original:);
+
@implementation NSApplication (QT_MANGLE_NAMESPACE(QApplicationIntegration))
- (void)QT_MANGLE_NAMESPACE(qt_setDockMenu):(NSMenu *)newMenu
@@ -153,34 +155,22 @@ static const QByteArray q_macLocalEventType = QByteArrayLiteral("mac_generic_NSE
return false;
}
-@end
-
-@implementation QNSApplication
-
-- (void)qt_sendEvent_original:(NSEvent *)event
+- (BOOL)qt_filterOrSendEvent:(NSEvent *)event
{
- Q_UNUSED(event);
- // This method will only be used as a signature
- // template for the method we add into NSApplication
- // containing the original [NSApplication sendEvent:] implementation
+ if ([self qt_filterEvent:event])
+ return false;
+
+ Q_ASSERT_X([self respondsToSelector:qt_sendEvent_original_SEL], "qt_filterOrSendEvent:",
+ "Running event loop before calling qt_redirectNSApplicationSendEvent()");
+ [self performSelector:qt_sendEvent_original_SEL withObject:event];
+ return true;
}
- (void)qt_sendEvent_replacement:(NSEvent *)event
{
// This method (or its implementation to be precise) will
- // be called instead of sendEvent if redirection occurs.
- // 'self' will then be an instance of NSApplication
- // (and not QNSApplication)
- if (![NSApp qt_filterEvent:event])
- [self qt_sendEvent_original:event];
-}
-
-- (void)sendEvent:(NSEvent *)event
-{
- // This method will be called if
- // no redirection occurs
- if (![NSApp qt_filterEvent:event])
- [super sendEvent:event];
+ // be called instead of sendEvent: after redirection occurs.
+ [self qt_filterOrSendEvent:event];
}
@end
@@ -189,22 +179,16 @@ QT_BEGIN_NAMESPACE
void qt_redirectNSApplicationSendEvent()
{
- if ([NSApp isMemberOfClass:[QNSApplication class]]) {
- // No need to change implementation since Qt
- // already controls a subclass of NSApplication
- return;
- }
-
// Change the implementation of [NSApplication sendEvent] to the
- // implementation of qt_sendEvent_replacement found in QNSApplication.
+ // implementation of qt_sendEvent_replacement.
// And keep the old implementation that gets overwritten inside a new
// method 'qt_sendEvent_original' that we add to NSApplication
qt_cocoa_change_implementation(
[NSApplication class],
@selector(sendEvent:),
- [QNSApplication class],
+ [NSApplication class],
@selector(qt_sendEvent_replacement:),
- @selector(qt_sendEvent_original:));
+ qt_sendEvent_original_SEL);
}
void qt_resetNSApplicationSendEvent()
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h
index 7f6c4224df..8ffab71eec 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h
@@ -99,15 +99,13 @@
NSMenu *dockMenu;
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader;
NSObject <NSApplicationDelegate> *reflectionDelegate;
- bool inLaunch;
}
+ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate;
- (void)setDockMenu:(NSMenu *)newMenu;
- (void)setMenuLoader:(QT_MANGLE_NAMESPACE(QCocoaMenuLoader)*)menuLoader;
- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader;
- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate;
-- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
-- (void) removeAppleEventHandlers;
+- (NSObject<NSApplicationDelegate> *)reflectionDelegate;
@end
QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaApplicationDelegate);
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index 423d552627..7e0fb62dcf 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -101,7 +101,6 @@ static void cleanupCocoaApplicationDelegate()
{
self = [super init];
if (self) {
- inLaunch = true;
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(updateScreens:)
@@ -241,55 +240,6 @@ static void cleanupCocoaApplicationDelegate()
return NSTerminateCancel;
}
-- (void) applicationWillFinishLaunching:(NSNotification *)notification
-{
- Q_UNUSED(notification);
-
- /*
- From the Cocoa documentation: "A good place to install event handlers
- is in the applicationWillFinishLaunching: method of the application
- delegate. At that point, the Application Kit has installed its default
- event handlers, so if you install a handler for one of the same events,
- it will replace the Application Kit version."
- */
-
- /*
- If Qt is used as a plugin, we let the 3rd party application handle
- events like quit and open file events. Otherwise, if we install our own
- handlers, we easily end up breaking functionality the 3rd party
- application depends on.
- */
- NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
- [eventManager setEventHandler:self
- andSelector:@selector(appleEventQuit:withReplyEvent:)
- forEventClass:kCoreEventClass
- andEventID:kAEQuitApplication];
- [eventManager setEventHandler:self
- andSelector:@selector(getUrl:withReplyEvent:)
- forEventClass:kInternetEventClass
- andEventID:kAEGetURL];
-}
-
-// called by QCocoaIntegration's destructor before resetting the application delegate to nil
-- (void) removeAppleEventHandlers
-{
- NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
- [eventManager removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEQuitApplication];
- [eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
-}
-
-- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
-{
- Q_UNUSED(aNotification);
- inLaunch = false;
- // qt_release_apple_event_handler();
-
-
- // Insert code here to initialize your application
-}
-
-
-
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
Q_UNUSED(filenames);
@@ -297,14 +247,6 @@ static void cleanupCocoaApplicationDelegate()
for (NSString *fileName in filenames) {
QString qtFileName = QCFString::toQString(fileName);
- if (inLaunch) {
- // We need to be careful because Cocoa will be nice enough to take
- // command line arguments and send them to us as events. Given the history
- // of Qt Applications, this will result in behavior people don't want, as
- // they might be doing the opening themselves with the command line parsing.
- if (qApp->arguments().contains(qtFileName))
- continue;
- }
QWindowSystemInterface::handleFileOpenEvent(qtFileName);
}
@@ -367,6 +309,11 @@ static void cleanupCocoaApplicationDelegate()
*/
}
+- (NSObject<NSApplicationDelegate> *)reflectionDelegate
+{
+ return reflectionDelegate;
+}
+
- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate
{
[oldDelegate retain];
@@ -400,20 +347,6 @@ static void cleanupCocoaApplicationDelegate()
[self doesNotRecognizeSelector:invocationSelector];
}
-- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
-{
- Q_UNUSED(replyEvent);
- NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
- QWindowSystemInterface::handleFileOpenEvent(QCFString::toQString(urlString));
-}
-
-- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
-{
- Q_UNUSED(event);
- Q_UNUSED(replyEvent);
- [NSApp terminate:self];
-}
-
- (void)qtDispatcherToQAction:(id)sender
{
Q_UNUSED(sender);
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
index 93476ee1b4..550460a3e1 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
@@ -162,8 +162,6 @@ public:
bool blockSendPostedEvents;
// The following variables help organizing modal sessions:
QStack<QCocoaModalSessionInfo> cocoaModalSessionStack;
- bool currentExecIsNSAppRun;
- bool nsAppRunCalledByQt;
bool cleanupModalSessionsNeeded;
NSModalSession currentModalSessionCached;
NSModalSession currentModalSession();
@@ -175,7 +173,6 @@ public:
void cancelWaitForMoreEvents();
void maybeCancelWaitForMoreEvents();
- void ensureNSAppInitialized();
QCFSocketNotifier cfSocketNotifier;
QList<void *> queuedUserInputEvents; // NSEvent *
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index 2ac9a5dac9..5f268dbf35 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -86,20 +86,13 @@
#include "private/qthread_p.h"
#include "private/qguiapplication_p.h"
#include <qdebug.h>
-
-#undef slots
-#include <Cocoa/Cocoa.h>
-#include <Carbon/Carbon.h>
+#include "qcocoahelpers.h"
+#include "qcocoaapplication.h"
QT_BEGIN_NAMESPACE
QT_USE_NAMESPACE
-enum {
- QtCocoaEventSubTypeWakeup = SHRT_MAX,
- QtCocoaEventSubTypePostMessage = SHRT_MAX-1
-};
-
static inline CFRunLoopRef mainRunLoop()
{
return CFRunLoopGetMain();
@@ -377,53 +370,17 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
if (!excludeUserEvents) {
while (!d->queuedUserInputEvents.isEmpty()) {
event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
- if (!filterNativeEvent("NSEvent", event, 0)) {
- [NSApp sendEvent:event];
+ if ([NSApp qt_filterOrSendEvent:event])
retVal = true;
- }
[event release];
}
}
- // If Qt is used as a plugin, or as an extension in a native cocoa
- // application, we should not run or stop NSApplication; This will be
- // done from the application itself. And if processEvents is called
- // manually (rather than from a QEventLoop), we cannot enter a tight
- // loop and block this call, but instead we need to return after one flush.
- // Finally, if we are to exclude user input events, we cannot call [NSApp run]
- // as we then loose control over which events gets dispatched:
- const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning];
- const bool canExec_Qt = (!excludeUserEvents
- && ((d->processEventsFlags & QEventLoop::DialogExec)
- || (d->processEventsFlags & QEventLoop::EventLoopExec)));
-
- if (canExec_Qt && canExec_3rdParty) {
- // We can use exec-mode, meaning that we can stay in a tight loop until
- // interrupted. This is mostly an optimization, but it allow us to use
- // [NSApp run], which is the normal code path for cocoa applications.
- if (NSModalSession session = d->currentModalSession()) {
- QBoolBlocker execGuard(d->currentExecIsNSAppRun, false);
- while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
- qt_mac_waitForMoreEvents(NSModalPanelRunLoopMode);
-
- if (!d->interrupt && session == d->currentModalSessionCached) {
- // Someone called [NSApp stopModal:] from outside the event
- // dispatcher (e.g to stop a native dialog). But that call wrongly stopped
- // 'session' as well. As a result, we need to restart all internal sessions:
- d->temporarilyStopAllModalSessions();
- }
- } else {
- d->nsAppRunCalledByQt = true;
- QBoolBlocker execGuard(d->currentExecIsNSAppRun, true);
- [NSApp run];
- }
- retVal = true;
- } else {
+ {
int lastSerialCopy = d->lastSerial;
bool hadModalSession = d->currentModalSessionCached != 0;
// We cannot block the thread (and run in a tight loop).
// Instead we will process all current pending events and return.
- d->ensureNSAppInitialized();
if (NSModalSession session = d->currentModalSession()) {
// INVARIANT: a modal window is executing.
if (!excludeUserEvents) {
@@ -444,9 +401,9 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
// this case, we need more control over which events gets dispatched, and
// cannot use [NSApp runModalSession:session]:
event = [NSApp nextEventMatchingMask:NSAnyEventMask
- untilDate:nil
- inMode:NSModalPanelRunLoopMode
- dequeue: YES];
+ untilDate:nil
+ inMode:NSModalPanelRunLoopMode
+ dequeue:YES];
if (event) {
if (IsMouseOrKeyEvent(event)) {
@@ -454,18 +411,16 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
d->queuedUserInputEvents.append(event);
continue;
}
- if (!filterNativeEvent("NSEvent", event, 0)) {
- [NSApp sendEvent:event];
+ if ([NSApp qt_filterOrSendEvent:event])
retVal = true;
- }
}
} while (!d->interrupt && event != nil);
} else do {
// INVARIANT: No modal window is executing.
event = [NSApp nextEventMatchingMask:NSAnyEventMask
- untilDate:nil
- inMode:NSDefaultRunLoopMode
- dequeue: YES];
+ untilDate:nil
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
if (event) {
if (flags & QEventLoop::ExcludeUserInputEvents) {
@@ -475,10 +430,8 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
continue;
}
}
- if (!filterNativeEvent("NSEvent", event, 0)) {
- [NSApp sendEvent:event];
+ if ([NSApp qt_filterOrSendEvent:event])
retVal = true;
- }
}
} while (!d->interrupt && event != nil);
@@ -557,26 +510,6 @@ void QCocoaEventDispatcher::wakeUp()
QEventDispatcherMac Implementation
*****************************************************************************/
-void QCocoaEventDispatcherPrivate::ensureNSAppInitialized()
-{
- // Some elements in Cocoa require NSApplication to be running before
- // they get fully initialized, in particular the menu bar. This
- // function is intended for cases where a dialog is told to execute before
- // QGuiApplication::exec is called, or the application spins the events loop
- // manually rather than calling QGuiApplication:exec.
- // The function makes sure that NSApplication starts running, but stops
- // it again as soon as the send posted events callback is called. That way
- // we let Cocoa finish the initialization it seems to need. We'll only
- // apply this trick at most once for any application, and we avoid doing it
- // for the common case where main just starts QGuiApplication::exec.
- if (nsAppRunCalledByQt || [NSApp isRunning])
- return;
- nsAppRunCalledByQt = true;
- QBoolBlocker block1(interrupt, true);
- QBoolBlocker block2(currentExecIsNSAppRun, true);
- [NSApp run];
-}
-
void QCocoaEventDispatcherPrivate::temporarilyStopAllModalSessions()
{
// Flush, and Stop, all created modal session, and as
@@ -621,7 +554,6 @@ NSModalSession QCocoaEventDispatcherPrivate::currentModalSession()
if (!nswindow)
continue;
- ensureNSAppInitialized();
QBoolBlocker block1(blockSendPostedEvents, true);
info.nswindow = nswindow;
[(NSWindow*) info.nswindow retain];
@@ -766,8 +698,6 @@ QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate()
: processEventsFlags(0),
runLoopTimerRef(0),
blockSendPostedEvents(false),
- currentExecIsNSAppRun(false),
- nsAppRunCalledByQt(false),
cleanupModalSessionsNeeded(false),
currentModalSessionCached(0),
lastSerial(-1),
@@ -858,19 +788,8 @@ void QCocoaEventDispatcherPrivate::processPostedEvents()
if (cleanupModalSessionsNeeded)
cleanupModalSessions();
- if (interrupt) {
- if (currentExecIsNSAppRun) {
- // The event dispatcher has been interrupted. But since
- // [NSApplication run] is running the event loop, we
- // delayed stopping it until now (to let cocoa process
- // pending cocoa events first).
- if (currentModalSessionCached)
- temporarilyStopAllModalSessions();
- [NSApp stop:NSApp];
- cancelWaitForMoreEvents();
- }
+ if (interrupt)
return;
- }
int serial = serialNumber.load();
if (!threadData->canWait || (serial != lastSerial)) {
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 6d1882f622..ef242195c0 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -232,7 +232,7 @@ QCocoaIntegration::QCocoaIntegration()
qApp->setAttribute(Qt::AA_DontUseNativeMenuBar, false);
- NSApplication *cocoaApplication = [QNSApplication sharedApplication];
+ NSApplication *cocoaApplication = NSApp;
qt_redirectNSApplicationSendEvent();
if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
@@ -280,9 +280,8 @@ QCocoaIntegration::~QCocoaIntegration()
if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) {
// remove the apple event handlers installed by QCocoaApplicationDelegate
QCocoaApplicationDelegate *delegate = [QCocoaApplicationDelegate sharedDelegate];
- [delegate removeAppleEventHandlers];
// reset the application delegate
- [[NSApplication sharedApplication] setDelegate: 0];
+ [[NSApplication sharedApplication] setDelegate:[delegate reflectionDelegate]];
}
// Delete the clipboard integration and destroy mime type converters.
diff --git a/src/plugins/platforms/cocoa/qcocoaintrospection.mm b/src/plugins/platforms/cocoa/qcocoaintrospection.mm
index 806effc929..654ed68e26 100644
--- a/src/plugins/platforms/cocoa/qcocoaintrospection.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintrospection.mm
@@ -79,43 +79,33 @@ QT_BEGIN_NAMESPACE
void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel, SEL backupSel)
{
-#ifndef QT_MAC_USE_COCOA
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
-#endif
- {
- // The following code replaces the _implementation_ for the selector we want to hack
- // (originalSel) with the implementation found in proxyClass. Then it creates
- // a new 'backup' method inside baseClass containing the old, original,
- // implementation (fakeSel). You can let the proxy implementation of originalSel
- // call fakeSel if needed (similar approach to calling a super class implementation).
- // fakeSel must also be implemented in proxyClass, as the signature is used
- // as template for the method one we add into baseClass.
- // NB: You will typically never create any instances of proxyClass; we use it
- // only for stealing its contents and put it into baseClass.
- if (!replacementSel)
- replacementSel = originalSel;
+ // The following code replaces the _implementation_ for the selector we want to hack
+ // (originalSel) with the implementation found in proxyClass. Then it creates
+ // a new 'backup' method inside baseClass containing the old, original,
+ // implementation (fakeSel). You can let the proxy implementation of originalSel
+ // call fakeSel if needed (similar approach to calling a super class implementation).
+ // fakeSel must also be implemented in proxyClass, as the signature is used
+ // as template for the method one we add into baseClass.
+ // NB: You will typically never create any instances of proxyClass; we use it
+ // only for stealing its contents and put it into baseClass.
+ if (!replacementSel)
+ replacementSel = originalSel;
- Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
- Method replacementMethod = class_getInstanceMethod(proxyClass, replacementSel);
- IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(replacementMethod));
+ Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
+ Method replacementMethod = class_getInstanceMethod(proxyClass, replacementSel);
+ IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(replacementMethod));
- if (backupSel) {
- Method backupMethod = class_getInstanceMethod(proxyClass, backupSel);
- class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod));
- }
+ if (backupSel) {
+ Method backupMethod = class_getInstanceMethod(proxyClass, backupSel);
+ class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod));
}
}
void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel)
{
-#ifndef QT_MAC_USE_COCOA
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
-#endif
- {
- Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
- Method backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel);
- method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass));
- }
+ Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
+ Method backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel);
+ method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass));
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoamain.mm b/src/plugins/platforms/cocoa/qcocoamain.mm
new file mode 100644
index 0000000000..51d27e0c72
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamain.mm
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** 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 <Cocoa/Cocoa.h>
+#include <QtGui/qpa/qwindowsysteminterface.h>
+#include <QtCore/private/qcore_mac_p.h>
+#include "qcocoaintrospection.h"
+
+extern int qMain(int argc, char *argv[]);
+
+@interface QCocoaMainWrapper : NSObject
+
+- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
+- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
+- (void)runUserMain;
+
+@end
+
+@implementation QCocoaMainWrapper
+
+- (void)applicationWillFinishLaunching:(NSNotification *)notification
+{
+ if ([notification object] != NSApp) // Shouldn't happen AFAIK, but still
+ return;
+
+ /*
+ From the Cocoa documentation: "A good place to install event handlers
+ is in the applicationWillFinishLaunching: method of the application
+ delegate. At that point, the Application Kit has installed its default
+ event handlers, so if you install a handler for one of the same events,
+ it will replace the Application Kit version."
+ */
+
+ /*
+ If Qt is used as a plugin, we let the 3rd party application handle
+ events like quit and open file events. Otherwise, if we install our own
+ handlers, we easily end up breaking functionality the 3rd party
+ application depends on.
+ */
+ NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
+ [eventManager setEventHandler:self
+ andSelector:@selector(appleEventQuit:withReplyEvent:)
+ forEventClass:kCoreEventClass
+ andEventID:kAEQuitApplication];
+ [eventManager setEventHandler:self
+ andSelector:@selector(getUrl:withReplyEvent:)
+ forEventClass:kInternetEventClass
+ andEventID:kAEGetURL];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)notification
+{
+ if ([notification object] != NSApp) // Shouldn't happen AFAIK, but still
+ return;
+
+ // We schedule the main-redirection for the next eventloop pass so that we
+ // can return from this function and let NSApplicationMain finish its job.
+ [NSTimer scheduledTimerWithTimeInterval:0 target:self
+ selector:@selector(runUserMain) userInfo:nil repeats:NO];
+}
+
+- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+{
+ Q_UNUSED(replyEvent);
+ NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+ QWindowSystemInterface::handleFileOpenEvent(QCFString::toQString(urlString));
+}
+
+- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+{
+ Q_UNUSED(event);
+ Q_UNUSED(replyEvent);
+ [NSApp terminate:self];
+}
+
+- (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]]);
+ }
+
+ qMain(argc, argv);
+ delete[] argv;
+
+ NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
+ [eventManager removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEQuitApplication];
+ [eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
+
+ [NSApp terminate:self];
+}
+
+@end
+
+static SEL qt_infoDictionary_original_SEL = @selector(qt_infoDictionary_original);
+
+@implementation NSBundle (QT_MANGLE_NAMESPACE(QCocoaMain))
+
+- (Class)qt_infoDictionary_replacement
+{
+ if (self == [NSBundle mainBundle]) {
+ static NSMutableDictionary *infoDict = nil;
+ if (!infoDict) {
+ infoDict = [[self performSelector:qt_infoDictionary_original_SEL] mutableCopy];
+ [infoDict setValue:@"NSApplication" forKey:@"NSPrincipalClass"];
+ }
+ return infoDict;
+ }
+
+ return [self performSelector:qt_infoDictionary_original_SEL];
+}
+
+@end
+
+int main(int argc, char *argv[])
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ QCocoaMainWrapper *mainWrapper = [[QCocoaMainWrapper alloc] init];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:mainWrapper selector:@selector(applicationWillFinishLaunching:)
+ name:NSApplicationWillFinishLaunchingNotification object:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:mainWrapper selector:@selector(applicationDidFinishLaunching:)
+ name:NSApplicationDidFinishLaunchingNotification object:nil];
+
+ NSBundle *mainBundle = [NSBundle mainBundle];
+ if (!mainBundle.principalClass) {
+ // Since several of the GUI based Qt utilities (e.g., qmlscene) are command
+ // line applications, meaning non-bundle applications, we need to make Cocoa
+ // believe everything is fine. So we fake the main bundle's dictionary by
+ // adding the "NSPrincipalClass" property. So far, this seems to be enough to
+ // keep NSApplicationMain() happy and running...
+ qt_cocoa_change_implementation([NSBundle class], @selector(infoDictionary),
+ [NSBundle class], @selector(qt_infoDictionary_replacement), qt_infoDictionary_original_SEL);
+ }
+
+ return NSApplicationMain(argc, (const char **)argv);
+}