Browse Source

Changed the Message class to be reference-counted, and used this to tighten up some messaging code. Minor tweaks to AudioThumbnail, ReferenceCountedArray.

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
d60f661789
22 changed files with 569 additions and 523 deletions
  1. +165
    -174
      juce_amalgamated.cpp
  2. +173
    -143
      juce_amalgamated.h
  3. +1
    -1
      src/audio/audio_file_formats/juce_AudioFormatWriter.cpp
  4. +3
    -2
      src/audio/audio_file_formats/juce_AudioThumbnail.cpp
  5. +1
    -1
      src/audio/audio_file_formats/juce_AudioThumbnail.h
  6. +2
    -0
      src/audio/dsp/juce_AudioDataConverters.h
  7. +43
    -4
      src/containers/juce_ReferenceCountedArray.h
  8. +1
    -1
      src/core/juce_StandardHeader.h
  9. +15
    -10
      src/events/juce_AsyncUpdater.cpp
  10. +9
    -7
      src/events/juce_AsyncUpdater.h
  11. +1
    -14
      src/events/juce_CallbackMessage.h
  12. +5
    -1
      src/events/juce_Message.h
  13. +21
    -42
      src/events/juce_MessageManager.cpp
  14. +2
    -4
      src/events/juce_MessageManager.h
  15. +2
    -1
      src/events/juce_Timer.cpp
  16. +12
    -0
      src/gui/components/juce_Component.cpp
  17. +69
    -0
      src/native/juce_mac_NativeCode.mm
  18. +6
    -6
      src/native/linux/juce_linux_Messaging.cpp
  19. +26
    -64
      src/native/mac/juce_iphone_MessageManager.mm
  20. +3
    -3
      src/native/mac/juce_mac_Files.mm
  21. +2
    -43
      src/native/mac/juce_mac_MessageManager.mm
  22. +7
    -2
      src/native/windows/juce_win32_Messaging.cpp

+ 165
- 174
juce_amalgamated.cpp View File

@@ -21666,7 +21666,7 @@ public:
void setThumbnail (AudioThumbnail* thumb) void setThumbnail (AudioThumbnail* thumb)
{ {
if (thumb != 0) if (thumb != 0)
thumb->reset (buffer.getNumChannels(), writer->getSampleRate());
thumb->reset (buffer.getNumChannels(), writer->getSampleRate(), 0);


const ScopedLock sl (thumbnailLock); const ScopedLock sl (thumbnailLock);
thumbnailToUpdate = thumb; thumbnailToUpdate = thumb;
@@ -22475,14 +22475,15 @@ void AudioThumbnail::clear()
sendChangeMessage(); sendChangeMessage();
} }


void AudioThumbnail::reset (int newNumChannels, double newSampleRate)
void AudioThumbnail::reset (int newNumChannels, double newSampleRate, int64 totalSamplesInSource)
{ {
clear(); clear();


numChannels = newNumChannels; numChannels = newNumChannels;
sampleRate = newSampleRate; sampleRate = newSampleRate;
totalSamples = totalSamplesInSource;


createChannels (0);
createChannels (1 + (int) (totalSamplesInSource / samplesPerThumbSample));
} }


void AudioThumbnail::createChannels (const int length) void AudioThumbnail::createChannels (const int length)
@@ -38416,28 +38417,34 @@ END_JUCE_NAMESPACE
/*** Start of inlined file: juce_AsyncUpdater.cpp ***/ /*** Start of inlined file: juce_AsyncUpdater.cpp ***/
BEGIN_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE


class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage
class AsyncUpdaterMessage : public CallbackMessage
{ {
public: public:
AsyncUpdaterMessage (AsyncUpdater& owner_) AsyncUpdaterMessage (AsyncUpdater& owner_)
: owner (owner_) : owner (owner_)
{ {
setMessageIsDeletedOnDelivery (false);
} }


void messageCallback() void messageCallback()
{ {
if (owner.pendingMessage.compareAndSetBool (0, this))
if (shouldDeliver.compareAndSetBool (0, 1))
owner.handleAsyncUpdate(); owner.handleAsyncUpdate();
} }


Atomic<int> shouldDeliver;

private: private:
AsyncUpdater& owner; AsyncUpdater& owner;
}; };


AsyncUpdater::AsyncUpdater() AsyncUpdater::AsyncUpdater()
: message (new AsyncUpdaterMessage (*this))
{ {
message = new AsyncUpdaterMessage (*this);
}

inline Atomic<int>& AsyncUpdater::getDeliveryFlag() const throw()
{
return static_cast <AsyncUpdaterMessage*> (message.getObject())->shouldDeliver;
} }


AsyncUpdater::~AsyncUpdater() AsyncUpdater::~AsyncUpdater()
@@ -38448,19 +38455,18 @@ AsyncUpdater::~AsyncUpdater()
// deleting this object, or find some other way to avoid such a race condition. // deleting this object, or find some other way to avoid such a race condition.
jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager());


if (pendingMessage.exchange (0) != 0)
message.release()->setMessageIsDeletedOnDelivery (true);
getDeliveryFlag().set (0);
} }


void AsyncUpdater::triggerAsyncUpdate() void AsyncUpdater::triggerAsyncUpdate()
{ {
if (pendingMessage.compareAndSetBool (message, 0))
if (getDeliveryFlag().compareAndSetBool (1, 0))
message->post(); message->post();
} }


void AsyncUpdater::cancelPendingUpdate() throw() void AsyncUpdater::cancelPendingUpdate() throw()
{ {
pendingMessage = 0;
getDeliveryFlag().set (0);
} }


void AsyncUpdater::handleUpdateNowIfNeeded() void AsyncUpdater::handleUpdateNowIfNeeded()
@@ -38468,13 +38474,13 @@ void AsyncUpdater::handleUpdateNowIfNeeded()
// This can only be called by the event thread. // This can only be called by the event thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());


if (pendingMessage.exchange (0) != 0)
if (getDeliveryFlag().exchange (0) != 0)
handleAsyncUpdate(); handleAsyncUpdate();
} }


bool AsyncUpdater::isUpdatePending() const throw() bool AsyncUpdater::isUpdatePending() const throw()
{ {
return pendingMessage.value != 0;
return getDeliveryFlag().value != 0;
} }


END_JUCE_NAMESPACE END_JUCE_NAMESPACE
@@ -39088,10 +39094,10 @@ MessageManager* MessageManager::getInstance() throw()
void MessageManager::postMessageToQueue (Message* const message) void MessageManager::postMessageToQueue (Message* const message)
{ {
if (quitMessagePosted || ! juce_postMessageToSystemQueue (message)) if (quitMessagePosted || ! juce_postMessageToSystemQueue (message))
delete message;
Message::Ptr deleter (message); // (this will delete messages that were just created with a 0 ref count)
} }


CallbackMessage::CallbackMessage() throw() : deleteOnDelivery (true) {}
CallbackMessage::CallbackMessage() throw() {}
CallbackMessage::~CallbackMessage() {} CallbackMessage::~CallbackMessage() {}


void CallbackMessage::post() void CallbackMessage::post()
@@ -39105,7 +39111,6 @@ void MessageManager::deliverMessage (Message* const message)
{ {
JUCE_TRY JUCE_TRY
{ {
ScopedPointer <Message> messageDeleter (message);
MessageListener* const recipient = message->messageRecipient; MessageListener* const recipient = message->messageRecipient;


if (recipient == 0) if (recipient == 0)
@@ -39115,9 +39120,6 @@ void MessageManager::deliverMessage (Message* const message)
if (callbackMessage != 0) if (callbackMessage != 0)
{ {
callbackMessage->messageCallback(); callbackMessage->messageCallback();

if (! callbackMessage->isMessageDeletedOnDelivery())
messageDeleter.release();
} }
else if (message->intParameter1 == quitMessageId) else if (message->intParameter1 == quitMessageId)
{ {
@@ -39200,9 +39202,11 @@ bool MessageManager::isThisTheMessageThread() const throw()


void MessageManager::setCurrentThreadAsMessageThread() void MessageManager::setCurrentThreadAsMessageThread()
{ {
if (messageThreadId != Thread::getCurrentThreadId())
const Thread::ThreadID thisThread = Thread::getCurrentThreadId();

if (messageThreadId != thisThread)
{ {
messageThreadId = Thread::getCurrentThreadId();
messageThreadId = thisThread;


// This is needed on windows to make sure the message window is created by this thread // This is needed on windows to make sure the message window is created by this thread
doPlatformSpecificShutdown(); doPlatformSpecificShutdown();
@@ -39225,47 +39229,31 @@ bool MessageManager::currentThreadHasLockedMessageManager() const throw()
accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens
in Cocoa). in Cocoa).
*/ */
class MessageManagerLock::SharedEvents : public ReferenceCountedObject
{
public:
SharedEvents() {}

/* This class just holds a couple of events to communicate between the BlockingMessage
and the MessageManagerLock. Because both of these objects may be deleted at any time,
this shared data must be kept in a separate, ref-counted container. */
WaitableEvent lockedEvent, releaseEvent;

private:
JUCE_DECLARE_NON_COPYABLE (SharedEvents);
};

class MessageManagerLock::BlockingMessage : public CallbackMessage class MessageManagerLock::BlockingMessage : public CallbackMessage
{ {
public: public:
BlockingMessage (MessageManagerLock::SharedEvents* const events_) : events (events_) {}
BlockingMessage() {}


void messageCallback() void messageCallback()
{ {
events->lockedEvent.signal();
events->releaseEvent.wait();
lockedEvent.signal();
releaseEvent.wait();
} }


private:
ReferenceCountedObjectPtr <MessageManagerLock::SharedEvents> events;
WaitableEvent lockedEvent, releaseEvent;


private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockingMessage); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockingMessage);
}; };


MessageManagerLock::MessageManagerLock (Thread* const threadToCheck) MessageManagerLock::MessageManagerLock (Thread* const threadToCheck)
: sharedEvents (0),
locked (false)
: locked (false)
{ {
init (threadToCheck, 0); init (threadToCheck, 0);
} }


MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal)
: sharedEvents (0),
locked (false)
: locked (false)
{ {
init (0, jobToCheckForExitSignal); init (0, jobToCheckForExitSignal);
} }
@@ -39296,19 +39284,16 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const
} }
} }


sharedEvents = new SharedEvents();
sharedEvents->incReferenceCount();

(new BlockingMessage (sharedEvents))->post();
blockingMessage = new BlockingMessage();
blockingMessage->post();


while (! sharedEvents->lockedEvent.wait (50))
while (! blockingMessage->lockedEvent.wait (20))
{ {
if ((threadToCheck != 0 && threadToCheck->threadShouldExit()) if ((threadToCheck != 0 && threadToCheck->threadShouldExit())
|| (job != 0 && job->shouldExit())) || (job != 0 && job->shouldExit()))
{ {
sharedEvents->releaseEvent.signal();
sharedEvents->decReferenceCount();
sharedEvents = 0;
blockingMessage->releaseEvent.signal();
blockingMessage = 0;
MessageManager::instance->lockingLock.exit(); MessageManager::instance->lockingLock.exit();
return; return;
} }
@@ -39324,12 +39309,12 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const


MessageManagerLock::~MessageManagerLock() throw() MessageManagerLock::~MessageManagerLock() throw()
{ {
if (sharedEvents != 0)
if (blockingMessage != 0)
{ {
jassert (MessageManager::instance == 0 || MessageManager::instance->currentThreadHasLockedMessageManager()); jassert (MessageManager::instance == 0 || MessageManager::instance->currentThreadHasLockedMessageManager());


sharedEvents->releaseEvent.signal();
sharedEvents->decReferenceCount();
blockingMessage->releaseEvent.signal();
blockingMessage = 0;


if (MessageManager::instance != 0) if (MessageManager::instance != 0)
{ {
@@ -39478,6 +39463,7 @@ public:
void run() void run()
{ {
uint32 lastTime = Time::getMillisecondCounter(); uint32 lastTime = Time::getMillisecondCounter();
Message::Ptr message (new Message());


while (! threadShouldExit()) while (! threadShouldExit())
{ {
@@ -39503,7 +39489,7 @@ public:
*/ */
if (callbackNeeded.compareAndSetBool (1, 0)) if (callbackNeeded.compareAndSetBool (1, 0))
{ {
postMessage (new Message());
postMessage (message);


/* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS /* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
when the app has a modal loop), so this is how long to wait before assuming the when the app has a modal loop), so this is how long to wait before assuming the
@@ -41290,6 +41276,10 @@ void Component::exitModalState (const int returnValue)
} }
else else
{ {
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
CHECK_MESSAGE_MANAGER_IS_LOCKED

class ExitModalStateMessage : public CallbackMessage class ExitModalStateMessage : public CallbackMessage
{ {
public: public:
@@ -41854,6 +41844,10 @@ void Component::parentSizeChanged()


void Component::addComponentListener (ComponentListener* const newListener) void Component::addComponentListener (ComponentListener* const newListener)
{ {
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
CHECK_MESSAGE_MANAGER_IS_LOCKED

componentListeners.add (newListener); componentListeners.add (newListener);
} }


@@ -41895,6 +41889,10 @@ void Component::paintOverChildren (Graphics&)


void Component::postCommandMessage (const int commandId) void Component::postCommandMessage (const int commandId)
{ {
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
CHECK_MESSAGE_MANAGER_IS_LOCKED

class CustomCommandMessage : public CallbackMessage class CustomCommandMessage : public CallbackMessage
{ {
public: public:
@@ -239896,7 +239894,9 @@ static LRESULT CALLBACK juce_MessageWndProc (HWND h,
// here in case there are windows modal dialog boxes doing their own // here in case there are windows modal dialog boxes doing their own
// dispatch loop and not calling our version // dispatch loop and not calling our version


MessageManager::getInstance()->deliverMessage ((Message*) lParam);
Message* const message = reinterpret_cast <Message*> (lParam);
MessageManager::getInstance()->deliverMessage (message);
message->decReferenceCount();
return 0; return 0;
} }
else if (message == broadcastId) else if (message == broadcastId)
@@ -239984,7 +239984,9 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages
{ {
if (m.message == specialId && m.hwnd == juce_messageWindowHandle) if (m.message == specialId && m.hwnd == juce_messageWindowHandle)
{ {
MessageManager::getInstance()->deliverMessage ((Message*) (void*) m.lParam);
Message* const message = reinterpret_cast <Message*> (m.lParam);
MessageManager::getInstance()->deliverMessage (message);
message->decReferenceCount();
} }
else if (m.message == WM_QUIT) else if (m.message == WM_QUIT)
{ {
@@ -240014,6 +240016,7 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages


bool juce_postMessageToSystemQueue (Message* message) bool juce_postMessageToSystemQueue (Message* message)
{ {
message->incReferenceCount();
return PostMessage (juce_messageWindowHandle, specialId, 0, (LPARAM) message) != 0; return PostMessage (juce_messageWindowHandle, specialId, 0, (LPARAM) message) != 0;
} }


@@ -255883,7 +255886,7 @@ public:


private: private:
CriticalSection lock; CriticalSection lock;
OwnedArray <Message> queue;
ReferenceCountedArray <Message> queue;
int fd[2]; int fd[2];
int bytesInSocket; int bytesInSocket;
int totalEventCount; int totalEventCount;
@@ -255923,15 +255926,15 @@ private:
return true; return true;
} }


Message* popNextMessage()
const Message::Ptr popNextMessage()
{ {
ScopedLock sl (lock);
const ScopedLock sl (lock);


if (bytesInSocket > 0) if (bytesInSocket > 0)
{ {
--bytesInSocket; --bytesInSocket;


ScopedUnlock ul (lock);
const ScopedUnlock ul (lock);
unsigned char x; unsigned char x;
size_t numBytes = read (fd[1], &x, 1); size_t numBytes = read (fd[1], &x, 1);
(void) numBytes; (void) numBytes;
@@ -255942,7 +255945,7 @@ private:


bool dispatchNextInternalMessage() bool dispatchNextInternalMessage()
{ {
ScopedPointer <Message> msg (popNextMessage());
const Message::Ptr msg (popNextMessage());


if (msg == 0) if (msg == 0)
return false; return false;
@@ -255958,7 +255961,7 @@ private:
else else
{ {
// Handle "normal" messages // Handle "normal" messages
MessageManager::getInstance()->deliverMessage (msg.release());
MessageManager::getInstance()->deliverMessage (msg);
} }


return true; return true;
@@ -262337,6 +262340,73 @@ namespace
} }
} }


class MessageQueue
{
public:
MessageQueue()
{
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 && ! JUCE_IOS
runLoop = CFRunLoopGetMain();
#else
runLoop = CFRunLoopGetCurrent();
#endif

CFRunLoopSourceContext sourceContext;
zerostruct (sourceContext);
sourceContext.info = this;
sourceContext.perform = runLoopSourceCallback;
runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext);
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
}

~MessageQueue()
{
CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
CFRunLoopSourceInvalidate (runLoopSource);
CFRelease (runLoopSource);
}

void post (Message* const message)
{
messages.add (message);
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}

private:
ReferenceCountedArray <Message, CriticalSection> messages;
CriticalSection lock;
CFRunLoopRef runLoop;
CFRunLoopSourceRef runLoopSource;

bool deliverNextMessage()
{
const Message::Ptr nextMessage (messages.removeAndReturn (0));

if (nextMessage == 0)
return false;

const ScopedAutoReleasePool pool;
MessageManager::getInstance()->deliverMessage (nextMessage);
return true;
}

void runLoopCallback()
{
for (int i = 4; --i >= 0;)
if (! deliverNextMessage())
return;

CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}

static void runLoopSourceCallback (void* info)
{
static_cast <MessageQueue*> (info)->runLoopCallback();
}
};

#define JUCE_INCLUDED_FILE 1 #define JUCE_INCLUDED_FILE 1


// Now include the actual code files.. // Now include the actual code files..
@@ -264281,12 +264351,12 @@ const File File::getSpecialLocation (const SpecialLocationType type)
case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break; case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break;


#if JUCE_IOS #if JUCE_IOS
case userDocumentsDirectory: resultPath = getIOSSystemLocation (NSDocumentDirectory); break;
case userDesktopDirectory: resultPath = getIOSSystemLocation (NSDesktopDirectory); break;
case userDocumentsDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDocumentDirectory); break;
case userDesktopDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDesktopDirectory); break;


case tempDirectory: case tempDirectory:
{ {
File tmp (getIOSSystemLocation (NSCachesDirectory));
File tmp (FileHelpers::getIOSSystemLocation (NSCachesDirectory));
tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension()); tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension());
tmp.createDirectory(); tmp.createDirectory();
return tmp.getFullPathName(); return tmp.getFullPathName();
@@ -267433,79 +267503,41 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
return ! quitMessagePosted; return ! quitMessagePosted;
} }


namespace iOSMessageLoopHelpers
struct MessageDispatchSystem
{ {
static CFRunLoopRef runLoop = 0;
static CFRunLoopSourceRef runLoopSource = 0;
static OwnedArray <Message, CriticalSection>* pendingMessages = 0;
static JuceCustomMessageHandler* juceCustomMessageHandler = 0;

void runLoopSourceCallback (void*)
MessageDispatchSystem()
: juceCustomMessageHandler (0)
{ {
if (pendingMessages != 0)
{
int numDispatched = 0;

do
{
Message* const nextMessage = pendingMessages->removeAndReturn (0);

if (nextMessage == 0)
return;
juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init];
}


const ScopedAutoReleasePool pool;
MessageManager::getInstance()->deliverMessage (nextMessage);
~MessageDispatchSystem()
{
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler];
[juceCustomMessageHandler release];
}


} while (++numDispatched <= 4);
JuceCustomMessageHandler* juceCustomMessageHandler;
MessageQueue messageQueue;
};


CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}
}
}
static MessageDispatchSystem* dispatcher = 0;


void MessageManager::doPlatformSpecificInitialisation() void MessageManager::doPlatformSpecificInitialisation()
{ {
using namespace iOSMessageLoopHelpers;
pendingMessages = new OwnedArray <Message, CriticalSection>();

runLoop = CFRunLoopGetCurrent();
CFRunLoopSourceContext sourceContext;
zerostruct (sourceContext);
sourceContext.perform = runLoopSourceCallback;
runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext);
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);

if (juceCustomMessageHandler == 0)
juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init];
if (dispatcher == 0)
dispatcher = new MessageDispatchSystem();
} }


void MessageManager::doPlatformSpecificShutdown() void MessageManager::doPlatformSpecificShutdown()
{ {
using namespace iOSMessageLoopHelpers;
CFRunLoopSourceInvalidate (runLoopSource);
CFRelease (runLoopSource);
runLoopSource = 0;
deleteAndZero (pendingMessages);

if (juceCustomMessageHandler != 0)
{
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler];
[juceCustomMessageHandler release];
juceCustomMessageHandler = 0;
}
deleteAndZero (dispatcher);
} }


bool juce_postMessageToSystemQueue (Message* message) bool juce_postMessageToSystemQueue (Message* message)
{ {
using namespace iOSMessageLoopHelpers;

if (pendingMessages != 0)
{
pendingMessages->add (message);
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}
if (dispatcher != 0)
dispatcher->messageQueue.post (message);


return true; return true;
} }
@@ -267516,14 +267548,14 @@ void MessageManager::broadcastMessage (const String& value)


void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, void* data) void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, void* data)
{ {
using namespace iOSMessageLoopHelpers;

if (isThisTheMessageThread()) if (isThisTheMessageThread())
{ {
return (*callback) (data); return (*callback) (data);
} }
else else
{ {
jassert (dispatcher != 0); // trying to call this when the juce system isn't initialised..

// If a thread has a MessageManagerLock and then tries to call this method, it'll // If a thread has a MessageManagerLock and then tries to call this method, it'll
// deadlock because the message manager is blocked from running, so can never // deadlock because the message manager is blocked from running, so can never
// call your function.. // call your function..
@@ -267537,11 +267569,11 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call
cmp.result = 0; cmp.result = 0;
cmp.hasBeenExecuted = false; cmp.hasBeenExecuted = false;


[juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:)
withObject: [NSData dataWithBytesNoCopy: &cmp
length: sizeof (cmp)
freeWhenDone: NO]
waitUntilDone: YES];
[dispatcher->juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:)
withObject: [NSData dataWithBytesNoCopy: &cmp
length: sizeof (cmp)
freeWhenDone: NO]
waitUntilDone: YES];


return cmp.result; return cmp.result;
} }
@@ -275660,24 +275692,10 @@ class AppDelegateRedirector
public: public:
AppDelegateRedirector() AppDelegateRedirector()
{ {
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
runLoop = CFRunLoopGetMain();
#else
runLoop = CFRunLoopGetCurrent();
#endif
CFRunLoopSourceContext sourceContext;
zerostruct (sourceContext);
sourceContext.info = this;
sourceContext.perform = runLoopSourceCallback;
runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext);
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
} }


virtual ~AppDelegateRedirector() virtual ~AppDelegateRedirector()
{ {
CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
CFRunLoopSourceInvalidate (runLoopSource);
CFRelease (runLoopSource);
} }


virtual NSApplicationTerminateReply shouldTerminate() virtual NSApplicationTerminateReply shouldTerminate()
@@ -275753,40 +275771,13 @@ public:


void postMessage (Message* const m) void postMessage (Message* const m)
{ {
messages.add (m);
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
messageQueue.post (m);
} }


private: private:
CFRunLoopRef runLoop; CFRunLoopRef runLoop;
CFRunLoopSourceRef runLoopSource; CFRunLoopSourceRef runLoopSource;
OwnedArray <Message, CriticalSection> messages;

void runLoopCallback()
{
int numDispatched = 0;

do
{
Message* const nextMessage = messages.removeAndReturn (0);

if (nextMessage == 0)
return;

const ScopedAutoReleasePool pool;
MessageManager::getInstance()->deliverMessage (nextMessage);

} while (++numDispatched <= 4);

CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}

static void runLoopSourceCallback (void* info)
{
static_cast <AppDelegateRedirector*> (info)->runLoopCallback();
}
MessageQueue messageQueue;
}; };


END_JUCE_NAMESPACE END_JUCE_NAMESPACE


+ 173
- 143
juce_amalgamated.h View File

@@ -64,7 +64,7 @@
*/ */
#define JUCE_MAJOR_VERSION 1 #define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52 #define JUCE_MINOR_VERSION 52
#define JUCE_BUILDNUMBER 106
#define JUCE_BUILDNUMBER 107


/** Current Juce version number. /** Current Juce version number.


@@ -11025,6 +11025,7 @@ template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSec
class ReferenceCountedArray class ReferenceCountedArray
{ {
public: public:
typedef ReferenceCountedObjectPtr<ObjectClass> ObjectClassPtr;


/** Creates an empty array. /** Creates an empty array.
@see ReferenceCountedObject, Array, OwnedArray @see ReferenceCountedObject, Array, OwnedArray
@@ -11101,7 +11102,7 @@ public:


@see getUnchecked @see getUnchecked
*/ */
inline const ReferenceCountedObjectPtr<ObjectClass> operator[] (const int index) const throw()
inline const ObjectClassPtr operator[] (const int index) const throw()
{ {
const ScopedLockType lock (getLock()); const ScopedLockType lock (getLock());
return isPositiveAndBelow (index, numUsed) ? data.elements [index] return isPositiveAndBelow (index, numUsed) ? data.elements [index]
@@ -11113,7 +11114,7 @@ public:
This is a faster and less safe version of operator[] which doesn't check the index passed in, so This is a faster and less safe version of operator[] which doesn't check the index passed in, so
it can be used when you're sure the index if always going to be legal. it can be used when you're sure the index if always going to be legal.
*/ */
inline const ReferenceCountedObjectPtr<ObjectClass> getUnchecked (const int index) const throw()
inline const ObjectClassPtr getUnchecked (const int index) const throw()
{ {
const ScopedLockType lock (getLock()); const ScopedLockType lock (getLock());
jassert (isPositiveAndBelow (index, numUsed)); jassert (isPositiveAndBelow (index, numUsed));
@@ -11125,7 +11126,7 @@ public:
This will return a null pointer if the array's empty. This will return a null pointer if the array's empty.
@see getLast @see getLast
*/ */
inline const ReferenceCountedObjectPtr<ObjectClass> getFirst() const throw()
inline const ObjectClassPtr getFirst() const throw()
{ {
const ScopedLockType lock (getLock()); const ScopedLockType lock (getLock());
return numUsed > 0 ? data.elements [0] return numUsed > 0 ? data.elements [0]
@@ -11137,7 +11138,7 @@ public:
This will return a null pointer if the array's empty. This will return a null pointer if the array's empty.
@see getFirst @see getFirst
*/ */
inline const ReferenceCountedObjectPtr<ObjectClass> getLast() const throw()
inline const ObjectClassPtr getLast() const throw()
{ {
const ScopedLockType lock (getLock()); const ScopedLockType lock (getLock());
return numUsed > 0 ? data.elements [numUsed - 1] return numUsed > 0 ? data.elements [numUsed - 1]
@@ -11409,6 +11410,43 @@ public:
} }
} }


/** Removes and returns an object from the array.

This will remove the object at a given index and return it, moving back all
the subsequent objects to close the gap. If the index passed in is out-of-range,
nothing will happen and a null pointer will be returned.

@param indexToRemove the index of the element to remove
@see remove, removeObject, removeRange
*/
const ObjectClassPtr removeAndReturn (const int indexToRemove)
{
ObjectClassPtr removedItem;
const ScopedLockType lock (getLock());

if (isPositiveAndBelow (indexToRemove, numUsed))
{
ObjectClass** const e = data.elements + indexToRemove;

if (*e != 0)
{
removedItem = *e;
(*e)->decReferenceCount();
}

--numUsed;
const int numberToShift = numUsed - indexToRemove;

if (numberToShift > 0)
memmove (e, e + 1, numberToShift * sizeof (ObjectClass*));

if ((numUsed << 1) < data.numAllocated)
minimiseStorageOverheads();
}

return removedItem;
}

/** Removes the first occurrence of a specified object from the array. /** Removes the first occurrence of a specified object from the array.


If the item isn't found, no action is taken. If it is found, it is If the item isn't found, no action is taken. If it is found, it is
@@ -12476,6 +12514,123 @@ private:
#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__ #ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__
#define __JUCE_ASYNCUPDATER_JUCEHEADER__ #define __JUCE_ASYNCUPDATER_JUCEHEADER__



/*** Start of inlined file: juce_CallbackMessage.h ***/
#ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__
#define __JUCE_CALLBACKMESSAGE_JUCEHEADER__


/*** Start of inlined file: juce_Message.h ***/
#ifndef __JUCE_MESSAGE_JUCEHEADER__
#define __JUCE_MESSAGE_JUCEHEADER__

class MessageListener;
class MessageManager;

/** The base class for objects that can be delivered to a MessageListener.

The simplest Message object contains a few integer and pointer parameters
that the user can set, and this is enough for a lot of purposes. For passing more
complex data, subclasses of Message can also be used.

@see MessageListener, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API Message : public ReferenceCountedObject
{
public:

/** Creates an uninitialised message.

The class's variables will also be left uninitialised.
*/
Message() throw();

/** Creates a message object, filling in the member variables.

The corresponding public member variables will be set from the parameters
passed in.
*/
Message (int intParameter1,
int intParameter2,
int intParameter3,
void* pointerParameter) throw();

/** Destructor. */
virtual ~Message();

// These values can be used for carrying simple data that the application needs to
// pass around. For more complex messages, just create a subclass.

int intParameter1; /**< user-defined integer value. */
int intParameter2; /**< user-defined integer value. */
int intParameter3; /**< user-defined integer value. */
void* pointerParameter; /**< user-defined pointer value. */

/** A typedef for pointers to messages. */
typedef ReferenceCountedObjectPtr <Message> Ptr;

private:
friend class MessageListener;
friend class MessageManager;
MessageListener* messageRecipient;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message);
};

#endif // __JUCE_MESSAGE_JUCEHEADER__
/*** End of inlined file: juce_Message.h ***/

/**
A message that calls a custom function when it gets delivered.

You can use this class to fire off actions that you want to be performed later
on the message thread.

Unlike other Message objects, these don't get sent to a MessageListener, you
just call the post() method to send them, and when they arrive, your
messageCallback() method will automatically be invoked.

Always create an instance of a CallbackMessage on the heap, as it will be
deleted automatically after the message has been delivered.

@see MessageListener, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API CallbackMessage : public Message
{
public:

CallbackMessage() throw();

/** Destructor. */
~CallbackMessage();

/** Called when the message is delivered.

You should implement this method and make it do whatever action you want
to perform.

Note that like all other messages, this object will be deleted immediately
after this method has been invoked.
*/
virtual void messageCallback() = 0;

/** Instead of sending this message to a MessageListener, just call this method
to post it to the event queue.

After you've called this, this object will belong to the MessageManager,
which will delete it later. So make sure you don't delete the object yourself,
call post() more than once, or call post() on a stack-based obect!
*/
void post();

private:

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackMessage);
};

#endif // __JUCE_CALLBACKMESSAGE_JUCEHEADER__
/*** End of inlined file: juce_CallbackMessage.h ***/

/** /**
Has a callback method that is triggered asynchronously. Has a callback method that is triggered asynchronously.


@@ -12515,6 +12670,10 @@ public:


If called after triggerAsyncUpdate() and before the handleAsyncUpdate() If called after triggerAsyncUpdate() and before the handleAsyncUpdate()
callback happens, this will cancel the handleAsyncUpdate() callback. callback happens, this will cancel the handleAsyncUpdate() callback.

Note that this method simply cancels the next callback - if a callback is already
in progress on a different thread, this won't block until it finishes, so there's
no guarantee that the callback isn't still running when you return from
*/ */
void cancelPendingUpdate() throw(); void cancelPendingUpdate() throw();


@@ -12542,11 +12701,10 @@ public:


private: private:


class AsyncUpdaterMessage;
friend class AsyncUpdaterMessage;
friend class ScopedPointer<AsyncUpdaterMessage>;
ScopedPointer<AsyncUpdaterMessage> message;
Atomic<AsyncUpdaterMessage*> pendingMessage;
ReferenceCountedObjectPtr<CallbackMessage> message;
Atomic<int>& getDeliveryFlag() const throw();

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater);
}; };


#endif // __JUCE_ASYNCUPDATER_JUCEHEADER__ #endif // __JUCE_ASYNCUPDATER_JUCEHEADER__
@@ -28476,64 +28634,6 @@ struct JUCE_API ApplicationCommandInfo
#ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__ #ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__
#define __JUCE_MESSAGELISTENER_JUCEHEADER__ #define __JUCE_MESSAGELISTENER_JUCEHEADER__



/*** Start of inlined file: juce_Message.h ***/
#ifndef __JUCE_MESSAGE_JUCEHEADER__
#define __JUCE_MESSAGE_JUCEHEADER__

class MessageListener;
class MessageManager;

/** The base class for objects that can be delivered to a MessageListener.

The simplest Message object contains a few integer and pointer parameters
that the user can set, and this is enough for a lot of purposes. For passing more
complex data, subclasses of Message can also be used.

@see MessageListener, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API Message
{
public:

/** Creates an uninitialised message.

The class's variables will also be left uninitialised.
*/
Message() throw();

/** Creates a message object, filling in the member variables.

The corresponding public member variables will be set from the parameters
passed in.
*/
Message (int intParameter1,
int intParameter2,
int intParameter3,
void* pointerParameter) throw();

/** Destructor. */
virtual ~Message();

// These values can be used for carrying simple data that the application needs to
// pass around. For more complex messages, just create a subclass.

int intParameter1; /**< user-defined integer value. */
int intParameter2; /**< user-defined integer value. */
int intParameter3; /**< user-defined integer value. */
void* pointerParameter; /**< user-defined pointer value. */

private:
friend class MessageListener;
friend class MessageManager;
MessageListener* messageRecipient;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message);
};

#endif // __JUCE_MESSAGE_JUCEHEADER__
/*** End of inlined file: juce_Message.h ***/

/** /**
MessageListener subclasses can post and receive Message objects. MessageListener subclasses can post and receive Message objects.


@@ -30889,6 +30989,8 @@ public:
A set of routines to convert buffers of 32-bit floating point data to and from A set of routines to convert buffers of 32-bit floating point data to and from
various integer formats. various integer formats.


Note that these functions are deprecated - the AudioData class provides a much more
flexible set of conversion classes now.
*/ */
class JUCE_API AudioDataConverters class JUCE_API AudioDataConverters
{ {
@@ -32681,7 +32783,7 @@ public:
If you're going to generate a thumbnail yourself, call this before using addBlock() If you're going to generate a thumbnail yourself, call this before using addBlock()
to add the data. to add the data.
*/ */
void reset (int numChannels, double sampleRate);
void reset (int numChannels, double sampleRate, int64 totalSamplesInSource = 0);


/** Adds a block of level data to the thumbnail. /** Adds a block of level data to the thumbnail.
Call reset() before using this, to tell the thumbnail about the data format. Call reset() before using this, to tell the thumbnail about the data format.
@@ -43777,76 +43879,6 @@ private:
#endif #endif
#ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__ #ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__


/*** Start of inlined file: juce_CallbackMessage.h ***/
#ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__
#define __JUCE_CALLBACKMESSAGE_JUCEHEADER__

/**
A message that calls a custom function when it gets delivered.

You can use this class to fire off actions that you want to be performed later
on the message thread.

Unlike other Message objects, these don't get sent to a MessageListener, you
just call the post() method to send them, and when they arrive, your
messageCallback() method will automatically be invoked.

Always create an instance of a CallbackMessage on the heap, as it will be
deleted automatically after the message has been delivered.

@see MessageListener, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API CallbackMessage : public Message
{
public:

CallbackMessage() throw();

/** Destructor. */
~CallbackMessage();

/** Called when the message is delivered.

You should implement this method and make it do whatever action you want
to perform.

Note that like all other messages, this object will be deleted immediately
after this method has been invoked.
*/
virtual void messageCallback() = 0;

/** Instead of sending this message to a MessageListener, just call this method
to post it to the event queue.

After you've called this, this object will belong to the MessageManager,
which will delete it later. So make sure you don't delete the object yourself,
call post() more than once, or call post() on a stack-based obect!
*/
void post();

/** This can be used to indicate whether the MessageManager should delete the
message after it has been delivered.
By default, messages will be deleted, but you might want to disable this so that you
can re-use the same message.
*/
void setMessageIsDeletedOnDelivery (bool shouldBeDeleted) throw() { deleteOnDelivery = shouldBeDeleted; }

/** Returns true if the message should be deleted after is has been delivered.
@see setMessageIsDeletedOnDelivery
*/
bool isMessageDeletedOnDelivery() const throw() { return deleteOnDelivery; }

private:

bool deleteOnDelivery;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackMessage);
};

#endif // __JUCE_CALLBACKMESSAGE_JUCEHEADER__
/*** End of inlined file: juce_CallbackMessage.h ***/


#endif #endif
#ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__ #ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__


@@ -44374,11 +44406,9 @@ public:
bool lockWasGained() const throw() { return locked; } bool lockWasGained() const throw() { return locked; }


private: private:
class SharedEvents;
class BlockingMessage; class BlockingMessage;
friend class SharedEvents;
friend class BlockingMessage;
SharedEvents* sharedEvents;
friend class ReferenceCountedObjectPtr<BlockingMessage>;
ReferenceCountedObjectPtr<BlockingMessage> blockingMessage;
bool locked; bool locked;


void init (Thread* thread, ThreadPoolJob* job); void init (Thread* thread, ThreadPoolJob* job);


+ 1
- 1
src/audio/audio_file_formats/juce_AudioFormatWriter.cpp View File

@@ -264,7 +264,7 @@ public:
void setThumbnail (AudioThumbnail* thumb) void setThumbnail (AudioThumbnail* thumb)
{ {
if (thumb != 0) if (thumb != 0)
thumb->reset (buffer.getNumChannels(), writer->getSampleRate());
thumb->reset (buffer.getNumChannels(), writer->getSampleRate(), 0);
const ScopedLock sl (thumbnailLock); const ScopedLock sl (thumbnailLock);
thumbnailToUpdate = thumb; thumbnailToUpdate = thumb;


+ 3
- 2
src/audio/audio_file_formats/juce_AudioThumbnail.cpp View File

@@ -553,14 +553,15 @@ void AudioThumbnail::clear()
sendChangeMessage(); sendChangeMessage();
} }
void AudioThumbnail::reset (int newNumChannels, double newSampleRate)
void AudioThumbnail::reset (int newNumChannels, double newSampleRate, int64 totalSamplesInSource)
{ {
clear(); clear();
numChannels = newNumChannels; numChannels = newNumChannels;
sampleRate = newSampleRate; sampleRate = newSampleRate;
totalSamples = totalSamplesInSource;
createChannels (0);
createChannels (1 + (int) (totalSamplesInSource / samplesPerThumbSample));
} }
void AudioThumbnail::createChannels (const int length) void AudioThumbnail::createChannels (const int length)


+ 1
- 1
src/audio/audio_file_formats/juce_AudioThumbnail.h View File

@@ -113,7 +113,7 @@ public:
If you're going to generate a thumbnail yourself, call this before using addBlock() If you're going to generate a thumbnail yourself, call this before using addBlock()
to add the data. to add the data.
*/ */
void reset (int numChannels, double sampleRate);
void reset (int numChannels, double sampleRate, int64 totalSamplesInSource = 0);
/** Adds a block of level data to the thumbnail. /** Adds a block of level data to the thumbnail.
Call reset() before using this, to tell the thumbnail about the data format. Call reset() before using this, to tell the thumbnail about the data format.


+ 2
- 0
src/audio/dsp/juce_AudioDataConverters.h View File

@@ -581,6 +581,8 @@ public:
A set of routines to convert buffers of 32-bit floating point data to and from A set of routines to convert buffers of 32-bit floating point data to and from
various integer formats. various integer formats.
Note that these functions are deprecated - the AudioData class provides a much more
flexible set of conversion classes now.
*/ */
class JUCE_API AudioDataConverters class JUCE_API AudioDataConverters
{ {


+ 43
- 4
src/containers/juce_ReferenceCountedArray.h View File

@@ -49,6 +49,8 @@ template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSec
class ReferenceCountedArray class ReferenceCountedArray
{ {
public: public:
typedef ReferenceCountedObjectPtr<ObjectClass> ObjectClassPtr;
//============================================================================== //==============================================================================
/** Creates an empty array. /** Creates an empty array.
@see ReferenceCountedObject, Array, OwnedArray @see ReferenceCountedObject, Array, OwnedArray
@@ -126,7 +128,7 @@ public:
@see getUnchecked @see getUnchecked
*/ */
inline const ReferenceCountedObjectPtr<ObjectClass> operator[] (const int index) const throw()
inline const ObjectClassPtr operator[] (const int index) const throw()
{ {
const ScopedLockType lock (getLock()); const ScopedLockType lock (getLock());
return isPositiveAndBelow (index, numUsed) ? data.elements [index] return isPositiveAndBelow (index, numUsed) ? data.elements [index]
@@ -138,7 +140,7 @@ public:
This is a faster and less safe version of operator[] which doesn't check the index passed in, so This is a faster and less safe version of operator[] which doesn't check the index passed in, so
it can be used when you're sure the index if always going to be legal. it can be used when you're sure the index if always going to be legal.
*/ */
inline const ReferenceCountedObjectPtr<ObjectClass> getUnchecked (const int index) const throw()
inline const ObjectClassPtr getUnchecked (const int index) const throw()
{ {
const ScopedLockType lock (getLock()); const ScopedLockType lock (getLock());
jassert (isPositiveAndBelow (index, numUsed)); jassert (isPositiveAndBelow (index, numUsed));
@@ -150,7 +152,7 @@ public:
This will return a null pointer if the array's empty. This will return a null pointer if the array's empty.
@see getLast @see getLast
*/ */
inline const ReferenceCountedObjectPtr<ObjectClass> getFirst() const throw()
inline const ObjectClassPtr getFirst() const throw()
{ {
const ScopedLockType lock (getLock()); const ScopedLockType lock (getLock());
return numUsed > 0 ? data.elements [0] return numUsed > 0 ? data.elements [0]
@@ -162,7 +164,7 @@ public:
This will return a null pointer if the array's empty. This will return a null pointer if the array's empty.
@see getFirst @see getFirst
*/ */
inline const ReferenceCountedObjectPtr<ObjectClass> getLast() const throw()
inline const ObjectClassPtr getLast() const throw()
{ {
const ScopedLockType lock (getLock()); const ScopedLockType lock (getLock());
return numUsed > 0 ? data.elements [numUsed - 1] return numUsed > 0 ? data.elements [numUsed - 1]
@@ -436,6 +438,43 @@ public:
} }
} }
/** Removes and returns an object from the array.
This will remove the object at a given index and return it, moving back all
the subsequent objects to close the gap. If the index passed in is out-of-range,
nothing will happen and a null pointer will be returned.
@param indexToRemove the index of the element to remove
@see remove, removeObject, removeRange
*/
const ObjectClassPtr removeAndReturn (const int indexToRemove)
{
ObjectClassPtr removedItem;
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, numUsed))
{
ObjectClass** const e = data.elements + indexToRemove;
if (*e != 0)
{
removedItem = *e;
(*e)->decReferenceCount();
}
--numUsed;
const int numberToShift = numUsed - indexToRemove;
if (numberToShift > 0)
memmove (e, e + 1, numberToShift * sizeof (ObjectClass*));
if ((numUsed << 1) < data.numAllocated)
minimiseStorageOverheads();
}
return removedItem;
}
/** Removes the first occurrence of a specified object from the array. /** Removes the first occurrence of a specified object from the array.
If the item isn't found, no action is taken. If it is found, it is If the item isn't found, no action is taken. If it is found, it is


+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -33,7 +33,7 @@
*/ */
#define JUCE_MAJOR_VERSION 1 #define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52 #define JUCE_MINOR_VERSION 52
#define JUCE_BUILDNUMBER 106
#define JUCE_BUILDNUMBER 107
/** Current Juce version number. /** Current Juce version number.


+ 15
- 10
src/events/juce_AsyncUpdater.cpp View File

@@ -33,29 +33,35 @@ BEGIN_JUCE_NAMESPACE
//============================================================================== //==============================================================================
class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage
class AsyncUpdaterMessage : public CallbackMessage
{ {
public: public:
AsyncUpdaterMessage (AsyncUpdater& owner_) AsyncUpdaterMessage (AsyncUpdater& owner_)
: owner (owner_) : owner (owner_)
{ {
setMessageIsDeletedOnDelivery (false);
} }
void messageCallback() void messageCallback()
{ {
if (owner.pendingMessage.compareAndSetBool (0, this))
if (shouldDeliver.compareAndSetBool (0, 1))
owner.handleAsyncUpdate(); owner.handleAsyncUpdate();
} }
Atomic<int> shouldDeliver;
private: private:
AsyncUpdater& owner; AsyncUpdater& owner;
}; };
//============================================================================== //==============================================================================
AsyncUpdater::AsyncUpdater() AsyncUpdater::AsyncUpdater()
: message (new AsyncUpdaterMessage (*this))
{ {
message = new AsyncUpdaterMessage (*this);
}
inline Atomic<int>& AsyncUpdater::getDeliveryFlag() const throw()
{
return static_cast <AsyncUpdaterMessage*> (message.getObject())->shouldDeliver;
} }
AsyncUpdater::~AsyncUpdater() AsyncUpdater::~AsyncUpdater()
@@ -66,19 +72,18 @@ AsyncUpdater::~AsyncUpdater()
// deleting this object, or find some other way to avoid such a race condition. // deleting this object, or find some other way to avoid such a race condition.
jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager());
if (pendingMessage.exchange (0) != 0)
message.release()->setMessageIsDeletedOnDelivery (true);
getDeliveryFlag().set (0);
} }
void AsyncUpdater::triggerAsyncUpdate() void AsyncUpdater::triggerAsyncUpdate()
{ {
if (pendingMessage.compareAndSetBool (message, 0))
if (getDeliveryFlag().compareAndSetBool (1, 0))
message->post(); message->post();
} }
void AsyncUpdater::cancelPendingUpdate() throw() void AsyncUpdater::cancelPendingUpdate() throw()
{ {
pendingMessage = 0;
getDeliveryFlag().set (0);
} }
void AsyncUpdater::handleUpdateNowIfNeeded() void AsyncUpdater::handleUpdateNowIfNeeded()
@@ -86,13 +91,13 @@ void AsyncUpdater::handleUpdateNowIfNeeded()
// This can only be called by the event thread. // This can only be called by the event thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
if (pendingMessage.exchange (0) != 0)
if (getDeliveryFlag().exchange (0) != 0)
handleAsyncUpdate(); handleAsyncUpdate();
} }
bool AsyncUpdater::isUpdatePending() const throw() bool AsyncUpdater::isUpdatePending() const throw()
{ {
return pendingMessage.value != 0;
return getDeliveryFlag().value != 0;
} }


+ 9
- 7
src/events/juce_AsyncUpdater.h View File

@@ -27,7 +27,7 @@
#define __JUCE_ASYNCUPDATER_JUCEHEADER__ #define __JUCE_ASYNCUPDATER_JUCEHEADER__
#include "../core/juce_Atomic.h" #include "../core/juce_Atomic.h"
#include "../containers/juce_ScopedPointer.h"
#include "../events/juce_CallbackMessage.h"
//============================================================================== //==============================================================================
@@ -71,6 +71,10 @@ public:
If called after triggerAsyncUpdate() and before the handleAsyncUpdate() If called after triggerAsyncUpdate() and before the handleAsyncUpdate()
callback happens, this will cancel the handleAsyncUpdate() callback. callback happens, this will cancel the handleAsyncUpdate() callback.
Note that this method simply cancels the next callback - if a callback is already
in progress on a different thread, this won't block until it finishes, so there's
no guarantee that the callback isn't still running when you return from
*/ */
void cancelPendingUpdate() throw(); void cancelPendingUpdate() throw();
@@ -97,14 +101,12 @@ public:
*/ */
virtual void handleAsyncUpdate() = 0; virtual void handleAsyncUpdate() = 0;
private: private:
//============================================================================== //==============================================================================
class AsyncUpdaterMessage;
friend class AsyncUpdaterMessage;
friend class ScopedPointer<AsyncUpdaterMessage>;
ScopedPointer<AsyncUpdaterMessage> message;
Atomic<AsyncUpdaterMessage*> pendingMessage;
ReferenceCountedObjectPtr<CallbackMessage> message;
Atomic<int>& getDeliveryFlag() const throw();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater);
}; };


+ 1
- 14
src/events/juce_CallbackMessage.h View File

@@ -28,6 +28,7 @@
#include "juce_Message.h" #include "juce_Message.h"
//============================================================================== //==============================================================================
/** /**
A message that calls a custom function when it gets delivered. A message that calls a custom function when it gets delivered.
@@ -73,22 +74,8 @@ public:
*/ */
void post(); void post();
/** This can be used to indicate whether the MessageManager should delete the
message after it has been delivered.
By default, messages will be deleted, but you might want to disable this so that you
can re-use the same message.
*/
void setMessageIsDeletedOnDelivery (bool shouldBeDeleted) throw() { deleteOnDelivery = shouldBeDeleted; }
/** Returns true if the message should be deleted after is has been delivered.
@see setMessageIsDeletedOnDelivery
*/
bool isMessageDeletedOnDelivery() const throw() { return deleteOnDelivery; }
private: private:
//============================================================================== //==============================================================================
bool deleteOnDelivery;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackMessage); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackMessage);
}; };


+ 5
- 1
src/events/juce_Message.h View File

@@ -26,9 +26,11 @@
#ifndef __JUCE_MESSAGE_JUCEHEADER__ #ifndef __JUCE_MESSAGE_JUCEHEADER__
#define __JUCE_MESSAGE_JUCEHEADER__ #define __JUCE_MESSAGE_JUCEHEADER__
#include "../containers/juce_ReferenceCountedObject.h"
class MessageListener; class MessageListener;
class MessageManager; class MessageManager;
//============================================================================== //==============================================================================
/** The base class for objects that can be delivered to a MessageListener. /** The base class for objects that can be delivered to a MessageListener.
@@ -38,7 +40,7 @@ class MessageManager;
@see MessageListener, MessageManager, ActionListener, ChangeListener @see MessageListener, MessageManager, ActionListener, ChangeListener
*/ */
class JUCE_API Message
class JUCE_API Message : public ReferenceCountedObject
{ {
public: public:
//============================================================================== //==============================================================================
@@ -70,6 +72,8 @@ public:
int intParameter3; /**< user-defined integer value. */ int intParameter3; /**< user-defined integer value. */
void* pointerParameter; /**< user-defined pointer value. */ void* pointerParameter; /**< user-defined pointer value. */
/** A typedef for pointers to messages. */
typedef ReferenceCountedObjectPtr <Message> Ptr;
//============================================================================== //==============================================================================
private: private:


+ 21
- 42
src/events/juce_MessageManager.cpp View File

@@ -84,11 +84,11 @@ MessageManager* MessageManager::getInstance() throw()
void MessageManager::postMessageToQueue (Message* const message) void MessageManager::postMessageToQueue (Message* const message)
{ {
if (quitMessagePosted || ! juce_postMessageToSystemQueue (message)) if (quitMessagePosted || ! juce_postMessageToSystemQueue (message))
delete message;
Message::Ptr deleter (message); // (this will delete messages that were just created with a 0 ref count)
} }
//============================================================================== //==============================================================================
CallbackMessage::CallbackMessage() throw() : deleteOnDelivery (true) {}
CallbackMessage::CallbackMessage() throw() {}
CallbackMessage::~CallbackMessage() {} CallbackMessage::~CallbackMessage() {}
void CallbackMessage::post() void CallbackMessage::post()
@@ -103,7 +103,6 @@ void MessageManager::deliverMessage (Message* const message)
{ {
JUCE_TRY JUCE_TRY
{ {
ScopedPointer <Message> messageDeleter (message);
MessageListener* const recipient = message->messageRecipient; MessageListener* const recipient = message->messageRecipient;
if (recipient == 0) if (recipient == 0)
@@ -113,9 +112,6 @@ void MessageManager::deliverMessage (Message* const message)
if (callbackMessage != 0) if (callbackMessage != 0)
{ {
callbackMessage->messageCallback(); callbackMessage->messageCallback();
if (! callbackMessage->isMessageDeletedOnDelivery())
messageDeleter.release();
} }
else if (message->intParameter1 == quitMessageId) else if (message->intParameter1 == quitMessageId)
{ {
@@ -201,9 +197,11 @@ bool MessageManager::isThisTheMessageThread() const throw()
void MessageManager::setCurrentThreadAsMessageThread() void MessageManager::setCurrentThreadAsMessageThread()
{ {
if (messageThreadId != Thread::getCurrentThreadId())
const Thread::ThreadID thisThread = Thread::getCurrentThreadId();
if (messageThreadId != thisThread)
{ {
messageThreadId = Thread::getCurrentThreadId();
messageThreadId = thisThread;
// This is needed on windows to make sure the message window is created by this thread // This is needed on windows to make sure the message window is created by this thread
doPlatformSpecificShutdown(); doPlatformSpecificShutdown();
@@ -228,48 +226,32 @@ bool MessageManager::currentThreadHasLockedMessageManager() const throw()
accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens
in Cocoa). in Cocoa).
*/ */
class MessageManagerLock::SharedEvents : public ReferenceCountedObject
{
public:
SharedEvents() {}
/* This class just holds a couple of events to communicate between the BlockingMessage
and the MessageManagerLock. Because both of these objects may be deleted at any time,
this shared data must be kept in a separate, ref-counted container. */
WaitableEvent lockedEvent, releaseEvent;
private:
JUCE_DECLARE_NON_COPYABLE (SharedEvents);
};
class MessageManagerLock::BlockingMessage : public CallbackMessage class MessageManagerLock::BlockingMessage : public CallbackMessage
{ {
public: public:
BlockingMessage (MessageManagerLock::SharedEvents* const events_) : events (events_) {}
BlockingMessage() {}
void messageCallback() void messageCallback()
{ {
events->lockedEvent.signal();
events->releaseEvent.wait();
lockedEvent.signal();
releaseEvent.wait();
} }
private:
ReferenceCountedObjectPtr <MessageManagerLock::SharedEvents> events;
WaitableEvent lockedEvent, releaseEvent;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockingMessage); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockingMessage);
}; };
//============================================================================== //==============================================================================
MessageManagerLock::MessageManagerLock (Thread* const threadToCheck) MessageManagerLock::MessageManagerLock (Thread* const threadToCheck)
: sharedEvents (0),
locked (false)
: locked (false)
{ {
init (threadToCheck, 0); init (threadToCheck, 0);
} }
MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal)
: sharedEvents (0),
locked (false)
: locked (false)
{ {
init (0, jobToCheckForExitSignal); init (0, jobToCheckForExitSignal);
} }
@@ -300,19 +282,16 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const
} }
} }
sharedEvents = new SharedEvents();
sharedEvents->incReferenceCount();
(new BlockingMessage (sharedEvents))->post();
blockingMessage = new BlockingMessage();
blockingMessage->post();
while (! sharedEvents->lockedEvent.wait (50))
while (! blockingMessage->lockedEvent.wait (20))
{ {
if ((threadToCheck != 0 && threadToCheck->threadShouldExit()) if ((threadToCheck != 0 && threadToCheck->threadShouldExit())
|| (job != 0 && job->shouldExit())) || (job != 0 && job->shouldExit()))
{ {
sharedEvents->releaseEvent.signal();
sharedEvents->decReferenceCount();
sharedEvents = 0;
blockingMessage->releaseEvent.signal();
blockingMessage = 0;
MessageManager::instance->lockingLock.exit(); MessageManager::instance->lockingLock.exit();
return; return;
} }
@@ -328,12 +307,12 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const
MessageManagerLock::~MessageManagerLock() throw() MessageManagerLock::~MessageManagerLock() throw()
{ {
if (sharedEvents != 0)
if (blockingMessage != 0)
{ {
jassert (MessageManager::instance == 0 || MessageManager::instance->currentThreadHasLockedMessageManager()); jassert (MessageManager::instance == 0 || MessageManager::instance->currentThreadHasLockedMessageManager());
sharedEvents->releaseEvent.signal();
sharedEvents->decReferenceCount();
blockingMessage->releaseEvent.signal();
blockingMessage = 0;
if (MessageManager::instance != 0) if (MessageManager::instance != 0)
{ {


+ 2
- 4
src/events/juce_MessageManager.h View File

@@ -299,11 +299,9 @@ public:
private: private:
class SharedEvents;
class BlockingMessage; class BlockingMessage;
friend class SharedEvents;
friend class BlockingMessage;
SharedEvents* sharedEvents;
friend class ReferenceCountedObjectPtr<BlockingMessage>;
ReferenceCountedObjectPtr<BlockingMessage> blockingMessage;
bool locked; bool locked;
void init (Thread* thread, ThreadPoolJob* job); void init (Thread* thread, ThreadPoolJob* job);


+ 2
- 1
src/events/juce_Timer.cpp View File

@@ -64,6 +64,7 @@ public:
void run() void run()
{ {
uint32 lastTime = Time::getMillisecondCounter(); uint32 lastTime = Time::getMillisecondCounter();
Message::Ptr message (new Message());
while (! threadShouldExit()) while (! threadShouldExit())
{ {
@@ -89,7 +90,7 @@ public:
*/ */
if (callbackNeeded.compareAndSetBool (1, 0)) if (callbackNeeded.compareAndSetBool (1, 0))
{ {
postMessage (new Message());
postMessage (message);
/* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS /* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
when the app has a modal loop), so this is how long to wait before assuming the when the app has a modal loop), so this is how long to wait before assuming the


+ 12
- 0
src/gui/components/juce_Component.cpp View File

@@ -1536,6 +1536,10 @@ void Component::exitModalState (const int returnValue)
} }
else else
{ {
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
CHECK_MESSAGE_MANAGER_IS_LOCKED
class ExitModalStateMessage : public CallbackMessage class ExitModalStateMessage : public CallbackMessage
{ {
public: public:
@@ -2111,6 +2115,10 @@ void Component::parentSizeChanged()
void Component::addComponentListener (ComponentListener* const newListener) void Component::addComponentListener (ComponentListener* const newListener)
{ {
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
CHECK_MESSAGE_MANAGER_IS_LOCKED
componentListeners.add (newListener); componentListeners.add (newListener);
} }
@@ -2156,6 +2164,10 @@ void Component::paintOverChildren (Graphics&)
//============================================================================== //==============================================================================
void Component::postCommandMessage (const int commandId) void Component::postCommandMessage (const int commandId)
{ {
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
CHECK_MESSAGE_MANAGER_IS_LOCKED
class CustomCommandMessage : public CallbackMessage class CustomCommandMessage : public CallbackMessage
{ {
public: public:


+ 69
- 0
src/native/juce_mac_NativeCode.mm View File

@@ -63,6 +63,7 @@ BEGIN_JUCE_NAMESPACE
#include "../application/juce_Application.h" #include "../application/juce_Application.h"
#include "../utilities/juce_SystemClipboard.h" #include "../utilities/juce_SystemClipboard.h"
#include "../events/juce_MessageManager.h" #include "../events/juce_MessageManager.h"
#include "../containers/juce_ReferenceCountedArray.h"
#include "../gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" #include "../gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h"
#include "../gui/graphics/imaging/juce_ImageFileFormat.h" #include "../gui/graphics/imaging/juce_ImageFileFormat.h"
#include "../gui/graphics/imaging/juce_CameraDevice.h" #include "../gui/graphics/imaging/juce_CameraDevice.h"
@@ -112,6 +113,74 @@ namespace
} }
} }
//==============================================================================
class MessageQueue
{
public:
MessageQueue()
{
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 && ! JUCE_IOS
runLoop = CFRunLoopGetMain();
#else
runLoop = CFRunLoopGetCurrent();
#endif
CFRunLoopSourceContext sourceContext;
zerostruct (sourceContext);
sourceContext.info = this;
sourceContext.perform = runLoopSourceCallback;
runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext);
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
}
~MessageQueue()
{
CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
CFRunLoopSourceInvalidate (runLoopSource);
CFRelease (runLoopSource);
}
void post (Message* const message)
{
messages.add (message);
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}
private:
ReferenceCountedArray <Message, CriticalSection> messages;
CriticalSection lock;
CFRunLoopRef runLoop;
CFRunLoopSourceRef runLoopSource;
bool deliverNextMessage()
{
const Message::Ptr nextMessage (messages.removeAndReturn (0));
if (nextMessage == 0)
return false;
const ScopedAutoReleasePool pool;
MessageManager::getInstance()->deliverMessage (nextMessage);
return true;
}
void runLoopCallback()
{
for (int i = 4; --i >= 0;)
if (! deliverNextMessage())
return;
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}
static void runLoopSourceCallback (void* info)
{
static_cast <MessageQueue*> (info)->runLoopCallback();
}
};
//============================================================================== //==============================================================================
#define JUCE_INCLUDED_FILE 1 #define JUCE_INCLUDED_FILE 1


+ 6
- 6
src/native/linux/juce_linux_Messaging.cpp View File

@@ -153,7 +153,7 @@ public:
private: private:
CriticalSection lock; CriticalSection lock;
OwnedArray <Message> queue;
ReferenceCountedArray <Message> queue;
int fd[2]; int fd[2];
int bytesInSocket; int bytesInSocket;
int totalEventCount; int totalEventCount;
@@ -193,15 +193,15 @@ private:
return true; return true;
} }
Message* popNextMessage()
const Message::Ptr popNextMessage()
{ {
ScopedLock sl (lock);
const ScopedLock sl (lock);
if (bytesInSocket > 0) if (bytesInSocket > 0)
{ {
--bytesInSocket; --bytesInSocket;
ScopedUnlock ul (lock);
const ScopedUnlock ul (lock);
unsigned char x; unsigned char x;
size_t numBytes = read (fd[1], &x, 1); size_t numBytes = read (fd[1], &x, 1);
(void) numBytes; (void) numBytes;
@@ -212,7 +212,7 @@ private:
bool dispatchNextInternalMessage() bool dispatchNextInternalMessage()
{ {
ScopedPointer <Message> msg (popNextMessage());
const Message::Ptr msg (popNextMessage());
if (msg == 0) if (msg == 0)
return false; return false;
@@ -228,7 +228,7 @@ private:
else else
{ {
// Handle "normal" messages // Handle "normal" messages
MessageManager::getInstance()->deliverMessage (msg.release());
MessageManager::getInstance()->deliverMessage (msg);
} }
return true; return true;


+ 26
- 64
src/native/mac/juce_iphone_MessageManager.mm View File

@@ -107,79 +107,41 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
} }
//============================================================================== //==============================================================================
namespace iOSMessageLoopHelpers
struct MessageDispatchSystem
{ {
static CFRunLoopRef runLoop = 0;
static CFRunLoopSourceRef runLoopSource = 0;
static OwnedArray <Message, CriticalSection>* pendingMessages = 0;
static JuceCustomMessageHandler* juceCustomMessageHandler = 0;
void runLoopSourceCallback (void*)
MessageDispatchSystem()
: juceCustomMessageHandler (0)
{ {
if (pendingMessages != 0)
{
int numDispatched = 0;
do
{
Message* const nextMessage = pendingMessages->removeAndReturn (0);
if (nextMessage == 0)
return;
juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init];
}
const ScopedAutoReleasePool pool;
MessageManager::getInstance()->deliverMessage (nextMessage);
~MessageDispatchSystem()
{
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler];
[juceCustomMessageHandler release];
}
} while (++numDispatched <= 4);
JuceCustomMessageHandler* juceCustomMessageHandler;
MessageQueue messageQueue;
};
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}
}
}
static MessageDispatchSystem* dispatcher = 0;
void MessageManager::doPlatformSpecificInitialisation() void MessageManager::doPlatformSpecificInitialisation()
{ {
using namespace iOSMessageLoopHelpers;
pendingMessages = new OwnedArray <Message, CriticalSection>();
runLoop = CFRunLoopGetCurrent();
CFRunLoopSourceContext sourceContext;
zerostruct (sourceContext);
sourceContext.perform = runLoopSourceCallback;
runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext);
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
if (juceCustomMessageHandler == 0)
juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init];
if (dispatcher == 0)
dispatcher = new MessageDispatchSystem();
} }
void MessageManager::doPlatformSpecificShutdown() void MessageManager::doPlatformSpecificShutdown()
{ {
using namespace iOSMessageLoopHelpers;
CFRunLoopSourceInvalidate (runLoopSource);
CFRelease (runLoopSource);
runLoopSource = 0;
deleteAndZero (pendingMessages);
if (juceCustomMessageHandler != 0)
{
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler];
[juceCustomMessageHandler release];
juceCustomMessageHandler = 0;
}
deleteAndZero (dispatcher);
} }
bool juce_postMessageToSystemQueue (Message* message) bool juce_postMessageToSystemQueue (Message* message)
{ {
using namespace iOSMessageLoopHelpers;
if (pendingMessages != 0)
{
pendingMessages->add (message);
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}
if (dispatcher != 0)
dispatcher->messageQueue.post (message);
return true; return true;
} }
@@ -190,14 +152,14 @@ void MessageManager::broadcastMessage (const String& value)
void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, void* data) void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, void* data)
{ {
using namespace iOSMessageLoopHelpers;
if (isThisTheMessageThread()) if (isThisTheMessageThread())
{ {
return (*callback) (data); return (*callback) (data);
} }
else else
{ {
jassert (dispatcher != 0); // trying to call this when the juce system isn't initialised..
// If a thread has a MessageManagerLock and then tries to call this method, it'll // If a thread has a MessageManagerLock and then tries to call this method, it'll
// deadlock because the message manager is blocked from running, so can never // deadlock because the message manager is blocked from running, so can never
// call your function.. // call your function..
@@ -211,11 +173,11 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call
cmp.result = 0; cmp.result = 0;
cmp.hasBeenExecuted = false; cmp.hasBeenExecuted = false;
[juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:)
withObject: [NSData dataWithBytesNoCopy: &cmp
length: sizeof (cmp)
freeWhenDone: NO]
waitUntilDone: YES];
[dispatcher->juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:)
withObject: [NSData dataWithBytesNoCopy: &cmp
length: sizeof (cmp)
freeWhenDone: NO]
waitUntilDone: YES];
return cmp.result; return cmp.result;
} }


+ 3
- 3
src/native/mac/juce_mac_Files.mm View File

@@ -182,12 +182,12 @@ const File File::getSpecialLocation (const SpecialLocationType type)
case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break; case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break;
#if JUCE_IOS #if JUCE_IOS
case userDocumentsDirectory: resultPath = getIOSSystemLocation (NSDocumentDirectory); break;
case userDesktopDirectory: resultPath = getIOSSystemLocation (NSDesktopDirectory); break;
case userDocumentsDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDocumentDirectory); break;
case userDesktopDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDesktopDirectory); break;
case tempDirectory: case tempDirectory:
{ {
File tmp (getIOSSystemLocation (NSCachesDirectory));
File tmp (FileHelpers::getIOSSystemLocation (NSCachesDirectory));
tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension()); tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension());
tmp.createDirectory(); tmp.createDirectory();
return tmp.getFullPathName(); return tmp.getFullPathName();


+ 2
- 43
src/native/mac/juce_mac_MessageManager.mm View File

@@ -44,24 +44,10 @@ class AppDelegateRedirector
public: public:
AppDelegateRedirector() AppDelegateRedirector()
{ {
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
runLoop = CFRunLoopGetMain();
#else
runLoop = CFRunLoopGetCurrent();
#endif
CFRunLoopSourceContext sourceContext;
zerostruct (sourceContext);
sourceContext.info = this;
sourceContext.perform = runLoopSourceCallback;
runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext);
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
} }
virtual ~AppDelegateRedirector() virtual ~AppDelegateRedirector()
{ {
CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
CFRunLoopSourceInvalidate (runLoopSource);
CFRelease (runLoopSource);
} }
virtual NSApplicationTerminateReply shouldTerminate() virtual NSApplicationTerminateReply shouldTerminate()
@@ -137,40 +123,13 @@ public:
void postMessage (Message* const m) void postMessage (Message* const m)
{ {
messages.add (m);
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
messageQueue.post (m);
} }
private: private:
CFRunLoopRef runLoop; CFRunLoopRef runLoop;
CFRunLoopSourceRef runLoopSource; CFRunLoopSourceRef runLoopSource;
OwnedArray <Message, CriticalSection> messages;
void runLoopCallback()
{
int numDispatched = 0;
do
{
Message* const nextMessage = messages.removeAndReturn (0);
if (nextMessage == 0)
return;
const ScopedAutoReleasePool pool;
MessageManager::getInstance()->deliverMessage (nextMessage);
} while (++numDispatched <= 4);
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}
static void runLoopSourceCallback (void* info)
{
static_cast <AppDelegateRedirector*> (info)->runLoopCallback();
}
MessageQueue messageQueue;
}; };


+ 7
- 2
src/native/windows/juce_win32_Messaging.cpp View File

@@ -65,7 +65,9 @@ static LRESULT CALLBACK juce_MessageWndProc (HWND h,
// here in case there are windows modal dialog boxes doing their own // here in case there are windows modal dialog boxes doing their own
// dispatch loop and not calling our version // dispatch loop and not calling our version
MessageManager::getInstance()->deliverMessage ((Message*) lParam);
Message* const message = reinterpret_cast <Message*> (lParam);
MessageManager::getInstance()->deliverMessage (message);
message->decReferenceCount();
return 0; return 0;
} }
else if (message == broadcastId) else if (message == broadcastId)
@@ -153,7 +155,9 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages
{ {
if (m.message == specialId && m.hwnd == juce_messageWindowHandle) if (m.message == specialId && m.hwnd == juce_messageWindowHandle)
{ {
MessageManager::getInstance()->deliverMessage ((Message*) (void*) m.lParam);
Message* const message = reinterpret_cast <Message*> (m.lParam);
MessageManager::getInstance()->deliverMessage (message);
message->decReferenceCount();
} }
else if (m.message == WM_QUIT) else if (m.message == WM_QUIT)
{ {
@@ -184,6 +188,7 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages
//============================================================================== //==============================================================================
bool juce_postMessageToSystemQueue (Message* message) bool juce_postMessageToSystemQueue (Message* message)
{ {
message->incReferenceCount();
return PostMessage (juce_messageWindowHandle, specialId, 0, (LPARAM) message) != 0; return PostMessage (juce_messageWindowHandle, specialId, 0, (LPARAM) message) != 0;
} }


Loading…
Cancel
Save