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
14KB

  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. {
  72. JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" ")));
  73. }
  74. }
  75. virtual void focusChanged()
  76. {
  77. juce_HandleProcessFocusChange();
  78. }
  79. virtual void deliverMessage (void* message)
  80. {
  81. // no need for an mm lock here - deliverMessage locks it
  82. MessageManager::getInstance()->deliverMessage (message);
  83. }
  84. virtual void performCallback (CallbackMessagePayload* pl)
  85. {
  86. pl->result = (*pl->function) (pl->parameter);
  87. pl->hasBeenExecuted = true;
  88. }
  89. virtual void deleteSelf()
  90. {
  91. delete this;
  92. }
  93. };
  94. END_JUCE_NAMESPACE
  95. using namespace JUCE_NAMESPACE;
  96. #define JuceAppDelegate MakeObjCClassName(JuceAppDelegate)
  97. static int numPendingMessages = 0;
  98. @interface JuceAppDelegate : NSObject
  99. {
  100. @private
  101. id oldDelegate;
  102. AppDelegateRedirector* redirector;
  103. bool flushingMessages;
  104. }
  105. - (JuceAppDelegate*) init;
  106. - (void) dealloc;
  107. - (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename;
  108. - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames;
  109. - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app;
  110. - (void) applicationDidBecomeActive: (NSNotification*) aNotification;
  111. - (void) applicationDidResignActive: (NSNotification*) aNotification;
  112. - (void) applicationWillUnhide: (NSNotification*) aNotification;
  113. - (void) customEvent: (id) data;
  114. - (void) performCallback: (id) info;
  115. - (void) dummyMethod;
  116. @end
  117. @implementation JuceAppDelegate
  118. - (JuceAppDelegate*) init
  119. {
  120. [super init];
  121. redirector = new AppDelegateRedirector();
  122. numPendingMessages = 0;
  123. flushingMessages = false;
  124. NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  125. if (JUCEApplication::getInstance() != 0)
  126. {
  127. oldDelegate = [NSApp delegate];
  128. [NSApp setDelegate: self];
  129. }
  130. else
  131. {
  132. oldDelegate = 0;
  133. [center addObserver: self selector: @selector (applicationDidResignActive:)
  134. name: NSApplicationDidResignActiveNotification object: NSApp];
  135. [center addObserver: self selector: @selector (applicationDidBecomeActive:)
  136. name: NSApplicationDidBecomeActiveNotification object: NSApp];
  137. [center addObserver: self selector: @selector (applicationWillUnhide:)
  138. name: NSApplicationWillUnhideNotification object: NSApp];
  139. }
  140. return self;
  141. }
  142. - (void) dealloc
  143. {
  144. if (oldDelegate != 0)
  145. [NSApp setDelegate: oldDelegate];
  146. redirector->deleteSelf();
  147. [super dealloc];
  148. }
  149. - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app
  150. {
  151. return redirector->shouldTerminate();
  152. }
  153. - (BOOL) application: (NSApplication*) app openFile: (NSString*) filename
  154. {
  155. return redirector->openFile (filename);
  156. }
  157. - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames
  158. {
  159. return redirector->openFiles (filenames);
  160. }
  161. - (void) applicationDidBecomeActive: (NSNotification*) aNotification
  162. {
  163. redirector->focusChanged();
  164. }
  165. - (void) applicationDidResignActive: (NSNotification*) aNotification
  166. {
  167. redirector->focusChanged();
  168. }
  169. - (void) applicationWillUnhide: (NSNotification*) aNotification
  170. {
  171. redirector->focusChanged();
  172. }
  173. - (void) customEvent: (id) n
  174. {
  175. atomicDecrement (numPendingMessages);
  176. [self release];
  177. NSData* data = (NSData*) n;
  178. void* message = 0;
  179. [data getBytes: &message length: sizeof (message)];
  180. if (message != 0 && ! flushingMessages)
  181. redirector->deliverMessage (message);
  182. [data release];
  183. }
  184. - (void) performCallback: (id) info
  185. {
  186. if ([info isKindOfClass: [NSData class]])
  187. {
  188. CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes];
  189. if (pl != 0)
  190. redirector->performCallback (pl);
  191. }
  192. else
  193. {
  194. jassertfalse // should never get here!
  195. }
  196. }
  197. - (void) dummyMethod {} // (used as a way of running a dummy thread)
  198. @end
  199. BEGIN_JUCE_NAMESPACE
  200. static JuceAppDelegate* juceAppDelegate = 0;
  201. void MessageManager::runDispatchLoop()
  202. {
  203. if (! quitMessagePosted) // check that the quit message wasn't already posted..
  204. {
  205. const ScopedAutoReleasePool pool;
  206. // must only be called by the message thread!
  207. jassert (isThisTheMessageThread());
  208. [NSApp run];
  209. }
  210. }
  211. void MessageManager::stopDispatchLoop()
  212. {
  213. quitMessagePosted = true;
  214. [NSApp stop: nil];
  215. [NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated)
  216. }
  217. static bool isEventBlockedByModalComps (NSEvent* e)
  218. {
  219. if (Component::getNumCurrentlyModalComponents() == 0)
  220. return false;
  221. NSWindow* const w = [e window];
  222. if (w == 0 || [w worksWhenModal])
  223. return false;
  224. bool isKey = false, isInputAttempt = false;
  225. switch ([e type])
  226. {
  227. case NSKeyDown:
  228. case NSKeyUp:
  229. isKey = isInputAttempt = true;
  230. break;
  231. case NSLeftMouseDown:
  232. case NSRightMouseDown:
  233. case NSOtherMouseDown:
  234. isInputAttempt = true;
  235. break;
  236. case NSLeftMouseDragged:
  237. case NSRightMouseDragged:
  238. case NSLeftMouseUp:
  239. case NSRightMouseUp:
  240. case NSOtherMouseUp:
  241. case NSOtherMouseDragged:
  242. if (Component::getComponentUnderMouse() != 0)
  243. return false;
  244. break;
  245. case NSMouseMoved:
  246. case NSMouseEntered:
  247. case NSMouseExited:
  248. case NSCursorUpdate:
  249. case NSScrollWheel:
  250. case NSTabletPoint:
  251. case NSTabletProximity:
  252. break;
  253. default:
  254. return false;
  255. }
  256. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  257. {
  258. ComponentPeer* const peer = ComponentPeer::getPeer (i);
  259. NSView* const compView = (NSView*) peer->getNativeHandle();
  260. if ([compView window] == w)
  261. {
  262. if (isKey)
  263. {
  264. if (compView == [w firstResponder])
  265. return false;
  266. }
  267. else
  268. {
  269. if (NSPointInRect ([compView convertPoint: [e locationInWindow] fromView: nil],
  270. [compView bounds]))
  271. return false;
  272. }
  273. }
  274. }
  275. if (isInputAttempt)
  276. {
  277. if (! [NSApp isActive])
  278. [NSApp activateIgnoringOtherApps: YES];
  279. Component* const modal = Component::getCurrentlyModalComponent (0);
  280. if (modal != 0)
  281. modal->inputAttemptWhenModal();
  282. }
  283. return true;
  284. }
  285. bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
  286. {
  287. const ScopedAutoReleasePool pool;
  288. jassert (isThisTheMessageThread()); // must only be called by the message thread
  289. uint32 endTime = Time::getMillisecondCounter() + millisecondsToRunFor;
  290. NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001];
  291. while (Time::getMillisecondCounter() < endTime && ! quitMessagePosted)
  292. {
  293. const ScopedAutoReleasePool pool;
  294. [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
  295. beforeDate: endDate];
  296. NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask
  297. untilDate: endDate
  298. inMode: NSDefaultRunLoopMode
  299. dequeue: YES];
  300. if (e != 0 && ! isEventBlockedByModalComps (e))
  301. [NSApp sendEvent: e];
  302. }
  303. return ! quitMessagePosted;
  304. }
  305. //==============================================================================
  306. void MessageManager::doPlatformSpecificInitialisation()
  307. {
  308. if (juceAppDelegate == 0)
  309. juceAppDelegate = [[JuceAppDelegate alloc] init];
  310. // This launches a dummy thread, which forces Cocoa to initialise NSThreads
  311. // correctly (needed prior to 10.5)
  312. if (! [NSThread isMultiThreaded])
  313. [NSThread detachNewThreadSelector: @selector (dummyMethod)
  314. toTarget: juceAppDelegate
  315. withObject: nil];
  316. initialiseMainMenu();
  317. }
  318. void MessageManager::doPlatformSpecificShutdown()
  319. {
  320. if (juceAppDelegate != 0)
  321. {
  322. [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate];
  323. [[NSNotificationCenter defaultCenter] removeObserver: juceAppDelegate];
  324. // Annoyingly, cancelPerformSelectorsWithTarget can't actually cancel the messages
  325. // sent by performSelectorOnMainThread, so need to manually flush these before quitting..
  326. juceAppDelegate->flushingMessages = true;
  327. if (JUCEApplication::getInstance() != 0) // (must avoid blocking here when running as a plugin)
  328. for (int i = 100; --i >= 0 && numPendingMessages > 0;)
  329. getInstance()->runDispatchLoopUntil (10);
  330. [juceAppDelegate release];
  331. juceAppDelegate = 0;
  332. }
  333. }
  334. bool juce_postMessageToSystemQueue (void* message)
  335. {
  336. atomicIncrement (numPendingMessages);
  337. [juceAppDelegate retain];
  338. [juceAppDelegate performSelectorOnMainThread: @selector (customEvent:)
  339. withObject: (id) [[NSData alloc] initWithBytes: &message
  340. length: (int) sizeof (message)]
  341. waitUntilDone: NO];
  342. return true;
  343. }
  344. void MessageManager::broadcastMessage (const String& value) throw()
  345. {
  346. }
  347. void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
  348. void* data)
  349. {
  350. if (isThisTheMessageThread())
  351. {
  352. return (*callback) (data);
  353. }
  354. else
  355. {
  356. // If a thread has a MessageManagerLock and then tries to call this method, it'll
  357. // deadlock because the message manager is blocked from running, so can never
  358. // call your function..
  359. jassert (! MessageManager::getInstance()->currentThreadHasLockedMessageManager());
  360. const ScopedAutoReleasePool pool;
  361. CallbackMessagePayload cmp;
  362. cmp.function = callback;
  363. cmp.parameter = data;
  364. cmp.result = 0;
  365. cmp.hasBeenExecuted = false;
  366. [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:)
  367. withObject: [NSData dataWithBytesNoCopy: &cmp
  368. length: sizeof (cmp)
  369. freeWhenDone: NO]
  370. waitUntilDone: YES];
  371. return cmp.result;
  372. }
  373. }
  374. #endif