Browse Source

tags/2021-05-28
jules 18 years ago
parent
commit
48c36f7755
4 changed files with 2206 additions and 2190 deletions
  1. +344
    -342
      build/macosx/platform_specific_code/juce_mac_Messaging.cpp
  2. +1092
    -1085
      extras/audio plugins/wrapper/formats/RTAS/juce_RTASWrapper.cpp
  3. +373
    -373
      src/juce_appframework/events/juce_MessageManager.cpp
  4. +397
    -390
      src/juce_appframework/events/juce_Timer.cpp

+ 344
- 342
build/macosx/platform_specific_code/juce_mac_Messaging.cpp View File

@@ -1,342 +1,344 @@
/*
==============================================================================
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 "../../../src/juce_core/basics/juce_StandardHeader.h"
#include <Carbon/Carbon.h>
BEGIN_JUCE_NAMESPACE
#include "../../../src/juce_appframework/events/juce_MessageManager.h"
#include "../../../src/juce_appframework/application/juce_Application.h"
#include "../../../src/juce_appframework/gui/components/juce_Desktop.h"
#include "../../../src/juce_core/text/juce_StringArray.h"
#include "../../../src/juce_core/threads/juce_Thread.h"
#include "../../../src/juce_core/misc/juce_PlatformUtilities.h"
#undef Point
static int kJUCEClass = FOUR_CHAR_CODE ('JUCE');
const int kJUCEKind = 1;
const int kCallbackKind = 2;
extern void juce_HandleProcessFocusChange();
extern void juce_maximiseAllMinimisedWindows();
extern void juce_InvokeMainMenuCommand (const HICommand& command);
extern void juce_MainMenuAboutToBeUsed();
static pascal OSStatus EventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
{
void* event = 0;
GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof (void*), 0, &event);
if (event != 0)
MessageManager::getInstance()->deliverMessage (event);
return noErr;
}
struct CallbackMessagePayload
{
MessageCallbackFunction* function;
void* parameter;
void* volatile result;
bool volatile hasBeenExecuted;
};
static pascal OSStatus CallbackHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
{
CallbackMessagePayload* pl = 0;
GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof(pl), 0, &pl);
if (pl != 0)
{
pl->result = (*pl->function) (pl->parameter);
pl->hasBeenExecuted = true;
}
return noErr;
}
static pascal OSStatus MouseClickHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
{
::Point where;
GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof(::Point), 0, &where);
WindowRef window;
if (FindWindow (where, &window) == inMenuBar)
{
// turn off the wait cursor before going in here..
const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor();
MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0);
if (Component::getCurrentlyModalComponent() != 0)
Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
juce_MainMenuAboutToBeUsed();
MenuSelect (where);
HiliteMenu (0);
MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor);
return noErr;
}
return eventNotHandledErr;
}
static pascal OSErr QuitAppleEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon)
{
if (JUCEApplication::getInstance() != 0)
JUCEApplication::getInstance()->systemRequestedQuit();
return noErr;
}
static pascal OSErr OpenDocEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon)
{
AEDescList docs;
StringArray files;
if (AEGetParamDesc (appleEvt, keyDirectObject, typeAEList, &docs) == noErr)
{
long num;
if (AECountItems (&docs, &num) == noErr)
{
for (int i = 1; i <= num; ++i)
{
FSRef file;
AEKeyword keyword;
DescType type;
Size size;
if (AEGetNthPtr (&docs, i, typeFSRef, &keyword, &type,
&file, sizeof (file), &size) == noErr)
{
const String path (PlatformUtilities::makePathFromFSRef (&file));
if (path.isNotEmpty())
files.add (path.quoted());
}
}
if (files.size() > 0
&& JUCEApplication::getInstance() != 0)
{
JUCE_TRY
{
JUCEApplication::getInstance()
->anotherInstanceStarted (files.joinIntoString (T(" ")));
}
JUCE_CATCH_ALL
}
}
AEDisposeDesc (&docs);
};
return noErr;
}
static pascal OSStatus AppEventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
{
const UInt32 eventClass = GetEventClass (theEvent);
if (eventClass == kEventClassCommand)
{
HICommand command;
if (GetEventParameter (theEvent, kEventParamHICommand, typeHICommand, 0, sizeof (command), 0, &command) == noErr
|| GetEventParameter (theEvent, kEventParamDirectObject, typeHICommand, 0, sizeof (command), 0, &command) == noErr)
{
if (command.commandID == kHICommandQuit)
{
if (JUCEApplication::getInstance() != 0)
JUCEApplication::getInstance()->systemRequestedQuit();
return noErr;
}
else if (command.commandID == kHICommandMaximizeAll
|| command.commandID == kHICommandMaximizeWindow
|| command.commandID == kHICommandBringAllToFront)
{
juce_maximiseAllMinimisedWindows();
return noErr;
}
else
{
juce_InvokeMainMenuCommand (command);
}
}
}
else if (eventClass == kEventClassApplication)
{
if (GetEventKind (theEvent) == kEventAppFrontSwitched)
{
juce_HandleProcessFocusChange();
}
else if (GetEventKind (theEvent) == kEventAppShown)
{
// this seems to blank the windows, so we need to do a repaint..
for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
{
Component* const c = Desktop::getInstance().getComponent (i);
if (c != 0)
c->repaint();
}
}
}
return eventNotHandledErr;
}
static EventQueueRef mainQueue;
static EventHandlerRef juceEventHandler = 0;
static EventHandlerRef callbackEventHandler = 0;
//==============================================================================
void MessageManager::doPlatformSpecificInitialisation()
{
static bool initialised = false;
if (! initialised)
{
initialised = true;
// work-around for a bug in MacOS 10.2..
ProcessSerialNumber junkPSN;
(void) GetCurrentProcess (&junkPSN);
mainQueue = GetMainEventQueue();
// if we're linking a Juce app to one or more dynamic libraries, we'll need different values
// for this so each module doesn't interfere with the others.
UnsignedWide t;
Microseconds (&t);
kJUCEClass ^= t.lo;
}
const EventTypeSpec type1 = { kJUCEClass, kJUCEKind };
InstallApplicationEventHandler (NewEventHandlerUPP (EventHandlerProc), 1, &type1, 0, &juceEventHandler);
const EventTypeSpec type2 = { kJUCEClass, kCallbackKind };
InstallApplicationEventHandler (NewEventHandlerUPP (CallbackHandlerProc), 1, &type2, 0, &callbackEventHandler);
// only do this stuff if we're running as an application rather than a library..
if (JUCEApplication::getInstance() != 0)
{
const EventTypeSpec type3 = { kEventClassMouse, kEventMouseDown };
InstallApplicationEventHandler (NewEventHandlerUPP (MouseClickHandlerProc), 1, &type3, 0, 0);
const EventTypeSpec type4[] = { { kEventClassApplication, kEventAppShown },
{ kEventClassApplication, kEventAppFrontSwitched },
{ kEventClassCommand, kEventProcessCommand } };
InstallApplicationEventHandler (NewEventHandlerUPP (AppEventHandlerProc), 3, type4, 0, 0);
AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
NewAEEventHandlerUPP (QuitAppleEventHandler), 0, false);
AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments,
NewAEEventHandlerUPP (OpenDocEventHandler), 0, false);
}
}
void MessageManager::doPlatformSpecificShutdown()
{
if (juceEventHandler != 0)
{
RemoveEventHandler (juceEventHandler);
juceEventHandler = 0;
}
if (callbackEventHandler != 0)
{
RemoveEventHandler (callbackEventHandler);
callbackEventHandler = 0;
}
}
bool juce_postMessageToSystemQueue (void* message)
{
jassert (mainQueue == GetMainEventQueue());
EventRef event;
if (CreateEvent (0, kJUCEClass, kJUCEKind, 0, kEventAttributeUserEvent, &event) == noErr)
{
SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &message);
const bool ok = PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr;
ReleaseEvent (event);
return ok;
}
return false;
}
void MessageManager::broadcastMessage (const String& value) throw()
{
}
void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
void* data)
{
if (isThisTheMessageThread())
{
return (*callback) (data);
}
else
{
jassert (mainQueue == GetMainEventQueue());
CallbackMessagePayload cmp;
cmp.function = callback;
cmp.parameter = data;
cmp.result = 0;
cmp.hasBeenExecuted = false;
EventRef event;
if (CreateEvent (0, kJUCEClass, kCallbackKind, 0, kEventAttributeUserEvent, &event) == noErr)
{
void* v = &cmp;
SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &v);
if (PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr)
{
while (! cmp.hasBeenExecuted)
Thread::yield();
return cmp.result;
}
}
return 0;
}
}
END_JUCE_NAMESPACE
/*
==============================================================================

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 "../../../src/juce_core/basics/juce_StandardHeader.h"
#include <Carbon/Carbon.h>

BEGIN_JUCE_NAMESPACE

#include "../../../src/juce_appframework/events/juce_MessageManager.h"
#include "../../../src/juce_appframework/application/juce_Application.h"
#include "../../../src/juce_appframework/gui/components/juce_Desktop.h"
#include "../../../src/juce_core/text/juce_StringArray.h"
#include "../../../src/juce_core/threads/juce_Thread.h"
#include "../../../src/juce_core/misc/juce_PlatformUtilities.h"

#undef Point

static int kJUCEClass = FOUR_CHAR_CODE ('JUCE');
const int kJUCEKind = 1;
const int kCallbackKind = 2;

extern void juce_HandleProcessFocusChange();
extern void juce_maximiseAllMinimisedWindows();
extern void juce_InvokeMainMenuCommand (const HICommand& command);
extern void juce_MainMenuAboutToBeUsed();

static pascal OSStatus EventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
{
void* event = 0;
GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof (void*), 0, &event);

if (event != 0)
MessageManager::getInstance()->deliverMessage (event);

return noErr;
}

struct CallbackMessagePayload
{
MessageCallbackFunction* function;
void* parameter;
void* volatile result;
bool volatile hasBeenExecuted;
};

static pascal OSStatus CallbackHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
{
CallbackMessagePayload* pl = 0;
GetEventParameter (theEvent, 'mess', typeVoidPtr, 0, sizeof(pl), 0, &pl);

if (pl != 0)
{
pl->result = (*pl->function) (pl->parameter);
pl->hasBeenExecuted = true;
}

return noErr;
}

static pascal OSStatus MouseClickHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
{
::Point where;
GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, 0, sizeof(::Point), 0, &where);
WindowRef window;
if (FindWindow (where, &window) == inMenuBar)
{
// turn off the wait cursor before going in here..
const int oldTimeBeforeWaitCursor = MessageManager::getInstance()->getTimeBeforeShowingWaitCursor();
MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0);

if (Component::getCurrentlyModalComponent() != 0)
Component::getCurrentlyModalComponent()->inputAttemptWhenModal();

juce_MainMenuAboutToBeUsed();
MenuSelect (where);
HiliteMenu (0);

MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (oldTimeBeforeWaitCursor);
return noErr;
}

return eventNotHandledErr;
}

static pascal OSErr QuitAppleEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon)
{
if (JUCEApplication::getInstance() != 0)
JUCEApplication::getInstance()->systemRequestedQuit();

return noErr;
}

static pascal OSErr OpenDocEventHandler (const AppleEvent *appleEvt, AppleEvent* reply, long refcon)
{
AEDescList docs;
StringArray files;

if (AEGetParamDesc (appleEvt, keyDirectObject, typeAEList, &docs) == noErr)
{
long num;
if (AECountItems (&docs, &num) == noErr)
{
for (int i = 1; i <= num; ++i)
{
FSRef file;
AEKeyword keyword;
DescType type;
Size size;

if (AEGetNthPtr (&docs, i, typeFSRef, &keyword, &type,
&file, sizeof (file), &size) == noErr)
{
const String path (PlatformUtilities::makePathFromFSRef (&file));

if (path.isNotEmpty())
files.add (path.quoted());
}
}

if (files.size() > 0
&& JUCEApplication::getInstance() != 0)
{
JUCE_TRY
{
JUCEApplication::getInstance()
->anotherInstanceStarted (files.joinIntoString (T(" ")));
}
JUCE_CATCH_ALL
}
}

AEDisposeDesc (&docs);
};

return noErr;
}

static pascal OSStatus AppEventHandlerProc (EventHandlerCallRef, EventRef theEvent, void* userData)
{
const UInt32 eventClass = GetEventClass (theEvent);

if (eventClass == kEventClassCommand)
{
HICommand command;

if (GetEventParameter (theEvent, kEventParamHICommand, typeHICommand, 0, sizeof (command), 0, &command) == noErr
|| GetEventParameter (theEvent, kEventParamDirectObject, typeHICommand, 0, sizeof (command), 0, &command) == noErr)
{
if (command.commandID == kHICommandQuit)
{
if (JUCEApplication::getInstance() != 0)
JUCEApplication::getInstance()->systemRequestedQuit();

return noErr;
}
else if (command.commandID == kHICommandMaximizeAll
|| command.commandID == kHICommandMaximizeWindow
|| command.commandID == kHICommandBringAllToFront)
{
juce_maximiseAllMinimisedWindows();
return noErr;
}
else
{
juce_InvokeMainMenuCommand (command);
}
}
}
else if (eventClass == kEventClassApplication)
{
if (GetEventKind (theEvent) == kEventAppFrontSwitched)
{
juce_HandleProcessFocusChange();
}
else if (GetEventKind (theEvent) == kEventAppShown)
{
// this seems to blank the windows, so we need to do a repaint..
for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
{
Component* const c = Desktop::getInstance().getComponent (i);

if (c != 0)
c->repaint();
}
}
}

return eventNotHandledErr;
}

static EventQueueRef mainQueue;
static EventHandlerRef juceEventHandler = 0;
static EventHandlerRef callbackEventHandler = 0;

//==============================================================================
void MessageManager::doPlatformSpecificInitialisation()
{
static bool initialised = false;

if (! initialised)
{
initialised = true;

#if MACOS_10_3_OR_EARLIER
// work-around for a bug in MacOS 10.2..
ProcessSerialNumber junkPSN;
(void) GetCurrentProcess (&junkPSN);
#endif

mainQueue = GetMainEventQueue();

// if we're linking a Juce app to one or more dynamic libraries, we'll need different values
// for this so each module doesn't interfere with the others.
UnsignedWide t;
Microseconds (&t);
kJUCEClass ^= t.lo;
}

const EventTypeSpec type1 = { kJUCEClass, kJUCEKind };
InstallApplicationEventHandler (NewEventHandlerUPP (EventHandlerProc), 1, &type1, 0, &juceEventHandler);

const EventTypeSpec type2 = { kJUCEClass, kCallbackKind };
InstallApplicationEventHandler (NewEventHandlerUPP (CallbackHandlerProc), 1, &type2, 0, &callbackEventHandler);

// only do this stuff if we're running as an application rather than a library..
if (JUCEApplication::getInstance() != 0)
{
const EventTypeSpec type3 = { kEventClassMouse, kEventMouseDown };
InstallApplicationEventHandler (NewEventHandlerUPP (MouseClickHandlerProc), 1, &type3, 0, 0);

const EventTypeSpec type4[] = { { kEventClassApplication, kEventAppShown },
{ kEventClassApplication, kEventAppFrontSwitched },
{ kEventClassCommand, kEventProcessCommand } };

InstallApplicationEventHandler (NewEventHandlerUPP (AppEventHandlerProc), 3, type4, 0, 0);

AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
NewAEEventHandlerUPP (QuitAppleEventHandler), 0, false);

AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments,
NewAEEventHandlerUPP (OpenDocEventHandler), 0, false);
}
}

void MessageManager::doPlatformSpecificShutdown()
{
if (juceEventHandler != 0)
{
RemoveEventHandler (juceEventHandler);
juceEventHandler = 0;
}

if (callbackEventHandler != 0)
{
RemoveEventHandler (callbackEventHandler);
callbackEventHandler = 0;
}
}

bool juce_postMessageToSystemQueue (void* message)
{
jassert (mainQueue == GetMainEventQueue());

EventRef event;
if (CreateEvent (0, kJUCEClass, kJUCEKind, 0, kEventAttributeUserEvent, &event) == noErr)
{
SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &message);
const bool ok = PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr;
ReleaseEvent (event);
return ok;
}

return false;
}

void MessageManager::broadcastMessage (const String& value) throw()
{
}

void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
void* data)
{
if (isThisTheMessageThread())
{
return (*callback) (data);
}
else
{
jassert (mainQueue == GetMainEventQueue());

CallbackMessagePayload cmp;
cmp.function = callback;
cmp.parameter = data;
cmp.result = 0;
cmp.hasBeenExecuted = false;

EventRef event;
if (CreateEvent (0, kJUCEClass, kCallbackKind, 0, kEventAttributeUserEvent, &event) == noErr)
{
void* v = &cmp;
SetEventParameter (event, 'mess', typeVoidPtr, sizeof (void*), &v);

if (PostEventToQueue (mainQueue, event, kEventPriorityStandard) == noErr)
{
while (! cmp.hasBeenExecuted)
Thread::yield();

return cmp.result;
}
}

return 0;
}
}

END_JUCE_NAMESPACE

+ 1092
- 1085
extras/audio plugins/wrapper/formats/RTAS/juce_RTASWrapper.cpp
File diff suppressed because it is too large
View File


+ 373
- 373
src/juce_appframework/events/juce_MessageManager.cpp View File

@@ -1,373 +1,373 @@
/*
==============================================================================
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)
{
Message* const m = new Message (quitMessageId, (useMaximumForce) ? 1 : 0, 0, 0);
m->messageRecipient = 0;
postMessageToQueue (m);
quitMessagePosted = true;
}
bool MessageManager::hasQuitMessageBeenPosted() const throw()
{
return quitMessagePosted;
}
//==============================================================================
void MessageManager::deliverBroadcastMessage (const String& value)
{
if (broadcastListeners != 0)
broadcastListeners->sendActionMessage (value);
}
void MessageManager::registerBroadcastListener (ActionListener* const listener) throw()
{
if (broadcastListeners == 0)
broadcastListeners = new ActionListenerList();
broadcastListeners->addActionListener (listener);
}
void MessageManager::deregisterBroadcastListener (ActionListener* const listener) throw()
{
if (broadcastListeners != 0)
broadcastListeners->removeActionListener (listener);
}
//==============================================================================
// This gets called occasionally by the timer thread (to save using an extra thread
// for it).
void MessageManager::inactivityCheckCallback() throw()
{
if (instance != 0)
instance->inactivityCheckCallbackInt();
}
void MessageManager::inactivityCheckCallbackInt() throw()
{
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() throw()
{
if (instance != 0)
{
instance->messageCounter++;
if (instance->needToGetRidOfWaitCursor)
{
instance->needToGetRidOfWaitCursor = false;
MouseCursor::hideWaitCursor();
}
}
}
void MessageManager::setTimeBeforeShowingWaitCursor (const int millisecs) throw()
{
// 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 throw()
{
return timeBeforeWaitCursor;
}
bool MessageManager::isThisTheMessageThread() const throw()
{
return Thread::getCurrentThreadId() == messageThreadId;
}
void MessageManager::setCurrentMessageThread (const int threadId) throw()
{
messageThreadId = threadId;
}
bool MessageManager::currentThreadHasLockedMessageManager() const throw()
{
return Thread::getCurrentThreadId() == currentLockingThreadId;
}
//==============================================================================
MessageManagerLock::MessageManagerLock() throw()
: locked (false)
{
if (MessageManager::instance != 0)
{
MessageManager::instance->messageDispatchLock.enter();
lastLockingThreadId = MessageManager::instance->currentLockingThreadId;
MessageManager::instance->currentLockingThreadId = Thread::getCurrentThreadId();
locked = true;
}
}
MessageManagerLock::MessageManagerLock (Thread* const thread) throw()
: locked (false)
{
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())
break;
Thread::sleep (1);
}
}
}
MessageManagerLock::~MessageManagerLock() throw()
{
if (locked && MessageManager::instance != 0)
{
MessageManager::instance->currentLockingThreadId = lastLockingThreadId;
MessageManager::instance->messageDispatchLock.exit();
}
}
END_JUCE_NAMESPACE
/*
==============================================================================
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)
{
Message* const m = new Message (quitMessageId, (useMaximumForce) ? 1 : 0, 0, 0);
m->messageRecipient = 0;
postMessageToQueue (m);
quitMessagePosted = true;
}
bool MessageManager::hasQuitMessageBeenPosted() const throw()
{
return quitMessagePosted;
}
//==============================================================================
void MessageManager::deliverBroadcastMessage (const String& value)
{
if (broadcastListeners != 0)
broadcastListeners->sendActionMessage (value);
}
void MessageManager::registerBroadcastListener (ActionListener* const listener) throw()
{
if (broadcastListeners == 0)
broadcastListeners = new ActionListenerList();
broadcastListeners->addActionListener (listener);
}
void MessageManager::deregisterBroadcastListener (ActionListener* const listener) throw()
{
if (broadcastListeners != 0)
broadcastListeners->removeActionListener (listener);
}
//==============================================================================
// This gets called occasionally by the timer thread (to save using an extra thread
// for it).
void MessageManager::inactivityCheckCallback() throw()
{
if (instance != 0)
instance->inactivityCheckCallbackInt();
}
void MessageManager::inactivityCheckCallbackInt() throw()
{
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() throw()
{
if (instance != 0)
{
instance->messageCounter++;
if (instance->needToGetRidOfWaitCursor)
{
instance->needToGetRidOfWaitCursor = false;
MouseCursor::hideWaitCursor();
}
}
}
void MessageManager::setTimeBeforeShowingWaitCursor (const int millisecs) throw()
{
// 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 throw()
{
return timeBeforeWaitCursor;
}
bool MessageManager::isThisTheMessageThread() const throw()
{
return Thread::getCurrentThreadId() == messageThreadId;
}
void MessageManager::setCurrentMessageThread (const int threadId) throw()
{
messageThreadId = threadId;
}
bool MessageManager::currentThreadHasLockedMessageManager() const throw()
{
return Thread::getCurrentThreadId() == currentLockingThreadId;
}
//==============================================================================
MessageManagerLock::MessageManagerLock() throw()
: locked (false)
{
if (MessageManager::instance != 0)
{
MessageManager::instance->messageDispatchLock.enter();
lastLockingThreadId = MessageManager::instance->currentLockingThreadId;
MessageManager::instance->currentLockingThreadId = Thread::getCurrentThreadId();
locked = true;
}
}
MessageManagerLock::MessageManagerLock (Thread* const thread) throw()
: locked (false)
{
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())
break;
Thread::sleep (1);
}
}
}
MessageManagerLock::~MessageManagerLock() throw()
{
if (locked && MessageManager::instance != 0)
{
MessageManager::instance->currentLockingThreadId = lastLockingThreadId;
MessageManager::instance->messageDispatchLock.exit();
}
}
END_JUCE_NAMESPACE

+ 397
- 390
src/juce_appframework/events/juce_Timer.cpp View File

@@ -1,390 +1,397 @@
/*
==============================================================================
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_Timer.h"
#include "juce_MessageManager.h"
#include "juce_AsyncUpdater.h"
#include "../application/juce_Application.h"
#include "../application/juce_DeletedAtShutdown.h"
#include "../../juce_core/basics/juce_Time.h"
#include "../../juce_core/threads/juce_Thread.h"
#include "../../juce_core/threads/juce_ScopedLock.h"
#include "../../juce_core/containers/juce_VoidArray.h"
//==============================================================================
class InternalTimerThread : private Thread,
private MessageListener,
private DeletedAtShutdown,
private AsyncUpdater
{
private:
friend class Timer;
static InternalTimerThread* instance;
static CriticalSection lock;
Timer* volatile firstTimer;
bool volatile callbackNeeded;
InternalTimerThread (const InternalTimerThread&);
const InternalTimerThread& operator= (const InternalTimerThread&);
void addTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
while (tt != 0)
{
// trying to add a timer that's already here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (tt != t);
tt = tt->next;
}
jassert (t->previous == 0 && t->next == 0);
#endif
Timer* i = firstTimer;
if (i == 0 || i->countdownMs > t->countdownMs)
{
t->next = firstTimer;
firstTimer = t;
}
else
{
while (i->next != 0 && i->next->countdownMs <= t->countdownMs)
i = i->next;
jassert (i != 0);
t->next = i->next;
t->previous = i;
i->next = t;
}
if (t->next != 0)
t->next->previous = t;
jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs)
&& (t->previous == 0 || t->previous->countdownMs <= t->countdownMs));
notify();
}
void removeTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
bool found = false;
while (tt != 0)
{
if (tt == t)
{
found = true;
break;
}
tt = tt->next;
}
// trying to remove a timer that's not here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (found);
#endif
if (t->previous != 0)
{
jassert (firstTimer != t);
t->previous->next = t->next;
}
else
{
jassert (firstTimer == t);
firstTimer = t->next;
}
if (t->next != 0)
t->next->previous = t->previous;
t->next = 0;
t->previous = 0;
}
void decrementAllCounters (const int numMillisecs) const
{
Timer* t = firstTimer;
while (t != 0)
{
t->countdownMs -= numMillisecs;
t = t->next;
}
}
void handleAsyncUpdate()
{
startThread (7);
}
public:
InternalTimerThread()
: Thread ("Juce Timer"),
firstTimer (0),
callbackNeeded (false)
{
triggerAsyncUpdate();
}
~InternalTimerThread() throw()
{
stopThread (4000);
jassert (instance == this || instance == 0);
if (instance == this)
instance = 0;
}
void run()
{
uint32 lastTime = Time::getMillisecondCounter();
uint32 lastMessageManagerCallback = lastTime;
while (! threadShouldExit())
{
uint32 now = Time::getMillisecondCounter();
if (now <= lastTime)
{
wait (2);
continue;
}
const int elapsed = now - lastTime;
lastTime = now;
lock.enter();
decrementAllCounters (elapsed);
const int timeUntilFirstTimer = (firstTimer != 0) ? firstTimer->countdownMs
: 1000;
lock.exit();
if (timeUntilFirstTimer <= 0)
{
callbackNeeded = true;
postMessage (new Message());
while (callbackNeeded)
{
wait (4);
if (threadShouldExit())
return;
now = Time::getMillisecondCounter();
if (now > lastMessageManagerCallback + 200)
{
lastMessageManagerCallback = now;
MessageManager::inactivityCheckCallback();
}
}
}
else
{
// don't wait for too long because running this loop also helps keep the
// Time::getApproximateMillisecondTimer value stay up-to-date
wait (jlimit (1, 50, timeUntilFirstTimer));
}
if (now - lastMessageManagerCallback > 200)
{
lastMessageManagerCallback = now;
MessageManager::inactivityCheckCallback();
}
}
}
void handleMessage (const Message&)
{
const ScopedLock sl (lock);
while (firstTimer != 0 && firstTimer->countdownMs <= 0)
{
Timer* const t = firstTimer;
t->countdownMs = t->periodMs;
removeTimer (t);
addTimer (t);
const ScopedUnlock ul (lock);
callbackNeeded = false;
JUCE_TRY
{
t->timerCallback();
}
JUCE_CATCH_EXCEPTION
}
callbackNeeded = false;
}
static void callAnyTimersSynchronously()
{
if (InternalTimerThread::instance != 0)
{
const Message m;
InternalTimerThread::instance->handleMessage (m);
}
}
static inline void add (Timer* const tim) throw()
{
if (instance == 0)
instance = new InternalTimerThread();
instance->addTimer (tim);
}
static inline void remove (Timer* const tim) throw()
{
if (instance != 0)
instance->removeTimer (tim);
}
static inline void resetCounter (Timer* const tim,
const int newCounter) throw()
{
if (instance != 0)
{
tim->countdownMs = newCounter;
tim->periodMs = newCounter;
if ((tim->next != 0 && tim->next->countdownMs < tim->countdownMs)
|| (tim->previous != 0 && tim->previous->countdownMs > tim->countdownMs))
{
instance->removeTimer (tim);
instance->addTimer (tim);
}
}
}
};
InternalTimerThread* InternalTimerThread::instance = 0;
CriticalSection InternalTimerThread::lock;
void juce_callAnyTimersSynchronously()
{
InternalTimerThread::callAnyTimersSynchronously();
}
//==============================================================================
#ifdef JUCE_DEBUG
static SortedSet <Timer*> activeTimers;
#endif
Timer::Timer() throw()
: countdownMs (0),
periodMs (0),
previous (0),
next (0)
{
#ifdef JUCE_DEBUG
activeTimers.add (this);
#endif
}
Timer::Timer (const Timer&) throw()
: countdownMs (0),
periodMs (0),
previous (0),
next (0)
{
#ifdef JUCE_DEBUG
activeTimers.add (this);
#endif
}
Timer::~Timer()
{
stopTimer();
#ifdef JUCE_DEBUG
activeTimers.removeValue (this);
#endif
}
void Timer::startTimer (const int interval) throw()
{
const ScopedLock sl (InternalTimerThread::lock);
#ifdef JUCE_DEBUG
// this isn't a valid object! Your timer might be a dangling pointer or something..
jassert (activeTimers.contains (this));
#endif
if (periodMs == 0)
{
countdownMs = interval;
periodMs = jmax (1, interval);
InternalTimerThread::add (this);
}
else
{
InternalTimerThread::resetCounter (this, interval);
}
}
void Timer::stopTimer() throw()
{
const ScopedLock sl (InternalTimerThread::lock);
#ifdef JUCE_DEBUG
// this isn't a valid object! Your timer might be a dangling pointer or something..
jassert (activeTimers.contains (this));
#endif
if (periodMs > 0)
{
InternalTimerThread::remove (this);
periodMs = 0;
}
}
END_JUCE_NAMESPACE
/*
==============================================================================

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_Timer.h"
#include "juce_MessageManager.h"
#include "juce_AsyncUpdater.h"
#include "../application/juce_Application.h"
#include "../application/juce_DeletedAtShutdown.h"
#include "../../juce_core/basics/juce_Time.h"
#include "../../juce_core/threads/juce_Thread.h"
#include "../../juce_core/threads/juce_ScopedLock.h"
#include "../../juce_core/containers/juce_VoidArray.h"


//==============================================================================
class InternalTimerThread : private Thread,
private MessageListener,
private DeletedAtShutdown,
private AsyncUpdater
{
private:
friend class Timer;
static InternalTimerThread* instance;
static CriticalSection lock;

Timer* volatile firstTimer;
bool volatile callbackNeeded;

InternalTimerThread (const InternalTimerThread&);
const InternalTimerThread& operator= (const InternalTimerThread&);

void addTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;

while (tt != 0)
{
// trying to add a timer that's already here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (tt != t);

tt = tt->next;
}

jassert (t->previous == 0 && t->next == 0);
#endif

Timer* i = firstTimer;

if (i == 0 || i->countdownMs > t->countdownMs)
{
t->next = firstTimer;
firstTimer = t;
}
else
{
while (i->next != 0 && i->next->countdownMs <= t->countdownMs)
i = i->next;

jassert (i != 0);

t->next = i->next;
t->previous = i;
i->next = t;
}

if (t->next != 0)
t->next->previous = t;

jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs)
&& (t->previous == 0 || t->previous->countdownMs <= t->countdownMs));

notify();
}

void removeTimer (Timer* const t) throw()
{
#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
bool found = false;

while (tt != 0)
{
if (tt == t)
{
found = true;
break;
}

tt = tt->next;
}

// trying to remove a timer that's not here - shouldn't get to this point,
// so if you get this assertion, let me know!
jassert (found);
#endif

if (t->previous != 0)
{
jassert (firstTimer != t);
t->previous->next = t->next;
}
else
{
jassert (firstTimer == t);
firstTimer = t->next;
}

if (t->next != 0)
t->next->previous = t->previous;

t->next = 0;
t->previous = 0;
}

void decrementAllCounters (const int numMillisecs) const
{
Timer* t = firstTimer;

while (t != 0)
{
t->countdownMs -= numMillisecs;
t = t->next;
}
}

void handleAsyncUpdate()
{
startThread (7);
}

public:
InternalTimerThread()
: Thread ("Juce Timer"),
firstTimer (0),
callbackNeeded (false)
{
triggerAsyncUpdate();
}

~InternalTimerThread() throw()
{
stopThread (4000);

jassert (instance == this || instance == 0);
if (instance == this)
instance = 0;
}

void run()
{
uint32 lastTime = Time::getMillisecondCounter();
uint32 lastMessageManagerCallback = lastTime;

while (! threadShouldExit())
{
uint32 now = Time::getMillisecondCounter();

if (now <= lastTime)
{
wait (2);
continue;
}

const int elapsed = now - lastTime;
lastTime = now;

lock.enter();
decrementAllCounters (elapsed);
const int timeUntilFirstTimer = (firstTimer != 0) ? firstTimer->countdownMs
: 1000;
lock.exit();

if (timeUntilFirstTimer <= 0)
{
callbackNeeded = true;
postMessage (new Message());

// sometimes, our message could get discarded by the OS (particularly when running as an RTAS when the app has a modal loop),
// so this is how long to wait before assuming the message has been lost and trying again.
const uint32 messageDeliveryTimeout = now + 2000;

while (callbackNeeded)
{
wait (4);

if (threadShouldExit())
return;

now = Time::getMillisecondCounter();

if (now > lastMessageManagerCallback + 200)
{
lastMessageManagerCallback = now;
MessageManager::inactivityCheckCallback();
}

if (now > messageDeliveryTimeout)
break;
}
}
else
{
// don't wait for too long because running this loop also helps keep the
// Time::getApproximateMillisecondTimer value stay up-to-date
wait (jlimit (1, 50, timeUntilFirstTimer));
}

if (now > lastMessageManagerCallback + 200)
{
lastMessageManagerCallback = now;
MessageManager::inactivityCheckCallback();
}
}
}

void handleMessage (const Message&)
{
const ScopedLock sl (lock);

while (firstTimer != 0 && firstTimer->countdownMs <= 0)
{
Timer* const t = firstTimer;
t->countdownMs = t->periodMs;

removeTimer (t);
addTimer (t);

const ScopedUnlock ul (lock);
callbackNeeded = false;

JUCE_TRY
{
t->timerCallback();
}
JUCE_CATCH_EXCEPTION
}

callbackNeeded = false;
}

static void callAnyTimersSynchronously()
{
if (InternalTimerThread::instance != 0)
{
const Message m;
InternalTimerThread::instance->handleMessage (m);
}
}

static inline void add (Timer* const tim) throw()
{
if (instance == 0)
instance = new InternalTimerThread();

instance->addTimer (tim);
}

static inline void remove (Timer* const tim) throw()
{
if (instance != 0)
instance->removeTimer (tim);
}

static inline void resetCounter (Timer* const tim,
const int newCounter) throw()
{
if (instance != 0)
{
tim->countdownMs = newCounter;
tim->periodMs = newCounter;

if ((tim->next != 0 && tim->next->countdownMs < tim->countdownMs)
|| (tim->previous != 0 && tim->previous->countdownMs > tim->countdownMs))
{
instance->removeTimer (tim);
instance->addTimer (tim);
}
}
}
};

InternalTimerThread* InternalTimerThread::instance = 0;
CriticalSection InternalTimerThread::lock;

void juce_callAnyTimersSynchronously()
{
InternalTimerThread::callAnyTimersSynchronously();
}

//==============================================================================
#ifdef JUCE_DEBUG
static SortedSet <Timer*> activeTimers;
#endif

Timer::Timer() throw()
: countdownMs (0),
periodMs (0),
previous (0),
next (0)
{
#ifdef JUCE_DEBUG
activeTimers.add (this);
#endif
}

Timer::Timer (const Timer&) throw()
: countdownMs (0),
periodMs (0),
previous (0),
next (0)
{
#ifdef JUCE_DEBUG
activeTimers.add (this);
#endif
}

Timer::~Timer()
{
stopTimer();

#ifdef JUCE_DEBUG
activeTimers.removeValue (this);
#endif
}

void Timer::startTimer (const int interval) throw()
{
const ScopedLock sl (InternalTimerThread::lock);

#ifdef JUCE_DEBUG
// this isn't a valid object! Your timer might be a dangling pointer or something..
jassert (activeTimers.contains (this));
#endif

if (periodMs == 0)
{
countdownMs = interval;
periodMs = jmax (1, interval);
InternalTimerThread::add (this);
}
else
{
InternalTimerThread::resetCounter (this, interval);
}
}

void Timer::stopTimer() throw()
{
const ScopedLock sl (InternalTimerThread::lock);

#ifdef JUCE_DEBUG
// this isn't a valid object! Your timer might be a dangling pointer or something..
jassert (activeTimers.contains (this));
#endif

if (periodMs > 0)
{
InternalTimerThread::remove (this);
periodMs = 0;
}
}

END_JUCE_NAMESPACE

Loading…
Cancel
Save