diff options
author | Morten Johan Sørvig <morten.sorvig@digia.com> | 2013-09-02 09:30:26 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-02 13:07:35 +0200 |
commit | 8fc97fdfc76fc62e1faa23f8b768d1ec303329d4 (patch) | |
tree | 3991bf746691a6c33615718dd05f6e23b9795ec6 /src | |
parent | 585758389c64e556ef4808308dee27e19241581e (diff) |
Revert Mac event loop changes.
"Make QGuiApplication::exec() run within NSApplicationMain()"
"Make Qt process native and timer events on Cocoa applications"
"Cocoa: Fix QFontDialog, QColorDialog auto-tests"
This reverts commits
1e14762b8d79118540bd09a84dd3e48f4f5e113e
e4b2a0b4bab2a17a65fedafe9bae50af1fe019f6
df7944e7d7dd8b2bbccbd639eff0ab09745d6cc3
Change-Id: I80b65b5ee0297b090f807bd420664233dfc44f7b
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@digia.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src')
17 files changed, 365 insertions, 401 deletions
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index dd13882f8c..a2fd8c0613 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -1,7 +1,109 @@ -TEMPLATE = subdirs +TARGET = qcocoa -cocoamain.file = cocoamain.pro -cocoaplugin.file = cocoaplugin.pro +PLUGIN_TYPE = platforms +PLUGIN_CLASS_NAME = QCocoaIntegrationPlugin +load(qt_plugin) -SUBDIRS = cocoamain cocoaplugin +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/cocoamain.pro b/src/plugins/platforms/cocoa/cocoamain.pro deleted file mode 100644 index 8065f8b785..0000000000 --- a/src/plugins/platforms/cocoa/cocoamain.pro +++ /dev/null @@ -1,19 +0,0 @@ -# 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 deleted file mode 100644 index a2fd8c0613..0000000000 --- a/src/plugins/platforms/cocoa/cocoaplugin.pro +++ /dev/null @@ -1,109 +0,0 @@ -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 b01f3ca93e..ffb12ea846 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplication.h +++ b/src/plugins/platforms/cocoa/qcocoaapplication.h @@ -102,9 +102,13 @@ - (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 cf9644fe9b..c293f4cd52 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplication.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplication.mm @@ -83,8 +83,6 @@ 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 @@ -155,22 +153,34 @@ static const QByteArray q_macLocalEventType = QByteArrayLiteral("mac_generic_NSE return false; } -- (BOOL)qt_filterOrSendEvent:(NSEvent *)event -{ - if ([self qt_filterEvent:event]) - return false; +@end - 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; +@implementation QNSApplication + +- (void)qt_sendEvent_original:(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 } - (void)qt_sendEvent_replacement:(NSEvent *)event { // This method (or its implementation to be precise) will - // be called instead of sendEvent: after redirection occurs. - [self qt_filterOrSendEvent:event]; + // 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]; } @end @@ -179,16 +189,22 @@ 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. + // implementation of qt_sendEvent_replacement found in QNSApplication. // 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:), - [NSApplication class], + [QNSApplication class], @selector(qt_sendEvent_replacement:), - qt_sendEvent_original_SEL); + @selector(qt_sendEvent_original:)); } void qt_resetNSApplicationSendEvent() diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h index 8ffab71eec..7f6c4224df 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h @@ -99,13 +99,15 @@ 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; -- (NSObject<NSApplicationDelegate> *)reflectionDelegate; +- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; +- (void) removeAppleEventHandlers; @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaApplicationDelegate); diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 7e0fb62dcf..423d552627 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -101,6 +101,7 @@ static void cleanupCocoaApplicationDelegate() { self = [super init]; if (self) { + inLaunch = true; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateScreens:) @@ -240,6 +241,55 @@ 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); @@ -247,6 +297,14 @@ 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); } @@ -309,11 +367,6 @@ static void cleanupCocoaApplicationDelegate() */ } -- (NSObject<NSApplicationDelegate> *)reflectionDelegate -{ - return reflectionDelegate; -} - - (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate { [oldDelegate retain]; @@ -347,6 +400,20 @@ 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/qcocoacolordialoghelper.h b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h index 2b34f909d1..59e029769d 100644 --- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h @@ -59,8 +59,6 @@ public: void setCurrentColor(const QColor&); QColor currentColor() const; - - bool event(QEvent *); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm index ef2b4cbcfb..d90d77ec1d 100644 --- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm @@ -161,10 +161,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); - (void)closePanel { - if (mDialogIsExecuting) { - mDialogIsExecuting = false; - [NSApp stopModal]; - } [mColorPanel close]; } @@ -492,22 +488,6 @@ QColor QCocoaColorDialogHelper::currentColor() const return sharedColorPanel()->currentColor(); } -bool QCocoaColorDialogHelper::event(QEvent *e) -{ - if (e->type() == QEvent::KeyPress) { - QKeyEvent *ke = static_cast<QKeyEvent *>(e); - if (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) { - emit accept(); - return true; - } else if (ke->key() == Qt::Key_Escape) { - emit reject(); - return true; - } - } - - return false; -} - QT_END_NAMESPACE #endif // QT_NO_COLORDIALOG diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index 550460a3e1..93476ee1b4 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -162,6 +162,8 @@ public: bool blockSendPostedEvents; // The following variables help organizing modal sessions: QStack<QCocoaModalSessionInfo> cocoaModalSessionStack; + bool currentExecIsNSAppRun; + bool nsAppRunCalledByQt; bool cleanupModalSessionsNeeded; NSModalSession currentModalSessionCached; NSModalSession currentModalSession(); @@ -173,6 +175,7 @@ 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 c236243438..2ac9a5dac9 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -86,13 +86,20 @@ #include "private/qthread_p.h" #include "private/qguiapplication_p.h" #include <qdebug.h> -#include "qcocoahelpers.h" -#include "qcocoaapplication.h" + +#undef slots +#include <Cocoa/Cocoa.h> +#include <Carbon/Carbon.h> QT_BEGIN_NAMESPACE QT_USE_NAMESPACE +enum { + QtCocoaEventSubTypeWakeup = SHRT_MAX, + QtCocoaEventSubTypePostMessage = SHRT_MAX-1 +}; + static inline CFRunLoopRef mainRunLoop() { return CFRunLoopGetMain(); @@ -111,6 +118,11 @@ static Boolean runLoopSourceEqualCallback(const void *info1, const void *info2) void QCocoaEventDispatcherPrivate::runLoopTimerCallback(CFRunLoopTimerRef, void *info) { QCocoaEventDispatcherPrivate *d = static_cast<QCocoaEventDispatcherPrivate *>(info); + if ((d->processEventsFlags & QEventLoop::EventLoopExec) == 0) { + // processEvents() was called "manually," ignore this source for now + d->maybeCancelWaitForMoreEvents(); + return; + } CFRunLoopSourceSignal(d->activateTimersSourceRef); } @@ -365,17 +377,53 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) if (!excludeUserEvents) { while (!d->queuedUserInputEvents.isEmpty()) { event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); - if ([NSApp qt_filterOrSendEvent:event]) + if (!filterNativeEvent("NSEvent", event, 0)) { + [NSApp sendEvent: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) { @@ -396,9 +444,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)) { @@ -406,16 +454,18 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) d->queuedUserInputEvents.append(event); continue; } - if ([NSApp qt_filterOrSendEvent:event]) + if (!filterNativeEvent("NSEvent", event, 0)) { + [NSApp sendEvent: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) { @@ -425,8 +475,10 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) continue; } } - if ([NSApp qt_filterOrSendEvent:event]) + if (!filterNativeEvent("NSEvent", event, 0)) { + [NSApp sendEvent:event]; retVal = true; + } } } while (!d->interrupt && event != nil); @@ -505,6 +557,26 @@ 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 @@ -549,6 +621,7 @@ NSModalSession QCocoaEventDispatcherPrivate::currentModalSession() if (!nswindow) continue; + ensureNSAppInitialized(); QBoolBlocker block1(blockSendPostedEvents, true); info.nswindow = nswindow; [(NSWindow*) info.nswindow retain]; @@ -693,6 +766,8 @@ QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate() : processEventsFlags(0), runLoopTimerRef(0), blockSendPostedEvents(false), + currentExecIsNSAppRun(false), + nsAppRunCalledByQt(false), cleanupModalSessionsNeeded(false), currentModalSessionCached(0), lastSerial(-1), @@ -783,8 +858,19 @@ void QCocoaEventDispatcherPrivate::processPostedEvents() if (cleanupModalSessionsNeeded) cleanupModalSessions(); - if (interrupt) + 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(); + } return; + } int serial = serialNumber.load(); if (!threadData->canWait || (serial != lastSerial)) { @@ -805,6 +891,11 @@ void QCocoaEventDispatcherPrivate::firstLoopEntry(CFRunLoopObserverRef ref, void QCocoaEventDispatcherPrivate::postedEventsSourceCallback(void *info) { QCocoaEventDispatcherPrivate *d = static_cast<QCocoaEventDispatcherPrivate *>(info); + if ((d->processEventsFlags & QEventLoop::EventLoopExec) == 0) { + // processEvents() was called "manually," ignore this source for now + d->maybeCancelWaitForMoreEvents(); + return; + } d->processPostedEvents(); d->maybeCancelWaitForMoreEvents(); } diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm index 5bd0ad2a43..91fb52eb6d 100644 --- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm @@ -203,10 +203,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate); - (void)closePanel { - if (mDialogIsExecuting) { - mDialogIsExecuting = false; - [NSApp stopModal]; - } [mFontPanel close]; } diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index ef242195c0..6d1882f622 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 = NSApp; + NSApplication *cocoaApplication = [QNSApplication sharedApplication]; qt_redirectNSApplicationSendEvent(); if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) { @@ -280,8 +280,9 @@ 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:[delegate reflectionDelegate]]; + [[NSApplication sharedApplication] setDelegate: 0]; } // 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 654ed68e26..806effc929 100644 --- a/src/plugins/platforms/cocoa/qcocoaintrospection.mm +++ b/src/plugins/platforms/cocoa/qcocoaintrospection.mm @@ -79,33 +79,43 @@ QT_BEGIN_NAMESPACE void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel, SEL backupSel) { - // 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; +#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; - 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) { - Method originalMethod = class_getInstanceMethod(baseClass, originalSel); - Method backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel); - method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass)); +#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)); + } } QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamain.mm b/src/plugins/platforms/cocoa/qcocoamain.mm deleted file mode 100644 index 51d27e0c72..0000000000 --- a/src/plugins/platforms/cocoa/qcocoamain.mm +++ /dev/null @@ -1,181 +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$ -** -****************************************************************************/ - -#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); -} diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp index c9f03c5a97..5da41aa0a5 100644 --- a/src/widgets/dialogs/qcolordialog.cpp +++ b/src/widgets/dialogs/qcolordialog.cpp @@ -2166,8 +2166,6 @@ void QColorDialog::keyPressEvent(QKeyEvent *e) } e->accept(); return; - } else if (d->nativeDialogInUse && d->platformColorDialogHelper()->event(e)) { - return; } QDialog::keyPressEvent(e); } diff --git a/src/widgets/dialogs/qfontdialog.cpp b/src/widgets/dialogs/qfontdialog.cpp index 28afd91a09..b989ea7c86 100644 --- a/src/widgets/dialogs/qfontdialog.cpp +++ b/src/widgets/dialogs/qfontdialog.cpp @@ -983,9 +983,14 @@ void QFontDialog::setVisible(bool visible) Q_D(QFontDialog); if (d->canBeNativeDialog()) d->setNativeDialogVisible(visible); - // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below - // updates the state correctly, but skips showing the non-native version: - setAttribute(Qt::WA_DontShowOnScreen, d->nativeDialogInUse); + if (d->nativeDialogInUse) { + // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below + // updates the state correctly, but skips showing the non-native version: + setAttribute(Qt::WA_DontShowOnScreen, true); + } else { + d->nativeDialogInUse = false; + setAttribute(Qt::WA_DontShowOnScreen, false); + } QDialog::setVisible(visible); } |