/* ============================================================================== 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. ============================================================================== */ #include "../../juce_core/basics/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE #include "juce_MessageManager.h" #include "juce_ActionListenerList.h" #include "../application/juce_Application.h" #include "../gui/components/juce_Component.h" #include "../../juce_core/threads/juce_Thread.h" #include "../../juce_core/basics/juce_Time.h" //============================================================================== // platform-specific functions.. bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); bool juce_postMessageToSystemQueue (void* message); //============================================================================== MessageManager* MessageManager::instance = 0; static const int quitMessageId = 0xfffff321; MessageManager::MessageManager() throw() : broadcastListeners (0), quitMessagePosted (false), quitMessageReceived (false), useMaximumForceWhenQuitting (true), messageCounter (0), lastMessageCounter (-1), isInMessageDispatcher (0), needToGetRidOfWaitCursor (false), timeBeforeWaitCursor (0), lastActivityCheckOkTime (0) { currentLockingThreadId = messageThreadId = Thread::getCurrentThreadId(); } MessageManager::~MessageManager() throw() { jassert (instance == this); instance = 0; deleteAndZero (broadcastListeners); doPlatformSpecificShutdown(); } MessageManager* MessageManager::getInstance() throw() { if (instance == 0) { instance = new MessageManager(); doPlatformSpecificInitialisation(); instance->setTimeBeforeShowingWaitCursor (500); } return instance; } void MessageManager::postMessageToQueue (Message* const message) { if (quitMessagePosted || ! juce_postMessageToSystemQueue (message)) delete message; } //============================================================================== // not for public use.. void MessageManager::deliverMessage (void* message) { const MessageManagerLock lock; Message* const m = (Message*) message; MessageListener* const recipient = m->messageRecipient; if (messageListeners.contains (recipient)) { JUCE_TRY { recipient->handleMessage (*m); } JUCE_CATCH_EXCEPTION if (needToGetRidOfWaitCursor) { needToGetRidOfWaitCursor = false; MouseCursor::hideWaitCursor(); } ++messageCounter; } else if (recipient == 0 && m->intParameter1 == quitMessageId) { quitMessageReceived = true; useMaximumForceWhenQuitting = (m->intParameter2 != 0); } delete m; } //============================================================================== bool MessageManager::dispatchNextMessage (const bool returnImmediatelyIfNoMessages, bool* const wasAMessageDispatched) { if (quitMessageReceived) { if (wasAMessageDispatched != 0) *wasAMessageDispatched = false; return false; } ++isInMessageDispatcher; bool result = false; JUCE_TRY { result = juce_dispatchNextMessageOnSystemQueue (returnImmediatelyIfNoMessages); if (wasAMessageDispatched != 0) *wasAMessageDispatched = result; if (instance == 0) return false; } JUCE_CATCH_EXCEPTION --isInMessageDispatcher; ++messageCounter; return result || ! returnImmediatelyIfNoMessages; } void MessageManager::dispatchPendingMessages (int maxNumberOfMessagesToDispatch) { jassert (isThisTheMessageThread()); // must only be called by the message thread while (--maxNumberOfMessagesToDispatch >= 0 && ! quitMessageReceived) { ++isInMessageDispatcher; bool carryOn = false; JUCE_TRY { carryOn = juce_dispatchNextMessageOnSystemQueue (true); } JUCE_CATCH_EXCEPTION --isInMessageDispatcher; ++messageCounter; if (! carryOn) break; } } bool MessageManager::runDispatchLoop() { jassert (isThisTheMessageThread()); // must only be called by the message thread while (dispatchNextMessage()) { } return useMaximumForceWhenQuitting; } //============================================================================== void MessageManager::postQuitMessage (const bool useMaximumForce) { if (! quitMessagePosted) { Message* const m = new Message (quitMessageId, (useMaximumForce) ? 1 : 0, 0, 0); m->messageRecipient = 0; if (! juce_postMessageToSystemQueue (m)) delete m; quitMessagePosted = true; } } bool MessageManager::hasQuitMessageBeenPosted() const { return quitMessagePosted; } //============================================================================== void MessageManager::deliverBroadcastMessage (const String& value) { if (broadcastListeners == 0) broadcastListeners = new ActionListenerList(); broadcastListeners->sendActionMessage (value); } void MessageManager::registerBroadcastListener (ActionListener* listener) { if (broadcastListeners == 0) broadcastListeners = new ActionListenerList(); broadcastListeners->addActionListener (listener); } void MessageManager::deregisterBroadcastListener (ActionListener* listener) { if (broadcastListeners == 0) broadcastListeners = new ActionListenerList(); broadcastListeners->removeActionListener (listener); } //============================================================================== // This gets called occasionally by the timer thread (to save using an extra thread // for it). void MessageManager::inactivityCheckCallback() { if (instance != 0) instance->inactivityCheckCallbackInt(); } void MessageManager::inactivityCheckCallbackInt() { const unsigned int now = Time::getApproximateMillisecondCounter(); if (isInMessageDispatcher > 0 && lastMessageCounter == messageCounter && timeBeforeWaitCursor > 0 && lastActivityCheckOkTime > 0 && ! ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) { if (now >= lastActivityCheckOkTime + timeBeforeWaitCursor && ! needToGetRidOfWaitCursor) { // been in the same message call too long.. MouseCursor::showWaitCursor(); needToGetRidOfWaitCursor = true; } } else { lastActivityCheckOkTime = now; lastMessageCounter = messageCounter; } } void MessageManager::delayWaitCursor() { if (instance != 0) { instance->messageCounter++; if (instance->needToGetRidOfWaitCursor) { instance->needToGetRidOfWaitCursor = false; MouseCursor::hideWaitCursor(); } } } void MessageManager::setTimeBeforeShowingWaitCursor (const int millisecs) { // if this is a bit too small you'll get a lot of unwanted hourglass cursors.. jassert (millisecs <= 0 || millisecs > 200); timeBeforeWaitCursor = millisecs; if (millisecs > 0) startTimer (millisecs / 2); // (see timerCallback() for explanation of this) else stopTimer(); } void MessageManager::timerCallback() { // dummy callback - the message manager is just a Timer to ensure that there are always // some events coming in - otherwise it'll show the egg-timer/beachball-of-death. ++messageCounter; } int MessageManager::getTimeBeforeShowingWaitCursor() const { return timeBeforeWaitCursor; } bool MessageManager::isThisTheMessageThread() const { return Thread::getCurrentThreadId() == messageThreadId; } void MessageManager::setCurrentMessageThread (const int threadId) { messageThreadId = threadId; } bool MessageManager::currentThreadHasLockedMessageManager() const { return Thread::getCurrentThreadId() == currentLockingThreadId; } //============================================================================== MessageManagerLock::MessageManagerLock() throw() : locked (true) { if (MessageManager::instance != 0) { MessageManager::instance->messageDispatchLock.enter(); lastLockingThreadId = MessageManager::instance->currentLockingThreadId; MessageManager::instance->currentLockingThreadId = Thread::getCurrentThreadId(); } } MessageManagerLock::MessageManagerLock (Thread* const thread) throw() { jassert (thread != 0); // This will only work if you give it a valid thread! if (MessageManager::instance != 0) { for (;;) { if (MessageManager::instance->messageDispatchLock.tryEnter()) { locked = true; lastLockingThreadId = MessageManager::instance->currentLockingThreadId; MessageManager::instance->currentLockingThreadId = Thread::getCurrentThreadId(); break; } if (thread != 0 && thread->threadShouldExit()) { locked = false; break; } Thread::sleep (1); } } } MessageManagerLock::~MessageManagerLock() throw() { if (locked && MessageManager::instance != 0) { MessageManager::instance->currentLockingThreadId = lastLockingThreadId; MessageManager::instance->messageDispatchLock.exit(); } } END_JUCE_NAMESPACE