/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { extern bool isIOSAppActive; struct AppInactivityCallback // NB: careful, this declaration is duplicated in other modules { virtual ~AppInactivityCallback() = default; virtual void appBecomingInactive() = 0; }; // This is an internal list of callbacks (but currently used between modules) Array appBecomingInactiveCallbacks; } #if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 @interface JuceAppStartupDelegate : NSObject #else @interface JuceAppStartupDelegate : NSObject #endif { UIBackgroundTaskIdentifier appSuspendTask; } @property (strong, nonatomic) UIWindow *window; - (id) init; - (void) dealloc; - (void) applicationDidFinishLaunching: (UIApplication*) application; - (void) applicationWillTerminate: (UIApplication*) application; - (void) applicationDidEnterBackground: (UIApplication*) application; - (void) applicationWillEnterForeground: (UIApplication*) application; - (void) applicationDidBecomeActive: (UIApplication*) application; - (void) applicationWillResignActive: (UIApplication*) application; - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*) identifier completionHandler: (void (^)(void)) completionHandler; - (void) applicationDidReceiveMemoryWarning: (UIApplication *) application; #if JUCE_PUSH_NOTIFICATIONS - (void) application: (UIApplication*) application didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings; - (void) application: (UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken; - (void) application: (UIApplication*) application didFailToRegisterForRemoteNotificationsWithError: (NSError*) error; - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo; - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler; - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier forRemoteNotification: (NSDictionary*) userInfo withResponseInfo: (NSDictionary*) responseInfo completionHandler: (void(^)()) completionHandler; - (void) application: (UIApplication*) application didReceiveLocalNotification: (UILocalNotification*) notification; - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier forLocalNotification: (UILocalNotification*) notification completionHandler: (void(^)()) completionHandler; - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier forLocalNotification: (UILocalNotification*) notification withResponseInfo: (NSDictionary*) responseInfo completionHandler: (void(^)()) completionHandler; #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - (void) userNotificationCenter: (UNUserNotificationCenter*) center willPresentNotification: (UNNotification*) notification withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler; - (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response withCompletionHandler: (void(^)())completionHandler; #endif #endif @end @implementation JuceAppStartupDelegate NSObject* _pushNotificationsDelegate; - (id) init { self = [super init]; appSuspendTask = UIBackgroundTaskInvalid; #if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 [UNUserNotificationCenter currentNotificationCenter].delegate = self; #endif return self; } - (void) dealloc { [super dealloc]; } - (void) applicationDidFinishLaunching: (UIApplication*) application { ignoreUnused (application); initialiseJuce_GUI(); if (auto* app = JUCEApplicationBase::createInstance()) { if (! app->initialiseApp()) exit (app->shutdownApp()); } else { jassertfalse; // you must supply an application object for an iOS app! } } - (void) applicationWillTerminate: (UIApplication*) application { ignoreUnused (application); JUCEApplicationBase::appWillTerminateByForce(); } - (void) applicationDidEnterBackground: (UIApplication*) application { if (auto* app = JUCEApplicationBase::getInstance()) { #if JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK appSuspendTask = [application beginBackgroundTaskWithName:@"JUCE Suspend Task" expirationHandler:^{ if (appSuspendTask != UIBackgroundTaskInvalid) { [application endBackgroundTask:appSuspendTask]; appSuspendTask = UIBackgroundTaskInvalid; } }]; MessageManager::callAsync ([app] { app->suspended(); }); #else ignoreUnused (application); app->suspended(); #endif } } - (void) applicationWillEnterForeground: (UIApplication*) application { ignoreUnused (application); if (auto* app = JUCEApplicationBase::getInstance()) app->resumed(); } - (void) applicationDidBecomeActive: (UIApplication*) application { application.applicationIconBadgeNumber = 0; isIOSAppActive = true; } - (void) applicationWillResignActive: (UIApplication*) application { ignoreUnused (application); isIOSAppActive = false; for (int i = appBecomingInactiveCallbacks.size(); --i >= 0;) appBecomingInactiveCallbacks.getReference(i)->appBecomingInactive(); } - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*)identifier completionHandler: (void (^)(void))completionHandler { ignoreUnused (application); URL::DownloadTask::juce_iosURLSessionNotify (nsStringToJuce (identifier)); completionHandler(); } - (void) applicationDidReceiveMemoryWarning: (UIApplication*) application { ignoreUnused (application); if (auto* app = JUCEApplicationBase::getInstance()) app->memoryWarningReceived(); } - (void) setPushNotificationsDelegateToUse: (NSObject*) delegate { _pushNotificationsDelegate = delegate; } #if JUCE_PUSH_NOTIFICATIONS - (void) application: (UIApplication*) application didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings { ignoreUnused (application); SEL selector = @selector (application:didRegisterUserNotificationSettings:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: ¬ificationSettings atIndex:3]; [invocation invoke]; } } - (void) application: (UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken { ignoreUnused (application); SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: &deviceToken atIndex:3]; [invocation invoke]; } } - (void) application: (UIApplication*) application didFailToRegisterForRemoteNotificationsWithError: (NSError*) error { ignoreUnused (application); SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: &error atIndex:3]; [invocation invoke]; } } - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo { ignoreUnused (application); SEL selector = @selector (application:didReceiveRemoteNotification:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: &userInfo atIndex:3]; [invocation invoke]; } } - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler { ignoreUnused (application); SEL selector = @selector (application:didReceiveRemoteNotification:fetchCompletionHandler:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: &userInfo atIndex:3]; [invocation setArgument: &completionHandler atIndex:4]; [invocation invoke]; } } - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier forRemoteNotification: (NSDictionary*) userInfo withResponseInfo: (NSDictionary*) responseInfo completionHandler: (void(^)()) completionHandler { ignoreUnused (application); SEL selector = @selector (application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: &identifier atIndex:3]; [invocation setArgument: &userInfo atIndex:4]; [invocation setArgument: &responseInfo atIndex:5]; [invocation setArgument: &completionHandler atIndex:6]; [invocation invoke]; } } - (void) application: (UIApplication*) application didReceiveLocalNotification: (UILocalNotification*) notification { ignoreUnused (application); SEL selector = @selector (application:didReceiveLocalNotification:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: ¬ification atIndex:3]; [invocation invoke]; } } - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier forLocalNotification: (UILocalNotification*) notification completionHandler: (void(^)()) completionHandler { ignoreUnused (application); SEL selector = @selector (application:handleActionWithIdentifier:forLocalNotification:completionHandler:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: &identifier atIndex:3]; [invocation setArgument: ¬ification atIndex:4]; [invocation setArgument: &completionHandler atIndex:5]; [invocation invoke]; } } - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier forLocalNotification: (UILocalNotification*) notification withResponseInfo: (NSDictionary*) responseInfo completionHandler: (void(^)()) completionHandler { ignoreUnused (application); SEL selector = @selector (application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: &application atIndex:2]; [invocation setArgument: &identifier atIndex:3]; [invocation setArgument: ¬ification atIndex:4]; [invocation setArgument: &responseInfo atIndex:5]; [invocation setArgument: &completionHandler atIndex:6]; [invocation invoke]; } } #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - (void) userNotificationCenter: (UNUserNotificationCenter*) center willPresentNotification: (UNNotification*) notification withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler { ignoreUnused (center); SEL selector = @selector (userNotificationCenter:willPresentNotification:withCompletionHandler:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: ¢er atIndex:2]; [invocation setArgument: ¬ification atIndex:3]; [invocation setArgument: &completionHandler atIndex:4]; [invocation invoke]; } } - (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response withCompletionHandler: (void(^)()) completionHandler { ignoreUnused (center); SEL selector = @selector (userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:); if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector]) { NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]]; [invocation setSelector: selector]; [invocation setTarget: _pushNotificationsDelegate]; [invocation setArgument: ¢er atIndex:2]; [invocation setArgument: &response atIndex:3]; [invocation setArgument: &completionHandler atIndex:4]; [invocation invoke]; } } #endif #endif @end namespace juce { int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr); int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr) { Class delegateClass = (customDelegatePtr != nullptr ? reinterpret_cast (customDelegatePtr) : [JuceAppStartupDelegate class]); return UIApplicationMain (argc, const_cast (argv), nil, NSStringFromClass (delegateClass)); } //============================================================================== void LookAndFeel::playAlertSound() { // TODO } //============================================================================== class iOSMessageBox { public: iOSMessageBox (const MessageBoxOptions& opts, std::unique_ptr&& cb, bool deleteOnCompletion) : callback (std::move (cb)), shouldDeleteThis (deleteOnCompletion) { if (currentlyFocusedPeer != nullptr) { UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (opts.getTitle()) message: juceStringToNS (opts.getMessage()) preferredStyle: UIAlertControllerStyleAlert]; addButton (alert, opts.getButtonText (0)); addButton (alert, opts.getButtonText (1)); addButton (alert, opts.getButtonText (2)); [currentlyFocusedPeer->controller presentViewController: alert animated: YES completion: nil]; } else { // Since iOS8, alert windows need to be associated with a window, so you need to // have at least one window on screen when you use this jassertfalse; } } int getResult() { jassert (callback == nullptr); JUCE_AUTORELEASEPOOL { while (result < 0) [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; } return result; } void buttonClicked (int buttonIndex) noexcept { result = buttonIndex; if (callback != nullptr) callback->modalStateFinished (result); if (shouldDeleteThis) delete this; } private: void addButton (UIAlertController* alert, const String& text) { if (! text.isEmpty()) { const auto index = [[alert actions] count]; [alert addAction: [UIAlertAction actionWithTitle: juceStringToNS (text) style: UIAlertActionStyleDefault handler: ^(UIAlertAction*) { this->buttonClicked ((int) index); }]]; } } int result = -1; std::unique_ptr callback; const bool shouldDeleteThis; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox) }; //============================================================================== static int showDialog (const MessageBoxOptions& options, ModalComponentManager::Callback* callbackIn, AlertWindowMappings::MapFn mapFn) { #if JUCE_MODAL_LOOPS_PERMITTED if (callbackIn == nullptr) { JUCE_AUTORELEASEPOOL { jassert (mapFn != nullptr); iOSMessageBox messageBox (options, nullptr, false); return mapFn (messageBox.getResult()); } } #endif const auto showBox = [options, callbackIn, mapFn] { new iOSMessageBox (options, AlertWindowMappings::getWrappedCallback (callbackIn, mapFn), true); }; if (MessageManager::getInstance()->isThisTheMessageThread()) showBox(); else MessageManager::callAsync (showBox); return 0; } #if JUCE_MODAL_LOOPS_PERMITTED void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/) { showDialog (MessageBoxOptions() .withTitle (title) .withMessage (message) .withButton (TRANS("OK")), nullptr, AlertWindowMappings::messageBox); } int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options) { return showDialog (options, nullptr, AlertWindowMappings::noMapping); } #endif void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { showDialog (MessageBoxOptions() .withTitle (title) .withMessage (message) .withButton (TRANS("OK")), callback, AlertWindowMappings::messageBox); } bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { return showDialog (MessageBoxOptions() .withTitle (title) .withMessage (message) .withButton (TRANS("OK")) .withButton (TRANS("Cancel")), callback, AlertWindowMappings::okCancel) != 0; } int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { return showDialog (MessageBoxOptions() .withTitle (title) .withMessage (message) .withButton (TRANS("Yes")) .withButton (TRANS("No")) .withButton (TRANS("Cancel")), callback, AlertWindowMappings::yesNoCancel); } int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { return showDialog (MessageBoxOptions() .withTitle (title) .withMessage (message) .withButton (TRANS("Yes")) .withButton (TRANS("No")), callback, AlertWindowMappings::okCancel); } void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, ModalComponentManager::Callback* callback) { showDialog (options, callback, AlertWindowMappings::noMapping); } void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, std::function callback) { showAsync (options, ModalCallbackFunction::create (callback)); } //============================================================================== bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*, std::function) { jassertfalse; // no such thing on iOS! return false; } bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*, std::function) { jassertfalse; // no such thing on iOS! return false; } //============================================================================== void Desktop::setScreenSaverEnabled (const bool isEnabled) { if (! SystemStats::isRunningInAppExtensionSandbox()) [[UIApplication sharedApplication] setIdleTimerDisabled: ! isEnabled]; } bool Desktop::isScreenSaverEnabled() { if (SystemStats::isRunningInAppExtensionSandbox()) return true; return ! [[UIApplication sharedApplication] isIdleTimerDisabled]; } //============================================================================== bool juce_areThereAnyAlwaysOnTopWindows() { return false; } //============================================================================== Image juce_createIconForFile (const File&) { return Image(); } //============================================================================== void SystemClipboard::copyTextToClipboard (const String& text) { [[UIPasteboard generalPasteboard] setValue: juceStringToNS (text) forPasteboardType: @"public.text"]; } String SystemClipboard::getTextFromClipboard() { return nsStringToJuce ([[UIPasteboard generalPasteboard] string]); } //============================================================================== bool MouseInputSource::SourceList::addSource() { addSource (sources.size(), MouseInputSource::InputSourceType::touch); return true; } bool MouseInputSource::SourceList::canUseTouch() { return true; } bool Desktop::canUseSemiTransparentWindows() noexcept { return true; } bool Desktop::isDarkModeActive() const { #if defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0 if (@available (iOS 12.0, *)) return [[[UIScreen mainScreen] traitCollection] userInterfaceStyle] == UIUserInterfaceStyleDark; #endif return false; } class Desktop::NativeDarkModeChangeDetectorImpl { public: NativeDarkModeChangeDetectorImpl() { static DelegateClass delegateClass; delegate = [delegateClass.createInstance() init]; object_setInstanceVariable (delegate, "owner", this); JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") [[NSNotificationCenter defaultCenter] addObserver: delegate selector: @selector (darkModeChanged:) name: UIViewComponentPeer::getDarkModeNotificationName() object: nil]; JUCE_END_IGNORE_WARNINGS_GCC_LIKE } ~NativeDarkModeChangeDetectorImpl() { object_setInstanceVariable (delegate, "owner", nullptr); [[NSNotificationCenter defaultCenter] removeObserver: delegate]; [delegate release]; } void darkModeChanged() { Desktop::getInstance().darkModeChanged(); } private: struct DelegateClass : public ObjCClass { DelegateClass() : ObjCClass ("JUCEDelegate_") { addIvar ("owner"); JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") addMethod (@selector (darkModeChanged:), darkModeChanged); JUCE_END_IGNORE_WARNINGS_GCC_LIKE registerClass(); } static void darkModeChanged (id self, SEL, NSNotification*) { if (auto* owner = getIvar (self, "owner")) owner->darkModeChanged(); } }; id delegate = nil; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) }; std::unique_ptr Desktop::createNativeDarkModeChangeDetectorImpl() { return std::make_unique(); } Point MouseInputSource::getCurrentRawMousePosition() { return juce_lastMousePos; } void MouseInputSource::setRawMousePosition (Point) { } double Desktop::getDefaultMasterScale() { return 1.0; } Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { UIInterfaceOrientation orientation = SystemStats::isRunningInAppExtensionSandbox() ? UIInterfaceOrientationPortrait : getWindowOrientation(); return Orientations::convertToJuce (orientation); } // The most straightforward way of retrieving the screen area available to an iOS app // seems to be to create a new window (which will take up all available space) and to // query its frame. struct TemporaryWindow { UIWindow* window = [[UIWindow alloc] init]; ~TemporaryWindow() noexcept { [window release]; } }; static Rectangle getRecommendedWindowBounds() { return convertToRectInt (TemporaryWindow().window.frame); } static BorderSize getSafeAreaInsets (float masterScale) { #if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0 if (@available (iOS 11.0, *)) { UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets; auto getInset = [&] (CGFloat original) { return roundToInt (original / masterScale); }; return { getInset (safeInsets.top), getInset (safeInsets.left), getInset (safeInsets.bottom), getInset (safeInsets.right) }; } #endif JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") auto statusBarSize = [UIApplication sharedApplication].statusBarFrame.size; JUCE_END_IGNORE_WARNINGS_GCC_LIKE auto statusBarHeight = jmin (statusBarSize.width, statusBarSize.height); return { roundToInt (statusBarHeight / masterScale), 0, 0, 0 }; } void Displays::findDisplays (float masterScale) { JUCE_AUTORELEASEPOOL { UIScreen* s = [UIScreen mainScreen]; Display d; d.totalArea = convertToRectInt ([s bounds]) / masterScale; d.userArea = getRecommendedWindowBounds() / masterScale; d.safeAreaInsets = getSafeAreaInsets (masterScale); d.isMain = true; d.scale = masterScale * s.scale; d.dpi = 160 * d.scale; displays.add (d); } } } // namespace juce