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.

489 lines
16KB

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