diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/cocoa/cocoa.pro | 110 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/cocoamain.pro | 19 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/cocoaplugin.pro | 109 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaapplication.h | 6 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaapplication.mm | 46 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm | 77 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaeventdispatcher.h | 3 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm | 107 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaintegration.mm | 5 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaintrospection.mm | 50 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamain.mm | 181 |
12 files changed, 370 insertions, 347 deletions
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); +} |