|
- /*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-7 by Raw Material Software ltd.
-
- ------------------------------------------------------------------------------
-
- JUCE can be redistributed and/or modified under the terms of the
- GNU General Public License, as published by the Free Software Foundation;
- either version 2 of the License, or (at your option) any later version.
-
- JUCE is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with JUCE; if not, visit www.gnu.org/licenses or write to the
- Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- Boston, MA 02111-1307 USA
-
- ------------------------------------------------------------------------------
-
- If you'd like to release a closed-source product which uses JUCE, commercial
- licenses are also available: visit www.rawmaterialsoftware.com/juce for
- more information.
-
- ==============================================================================
- */
-
- // (This file gets included by juce_mac_NativeCode.mm, rather than being
- // compiled on its own).
- #ifdef JUCE_INCLUDED_FILE
-
- struct CallbackMessagePayload
- {
- MessageCallbackFunction* function;
- void* parameter;
- void* volatile result;
- bool volatile hasBeenExecuted;
- };
-
- /* When you use multiple DLLs which share similarly-named obj-c classes - like
- for example having more than one juce plugin loaded into a host, then when a
- method is called, the actual code that runs might actually be in a different module
- than the one you expect... So any calls to library functions or statics that are
- made inside obj-c methods will probably end up getting executed in a different DLL's
- memory space. Not a great thing to happen - this obviously leads to bizarre crashes.
-
- To work around this insanity, I'm only allowing obj-c methods to make calls to
- virtual methods of an object that's known to live inside the right module's space.
- */
- class AppDelegateRedirector
- {
- public:
- AppDelegateRedirector() {}
- virtual ~AppDelegateRedirector() {}
-
- virtual NSApplicationTerminateReply shouldTerminate()
- {
- if (JUCEApplication::getInstance() != 0)
- {
- JUCEApplication::getInstance()->systemRequestedQuit();
- return NSTerminateCancel;
- }
-
- return NSTerminateNow;
- }
-
- virtual BOOL openFile (const NSString* filename)
- {
- if (JUCEApplication::getInstance() != 0)
- {
- JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename));
- return YES;
- }
-
- return NO;
- }
-
- virtual void openFiles (NSArray* filenames)
- {
- StringArray files;
- for (unsigned int i = 0; i < [filenames count]; ++i)
- files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i]));
-
- if (files.size() > 0 && JUCEApplication::getInstance() != 0)
- {
- JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" ")));
- }
- }
-
- virtual void focusChanged()
- {
- juce_HandleProcessFocusChange();
- }
-
- virtual void deliverMessage (void* message)
- {
- // no need for an mm lock here - deliverMessage locks it
- MessageManager::getInstance()->deliverMessage (message);
- }
-
- virtual void performCallback (CallbackMessagePayload* pl)
- {
- pl->result = (*pl->function) (pl->parameter);
- pl->hasBeenExecuted = true;
- }
-
- virtual void deleteSelf()
- {
- delete this;
- }
- };
-
- END_JUCE_NAMESPACE
- using namespace JUCE_NAMESPACE;
-
- #define JuceAppDelegate MakeObjCClassName(JuceAppDelegate)
-
- static int numPendingMessages = 0;
- static bool flushingMessages = false;
-
- @interface JuceAppDelegate : NSObject
- {
- @private
- id oldDelegate;
- AppDelegateRedirector* redirector;
- }
-
- - (JuceAppDelegate*) init;
- - (void) dealloc;
- - (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename;
- - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames;
- - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app;
- - (void) applicationDidBecomeActive: (NSNotification*) aNotification;
- - (void) applicationDidResignActive: (NSNotification*) aNotification;
- - (void) applicationWillUnhide: (NSNotification*) aNotification;
- - (void) customEvent: (id) data;
- - (void) performCallback: (id) info;
- - (void) dummyMethod;
- @end
-
- @implementation JuceAppDelegate
-
- - (JuceAppDelegate*) init
- {
- [super init];
-
- redirector = new AppDelegateRedirector();
- numPendingMessages = 0;
- flushingMessages = false;
-
- NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
-
- if (JUCEApplication::getInstance() != 0)
- {
- oldDelegate = [NSApp delegate];
- [NSApp setDelegate: self];
- }
- else
- {
- oldDelegate = 0;
- [center addObserver: self selector: @selector (applicationDidResignActive:)
- name: NSApplicationDidResignActiveNotification object: NSApp];
-
- [center addObserver: self selector: @selector (applicationDidBecomeActive:)
- name: NSApplicationDidBecomeActiveNotification object: NSApp];
-
- [center addObserver: self selector: @selector (applicationWillUnhide:)
- name: NSApplicationWillUnhideNotification object: NSApp];
-
- }
-
- return self;
- }
-
- - (void) dealloc
- {
- if (oldDelegate != 0)
- [NSApp setDelegate: oldDelegate];
-
- redirector->deleteSelf();
- [super dealloc];
- }
-
- - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app
- {
- return redirector->shouldTerminate();
- }
-
- - (BOOL) application: (NSApplication*) app openFile: (NSString*) filename
- {
- return redirector->openFile (filename);
- }
-
- - (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames
- {
- return redirector->openFiles (filenames);
- }
-
- - (void) applicationDidBecomeActive: (NSNotification*) aNotification
- {
- redirector->focusChanged();
- }
-
- - (void) applicationDidResignActive: (NSNotification*) aNotification
- {
- redirector->focusChanged();
- }
-
- - (void) applicationWillUnhide: (NSNotification*) aNotification
- {
- redirector->focusChanged();
- }
-
- - (void) customEvent: (id) n
- {
- atomicDecrement (numPendingMessages);
-
- NSData* data = (NSData*) n;
- void* message = 0;
- [data getBytes: &message length: sizeof (message)];
-
- if (message != 0 && ! flushingMessages)
- redirector->deliverMessage (message);
-
- [data release];
- }
-
- - (void) performCallback: (id) info
- {
- if ([info isKindOfClass: [NSData class]])
- {
- CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes];
-
- if (pl != 0)
- redirector->performCallback (pl);
- }
- else
- {
- jassertfalse // should never get here!
- }
- }
-
- - (void) dummyMethod {} // (used as a way of running a dummy thread)
-
- @end
-
- BEGIN_JUCE_NAMESPACE
-
- static JuceAppDelegate* juceAppDelegate = 0;
-
- void MessageManager::runDispatchLoop()
- {
- if (! quitMessagePosted) // check that the quit message wasn't already posted..
- {
- const ScopedAutoReleasePool pool;
-
- // must only be called by the message thread!
- jassert (isThisTheMessageThread());
-
- [NSApp run];
- }
- }
-
- void MessageManager::stopDispatchLoop()
- {
- quitMessagePosted = true;
- [NSApp stop: nil];
- }
-
- bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
- {
- const ScopedAutoReleasePool pool;
- jassert (isThisTheMessageThread()); // must only be called by the message thread
-
- uint32 endTime = Time::getMillisecondCounter() + millisecondsToRunFor;
- NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001];
-
- while (Time::getMillisecondCounter() < endTime)
- {
- const ScopedAutoReleasePool pool;
-
- [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
- beforeDate: endDate];
-
- NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask
- untilDate: endDate
- inMode: NSDefaultRunLoopMode
- dequeue: YES];
- [NSApp sendEvent: e];
- }
-
- return ! quitMessagePosted;
- }
-
- //==============================================================================
- void MessageManager::doPlatformSpecificInitialisation()
- {
- if (juceAppDelegate == 0)
- juceAppDelegate = [[JuceAppDelegate alloc] init];
-
- // This launches a dummy thread, which forces Cocoa to initialise NSThreads
- // correctly (needed prior to 10.5)
- if (! [NSThread isMultiThreaded])
- [NSThread detachNewThreadSelector: @selector (dummyMethod)
- toTarget: juceAppDelegate
- withObject: nil];
-
- initialiseMainMenu();
- }
-
- void MessageManager::doPlatformSpecificShutdown()
- {
- [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate];
- [[NSNotificationCenter defaultCenter] removeObserver: juceAppDelegate];
-
- // Annoyingly, cancelPerformSelectorsWithTarget can't actually cancel the messages
- // sent by performSelectorOnMainThread, so need to manually flush these before quitting..
- for (int i = 100; --i >= 0 && numPendingMessages > 0;)
- {
- flushingMessages = true;
- getInstance()->runDispatchLoopUntil (10);
- }
-
- jassert (numPendingMessages == 0); // failed to get all the pending messages cleared before quitting..
-
- [juceAppDelegate release];
- juceAppDelegate = 0;
- }
-
- bool juce_postMessageToSystemQueue (void* message)
- {
- atomicIncrement (numPendingMessages);
-
- [juceAppDelegate performSelectorOnMainThread: @selector (customEvent:)
- withObject: (id) [[NSData alloc] initWithBytes: &message
- length: (int) sizeof (message)]
- waitUntilDone: NO];
- return true;
- }
-
- void MessageManager::broadcastMessage (const String& value) throw()
- {
- }
-
- void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
- void* data)
- {
- if (isThisTheMessageThread())
- {
- return (*callback) (data);
- }
- else
- {
- // If a thread has a MessageManagerLock and then tries to call this method, it'll
- // deadlock because the message manager is blocked from running, so can never
- // call your function..
- jassert (! MessageManager::getInstance()->currentThreadHasLockedMessageManager());
-
- const ScopedAutoReleasePool pool;
-
- CallbackMessagePayload cmp;
- cmp.function = callback;
- cmp.parameter = data;
- cmp.result = 0;
- cmp.hasBeenExecuted = false;
-
- [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:)
- withObject: [NSData dataWithBytesNoCopy: &cmp
- length: sizeof (cmp)
- freeWhenDone: NO]
- waitUntilDone: YES];
-
- return cmp.result;
- }
- }
-
- #endif
|