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.

340 lines
10KB

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