The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

742 lines
28KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. extern bool isIOSAppActive;
  21. struct AppInactivityCallback // NB: careful, this declaration is duplicated in other modules
  22. {
  23. virtual ~AppInactivityCallback() = default;
  24. virtual void appBecomingInactive() = 0;
  25. };
  26. // This is an internal list of callbacks (but currently used between modules)
  27. Array<AppInactivityCallback*> appBecomingInactiveCallbacks;
  28. } // namespace juce
  29. #if JUCE_PUSH_NOTIFICATIONS
  30. @interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate, UNUserNotificationCenterDelegate>
  31. #else
  32. @interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate>
  33. #endif
  34. {
  35. UIBackgroundTaskIdentifier appSuspendTask;
  36. }
  37. @property (strong, nonatomic) UIWindow *window;
  38. - (id) init;
  39. - (void) dealloc;
  40. - (void) applicationDidFinishLaunching: (UIApplication*) application;
  41. - (void) applicationWillTerminate: (UIApplication*) application;
  42. - (void) applicationDidEnterBackground: (UIApplication*) application;
  43. - (void) applicationWillEnterForeground: (UIApplication*) application;
  44. - (void) applicationDidBecomeActive: (UIApplication*) application;
  45. - (void) applicationWillResignActive: (UIApplication*) application;
  46. - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*) identifier
  47. completionHandler: (void (^)(void)) completionHandler;
  48. - (void) applicationDidReceiveMemoryWarning: (UIApplication *) application;
  49. #if JUCE_PUSH_NOTIFICATIONS
  50. - (void) application: (UIApplication*) application
  51. didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken;
  52. - (void) application: (UIApplication*) application
  53. didFailToRegisterForRemoteNotificationsWithError: (NSError*) error;
  54. - (void) application: (UIApplication*) application
  55. didReceiveRemoteNotification: (NSDictionary*) userInfo;
  56. - (void) application: (UIApplication*) application
  57. didReceiveRemoteNotification: (NSDictionary*) userInfo
  58. fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler;
  59. - (void) application: (UIApplication*) application
  60. handleActionWithIdentifier: (NSString*) identifier
  61. forRemoteNotification: (NSDictionary*) userInfo
  62. withResponseInfo: (NSDictionary*) responseInfo
  63. completionHandler: (void(^)()) completionHandler;
  64. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  65. - (void) application: (UIApplication*) application
  66. didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings;
  67. - (void) application: (UIApplication*) application
  68. didReceiveLocalNotification: (UILocalNotification*) notification;
  69. - (void) application: (UIApplication*) application
  70. handleActionWithIdentifier: (NSString*) identifier
  71. forLocalNotification: (UILocalNotification*) notification
  72. completionHandler: (void(^)()) completionHandler;
  73. - (void) application: (UIApplication*) application
  74. handleActionWithIdentifier: (NSString*) identifier
  75. forLocalNotification: (UILocalNotification*) notification
  76. withResponseInfo: (NSDictionary*) responseInfo
  77. completionHandler: (void(^)()) completionHandler;
  78. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  79. - (void) userNotificationCenter: (UNUserNotificationCenter*) center
  80. willPresentNotification: (UNNotification*) notification
  81. withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler;
  82. - (void) userNotificationCenter: (UNUserNotificationCenter*) center
  83. didReceiveNotificationResponse: (UNNotificationResponse*) response
  84. withCompletionHandler: (void(^)())completionHandler;
  85. #endif
  86. @end
  87. @implementation JuceAppStartupDelegate
  88. NSObject* _pushNotificationsDelegate;
  89. - (id) init
  90. {
  91. self = [super init];
  92. appSuspendTask = UIBackgroundTaskInvalid;
  93. #if JUCE_PUSH_NOTIFICATIONS
  94. [UNUserNotificationCenter currentNotificationCenter].delegate = self;
  95. #endif
  96. return self;
  97. }
  98. - (void) dealloc
  99. {
  100. [super dealloc];
  101. }
  102. - (void) applicationDidFinishLaunching: (UIApplication*) application
  103. {
  104. ignoreUnused (application);
  105. initialiseJuce_GUI();
  106. if (auto* app = JUCEApplicationBase::createInstance())
  107. {
  108. if (! app->initialiseApp())
  109. exit (app->shutdownApp());
  110. }
  111. else
  112. {
  113. jassertfalse; // you must supply an application object for an iOS app!
  114. }
  115. }
  116. - (void) applicationWillTerminate: (UIApplication*) application
  117. {
  118. ignoreUnused (application);
  119. JUCEApplicationBase::appWillTerminateByForce();
  120. }
  121. - (void) applicationDidEnterBackground: (UIApplication*) application
  122. {
  123. if (auto* app = JUCEApplicationBase::getInstance())
  124. {
  125. #if JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK
  126. appSuspendTask = [application beginBackgroundTaskWithName:@"JUCE Suspend Task" expirationHandler:^{
  127. if (appSuspendTask != UIBackgroundTaskInvalid)
  128. {
  129. [application endBackgroundTask:appSuspendTask];
  130. appSuspendTask = UIBackgroundTaskInvalid;
  131. }
  132. }];
  133. MessageManager::callAsync ([app] { app->suspended(); });
  134. #else
  135. ignoreUnused (application);
  136. app->suspended();
  137. #endif
  138. }
  139. }
  140. - (void) applicationWillEnterForeground: (UIApplication*) application
  141. {
  142. ignoreUnused (application);
  143. if (auto* app = JUCEApplicationBase::getInstance())
  144. app->resumed();
  145. }
  146. - (void) applicationDidBecomeActive: (UIApplication*) application
  147. {
  148. application.applicationIconBadgeNumber = 0;
  149. isIOSAppActive = true;
  150. }
  151. - (void) applicationWillResignActive: (UIApplication*) application
  152. {
  153. ignoreUnused (application);
  154. isIOSAppActive = false;
  155. for (int i = appBecomingInactiveCallbacks.size(); --i >= 0;)
  156. appBecomingInactiveCallbacks.getReference (i)->appBecomingInactive();
  157. }
  158. - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*)identifier
  159. completionHandler: (void (^)(void))completionHandler
  160. {
  161. ignoreUnused (application);
  162. URL::DownloadTask::juce_iosURLSessionNotify (nsStringToJuce (identifier));
  163. completionHandler();
  164. }
  165. - (void) applicationDidReceiveMemoryWarning: (UIApplication*) application
  166. {
  167. ignoreUnused (application);
  168. if (auto* app = JUCEApplicationBase::getInstance())
  169. app->memoryWarningReceived();
  170. }
  171. - (void) setPushNotificationsDelegateToUse: (NSObject*) delegate
  172. {
  173. _pushNotificationsDelegate = delegate;
  174. }
  175. #if JUCE_PUSH_NOTIFICATIONS
  176. - (void) application: (UIApplication*) application
  177. didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken
  178. {
  179. ignoreUnused (application);
  180. SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
  181. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  182. {
  183. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  184. [invocation setSelector: selector];
  185. [invocation setTarget: _pushNotificationsDelegate];
  186. [invocation setArgument: &application atIndex:2];
  187. [invocation setArgument: &deviceToken atIndex:3];
  188. [invocation invoke];
  189. }
  190. }
  191. - (void) application: (UIApplication*) application
  192. didFailToRegisterForRemoteNotificationsWithError: (NSError*) error
  193. {
  194. ignoreUnused (application);
  195. SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
  196. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  197. {
  198. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  199. [invocation setSelector: selector];
  200. [invocation setTarget: _pushNotificationsDelegate];
  201. [invocation setArgument: &application atIndex:2];
  202. [invocation setArgument: &error atIndex:3];
  203. [invocation invoke];
  204. }
  205. }
  206. - (void) application: (UIApplication*) application
  207. didReceiveRemoteNotification: (NSDictionary*) userInfo
  208. {
  209. ignoreUnused (application);
  210. SEL selector = @selector (application:didReceiveRemoteNotification:);
  211. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  212. {
  213. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  214. [invocation setSelector: selector];
  215. [invocation setTarget: _pushNotificationsDelegate];
  216. [invocation setArgument: &application atIndex:2];
  217. [invocation setArgument: &userInfo atIndex:3];
  218. [invocation invoke];
  219. }
  220. }
  221. - (void) application: (UIApplication*) application
  222. didReceiveRemoteNotification: (NSDictionary*) userInfo
  223. fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler
  224. {
  225. ignoreUnused (application);
  226. SEL selector = @selector (application:didReceiveRemoteNotification:fetchCompletionHandler:);
  227. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  228. {
  229. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  230. [invocation setSelector: selector];
  231. [invocation setTarget: _pushNotificationsDelegate];
  232. [invocation setArgument: &application atIndex:2];
  233. [invocation setArgument: &userInfo atIndex:3];
  234. [invocation setArgument: &completionHandler atIndex:4];
  235. [invocation invoke];
  236. }
  237. }
  238. - (void) application: (UIApplication*) application
  239. handleActionWithIdentifier: (NSString*) identifier
  240. forRemoteNotification: (NSDictionary*) userInfo
  241. withResponseInfo: (NSDictionary*) responseInfo
  242. completionHandler: (void(^)()) completionHandler
  243. {
  244. ignoreUnused (application);
  245. SEL selector = @selector (application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:);
  246. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  247. {
  248. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  249. [invocation setSelector: selector];
  250. [invocation setTarget: _pushNotificationsDelegate];
  251. [invocation setArgument: &application atIndex:2];
  252. [invocation setArgument: &identifier atIndex:3];
  253. [invocation setArgument: &userInfo atIndex:4];
  254. [invocation setArgument: &responseInfo atIndex:5];
  255. [invocation setArgument: &completionHandler atIndex:6];
  256. [invocation invoke];
  257. }
  258. }
  259. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  260. - (void) application: (UIApplication*) application
  261. didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings
  262. {
  263. ignoreUnused (application);
  264. SEL selector = @selector (application:didRegisterUserNotificationSettings:);
  265. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector:selector])
  266. {
  267. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  268. [invocation setSelector: selector];
  269. [invocation setTarget: _pushNotificationsDelegate];
  270. [invocation setArgument: &application atIndex: 2];
  271. [invocation setArgument: &notificationSettings atIndex: 3];
  272. [invocation invoke];
  273. }
  274. }
  275. - (void) application: (UIApplication*) application
  276. didReceiveLocalNotification: (UILocalNotification*) notification
  277. {
  278. ignoreUnused (application);
  279. SEL selector = @selector (application:didReceiveLocalNotification:);
  280. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  281. {
  282. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  283. [invocation setSelector: selector];
  284. [invocation setTarget: _pushNotificationsDelegate];
  285. [invocation setArgument: &application atIndex: 2];
  286. [invocation setArgument: &notification atIndex: 3];
  287. [invocation invoke];
  288. }
  289. }
  290. - (void) application: (UIApplication*) application
  291. handleActionWithIdentifier: (NSString*) identifier
  292. forLocalNotification: (UILocalNotification*) notification
  293. completionHandler: (void(^)()) completionHandler
  294. {
  295. ignoreUnused (application);
  296. SEL selector = @selector (application:handleActionWithIdentifier:forLocalNotification:completionHandler:);
  297. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  298. {
  299. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  300. [invocation setSelector: selector];
  301. [invocation setTarget: _pushNotificationsDelegate];
  302. [invocation setArgument: &application atIndex:2];
  303. [invocation setArgument: &identifier atIndex:3];
  304. [invocation setArgument: &notification atIndex:4];
  305. [invocation setArgument: &completionHandler atIndex:5];
  306. [invocation invoke];
  307. }
  308. }
  309. - (void) application: (UIApplication*) application
  310. handleActionWithIdentifier: (NSString*) identifier
  311. forLocalNotification: (UILocalNotification*) notification
  312. withResponseInfo: (NSDictionary*) responseInfo
  313. completionHandler: (void(^)()) completionHandler
  314. {
  315. ignoreUnused (application);
  316. SEL selector = @selector (application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:);
  317. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  318. {
  319. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  320. [invocation setSelector: selector];
  321. [invocation setTarget: _pushNotificationsDelegate];
  322. [invocation setArgument: &application atIndex:2];
  323. [invocation setArgument: &identifier atIndex:3];
  324. [invocation setArgument: &notification atIndex:4];
  325. [invocation setArgument: &responseInfo atIndex:5];
  326. [invocation setArgument: &completionHandler atIndex:6];
  327. [invocation invoke];
  328. }
  329. }
  330. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  331. - (void) userNotificationCenter: (UNUserNotificationCenter*) center
  332. willPresentNotification: (UNNotification*) notification
  333. withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler
  334. {
  335. ignoreUnused (center);
  336. SEL selector = @selector (userNotificationCenter:willPresentNotification:withCompletionHandler:);
  337. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  338. {
  339. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  340. [invocation setSelector: selector];
  341. [invocation setTarget: _pushNotificationsDelegate];
  342. [invocation setArgument: &center atIndex:2];
  343. [invocation setArgument: &notification atIndex:3];
  344. [invocation setArgument: &completionHandler atIndex:4];
  345. [invocation invoke];
  346. }
  347. }
  348. - (void) userNotificationCenter: (UNUserNotificationCenter*) center
  349. didReceiveNotificationResponse: (UNNotificationResponse*) response
  350. withCompletionHandler: (void(^)()) completionHandler
  351. {
  352. ignoreUnused (center);
  353. SEL selector = @selector (userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:);
  354. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  355. {
  356. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  357. [invocation setSelector: selector];
  358. [invocation setTarget: _pushNotificationsDelegate];
  359. [invocation setArgument: &center atIndex:2];
  360. [invocation setArgument: &response atIndex:3];
  361. [invocation setArgument: &completionHandler atIndex:4];
  362. [invocation invoke];
  363. }
  364. }
  365. #endif
  366. @end
  367. namespace juce
  368. {
  369. int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr);
  370. int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr)
  371. {
  372. Class delegateClass = (customDelegatePtr != nullptr ? reinterpret_cast<Class> (customDelegatePtr) : [JuceAppStartupDelegate class]);
  373. return UIApplicationMain (argc, const_cast<char**> (argv), nil, NSStringFromClass (delegateClass));
  374. }
  375. //==============================================================================
  376. void LookAndFeel::playAlertSound()
  377. {
  378. // TODO
  379. }
  380. //==============================================================================
  381. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*, std::function<void()>)
  382. {
  383. jassertfalse; // no such thing on iOS!
  384. return false;
  385. }
  386. bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*, std::function<void()>)
  387. {
  388. jassertfalse; // no such thing on iOS!
  389. return false;
  390. }
  391. //==============================================================================
  392. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  393. {
  394. if (! SystemStats::isRunningInAppExtensionSandbox())
  395. [[UIApplication sharedApplication] setIdleTimerDisabled: ! isEnabled];
  396. }
  397. bool Desktop::isScreenSaverEnabled()
  398. {
  399. if (SystemStats::isRunningInAppExtensionSandbox())
  400. return true;
  401. return ! [[UIApplication sharedApplication] isIdleTimerDisabled];
  402. }
  403. //==============================================================================
  404. Image detail::WindowingHelpers::createIconForFile (const File&)
  405. {
  406. return {};
  407. }
  408. //==============================================================================
  409. void SystemClipboard::copyTextToClipboard (const String& text)
  410. {
  411. [[UIPasteboard generalPasteboard] setValue: juceStringToNS (text)
  412. forPasteboardType: @"public.text"];
  413. }
  414. String SystemClipboard::getTextFromClipboard()
  415. {
  416. return nsStringToJuce ([[UIPasteboard generalPasteboard] string]);
  417. }
  418. //==============================================================================
  419. bool detail::MouseInputSourceList::addSource()
  420. {
  421. addSource (sources.size(), MouseInputSource::InputSourceType::touch);
  422. return true;
  423. }
  424. bool detail::MouseInputSourceList::canUseTouch() const
  425. {
  426. return true;
  427. }
  428. bool Desktop::canUseSemiTransparentWindows() noexcept
  429. {
  430. return true;
  431. }
  432. bool Desktop::isDarkModeActive() const
  433. {
  434. if (@available (iOS 12.0, *))
  435. return [[[UIScreen mainScreen] traitCollection] userInterfaceStyle] == UIUserInterfaceStyleDark;
  436. return false;
  437. }
  438. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  439. static const auto darkModeSelector = @selector (darkModeChanged:);
  440. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  441. class Desktop::NativeDarkModeChangeDetectorImpl
  442. {
  443. public:
  444. NativeDarkModeChangeDetectorImpl()
  445. {
  446. static DelegateClass delegateClass;
  447. delegate.reset ([delegateClass.createInstance() init]);
  448. observer.emplace (delegate.get(), darkModeSelector, UIViewComponentPeer::getDarkModeNotificationName(), nil);
  449. }
  450. private:
  451. struct DelegateClass final : public ObjCClass<NSObject>
  452. {
  453. DelegateClass() : ObjCClass<NSObject> ("JUCEDelegate_")
  454. {
  455. addMethod (darkModeSelector, [] (id, SEL, NSNotification*) { Desktop::getInstance().darkModeChanged(); });
  456. registerClass();
  457. }
  458. };
  459. NSUniquePtr<NSObject> delegate;
  460. Optional<ScopedNotificationCenterObserver> observer;
  461. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  462. };
  463. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  464. {
  465. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  466. }
  467. //==============================================================================
  468. Point<float> MouseInputSource::getCurrentRawMousePosition()
  469. {
  470. return juce_lastMousePos;
  471. }
  472. void MouseInputSource::setRawMousePosition (Point<float>)
  473. {
  474. }
  475. double Desktop::getDefaultMasterScale()
  476. {
  477. return 1.0;
  478. }
  479. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  480. {
  481. UIInterfaceOrientation orientation = SystemStats::isRunningInAppExtensionSandbox() ? UIInterfaceOrientationPortrait
  482. : getWindowOrientation();
  483. return Orientations::convertToJuce (orientation);
  484. }
  485. // The most straightforward way of retrieving the screen area available to an iOS app
  486. // seems to be to create a new window (which will take up all available space) and to
  487. // query its frame.
  488. struct TemporaryWindow
  489. {
  490. UIWindow* window = [[UIWindow alloc] init];
  491. ~TemporaryWindow() noexcept { [window release]; }
  492. };
  493. static Rectangle<int> getRecommendedWindowBounds()
  494. {
  495. return convertToRectInt (TemporaryWindow().window.frame);
  496. }
  497. static BorderSize<int> getSafeAreaInsets (float masterScale)
  498. {
  499. if (@available (iOS 11.0, *))
  500. {
  501. UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets;
  502. return detail::WindowingHelpers::roundToInt (BorderSize<double> { safeInsets.top,
  503. safeInsets.left,
  504. safeInsets.bottom,
  505. safeInsets.right }.multipliedBy (1.0 / (double) masterScale));
  506. }
  507. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  508. auto statusBarSize = [UIApplication sharedApplication].statusBarFrame.size;
  509. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  510. auto statusBarHeight = jmin (statusBarSize.width, statusBarSize.height);
  511. return { roundToInt (statusBarHeight / masterScale), 0, 0, 0 };
  512. }
  513. //==============================================================================
  514. void Displays::findDisplays (float masterScale)
  515. {
  516. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  517. static const auto keyboardShownSelector = @selector (juceKeyboardShown:);
  518. static const auto keyboardHiddenSelector = @selector (juceKeyboardHidden:);
  519. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  520. class OnScreenKeyboardChangeDetectorImpl
  521. {
  522. public:
  523. OnScreenKeyboardChangeDetectorImpl()
  524. {
  525. static DelegateClass delegateClass;
  526. delegate.reset ([delegateClass.createInstance() init]);
  527. object_setInstanceVariable (delegate.get(), "owner", this);
  528. observers.emplace_back (delegate.get(), keyboardShownSelector, UIKeyboardDidShowNotification, nil);
  529. observers.emplace_back (delegate.get(), keyboardHiddenSelector, UIKeyboardDidHideNotification, nil);
  530. }
  531. auto getInsets() const { return insets; }
  532. private:
  533. struct DelegateClass final : public ObjCClass<NSObject>
  534. {
  535. DelegateClass() : ObjCClass<NSObject> ("JUCEOnScreenKeyboardObserver_")
  536. {
  537. addIvar<OnScreenKeyboardChangeDetectorImpl*> ("owner");
  538. addMethod (keyboardShownSelector, [] (id self, SEL, NSNotification* notification)
  539. {
  540. setKeyboardScreenBounds (self, [&]() -> BorderSize<double>
  541. {
  542. auto* info = [notification userInfo];
  543. if (info == nullptr)
  544. return {};
  545. auto* value = static_cast<NSValue*> ([info objectForKey: UIKeyboardFrameEndUserInfoKey]);
  546. if (value == nullptr)
  547. return {};
  548. auto* display = Desktop::getInstance().getDisplays().getPrimaryDisplay();
  549. if (display == nullptr)
  550. return {};
  551. const auto rect = convertToRectInt ([value CGRectValue]);
  552. BorderSize<double> result;
  553. if (rect.getY() == display->totalArea.getY())
  554. result.setTop (rect.getHeight());
  555. if (rect.getBottom() == display->totalArea.getBottom())
  556. result.setBottom (rect.getHeight());
  557. return result;
  558. }());
  559. });
  560. addMethod (keyboardHiddenSelector, [] (id self, SEL, NSNotification*)
  561. {
  562. setKeyboardScreenBounds (self, {});
  563. });
  564. registerClass();
  565. }
  566. private:
  567. static void setKeyboardScreenBounds (id self, BorderSize<double> insets)
  568. {
  569. if (std::exchange (getIvar<OnScreenKeyboardChangeDetectorImpl*> (self, "owner")->insets, insets) != insets)
  570. Desktop::getInstance().displays->refresh();
  571. }
  572. };
  573. BorderSize<double> insets;
  574. NSUniquePtr<NSObject> delegate;
  575. std::vector<ScopedNotificationCenterObserver> observers;
  576. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OnScreenKeyboardChangeDetectorImpl)
  577. };
  578. JUCE_AUTORELEASEPOOL
  579. {
  580. static OnScreenKeyboardChangeDetectorImpl keyboardChangeDetector;
  581. UIScreen* s = [UIScreen mainScreen];
  582. Display d;
  583. d.totalArea = convertToRectInt ([s bounds]) / masterScale;
  584. d.userArea = getRecommendedWindowBounds() / masterScale;
  585. d.safeAreaInsets = getSafeAreaInsets (masterScale);
  586. const auto scaledInsets = keyboardChangeDetector.getInsets().multipliedBy (1.0 / (double) masterScale);
  587. d.keyboardInsets = detail::WindowingHelpers::roundToInt (scaledInsets);
  588. d.isMain = true;
  589. d.scale = masterScale * s.scale;
  590. d.dpi = 160 * d.scale;
  591. displays.add (d);
  592. }
  593. }
  594. } // namespace juce