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.

369 lines
11KB

  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. JUCEApplication::getInstance()->systemRequestedQuit();
  52. return NSTerminateCancel;
  53. }
  54. return NSTerminateNow;
  55. }
  56. virtual BOOL openFile (const NSString* filename)
  57. {
  58. if (JUCEApplication::getInstance() != 0)
  59. {
  60. JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename));
  61. return YES;
  62. }
  63. return NO;
  64. }
  65. virtual void openFiles (NSArray* filenames)
  66. {
  67. StringArray files;
  68. for (unsigned int i = 0; i < [filenames count]; ++i)
  69. files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i]));
  70. if (files.size() > 0 && JUCEApplication::getInstance() != 0)
  71. JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" ")));
  72. }
  73. virtual void focusChanged()
  74. {
  75. juce_HandleProcessFocusChange();
  76. }
  77. virtual void deliverMessage (void* message)
  78. {
  79. MessageManager::getInstance()->deliverMessage (message);
  80. }
  81. virtual void deleteSelf()
  82. {
  83. delete this;
  84. }
  85. };
  86. END_JUCE_NAMESPACE
  87. using namespace JUCE_NAMESPACE;
  88. #define JuceAppDelegate MakeObjCClassName(JuceAppDelegate)
  89. static int numPendingMessages = 0;
  90. static bool flushingMessages = false;
  91. @interface JuceAppDelegate : NSObject
  92. {
  93. @private
  94. id oldDelegate;
  95. AppDelegateRedirector* redirector;
  96. }
  97. - (JuceAppDelegate*) init;
  98. - (void) dealloc;
  99. - (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename;
  100. - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames;
  101. - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app;
  102. - (void) applicationDidBecomeActive: (NSNotification*) aNotification;
  103. - (void) applicationDidResignActive: (NSNotification*) aNotification;
  104. - (void) applicationWillUnhide: (NSNotification*) aNotification;
  105. - (void) customEvent: (id) data;
  106. - (void) performCallback: (id) info;
  107. - (void) dummyMethod;
  108. @end
  109. @implementation JuceAppDelegate
  110. - (JuceAppDelegate*) init
  111. {
  112. [super init];
  113. redirector = new AppDelegateRedirector();
  114. numPendingMessages = 0;
  115. flushingMessages = false;
  116. NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  117. if (JUCEApplication::getInstance() != 0)
  118. {
  119. oldDelegate = [NSApp delegate];
  120. [NSApp setDelegate: self];
  121. }
  122. else
  123. {
  124. oldDelegate = 0;
  125. [center addObserver: self selector: @selector (applicationDidResignActive:)
  126. name: NSApplicationDidResignActiveNotification object: NSApp];
  127. [center addObserver: self selector: @selector (applicationDidBecomeActive:)
  128. name: NSApplicationDidBecomeActiveNotification object: NSApp];
  129. [center addObserver: self selector: @selector (applicationWillUnhide:)
  130. name: NSApplicationWillUnhideNotification object: NSApp];
  131. }
  132. return self;
  133. }
  134. - (void) dealloc
  135. {
  136. if (oldDelegate != 0)
  137. [NSApp setDelegate: oldDelegate];
  138. redirector->deleteSelf();
  139. [super dealloc];
  140. }
  141. - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app
  142. {
  143. return redirector->shouldTerminate();
  144. }
  145. - (BOOL) application: (NSApplication*) app openFile: (NSString*) filename
  146. {
  147. return redirector->openFile (filename);
  148. }
  149. - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames
  150. {
  151. return redirector->openFiles (filenames);
  152. }
  153. - (void) applicationDidBecomeActive: (NSNotification*) aNotification
  154. {
  155. redirector->focusChanged();
  156. }
  157. - (void) applicationDidResignActive: (NSNotification*) aNotification
  158. {
  159. redirector->focusChanged();
  160. }
  161. - (void) applicationWillUnhide: (NSNotification*) aNotification
  162. {
  163. redirector->focusChanged();
  164. }
  165. - (void) customEvent: (id) n
  166. {
  167. atomicDecrement (numPendingMessages);
  168. NSData* data = (NSData*) n;
  169. void* message = 0;
  170. [data getBytes: &message length: sizeof (message)];
  171. if (message != 0 && ! flushingMessages)
  172. redirector->deliverMessage (message);
  173. [data release];
  174. }
  175. - (void) performCallback: (id) info
  176. {
  177. if ([info isKindOfClass: [NSData class]])
  178. {
  179. CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes];
  180. if (pl != 0)
  181. {
  182. pl->result = (*pl->function) (pl->parameter);
  183. pl->hasBeenExecuted = true;
  184. }
  185. }
  186. else
  187. {
  188. jassertfalse // should never get here!
  189. }
  190. }
  191. - (void) dummyMethod {} // (used as a way of running a dummy thread)
  192. @end
  193. BEGIN_JUCE_NAMESPACE
  194. static JuceAppDelegate* juceAppDelegate = 0;
  195. void MessageManager::runDispatchLoop()
  196. {
  197. const ScopedAutoReleasePool pool;
  198. MessageManagerLock mml;
  199. // must only be called by the message thread!
  200. jassert (isThisTheMessageThread());
  201. [NSApp run];
  202. }
  203. void MessageManager::stopDispatchLoop()
  204. {
  205. quitMessagePosted = true;
  206. [NSApp stop: nil];
  207. }
  208. bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
  209. {
  210. const ScopedAutoReleasePool pool;
  211. jassert (isThisTheMessageThread()); // must only be called by the message thread
  212. uint32 endTime = Time::getMillisecondCounter() + millisecondsToRunFor;
  213. NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001];
  214. while (Time::getMillisecondCounter() < endTime)
  215. {
  216. const ScopedAutoReleasePool pool;
  217. [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
  218. beforeDate: endDate];
  219. NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask
  220. untilDate: endDate
  221. inMode: NSDefaultRunLoopMode
  222. dequeue: YES];
  223. [NSApp sendEvent: e];
  224. }
  225. return ! quitMessagePosted;
  226. }
  227. //==============================================================================
  228. void MessageManager::doPlatformSpecificInitialisation()
  229. {
  230. if (juceAppDelegate == 0)
  231. juceAppDelegate = [[JuceAppDelegate alloc] init];
  232. // This launches a dummy thread, which forces Cocoa to initialise NSThreads
  233. // correctly (needed prior to 10.5)
  234. if (! [NSThread isMultiThreaded])
  235. [NSThread detachNewThreadSelector: @selector (dummyMethod)
  236. toTarget: juceAppDelegate
  237. withObject: nil];
  238. initialiseMainMenu();
  239. }
  240. void MessageManager::doPlatformSpecificShutdown()
  241. {
  242. [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate];
  243. [[NSNotificationCenter defaultCenter] removeObserver: juceAppDelegate];
  244. // Annoyingly, cancelPerformSelectorsWithTarget can't actually cancel the messages
  245. // sent by performSelectorOnMainThread, so need to manually flush these before quitting..
  246. for (int i = 100; --i >= 0 && numPendingMessages > 0;)
  247. {
  248. flushingMessages = true;
  249. getInstance()->runDispatchLoopUntil (10);
  250. }
  251. jassert (numPendingMessages == 0); // failed to get all the pending messages cleared before quitting..
  252. [juceAppDelegate release];
  253. juceAppDelegate = 0;
  254. }
  255. bool juce_postMessageToSystemQueue (void* message)
  256. {
  257. atomicIncrement (numPendingMessages);
  258. [juceAppDelegate performSelectorOnMainThread: @selector (customEvent:)
  259. withObject: (id) [[NSData alloc] initWithBytes: &message
  260. length: (int) sizeof (message)]
  261. waitUntilDone: NO];
  262. return true;
  263. }
  264. void MessageManager::broadcastMessage (const String& value) throw()
  265. {
  266. }
  267. void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
  268. void* data)
  269. {
  270. if (isThisTheMessageThread())
  271. {
  272. return (*callback) (data);
  273. }
  274. else
  275. {
  276. const ScopedAutoReleasePool pool;
  277. CallbackMessagePayload cmp;
  278. cmp.function = callback;
  279. cmp.parameter = data;
  280. cmp.result = 0;
  281. cmp.hasBeenExecuted = false;
  282. [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:)
  283. withObject: [NSData dataWithBytesNoCopy: &cmp
  284. length: sizeof (cmp)
  285. freeWhenDone: NO]
  286. waitUntilDone: YES];
  287. return cmp.result;
  288. }
  289. }
  290. #endif