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.

504 lines
15KB

  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. #include "juce_mac_NativeHeaders.h"
  24. #include <Carbon/Carbon.h>
  25. BEGIN_JUCE_NAMESPACE
  26. #include "../../../src/juce_appframework/events/juce_MessageManager.h"
  27. #include "../../../src/juce_appframework/application/juce_Application.h"
  28. #include "../../../src/juce_appframework/gui/components/juce_Desktop.h"
  29. #include "../../../src/juce_core/text/juce_StringArray.h"
  30. #include "../../../src/juce_core/threads/juce_Thread.h"
  31. #include "../../../src/juce_core/misc/juce_PlatformUtilities.h"
  32. #undef Point
  33. extern void juce_HandleProcessFocusChange();
  34. extern void juce_maximiseAllMinimisedWindows();
  35. extern void juce_InvokeMainMenuCommand (const HICommand& command);
  36. extern void juce_MainMenuAboutToBeUsed();
  37. struct CallbackMessagePayload
  38. {
  39. MessageCallbackFunction* function;
  40. void* parameter;
  41. void* volatile result;
  42. bool volatile hasBeenExecuted;
  43. };
  44. END_JUCE_NAMESPACE
  45. #if JUCE_COCOA
  46. NSString* juceMessageName = 0;
  47. @interface JuceAppDelegate : NSObject
  48. id oldDelegate;
  49. - (JuceAppDelegate*) init;
  50. - (void) dealloc;
  51. - (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename;
  52. - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames;
  53. - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app;
  54. - (void) applicationDidBecomeActive: (NSNotification*) aNotification;
  55. - (void) applicationDidResignActive: (NSNotification*) aNotification;
  56. - (void) applicationWillUnhide: (NSNotification*) aNotification;
  57. - (void) customEvent: (NSNotification*) aNotification;
  58. - (void) performCallback: (id) info;
  59. @end
  60. @implementation JuceAppDelegate
  61. - (JuceAppDelegate*) init
  62. {
  63. [super init];
  64. NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  65. if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0)
  66. {
  67. oldDelegate = [NSApp delegate];
  68. [NSApp setDelegate: self];
  69. }
  70. else
  71. {
  72. oldDelegate = 0;
  73. [center addObserver: self selector: @selector (applicationDidResignActive:)
  74. name: NSApplicationDidResignActiveNotification object: NSApp];
  75. [center addObserver: self selector: @selector (applicationDidBecomeActive:)
  76. name: NSApplicationDidBecomeActiveNotification object: NSApp];
  77. [center addObserver: self selector: @selector (applicationWillUnhide:)
  78. name: NSApplicationWillUnhideNotification object: NSApp];
  79. }
  80. [center addObserver: self selector: @selector (customEvent:)
  81. name: juceMessageName object: nil];
  82. return self;
  83. }
  84. - (void) dealloc
  85. {
  86. if (oldDelegate != 0)
  87. [NSApp setDelegate: oldDelegate];
  88. [[NSNotificationCenter defaultCenter] removeObserver: self];
  89. [super dealloc];
  90. }
  91. - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app
  92. {
  93. if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0)
  94. JUCE_NAMESPACE::JUCEApplication::getInstance()->systemRequestedQuit();
  95. return NSTerminateLater;
  96. }
  97. - (BOOL) application: (NSApplication*) app openFile: (NSString*) filename
  98. {
  99. if (JUCE_NAMESPACE::JUCEApplication::getInstance() != 0)
  100. {
  101. JUCE_NAMESPACE::JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename));
  102. return YES;
  103. }
  104. return NO;
  105. }
  106. - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames
  107. {
  108. JUCE_NAMESPACE::StringArray files;
  109. for (int i = 0; i < [filenames count]; ++i)
  110. files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i]));
  111. if (files.size() > 0 && JUCE_NAMESPACE::JUCEApplication::getInstance() != 0)
  112. JUCE_NAMESPACE::JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" ")));
  113. }
  114. - (void) applicationDidBecomeActive: (NSNotification*) aNotification
  115. {
  116. JUCE_NAMESPACE::juce_HandleProcessFocusChange();
  117. }
  118. - (void) applicationDidResignActive: (NSNotification*) aNotification
  119. {
  120. JUCE_NAMESPACE::juce_HandleProcessFocusChange();
  121. }
  122. - (void) applicationWillUnhide: (NSNotification*) aNotification
  123. {
  124. JUCE_NAMESPACE::juce_maximiseAllMinimisedWindows();
  125. }
  126. - (void) customEvent: (NSNotification*) n
  127. {
  128. void* message = 0;
  129. [((NSData*) [n object]) getBytes: &message length: sizeof (message)];
  130. if (message != 0)
  131. JUCE_NAMESPACE::MessageManager::getInstance()->deliverMessage (message);
  132. }
  133. - (void) performCallback: (id) info
  134. {
  135. JUCE_NAMESPACE::CallbackMessagePayload* pl = (JUCE_NAMESPACE::CallbackMessagePayload*) info;
  136. if (pl != 0)
  137. {
  138. pl->result = (*pl->function) (pl->parameter);
  139. pl->hasBeenExecuted = true;
  140. }
  141. }
  142. @end
  143. #endif
  144. BEGIN_JUCE_NAMESPACE
  145. #if JUCE_COCOA
  146. static JuceAppDelegate* juceAppDelegate = 0;
  147. #else
  148. static int kJUCEClass = FOUR_CHAR_CODE ('JUCE');
  149. const int kJUCEKind = 1;
  150. const int kCallbackKind = 2;
  151. static pascal OSStatus EventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
  152. {
  153. void* event = 0;
  154. GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof (void*), 0, &event);
  155. if (event != 0)
  156. MessageManager::getInstance()->deliverMessage (event);
  157. return noErr;
  158. }
  159. static pascal OSStatus CallbackHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
  160. {
  161. CallbackMessagePayload* pl = 0;
  162. GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof(pl), 0, &pl);
  163. if (pl != 0)
  164. {
  165. pl->result = (*pl->function) (pl->parameter);
  166. pl->hasBeenExecuted = true;
  167. }
  168. return noErr;
  169. }
  170. static pascal OSStatus MouseClickHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
  171. {
  172. ::Point where;
  173. GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof(::Point), 0, &where);
  174. WindowRef window;
  175. if (FindWindow (where, &window) == inMenuBar)
  176. {
  177. // turn off the wait cursor before going in here..
  178. const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor();
  179. MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0);
  180. if (Component::getCurrentlyModalComponent() != 0)
  181. Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
  182. juce_MainMenuAboutToBeUsed();
  183. MenuSelect (where);
  184. HiliteMenu (0);
  185. MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor);
  186. return noErr;
  187. }
  188. return eventNotHandledErr;
  189. }
  190. static pascal OSErr QuitAppleEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon)
  191. {
  192. if (JUCEApplication::getInstance() != 0)
  193. JUCEApplication::getInstance()->systemRequestedQuit();
  194. return noErr;
  195. }
  196. static pascal OSErr OpenDocEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon)
  197. {
  198. AEDescList docs;
  199. StringArray files;
  200. if (AEGetParamDesc (appleEvt, keyDirectObject, typeAEList, &docs) == noErr)
  201. {
  202. long num;
  203. if (AECountItems (&docs, &num) == noErr)
  204. {
  205. for (int i = 1; i <= num; ++i)
  206. {
  207. FSRef file;
  208. AEKeyword keyword;
  209. DescType type;
  210. Size size;
  211. if (AEGetNthPtr (&docs, i, typeFSRef, &keyword, &type,
  212. &file, sizeof (file), &size) == noErr)
  213. {
  214. const String path (PlatformUtilities::makePathFromFSRef (&file));
  215. if (path.isNotEmpty())
  216. files.add (path.quoted());
  217. }
  218. }
  219. if (files.size() > 0
  220. && JUCEApplication::getInstance() != 0)
  221. {
  222. JUCE_TRY
  223. {
  224. JUCEApplication::getInstance()
  225. ->anotherInstanceStarted (files.joinIntoString (T(" ")));
  226. }
  227. JUCE_CATCH_ALL
  228. }
  229. }
  230. AEDisposeDesc (&docs);
  231. };
  232. return noErr;
  233. }
  234. static pascal OSStatus AppEventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
  235. {
  236. const UInt32 eventClass = GetEventClass (theEvent);
  237. if (eventClass == kEventClassCommand)
  238. {
  239. HICommand command;
  240. if (GetEventParameter (theEvent, kEventParamHICommand, typeHICommand, 0, sizeof (command), 0, &command) == noErr
  241. || GetEventParameter (theEvent, kEventParamDirectObject, typeHICommand, 0, sizeof (command), 0, &command) == noErr)
  242. {
  243. if (command.commandID == kHICommandQuit)
  244. {
  245. if (JUCEApplication::getInstance() != 0)
  246. JUCEApplication::getInstance()->systemRequestedQuit();
  247. return noErr;
  248. }
  249. else if (command.commandID == kHICommandMaximizeAll
  250. || command.commandID == kHICommandMaximizeWindow
  251. || command.commandID == kHICommandBringAllToFront)
  252. {
  253. juce_maximiseAllMinimisedWindows();
  254. return noErr;
  255. }
  256. else
  257. {
  258. juce_InvokeMainMenuCommand (command);
  259. }
  260. }
  261. }
  262. else if (eventClass == kEventClassApplication)
  263. {
  264. if (GetEventKind (theEvent) == kEventAppFrontSwitched)
  265. {
  266. juce_HandleProcessFocusChange();
  267. }
  268. else if (GetEventKind (theEvent) == kEventAppShown)
  269. {
  270. // this seems to blank the windows, so we need to do a repaint..
  271. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  272. {
  273. Component* const c = Desktop::getInstance().getComponent (i);
  274. if (c != 0)
  275. c->repaint();
  276. }
  277. }
  278. }
  279. return eventNotHandledErr;
  280. }
  281. static EventQueueRef mainQueue;
  282. static EventHandlerRef juceEventHandler = 0;
  283. static EventHandlerRef callbackEventHandler = 0;
  284. #endif
  285. //==============================================================================
  286. void MessageManager::doPlatformSpecificInitialisation()
  287. {
  288. static bool initialised = false;
  289. if (! initialised)
  290. {
  291. initialised = true;
  292. #if JUCE_COCOA
  293. // if we're linking a Juce app to one or more dynamic libraries, we'll need different values
  294. // for this so each module doesn't interfere with the others.
  295. UnsignedWide t;
  296. Microseconds (&t);
  297. kJUCEClass ^= t.lo;
  298. juceMessageName = juceStringToNS ("juce_" + String::toHexString ((int) t.lo));
  299. juceAppDelegate = [[JuceAppDelegate alloc] init];
  300. #else
  301. mainQueue = GetMainEventQueue();
  302. #endif
  303. }
  304. #if ! JUCE_COCOA
  305. const EventTypeSpec type1 = { kJUCEClass, kJUCEKind };
  306. InstallApplicationEventHandler (NewEventHandlerUPP (EventHandlerProc), 1, &type1, 0, &juceEventHandler);
  307. const EventTypeSpec type2 = { kJUCEClass, kCallbackKind };
  308. InstallApplicationEventHandler (NewEventHandlerUPP (CallbackHandlerProc), 1, &type2, 0, &callbackEventHandler);
  309. // only do this stuff if we're running as an application rather than a library..
  310. if (JUCEApplication::getInstance() != 0)
  311. {
  312. const EventTypeSpec type3 = { kEventClassMouse, kEventMouseDown };
  313. InstallApplicationEventHandler (NewEventHandlerUPP (MouseClickHandlerProc), 1, &type3, 0, 0);
  314. const EventTypeSpec type4[] = { { kEventClassApplication, kEventAppShown },
  315. { kEventClassApplication, kEventAppFrontSwitched },
  316. { kEventClassCommand, kEventProcessCommand } };
  317. InstallApplicationEventHandler (NewEventHandlerUPP (AppEventHandlerProc), 3, type4, 0, 0);
  318. AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
  319. NewAEEventHandlerUPP (QuitAppleEventHandler), 0, false);
  320. AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments,
  321. NewAEEventHandlerUPP (OpenDocEventHandler), 0, false);
  322. }
  323. #endif
  324. }
  325. void MessageManager::doPlatformSpecificShutdown()
  326. {
  327. if (juceEventHandler != 0)
  328. {
  329. RemoveEventHandler (juceEventHandler);
  330. juceEventHandler = 0;
  331. }
  332. if (callbackEventHandler != 0)
  333. {
  334. RemoveEventHandler (callbackEventHandler);
  335. callbackEventHandler = 0;
  336. }
  337. }
  338. bool juce_postMessageToSystemQueue (void* message)
  339. {
  340. #if JUCE_COCOA
  341. [[NSNotificationCenter defaultCenter] postNotificationName: juceMessageName
  342. object: [NSData dataWithBytes: &message
  343. length: (int) sizeof (message)]];
  344. return true;
  345. #else
  346. jassert (mainQueue == GetMainEventQueue());
  347. EventRef event;
  348. if (CreateEvent (0, kJUCEClass, kJUCEKind, 0, kEventAttributeUserEvent, &event) == noErr)
  349. {
  350. SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &message);
  351. const bool ok = PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr;
  352. ReleaseEvent (event);
  353. return ok;
  354. }
  355. return false;
  356. #endif
  357. }
  358. void MessageManager::broadcastMessage (const String& value) throw()
  359. {
  360. }
  361. void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
  362. void* data)
  363. {
  364. if (isThisTheMessageThread())
  365. {
  366. return (*callback) (data);
  367. }
  368. else
  369. {
  370. CallbackMessagePayload cmp;
  371. cmp.function = callback;
  372. cmp.parameter = data;
  373. cmp.result = 0;
  374. cmp.hasBeenExecuted = false;
  375. #if JUCE_COCOA
  376. [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:)
  377. withObject: (id) &cmp
  378. waitUntilDone: YES];
  379. return cmp.result;
  380. #else
  381. jassert (mainQueue == GetMainEventQueue());
  382. EventRef event;
  383. if (CreateEvent (0, kJUCEClass, kCallbackKind, 0, kEventAttributeUserEvent, &event) == noErr)
  384. {
  385. void* v = &cmp;
  386. SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &v);
  387. if (PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr)
  388. {
  389. while (! cmp.hasBeenExecuted)
  390. Thread::yield();
  391. return cmp.result;
  392. }
  393. }
  394. return 0;
  395. #endif
  396. }
  397. }
  398. END_JUCE_NAMESPACE