| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||