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.

314 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. typedef void (*AppFocusChangeCallback)();
  19. AppFocusChangeCallback appFocusChangeCallback = nullptr;
  20. typedef bool (*CheckEventBlockedByModalComps) (NSEvent*);
  21. CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
  22. //==============================================================================
  23. struct AppDelegateClass : public ObjCClass <NSObject>
  24. {
  25. AppDelegateClass() : ObjCClass ("JUCEAppDelegate_")
  26. {
  27. addMethod (@selector (init), init, "@@:");
  28. addMethod (@selector (dealloc), dealloc, "v@:");
  29. addMethod (@selector (unregisterObservers), unregisterObservers, "v@:");
  30. addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@");
  31. addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@");
  32. addMethod (@selector (application:openFile:), application_openFile, "c@:@@");
  33. addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@");
  34. addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@");
  35. addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@");
  36. addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@");
  37. addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@");
  38. addMethod (@selector (dummyMethod), dummyMethod, "v@:");
  39. registerClass();
  40. }
  41. static NSString* getBroacastEventName()
  42. {
  43. return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64()));
  44. }
  45. private:
  46. static id init (id self, SEL)
  47. {
  48. self = sendSuperclassMessage (self, @selector (init));
  49. if (JUCEApplicationBase::isStandaloneApp())
  50. {
  51. [NSApp setDelegate: self];
  52. [[NSDistributedNotificationCenter defaultCenter] addObserver: self
  53. selector: @selector (broadcastMessageCallback:)
  54. name: getBroacastEventName()
  55. object: nil];
  56. }
  57. else
  58. {
  59. NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  60. [center addObserver: self selector: @selector (applicationDidResignActive:)
  61. name: NSApplicationDidResignActiveNotification object: NSApp];
  62. [center addObserver: self selector: @selector (applicationDidBecomeActive:)
  63. name: NSApplicationDidBecomeActiveNotification object: NSApp];
  64. [center addObserver: self selector: @selector (applicationWillUnhide:)
  65. name: NSApplicationWillUnhideNotification object: NSApp];
  66. }
  67. return self;
  68. }
  69. static void dealloc (id self, SEL)
  70. {
  71. sendSuperclassMessage (self, @selector (dealloc));
  72. }
  73. static void unregisterObservers (id self, SEL)
  74. {
  75. [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: self];
  76. [[NSNotificationCenter defaultCenter] removeObserver: self];
  77. if (JUCEApplicationBase::isStandaloneApp())
  78. {
  79. [NSApp setDelegate: nil];
  80. [[NSDistributedNotificationCenter defaultCenter] removeObserver: self
  81. name: getBroacastEventName()
  82. object: nil];
  83. }
  84. }
  85. static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*)
  86. {
  87. if (JUCEApplicationBase::getInstance() != nullptr)
  88. {
  89. JUCEApplicationBase::getInstance()->systemRequestedQuit();
  90. if (! MessageManager::getInstance()->hasStopMessageBeenSent())
  91. return NSTerminateCancel;
  92. }
  93. return NSTerminateNow;
  94. }
  95. static void applicationWillTerminate (id /*self*/, SEL, NSNotification*)
  96. {
  97. JUCEApplicationBase::appWillTerminateByForce();
  98. }
  99. static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename)
  100. {
  101. if (JUCEApplicationBase::getInstance() != nullptr)
  102. {
  103. JUCEApplicationBase::getInstance()->anotherInstanceStarted (quotedIfContainsSpaces (filename));
  104. return YES;
  105. }
  106. return NO;
  107. }
  108. static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames)
  109. {
  110. StringArray files;
  111. for (unsigned int i = 0; i < [filenames count]; ++i)
  112. files.add (quotedIfContainsSpaces ((NSString*) [filenames objectAtIndex: i]));
  113. if (files.size() > 0 && JUCEApplicationBase::getInstance() != nullptr)
  114. JUCEApplicationBase::getInstance()->anotherInstanceStarted (files.joinIntoString (" "));
  115. }
  116. static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
  117. static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
  118. static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); }
  119. static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n)
  120. {
  121. NSDictionary* dict = (NSDictionary*) [n userInfo];
  122. const String messageString (nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]));
  123. MessageManager::getInstance()->deliverBroadcastMessage (messageString);
  124. }
  125. static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread)
  126. private:
  127. static void focusChanged()
  128. {
  129. if (appFocusChangeCallback != nullptr)
  130. (*appFocusChangeCallback)();
  131. }
  132. static String quotedIfContainsSpaces (NSString* file)
  133. {
  134. String s (nsStringToJuce (file));
  135. if (s.containsChar (' '))
  136. s = s.quoted ('"');
  137. return s;
  138. }
  139. };
  140. //==============================================================================
  141. void MessageManager::runDispatchLoop()
  142. {
  143. if (! quitMessagePosted) // check that the quit message wasn't already posted..
  144. {
  145. JUCE_AUTORELEASEPOOL
  146. // must only be called by the message thread!
  147. jassert (isThisTheMessageThread());
  148. #if JUCE_CATCH_UNHANDLED_EXCEPTIONS
  149. @try
  150. {
  151. [NSApp run];
  152. }
  153. @catch (NSException* e)
  154. {
  155. // An AppKit exception will kill the app, but at least this provides a chance to log it.,
  156. std::runtime_error ex (std::string ("NSException: ") + [[e name] UTF8String] + ", Reason:" + [[e reason] UTF8String]);
  157. JUCEApplication::sendUnhandledException (&ex, __FILE__, __LINE__);
  158. }
  159. @finally
  160. {
  161. }
  162. #else
  163. [NSApp run];
  164. #endif
  165. }
  166. }
  167. void MessageManager::stopDispatchLoop()
  168. {
  169. quitMessagePosted = true;
  170. [NSApp stop: nil];
  171. [NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated)
  172. [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1];
  173. }
  174. #if JUCE_MODAL_LOOPS_PERMITTED
  175. bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
  176. {
  177. jassert (millisecondsToRunFor >= 0);
  178. jassert (isThisTheMessageThread()); // must only be called by the message thread
  179. uint32 endTime = Time::getMillisecondCounter() + (uint32) millisecondsToRunFor;
  180. while (! quitMessagePosted)
  181. {
  182. JUCE_AUTORELEASEPOOL
  183. CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.001, true);
  184. NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask
  185. untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]
  186. inMode: NSDefaultRunLoopMode
  187. dequeue: YES];
  188. if (e != nil && (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e)))
  189. [NSApp sendEvent: e];
  190. if (Time::getMillisecondCounter() >= endTime)
  191. break;
  192. }
  193. return ! quitMessagePosted;
  194. }
  195. #endif
  196. //==============================================================================
  197. void initialiseNSApplication();
  198. void initialiseNSApplication()
  199. {
  200. JUCE_AUTORELEASEPOOL
  201. [NSApplication sharedApplication];
  202. }
  203. //==============================================================================
  204. struct AppDelegateHolder
  205. {
  206. public:
  207. AppDelegateHolder()
  208. {
  209. static AppDelegateClass cls;
  210. delegate = [cls.createInstance() init];
  211. }
  212. ~AppDelegateHolder()
  213. {
  214. [delegate performSelector: @selector (unregisterObservers)];
  215. [delegate release];
  216. }
  217. id delegate;
  218. CFRunLoopRef runLoop;
  219. CFRunLoopSourceRef runLoopSource;
  220. MessageQueue messageQueue;
  221. };
  222. static AppDelegateHolder* appDelegate = nullptr;
  223. void MessageManager::doPlatformSpecificInitialisation()
  224. {
  225. if (appDelegate == nil)
  226. appDelegate = new AppDelegateHolder();
  227. #if ! (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
  228. // This launches a dummy thread, which forces Cocoa to initialise NSThreads correctly (needed prior to 10.5)
  229. if (! [NSThread isMultiThreaded])
  230. [NSThread detachNewThreadSelector: @selector (dummyMethod)
  231. toTarget: appDelegate->delegate
  232. withObject: nil];
  233. #endif
  234. }
  235. void MessageManager::doPlatformSpecificShutdown()
  236. {
  237. delete appDelegate;
  238. appDelegate = nullptr;
  239. }
  240. bool MessageManager::postMessageToSystemQueue (MessageBase* message)
  241. {
  242. jassert (appDelegate != nil);
  243. appDelegate->messageQueue.post (message);
  244. return true;
  245. }
  246. void MessageManager::broadcastMessage (const String& message)
  247. {
  248. NSDictionary* info = [NSDictionary dictionaryWithObject: juceStringToNS (message)
  249. forKey: nsStringLiteral ("message")];
  250. [[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegateClass::getBroacastEventName()
  251. object: nil
  252. userInfo: info];
  253. }