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.

437 lines
14KB

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