|
|
@@ -33,300 +33,302 @@ using MenuTrackingChangedCallback = void (*)(bool); |
|
|
|
MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr;
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
struct AppDelegate
|
|
|
|
struct AppDelegateClass : public ObjCClass<NSObject>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
AppDelegate()
|
|
|
|
AppDelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_")
|
|
|
|
{
|
|
|
|
static AppDelegateClass cls;
|
|
|
|
delegate = [cls.createInstance() init];
|
|
|
|
addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@");
|
|
|
|
addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@");
|
|
|
|
addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@");
|
|
|
|
addMethod (@selector (application:openFile:), application_openFile, "c@:@@");
|
|
|
|
addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@");
|
|
|
|
addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@");
|
|
|
|
addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@");
|
|
|
|
addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@");
|
|
|
|
|
|
|
|
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@");
|
|
|
|
addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@");
|
|
|
|
addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@");
|
|
|
|
addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@");
|
|
|
|
addMethod (@selector (dummyMethod), dummyMethod, "v@:");
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
//==============================================================================
|
|
|
|
addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate");
|
|
|
|
|
|
|
|
addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@");
|
|
|
|
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
[center addObserver: delegate selector: @selector (mainMenuTrackingBegan:)
|
|
|
|
name: NSMenuDidBeginTrackingNotification object: nil];
|
|
|
|
[center addObserver: delegate selector: @selector (mainMenuTrackingEnded:)
|
|
|
|
name: NSMenuDidEndTrackingNotification object: nil];
|
|
|
|
addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@");
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
|
|
|
|
if (JUCEApplicationBase::isStandaloneApp())
|
|
|
|
{
|
|
|
|
[NSApp setDelegate: delegate];
|
|
|
|
addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@");
|
|
|
|
addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@");
|
|
|
|
addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
[[NSDistributedNotificationCenter defaultCenter] addObserver: delegate
|
|
|
|
selector: @selector (broadcastMessageCallback:)
|
|
|
|
name: getBroadcastEventName()
|
|
|
|
object: nil
|
|
|
|
suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[center addObserver: delegate selector: @selector (applicationDidResignActive:)
|
|
|
|
name: NSApplicationDidResignActiveNotification object: NSApp];
|
|
|
|
registerClass();
|
|
|
|
}
|
|
|
|
|
|
|
|
[center addObserver: delegate selector: @selector (applicationDidBecomeActive:)
|
|
|
|
name: NSApplicationDidBecomeActiveNotification object: NSApp];
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
[center addObserver: delegate selector: @selector (applicationWillUnhide:)
|
|
|
|
name: NSApplicationWillUnhideNotification object: NSApp];
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
static void 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
~AppDelegate()
|
|
|
|
static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*)
|
|
|
|
{
|
|
|
|
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate];
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver: delegate];
|
|
|
|
|
|
|
|
if (JUCEApplicationBase::isStandaloneApp())
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
[NSApp setDelegate: nil];
|
|
|
|
app->systemRequestedQuit();
|
|
|
|
|
|
|
|
[[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate
|
|
|
|
name: getBroadcastEventName()
|
|
|
|
object: nil];
|
|
|
|
if (! MessageManager::getInstance()->hasStopMessageBeenSent())
|
|
|
|
return NSTerminateCancel;
|
|
|
|
}
|
|
|
|
|
|
|
|
[delegate release];
|
|
|
|
return NSTerminateNow;
|
|
|
|
}
|
|
|
|
|
|
|
|
static NSString* getBroadcastEventName()
|
|
|
|
static void applicationWillTerminate (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64()));
|
|
|
|
JUCEApplicationBase::appWillTerminateByForce();
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageQueue messageQueue;
|
|
|
|
id delegate;
|
|
|
|
|
|
|
|
private:
|
|
|
|
//==============================================================================
|
|
|
|
struct AppDelegateClass : public ObjCClass<NSObject>
|
|
|
|
static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename)
|
|
|
|
{
|
|
|
|
AppDelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_")
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@");
|
|
|
|
addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@");
|
|
|
|
addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@");
|
|
|
|
addMethod (@selector (application:openFile:), application_openFile, "c@:@@");
|
|
|
|
addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@");
|
|
|
|
addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@");
|
|
|
|
addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@");
|
|
|
|
addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@");
|
|
|
|
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@");
|
|
|
|
addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@");
|
|
|
|
addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@");
|
|
|
|
addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@");
|
|
|
|
addMethod (@selector (dummyMethod), dummyMethod, "v@:");
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
//==============================================================================
|
|
|
|
addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate");
|
|
|
|
static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
StringArray files;
|
|
|
|
|
|
|
|
addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@");
|
|
|
|
for (NSString* f in filenames)
|
|
|
|
files.add (quotedIfContainsSpaces (f));
|
|
|
|
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@");
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
if (files.size() > 0)
|
|
|
|
app->anotherInstanceStarted (files.joinIntoString (" "));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@");
|
|
|
|
addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@");
|
|
|
|
addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@");
|
|
|
|
#endif
|
|
|
|
static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
|
|
|
|
registerClass();
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
if (menuTrackingChangedCallback != nullptr)
|
|
|
|
(*menuTrackingChangedCallback) (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
static void 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
if (menuTrackingChangedCallback != nullptr)
|
|
|
|
(*menuTrackingChangedCallback) (false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
app->systemRequestedQuit();
|
|
|
|
static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread)
|
|
|
|
|
|
|
|
if (! MessageManager::getInstance()->hasStopMessageBeenSent())
|
|
|
|
return NSTerminateCancel;
|
|
|
|
}
|
|
|
|
static void focusChanged()
|
|
|
|
{
|
|
|
|
if (appFocusChangeCallback != nullptr)
|
|
|
|
(*appFocusChangeCallback)();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NSTerminateNow;
|
|
|
|
}
|
|
|
|
static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void applicationWillTerminate (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
JUCEApplicationBase::appWillTerminateByForce();
|
|
|
|
}
|
|
|
|
static String quotedIfContainsSpaces (NSString* file)
|
|
|
|
{
|
|
|
|
String s (nsStringToJuce (file));
|
|
|
|
s = s.unquoted().replace ("\"", "\\\"");
|
|
|
|
|
|
|
|
static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
if (s.containsChar (' '))
|
|
|
|
s = s.quoted();
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
{
|
|
|
|
StringArray files;
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
//==============================================================================
|
|
|
|
static void setPushNotificationsDelegate (id self, SEL, NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate)
|
|
|
|
{
|
|
|
|
object_setInstanceVariable (self, "pushNotificationsDelegate", delegate);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (NSString* f in filenames)
|
|
|
|
files.add (quotedIfContainsSpaces (f));
|
|
|
|
static NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self)
|
|
|
|
{
|
|
|
|
return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (self, "pushNotificationsDelegate");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (files.size() > 0)
|
|
|
|
app->anotherInstanceStarted (files.joinIntoString (" "));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
|
|
|
SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
|
|
|
|
|
|
|
|
static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n)
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
NSDictionary* dict = (NSDictionary*) [n userInfo];
|
|
|
|
auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]);
|
|
|
|
MessageManager::getInstance()->deliverBroadcastMessage (messageString);
|
|
|
|
}
|
|
|
|
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
|
|
|
[invocation setSelector: selector];
|
|
|
|
[invocation setTarget: delegate];
|
|
|
|
[invocation setArgument: &application atIndex:2];
|
|
|
|
[invocation setArgument: &deviceToken atIndex:3];
|
|
|
|
|
|
|
|
static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
if (menuTrackingChangedCallback != nullptr)
|
|
|
|
(*menuTrackingChangedCallback) (true);
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*)
|
|
|
|
{
|
|
|
|
if (menuTrackingChangedCallback != nullptr)
|
|
|
|
(*menuTrackingChangedCallback) (false);
|
|
|
|
}
|
|
|
|
static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread)
|
|
|
|
SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
|
|
|
|
|
|
|
|
static void focusChanged()
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
if (appFocusChangeCallback != nullptr)
|
|
|
|
(*appFocusChangeCallback)();
|
|
|
|
}
|
|
|
|
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 getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*)
|
|
|
|
{
|
|
|
|
if (auto* app = JUCEApplicationBase::getInstance())
|
|
|
|
app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue]));
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static String quotedIfContainsSpaces (NSString* file)
|
|
|
|
{
|
|
|
|
String s (nsStringToJuce (file));
|
|
|
|
s = s.unquoted().replace ("\"", "\\\"");
|
|
|
|
static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
|
|
|
|
if (s.containsChar (' '))
|
|
|
|
s = s.quoted();
|
|
|
|
SEL selector = @selector (application:didReceiveRemoteNotification:);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if JUCE_PUSH_NOTIFICATIONS
|
|
|
|
//==============================================================================
|
|
|
|
static void setPushNotificationsDelegate (id self, SEL, NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate)
|
|
|
|
if (delegate != nil && [delegate respondsToSelector: selector])
|
|
|
|
{
|
|
|
|
object_setInstanceVariable (self, "pushNotificationsDelegate", delegate);
|
|
|
|
}
|
|
|
|
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
|
|
|
[invocation setSelector: selector];
|
|
|
|
[invocation setTarget: delegate];
|
|
|
|
[invocation setArgument: &application atIndex:2];
|
|
|
|
[invocation setArgument: &userInfo atIndex:3];
|
|
|
|
|
|
|
|
static NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self)
|
|
|
|
{
|
|
|
|
return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (self, "pushNotificationsDelegate");
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken)
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
// This is declared at file scope, so that it's guaranteed to be
|
|
|
|
// constructed before and destructed after `appDelegate` (below)
|
|
|
|
static AppDelegateClass appDelegateClass;
|
|
|
|
|
|
|
|
SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
|
|
|
|
//==============================================================================
|
|
|
|
struct AppDelegate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
AppDelegate()
|
|
|
|
{
|
|
|
|
delegate = [appDelegateClass.createInstance() init];
|
|
|
|
|
|
|
|
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];
|
|
|
|
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
|
|
|
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
[center addObserver: delegate selector: @selector (mainMenuTrackingBegan:)
|
|
|
|
name: NSMenuDidBeginTrackingNotification object: nil];
|
|
|
|
[center addObserver: delegate selector: @selector (mainMenuTrackingEnded:)
|
|
|
|
name: NSMenuDidEndTrackingNotification object: nil];
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
|
|
|
|
static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error)
|
|
|
|
if (JUCEApplicationBase::isStandaloneApp())
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
[NSApp setDelegate: delegate];
|
|
|
|
|
|
|
|
SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
|
|
|
|
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
|
|
|
[[NSDistributedNotificationCenter defaultCenter] addObserver: delegate
|
|
|
|
selector: @selector (broadcastMessageCallback:)
|
|
|
|
name: getBroadcastEventName()
|
|
|
|
object: nil
|
|
|
|
suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
|
|
|
|
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[center addObserver: delegate selector: @selector (applicationDidResignActive:)
|
|
|
|
name: NSApplicationDidResignActiveNotification object: NSApp];
|
|
|
|
|
|
|
|
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];
|
|
|
|
[center addObserver: delegate selector: @selector (applicationDidBecomeActive:)
|
|
|
|
name: NSApplicationDidBecomeActiveNotification object: NSApp];
|
|
|
|
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
[center addObserver: delegate selector: @selector (applicationWillUnhide:)
|
|
|
|
name: NSApplicationWillUnhideNotification object: NSApp];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo)
|
|
|
|
~AppDelegate()
|
|
|
|
{
|
|
|
|
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate];
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver: delegate];
|
|
|
|
|
|
|
|
if (JUCEApplicationBase::isStandaloneApp())
|
|
|
|
{
|
|
|
|
auto* delegate = getPushNotificationsDelegate (self);
|
|
|
|
[NSApp setDelegate: nil];
|
|
|
|
|
|
|
|
SEL selector = @selector (application:didReceiveRemoteNotification:);
|
|
|
|
[[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate
|
|
|
|
name: getBroadcastEventName()
|
|
|
|
object: nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
[delegate release];
|
|
|
|
}
|
|
|
|
|
|
|
|
[invocation invoke];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
static NSString* getBroadcastEventName()
|
|
|
|
{
|
|
|
|
return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64()));
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageQueue messageQueue;
|
|
|
|
id delegate;
|
|
|
|
};
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|