| @@ -45,10 +45,7 @@ public: | |||||
| void initialise (const String& commandLine) | void initialise (const String& commandLine) | ||||
| { | { | ||||
| LookAndFeel::setDefaultLookAndFeel (&lookAndFeel); | LookAndFeel::setDefaultLookAndFeel (&lookAndFeel); | ||||
| settings = new StoredSettings(); | settings = new StoredSettings(); | ||||
| icons = new Icons(); | |||||
| settings->initialise(); | settings->initialise(); | ||||
| if (commandLine.isNotEmpty()) | if (commandLine.isNotEmpty()) | ||||
| @@ -63,6 +60,15 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| if (sendCommandLineToPreexistingInstance()) | |||||
| { | |||||
| DBG ("Another instance is running - quitting..."); | |||||
| quit(); | |||||
| return; | |||||
| } | |||||
| icons = new Icons(); | |||||
| commandManager = new ApplicationCommandManager(); | commandManager = new ApplicationCommandManager(); | ||||
| commandManager->registerAllCommandsForTarget (this); | commandManager->registerAllCommandsForTarget (this); | ||||
| @@ -131,11 +137,7 @@ public: | |||||
| bool moreThanOneInstanceAllowed() | bool moreThanOneInstanceAllowed() | ||||
| { | { | ||||
| #ifndef JUCE_LINUX | |||||
| return false; | |||||
| #else | |||||
| return true; //xxx should be false but doesn't work on linux.. | |||||
| #endif | |||||
| return true; // this is handled manually in initialise() | |||||
| } | } | ||||
| void anotherInstanceStarted (const String& commandLine) | void anotherInstanceStarted (const String& commandLine) | ||||
| @@ -31,6 +31,13 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| namespace | namespace | ||||
| { | { | ||||
| void hideDockIcon() | |||||
| { | |||||
| #if JUCE_MAC | |||||
| Process::setDockIconVisible (false); | |||||
| #endif | |||||
| } | |||||
| File getFile (const String& filename) | File getFile (const String& filename) | ||||
| { | { | ||||
| return File::getCurrentWorkingDirectory().getChildFile (filename.unquoted()); | return File::getCurrentWorkingDirectory().getChildFile (filename.unquoted()); | ||||
| @@ -53,6 +60,8 @@ namespace | |||||
| */ | */ | ||||
| int resaveProject (const StringArray& args, bool justSaveResources) | int resaveProject (const StringArray& args, bool justSaveResources) | ||||
| { | { | ||||
| hideDockIcon(); | |||||
| if (! checkArgumentCount (args, 2)) | if (! checkArgumentCount (args, 2)) | ||||
| return 1; | return 1; | ||||
| @@ -145,6 +154,8 @@ namespace | |||||
| int buildModules (const StringArray& args, const bool buildAllWithIndex) | int buildModules (const StringArray& args, const bool buildAllWithIndex) | ||||
| { | { | ||||
| hideDockIcon(); | |||||
| if (! checkArgumentCount (args, 3)) | if (! checkArgumentCount (args, 3)) | ||||
| return 1; | return 1; | ||||
| @@ -200,6 +211,8 @@ namespace | |||||
| int listModules() | int listModules() | ||||
| { | { | ||||
| hideDockIcon(); | |||||
| std::cout << "Downloading list of available modules..." << std::endl; | std::cout << "Downloading list of available modules..." << std::endl; | ||||
| ModuleList list; | ModuleList list; | ||||
| list.loadFromWebsite(); | list.loadFromWebsite(); | ||||
| @@ -216,6 +229,8 @@ namespace | |||||
| int showStatus (const StringArray& args) | int showStatus (const StringArray& args) | ||||
| { | { | ||||
| hideDockIcon(); | |||||
| if (! checkArgumentCount (args, 2)) | if (! checkArgumentCount (args, 2)) | ||||
| return 1; | return 1; | ||||
| @@ -256,6 +271,8 @@ namespace | |||||
| //============================================================================== | //============================================================================== | ||||
| int showHelp() | int showHelp() | ||||
| { | { | ||||
| hideDockIcon(); | |||||
| std::cout << "The Introjucer!" << std::endl | std::cout << "The Introjucer!" << std::endl | ||||
| << std::endl | << std::endl | ||||
| << "Usage: " << std::endl | << "Usage: " << std::endl | ||||
| @@ -41,13 +41,11 @@ class JUCE_API InterProcessLock | |||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a lock object. | /** Creates a lock object. | ||||
| @param name a name that processes will use to identify this lock object | |||||
| @param name a name that processes will use to identify this lock object | |||||
| */ | */ | ||||
| explicit InterProcessLock (const String& name); | explicit InterProcessLock (const String& name); | ||||
| /** Destructor. | /** Destructor. | ||||
| This will also release the lock if it's currently held by this process. | This will also release the lock if it's currently held by this process. | ||||
| */ | */ | ||||
| ~InterProcessLock(); | ~InterProcessLock(); | ||||
| @@ -55,17 +53,15 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Attempts to lock the critical section. | /** Attempts to lock the critical section. | ||||
| @param timeOutMillisecs how many milliseconds to wait if the lock | |||||
| is already held by another process - a value of | |||||
| 0 will return immediately, negative values will wait | |||||
| forever | |||||
| @param timeOutMillisecs how many milliseconds to wait if the lock is already | |||||
| held by another process - a value of 0 will return | |||||
| immediately, negative values will wait forever | |||||
| @returns true if the lock could be gained within the timeout period, or | @returns true if the lock could be gained within the timeout period, or | ||||
| false if the timeout expired. | false if the timeout expired. | ||||
| */ | */ | ||||
| bool enter (int timeOutMillisecs = -1); | bool enter (int timeOutMillisecs = -1); | ||||
| /** Releases the lock if it's currently held by this process. | |||||
| */ | |||||
| /** Releases the lock if it's currently held by this process. */ | |||||
| void exit(); | void exit(); | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -94,7 +90,7 @@ public: | |||||
| otherwise there are no guarantees what will happen! Best just to use it | otherwise there are no guarantees what will happen! Best just to use it | ||||
| as a local stack object, rather than creating one with the new() operator. | as a local stack object, rather than creating one with the new() operator. | ||||
| */ | */ | ||||
| explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lockWasSuccessful = lock.enter(); } | |||||
| explicit ScopedLockType (InterProcessLock& lock) : ipLock (lock) { lockWasSuccessful = lock.enter(); } | |||||
| /** Destructor. | /** Destructor. | ||||
| @@ -103,14 +99,14 @@ public: | |||||
| Make sure this object is created and deleted by the same thread, | Make sure this object is created and deleted by the same thread, | ||||
| otherwise there are no guarantees what will happen! | otherwise there are no guarantees what will happen! | ||||
| */ | */ | ||||
| inline ~ScopedLockType() { lock_.exit(); } | |||||
| inline ~ScopedLockType() { ipLock.exit(); } | |||||
| /** Returns true if the InterProcessLock was successfully locked. */ | /** Returns true if the InterProcessLock was successfully locked. */ | ||||
| bool isLocked() const noexcept { return lockWasSuccessful; } | |||||
| bool isLocked() const noexcept { return lockWasSuccessful; } | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| InterProcessLock& lock_; | |||||
| InterProcessLock& ipLock; | |||||
| bool lockWasSuccessful; | bool lockWasSuccessful; | ||||
| JUCE_DECLARE_NON_COPYABLE (ScopedLockType); | JUCE_DECLARE_NON_COPYABLE (ScopedLockType); | ||||
| @@ -27,21 +27,54 @@ | |||||
| extern void juce_initialiseMacMainMenu(); | extern void juce_initialiseMacMainMenu(); | ||||
| #endif | #endif | ||||
| #if ! (JUCE_IOS || JUCE_ANDROID) | |||||
| #define JUCE_HANDLE_MULTIPLE_INSTANCES 1 | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| class AppBroadcastCallback : public ActionListener | |||||
| #if JUCE_HANDLE_MULTIPLE_INSTANCES | |||||
| struct JUCEApplication::MultipleInstanceHandler : public ActionListener | |||||
| { | { | ||||
| public: | public: | ||||
| AppBroadcastCallback() { MessageManager::getInstance()->registerBroadcastListener (this); } | |||||
| ~AppBroadcastCallback() { MessageManager::getInstance()->deregisterBroadcastListener (this); } | |||||
| MultipleInstanceHandler (const String& appName) | |||||
| : appLock ("juceAppLock_" + appName) | |||||
| { | |||||
| } | |||||
| bool sendCommandLineToPreexistingInstance() | |||||
| { | |||||
| if (appLock.enter (0)) | |||||
| return false; | |||||
| JUCEApplication* const app = JUCEApplication::getInstance(); | |||||
| jassert (app != nullptr); | |||||
| MessageManager::broadcastMessage (app->getApplicationName() | |||||
| + "/" + app->getCommandLineParameters()); | |||||
| return true; | |||||
| } | |||||
| void actionListenerCallback (const String& message) | void actionListenerCallback (const String& message) | ||||
| { | { | ||||
| JUCEApplication* const app = JUCEApplication::getInstance(); | JUCEApplication* const app = JUCEApplication::getInstance(); | ||||
| if (app != 0 && message.startsWith (app->getApplicationName() + "/")) | |||||
| app->anotherInstanceStarted (message.substring (app->getApplicationName().length() + 1)); | |||||
| if (app != nullptr) | |||||
| { | |||||
| const String appName (app->getApplicationName()); | |||||
| if (message.startsWith (appName + "/")) | |||||
| app->anotherInstanceStarted (message.substring (appName.length() + 1)); | |||||
| } | |||||
| } | } | ||||
| private: | |||||
| InterProcessLock appLock; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultipleInstanceHandler); | |||||
| }; | }; | ||||
| #else | |||||
| struct JUCEApplication::MultipleInstanceHandler {}; | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| JUCEApplication::JUCEApplication() | JUCEApplication::JUCEApplication() | ||||
| @@ -52,11 +85,6 @@ JUCEApplication::JUCEApplication() | |||||
| JUCEApplication::~JUCEApplication() | JUCEApplication::~JUCEApplication() | ||||
| { | { | ||||
| if (appLock != nullptr) | |||||
| { | |||||
| appLock->exit(); | |||||
| appLock = nullptr; | |||||
| } | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -82,9 +110,7 @@ void JUCEApplication::setApplicationReturnValue (const int newReturnValue) noexc | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void JUCEApplication::unhandledException (const std::exception*, | |||||
| const String&, | |||||
| const int) | |||||
| void JUCEApplication::unhandledException (const std::exception*, const String&, int) | |||||
| { | { | ||||
| jassertfalse; | jassertfalse; | ||||
| } | } | ||||
| @@ -132,39 +158,44 @@ bool JUCEApplication::perform (const InvocationInfo& info) | |||||
| return false; | return false; | ||||
| } | } | ||||
| #if JUCE_HANDLE_MULTIPLE_INSTANCES | |||||
| bool JUCEApplication::sendCommandLineToPreexistingInstance() | |||||
| { | |||||
| jassert (multipleInstanceHandler == nullptr); // this must only be called once! | |||||
| multipleInstanceHandler = new MultipleInstanceHandler (getApplicationName()); | |||||
| return multipleInstanceHandler->sendCommandLineToPreexistingInstance(); | |||||
| } | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| bool JUCEApplication::initialiseApp() | bool JUCEApplication::initialiseApp() | ||||
| { | { | ||||
| #if ! (JUCE_IOS || JUCE_ANDROID) | |||||
| jassert (appLock == nullptr); // initialiseApp must only be called once! | |||||
| if (! moreThanOneInstanceAllowed()) | |||||
| #if JUCE_HANDLE_MULTIPLE_INSTANCES | |||||
| if ((! moreThanOneInstanceAllowed()) && sendCommandLineToPreexistingInstance()) | |||||
| { | { | ||||
| appLock = new InterProcessLock ("juceAppLock_" + getApplicationName()); | |||||
| if (! appLock->enter(0)) | |||||
| { | |||||
| appLock = nullptr; | |||||
| MessageManager::broadcastMessage (getApplicationName() + "/" + getCommandLineParameters()); | |||||
| DBG ("Another instance is running - quitting..."); | |||||
| return false; | |||||
| } | |||||
| DBG ("Another instance is running - quitting..."); | |||||
| return false; | |||||
| } | } | ||||
| #endif | #endif | ||||
| // let the app do its setting-up.. | // let the app do its setting-up.. | ||||
| initialise (getCommandLineParameters()); | initialise (getCommandLineParameters()); | ||||
| #if JUCE_MAC | |||||
| juce_initialiseMacMainMenu(); // needs to be called after the app object has created, to get its name | |||||
| #endif | |||||
| stillInitialising = false; | |||||
| #if ! (JUCE_IOS || JUCE_ANDROID) | |||||
| broadcastCallback = new AppBroadcastCallback(); | |||||
| #endif | |||||
| if (! MessageManager::getInstance()->hasStopMessageBeenSent()) | |||||
| { | |||||
| #if JUCE_MAC | |||||
| juce_initialiseMacMainMenu(); // (needs to get the app's name) | |||||
| #endif | |||||
| #if JUCE_HANDLE_MULTIPLE_INSTANCES | |||||
| if (multipleInstanceHandler != nullptr) | |||||
| MessageManager::getInstance()->registerBroadcastListener (multipleInstanceHandler); | |||||
| #endif | |||||
| } | |||||
| stillInitialising = false; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -172,7 +203,10 @@ int JUCEApplication::shutdownApp() | |||||
| { | { | ||||
| jassert (JUCEApplicationBase::getInstance() == this); | jassert (JUCEApplicationBase::getInstance() == this); | ||||
| broadcastCallback = nullptr; | |||||
| #if JUCE_HANDLE_MULTIPLE_INSTANCES | |||||
| if (multipleInstanceHandler != nullptr) | |||||
| MessageManager::getInstance()->deregisterBroadcastListener (multipleInstanceHandler); | |||||
| #endif | |||||
| JUCE_TRY | JUCE_TRY | ||||
| { | { | ||||
| @@ -181,6 +215,7 @@ int JUCEApplication::shutdownApp() | |||||
| } | } | ||||
| JUCE_CATCH_EXCEPTION | JUCE_CATCH_EXCEPTION | ||||
| multipleInstanceHandler = nullptr; | |||||
| return getApplicationReturnValue(); | return getApplicationReturnValue(); | ||||
| } | } | ||||
| @@ -246,12 +246,18 @@ public: | |||||
| static void sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); | static void sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); | ||||
| bool initialiseApp(); | bool initialiseApp(); | ||||
| int shutdownApp(); | int shutdownApp(); | ||||
| protected: | |||||
| bool sendCommandLineToPreexistingInstance(); | |||||
| #endif | #endif | ||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| ScopedPointer<InterProcessLock> appLock; | |||||
| ScopedPointer<ActionListener> broadcastCallback; | |||||
| struct MultipleInstanceHandler; | |||||
| friend struct MultipleInstanceHandler; | |||||
| friend class ScopedPointer<MultipleInstanceHandler>; | |||||
| ScopedPointer<MultipleInstanceHandler> multipleInstanceHandler; | |||||
| int appReturnValue; | int appReturnValue; | ||||
| bool stillInitialising; | bool stillInitialising; | ||||
| @@ -103,7 +103,6 @@ public: | |||||
| styleMask: getNSWindowStyleMask (windowStyleFlags) | styleMask: getNSWindowStyleMask (windowStyleFlags) | ||||
| backing: NSBackingStoreBuffered | backing: NSBackingStoreBuffered | ||||
| defer: YES]; | defer: YES]; | ||||
| setOwner (window, this); | setOwner (window, this); | ||||
| [window orderOut: nil]; | [window orderOut: nil]; | ||||
| #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 | #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 | ||||
| @@ -132,6 +131,9 @@ public: | |||||
| #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) | #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) | ||||
| if ((windowStyleFlags & (windowHasMaximiseButton | windowHasTitleBar)) == (windowHasMaximiseButton | windowHasTitleBar)) | if ((windowStyleFlags & (windowHasMaximiseButton | windowHasTitleBar)) == (windowHasMaximiseButton | windowHasTitleBar)) | ||||
| [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; | [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary]; | ||||
| if ([window respondsToSelector: @selector (setRestorable:)]) | |||||
| [window setRestorable: NO]; | |||||
| #endif | #endif | ||||
| } | } | ||||