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.

468 lines
15KB

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