|
|
@@ -35,151 +35,186 @@ MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr; |
|
|
|
//==============================================================================
|
|
|
|
struct AppDelegateClass : public ObjCClass<NSObject>
|
|
|
|
{
|
|
|
|
AppDelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_")
|
|
|
|
AppDelegateClass() : ObjCClass ("JUCEAppDelegate_")
|
|
|
|
{
|
|
|
|
addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching);
|
|
|
|
addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate);
|
|
|
|
addMethod (@selector (applicationWillTerminate:), applicationWillTerminate);
|
|
|
|
addMethod (@selector (application:openFile:), application_openFile);
|
|
|
|
addMethod (@selector (application:openFiles:), application_openFiles);
|
|
|
|
addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive);
|
|
|
|
addMethod (@selector (applicationDidResignActive:), applicationDidResignActive);
|
|
|
|
addMethod (@selector (applicationWillUnhide:), applicationWillUnhide);
|
|
|
|
addMethod (@selector (applicationWillFinishLaunching:), [] (id self, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
[[NSAppleEventManager sharedAppleEventManager] setEventHandler: self
|
|
|
|
andSelector: @selector (getUrl:withReplyEvent:)
|
|
|
|
forEventClass: kInternetEventClass
|
|
|
|
andEventID: kAEGetURL];
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
});
|
|
|
|
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent);
|
|
|
|
addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback);
|
|
|
|
addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan);
|
|
|
|
addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded);
|
|
|
|
addMethod (@selector (dummyMethod), dummyMethod);
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
addMethod (@selector (applicationShouldTerminate:), [] (id /*self*/, SEL, NSApplication*)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
app->systemRequestedQuit();
|
|
|
|
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
//==============================================================================
|
|
|
|
addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate");
|
|
|
|
if (! MessageManager::getInstance()->hasStopMessageBeenSent())
|
|
|
|
return NSTerminateCancel;
|
|
|
|
}
|
|
|
|
|
|
|
|
addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching);
|
|
|
|
return NSTerminateNow;
|
|
|
|
});
|
|
|
|
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate);
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
addMethod (@selector (applicationWillTerminate:), [] (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
JUCEApplicationBase::appWillTerminateByForce();
|
|
|
|
});
|
|
|
|
|
|
|
|
addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications);
|
|
|
|
addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications);
|
|
|
|
addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification);
|
|
|
|
#endif
|
|
|
|
addMethod (@selector (application:openFile:), [] (id /*self*/, SEL, NSApplication*, NSString* filename)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
registerClass();
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
});
|
|
|
|
|
|
|
|
addMethod (@selector (application:openFiles:), [] (id /*self*/, SEL, NSApplication*, NSArray* filenames)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
StringArray files;
|
|
|
|
|
|
|
|
for (NSString* f in filenames)
|
|
|
|
files.add (quotedIfContainsSpaces (f));
|
|
|
|
|
|
|
|
if (files.size() > 0)
|
|
|
|
app->anotherInstanceStarted (files.joinIntoString (" "));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
addMethod (@selector (applicationDidBecomeActive:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); });
|
|
|
|
addMethod (@selector (applicationDidResignActive:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); });
|
|
|
|
addMethod (@selector (applicationWillUnhide:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); });
|
|
|
|
|
|
|
|
private:
|
|
|
|
static void applicationWillFinishLaunching (id self, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
[[NSAppleEventManager sharedAppleEventManager] setEventHandler: self
|
|
|
|
andSelector: @selector (getUrl:withReplyEvent:)
|
|
|
|
forEventClass: kInternetEventClass
|
|
|
|
andEventID: kAEGetURL];
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
}
|
|
|
|
addMethod (@selector (getUrl:withReplyEvent:), [] (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue]));
|
|
|
|
});
|
|
|
|
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification)
|
|
|
|
{
|
|
|
|
if (notification.userInfo != nil)
|
|
|
|
addMethod (@selector (broadcastMessageCallback:), [] (id /*self*/, SEL, NSNotification* n)
|
|
|
|
{
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
|
|
|
// NSUserNotification is deprecated from macOS 11, but there doesn't seem to be a
|
|
|
|
// replacement for NSApplicationLaunchUserNotificationKey returning a non-deprecated type
|
|
|
|
NSUserNotification* userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
NSDictionary* dict = (NSDictionary*) [n userInfo];
|
|
|
|
auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]);
|
|
|
|
MessageManager::getInstance()->deliverBroadcastMessage (messageString);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (userNotification != nil && userNotification.userInfo != nil)
|
|
|
|
didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
addMethod (@selector (mainMenuTrackingBegan:), [] (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
if (menuTrackingChangedCallback != nullptr)
|
|
|
|
menuTrackingChangedCallback (true);
|
|
|
|
});
|
|
|
|
|
|
|
|
static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
addMethod (@selector (mainMenuTrackingEnded:), [] (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
app->systemRequestedQuit();
|
|
|
|
if (menuTrackingChangedCallback != nullptr)
|
|
|
|
menuTrackingChangedCallback (false);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (! MessageManager::getInstance()->hasStopMessageBeenSent())
|
|
|
|
return NSTerminateCancel;
|
|
|
|
}
|
|
|
|
// (used as a way of running a dummy thread)
|
|
|
|
addMethod (@selector (dummyMethod), [] (id /*self*/, SEL) {});
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
|
|
|
|
return NSTerminateNow;
|
|
|
|
}
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
//==============================================================================
|
|
|
|
addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate");
|
|
|
|
|
|
|
|
static void applicationWillTerminate (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
JUCEApplicationBase::appWillTerminateByForce();
|
|
|
|
}
|
|
|
|
addMethod (@selector (applicationDidFinishLaunching:), [] (id self, SEL, NSNotification* notification)
|
|
|
|
{
|
|
|
|
if (notification.userInfo != nil)
|
|
|
|
{
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
|
|
|
// NSUserNotification is deprecated from macOS 11, but there doesn't seem to be a
|
|
|
|
// replacement for NSApplicationLaunchUserNotificationKey returning a non-deprecated type
|
|
|
|
NSUserNotification* userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
|
|
|
|
if (userNotification != nil && userNotification.userInfo != nil)
|
|
|
|
didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
addMethod (@selector (setPushNotificationsDelegate:), [] (id self, SEL, NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate)
|
|
|
|
{
|
|
|
|
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
object_setInstanceVariable (self, "pushNotificationsDelegate", delegate);
|
|
|
|
});
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), [] (id self, SEL, NSApplication* application, NSData* deviceToken)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
|
|
|
|
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
|
|
|
[invocation setSelector: selector];
|
|
|
|
[invocation setTarget: delegate];
|
|
|
|
[invocation setArgument: &application atIndex:2];
|
|
|
|
[invocation setArgument: &deviceToken atIndex:3];
|
|
|
|
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), [] (id self, SEL, NSApplication* application, NSError* error)
|
|
|
|
{
|
|
|
|
StringArray files;
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
for (NSString* f in filenames)
|
|
|
|
files.add (quotedIfContainsSpaces (f));
|
|
|
|
SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
|
|
|
|
|
|
|
|
if (files.size() > 0)
|
|
|
|
app->anotherInstanceStarted (files.joinIntoString (" "));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
|
|
|
[invocation setSelector: selector];
|
|
|
|
[invocation setTarget: delegate];
|
|
|
|
[invocation setArgument: &application atIndex:2];
|
|
|
|
[invocation setArgument: &error atIndex:3];
|
|
|
|
|
|
|
|
static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n)
|
|
|
|
{
|
|
|
|
NSDictionary* dict = (NSDictionary*) [n userInfo];
|
|
|
|
auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]);
|
|
|
|
MessageManager::getInstance()->deliverBroadcastMessage (messageString);
|
|
|
|
}
|
|
|
|
addMethod (@selector (application:didReceiveRemoteNotification:), [] (id self, SEL, NSApplication* application, NSDictionary* userInfo)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
if (menuTrackingChangedCallback != nullptr)
|
|
|
|
menuTrackingChangedCallback (true);
|
|
|
|
}
|
|
|
|
SEL selector = @selector (application:didReceiveRemoteNotification:);
|
|
|
|
|
|
|
|
static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
if (menuTrackingChangedCallback != nullptr)
|
|
|
|
menuTrackingChangedCallback (false);
|
|
|
|
}
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
|
|
|
[invocation setSelector: selector];
|
|
|
|
[invocation setTarget: delegate];
|
|
|
|
[invocation setArgument: &application atIndex:2];
|
|
|
|
[invocation setArgument: &userInfo atIndex:3];
|
|
|
|
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread)
|
|
|
|
registerClass();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static void focusChanged()
|
|
|
|
{
|
|
|
|
if (appFocusChangeCallback != nullptr)
|
|
|
|
(*appFocusChangeCallback)();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static String quotedIfContainsSpaces (NSString* file)
|
|
|
|
{
|
|
|
|
String s (nsStringToJuce (file));
|
|
|
@@ -191,71 +226,12 @@ private: |
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
//==============================================================================
|
|
|
|
static void setPushNotificationsDelegate (id self, SEL, NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate)
|
|
|
|
{
|
|
|
|
object_setInstanceVariable (self, "pushNotificationsDelegate", delegate);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
static NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self)
|
|
|
|
{
|
|
|
|
return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (self, "pushNotificationsDelegate");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
|
|
|
|
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
|
|
|
[invocation setSelector: selector];
|
|
|
|
[invocation setTarget: delegate];
|
|
|
|
[invocation setArgument: &application atIndex:2];
|
|
|
|
[invocation setArgument: &deviceToken atIndex:3];
|
|
|
|
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
|
|
|
|
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
|
|
|
[invocation setSelector: selector];
|
|
|
|
[invocation setTarget: delegate];
|
|
|
|
[invocation setArgument: &application atIndex:2];
|
|
|
|
[invocation setArgument: &error atIndex:3];
|
|
|
|
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
SEL selector = @selector (application:didReceiveRemoteNotification:);
|
|
|
|
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
|
|
|
[invocation setSelector: selector];
|
|
|
|
[invocation setTarget: delegate];
|
|
|
|
[invocation setArgument: &application atIndex:2];
|
|
|
|
[invocation setArgument: &userInfo atIndex:3];
|
|
|
|
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|