Audio plugin host https://kx.studio/carla
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.

juce_ios_Windowing.mm 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  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. }
  29. #if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  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 didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings;
  51. - (void) application: (UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken;
  52. - (void) application: (UIApplication*) application didFailToRegisterForRemoteNotificationsWithError: (NSError*) error;
  53. - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo;
  54. - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo
  55. fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler;
  56. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  57. forRemoteNotification: (NSDictionary*) userInfo withResponseInfo: (NSDictionary*) responseInfo
  58. completionHandler: (void(^)()) completionHandler;
  59. - (void) application: (UIApplication*) application didReceiveLocalNotification: (UILocalNotification*) notification;
  60. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  61. forLocalNotification: (UILocalNotification*) notification completionHandler: (void(^)()) completionHandler;
  62. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  63. forLocalNotification: (UILocalNotification*) notification withResponseInfo: (NSDictionary*) responseInfo
  64. completionHandler: (void(^)()) completionHandler;
  65. #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  66. - (void) userNotificationCenter: (UNUserNotificationCenter*) center willPresentNotification: (UNNotification*) notification
  67. withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler;
  68. - (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response
  69. withCompletionHandler: (void(^)())completionHandler;
  70. #endif
  71. #endif
  72. @end
  73. @implementation JuceAppStartupDelegate
  74. NSObject* _pushNotificationsDelegate;
  75. - (id) init
  76. {
  77. self = [super init];
  78. appSuspendTask = UIBackgroundTaskInvalid;
  79. #if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  80. [UNUserNotificationCenter currentNotificationCenter].delegate = self;
  81. #endif
  82. return self;
  83. }
  84. - (void) dealloc
  85. {
  86. [super dealloc];
  87. }
  88. - (void) applicationDidFinishLaunching: (UIApplication*) application
  89. {
  90. ignoreUnused (application);
  91. initialiseJuce_GUI();
  92. if (auto* app = JUCEApplicationBase::createInstance())
  93. {
  94. if (! app->initialiseApp())
  95. exit (app->shutdownApp());
  96. }
  97. else
  98. {
  99. jassertfalse; // you must supply an application object for an iOS app!
  100. }
  101. }
  102. - (void) applicationWillTerminate: (UIApplication*) application
  103. {
  104. ignoreUnused (application);
  105. JUCEApplicationBase::appWillTerminateByForce();
  106. }
  107. - (void) applicationDidEnterBackground: (UIApplication*) application
  108. {
  109. if (auto* app = JUCEApplicationBase::getInstance())
  110. {
  111. #if JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK
  112. appSuspendTask = [application beginBackgroundTaskWithName:@"JUCE Suspend Task" expirationHandler:^{
  113. if (appSuspendTask != UIBackgroundTaskInvalid)
  114. {
  115. [application endBackgroundTask:appSuspendTask];
  116. appSuspendTask = UIBackgroundTaskInvalid;
  117. }
  118. }];
  119. MessageManager::callAsync ([app] { app->suspended(); });
  120. #else
  121. ignoreUnused (application);
  122. app->suspended();
  123. #endif
  124. }
  125. }
  126. - (void) applicationWillEnterForeground: (UIApplication*) application
  127. {
  128. ignoreUnused (application);
  129. if (auto* app = JUCEApplicationBase::getInstance())
  130. app->resumed();
  131. }
  132. - (void) applicationDidBecomeActive: (UIApplication*) application
  133. {
  134. application.applicationIconBadgeNumber = 0;
  135. isIOSAppActive = true;
  136. }
  137. - (void) applicationWillResignActive: (UIApplication*) application
  138. {
  139. ignoreUnused (application);
  140. isIOSAppActive = false;
  141. for (int i = appBecomingInactiveCallbacks.size(); --i >= 0;)
  142. appBecomingInactiveCallbacks.getReference(i)->appBecomingInactive();
  143. }
  144. - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*)identifier
  145. completionHandler: (void (^)(void))completionHandler
  146. {
  147. ignoreUnused (application);
  148. URL::DownloadTask::juce_iosURLSessionNotify (nsStringToJuce (identifier));
  149. completionHandler();
  150. }
  151. - (void) applicationDidReceiveMemoryWarning: (UIApplication*) application
  152. {
  153. ignoreUnused (application);
  154. if (auto* app = JUCEApplicationBase::getInstance())
  155. app->memoryWarningReceived();
  156. }
  157. - (void) setPushNotificationsDelegateToUse: (NSObject*) delegate
  158. {
  159. _pushNotificationsDelegate = delegate;
  160. }
  161. #if JUCE_PUSH_NOTIFICATIONS
  162. - (void) application: (UIApplication*) application didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings
  163. {
  164. ignoreUnused (application);
  165. SEL selector = @selector (application:didRegisterUserNotificationSettings:);
  166. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  167. {
  168. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  169. [invocation setSelector: selector];
  170. [invocation setTarget: _pushNotificationsDelegate];
  171. [invocation setArgument: &application atIndex:2];
  172. [invocation setArgument: &notificationSettings atIndex:3];
  173. [invocation invoke];
  174. }
  175. }
  176. - (void) application: (UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken
  177. {
  178. ignoreUnused (application);
  179. SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
  180. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  181. {
  182. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  183. [invocation setSelector: selector];
  184. [invocation setTarget: _pushNotificationsDelegate];
  185. [invocation setArgument: &application atIndex:2];
  186. [invocation setArgument: &deviceToken atIndex:3];
  187. [invocation invoke];
  188. }
  189. }
  190. - (void) application: (UIApplication*) application didFailToRegisterForRemoteNotificationsWithError: (NSError*) error
  191. {
  192. ignoreUnused (application);
  193. SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
  194. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  195. {
  196. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  197. [invocation setSelector: selector];
  198. [invocation setTarget: _pushNotificationsDelegate];
  199. [invocation setArgument: &application atIndex:2];
  200. [invocation setArgument: &error atIndex:3];
  201. [invocation invoke];
  202. }
  203. }
  204. - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo
  205. {
  206. ignoreUnused (application);
  207. SEL selector = @selector (application:didReceiveRemoteNotification:);
  208. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  209. {
  210. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  211. [invocation setSelector: selector];
  212. [invocation setTarget: _pushNotificationsDelegate];
  213. [invocation setArgument: &application atIndex:2];
  214. [invocation setArgument: &userInfo atIndex:3];
  215. [invocation invoke];
  216. }
  217. }
  218. - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo
  219. fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler
  220. {
  221. ignoreUnused (application);
  222. SEL selector = @selector (application:didReceiveRemoteNotification:fetchCompletionHandler:);
  223. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  224. {
  225. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  226. [invocation setSelector: selector];
  227. [invocation setTarget: _pushNotificationsDelegate];
  228. [invocation setArgument: &application atIndex:2];
  229. [invocation setArgument: &userInfo atIndex:3];
  230. [invocation setArgument: &completionHandler atIndex:4];
  231. [invocation invoke];
  232. }
  233. }
  234. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  235. forRemoteNotification: (NSDictionary*) userInfo withResponseInfo: (NSDictionary*) responseInfo
  236. completionHandler: (void(^)()) completionHandler
  237. {
  238. ignoreUnused (application);
  239. SEL selector = @selector (application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:);
  240. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  241. {
  242. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  243. [invocation setSelector: selector];
  244. [invocation setTarget: _pushNotificationsDelegate];
  245. [invocation setArgument: &application atIndex:2];
  246. [invocation setArgument: &identifier atIndex:3];
  247. [invocation setArgument: &userInfo atIndex:4];
  248. [invocation setArgument: &responseInfo atIndex:5];
  249. [invocation setArgument: &completionHandler atIndex:6];
  250. [invocation invoke];
  251. }
  252. }
  253. - (void) application: (UIApplication*) application didReceiveLocalNotification: (UILocalNotification*) notification
  254. {
  255. ignoreUnused (application);
  256. SEL selector = @selector (application:didReceiveLocalNotification:);
  257. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  258. {
  259. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  260. [invocation setSelector: selector];
  261. [invocation setTarget: _pushNotificationsDelegate];
  262. [invocation setArgument: &application atIndex:2];
  263. [invocation setArgument: &notification atIndex:3];
  264. [invocation invoke];
  265. }
  266. }
  267. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  268. forLocalNotification: (UILocalNotification*) notification completionHandler: (void(^)()) completionHandler
  269. {
  270. ignoreUnused (application);
  271. SEL selector = @selector (application:handleActionWithIdentifier:forLocalNotification:completionHandler:);
  272. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  273. {
  274. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  275. [invocation setSelector: selector];
  276. [invocation setTarget: _pushNotificationsDelegate];
  277. [invocation setArgument: &application atIndex:2];
  278. [invocation setArgument: &identifier atIndex:3];
  279. [invocation setArgument: &notification atIndex:4];
  280. [invocation setArgument: &completionHandler atIndex:5];
  281. [invocation invoke];
  282. }
  283. }
  284. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  285. forLocalNotification: (UILocalNotification*) notification withResponseInfo: (NSDictionary*) responseInfo
  286. completionHandler: (void(^)()) completionHandler
  287. {
  288. ignoreUnused (application);
  289. SEL selector = @selector (application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:);
  290. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  291. {
  292. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  293. [invocation setSelector: selector];
  294. [invocation setTarget: _pushNotificationsDelegate];
  295. [invocation setArgument: &application atIndex:2];
  296. [invocation setArgument: &identifier atIndex:3];
  297. [invocation setArgument: &notification atIndex:4];
  298. [invocation setArgument: &responseInfo atIndex:5];
  299. [invocation setArgument: &completionHandler atIndex:6];
  300. [invocation invoke];
  301. }
  302. }
  303. #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  304. - (void) userNotificationCenter: (UNUserNotificationCenter*) center willPresentNotification: (UNNotification*) notification
  305. withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler
  306. {
  307. ignoreUnused (center);
  308. SEL selector = @selector (userNotificationCenter:willPresentNotification:withCompletionHandler:);
  309. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  310. {
  311. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  312. [invocation setSelector: selector];
  313. [invocation setTarget: _pushNotificationsDelegate];
  314. [invocation setArgument: &center atIndex:2];
  315. [invocation setArgument: &notification atIndex:3];
  316. [invocation setArgument: &completionHandler atIndex:4];
  317. [invocation invoke];
  318. }
  319. }
  320. - (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response
  321. withCompletionHandler: (void(^)()) completionHandler
  322. {
  323. ignoreUnused (center);
  324. SEL selector = @selector (userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:);
  325. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  326. {
  327. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  328. [invocation setSelector: selector];
  329. [invocation setTarget: _pushNotificationsDelegate];
  330. [invocation setArgument: &center atIndex:2];
  331. [invocation setArgument: &response atIndex:3];
  332. [invocation setArgument: &completionHandler atIndex:4];
  333. [invocation invoke];
  334. }
  335. }
  336. #endif
  337. #endif
  338. @end
  339. namespace juce
  340. {
  341. int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr);
  342. int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr)
  343. {
  344. Class delegateClass = (customDelegatePtr != nullptr ? reinterpret_cast<Class> (customDelegatePtr) : [JuceAppStartupDelegate class]);
  345. return UIApplicationMain (argc, const_cast<char**> (argv), nil, NSStringFromClass (delegateClass));
  346. }
  347. //==============================================================================
  348. void LookAndFeel::playAlertSound()
  349. {
  350. // TODO
  351. }
  352. //==============================================================================
  353. class iOSMessageBox
  354. {
  355. public:
  356. iOSMessageBox (const MessageBoxOptions& opts,
  357. std::unique_ptr<ModalComponentManager::Callback>&& cb,
  358. bool deleteOnCompletion)
  359. : callback (std::move (cb)),
  360. shouldDeleteThis (deleteOnCompletion)
  361. {
  362. if (currentlyFocusedPeer != nullptr)
  363. {
  364. UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (opts.getTitle())
  365. message: juceStringToNS (opts.getMessage())
  366. preferredStyle: UIAlertControllerStyleAlert];
  367. addButton (alert, opts.getButtonText (0));
  368. addButton (alert, opts.getButtonText (1));
  369. addButton (alert, opts.getButtonText (2));
  370. [currentlyFocusedPeer->controller presentViewController: alert
  371. animated: YES
  372. completion: nil];
  373. }
  374. else
  375. {
  376. // Since iOS8, alert windows need to be associated with a window, so you need to
  377. // have at least one window on screen when you use this
  378. jassertfalse;
  379. }
  380. }
  381. int getResult()
  382. {
  383. jassert (callback == nullptr);
  384. JUCE_AUTORELEASEPOOL
  385. {
  386. while (result < 0)
  387. [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
  388. }
  389. return result;
  390. }
  391. void buttonClicked (int buttonIndex) noexcept
  392. {
  393. result = buttonIndex;
  394. if (callback != nullptr)
  395. callback->modalStateFinished (result);
  396. if (shouldDeleteThis)
  397. delete this;
  398. }
  399. private:
  400. void addButton (UIAlertController* alert, const String& text)
  401. {
  402. if (! text.isEmpty())
  403. {
  404. const auto index = [[alert actions] count];
  405. [alert addAction: [UIAlertAction actionWithTitle: juceStringToNS (text)
  406. style: UIAlertActionStyleDefault
  407. handler: ^(UIAlertAction*) { this->buttonClicked ((int) index); }]];
  408. }
  409. }
  410. int result = -1;
  411. std::unique_ptr<ModalComponentManager::Callback> callback;
  412. const bool shouldDeleteThis;
  413. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox)
  414. };
  415. //==============================================================================
  416. static int showDialog (const MessageBoxOptions& options,
  417. ModalComponentManager::Callback* callbackIn,
  418. AlertWindowMappings::MapFn mapFn)
  419. {
  420. #if JUCE_MODAL_LOOPS_PERMITTED
  421. if (callbackIn == nullptr)
  422. {
  423. JUCE_AUTORELEASEPOOL
  424. {
  425. jassert (mapFn != nullptr);
  426. iOSMessageBox messageBox (options, nullptr, false);
  427. return mapFn (messageBox.getResult());
  428. }
  429. }
  430. #endif
  431. const auto showBox = [options, callbackIn, mapFn]
  432. {
  433. new iOSMessageBox (options,
  434. AlertWindowMappings::getWrappedCallback (callbackIn, mapFn),
  435. true);
  436. };
  437. if (MessageManager::getInstance()->isThisTheMessageThread())
  438. showBox();
  439. else
  440. MessageManager::callAsync (showBox);
  441. return 0;
  442. }
  443. #if JUCE_MODAL_LOOPS_PERMITTED
  444. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType /*iconType*/,
  445. const String& title, const String& message,
  446. Component* /*associatedComponent*/)
  447. {
  448. showDialog (MessageBoxOptions()
  449. .withTitle (title)
  450. .withMessage (message)
  451. .withButton (TRANS("OK")),
  452. nullptr, AlertWindowMappings::messageBox);
  453. }
  454. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  455. {
  456. return showDialog (options, nullptr, AlertWindowMappings::noMapping);
  457. }
  458. #endif
  459. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType /*iconType*/,
  460. const String& title, const String& message,
  461. Component* /*associatedComponent*/,
  462. ModalComponentManager::Callback* callback)
  463. {
  464. showDialog (MessageBoxOptions()
  465. .withTitle (title)
  466. .withMessage (message)
  467. .withButton (TRANS("OK")),
  468. callback, AlertWindowMappings::messageBox);
  469. }
  470. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType /*iconType*/,
  471. const String& title, const String& message,
  472. Component* /*associatedComponent*/,
  473. ModalComponentManager::Callback* callback)
  474. {
  475. return showDialog (MessageBoxOptions()
  476. .withTitle (title)
  477. .withMessage (message)
  478. .withButton (TRANS("OK"))
  479. .withButton (TRANS("Cancel")),
  480. callback, AlertWindowMappings::okCancel) != 0;
  481. }
  482. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType /*iconType*/,
  483. const String& title, const String& message,
  484. Component* /*associatedComponent*/,
  485. ModalComponentManager::Callback* callback)
  486. {
  487. return showDialog (MessageBoxOptions()
  488. .withTitle (title)
  489. .withMessage (message)
  490. .withButton (TRANS("Yes"))
  491. .withButton (TRANS("No"))
  492. .withButton (TRANS("Cancel")),
  493. callback, AlertWindowMappings::yesNoCancel);
  494. }
  495. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType /*iconType*/,
  496. const String& title, const String& message,
  497. Component* /*associatedComponent*/,
  498. ModalComponentManager::Callback* callback)
  499. {
  500. return showDialog (MessageBoxOptions()
  501. .withTitle (title)
  502. .withMessage (message)
  503. .withButton (TRANS("Yes"))
  504. .withButton (TRANS("No")),
  505. callback, AlertWindowMappings::okCancel);
  506. }
  507. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  508. ModalComponentManager::Callback* callback)
  509. {
  510. showDialog (options, callback, AlertWindowMappings::noMapping);
  511. }
  512. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  513. std::function<void (int)> callback)
  514. {
  515. showAsync (options, ModalCallbackFunction::create (callback));
  516. }
  517. //==============================================================================
  518. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*, std::function<void()>)
  519. {
  520. jassertfalse; // no such thing on iOS!
  521. return false;
  522. }
  523. bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*, std::function<void()>)
  524. {
  525. jassertfalse; // no such thing on iOS!
  526. return false;
  527. }
  528. //==============================================================================
  529. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  530. {
  531. if (! SystemStats::isRunningInAppExtensionSandbox())
  532. [[UIApplication sharedApplication] setIdleTimerDisabled: ! isEnabled];
  533. }
  534. bool Desktop::isScreenSaverEnabled()
  535. {
  536. if (SystemStats::isRunningInAppExtensionSandbox())
  537. return true;
  538. return ! [[UIApplication sharedApplication] isIdleTimerDisabled];
  539. }
  540. //==============================================================================
  541. bool juce_areThereAnyAlwaysOnTopWindows()
  542. {
  543. return false;
  544. }
  545. //==============================================================================
  546. Image juce_createIconForFile (const File&)
  547. {
  548. return Image();
  549. }
  550. //==============================================================================
  551. void SystemClipboard::copyTextToClipboard (const String& text)
  552. {
  553. [[UIPasteboard generalPasteboard] setValue: juceStringToNS (text)
  554. forPasteboardType: @"public.text"];
  555. }
  556. String SystemClipboard::getTextFromClipboard()
  557. {
  558. return nsStringToJuce ([[UIPasteboard generalPasteboard] string]);
  559. }
  560. //==============================================================================
  561. bool MouseInputSource::SourceList::addSource()
  562. {
  563. addSource (sources.size(), MouseInputSource::InputSourceType::touch);
  564. return true;
  565. }
  566. bool MouseInputSource::SourceList::canUseTouch()
  567. {
  568. return true;
  569. }
  570. bool Desktop::canUseSemiTransparentWindows() noexcept
  571. {
  572. return true;
  573. }
  574. bool Desktop::isDarkModeActive() const
  575. {
  576. #if defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0
  577. if (@available (iOS 12.0, *))
  578. return [[[UIScreen mainScreen] traitCollection] userInterfaceStyle] == UIUserInterfaceStyleDark;
  579. #endif
  580. return false;
  581. }
  582. class Desktop::NativeDarkModeChangeDetectorImpl
  583. {
  584. public:
  585. NativeDarkModeChangeDetectorImpl()
  586. {
  587. static DelegateClass delegateClass;
  588. delegate = [delegateClass.createInstance() init];
  589. object_setInstanceVariable (delegate, "owner", this);
  590. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  591. [[NSNotificationCenter defaultCenter] addObserver: delegate
  592. selector: @selector (darkModeChanged:)
  593. name: UIViewComponentPeer::getDarkModeNotificationName()
  594. object: nil];
  595. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  596. }
  597. ~NativeDarkModeChangeDetectorImpl()
  598. {
  599. object_setInstanceVariable (delegate, "owner", nullptr);
  600. [[NSNotificationCenter defaultCenter] removeObserver: delegate];
  601. [delegate release];
  602. }
  603. void darkModeChanged()
  604. {
  605. Desktop::getInstance().darkModeChanged();
  606. }
  607. private:
  608. struct DelegateClass : public ObjCClass<NSObject>
  609. {
  610. DelegateClass() : ObjCClass<NSObject> ("JUCEDelegate_")
  611. {
  612. addIvar<NativeDarkModeChangeDetectorImpl*> ("owner");
  613. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  614. addMethod (@selector (darkModeChanged:), darkModeChanged);
  615. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  616. registerClass();
  617. }
  618. static void darkModeChanged (id self, SEL, NSNotification*)
  619. {
  620. if (auto* owner = getIvar<NativeDarkModeChangeDetectorImpl*> (self, "owner"))
  621. owner->darkModeChanged();
  622. }
  623. };
  624. id delegate = nil;
  625. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  626. };
  627. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  628. {
  629. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  630. }
  631. Point<float> MouseInputSource::getCurrentRawMousePosition()
  632. {
  633. return juce_lastMousePos;
  634. }
  635. void MouseInputSource::setRawMousePosition (Point<float>)
  636. {
  637. }
  638. double Desktop::getDefaultMasterScale()
  639. {
  640. return 1.0;
  641. }
  642. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  643. {
  644. UIInterfaceOrientation orientation = SystemStats::isRunningInAppExtensionSandbox() ? UIInterfaceOrientationPortrait
  645. : getWindowOrientation();
  646. return Orientations::convertToJuce (orientation);
  647. }
  648. // The most straightforward way of retrieving the screen area available to an iOS app
  649. // seems to be to create a new window (which will take up all available space) and to
  650. // query its frame.
  651. struct TemporaryWindow
  652. {
  653. UIWindow* window = [[UIWindow alloc] init];
  654. ~TemporaryWindow() noexcept { [window release]; }
  655. };
  656. static Rectangle<int> getRecommendedWindowBounds()
  657. {
  658. return convertToRectInt (TemporaryWindow().window.frame);
  659. }
  660. static BorderSize<int> getSafeAreaInsets (float masterScale)
  661. {
  662. #if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
  663. if (@available (iOS 11.0, *))
  664. {
  665. UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets;
  666. auto getInset = [&] (CGFloat original) { return roundToInt (original / masterScale); };
  667. return { getInset (safeInsets.top), getInset (safeInsets.left),
  668. getInset (safeInsets.bottom), getInset (safeInsets.right) };
  669. }
  670. #endif
  671. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  672. auto statusBarSize = [UIApplication sharedApplication].statusBarFrame.size;
  673. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  674. auto statusBarHeight = jmin (statusBarSize.width, statusBarSize.height);
  675. return { roundToInt (statusBarHeight / masterScale), 0, 0, 0 };
  676. }
  677. void Displays::findDisplays (float masterScale)
  678. {
  679. JUCE_AUTORELEASEPOOL
  680. {
  681. UIScreen* s = [UIScreen mainScreen];
  682. Display d;
  683. d.totalArea = convertToRectInt ([s bounds]) / masterScale;
  684. d.userArea = getRecommendedWindowBounds() / masterScale;
  685. d.safeAreaInsets = getSafeAreaInsets (masterScale);
  686. d.isMain = true;
  687. d.scale = masterScale * s.scale;
  688. d.dpi = 160 * d.scale;
  689. displays.add (d);
  690. }
  691. }
  692. } // namespace juce