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.

379 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. // (This file gets included by juce_mac_NativeCode.mm, rather than being
  24. // compiled on its own).
  25. #ifdef JUCE_INCLUDED_FILE
  26. struct CallbackMessagePayload
  27. {
  28. MessageCallbackFunction* function;
  29. void* parameter;
  30. void* volatile result;
  31. bool volatile hasBeenExecuted;
  32. };
  33. /* When you use multiple DLLs which share similarly-named obj-c classes - like
  34. for example having more than one juce plugin loaded into a host, then when a
  35. method is called, the actual code that runs might actually be in a different module
  36. than the one you expect... So any calls to library functions or statics that are
  37. made inside obj-c methods will probably end up getting executed in a different DLL's
  38. memory space. Not a great thing to happen - this obviously leads to bizarre crashes.
  39. To work around this insanity, I'm only allowing obj-c methods to make calls to
  40. virtual methods of an object that's known to live inside the right module's space.
  41. */
  42. class AppDelegateRedirector
  43. {
  44. public:
  45. AppDelegateRedirector() {}
  46. virtual ~AppDelegateRedirector() {}
  47. virtual NSApplicationTerminateReply shouldTerminate()
  48. {
  49. if (JUCEApplication::getInstance() != 0)
  50. {
  51. const MessageManagerLock mml;
  52. JUCEApplication::getInstance()->systemRequestedQuit();
  53. return NSTerminateCancel;
  54. }
  55. return NSTerminateNow;
  56. }
  57. virtual BOOL openFile (const NSString* filename)
  58. {
  59. if (JUCEApplication::getInstance() != 0)
  60. {
  61. const MessageManagerLock mml;
  62. JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename));
  63. return YES;
  64. }
  65. return NO;
  66. }
  67. virtual void openFiles (NSArray* filenames)
  68. {
  69. StringArray files;
  70. for (unsigned int i = 0; i < [filenames count]; ++i)
  71. files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i]));
  72. if (files.size() > 0 && JUCEApplication::getInstance() != 0)
  73. {
  74. const MessageManagerLock mml;
  75. JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" ")));
  76. }
  77. }
  78. virtual void focusChanged()
  79. {
  80. const MessageManagerLock mml;
  81. juce_HandleProcessFocusChange();
  82. }
  83. virtual void deliverMessage (void* message)
  84. {
  85. // no need for an mm lock here - deliverMessage locks it
  86. MessageManager::getInstance()->deliverMessage (message);
  87. }
  88. virtual void performCallback (CallbackMessagePayload* pl)
  89. {
  90. const MessageManagerLock mml;
  91. pl->result = (*pl->function) (pl->parameter);
  92. pl->hasBeenExecuted = true;
  93. }
  94. virtual void deleteSelf()
  95. {
  96. delete this;
  97. }
  98. };
  99. END_JUCE_NAMESPACE
  100. using namespace JUCE_NAMESPACE;
  101. #define JuceAppDelegate MakeObjCClassName(JuceAppDelegate)
  102. static int numPendingMessages = 0;
  103. static bool flushingMessages = false;
  104. @interface JuceAppDelegate : NSObject
  105. {
  106. @private
  107. id oldDelegate;
  108. AppDelegateRedirector* redirector;
  109. }
  110. - (JuceAppDelegate*) init;
  111. - (void) dealloc;
  112. - (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename;
  113. - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames;
  114. - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app;
  115. - (void) applicationDidBecomeActive: (NSNotification*) aNotification;
  116. - (void) applicationDidResignActive: (NSNotification*) aNotification;
  117. - (void) applicationWillUnhide: (NSNotification*) aNotification;
  118. - (void) customEvent: (id) data;
  119. - (void) performCallback: (id) info;
  120. - (void) dummyMethod;
  121. @end
  122. @implementation JuceAppDelegate
  123. - (JuceAppDelegate*) init
  124. {
  125. [super init];
  126. redirector = new AppDelegateRedirector();
  127. numPendingMessages = 0;
  128. flushingMessages = false;
  129. NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  130. if (JUCEApplication::getInstance() != 0)
  131. {
  132. oldDelegate = [NSApp delegate];
  133. [NSApp setDelegate: self];
  134. }
  135. else
  136. {
  137. oldDelegate = 0;
  138. [center addObserver: self selector: @selector (applicationDidResignActive:)
  139. name: NSApplicationDidResignActiveNotification object: NSApp];
  140. [center addObserver: self selector: @selector (applicationDidBecomeActive:)
  141. name: NSApplicationDidBecomeActiveNotification object: NSApp];
  142. [center addObserver: self selector: @selector (applicationWillUnhide:)
  143. name: NSApplicationWillUnhideNotification object: NSApp];
  144. }
  145. return self;
  146. }
  147. - (void) dealloc
  148. {
  149. if (oldDelegate != 0)
  150. [NSApp setDelegate: oldDelegate];
  151. redirector->deleteSelf();
  152. [super dealloc];
  153. }
  154. - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app
  155. {
  156. return redirector->shouldTerminate();
  157. }
  158. - (BOOL) application: (NSApplication*) app openFile: (NSString*) filename
  159. {
  160. return redirector->openFile (filename);
  161. }
  162. - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames
  163. {
  164. return redirector->openFiles (filenames);
  165. }
  166. - (void) applicationDidBecomeActive: (NSNotification*) aNotification
  167. {
  168. redirector->focusChanged();
  169. }
  170. - (void) applicationDidResignActive: (NSNotification*) aNotification
  171. {
  172. redirector->focusChanged();
  173. }
  174. - (void) applicationWillUnhide: (NSNotification*) aNotification
  175. {
  176. redirector->focusChanged();
  177. }
  178. - (void) customEvent: (id) n
  179. {
  180. atomicDecrement (numPendingMessages);
  181. NSData* data = (NSData*) n;
  182. void* message = 0;
  183. [data getBytes: &message length: sizeof (message)];
  184. if (message != 0 && ! flushingMessages)
  185. redirector->deliverMessage (message);
  186. [data release];
  187. }
  188. - (void) performCallback: (id) info
  189. {
  190. if ([info isKindOfClass: [NSData class]])
  191. {
  192. CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes];
  193. if (pl != 0)
  194. redirector->performCallback (pl);
  195. }
  196. else
  197. {
  198. jassertfalse // should never get here!
  199. }
  200. }
  201. - (void) dummyMethod {} // (used as a way of running a dummy thread)
  202. @end
  203. BEGIN_JUCE_NAMESPACE
  204. static JuceAppDelegate* juceAppDelegate = 0;
  205. void MessageManager::runDispatchLoop()
  206. {
  207. const ScopedAutoReleasePool pool;
  208. // must only be called by the message thread!
  209. jassert (isThisTheMessageThread());
  210. [NSApp run];
  211. }
  212. void MessageManager::stopDispatchLoop()
  213. {
  214. quitMessagePosted = true;
  215. [NSApp stop: nil];
  216. }
  217. bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
  218. {
  219. const ScopedAutoReleasePool pool;
  220. jassert (isThisTheMessageThread()); // must only be called by the message thread
  221. uint32 endTime = Time::getMillisecondCounter() + millisecondsToRunFor;
  222. NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001];
  223. while (Time::getMillisecondCounter() < endTime)
  224. {
  225. const ScopedAutoReleasePool pool;
  226. [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
  227. beforeDate: endDate];
  228. NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask
  229. untilDate: endDate
  230. inMode: NSDefaultRunLoopMode
  231. dequeue: YES];
  232. [NSApp sendEvent: e];
  233. }
  234. return ! quitMessagePosted;
  235. }
  236. //==============================================================================
  237. void MessageManager::doPlatformSpecificInitialisation()
  238. {
  239. if (juceAppDelegate == 0)
  240. juceAppDelegate = [[JuceAppDelegate alloc] init];
  241. // This launches a dummy thread, which forces Cocoa to initialise NSThreads
  242. // correctly (needed prior to 10.5)
  243. if (! [NSThread isMultiThreaded])
  244. [NSThread detachNewThreadSelector: @selector (dummyMethod)
  245. toTarget: juceAppDelegate
  246. withObject: nil];
  247. initialiseMainMenu();
  248. }
  249. void MessageManager::doPlatformSpecificShutdown()
  250. {
  251. [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate];
  252. [[NSNotificationCenter defaultCenter] removeObserver: juceAppDelegate];
  253. // Annoyingly, cancelPerformSelectorsWithTarget can't actually cancel the messages
  254. // sent by performSelectorOnMainThread, so need to manually flush these before quitting..
  255. for (int i = 100; --i >= 0 && numPendingMessages > 0;)
  256. {
  257. flushingMessages = true;
  258. getInstance()->runDispatchLoopUntil (10);
  259. }
  260. jassert (numPendingMessages == 0); // failed to get all the pending messages cleared before quitting..
  261. [juceAppDelegate release];
  262. juceAppDelegate = 0;
  263. }
  264. bool juce_postMessageToSystemQueue (void* message)
  265. {
  266. atomicIncrement (numPendingMessages);
  267. [juceAppDelegate performSelectorOnMainThread: @selector (customEvent:)
  268. withObject: (id) [[NSData alloc] initWithBytes: &message
  269. length: (int) sizeof (message)]
  270. waitUntilDone: NO];
  271. return true;
  272. }
  273. void MessageManager::broadcastMessage (const String& value) throw()
  274. {
  275. }
  276. void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
  277. void* data)
  278. {
  279. if (isThisTheMessageThread())
  280. {
  281. return (*callback) (data);
  282. }
  283. else
  284. {
  285. const ScopedAutoReleasePool pool;
  286. CallbackMessagePayload cmp;
  287. cmp.function = callback;
  288. cmp.parameter = data;
  289. cmp.result = 0;
  290. cmp.hasBeenExecuted = false;
  291. [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:)
  292. withObject: [NSData dataWithBytesNoCopy: &cmp
  293. length: sizeof (cmp)
  294. freeWhenDone: NO]
  295. waitUntilDone: YES];
  296. return cmp.result;
  297. }
  298. }
  299. #endif