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 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  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 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. extern bool isIOSAppActive;
  22. struct AppInactivityCallback // NB: careful, this declaration is duplicated in other modules
  23. {
  24. virtual ~AppInactivityCallback() {}
  25. virtual void appBecomingInactive() = 0;
  26. };
  27. // This is an internal list of callbacks (but currently used between modules)
  28. Array<AppInactivityCallback*> appBecomingInactiveCallbacks;
  29. }
  30. @interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate>
  31. {
  32. UIBackgroundTaskIdentifier appSuspendTask;
  33. }
  34. @property (strong, nonatomic) UIWindow *window;
  35. - (id)init;
  36. - (void) applicationDidFinishLaunching: (UIApplication*) application;
  37. - (void) applicationWillTerminate: (UIApplication*) application;
  38. - (void) applicationDidEnterBackground: (UIApplication*) application;
  39. - (void) applicationWillEnterForeground: (UIApplication*) application;
  40. - (void) applicationDidBecomeActive: (UIApplication*) application;
  41. - (void) applicationWillResignActive: (UIApplication*) application;
  42. - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*)identifier
  43. completionHandler: (void (^)(void))completionHandler;
  44. @end
  45. @implementation JuceAppStartupDelegate
  46. - (id)init
  47. {
  48. self = [super init];
  49. appSuspendTask = UIBackgroundTaskInvalid;
  50. return self;
  51. }
  52. - (void) applicationDidFinishLaunching: (UIApplication*) application
  53. {
  54. ignoreUnused (application);
  55. initialiseJuce_GUI();
  56. if (JUCEApplicationBase* app = JUCEApplicationBase::createInstance())
  57. {
  58. if (! app->initialiseApp())
  59. exit (app->shutdownApp());
  60. }
  61. else
  62. {
  63. jassertfalse; // you must supply an application object for an iOS app!
  64. }
  65. }
  66. - (void) applicationWillTerminate: (UIApplication*) application
  67. {
  68. ignoreUnused (application);
  69. JUCEApplicationBase::appWillTerminateByForce();
  70. }
  71. - (void) applicationDidEnterBackground: (UIApplication*) application
  72. {
  73. if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
  74. {
  75. #if JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK
  76. appSuspendTask = [application beginBackgroundTaskWithName:@"JUCE Suspend Task" expirationHandler:^{
  77. if (appSuspendTask != UIBackgroundTaskInvalid)
  78. {
  79. [application endBackgroundTask:appSuspendTask];
  80. appSuspendTask = UIBackgroundTaskInvalid;
  81. }
  82. }];
  83. MessageManager::callAsync ([self,application,app] () { app->suspended(); });
  84. #else
  85. ignoreUnused (application);
  86. app->suspended();
  87. #endif
  88. }
  89. }
  90. - (void) applicationWillEnterForeground: (UIApplication*) application
  91. {
  92. ignoreUnused (application);
  93. if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
  94. app->resumed();
  95. }
  96. - (void) applicationDidBecomeActive: (UIApplication*) application
  97. {
  98. ignoreUnused (application);
  99. isIOSAppActive = true;
  100. }
  101. - (void) applicationWillResignActive: (UIApplication*) application
  102. {
  103. ignoreUnused (application);
  104. isIOSAppActive = false;
  105. for (int i = appBecomingInactiveCallbacks.size(); --i >= 0;)
  106. appBecomingInactiveCallbacks.getReference(i)->appBecomingInactive();
  107. }
  108. - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*)identifier
  109. completionHandler: (void (^)(void))completionHandler
  110. {
  111. ignoreUnused (application);
  112. URL::DownloadTask::juce_iosURLSessionNotify (nsStringToJuce (identifier));
  113. completionHandler();
  114. }
  115. @end
  116. namespace juce
  117. {
  118. int juce_iOSMain (int argc, const char* argv[], void* customDelgatePtr);
  119. int juce_iOSMain (int argc, const char* argv[], void* customDelagetPtr)
  120. {
  121. Class delegateClass = (customDelagetPtr != nullptr ? reinterpret_cast<Class> (customDelagetPtr) : [JuceAppStartupDelegate class]);
  122. return UIApplicationMain (argc, const_cast<char**> (argv), nil, NSStringFromClass (delegateClass));
  123. }
  124. //==============================================================================
  125. void LookAndFeel::playAlertSound()
  126. {
  127. // TODO
  128. }
  129. //==============================================================================
  130. class iOSMessageBox;
  131. #if defined (__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
  132. #define JUCE_USE_NEW_IOS_ALERTWINDOW 1
  133. #endif
  134. #if ! JUCE_USE_NEW_IOS_ALERTWINDOW
  135. } // (juce namespace)
  136. @interface JuceAlertBoxDelegate : NSObject <UIAlertViewDelegate>
  137. {
  138. @public
  139. iOSMessageBox* owner;
  140. }
  141. - (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex;
  142. @end
  143. namespace juce
  144. {
  145. #endif
  146. class iOSMessageBox
  147. {
  148. public:
  149. iOSMessageBox (const String& title, const String& message,
  150. NSString* button1, NSString* button2, NSString* button3,
  151. ModalComponentManager::Callback* cb, const bool async)
  152. : result (0), resultReceived (false), callback (cb), isAsync (async)
  153. {
  154. #if JUCE_USE_NEW_IOS_ALERTWINDOW
  155. if (currentlyFocusedPeer != nullptr)
  156. {
  157. UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (title)
  158. message: juceStringToNS (message)
  159. preferredStyle: UIAlertControllerStyleAlert];
  160. addButton (alert, button1, 0);
  161. addButton (alert, button2, 1);
  162. addButton (alert, button3, 2);
  163. [currentlyFocusedPeer->controller presentViewController: alert
  164. animated: YES
  165. completion: nil];
  166. }
  167. else
  168. {
  169. // Since iOS8, alert windows need to be associated with a window, so you need to
  170. // have at least one window on screen when you use this
  171. jassertfalse;
  172. }
  173. #else
  174. delegate = [[JuceAlertBoxDelegate alloc] init];
  175. delegate->owner = this;
  176. alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title)
  177. message: juceStringToNS (message)
  178. delegate: delegate
  179. cancelButtonTitle: button1
  180. otherButtonTitles: button2, button3, nil];
  181. [alert retain];
  182. [alert show];
  183. #endif
  184. }
  185. ~iOSMessageBox()
  186. {
  187. #if ! JUCE_USE_NEW_IOS_ALERTWINDOW
  188. [alert release];
  189. [delegate release];
  190. #endif
  191. }
  192. int getResult()
  193. {
  194. jassert (callback == nullptr);
  195. JUCE_AUTORELEASEPOOL
  196. {
  197. #if JUCE_USE_NEW_IOS_ALERTWINDOW
  198. while (! resultReceived)
  199. #else
  200. while (! (alert.hidden || resultReceived))
  201. #endif
  202. [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
  203. }
  204. return result;
  205. }
  206. void buttonClicked (const int buttonIndex) noexcept
  207. {
  208. result = buttonIndex;
  209. resultReceived = true;
  210. if (callback != nullptr)
  211. callback->modalStateFinished (result);
  212. if (isAsync)
  213. delete this;
  214. }
  215. private:
  216. int result;
  217. bool resultReceived;
  218. ScopedPointer<ModalComponentManager::Callback> callback;
  219. const bool isAsync;
  220. #if JUCE_USE_NEW_IOS_ALERTWINDOW
  221. void addButton (UIAlertController* alert, NSString* text, int index)
  222. {
  223. if (text != nil)
  224. [alert addAction: [UIAlertAction actionWithTitle: text
  225. style: UIAlertActionStyleDefault
  226. handler: ^(UIAlertAction*) { this->buttonClicked (index); }]];
  227. }
  228. #else
  229. UIAlertView* alert;
  230. JuceAlertBoxDelegate* delegate;
  231. #endif
  232. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox)
  233. };
  234. #if ! JUCE_USE_NEW_IOS_ALERTWINDOW
  235. } // (juce namespace)
  236. @implementation JuceAlertBoxDelegate
  237. - (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex
  238. {
  239. owner->buttonClicked ((int) buttonIndex);
  240. alertView.hidden = true;
  241. }
  242. @end
  243. namespace juce
  244. {
  245. #endif
  246. //==============================================================================
  247. #if JUCE_MODAL_LOOPS_PERMITTED
  248. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType /*iconType*/,
  249. const String& title, const String& message,
  250. Component* /*associatedComponent*/)
  251. {
  252. JUCE_AUTORELEASEPOOL
  253. {
  254. iOSMessageBox mb (title, message, @"OK", nil, nil, nullptr, false);
  255. ignoreUnused (mb.getResult());
  256. }
  257. }
  258. #endif
  259. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType /*iconType*/,
  260. const String& title, const String& message,
  261. Component* /*associatedComponent*/,
  262. ModalComponentManager::Callback* callback)
  263. {
  264. new iOSMessageBox (title, message, @"OK", nil, nil, callback, true);
  265. }
  266. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType /*iconType*/,
  267. const String& title, const String& message,
  268. Component* /*associatedComponent*/,
  269. ModalComponentManager::Callback* callback)
  270. {
  271. ScopedPointer<iOSMessageBox> mb (new iOSMessageBox (title, message, @"Cancel", @"OK",
  272. nil, callback, callback != nullptr));
  273. if (callback == nullptr)
  274. return mb->getResult() == 1;
  275. mb.release();
  276. return false;
  277. }
  278. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType /*iconType*/,
  279. const String& title, const String& message,
  280. Component* /*associatedComponent*/,
  281. ModalComponentManager::Callback* callback)
  282. {
  283. ScopedPointer<iOSMessageBox> mb (new iOSMessageBox (title, message, @"Cancel", @"Yes", @"No", callback, callback != nullptr));
  284. if (callback == nullptr)
  285. return mb->getResult();
  286. mb.release();
  287. return 0;
  288. }
  289. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType /*iconType*/,
  290. const String& title, const String& message,
  291. Component* /*associatedComponent*/,
  292. ModalComponentManager::Callback* callback)
  293. {
  294. ScopedPointer<iOSMessageBox> mb (new iOSMessageBox (title, message, @"No", @"Yes", nil, callback, callback != nullptr));
  295. if (callback == nullptr)
  296. return mb->getResult();
  297. mb.release();
  298. return 0;
  299. }
  300. //==============================================================================
  301. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*)
  302. {
  303. jassertfalse; // no such thing on iOS!
  304. return false;
  305. }
  306. bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*)
  307. {
  308. jassertfalse; // no such thing on iOS!
  309. return false;
  310. }
  311. //==============================================================================
  312. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  313. {
  314. if (! SystemStats::isRunningInAppExtensionSandbox())
  315. [[UIApplication sharedApplication] setIdleTimerDisabled: ! isEnabled];
  316. }
  317. bool Desktop::isScreenSaverEnabled()
  318. {
  319. if (SystemStats::isRunningInAppExtensionSandbox())
  320. return true;
  321. return ! [[UIApplication sharedApplication] isIdleTimerDisabled];
  322. }
  323. //==============================================================================
  324. bool juce_areThereAnyAlwaysOnTopWindows()
  325. {
  326. return false;
  327. }
  328. //==============================================================================
  329. Image juce_createIconForFile (const File&)
  330. {
  331. return Image();
  332. }
  333. //==============================================================================
  334. void SystemClipboard::copyTextToClipboard (const String& text)
  335. {
  336. [[UIPasteboard generalPasteboard] setValue: juceStringToNS (text)
  337. forPasteboardType: @"public.text"];
  338. }
  339. String SystemClipboard::getTextFromClipboard()
  340. {
  341. return nsStringToJuce ([[UIPasteboard generalPasteboard] valueForPasteboardType: @"public.text"]);
  342. }
  343. //==============================================================================
  344. bool MouseInputSource::SourceList::addSource()
  345. {
  346. addSource (sources.size(), MouseInputSource::InputSourceType::touch);
  347. return true;
  348. }
  349. bool MouseInputSource::SourceList::canUseTouch()
  350. {
  351. return true;
  352. }
  353. bool Desktop::canUseSemiTransparentWindows() noexcept
  354. {
  355. return true;
  356. }
  357. Point<float> MouseInputSource::getCurrentRawMousePosition()
  358. {
  359. return juce_lastMousePos;
  360. }
  361. void MouseInputSource::setRawMousePosition (Point<float>)
  362. {
  363. }
  364. double Desktop::getDefaultMasterScale()
  365. {
  366. return 1.0;
  367. }
  368. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  369. {
  370. UIInterfaceOrientation orientation = SystemStats::isRunningInAppExtensionSandbox() ? UIInterfaceOrientationPortrait
  371. : [[UIApplication sharedApplication] statusBarOrientation];
  372. return Orientations::convertToJuce (orientation);
  373. }
  374. void Desktop::Displays::findDisplays (float masterScale)
  375. {
  376. JUCE_AUTORELEASEPOOL
  377. {
  378. UIScreen* s = [UIScreen mainScreen];
  379. Display d;
  380. d.userArea = d.totalArea = UIViewComponentPeer::realScreenPosToRotated (convertToRectInt ([s bounds])) / masterScale;
  381. d.isMain = true;
  382. d.scale = masterScale;
  383. if ([s respondsToSelector: @selector (scale)])
  384. d.scale *= s.scale;
  385. d.dpi = 160 * d.scale;
  386. displays.add (d);
  387. }
  388. }
  389. } // namespace juce