git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
92
deps/juce/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp
vendored
Normal file
92
deps/juce/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class ActionBroadcaster::ActionMessage : public MessageManager::MessageBase
|
||||
{
|
||||
public:
|
||||
ActionMessage (const ActionBroadcaster* ab,
|
||||
const String& messageText, ActionListener* l) noexcept
|
||||
: broadcaster (const_cast<ActionBroadcaster*> (ab)),
|
||||
message (messageText),
|
||||
listener (l)
|
||||
{}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (auto b = broadcaster.get())
|
||||
if (b->actionListeners.contains (listener))
|
||||
listener->actionListenerCallback (message);
|
||||
}
|
||||
|
||||
private:
|
||||
WeakReference<ActionBroadcaster> broadcaster;
|
||||
const String message;
|
||||
ActionListener* const listener;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ActionMessage)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ActionBroadcaster::ActionBroadcaster()
|
||||
{
|
||||
// are you trying to create this object before or after juce has been initialised??
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
}
|
||||
|
||||
ActionBroadcaster::~ActionBroadcaster()
|
||||
{
|
||||
// all event-based objects must be deleted BEFORE juce is shut down!
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
}
|
||||
|
||||
void ActionBroadcaster::addActionListener (ActionListener* const listener)
|
||||
{
|
||||
const ScopedLock sl (actionListenerLock);
|
||||
|
||||
if (listener != nullptr)
|
||||
actionListeners.add (listener);
|
||||
}
|
||||
|
||||
void ActionBroadcaster::removeActionListener (ActionListener* const listener)
|
||||
{
|
||||
const ScopedLock sl (actionListenerLock);
|
||||
actionListeners.removeValue (listener);
|
||||
}
|
||||
|
||||
void ActionBroadcaster::removeAllActionListeners()
|
||||
{
|
||||
const ScopedLock sl (actionListenerLock);
|
||||
actionListeners.clear();
|
||||
}
|
||||
|
||||
void ActionBroadcaster::sendActionMessage (const String& message) const
|
||||
{
|
||||
const ScopedLock sl (actionListenerLock);
|
||||
|
||||
for (int i = actionListeners.size(); --i >= 0;)
|
||||
(new ActionMessage (this, message, actionListeners.getUnchecked(i)))->post();
|
||||
}
|
||||
|
||||
} // namespace juce
|
79
deps/juce/modules/juce_events/broadcasters/juce_ActionBroadcaster.h
vendored
Normal file
79
deps/juce/modules/juce_events/broadcasters/juce_ActionBroadcaster.h
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** Manages a list of ActionListeners, and can send them messages.
|
||||
|
||||
To quickly add methods to your class that can add/remove action
|
||||
listeners and broadcast to them, you can derive from this.
|
||||
|
||||
@see ActionListener, ChangeListener
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API ActionBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ActionBroadcaster. */
|
||||
ActionBroadcaster();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ActionBroadcaster();
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener to the list.
|
||||
Trying to add a listener that's already on the list will have no effect.
|
||||
*/
|
||||
void addActionListener (ActionListener* listener);
|
||||
|
||||
/** Removes a listener from the list.
|
||||
If the listener isn't on the list, this won't have any effect.
|
||||
*/
|
||||
void removeActionListener (ActionListener* listener);
|
||||
|
||||
/** Removes all listeners from the list. */
|
||||
void removeAllActionListeners();
|
||||
|
||||
//==============================================================================
|
||||
/** Broadcasts a message to all the registered listeners.
|
||||
@see ActionListener::actionListenerCallback
|
||||
*/
|
||||
void sendActionMessage (const String& message) const;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ActionMessage;
|
||||
friend class ActionMessage;
|
||||
|
||||
SortedSet<ActionListener*> actionListeners;
|
||||
CriticalSection actionListenerLock;
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ActionBroadcaster)
|
||||
JUCE_DECLARE_NON_COPYABLE (ActionBroadcaster)
|
||||
};
|
||||
|
||||
} // namespace juce
|
48
deps/juce/modules/juce_events/broadcasters/juce_ActionListener.h
vendored
Normal file
48
deps/juce/modules/juce_events/broadcasters/juce_ActionListener.h
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Interface class for delivery of events that are sent by an ActionBroadcaster.
|
||||
|
||||
@see ActionBroadcaster, ChangeListener
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API ActionListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ActionListener() = default;
|
||||
|
||||
/** Overridden by your subclass to receive the callback.
|
||||
|
||||
@param message the string that was specified when the event was triggered
|
||||
by a call to ActionBroadcaster::sendActionMessage()
|
||||
*/
|
||||
virtual void actionListenerCallback (const String& message) = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
93
deps/juce/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp
vendored
Normal file
93
deps/juce/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage
|
||||
{
|
||||
public:
|
||||
AsyncUpdaterMessage (AsyncUpdater& au) : owner (au) {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (shouldDeliver.compareAndSetBool (0, 1))
|
||||
owner.handleAsyncUpdate();
|
||||
}
|
||||
|
||||
AsyncUpdater& owner;
|
||||
Atomic<int> shouldDeliver;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AsyncUpdaterMessage)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AsyncUpdater::AsyncUpdater()
|
||||
{
|
||||
activeMessage = *new AsyncUpdaterMessage (*this);
|
||||
}
|
||||
|
||||
AsyncUpdater::~AsyncUpdater()
|
||||
{
|
||||
// You're deleting this object with a background thread while there's an update
|
||||
// pending on the main event thread - that's pretty dodgy threading, as the callback could
|
||||
// happen after this destructor has finished. You should either use a MessageManagerLock while
|
||||
// deleting this object, or find some other way to avoid such a race condition.
|
||||
jassert ((! isUpdatePending())
|
||||
|| MessageManager::getInstanceWithoutCreating() == nullptr
|
||||
|| MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
|
||||
|
||||
activeMessage->shouldDeliver.set (0);
|
||||
}
|
||||
|
||||
void AsyncUpdater::triggerAsyncUpdate()
|
||||
{
|
||||
// If you're calling this before (or after) the MessageManager is
|
||||
// running, then you're not going to get any callbacks!
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
|
||||
if (activeMessage->shouldDeliver.compareAndSetBool (1, 0))
|
||||
if (! activeMessage->post())
|
||||
cancelPendingUpdate(); // if the message queue fails, this avoids getting
|
||||
// trapped waiting for the message to arrive
|
||||
}
|
||||
|
||||
void AsyncUpdater::cancelPendingUpdate() noexcept
|
||||
{
|
||||
activeMessage->shouldDeliver.set (0);
|
||||
}
|
||||
|
||||
void AsyncUpdater::handleUpdateNowIfNeeded()
|
||||
{
|
||||
// This can only be called by the event thread.
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
if (activeMessage->shouldDeliver.exchange (0) != 0)
|
||||
handleAsyncUpdate();
|
||||
}
|
||||
|
||||
bool AsyncUpdater::isUpdatePending() const noexcept
|
||||
{
|
||||
return activeMessage->shouldDeliver.value != 0;
|
||||
}
|
||||
|
||||
} // namespace juce
|
110
deps/juce/modules/juce_events/broadcasters/juce_AsyncUpdater.h
vendored
Normal file
110
deps/juce/modules/juce_events/broadcasters/juce_AsyncUpdater.h
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Has a callback method that is triggered asynchronously.
|
||||
|
||||
This object allows an asynchronous callback function to be triggered, for
|
||||
tasks such as coalescing multiple updates into a single callback later on.
|
||||
|
||||
Basically, one or more calls to the triggerAsyncUpdate() will result in the
|
||||
message thread calling handleAsyncUpdate() as soon as it can.
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API AsyncUpdater
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an AsyncUpdater object. */
|
||||
AsyncUpdater();
|
||||
|
||||
/** Destructor.
|
||||
If there are any pending callbacks when the object is deleted, these are lost.
|
||||
*/
|
||||
virtual ~AsyncUpdater();
|
||||
|
||||
//==============================================================================
|
||||
/** Causes the callback to be triggered at a later time.
|
||||
|
||||
This method returns immediately, after which a callback to the
|
||||
handleAsyncUpdate() method will be made by the message thread as
|
||||
soon as possible.
|
||||
|
||||
If an update callback is already pending but hasn't happened yet, calling
|
||||
this method will have no effect.
|
||||
|
||||
It's thread-safe to call this method from any thread, BUT beware of calling
|
||||
it from a real-time (e.g. audio) thread, because it involves posting a message
|
||||
to the system queue, which means it may block (and in general will do on
|
||||
most OSes).
|
||||
*/
|
||||
void triggerAsyncUpdate();
|
||||
|
||||
/** This will stop any pending updates from happening.
|
||||
|
||||
If called after triggerAsyncUpdate() and before the handleAsyncUpdate()
|
||||
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 the callback finishes, so
|
||||
there's no guarantee that the callback isn't still running when the method returns.
|
||||
*/
|
||||
void cancelPendingUpdate() noexcept;
|
||||
|
||||
/** If an update has been triggered and is pending, this will invoke it
|
||||
synchronously.
|
||||
|
||||
Use this as a kind of "flush" operation - if an update is pending, the
|
||||
handleAsyncUpdate() method will be called immediately; if no update is
|
||||
pending, then nothing will be done.
|
||||
|
||||
Because this may invoke the callback, this method must only be called on
|
||||
the main event thread.
|
||||
*/
|
||||
void handleUpdateNowIfNeeded();
|
||||
|
||||
/** Returns true if there's an update callback in the pipeline. */
|
||||
bool isUpdatePending() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Called back to do whatever your class needs to do.
|
||||
|
||||
This method is called by the message thread at the next convenient time
|
||||
after the triggerAsyncUpdate() method has been called.
|
||||
*/
|
||||
virtual void handleAsyncUpdate() = 0;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class AsyncUpdaterMessage;
|
||||
friend class ReferenceCountedObjectPtr<AsyncUpdaterMessage>;
|
||||
ReferenceCountedObjectPtr<AsyncUpdaterMessage> activeMessage;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater)
|
||||
};
|
||||
|
||||
} // namespace juce
|
102
deps/juce/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp
vendored
Normal file
102
deps/juce/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ChangeBroadcaster::ChangeBroadcaster() noexcept
|
||||
{
|
||||
broadcastCallback.owner = this;
|
||||
}
|
||||
|
||||
ChangeBroadcaster::~ChangeBroadcaster()
|
||||
{
|
||||
}
|
||||
|
||||
void ChangeBroadcaster::addChangeListener (ChangeListener* const listener)
|
||||
{
|
||||
// Listeners can only be safely added when the event thread is locked
|
||||
// You can use a MessageManagerLock if you need to call this from another thread.
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
changeListeners.add (listener);
|
||||
anyListeners = true;
|
||||
}
|
||||
|
||||
void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener)
|
||||
{
|
||||
// Listeners can only be safely removed when the event thread is locked
|
||||
// You can use a MessageManagerLock if you need to call this from another thread.
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
changeListeners.remove (listener);
|
||||
anyListeners = changeListeners.size() > 0;
|
||||
}
|
||||
|
||||
void ChangeBroadcaster::removeAllChangeListeners()
|
||||
{
|
||||
// Listeners can only be safely removed when the event thread is locked
|
||||
// You can use a MessageManagerLock if you need to call this from another thread.
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
changeListeners.clear();
|
||||
anyListeners = false;
|
||||
}
|
||||
|
||||
void ChangeBroadcaster::sendChangeMessage()
|
||||
{
|
||||
if (anyListeners)
|
||||
broadcastCallback.triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void ChangeBroadcaster::sendSynchronousChangeMessage()
|
||||
{
|
||||
// This can only be called by the event thread.
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
broadcastCallback.cancelPendingUpdate();
|
||||
callListeners();
|
||||
}
|
||||
|
||||
void ChangeBroadcaster::dispatchPendingMessages()
|
||||
{
|
||||
broadcastCallback.handleUpdateNowIfNeeded();
|
||||
}
|
||||
|
||||
void ChangeBroadcaster::callListeners()
|
||||
{
|
||||
changeListeners.call ([this] (ChangeListener& l) { l.changeListenerCallback (this); });
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ChangeBroadcaster::ChangeBroadcasterCallback::ChangeBroadcasterCallback()
|
||||
: owner (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void ChangeBroadcaster::ChangeBroadcasterCallback::handleAsyncUpdate()
|
||||
{
|
||||
jassert (owner != nullptr);
|
||||
owner->callListeners();
|
||||
}
|
||||
|
||||
} // namespace juce
|
105
deps/juce/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h
vendored
Normal file
105
deps/juce/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a list of ChangeListeners, and sends messages to them when instructed.
|
||||
|
||||
@see ChangeListener
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ChangeBroadcaster. */
|
||||
ChangeBroadcaster() noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ChangeBroadcaster();
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a listener to receive change callbacks from this broadcaster.
|
||||
Trying to add a listener that's already on the list will have no effect.
|
||||
*/
|
||||
void addChangeListener (ChangeListener* listener);
|
||||
|
||||
/** Unregisters a listener from the list.
|
||||
If the listener isn't on the list, this won't have any effect.
|
||||
*/
|
||||
void removeChangeListener (ChangeListener* listener);
|
||||
|
||||
/** Removes all listeners from the list. */
|
||||
void removeAllChangeListeners();
|
||||
|
||||
//==============================================================================
|
||||
/** Causes an asynchronous change message to be sent to all the registered listeners.
|
||||
|
||||
The message will be delivered asynchronously by the main message thread, so this
|
||||
method will return immediately. To call the listeners synchronously use
|
||||
sendSynchronousChangeMessage().
|
||||
*/
|
||||
void sendChangeMessage();
|
||||
|
||||
/** Sends a synchronous change message to all the registered listeners.
|
||||
|
||||
This will immediately call all the listeners that are registered. For thread-safety
|
||||
reasons, you must only call this method on the main message thread.
|
||||
|
||||
@see dispatchPendingMessages
|
||||
*/
|
||||
void sendSynchronousChangeMessage();
|
||||
|
||||
/** If a change message has been sent but not yet dispatched, this will call
|
||||
sendSynchronousChangeMessage() to make the callback immediately.
|
||||
|
||||
For thread-safety reasons, you must only call this method on the main message thread.
|
||||
*/
|
||||
void dispatchPendingMessages();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ChangeBroadcasterCallback : public AsyncUpdater
|
||||
{
|
||||
public:
|
||||
ChangeBroadcasterCallback();
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
ChangeBroadcaster* owner;
|
||||
};
|
||||
|
||||
friend class ChangeBroadcasterCallback;
|
||||
ChangeBroadcasterCallback broadcastCallback;
|
||||
ListenerList <ChangeListener> changeListeners;
|
||||
|
||||
std::atomic<bool> anyListeners { false };
|
||||
|
||||
void callListeners();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ChangeBroadcaster)
|
||||
};
|
||||
|
||||
} // namespace juce
|
56
deps/juce/modules/juce_events/broadcasters/juce_ChangeListener.h
vendored
Normal file
56
deps/juce/modules/juce_events/broadcasters/juce_ChangeListener.h
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class ChangeBroadcaster;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Receives change event callbacks that are sent out by a ChangeBroadcaster.
|
||||
|
||||
A ChangeBroadcaster keeps a set of listeners to which it broadcasts a message when
|
||||
the ChangeBroadcaster::sendChangeMessage() method is called. A subclass of
|
||||
ChangeListener is used to receive these callbacks.
|
||||
|
||||
Note that the major difference between an ActionListener and a ChangeListener
|
||||
is that for a ChangeListener, multiple changes will be coalesced into fewer
|
||||
callbacks, but ActionListeners perform one callback for every event posted.
|
||||
|
||||
@see ChangeBroadcaster, ActionListener
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API ChangeListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ChangeListener() = default;
|
||||
|
||||
/** Your subclass should implement this method to receive the callback.
|
||||
@param source the ChangeBroadcaster that triggered the callback.
|
||||
*/
|
||||
virtual void changeListenerCallback (ChangeBroadcaster* source) = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
267
deps/juce/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp
vendored
Normal file
267
deps/juce/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
enum { magicMastSlaveConnectionHeader = 0x712baf04 };
|
||||
|
||||
static const char* startMessage = "__ipc_st";
|
||||
static const char* killMessage = "__ipc_k_";
|
||||
static const char* pingMessage = "__ipc_p_";
|
||||
enum { specialMessageSize = 8, defaultTimeoutMs = 8000 };
|
||||
|
||||
static bool isMessageType (const MemoryBlock& mb, const char* messageType) noexcept
|
||||
{
|
||||
return mb.matches (messageType, (size_t) specialMessageSize);
|
||||
}
|
||||
|
||||
static String getCommandLinePrefix (const String& commandLineUniqueID)
|
||||
{
|
||||
return "--" + commandLineUniqueID + ":";
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// This thread sends and receives ping messages every second, so that it
|
||||
// can find out if the other process has stopped running.
|
||||
struct ChildProcessPingThread : public Thread,
|
||||
private AsyncUpdater
|
||||
{
|
||||
ChildProcessPingThread (int timeout) : Thread ("IPC ping"), timeoutMs (timeout)
|
||||
{
|
||||
pingReceived();
|
||||
}
|
||||
|
||||
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
|
||||
void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
|
||||
|
||||
virtual bool sendPingMessage (const MemoryBlock&) = 0;
|
||||
virtual void pingFailed() = 0;
|
||||
|
||||
int timeoutMs;
|
||||
|
||||
private:
|
||||
Atomic<int> countdown;
|
||||
|
||||
void handleAsyncUpdate() override { pingFailed(); }
|
||||
|
||||
void run() override
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (--countdown <= 0 || ! sendPingMessage ({ pingMessage, specialMessageSize }))
|
||||
{
|
||||
triggerConnectionLostMessage();
|
||||
break;
|
||||
}
|
||||
|
||||
wait (1000);
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessPingThread)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ChildProcessMaster::Connection : public InterprocessConnection,
|
||||
private ChildProcessPingThread
|
||||
{
|
||||
Connection (ChildProcessMaster& m, const String& pipeName, int timeout)
|
||||
: InterprocessConnection (false, magicMastSlaveConnectionHeader),
|
||||
ChildProcessPingThread (timeout),
|
||||
owner (m)
|
||||
{
|
||||
if (createPipe (pipeName, timeoutMs))
|
||||
startThread (4);
|
||||
}
|
||||
|
||||
~Connection() override
|
||||
{
|
||||
stopThread (10000);
|
||||
}
|
||||
|
||||
private:
|
||||
void connectionMade() override {}
|
||||
void connectionLost() override { owner.handleConnectionLost(); }
|
||||
|
||||
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); }
|
||||
void pingFailed() override { connectionLost(); }
|
||||
|
||||
void messageReceived (const MemoryBlock& m) override
|
||||
{
|
||||
pingReceived();
|
||||
|
||||
if (m.getSize() != specialMessageSize || ! isMessageType (m, pingMessage))
|
||||
owner.handleMessageFromSlave (m);
|
||||
}
|
||||
|
||||
ChildProcessMaster& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ChildProcessMaster::ChildProcessMaster() {}
|
||||
|
||||
ChildProcessMaster::~ChildProcessMaster()
|
||||
{
|
||||
killSlaveProcess();
|
||||
}
|
||||
|
||||
void ChildProcessMaster::handleConnectionLost() {}
|
||||
|
||||
bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb)
|
||||
{
|
||||
if (connection != nullptr)
|
||||
return connection->sendMessage (mb);
|
||||
|
||||
jassertfalse; // this can only be used when the connection is active!
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID,
|
||||
int timeoutMs, int streamFlags)
|
||||
{
|
||||
killSlaveProcess();
|
||||
|
||||
auto pipeName = "p" + String::toHexString (Random().nextInt64());
|
||||
|
||||
StringArray args;
|
||||
args.add (executable.getFullPathName());
|
||||
args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName);
|
||||
|
||||
childProcess.reset (new ChildProcess());
|
||||
|
||||
if (childProcess->start (args, streamFlags))
|
||||
{
|
||||
connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs));
|
||||
|
||||
if (connection->isConnected())
|
||||
{
|
||||
sendMessageToSlave ({ startMessage, specialMessageSize });
|
||||
return true;
|
||||
}
|
||||
|
||||
connection.reset();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ChildProcessMaster::killSlaveProcess()
|
||||
{
|
||||
if (connection != nullptr)
|
||||
{
|
||||
sendMessageToSlave ({ killMessage, specialMessageSize });
|
||||
connection->disconnect();
|
||||
connection.reset();
|
||||
}
|
||||
|
||||
childProcess.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ChildProcessSlave::Connection : public InterprocessConnection,
|
||||
private ChildProcessPingThread
|
||||
{
|
||||
Connection (ChildProcessSlave& p, const String& pipeName, int timeout)
|
||||
: InterprocessConnection (false, magicMastSlaveConnectionHeader),
|
||||
ChildProcessPingThread (timeout),
|
||||
owner (p)
|
||||
{
|
||||
connectToPipe (pipeName, timeoutMs);
|
||||
startThread (4);
|
||||
}
|
||||
|
||||
~Connection() override
|
||||
{
|
||||
stopThread (10000);
|
||||
}
|
||||
|
||||
private:
|
||||
ChildProcessSlave& owner;
|
||||
|
||||
void connectionMade() override {}
|
||||
void connectionLost() override { owner.handleConnectionLost(); }
|
||||
|
||||
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); }
|
||||
void pingFailed() override { connectionLost(); }
|
||||
|
||||
void messageReceived (const MemoryBlock& m) override
|
||||
{
|
||||
pingReceived();
|
||||
|
||||
if (isMessageType (m, pingMessage))
|
||||
return;
|
||||
|
||||
if (isMessageType (m, killMessage))
|
||||
return triggerConnectionLostMessage();
|
||||
|
||||
if (isMessageType (m, startMessage))
|
||||
return owner.handleConnectionMade();
|
||||
|
||||
owner.handleMessageFromMaster (m);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ChildProcessSlave::ChildProcessSlave() {}
|
||||
ChildProcessSlave::~ChildProcessSlave() {}
|
||||
|
||||
void ChildProcessSlave::handleConnectionMade() {}
|
||||
void ChildProcessSlave::handleConnectionLost() {}
|
||||
|
||||
bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb)
|
||||
{
|
||||
if (connection != nullptr)
|
||||
return connection->sendMessage (mb);
|
||||
|
||||
jassertfalse; // this can only be used when the connection is active!
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine,
|
||||
const String& commandLineUniqueID,
|
||||
int timeoutMs)
|
||||
{
|
||||
auto prefix = getCommandLinePrefix (commandLineUniqueID);
|
||||
|
||||
if (commandLine.trim().startsWith (prefix))
|
||||
{
|
||||
auto pipeName = commandLine.fromFirstOccurrenceOf (prefix, false, false)
|
||||
.upToFirstOccurrenceOf (" ", false, false).trim();
|
||||
|
||||
if (pipeName.isNotEmpty())
|
||||
{
|
||||
connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs));
|
||||
|
||||
if (! connection->isConnected())
|
||||
connection.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return connection != nullptr;
|
||||
}
|
||||
|
||||
} // namespace juce
|
200
deps/juce/modules/juce_events/interprocess/juce_ConnectedChildProcess.h
vendored
Normal file
200
deps/juce/modules/juce_events/interprocess/juce_ConnectedChildProcess.h
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Acts as the slave end of a master/slave pair of connected processes.
|
||||
|
||||
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app
|
||||
to spawn a child process, and to manage a 2-way messaging connection to control it.
|
||||
|
||||
To use the system, you need to create subclasses of both ChildProcessSlave and
|
||||
ChildProcessMaster. To instantiate the ChildProcessSlave object, you must
|
||||
add some code to your main() or JUCEApplication::initialise() function that
|
||||
calls the initialiseFromCommandLine() method to check the app's command-line
|
||||
parameters to see whether it's being launched as a child process. If this returns
|
||||
true then the slave process can be allowed to run, and its handleMessageFromMaster()
|
||||
method will be called whenever a message arrives.
|
||||
|
||||
The juce demo app has a good example of this class in action.
|
||||
|
||||
@see ChildProcessMaster, InterprocessConnection, ChildProcess
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API ChildProcessSlave
|
||||
{
|
||||
public:
|
||||
/** Creates a non-connected slave process.
|
||||
Use initialiseFromCommandLine to connect to a master process.
|
||||
*/
|
||||
ChildProcessSlave();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ChildProcessSlave();
|
||||
|
||||
/** This checks some command-line parameters to see whether they were generated by
|
||||
ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process.
|
||||
|
||||
In an exe that can be used as a child process, you should add some code to your
|
||||
main() or JUCEApplication::initialise() that calls this method.
|
||||
|
||||
The commandLineUniqueID should be a short alphanumeric identifier (no spaces!)
|
||||
that matches the string passed to ChildProcessMaster::launchSlaveProcess().
|
||||
|
||||
The timeoutMs parameter lets you specify how long the child process is allowed
|
||||
to run without receiving a ping from the master before the master is considered to
|
||||
have died, and handleConnectionLost() will be called. Passing <= 0 for this timeout
|
||||
makes it use a default value.
|
||||
|
||||
Returns true if the command-line matches and the connection is made successfully.
|
||||
*/
|
||||
bool initialiseFromCommandLine (const String& commandLine,
|
||||
const String& commandLineUniqueID,
|
||||
int timeoutMs = 0);
|
||||
|
||||
//==============================================================================
|
||||
/** This will be called to deliver messages from the master process.
|
||||
The call will probably be made on a background thread, so be careful with your
|
||||
thread-safety! You may want to respond by sending back a message with
|
||||
sendMessageToMaster()
|
||||
*/
|
||||
virtual void handleMessageFromMaster (const MemoryBlock&) = 0;
|
||||
|
||||
/** This will be called when the master process finishes connecting to this slave.
|
||||
The call will probably be made on a background thread, so be careful with your thread-safety!
|
||||
*/
|
||||
virtual void handleConnectionMade();
|
||||
|
||||
/** This will be called when the connection to the master process is lost.
|
||||
The call may be made from any thread (including the message thread).
|
||||
Typically, if your process only exists to act as a slave, you should probably exit
|
||||
when this happens.
|
||||
*/
|
||||
virtual void handleConnectionLost();
|
||||
|
||||
/** Tries to send a message to the master process.
|
||||
This returns true if the message was sent, but doesn't check that it actually gets
|
||||
delivered at the other end. If successful, the data will emerge in a call to your
|
||||
ChildProcessMaster::handleMessageFromSlave().
|
||||
*/
|
||||
bool sendMessageToMaster (const MemoryBlock&);
|
||||
|
||||
private:
|
||||
struct Connection;
|
||||
std::unique_ptr<Connection> connection;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Acts as the master in a master/slave pair of connected processes.
|
||||
|
||||
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app
|
||||
to spawn a child process, and to manage a 2-way messaging connection to control it.
|
||||
|
||||
To use the system, you need to create subclasses of both ChildProcessSlave and
|
||||
ChildProcessMaster. When you want your master process to launch the slave, you
|
||||
just call launchSlaveProcess(), and it'll attempt to launch the executable that
|
||||
you specify (which may be the same exe), and assuming it has been set-up to
|
||||
correctly parse the command-line parameters (see ChildProcessSlave) then a
|
||||
two-way connection will be created.
|
||||
|
||||
The juce demo app has a good example of this class in action.
|
||||
|
||||
@see ChildProcessSlave, InterprocessConnection, ChildProcess
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API ChildProcessMaster
|
||||
{
|
||||
public:
|
||||
/** Creates an uninitialised master process object.
|
||||
Use launchSlaveProcess to launch and connect to a child process.
|
||||
*/
|
||||
ChildProcessMaster();
|
||||
|
||||
/** Destructor.
|
||||
Note that the destructor calls killSlaveProcess(), but doesn't wait for
|
||||
the child process to finish terminating.
|
||||
*/
|
||||
virtual ~ChildProcessMaster();
|
||||
|
||||
/** Attempts to launch and connect to a slave process.
|
||||
This will start the given executable, passing it a special command-line
|
||||
parameter based around the commandLineUniqueID string, which must be a
|
||||
short alphanumeric string (no spaces!) that identifies your app. The exe
|
||||
that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine()
|
||||
in its startup code, and must use a matching ID to commandLineUniqueID.
|
||||
|
||||
The timeoutMs parameter lets you specify how long the child process is allowed
|
||||
to go without sending a ping before it is considered to have died and
|
||||
handleConnectionLost() will be called. Passing <= 0 for this timeout makes
|
||||
it use a default value.
|
||||
|
||||
If this all works, the method returns true, and you can begin sending and
|
||||
receiving messages with the slave process.
|
||||
|
||||
If a child process is already running, this will call killSlaveProcess() and
|
||||
start a new one.
|
||||
*/
|
||||
bool launchSlaveProcess (const File& executableToLaunch,
|
||||
const String& commandLineUniqueID,
|
||||
int timeoutMs = 0,
|
||||
int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr);
|
||||
|
||||
/** Sends a kill message to the slave, and disconnects from it.
|
||||
Note that this won't wait for it to terminate.
|
||||
*/
|
||||
void killSlaveProcess();
|
||||
|
||||
/** This will be called to deliver a message from the slave process.
|
||||
The call will probably be made on a background thread, so be careful with your thread-safety!
|
||||
*/
|
||||
virtual void handleMessageFromSlave (const MemoryBlock&) = 0;
|
||||
|
||||
/** This will be called when the slave process dies or is somehow disconnected.
|
||||
The call will probably be made on a background thread, so be careful with your thread-safety!
|
||||
*/
|
||||
virtual void handleConnectionLost();
|
||||
|
||||
/** Attempts to send a message to the slave process.
|
||||
This returns true if the message was dispatched, but doesn't check that it actually
|
||||
gets delivered at the other end. If successful, the data will emerge in a call to
|
||||
your ChildProcessSlave::handleMessageFromMaster().
|
||||
*/
|
||||
bool sendMessageToSlave (const MemoryBlock&);
|
||||
|
||||
private:
|
||||
std::unique_ptr<ChildProcess> childProcess;
|
||||
|
||||
struct Connection;
|
||||
std::unique_ptr<Connection> connection;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster)
|
||||
};
|
||||
|
||||
} // namespace juce
|
431
deps/juce/modules/juce_events/interprocess/juce_InterprocessConnection.cpp
vendored
Normal file
431
deps/juce/modules/juce_events/interprocess/juce_InterprocessConnection.cpp
vendored
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct InterprocessConnection::ConnectionThread : public Thread
|
||||
{
|
||||
ConnectionThread (InterprocessConnection& c) : Thread ("JUCE IPC"), owner (c) {}
|
||||
void run() override { owner.runThread(); }
|
||||
|
||||
InterprocessConnection& owner;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionThread)
|
||||
};
|
||||
|
||||
class SafeActionImpl
|
||||
{
|
||||
public:
|
||||
explicit SafeActionImpl (InterprocessConnection& p)
|
||||
: ref (p) {}
|
||||
|
||||
template <typename Fn>
|
||||
void ifSafe (Fn&& fn)
|
||||
{
|
||||
const ScopedLock lock (mutex);
|
||||
|
||||
if (safe)
|
||||
fn (ref);
|
||||
}
|
||||
|
||||
void setSafe (bool s)
|
||||
{
|
||||
const ScopedLock lock (mutex);
|
||||
safe = s;
|
||||
}
|
||||
|
||||
bool isSafe()
|
||||
{
|
||||
const ScopedLock lock (mutex);
|
||||
return safe;
|
||||
}
|
||||
|
||||
private:
|
||||
CriticalSection mutex;
|
||||
InterprocessConnection& ref;
|
||||
bool safe = false;
|
||||
};
|
||||
|
||||
class InterprocessConnection::SafeAction : public SafeActionImpl
|
||||
{
|
||||
using SafeActionImpl::SafeActionImpl;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
InterprocessConnection::InterprocessConnection (bool callbacksOnMessageThread, uint32 magicMessageHeaderNumber)
|
||||
: useMessageThread (callbacksOnMessageThread),
|
||||
magicMessageHeader (magicMessageHeaderNumber),
|
||||
safeAction (std::make_shared<SafeAction> (*this))
|
||||
{
|
||||
thread.reset (new ConnectionThread (*this));
|
||||
}
|
||||
|
||||
InterprocessConnection::~InterprocessConnection()
|
||||
{
|
||||
// You *must* call `disconnect` in the destructor of your derived class to ensure
|
||||
// that any pending messages are not delivered. If the messages were delivered after
|
||||
// destroying the derived class, we'd end up calling the pure virtual implementations
|
||||
// of `messageReceived`, `connectionMade` and `connectionLost` which is definitely
|
||||
// not a good idea!
|
||||
jassert (! safeAction->isSafe());
|
||||
|
||||
callbackConnectionState = false;
|
||||
disconnect (4000, Notify::no);
|
||||
thread.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool InterprocessConnection::connectToSocket (const String& hostName,
|
||||
int portNumber, int timeOutMillisecs)
|
||||
{
|
||||
disconnect();
|
||||
|
||||
auto s = std::make_unique<StreamingSocket>();
|
||||
|
||||
if (s->connect (hostName, portNumber, timeOutMillisecs))
|
||||
{
|
||||
const ScopedWriteLock sl (pipeAndSocketLock);
|
||||
initialiseWithSocket (std::move (s));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InterprocessConnection::connectToPipe (const String& pipeName, int timeoutMs)
|
||||
{
|
||||
disconnect();
|
||||
|
||||
auto newPipe = std::make_unique<NamedPipe>();
|
||||
|
||||
if (newPipe->openExisting (pipeName))
|
||||
{
|
||||
const ScopedWriteLock sl (pipeAndSocketLock);
|
||||
pipeReceiveMessageTimeout = timeoutMs;
|
||||
initialiseWithPipe (std::move (newPipe));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InterprocessConnection::createPipe (const String& pipeName, int timeoutMs, bool mustNotExist)
|
||||
{
|
||||
disconnect();
|
||||
|
||||
auto newPipe = std::make_unique<NamedPipe>();
|
||||
|
||||
if (newPipe->createNewPipe (pipeName, mustNotExist))
|
||||
{
|
||||
const ScopedWriteLock sl (pipeAndSocketLock);
|
||||
pipeReceiveMessageTimeout = timeoutMs;
|
||||
initialiseWithPipe (std::move (newPipe));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InterprocessConnection::disconnect (int timeoutMs, Notify notify)
|
||||
{
|
||||
thread->signalThreadShouldExit();
|
||||
|
||||
{
|
||||
const ScopedReadLock sl (pipeAndSocketLock);
|
||||
if (socket != nullptr) socket->close();
|
||||
if (pipe != nullptr) pipe->close();
|
||||
}
|
||||
|
||||
thread->stopThread (timeoutMs);
|
||||
deletePipeAndSocket();
|
||||
|
||||
if (notify == Notify::yes)
|
||||
connectionLostInt();
|
||||
|
||||
callbackConnectionState = false;
|
||||
safeAction->setSafe (false);
|
||||
}
|
||||
|
||||
void InterprocessConnection::deletePipeAndSocket()
|
||||
{
|
||||
const ScopedWriteLock sl (pipeAndSocketLock);
|
||||
socket.reset();
|
||||
pipe.reset();
|
||||
}
|
||||
|
||||
bool InterprocessConnection::isConnected() const
|
||||
{
|
||||
const ScopedReadLock sl (pipeAndSocketLock);
|
||||
|
||||
return ((socket != nullptr && socket->isConnected())
|
||||
|| (pipe != nullptr && pipe->isOpen()))
|
||||
&& threadIsRunning;
|
||||
}
|
||||
|
||||
String InterprocessConnection::getConnectedHostName() const
|
||||
{
|
||||
{
|
||||
const ScopedReadLock sl (pipeAndSocketLock);
|
||||
|
||||
if (pipe == nullptr && socket == nullptr)
|
||||
return {};
|
||||
|
||||
if (socket != nullptr && ! socket->isLocal())
|
||||
return socket->getHostName();
|
||||
}
|
||||
|
||||
return IPAddress::local().toString();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool InterprocessConnection::sendMessage (const MemoryBlock& message)
|
||||
{
|
||||
uint32 messageHeader[2] = { ByteOrder::swapIfBigEndian (magicMessageHeader),
|
||||
ByteOrder::swapIfBigEndian ((uint32) message.getSize()) };
|
||||
|
||||
MemoryBlock messageData (sizeof (messageHeader) + message.getSize());
|
||||
messageData.copyFrom (messageHeader, 0, sizeof (messageHeader));
|
||||
messageData.copyFrom (message.getData(), sizeof (messageHeader), message.getSize());
|
||||
|
||||
return writeData (messageData.getData(), (int) messageData.getSize()) == (int) messageData.getSize();
|
||||
}
|
||||
|
||||
int InterprocessConnection::writeData (void* data, int dataSize)
|
||||
{
|
||||
const ScopedReadLock sl (pipeAndSocketLock);
|
||||
|
||||
if (socket != nullptr)
|
||||
return socket->write (data, dataSize);
|
||||
|
||||
if (pipe != nullptr)
|
||||
return pipe->write (data, dataSize, pipeReceiveMessageTimeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void InterprocessConnection::initialise()
|
||||
{
|
||||
safeAction->setSafe (true);
|
||||
threadIsRunning = true;
|
||||
connectionMadeInt();
|
||||
thread->startThread();
|
||||
}
|
||||
|
||||
void InterprocessConnection::initialiseWithSocket (std::unique_ptr<StreamingSocket> newSocket)
|
||||
{
|
||||
jassert (socket == nullptr && pipe == nullptr);
|
||||
socket = std::move (newSocket);
|
||||
initialise();
|
||||
}
|
||||
|
||||
void InterprocessConnection::initialiseWithPipe (std::unique_ptr<NamedPipe> newPipe)
|
||||
{
|
||||
jassert (socket == nullptr && pipe == nullptr);
|
||||
pipe = std::move (newPipe);
|
||||
initialise();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ConnectionStateMessage : public MessageManager::MessageBase
|
||||
{
|
||||
ConnectionStateMessage (std::shared_ptr<SafeActionImpl> ipc, bool connected) noexcept
|
||||
: safeAction (ipc), connectionMade (connected)
|
||||
{}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
safeAction->ifSafe ([this] (InterprocessConnection& owner)
|
||||
{
|
||||
if (connectionMade)
|
||||
owner.connectionMade();
|
||||
else
|
||||
owner.connectionLost();
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<SafeActionImpl> safeAction;
|
||||
bool connectionMade;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionStateMessage)
|
||||
};
|
||||
|
||||
void InterprocessConnection::connectionMadeInt()
|
||||
{
|
||||
if (! callbackConnectionState)
|
||||
{
|
||||
callbackConnectionState = true;
|
||||
|
||||
if (useMessageThread)
|
||||
(new ConnectionStateMessage (safeAction, true))->post();
|
||||
else
|
||||
connectionMade();
|
||||
}
|
||||
}
|
||||
|
||||
void InterprocessConnection::connectionLostInt()
|
||||
{
|
||||
if (callbackConnectionState)
|
||||
{
|
||||
callbackConnectionState = false;
|
||||
|
||||
if (useMessageThread)
|
||||
(new ConnectionStateMessage (safeAction, false))->post();
|
||||
else
|
||||
connectionLost();
|
||||
}
|
||||
}
|
||||
|
||||
struct DataDeliveryMessage : public Message
|
||||
{
|
||||
DataDeliveryMessage (std::shared_ptr<SafeActionImpl> ipc, const MemoryBlock& d)
|
||||
: safeAction (ipc), data (d)
|
||||
{}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
safeAction->ifSafe ([this] (InterprocessConnection& owner)
|
||||
{
|
||||
owner.messageReceived (data);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<SafeActionImpl> safeAction;
|
||||
MemoryBlock data;
|
||||
};
|
||||
|
||||
void InterprocessConnection::deliverDataInt (const MemoryBlock& data)
|
||||
{
|
||||
jassert (callbackConnectionState);
|
||||
|
||||
if (useMessageThread)
|
||||
(new DataDeliveryMessage (safeAction, data))->post();
|
||||
else
|
||||
messageReceived (data);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int InterprocessConnection::readData (void* data, int num)
|
||||
{
|
||||
const ScopedReadLock sl (pipeAndSocketLock);
|
||||
|
||||
if (socket != nullptr)
|
||||
return socket->read (data, num, true);
|
||||
|
||||
if (pipe != nullptr)
|
||||
return pipe->read (data, num, pipeReceiveMessageTimeout);
|
||||
|
||||
jassertfalse;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool InterprocessConnection::readNextMessage()
|
||||
{
|
||||
uint32 messageHeader[2];
|
||||
auto bytes = readData (messageHeader, sizeof (messageHeader));
|
||||
|
||||
if (bytes == (int) sizeof (messageHeader)
|
||||
&& ByteOrder::swapIfBigEndian (messageHeader[0]) == magicMessageHeader)
|
||||
{
|
||||
auto bytesInMessage = (int) ByteOrder::swapIfBigEndian (messageHeader[1]);
|
||||
|
||||
if (bytesInMessage > 0)
|
||||
{
|
||||
MemoryBlock messageData ((size_t) bytesInMessage, true);
|
||||
int bytesRead = 0;
|
||||
|
||||
while (bytesInMessage > 0)
|
||||
{
|
||||
if (thread->threadShouldExit())
|
||||
return false;
|
||||
|
||||
auto numThisTime = jmin (bytesInMessage, 65536);
|
||||
auto bytesIn = readData (addBytesToPointer (messageData.getData(), bytesRead), numThisTime);
|
||||
|
||||
if (bytesIn <= 0)
|
||||
break;
|
||||
|
||||
bytesRead += bytesIn;
|
||||
bytesInMessage -= bytesIn;
|
||||
}
|
||||
|
||||
if (bytesRead >= 0)
|
||||
deliverDataInt (messageData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bytes < 0)
|
||||
{
|
||||
if (socket != nullptr)
|
||||
deletePipeAndSocket();
|
||||
|
||||
connectionLostInt();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InterprocessConnection::runThread()
|
||||
{
|
||||
while (! thread->threadShouldExit())
|
||||
{
|
||||
if (socket != nullptr)
|
||||
{
|
||||
auto ready = socket->waitUntilReady (true, 100);
|
||||
|
||||
if (ready < 0)
|
||||
{
|
||||
deletePipeAndSocket();
|
||||
connectionLostInt();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ready == 0)
|
||||
{
|
||||
thread->wait (1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (pipe != nullptr)
|
||||
{
|
||||
if (! pipe->isOpen())
|
||||
{
|
||||
deletePipeAndSocket();
|
||||
connectionLostInt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (thread->threadShouldExit() || ! readNextMessage())
|
||||
break;
|
||||
}
|
||||
|
||||
threadIsRunning = false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
226
deps/juce/modules/juce_events/interprocess/juce_InterprocessConnection.h
vendored
Normal file
226
deps/juce/modules/juce_events/interprocess/juce_InterprocessConnection.h
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class InterprocessConnectionServer;
|
||||
class MemoryBlock;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages a simple two-way messaging connection to another process, using either
|
||||
a socket or a named pipe as the transport medium.
|
||||
|
||||
To connect to a waiting socket or an open pipe, use the connectToSocket() or
|
||||
connectToPipe() methods. If this succeeds, messages can be sent to the other end,
|
||||
and incoming messages will result in a callback via the messageReceived()
|
||||
method.
|
||||
|
||||
To open a pipe and wait for another client to connect to it, use the createPipe()
|
||||
method.
|
||||
|
||||
To act as a socket server and create connections for one or more client, see the
|
||||
InterprocessConnectionServer class.
|
||||
|
||||
IMPORTANT NOTE: Your derived Connection class *must* call `disconnect` in its destructor
|
||||
in order to cancel any pending messages before the class is destroyed.
|
||||
|
||||
@see InterprocessConnectionServer, Socket, NamedPipe
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API InterprocessConnection
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a connection.
|
||||
|
||||
Connections are created manually, connecting them with the connectToSocket()
|
||||
or connectToPipe() methods, or they are created automatically by a InterprocessConnectionServer
|
||||
when a client wants to connect.
|
||||
|
||||
@param callbacksOnMessageThread if true, callbacks to the connectionMade(),
|
||||
connectionLost() and messageReceived() methods will
|
||||
always be made using the message thread; if false,
|
||||
these will be called immediately on the connection's
|
||||
own thread.
|
||||
@param magicMessageHeaderNumber a magic number to use in the header to check the
|
||||
validity of the data blocks being sent and received. This
|
||||
can be any number, but the sender and receiver must obviously
|
||||
use matching values or they won't recognise each other.
|
||||
*/
|
||||
InterprocessConnection (bool callbacksOnMessageThread = true,
|
||||
uint32 magicMessageHeaderNumber = 0xf2b49e2c);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~InterprocessConnection();
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to connect this object to a socket.
|
||||
|
||||
For this to work, the machine on the other end needs to have a InterprocessConnectionServer
|
||||
object waiting to receive client connections on this port number.
|
||||
|
||||
@param hostName the host computer, either a network address or name
|
||||
@param portNumber the socket port number to try to connect to
|
||||
@param timeOutMillisecs how long to keep trying before giving up
|
||||
@returns true if the connection is established successfully
|
||||
@see Socket
|
||||
*/
|
||||
bool connectToSocket (const String& hostName,
|
||||
int portNumber,
|
||||
int timeOutMillisecs);
|
||||
|
||||
/** Tries to connect the object to an existing named pipe.
|
||||
|
||||
For this to work, another process on the same computer must already have opened
|
||||
an InterprocessConnection object and used createPipe() to create a pipe for this
|
||||
to connect to.
|
||||
|
||||
@param pipeName the name to use for the pipe - this should be unique to your app
|
||||
@param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing
|
||||
to the pipe, or -1 for an infinite timeout.
|
||||
@returns true if it connects successfully.
|
||||
@see createPipe, NamedPipe
|
||||
*/
|
||||
bool connectToPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs);
|
||||
|
||||
/** Tries to create a new pipe for other processes to connect to.
|
||||
|
||||
This creates a pipe with the given name, so that other processes can use
|
||||
connectToPipe() to connect to the other end.
|
||||
|
||||
@param pipeName the name to use for the pipe - this should be unique to your app
|
||||
@param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing
|
||||
to the pipe, or -1 for an infinite timeout
|
||||
@param mustNotExist if set to true, the method will fail if the pipe already exists
|
||||
@returns true if the pipe was created, or false if it fails (e.g. if another process is
|
||||
already using the pipe)
|
||||
*/
|
||||
bool createPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs, bool mustNotExist = false);
|
||||
|
||||
/** Whether the disconnect call should trigger callbacks. */
|
||||
enum class Notify { no, yes };
|
||||
|
||||
/** Disconnects and closes any currently-open sockets or pipes.
|
||||
|
||||
Derived classes *must* call this in their destructors in order to avoid undefined
|
||||
behaviour.
|
||||
|
||||
@param timeoutMs the time in ms to wait before killing the thread by force
|
||||
@param notify whether or not to call `connectionLost`
|
||||
*/
|
||||
void disconnect (int timeoutMs = -1, Notify notify = Notify::yes);
|
||||
|
||||
/** True if a socket or pipe is currently active. */
|
||||
bool isConnected() const;
|
||||
|
||||
/** Returns the socket that this connection is using (or nullptr if it uses a pipe). */
|
||||
StreamingSocket* getSocket() const noexcept { return socket.get(); }
|
||||
|
||||
/** Returns the pipe that this connection is using (or nullptr if it uses a socket). */
|
||||
NamedPipe* getPipe() const noexcept { return pipe.get(); }
|
||||
|
||||
/** Returns the name of the machine at the other end of this connection.
|
||||
This may return an empty string if the name is unknown.
|
||||
*/
|
||||
String getConnectedHostName() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to send a message to the other end of this connection.
|
||||
|
||||
This will fail if it's not connected, or if there's some kind of write error. If
|
||||
it succeeds, the connection object at the other end will receive the message by
|
||||
a callback to its messageReceived() method.
|
||||
|
||||
@see messageReceived
|
||||
*/
|
||||
bool sendMessage (const MemoryBlock& message);
|
||||
|
||||
//==============================================================================
|
||||
/** Called when the connection is first connected.
|
||||
|
||||
If the connection was created with the callbacksOnMessageThread flag set, then
|
||||
this will be called on the message thread; otherwise it will be called on a server
|
||||
thread.
|
||||
*/
|
||||
virtual void connectionMade() = 0;
|
||||
|
||||
/** Called when the connection is broken.
|
||||
|
||||
If the connection was created with the callbacksOnMessageThread flag set, then
|
||||
this will be called on the message thread; otherwise it will be called on a server
|
||||
thread.
|
||||
*/
|
||||
virtual void connectionLost() = 0;
|
||||
|
||||
/** Called when a message arrives.
|
||||
|
||||
When the object at the other end of this connection sends us a message with sendMessage(),
|
||||
this callback is used to deliver it to us.
|
||||
|
||||
If the connection was created with the callbacksOnMessageThread flag set, then
|
||||
this will be called on the message thread; otherwise it will be called on a server
|
||||
thread.
|
||||
|
||||
@see sendMessage
|
||||
*/
|
||||
virtual void messageReceived (const MemoryBlock& message) = 0;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ReadWriteLock pipeAndSocketLock;
|
||||
std::unique_ptr<StreamingSocket> socket;
|
||||
std::unique_ptr<NamedPipe> pipe;
|
||||
bool callbackConnectionState = false;
|
||||
const bool useMessageThread;
|
||||
const uint32 magicMessageHeader;
|
||||
int pipeReceiveMessageTimeout = -1;
|
||||
|
||||
friend class InterprocessConnectionServer;
|
||||
void initialise();
|
||||
void initialiseWithSocket (std::unique_ptr<StreamingSocket>);
|
||||
void initialiseWithPipe (std::unique_ptr<NamedPipe>);
|
||||
void deletePipeAndSocket();
|
||||
void connectionMadeInt();
|
||||
void connectionLostInt();
|
||||
void deliverDataInt (const MemoryBlock&);
|
||||
bool readNextMessage();
|
||||
int readData (void*, int);
|
||||
|
||||
struct ConnectionThread;
|
||||
std::unique_ptr<ConnectionThread> thread;
|
||||
std::atomic<bool> threadIsRunning { false };
|
||||
|
||||
class SafeAction;
|
||||
std::shared_ptr<SafeAction> safeAction;
|
||||
|
||||
void runThread();
|
||||
int writeData (void*, int);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection)
|
||||
};
|
||||
|
||||
} // namespace juce
|
80
deps/juce/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp
vendored
Normal file
80
deps/juce/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
InterprocessConnectionServer::InterprocessConnectionServer() : Thread ("JUCE IPC server")
|
||||
{
|
||||
}
|
||||
|
||||
InterprocessConnectionServer::~InterprocessConnectionServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool InterprocessConnectionServer::beginWaitingForSocket (const int portNumber, const String& bindAddress)
|
||||
{
|
||||
stop();
|
||||
|
||||
socket.reset (new StreamingSocket());
|
||||
|
||||
if (socket->createListener (portNumber, bindAddress))
|
||||
{
|
||||
startThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
socket.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
void InterprocessConnectionServer::stop()
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
|
||||
if (socket != nullptr)
|
||||
socket->close();
|
||||
|
||||
stopThread (4000);
|
||||
socket.reset();
|
||||
}
|
||||
|
||||
int InterprocessConnectionServer::getBoundPort() const noexcept
|
||||
{
|
||||
return (socket == nullptr) ? -1 : socket->getBoundPort();
|
||||
}
|
||||
|
||||
void InterprocessConnectionServer::run()
|
||||
{
|
||||
while ((! threadShouldExit()) && socket != nullptr)
|
||||
{
|
||||
std::unique_ptr<StreamingSocket> clientSocket (socket->waitForNextConnection());
|
||||
|
||||
if (clientSocket != nullptr)
|
||||
if (auto* newConnection = createConnectionObject())
|
||||
newConnection->initialiseWithSocket (std::move (clientSocket));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
106
deps/juce/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h
vendored
Normal file
106
deps/juce/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An object that waits for client sockets to connect to a port on this host, and
|
||||
creates InterprocessConnection objects for each one.
|
||||
|
||||
To use this, create a class derived from it which implements the createConnectionObject()
|
||||
method, so that it creates suitable connection objects for each client that tries
|
||||
to connect.
|
||||
|
||||
@see InterprocessConnection
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API InterprocessConnectionServer : private Thread
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an uninitialised server object.
|
||||
*/
|
||||
InterprocessConnectionServer();
|
||||
|
||||
/** Destructor. */
|
||||
~InterprocessConnectionServer() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts an internal thread which listens on the given port number.
|
||||
|
||||
While this is running, if another process tries to connect with the
|
||||
InterprocessConnection::connectToSocket() method, this object will call
|
||||
createConnectionObject() to create a connection to that client.
|
||||
|
||||
Use stop() to stop the thread running.
|
||||
|
||||
@param portNumber The port on which the server will receive
|
||||
connections
|
||||
@param bindAddress The address on which the server will listen
|
||||
for connections. An empty string indicates
|
||||
that it should listen on all addresses
|
||||
assigned to this machine.
|
||||
|
||||
@see createConnectionObject, stop
|
||||
*/
|
||||
bool beginWaitingForSocket (int portNumber, const String& bindAddress = String());
|
||||
|
||||
/** Terminates the listener thread, if it's active.
|
||||
|
||||
@see beginWaitingForSocket
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Returns the local port number to which this server is currently bound.
|
||||
|
||||
This is useful if you need to know to which port the OS has actually bound your
|
||||
socket when calling beginWaitingForSocket with a port number of zero.
|
||||
|
||||
Returns -1 if the function fails.
|
||||
*/
|
||||
int getBoundPort() const noexcept;
|
||||
|
||||
protected:
|
||||
/** Creates a suitable connection object for a client process that wants to
|
||||
connect to this one.
|
||||
|
||||
This will be called by the listener thread when a client process tries
|
||||
to connect, and must return a new InterprocessConnection object that will
|
||||
then run as this end of the connection.
|
||||
|
||||
@see InterprocessConnection
|
||||
*/
|
||||
virtual InterprocessConnection* createConnectionObject() = 0;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<StreamingSocket> socket;
|
||||
|
||||
void run() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnectionServer)
|
||||
};
|
||||
|
||||
} // namespace juce
|
212
deps/juce/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp
vendored
Normal file
212
deps/juce/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_ANDROID
|
||||
extern void acquireMulticastLock();
|
||||
extern void releaseMulticastLock();
|
||||
#endif
|
||||
|
||||
NetworkServiceDiscovery::Advertiser::Advertiser (const String& serviceTypeUID,
|
||||
const String& serviceDescription,
|
||||
int broadcastPortToUse, int connectionPort,
|
||||
RelativeTime minTimeBetweenBroadcasts)
|
||||
: Thread ("Discovery_broadcast"),
|
||||
message (serviceTypeUID), broadcastPort (broadcastPortToUse),
|
||||
minInterval (minTimeBetweenBroadcasts)
|
||||
{
|
||||
message.setAttribute ("id", Uuid().toString());
|
||||
message.setAttribute ("name", serviceDescription);
|
||||
message.setAttribute ("address", String());
|
||||
message.setAttribute ("port", connectionPort);
|
||||
|
||||
startThread (2);
|
||||
}
|
||||
|
||||
NetworkServiceDiscovery::Advertiser::~Advertiser()
|
||||
{
|
||||
stopThread (2000);
|
||||
socket.shutdown();
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::Advertiser::run()
|
||||
{
|
||||
if (! socket.bindToPort (0))
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
sendBroadcast();
|
||||
wait ((int) minInterval.inMilliseconds());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::Advertiser::sendBroadcast()
|
||||
{
|
||||
static IPAddress local = IPAddress::local();
|
||||
|
||||
for (auto& address : IPAddress::getAllAddresses())
|
||||
{
|
||||
if (address == local)
|
||||
continue;
|
||||
|
||||
message.setAttribute ("address", address.toString());
|
||||
|
||||
auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (address);
|
||||
auto data = message.toString (XmlElement::TextFormat().singleLine().withoutHeader());
|
||||
|
||||
socket.write (broadcastAddress.toString(), broadcastPort, data.toRawUTF8(), (int) data.getNumBytesAsUTF8());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
NetworkServiceDiscovery::AvailableServiceList::AvailableServiceList (const String& serviceType, int broadcastPort)
|
||||
: Thread ("Discovery_listen"), serviceTypeUID (serviceType)
|
||||
{
|
||||
#if JUCE_ANDROID
|
||||
acquireMulticastLock();
|
||||
#endif
|
||||
|
||||
socket.bindToPort (broadcastPort);
|
||||
startThread (2);
|
||||
}
|
||||
|
||||
NetworkServiceDiscovery::AvailableServiceList::~AvailableServiceList()
|
||||
{
|
||||
socket.shutdown();
|
||||
stopThread (2000);
|
||||
|
||||
#if JUCE_ANDROID
|
||||
releaseMulticastLock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::run()
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (socket.waitUntilReady (true, 200) == 1)
|
||||
{
|
||||
char buffer[1024];
|
||||
auto bytesRead = socket.read (buffer, sizeof (buffer) - 1, false);
|
||||
|
||||
if (bytesRead > 10)
|
||||
if (auto xml = parseXML (String (CharPointer_UTF8 (buffer),
|
||||
CharPointer_UTF8 (buffer + bytesRead))))
|
||||
if (xml->hasTagName (serviceTypeUID))
|
||||
handleMessage (*xml);
|
||||
}
|
||||
|
||||
removeTimedOutServices();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<NetworkServiceDiscovery::Service> NetworkServiceDiscovery::AvailableServiceList::getServices() const
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
auto listCopy = services;
|
||||
return listCopy;
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::handleAsyncUpdate()
|
||||
{
|
||||
if (onChange != nullptr)
|
||||
onChange();
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const XmlElement& xml)
|
||||
{
|
||||
Service service;
|
||||
service.instanceID = xml.getStringAttribute ("id");
|
||||
|
||||
if (service.instanceID.trim().isNotEmpty())
|
||||
{
|
||||
service.description = xml.getStringAttribute ("name");
|
||||
service.address = IPAddress (xml.getStringAttribute ("address"));
|
||||
service.port = xml.getIntAttribute ("port");
|
||||
service.lastSeen = Time::getCurrentTime();
|
||||
|
||||
handleMessage (service);
|
||||
}
|
||||
}
|
||||
|
||||
static void sortServiceList (std::vector<NetworkServiceDiscovery::Service>& services)
|
||||
{
|
||||
auto compareServices = [] (const NetworkServiceDiscovery::Service& s1,
|
||||
const NetworkServiceDiscovery::Service& s2)
|
||||
{
|
||||
return s1.instanceID < s2.instanceID;
|
||||
};
|
||||
|
||||
std::sort (services.begin(), services.end(), compareServices);
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const Service& service)
|
||||
{
|
||||
const ScopedLock sl (listLock);
|
||||
|
||||
for (auto& s : services)
|
||||
{
|
||||
if (s.instanceID == service.instanceID)
|
||||
{
|
||||
if (s.description != service.description
|
||||
|| s.address != service.address
|
||||
|| s.port != service.port)
|
||||
{
|
||||
s = service;
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
s.lastSeen = service.lastSeen;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
services.push_back (service);
|
||||
sortServiceList (services);
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void NetworkServiceDiscovery::AvailableServiceList::removeTimedOutServices()
|
||||
{
|
||||
const double timeoutSeconds = 5.0;
|
||||
auto oldestAllowedTime = Time::getCurrentTime() - RelativeTime::seconds (timeoutSeconds);
|
||||
|
||||
const ScopedLock sl (listLock);
|
||||
|
||||
auto oldEnd = std::end (services);
|
||||
auto newEnd = std::remove_if (std::begin (services), oldEnd,
|
||||
[=] (const Service& s) { return s.lastSeen < oldestAllowedTime; });
|
||||
|
||||
if (newEnd != oldEnd)
|
||||
{
|
||||
services.erase (newEnd, oldEnd);
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
138
deps/juce/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h
vendored
Normal file
138
deps/juce/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains classes that implement a simple protocol for broadcasting the availability
|
||||
and location of a discoverable service on the local network, and for maintaining a
|
||||
list of known services.
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
struct NetworkServiceDiscovery
|
||||
{
|
||||
/** An object which runs a thread to repeatedly broadcast the existence of a
|
||||
discoverable service.
|
||||
|
||||
To use, simply create an instance of an Advertiser and it'll broadcast until
|
||||
you delete it.
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
struct Advertiser : private Thread
|
||||
{
|
||||
/** Creates and starts an Advertiser thread, broadcasting with the given properties.
|
||||
@param serviceTypeUID A user-supplied string to define the type of service this represents
|
||||
@param serviceDescription A description string that will appear in the Service::description field for clients
|
||||
@param broadcastPort The port number on which to broadcast the service discovery packets
|
||||
@param connectionPort The port number that will be sent to appear in the Service::port field
|
||||
@param minTimeBetweenBroadcasts The interval to wait between sending broadcast messages
|
||||
*/
|
||||
Advertiser (const String& serviceTypeUID,
|
||||
const String& serviceDescription,
|
||||
int broadcastPort,
|
||||
int connectionPort,
|
||||
RelativeTime minTimeBetweenBroadcasts = RelativeTime::seconds (1.5));
|
||||
|
||||
/** Destructor */
|
||||
~Advertiser() override;
|
||||
|
||||
private:
|
||||
XmlElement message;
|
||||
const int broadcastPort;
|
||||
const RelativeTime minInterval;
|
||||
DatagramSocket socket { true };
|
||||
|
||||
void run() override;
|
||||
void sendBroadcast();
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains information about a service that has been found on the network.
|
||||
|
||||
@see AvailableServiceList, Advertiser
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
struct Service
|
||||
{
|
||||
String instanceID; /**< A UUID that identifies the particular instance of the Advertiser class. */
|
||||
String description; /**< The service description as sent by the Advertiser */
|
||||
IPAddress address; /**< The IP address of the advertiser */
|
||||
int port; /**< The port number of the advertiser */
|
||||
Time lastSeen; /**< The time of the last ping received from the advertiser */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Watches the network for broadcasts from Advertiser objects, and keeps a list of
|
||||
all the currently active instances.
|
||||
|
||||
Just create an instance of AvailableServiceList and it will start listening - you
|
||||
can register a callback with its onChange member to find out when services
|
||||
appear/disappear, and you can call getServices() to find out the current list.
|
||||
|
||||
@see Service, Advertiser
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
struct AvailableServiceList : private Thread,
|
||||
private AsyncUpdater
|
||||
{
|
||||
/** Creates an AvailableServiceList that will bind to the given port number and watch
|
||||
the network for Advertisers broadcasting the given service type.
|
||||
|
||||
This will only detect broadcasts from an Advertiser object with a matching
|
||||
serviceTypeUID value, and where the broadcastPort matches.
|
||||
*/
|
||||
AvailableServiceList (const String& serviceTypeUID, int broadcastPort);
|
||||
|
||||
/** Destructor */
|
||||
~AvailableServiceList() override;
|
||||
|
||||
/** A lambda that can be set to receive a callback when the list changes */
|
||||
std::function<void()> onChange;
|
||||
|
||||
/** Returns a list of the currently known services. */
|
||||
std::vector<Service> getServices() const;
|
||||
|
||||
private:
|
||||
DatagramSocket socket { true };
|
||||
String serviceTypeUID;
|
||||
CriticalSection listLock;
|
||||
std::vector<Service> services;
|
||||
|
||||
void run() override;
|
||||
void handleAsyncUpdate() override;
|
||||
void handleMessage (const XmlElement&);
|
||||
void handleMessage (const Service&);
|
||||
void removeTimedOutServices();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableServiceList)
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace juce
|
95
deps/juce/modules/juce_events/juce_events.cpp
vendored
Normal file
95
deps/juce/modules/juce_events/juce_events.cpp
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_EVENTS_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
|
||||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
|
||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1
|
||||
|
||||
#if JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_EVENTS_INCLUDE_WINRT_WRAPPER 1
|
||||
#endif
|
||||
|
||||
#include "juce_events.h"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <IOKit/IOCFPlugIn.h>
|
||||
#import <IOKit/hid/IOHIDLib.h>
|
||||
#import <IOKit/hid/IOHIDKeys.h>
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#include "messages/juce_ApplicationBase.cpp"
|
||||
#include "messages/juce_DeletedAtShutdown.cpp"
|
||||
#include "messages/juce_MessageListener.cpp"
|
||||
#include "messages/juce_MessageManager.cpp"
|
||||
#include "broadcasters/juce_ActionBroadcaster.cpp"
|
||||
#include "broadcasters/juce_AsyncUpdater.cpp"
|
||||
#include "broadcasters/juce_ChangeBroadcaster.cpp"
|
||||
#include "timers/juce_MultiTimer.cpp"
|
||||
#include "timers/juce_Timer.cpp"
|
||||
#include "interprocess/juce_InterprocessConnection.cpp"
|
||||
#include "interprocess/juce_InterprocessConnectionServer.cpp"
|
||||
#include "interprocess/juce_ConnectedChildProcess.cpp"
|
||||
#include "interprocess/juce_NetworkServiceDiscovery.cpp"
|
||||
#include "native/juce_ScopedLowPowerModeDisabler.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
|
||||
#include "native/juce_osx_MessageQueue.h"
|
||||
|
||||
#if JUCE_MAC
|
||||
#include "native/juce_mac_MessageManager.mm"
|
||||
#else
|
||||
#include "native/juce_ios_MessageManager.mm"
|
||||
#endif
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
#include "native/juce_win32_Messaging.cpp"
|
||||
#if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER
|
||||
#include "native/juce_win32_WinRTWrapper.cpp"
|
||||
#endif
|
||||
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
#include "native/juce_linux_Messaging.cpp"
|
||||
|
||||
#elif JUCE_ANDROID
|
||||
#include "native/juce_android_Messaging.cpp"
|
||||
|
||||
#endif
|
106
deps/juce/modules/juce_events/juce_events.h
vendored
Normal file
106
deps/juce/modules/juce_events/juce_events.h
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_events
|
||||
vendor: juce
|
||||
version: 6.1.2
|
||||
name: JUCE message and event handling classes
|
||||
description: Classes for running an application's main event loop and sending/receiving messages, timers, etc.
|
||||
website: http://www.juce.com/juce
|
||||
license: ISC
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_core
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_EVENTS_H_INCLUDED
|
||||
|
||||
#include <juce_core/juce_core.h>
|
||||
|
||||
//==============================================================================
|
||||
/** Config: JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK
|
||||
Will execute your application's suspend method on an iOS background task, giving
|
||||
you extra time to save your applications state.
|
||||
*/
|
||||
#ifndef JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK
|
||||
#define JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK 0
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_EVENTS_INCLUDE_WINRT_WRAPPER
|
||||
// If this header file is missing then you are probably attempting to use WinRT
|
||||
// functionality without the WinRT libraries installed on your system. Try installing
|
||||
// the latest Windows Standalone SDK and maybe also adding the path to the WinRT
|
||||
// headers to your build system. This path should have the form
|
||||
// "C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
#include <inspectable.h>
|
||||
#include <hstring.h>
|
||||
#endif
|
||||
|
||||
#include "messages/juce_MessageManager.h"
|
||||
#include "messages/juce_Message.h"
|
||||
#include "messages/juce_MessageListener.h"
|
||||
#include "messages/juce_CallbackMessage.h"
|
||||
#include "messages/juce_DeletedAtShutdown.h"
|
||||
#include "messages/juce_NotificationType.h"
|
||||
#include "messages/juce_ApplicationBase.h"
|
||||
#include "messages/juce_Initialisation.h"
|
||||
#include "messages/juce_MountedVolumeListChangeDetector.h"
|
||||
#include "broadcasters/juce_ActionBroadcaster.h"
|
||||
#include "broadcasters/juce_ActionListener.h"
|
||||
#include "broadcasters/juce_AsyncUpdater.h"
|
||||
#include "broadcasters/juce_ChangeListener.h"
|
||||
#include "broadcasters/juce_ChangeBroadcaster.h"
|
||||
#include "timers/juce_Timer.h"
|
||||
#include "timers/juce_MultiTimer.h"
|
||||
#include "interprocess/juce_InterprocessConnection.h"
|
||||
#include "interprocess/juce_InterprocessConnectionServer.h"
|
||||
#include "interprocess/juce_ConnectedChildProcess.h"
|
||||
#include "interprocess/juce_NetworkServiceDiscovery.h"
|
||||
#include "native/juce_ScopedLowPowerModeDisabler.h"
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
#include "native/juce_linux_EventLoop.h"
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
#if JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW
|
||||
#include "native/juce_win32_HiddenMessageWindow.h"
|
||||
#endif
|
||||
#if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER
|
||||
#include "native/juce_win32_WinRTWrapper.h"
|
||||
#endif
|
||||
#endif
|
23
deps/juce/modules/juce_events/juce_events.mm
vendored
Normal file
23
deps/juce/modules/juce_events/juce_events.mm
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "juce_events.cpp"
|
332
deps/juce/modules/juce_events/messages/juce_ApplicationBase.cpp
vendored
Normal file
332
deps/juce/modules/juce_events/messages/juce_ApplicationBase.cpp
vendored
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCEApplicationBase::CreateInstanceFunction JUCEApplicationBase::createInstance = nullptr;
|
||||
JUCEApplicationBase* JUCEApplicationBase::appInstance = nullptr;
|
||||
|
||||
#if JUCE_IOS
|
||||
void* JUCEApplicationBase::iOSCustomDelegate = nullptr;
|
||||
#endif
|
||||
|
||||
JUCEApplicationBase::JUCEApplicationBase()
|
||||
{
|
||||
jassert (isStandaloneApp() && appInstance == nullptr);
|
||||
appInstance = this;
|
||||
}
|
||||
|
||||
JUCEApplicationBase::~JUCEApplicationBase()
|
||||
{
|
||||
jassert (appInstance == this);
|
||||
appInstance = nullptr;
|
||||
}
|
||||
|
||||
void JUCEApplicationBase::setApplicationReturnValue (const int newReturnValue) noexcept
|
||||
{
|
||||
appReturnValue = newReturnValue;
|
||||
}
|
||||
|
||||
// This is called on the Mac and iOS where the OS doesn't allow the stack to unwind on shutdown..
|
||||
void JUCEApplicationBase::appWillTerminateByForce()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
{
|
||||
const std::unique_ptr<JUCEApplicationBase> app (appInstance);
|
||||
|
||||
if (app != nullptr)
|
||||
app->shutdownApp();
|
||||
}
|
||||
|
||||
DeletedAtShutdown::deleteAll();
|
||||
MessageManager::deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
void JUCEApplicationBase::quit()
|
||||
{
|
||||
MessageManager::getInstance()->stopDispatchLoop();
|
||||
}
|
||||
|
||||
void JUCEApplicationBase::sendUnhandledException (const std::exception* const e,
|
||||
const char* const sourceFile,
|
||||
const int lineNumber)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
// If you hit this assertion then the __FILE__ macro is providing a
|
||||
// relative path instead of an absolute path. On Windows this will be
|
||||
// a path relative to the build directory rather than the currently
|
||||
// running application. To fix this you must compile with the /FC flag.
|
||||
jassert (File::isAbsolutePath (sourceFile));
|
||||
|
||||
app->unhandledException (e, sourceFile, lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! (JUCE_IOS || JUCE_ANDROID)
|
||||
#define JUCE_HANDLE_MULTIPLE_INSTANCES 1
|
||||
#endif
|
||||
|
||||
#if JUCE_HANDLE_MULTIPLE_INSTANCES
|
||||
struct JUCEApplicationBase::MultipleInstanceHandler : public ActionListener
|
||||
{
|
||||
MultipleInstanceHandler (const String& appName)
|
||||
: appLock ("juceAppLock_" + appName)
|
||||
{
|
||||
}
|
||||
|
||||
bool sendCommandLineToPreexistingInstance()
|
||||
{
|
||||
if (appLock.enter (0))
|
||||
return false;
|
||||
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
MessageManager::broadcastMessage (app->getApplicationName() + "/" + app->getCommandLineParameters());
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
void actionListenerCallback (const String& message) override
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
auto appName = app->getApplicationName();
|
||||
|
||||
if (message.startsWith (appName + "/"))
|
||||
app->anotherInstanceStarted (message.substring (appName.length() + 1));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
InterProcessLock appLock;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultipleInstanceHandler)
|
||||
};
|
||||
|
||||
bool JUCEApplicationBase::sendCommandLineToPreexistingInstance()
|
||||
{
|
||||
jassert (multipleInstanceHandler == nullptr); // this must only be called once!
|
||||
|
||||
multipleInstanceHandler.reset (new MultipleInstanceHandler (getApplicationName()));
|
||||
return multipleInstanceHandler->sendCommandLineToPreexistingInstance();
|
||||
}
|
||||
|
||||
#else
|
||||
struct JUCEApplicationBase::MultipleInstanceHandler {};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_ANDROID
|
||||
|
||||
StringArray JUCEApplicationBase::getCommandLineParameterArray() { return {}; }
|
||||
String JUCEApplicationBase::getCommandLineParameters() { return {}; }
|
||||
|
||||
#else
|
||||
|
||||
#if JUCE_WINDOWS && ! defined (_CONSOLE)
|
||||
|
||||
String JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameters()
|
||||
{
|
||||
return CharacterFunctions::findEndOfToken (CharPointer_UTF16 (GetCommandLineW()),
|
||||
CharPointer_UTF16 (L" "),
|
||||
CharPointer_UTF16 (L"\"")).findEndOfWhitespace();
|
||||
}
|
||||
|
||||
StringArray JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameterArray()
|
||||
{
|
||||
StringArray s;
|
||||
int argc = 0;
|
||||
|
||||
if (auto argv = CommandLineToArgvW (GetCommandLineW(), &argc))
|
||||
{
|
||||
s = StringArray (argv + 1, argc - 1);
|
||||
LocalFree (argv);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if JUCE_IOS && JUCE_MODULE_AVAILABLE_juce_gui_basics
|
||||
extern int juce_iOSMain (int argc, const char* argv[], void* classPtr);
|
||||
#endif
|
||||
|
||||
#if JUCE_MAC
|
||||
extern void initialiseNSApplication();
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined(JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER)
|
||||
extern int juce_gtkWebkitMain (int argc, const char* argv[]);
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
const char* const* juce_argv = nullptr;
|
||||
int juce_argc = 0;
|
||||
#else
|
||||
extern const char* const* juce_argv; // declared in juce_core
|
||||
extern int juce_argc;
|
||||
#endif
|
||||
|
||||
String JUCEApplicationBase::getCommandLineParameters()
|
||||
{
|
||||
String argString;
|
||||
|
||||
for (int i = 1; i < juce_argc; ++i)
|
||||
{
|
||||
String arg { CharPointer_UTF8 (juce_argv[i]) };
|
||||
|
||||
if (arg.containsChar (' ') && ! arg.isQuotedString())
|
||||
arg = arg.quoted ('"');
|
||||
|
||||
argString << arg << ' ';
|
||||
}
|
||||
|
||||
return argString.trim();
|
||||
}
|
||||
|
||||
StringArray JUCEApplicationBase::getCommandLineParameterArray()
|
||||
{
|
||||
return StringArray (juce_argv + 1, juce_argc - 1);
|
||||
}
|
||||
|
||||
int JUCEApplicationBase::main (int argc, const char* argv[])
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
juce_argc = argc;
|
||||
juce_argv = argv;
|
||||
|
||||
#if JUCE_MAC
|
||||
initialiseNSApplication();
|
||||
#endif
|
||||
|
||||
#if (JUCE_LINUX || JUCE_BSD) && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined(JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER)
|
||||
if (argc >= 2 && String (argv[1]) == "--juce-gtkwebkitfork-child")
|
||||
return juce_gtkWebkitMain (argc, argv);
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS && JUCE_MODULE_AVAILABLE_juce_gui_basics
|
||||
return juce_iOSMain (argc, argv, iOSCustomDelegate);
|
||||
#else
|
||||
|
||||
return JUCEApplicationBase::main();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
int JUCEApplicationBase::main()
|
||||
{
|
||||
ScopedJuceInitialiser_GUI libraryInitialiser;
|
||||
jassert (createInstance != nullptr);
|
||||
|
||||
const std::unique_ptr<JUCEApplicationBase> app (createInstance());
|
||||
jassert (app != nullptr);
|
||||
|
||||
if (! app->initialiseApp())
|
||||
return app->shutdownApp();
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
// loop until a quit message is received..
|
||||
MessageManager::getInstance()->runDispatchLoop();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
return app->shutdownApp();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
bool JUCEApplicationBase::initialiseApp()
|
||||
{
|
||||
#if JUCE_HANDLE_MULTIPLE_INSTANCES
|
||||
if ((! moreThanOneInstanceAllowed()) && sendCommandLineToPreexistingInstance())
|
||||
{
|
||||
DBG ("Another instance is running - quitting...");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_STANDALONE_APPLICATION && (! defined (_CONSOLE)) && (! JUCE_MINGW)
|
||||
if (AttachConsole (ATTACH_PARENT_PROCESS) != 0)
|
||||
{
|
||||
// if we've launched a GUI app from cmd.exe or PowerShell, we need this to enable printf etc.
|
||||
// However, only reassign stdout, stderr, stdin if they have not been already opened by
|
||||
// a redirect or similar.
|
||||
FILE* ignore;
|
||||
|
||||
if (_fileno(stdout) < 0) freopen_s (&ignore, "CONOUT$", "w", stdout);
|
||||
if (_fileno(stderr) < 0) freopen_s (&ignore, "CONOUT$", "w", stderr);
|
||||
if (_fileno(stdin) < 0) freopen_s (&ignore, "CONIN$", "r", stdin);
|
||||
}
|
||||
#endif
|
||||
|
||||
// let the app do its setting-up..
|
||||
initialise (getCommandLineParameters());
|
||||
|
||||
stillInitialising = false;
|
||||
|
||||
if (MessageManager::getInstance()->hasStopMessageBeenSent())
|
||||
return false;
|
||||
|
||||
#if JUCE_HANDLE_MULTIPLE_INSTANCES
|
||||
if (auto* mih = multipleInstanceHandler.get())
|
||||
MessageManager::getInstance()->registerBroadcastListener (mih);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int JUCEApplicationBase::shutdownApp()
|
||||
{
|
||||
jassert (JUCEApplicationBase::getInstance() == this);
|
||||
|
||||
#if JUCE_HANDLE_MULTIPLE_INSTANCES
|
||||
if (auto* mih = multipleInstanceHandler.get())
|
||||
MessageManager::getInstance()->deregisterBroadcastListener (mih);
|
||||
#endif
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
// give the app a chance to clean up..
|
||||
shutdown();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
multipleInstanceHandler.reset();
|
||||
return getApplicationReturnValue();
|
||||
}
|
||||
|
||||
} // namespace juce
|
348
deps/juce/modules/juce_events/messages/juce_ApplicationBase.h
vendored
Normal file
348
deps/juce/modules/juce_events/messages/juce_ApplicationBase.h
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Abstract base class for application classes.
|
||||
|
||||
Note that in the juce_gui_basics module, there's a utility class JUCEApplication
|
||||
which derives from JUCEApplicationBase, and takes care of a few chores. Most
|
||||
of the time you'll want to derive your class from JUCEApplication rather than
|
||||
using JUCEApplicationBase directly, but if you're not using the juce_gui_basics
|
||||
module then you might need to go straight to this base class.
|
||||
|
||||
Any application that wants to run an event loop must declare a subclass of
|
||||
JUCEApplicationBase, and implement its various pure virtual methods.
|
||||
|
||||
It then needs to use the START_JUCE_APPLICATION macro somewhere in a CPP file
|
||||
to declare an instance of this class and generate suitable platform-specific
|
||||
boilerplate code to launch the app.
|
||||
|
||||
e.g. @code
|
||||
class MyJUCEApp : public JUCEApplication
|
||||
{
|
||||
public:
|
||||
MyJUCEApp() {}
|
||||
~MyJUCEApp() {}
|
||||
|
||||
void initialise (const String& commandLine) override
|
||||
{
|
||||
myMainWindow.reset (new MyApplicationWindow());
|
||||
myMainWindow->setBounds (100, 100, 400, 500);
|
||||
myMainWindow->setVisible (true);
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
myMainWindow = nullptr;
|
||||
}
|
||||
|
||||
const String getApplicationName() override
|
||||
{
|
||||
return "Super JUCE-o-matic";
|
||||
}
|
||||
|
||||
const String getApplicationVersion() override
|
||||
{
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MyApplicationWindow> myMainWindow;
|
||||
};
|
||||
|
||||
// this generates boilerplate code to launch our app class:
|
||||
START_JUCE_APPLICATION (MyJUCEApp)
|
||||
@endcode
|
||||
|
||||
@see JUCEApplication, START_JUCE_APPLICATION
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API JUCEApplicationBase
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
JUCEApplicationBase();
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~JUCEApplicationBase();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the global instance of the application object that's running. */
|
||||
static JUCEApplicationBase* getInstance() noexcept { return appInstance; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the application's name. */
|
||||
virtual const String getApplicationName() = 0;
|
||||
|
||||
/** Returns the application's version number. */
|
||||
virtual const String getApplicationVersion() = 0;
|
||||
|
||||
/** Checks whether multiple instances of the app are allowed.
|
||||
|
||||
If your application class returns true for this, more than one instance is
|
||||
permitted to run (except on the Mac where this isn't possible).
|
||||
|
||||
If it's false, the second instance won't start, but you will still get a
|
||||
callback to anotherInstanceStarted() to tell you about this - which
|
||||
gives you a chance to react to what the user was trying to do.
|
||||
|
||||
@see anotherInstanceStarted
|
||||
*/
|
||||
virtual bool moreThanOneInstanceAllowed() = 0;
|
||||
|
||||
/** Called when the application starts.
|
||||
|
||||
This will be called once to let the application do whatever initialisation
|
||||
it needs, create its windows, etc.
|
||||
|
||||
After the method returns, the normal event-dispatch loop will be run,
|
||||
until the quit() method is called, at which point the shutdown()
|
||||
method will be called to let the application clear up anything it needs
|
||||
to delete.
|
||||
|
||||
If during the initialise() method, the application decides not to start-up
|
||||
after all, it can just call the quit() method and the event loop won't be run.
|
||||
|
||||
@param commandLineParameters the line passed in does not include the name of
|
||||
the executable, just the parameter list. To get the
|
||||
parameters as an array, you can call
|
||||
JUCEApplication::getCommandLineParameters()
|
||||
@see shutdown, quit
|
||||
*/
|
||||
virtual void initialise (const String& commandLineParameters) = 0;
|
||||
|
||||
/* Called to allow the application to clear up before exiting.
|
||||
|
||||
After JUCEApplication::quit() has been called, the event-dispatch loop will
|
||||
terminate, and this method will get called to allow the app to sort itself
|
||||
out.
|
||||
|
||||
Be careful that nothing happens in this method that might rely on messages
|
||||
being sent, or any kind of window activity, because the message loop is no
|
||||
longer running at this point.
|
||||
|
||||
@see DeletedAtShutdown
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/** Indicates that the user has tried to start up another instance of the app.
|
||||
|
||||
This will get called even if moreThanOneInstanceAllowed() is false.
|
||||
It is currently only implemented on Windows and Mac.
|
||||
|
||||
@see moreThanOneInstanceAllowed
|
||||
*/
|
||||
virtual void anotherInstanceStarted (const String& commandLine) = 0;
|
||||
|
||||
/** Called when the operating system is trying to close the application.
|
||||
|
||||
The default implementation of this method is to call quit(), but it may
|
||||
be overloaded to ignore the request or do some other special behaviour
|
||||
instead. For example, you might want to offer the user the chance to save
|
||||
their changes before quitting, and give them the chance to cancel.
|
||||
|
||||
If you want to send a quit signal to your app, this is the correct method
|
||||
to call, because it means that requests that come from the system get handled
|
||||
in the same way as those from your own application code. So e.g. you'd
|
||||
call this method from a "quit" item on a menu bar.
|
||||
*/
|
||||
virtual void systemRequestedQuit() = 0;
|
||||
|
||||
/** This method is called when the application is being put into background mode
|
||||
by the operating system.
|
||||
*/
|
||||
virtual void suspended() = 0;
|
||||
|
||||
/** This method is called when the application is being woken from background mode
|
||||
by the operating system.
|
||||
*/
|
||||
virtual void resumed() = 0;
|
||||
|
||||
/** This method is called when the application (generally on android) is started
|
||||
*/
|
||||
virtual void started() {}
|
||||
|
||||
/** This method is called when the application (generally on android) is stopped
|
||||
*/
|
||||
virtual void stopped() {}
|
||||
|
||||
/** If any unhandled exceptions make it through to the message dispatch loop, this
|
||||
callback will be triggered, in case you want to log them or do some other
|
||||
type of error-handling.
|
||||
|
||||
If the type of exception is derived from the std::exception class, the pointer
|
||||
passed-in will be valid. If the exception is of unknown type, this pointer
|
||||
will be null.
|
||||
*/
|
||||
virtual void unhandledException (const std::exception*,
|
||||
const String& sourceFilename,
|
||||
int lineNumber) = 0;
|
||||
|
||||
/** Called by the operating system to indicate that you should reduce your memory
|
||||
footprint.
|
||||
|
||||
You should override this method to free up some memory gracefully, if possible,
|
||||
otherwise the host may forcibly kill your app.
|
||||
|
||||
At the moment this method is only called on iOS.
|
||||
*/
|
||||
virtual void memoryWarningReceived() { jassertfalse; }
|
||||
|
||||
/** Called by the operating system when a custom file type was opened. You are expected
|
||||
* to return true if you handled the URL.
|
||||
|
||||
At the moment this method is only called on iOS.
|
||||
*/
|
||||
virtual void urlOpened(const URL& url) { }
|
||||
|
||||
//==============================================================================
|
||||
/** This will be called when the back button on a device is pressed. The return value
|
||||
should be used to indicate whether the back button event has been handled by
|
||||
the application, for example if you want to implement custom navigation instead
|
||||
of the standard behaviour on Android.
|
||||
|
||||
This is currently only implemented on Android devices.
|
||||
|
||||
@returns true if the event has been handled, or false if the default OS
|
||||
behaviour should happen
|
||||
*/
|
||||
virtual bool backButtonPressed() { return false; }
|
||||
|
||||
//==============================================================================
|
||||
/** Signals that the main message loop should stop and the application should terminate.
|
||||
|
||||
This isn't synchronous, it just posts a quit message to the main queue, and
|
||||
when this message arrives, the message loop will stop, the shutdown() method
|
||||
will be called, and the app will exit.
|
||||
|
||||
Note that this will cause an unconditional quit to happen, so if you need an
|
||||
extra level before this, e.g. to give the user the chance to save their work
|
||||
and maybe cancel the quit, you'll need to handle this in the systemRequestedQuit()
|
||||
method - see that method's help for more info.
|
||||
|
||||
@see MessageManager
|
||||
*/
|
||||
static void quit();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the application's command line parameters as a set of strings.
|
||||
@see getCommandLineParameters
|
||||
*/
|
||||
static StringArray JUCE_CALLTYPE getCommandLineParameterArray();
|
||||
|
||||
/** Returns the application's command line parameters as a single string.
|
||||
@see getCommandLineParameterArray
|
||||
*/
|
||||
static String JUCE_CALLTYPE getCommandLineParameters();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the value that should be returned as the application's exit code when the
|
||||
app quits.
|
||||
|
||||
This is the value that's returned by the main() function. Normally you'd leave this
|
||||
as 0 unless you want to indicate an error code.
|
||||
|
||||
@see getApplicationReturnValue
|
||||
*/
|
||||
void setApplicationReturnValue (int newReturnValue) noexcept;
|
||||
|
||||
/** Returns the value that has been set as the application's exit code.
|
||||
@see setApplicationReturnValue
|
||||
*/
|
||||
int getApplicationReturnValue() const noexcept { return appReturnValue; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this executable is running as an app (as opposed to being a plugin
|
||||
or other kind of shared library. */
|
||||
static bool isStandaloneApp() noexcept { return createInstance != nullptr; }
|
||||
|
||||
/** Returns true if the application hasn't yet completed its initialise() method
|
||||
and entered the main event loop.
|
||||
|
||||
This is handy for things like splash screens to know when the app's up-and-running
|
||||
properly.
|
||||
*/
|
||||
bool isInitialising() const noexcept { return stillInitialising; }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// The following methods are for internal use only...
|
||||
static int main();
|
||||
static int main (int argc, const char* argv[]);
|
||||
|
||||
static void appWillTerminateByForce();
|
||||
using CreateInstanceFunction = JUCEApplicationBase* (*)();
|
||||
static CreateInstanceFunction createInstance;
|
||||
|
||||
#if JUCE_IOS
|
||||
static void* iOSCustomDelegate;
|
||||
#endif
|
||||
|
||||
virtual bool initialiseApp();
|
||||
int shutdownApp();
|
||||
static void JUCE_CALLTYPE sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber);
|
||||
bool sendCommandLineToPreexistingInstance();
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static JUCEApplicationBase* appInstance;
|
||||
int appReturnValue = 0;
|
||||
bool stillInitialising = true;
|
||||
|
||||
struct MultipleInstanceHandler;
|
||||
std::unique_ptr<MultipleInstanceHandler> multipleInstanceHandler;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS || DOXYGEN
|
||||
|
||||
/** The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to
|
||||
the JUCEApplicationBase::sendUnhandledException() method.
|
||||
This functionality can be enabled with the JUCE_CATCH_UNHANDLED_EXCEPTIONS macro.
|
||||
*/
|
||||
#define JUCE_TRY try
|
||||
|
||||
/** The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to
|
||||
the JUCEApplicationBase::sendUnhandledException() method.
|
||||
This functionality can be enabled with the JUCE_CATCH_UNHANDLED_EXCEPTIONS macro.
|
||||
*/
|
||||
#define JUCE_CATCH_EXCEPTION \
|
||||
catch (const std::exception& e) { juce::JUCEApplicationBase::sendUnhandledException (&e, __FILE__, __LINE__); } \
|
||||
catch (...) { juce::JUCEApplicationBase::sendUnhandledException (nullptr, __FILE__, __LINE__); }
|
||||
|
||||
#else
|
||||
#define JUCE_TRY
|
||||
#define JUCE_CATCH_EXCEPTION
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
74
deps/juce/modules/juce_events/messages/juce_CallbackMessage.h
vendored
Normal file
74
deps/juce/modules/juce_events/messages/juce_CallbackMessage.h
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A message that invokes a callback method when it gets delivered.
|
||||
|
||||
You can use this class to fire off actions that you want to be performed later
|
||||
on the message thread.
|
||||
|
||||
To use it, create a subclass of CallbackMessage which implements the messageCallback()
|
||||
method, then call post() to dispatch it. The event thread will then invoke your
|
||||
messageCallback() method later on, and will automatically delete the message object
|
||||
afterwards.
|
||||
|
||||
Always create a new instance of a CallbackMessage on the heap, as it will be
|
||||
deleted automatically after the message has been delivered.
|
||||
|
||||
Note that this class was essential back in the days before C++11, but in modern
|
||||
times you may prefer to use MessageManager::callAsync() with a lambda.
|
||||
|
||||
@see MessageManager::callAsync, MessageListener, ActionListener, ChangeListener
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API CallbackMessage : public MessageManager::MessageBase
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
CallbackMessage() = default;
|
||||
|
||||
/** Destructor. */
|
||||
~CallbackMessage() override = default;
|
||||
|
||||
//==============================================================================
|
||||
/** 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.
|
||||
*/
|
||||
void messageCallback() override = 0;
|
||||
|
||||
private:
|
||||
// Avoid the leak-detector because for plugins, the host can unload our DLL with undelivered
|
||||
// messages still in the system event queue. These aren't harmful, but can cause annoying assertions.
|
||||
JUCE_DECLARE_NON_COPYABLE (CallbackMessage)
|
||||
};
|
||||
|
||||
} // namespace juce
|
89
deps/juce/modules/juce_events/messages/juce_DeletedAtShutdown.cpp
vendored
Normal file
89
deps/juce/modules/juce_events/messages/juce_DeletedAtShutdown.cpp
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
static SpinLock deletedAtShutdownLock; // use a spin lock because it can be statically initialised
|
||||
|
||||
static Array<DeletedAtShutdown*>& getDeletedAtShutdownObjects()
|
||||
{
|
||||
static Array<DeletedAtShutdown*> objects;
|
||||
return objects;
|
||||
}
|
||||
|
||||
DeletedAtShutdown::DeletedAtShutdown()
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (deletedAtShutdownLock);
|
||||
getDeletedAtShutdownObjects().add (this);
|
||||
}
|
||||
|
||||
DeletedAtShutdown::~DeletedAtShutdown()
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (deletedAtShutdownLock);
|
||||
getDeletedAtShutdownObjects().removeFirstMatchingValue (this);
|
||||
}
|
||||
|
||||
// Disable unreachable code warning, in case the compiler manages to figure out that
|
||||
// you have no classes of DeletedAtShutdown that could throw an exception in their destructor.
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702)
|
||||
|
||||
void DeletedAtShutdown::deleteAll()
|
||||
{
|
||||
// make a local copy of the array, so it can't get into a loop if something
|
||||
// creates another DeletedAtShutdown object during its destructor.
|
||||
Array<DeletedAtShutdown*> localCopy;
|
||||
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (deletedAtShutdownLock);
|
||||
localCopy = getDeletedAtShutdownObjects();
|
||||
}
|
||||
|
||||
for (int i = localCopy.size(); --i >= 0;)
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
auto* deletee = localCopy.getUnchecked(i);
|
||||
|
||||
// double-check that it's not already been deleted during another object's destructor.
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (deletedAtShutdownLock);
|
||||
|
||||
if (! getDeletedAtShutdownObjects().contains (deletee))
|
||||
deletee = nullptr;
|
||||
}
|
||||
|
||||
delete deletee;
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
// if this fails, then it's likely that some new DeletedAtShutdown objects were
|
||||
// created while executing the destructors of the other ones.
|
||||
jassert (getDeletedAtShutdownObjects().isEmpty());
|
||||
|
||||
getDeletedAtShutdownObjects().clear(); // just to make sure the array doesn't have any memory still allocated
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
} // namespace juce
|
65
deps/juce/modules/juce_events/messages/juce_DeletedAtShutdown.h
vendored
Normal file
65
deps/juce/modules/juce_events/messages/juce_DeletedAtShutdown.h
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Classes derived from this will be automatically deleted when the application exits.
|
||||
|
||||
After JUCEApplicationBase::shutdown() has been called, any objects derived from
|
||||
DeletedAtShutdown which are still in existence will be deleted in the reverse
|
||||
order to that in which they were created.
|
||||
|
||||
So if you've got a singleton and don't want to have to explicitly delete it, just
|
||||
inherit from this and it'll be taken care of.
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API DeletedAtShutdown
|
||||
{
|
||||
protected:
|
||||
/** Creates a DeletedAtShutdown object. */
|
||||
DeletedAtShutdown();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
It's ok to delete these objects explicitly - it's only the ones left
|
||||
dangling at the end that will be deleted automatically.
|
||||
*/
|
||||
virtual ~DeletedAtShutdown();
|
||||
|
||||
|
||||
public:
|
||||
/** Deletes all extant objects.
|
||||
|
||||
This shouldn't be used by applications, as it's called automatically
|
||||
in the shutdown code of the JUCEApplicationBase class.
|
||||
*/
|
||||
static void deleteAll();
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (DeletedAtShutdown)
|
||||
};
|
||||
|
||||
} // namespace juce
|
213
deps/juce/modules/juce_events/messages/juce_Initialisation.h
vendored
Normal file
213
deps/juce/modules/juce_events/messages/juce_Initialisation.h
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises JUCE's GUI classes.
|
||||
|
||||
If you're embedding JUCE into an application that uses its own event-loop rather
|
||||
than using the START_JUCE_APPLICATION macro, call this function before making any
|
||||
JUCE calls, to make sure things are initialised correctly.
|
||||
|
||||
Note that if you're creating a JUCE DLL for Windows, you may also need to call the
|
||||
Process::setCurrentModuleInstanceHandle() method.
|
||||
|
||||
@see shutdownJuce_GUI()
|
||||
*/
|
||||
JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI();
|
||||
|
||||
/** Clears up any static data being used by JUCE's GUI classes.
|
||||
|
||||
If you're embedding JUCE into an application that uses its own event-loop rather
|
||||
than using the START_JUCE_APPLICATION macro, call this function in your shutdown
|
||||
code to clean up any JUCE objects that might be lying around.
|
||||
|
||||
@see initialiseJuce_GUI()
|
||||
*/
|
||||
JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A utility object that helps you initialise and shutdown JUCE correctly
|
||||
using an RAII pattern.
|
||||
|
||||
When the first instance of this class is created, it calls initialiseJuce_GUI(),
|
||||
and when the last instance is deleted, it calls shutdownJuce_GUI(), so that you
|
||||
can easily be sure that as long as at least one instance of the class exists, the
|
||||
library will be initialised.
|
||||
|
||||
This class is particularly handy to use at the beginning of a console app's
|
||||
main() function, because it'll take care of shutting down whenever you return
|
||||
from the main() call.
|
||||
|
||||
Be careful with your threading though - to be safe, you should always make sure
|
||||
that these objects are created and deleted on the message thread.
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API ScopedJuceInitialiser_GUI final
|
||||
{
|
||||
public:
|
||||
/** The constructor simply calls initialiseJuce_GUI(). */
|
||||
ScopedJuceInitialiser_GUI();
|
||||
|
||||
/** The destructor simply calls shutdownJuce_GUI(). */
|
||||
~ScopedJuceInitialiser_GUI();
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
To start a JUCE app, use this macro: START_JUCE_APPLICATION (AppSubClass) where
|
||||
AppSubClass is the name of a class derived from JUCEApplication or JUCEApplicationBase.
|
||||
|
||||
See the JUCEApplication and JUCEApplicationBase class documentation for more details.
|
||||
*/
|
||||
#if DOXYGEN
|
||||
#define START_JUCE_APPLICATION(AppClass)
|
||||
#else
|
||||
#if JUCE_WINDOWS && ! defined (_CONSOLE)
|
||||
#define JUCE_MAIN_FUNCTION \
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28251) \
|
||||
int __stdcall WinMain (struct HINSTANCE__*, struct HINSTANCE__*, char*, int) \
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
#define JUCE_MAIN_FUNCTION_ARGS
|
||||
#else
|
||||
#define JUCE_MAIN_FUNCTION int main (int argc, char* argv[])
|
||||
#define JUCE_MAIN_FUNCTION_ARGS argc, (const char**) argv
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
|
||||
#define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") \
|
||||
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
|
||||
void* juce_GetIOSCustomDelegateClass() { return nullptr; } \
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#define JUCE_CREATE_APPLICATION_DEFINE_CUSTOM_DELEGATE(AppClass, DelegateClass) \
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") \
|
||||
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
|
||||
void* juce_GetIOSCustomDelegateClass() { return [DelegateClass class]; } \
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#define JUCE_MAIN_FUNCTION_DEFINITION \
|
||||
extern "C" JUCE_MAIN_FUNCTION \
|
||||
{ \
|
||||
juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; \
|
||||
juce::JUCEApplicationBase::iOSCustomDelegate = juce_GetIOSCustomDelegateClass(); \
|
||||
return juce::JUCEApplicationBase::main (JUCE_MAIN_FUNCTION_ARGS); \
|
||||
}
|
||||
|
||||
#elif JUCE_ANDROID
|
||||
|
||||
#define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
|
||||
extern "C" juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); }
|
||||
|
||||
#define JUCE_MAIN_FUNCTION_DEFINITION
|
||||
|
||||
#else
|
||||
|
||||
#define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
|
||||
juce::JUCEApplicationBase* juce_CreateApplication(); \
|
||||
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); }
|
||||
|
||||
#define JUCE_MAIN_FUNCTION_DEFINITION \
|
||||
extern "C" JUCE_MAIN_FUNCTION \
|
||||
{ \
|
||||
juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; \
|
||||
return juce::JUCEApplicationBase::main (JUCE_MAIN_FUNCTION_ARGS); \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if JucePlugin_Build_Standalone
|
||||
#if JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP
|
||||
#define START_JUCE_APPLICATION(AppClass) JUCE_CREATE_APPLICATION_DEFINE(AppClass)
|
||||
#if JUCE_IOS
|
||||
#define START_JUCE_APPLICATION_WITH_CUSTOM_DELEGATE(AppClass, DelegateClass) JUCE_CREATE_APPLICATION_DEFINE_CUSTOM_DELEGATE(AppClass, DelegateClass)
|
||||
#endif
|
||||
#else
|
||||
#define START_JUCE_APPLICATION(AppClass) static_assert(false, "You are trying to use START_JUCE_APPLICATION in an audio plug-in. Define JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 if you want to use a custom standalone target app.");
|
||||
#if JUCE_IOS
|
||||
#define START_JUCE_APPLICATION_WITH_CUSTOM_DELEGATE(AppClass, DelegateClass) static_assert(false, "You are trying to use START_JUCE_APPLICATION in an audio plug-in. Define JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 if you want to use a custom standalone target app.");
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
|
||||
#define START_JUCE_APPLICATION(AppClass) \
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") \
|
||||
JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
|
||||
JUCE_MAIN_FUNCTION_DEFINITION \
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#if JUCE_IOS
|
||||
/**
|
||||
You can instruct JUCE to use a custom iOS app delegate class instead of JUCE's default
|
||||
app delegate. For JUCE to work you must pass all messages to JUCE's internal app delegate.
|
||||
Below is an example of minimal forwarding custom delegate. Note that you are at your own
|
||||
risk if you decide to use your own delegate and subtle, hard to debug bugs may occur.
|
||||
|
||||
@interface MyCustomDelegate : NSObject <UIApplicationDelegate> { NSObject<UIApplicationDelegate>* juceDelegate; } @end
|
||||
|
||||
@implementation MyCustomDelegate
|
||||
|
||||
-(id) init
|
||||
{
|
||||
self = [super init];
|
||||
juceDelegate = reinterpret_cast<NSObject<UIApplicationDelegate>*> ([[NSClassFromString (@"JuceAppStartupDelegate") alloc] init]);
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
[juceDelegate release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) forwardInvocation: (NSInvocation*) anInvocation
|
||||
{
|
||||
if (juceDelegate != nullptr && [juceDelegate respondsToSelector: [anInvocation selector]])
|
||||
[anInvocation invokeWithTarget: juceDelegate];
|
||||
else
|
||||
[super forwardInvocation: anInvocation];
|
||||
}
|
||||
|
||||
-(BOOL) respondsToSelector: (SEL) aSelector
|
||||
{
|
||||
if (juceDelegate != nullptr && [juceDelegate respondsToSelector: aSelector])
|
||||
return YES;
|
||||
|
||||
return [super respondsToSelector: aSelector];
|
||||
}
|
||||
@end
|
||||
*/
|
||||
#define START_JUCE_APPLICATION_WITH_CUSTOM_DELEGATE(AppClass, DelegateClass) \
|
||||
JUCE_CREATE_APPLICATION_DEFINE_CUSTOM_DELEGATE(AppClass, DelegateClass) \
|
||||
JUCE_MAIN_FUNCTION_DEFINITION
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
64
deps/juce/modules/juce_events/messages/juce_Message.h
vendored
Normal file
64
deps/juce/modules/juce_events/messages/juce_Message.h
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class MessageListener;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** The base class for objects that can be sent to a MessageListener.
|
||||
|
||||
If you want to send a message that carries some kind of custom data, just
|
||||
create a subclass of Message with some appropriate member variables to hold
|
||||
your data.
|
||||
|
||||
Always create a new instance of a Message object on the heap, as it will be
|
||||
deleted automatically after the message has been delivered.
|
||||
|
||||
@see MessageListener, MessageManager, ActionListener, ChangeListener
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API Message : public MessageManager::MessageBase
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an uninitialised message. */
|
||||
Message() noexcept;
|
||||
~Message() override;
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<Message>;
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
friend class MessageListener;
|
||||
WeakReference<MessageListener> recipient;
|
||||
void messageCallback() override;
|
||||
|
||||
// Avoid the leak-detector because for plugins, the host can unload our DLL with undelivered
|
||||
// messages still in the system event queue. These aren't harmful, but can cause annoying assertions.
|
||||
JUCE_DECLARE_NON_COPYABLE (Message)
|
||||
};
|
||||
|
||||
} // namespace juce
|
52
deps/juce/modules/juce_events/messages/juce_MessageListener.cpp
vendored
Normal file
52
deps/juce/modules/juce_events/messages/juce_MessageListener.cpp
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
Message::Message() noexcept {}
|
||||
Message::~Message() {}
|
||||
|
||||
void Message::messageCallback()
|
||||
{
|
||||
if (auto* r = recipient.get())
|
||||
r->handleMessage (*this);
|
||||
}
|
||||
|
||||
MessageListener::MessageListener() noexcept
|
||||
{
|
||||
// Are you trying to create a messagelistener before or after juce has been initialised??
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
}
|
||||
|
||||
MessageListener::~MessageListener()
|
||||
{
|
||||
masterReference.clear();
|
||||
}
|
||||
|
||||
void MessageListener::postMessage (Message* const message) const
|
||||
{
|
||||
message->recipient = const_cast<MessageListener*> (this);
|
||||
message->post();
|
||||
}
|
||||
|
||||
} // namespace juce
|
70
deps/juce/modules/juce_events/messages/juce_MessageListener.h
vendored
Normal file
70
deps/juce/modules/juce_events/messages/juce_MessageListener.h
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
MessageListener subclasses can post and receive Message objects.
|
||||
|
||||
@see Message, MessageManager, ActionListener, ChangeListener
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API MessageListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MessageListener() noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~MessageListener();
|
||||
|
||||
//==============================================================================
|
||||
/** This is the callback method that receives incoming messages.
|
||||
|
||||
This is called by the MessageManager from its dispatch loop.
|
||||
|
||||
@see postMessage
|
||||
*/
|
||||
virtual void handleMessage (const Message& message) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Sends a message to the message queue, for asynchronous delivery to this listener
|
||||
later on.
|
||||
|
||||
This method can be called safely by any thread.
|
||||
|
||||
@param message the message object to send - this will be deleted
|
||||
automatically by the message queue, so make sure it's
|
||||
allocated on the heap, not the stack!
|
||||
@see handleMessage
|
||||
*/
|
||||
void postMessage (Message* message) const;
|
||||
|
||||
private:
|
||||
WeakReference<MessageListener>::Master masterReference;
|
||||
friend class WeakReference<MessageListener>;
|
||||
};
|
||||
|
||||
} // namespace juce
|
483
deps/juce/modules/juce_events/messages/juce_MessageManager.cpp
vendored
Normal file
483
deps/juce/modules/juce_events/messages/juce_MessageManager.cpp
vendored
Normal file
@ -0,0 +1,483 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
MessageManager::MessageManager() noexcept
|
||||
: messageThreadId (Thread::getCurrentThreadId())
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
Thread::setCurrentThreadName ("JUCE Message Thread");
|
||||
}
|
||||
|
||||
MessageManager::~MessageManager() noexcept
|
||||
{
|
||||
broadcaster.reset();
|
||||
|
||||
doPlatformSpecificShutdown();
|
||||
|
||||
jassert (instance == this);
|
||||
instance = nullptr; // do this last in case this instance is still needed by doPlatformSpecificShutdown()
|
||||
}
|
||||
|
||||
MessageManager* MessageManager::instance = nullptr;
|
||||
|
||||
MessageManager* MessageManager::getInstance()
|
||||
{
|
||||
if (instance == nullptr)
|
||||
{
|
||||
instance = new MessageManager();
|
||||
doPlatformSpecificInitialisation();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
MessageManager* MessageManager::getInstanceWithoutCreating() noexcept
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
void MessageManager::deleteInstance()
|
||||
{
|
||||
deleteAndZero (instance);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool MessageManager::MessageBase::post()
|
||||
{
|
||||
auto* mm = MessageManager::instance;
|
||||
|
||||
if (mm == nullptr || mm->quitMessagePosted.get() != 0 || ! postMessageToSystemQueue (this))
|
||||
{
|
||||
Ptr deleter (this); // (this will delete messages that were just created with a 0 ref count)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID)
|
||||
// implemented in platform-specific code (juce_linux_Messaging.cpp and juce_win32_Messaging.cpp)
|
||||
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages);
|
||||
|
||||
class MessageManager::QuitMessage : public MessageManager::MessageBase
|
||||
{
|
||||
public:
|
||||
QuitMessage() {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (auto* mm = MessageManager::instance)
|
||||
mm->quitMessageReceived = true;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (QuitMessage)
|
||||
};
|
||||
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
while (quitMessageReceived.get() == 0)
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
if (! dispatchNextMessageOnSystemQueue (false))
|
||||
Thread::sleep (1);
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
(new QuitMessage())->post();
|
||||
quitMessagePosted = true;
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
|
||||
{
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
auto endTime = Time::currentTimeMillis() + millisecondsToRunFor;
|
||||
|
||||
while (quitMessageReceived.get() == 0)
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0))
|
||||
Thread::sleep (1);
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime)
|
||||
break;
|
||||
}
|
||||
|
||||
return quitMessageReceived.get() == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class AsyncFunctionCallback : public MessageManager::MessageBase
|
||||
{
|
||||
public:
|
||||
AsyncFunctionCallback (MessageCallbackFunction* const f, void* const param)
|
||||
: func (f), parameter (param)
|
||||
{}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
result = (*func) (parameter);
|
||||
finished.signal();
|
||||
}
|
||||
|
||||
WaitableEvent finished;
|
||||
std::atomic<void*> result { nullptr };
|
||||
|
||||
private:
|
||||
MessageCallbackFunction* const func;
|
||||
void* const parameter;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback)
|
||||
};
|
||||
|
||||
void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter)
|
||||
{
|
||||
if (isThisTheMessageThread())
|
||||
return func (parameter);
|
||||
|
||||
// If this thread has the message manager locked, then this will deadlock!
|
||||
jassert (! currentThreadHasLockedMessageManager());
|
||||
|
||||
const ReferenceCountedObjectPtr<AsyncFunctionCallback> message (new AsyncFunctionCallback (func, parameter));
|
||||
|
||||
if (message->post())
|
||||
{
|
||||
message->finished.wait();
|
||||
return message->result.load();
|
||||
}
|
||||
|
||||
jassertfalse; // the OS message queue failed to send the message!
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool MessageManager::callAsync (std::function<void()> fn)
|
||||
{
|
||||
struct AsyncCallInvoker : public MessageBase
|
||||
{
|
||||
AsyncCallInvoker (std::function<void()> f) : callback (std::move (f)) {}
|
||||
void messageCallback() override { callback(); }
|
||||
std::function<void()> callback;
|
||||
};
|
||||
|
||||
return (new AsyncCallInvoker (std::move (fn)))->post();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::deliverBroadcastMessage (const String& value)
|
||||
{
|
||||
if (broadcaster != nullptr)
|
||||
broadcaster->sendActionMessage (value);
|
||||
}
|
||||
|
||||
void MessageManager::registerBroadcastListener (ActionListener* const listener)
|
||||
{
|
||||
if (broadcaster == nullptr)
|
||||
broadcaster.reset (new ActionBroadcaster());
|
||||
|
||||
broadcaster->addActionListener (listener);
|
||||
}
|
||||
|
||||
void MessageManager::deregisterBroadcastListener (ActionListener* const listener)
|
||||
{
|
||||
if (broadcaster != nullptr)
|
||||
broadcaster->removeActionListener (listener);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool MessageManager::isThisTheMessageThread() const noexcept
|
||||
{
|
||||
return Thread::getCurrentThreadId() == messageThreadId;
|
||||
}
|
||||
|
||||
void MessageManager::setCurrentThreadAsMessageThread()
|
||||
{
|
||||
auto thisThread = Thread::getCurrentThreadId();
|
||||
|
||||
if (messageThreadId != thisThread)
|
||||
{
|
||||
messageThreadId = thisThread;
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
// This is needed on windows to make sure the message window is created by this thread
|
||||
doPlatformSpecificShutdown();
|
||||
doPlatformSpecificInitialisation();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageManager::currentThreadHasLockedMessageManager() const noexcept
|
||||
{
|
||||
auto thisThread = Thread::getCurrentThreadId();
|
||||
return thisThread == messageThreadId || thisThread == threadWithLock.get();
|
||||
}
|
||||
|
||||
bool MessageManager::existsAndIsLockedByCurrentThread() noexcept
|
||||
{
|
||||
if (auto i = getInstanceWithoutCreating())
|
||||
return i->currentThreadHasLockedMessageManager();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageManager::existsAndIsCurrentThread() noexcept
|
||||
{
|
||||
if (auto i = getInstanceWithoutCreating())
|
||||
return i->isThisTheMessageThread();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
/* The only safe way to lock the message thread while another thread does
|
||||
some work is by posting a special message, whose purpose is to tie up the event
|
||||
loop until the other thread has finished its business.
|
||||
|
||||
Any other approach can get horribly deadlocked if the OS uses its own hidden locks which
|
||||
get locked before making an event callback, because if the same OS lock gets indirectly
|
||||
accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens
|
||||
in Cocoa).
|
||||
*/
|
||||
struct MessageManager::Lock::BlockingMessage : public MessageManager::MessageBase
|
||||
{
|
||||
BlockingMessage (const MessageManager::Lock* parent) noexcept
|
||||
: owner (parent)
|
||||
{}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
{
|
||||
ScopedLock lock (ownerCriticalSection);
|
||||
|
||||
if (auto* o = owner.get())
|
||||
o->messageCallback();
|
||||
}
|
||||
|
||||
releaseEvent.wait();
|
||||
}
|
||||
|
||||
CriticalSection ownerCriticalSection;
|
||||
Atomic<const MessageManager::Lock*> owner;
|
||||
WaitableEvent releaseEvent;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (BlockingMessage)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
MessageManager::Lock::Lock() {}
|
||||
MessageManager::Lock::~Lock() { exit(); }
|
||||
void MessageManager::Lock::enter() const noexcept { tryAcquire (true); }
|
||||
bool MessageManager::Lock::tryEnter() const noexcept { return tryAcquire (false); }
|
||||
|
||||
bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept
|
||||
{
|
||||
auto* mm = MessageManager::instance;
|
||||
|
||||
if (mm == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! lockIsMandatory && (abortWait.get() != 0))
|
||||
{
|
||||
abortWait.set (0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mm->currentThreadHasLockedMessageManager())
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
blockingMessage = *new BlockingMessage (this);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
jassert (! lockIsMandatory);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! blockingMessage->post())
|
||||
{
|
||||
// post of message failed while trying to get the lock
|
||||
jassert (! lockIsMandatory);
|
||||
blockingMessage = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
while (abortWait.get() == 0)
|
||||
lockedEvent.wait (-1);
|
||||
|
||||
abortWait.set (0);
|
||||
|
||||
if (lockGained.get() != 0)
|
||||
{
|
||||
mm->threadWithLock = Thread::getCurrentThreadId();
|
||||
return true;
|
||||
}
|
||||
|
||||
} while (lockIsMandatory);
|
||||
|
||||
// we didn't get the lock
|
||||
blockingMessage->releaseEvent.signal();
|
||||
|
||||
{
|
||||
ScopedLock lock (blockingMessage->ownerCriticalSection);
|
||||
|
||||
lockGained.set (0);
|
||||
blockingMessage->owner.set (nullptr);
|
||||
}
|
||||
|
||||
blockingMessage = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageManager::Lock::exit() const noexcept
|
||||
{
|
||||
if (lockGained.compareAndSetBool (false, true))
|
||||
{
|
||||
auto* mm = MessageManager::instance;
|
||||
|
||||
jassert (mm == nullptr || mm->currentThreadHasLockedMessageManager());
|
||||
lockGained.set (0);
|
||||
|
||||
if (mm != nullptr)
|
||||
mm->threadWithLock = {};
|
||||
|
||||
if (blockingMessage != nullptr)
|
||||
{
|
||||
blockingMessage->releaseEvent.signal();
|
||||
blockingMessage = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::Lock::messageCallback() const
|
||||
{
|
||||
lockGained.set (1);
|
||||
abort();
|
||||
}
|
||||
|
||||
void MessageManager::Lock::abort() const noexcept
|
||||
{
|
||||
abortWait.set (1);
|
||||
lockedEvent.signal();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
MessageManagerLock::MessageManagerLock (Thread* threadToCheck)
|
||||
: locked (attemptLock (threadToCheck, nullptr))
|
||||
{}
|
||||
|
||||
MessageManagerLock::MessageManagerLock (ThreadPoolJob* jobToCheck)
|
||||
: locked (attemptLock (nullptr, jobToCheck))
|
||||
{}
|
||||
|
||||
bool MessageManagerLock::attemptLock (Thread* threadToCheck, ThreadPoolJob* jobToCheck)
|
||||
{
|
||||
jassert (threadToCheck == nullptr || jobToCheck == nullptr);
|
||||
|
||||
if (threadToCheck != nullptr)
|
||||
threadToCheck->addListener (this);
|
||||
|
||||
if (jobToCheck != nullptr)
|
||||
jobToCheck->addListener (this);
|
||||
|
||||
// tryEnter may have a spurious abort (return false) so keep checking the condition
|
||||
while ((threadToCheck == nullptr || ! threadToCheck->threadShouldExit())
|
||||
&& (jobToCheck == nullptr || ! jobToCheck->shouldExit()))
|
||||
{
|
||||
if (mmLock.tryEnter())
|
||||
break;
|
||||
}
|
||||
|
||||
if (threadToCheck != nullptr)
|
||||
{
|
||||
threadToCheck->removeListener (this);
|
||||
|
||||
if (threadToCheck->threadShouldExit())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (jobToCheck != nullptr)
|
||||
{
|
||||
jobToCheck->removeListener (this);
|
||||
|
||||
if (jobToCheck->shouldExit())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MessageManagerLock::~MessageManagerLock() { mmLock.exit(); }
|
||||
|
||||
void MessageManagerLock::exitSignalSent()
|
||||
{
|
||||
mmLock.abort();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
MessageManager::getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
DeletedAtShutdown::deleteAll();
|
||||
MessageManager::deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
static int numScopedInitInstances = 0;
|
||||
|
||||
ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { if (numScopedInitInstances++ == 0) initialiseJuce_GUI(); }
|
||||
ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { if (--numScopedInitInstances == 0) shutdownJuce_GUI(); }
|
||||
|
||||
} // namespace juce
|
483
deps/juce/modules/juce_events/messages/juce_MessageManager.h
vendored
Normal file
483
deps/juce/modules/juce_events/messages/juce_MessageManager.h
vendored
Normal file
@ -0,0 +1,483 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class MessageManagerLock;
|
||||
class ThreadPoolJob;
|
||||
class ActionListener;
|
||||
class ActionBroadcaster;
|
||||
|
||||
//==============================================================================
|
||||
/** See MessageManager::callFunctionOnMessageThread() for use of this function type. */
|
||||
using MessageCallbackFunction = void* (void* userData);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class is in charge of the application's event-dispatch loop.
|
||||
|
||||
@see Message, CallbackMessage, MessageManagerLock, JUCEApplication, JUCEApplicationBase
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API MessageManager final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns the global instance of the MessageManager. */
|
||||
static MessageManager* getInstance();
|
||||
|
||||
/** Returns the global instance of the MessageManager, or nullptr if it doesn't exist. */
|
||||
static MessageManager* getInstanceWithoutCreating() noexcept;
|
||||
|
||||
/** Deletes the global MessageManager instance.
|
||||
Does nothing if no instance had been created.
|
||||
*/
|
||||
static void deleteInstance();
|
||||
|
||||
//==============================================================================
|
||||
/** Runs the event dispatch loop until a stop message is posted.
|
||||
|
||||
This method is only intended to be run by the application's startup routine,
|
||||
as it blocks, and will only return after the stopDispatchLoop() method has been used.
|
||||
|
||||
@see stopDispatchLoop
|
||||
*/
|
||||
void runDispatchLoop();
|
||||
|
||||
/** Sends a signal that the dispatch loop should terminate.
|
||||
|
||||
After this is called, the runDispatchLoop() or runDispatchLoopUntil() methods
|
||||
will be interrupted and will return.
|
||||
|
||||
@see runDispatchLoop
|
||||
*/
|
||||
void stopDispatchLoop();
|
||||
|
||||
/** Returns true if the stopDispatchLoop() method has been called.
|
||||
*/
|
||||
bool hasStopMessageBeenSent() const noexcept { return quitMessagePosted.get() != 0; }
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Synchronously dispatches messages until a given time has elapsed.
|
||||
|
||||
Returns false if a quit message has been posted by a call to stopDispatchLoop(),
|
||||
otherwise returns true.
|
||||
*/
|
||||
bool runDispatchLoopUntil (int millisecondsToRunFor);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Asynchronously invokes a function or C++11 lambda on the message thread.
|
||||
|
||||
@returns true if the message was successfully posted to the message queue,
|
||||
or false otherwise.
|
||||
*/
|
||||
static bool callAsync (std::function<void()> functionToCall);
|
||||
|
||||
/** Calls a function using the message-thread.
|
||||
|
||||
This can be used by any thread to cause this function to be called-back
|
||||
by the message thread. If it's the message-thread that's calling this method,
|
||||
then the function will just be called; if another thread is calling, a message
|
||||
will be posted to the queue, and this method will block until that message
|
||||
is delivered, the function is called, and the result is returned.
|
||||
|
||||
Be careful not to cause any deadlocks with this! It's easy to do - e.g. if the caller
|
||||
thread has a critical section locked, which an unrelated message callback then tries to lock
|
||||
before the message thread gets round to processing this callback.
|
||||
|
||||
@param callback the function to call - its signature must be @code
|
||||
void* myCallbackFunction (void*) @endcode
|
||||
@param userData a user-defined pointer that will be passed to the function that gets called
|
||||
@returns the value that the callback function returns.
|
||||
@see MessageManagerLock
|
||||
*/
|
||||
void* callFunctionOnMessageThread (MessageCallbackFunction* callback, void* userData);
|
||||
|
||||
/** Returns true if the caller-thread is the message thread. */
|
||||
bool isThisTheMessageThread() const noexcept;
|
||||
|
||||
/** Called to tell the manager that the current thread is the one that's running the dispatch loop.
|
||||
|
||||
(Best to ignore this method unless you really know what you're doing..)
|
||||
@see getCurrentMessageThread
|
||||
*/
|
||||
void setCurrentThreadAsMessageThread();
|
||||
|
||||
/** Returns the ID of the current message thread, as set by setCurrentThreadAsMessageThread().
|
||||
|
||||
(Best to ignore this method unless you really know what you're doing..)
|
||||
@see setCurrentThreadAsMessageThread
|
||||
*/
|
||||
Thread::ThreadID getCurrentMessageThread() const noexcept { return messageThreadId; }
|
||||
|
||||
/** Returns true if the caller thread has currently got the message manager locked.
|
||||
|
||||
see the MessageManagerLock class for more info about this.
|
||||
|
||||
This will be true if the caller is the message thread, because that automatically
|
||||
gains a lock while a message is being dispatched.
|
||||
*/
|
||||
bool currentThreadHasLockedMessageManager() const noexcept;
|
||||
|
||||
/** Returns true if there's an instance of the MessageManager, and if the current thread
|
||||
has the lock on it.
|
||||
*/
|
||||
static bool existsAndIsLockedByCurrentThread() noexcept;
|
||||
|
||||
/** Returns true if there's an instance of the MessageManager, and if the current thread
|
||||
is running it.
|
||||
*/
|
||||
static bool existsAndIsCurrentThread() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sends a message to all other JUCE applications that are running.
|
||||
|
||||
@param messageText the string that will be passed to the actionListenerCallback()
|
||||
method of the broadcast listeners in the other app.
|
||||
@see registerBroadcastListener, ActionListener
|
||||
*/
|
||||
static void broadcastMessage (const String& messageText);
|
||||
|
||||
/** Registers a listener to get told about broadcast messages.
|
||||
|
||||
The actionListenerCallback() callback's string parameter
|
||||
is the message passed into broadcastMessage().
|
||||
|
||||
@see broadcastMessage
|
||||
*/
|
||||
void registerBroadcastListener (ActionListener* listener);
|
||||
|
||||
/** Deregisters a broadcast listener. */
|
||||
void deregisterBroadcastListener (ActionListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Internal class used as the base class for all message objects.
|
||||
You shouldn't need to use this directly - see the CallbackMessage or Message
|
||||
classes instead.
|
||||
*/
|
||||
class JUCE_API MessageBase : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
MessageBase() = default;
|
||||
~MessageBase() override = default;
|
||||
|
||||
virtual void messageCallback() = 0;
|
||||
bool post();
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<MessageBase>;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (MessageBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A lock you can use to lock the message manager. You can use this class with
|
||||
the RAII-based ScopedLock classes.
|
||||
*/
|
||||
class JUCE_API Lock
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Creates a new critical section to exclusively access methods which can
|
||||
only be called when the message manager is locked.
|
||||
|
||||
Unlike CrititcalSection, multiple instances of this lock class provide
|
||||
exclusive access to a single resource - the MessageManager.
|
||||
*/
|
||||
Lock();
|
||||
|
||||
/** Destructor. */
|
||||
~Lock();
|
||||
|
||||
/** Acquires the message manager lock.
|
||||
|
||||
If the caller thread already has exclusive access to the MessageManager, this method
|
||||
will return immediately.
|
||||
If another thread is currently using the MessageManager, this will wait until that
|
||||
thread releases the lock to the MessageManager.
|
||||
|
||||
This call will only exit if the lock was acquired by this thread. Calling abort while
|
||||
a thread is waiting for enter to finish, will have no effect.
|
||||
|
||||
@see exit, abort
|
||||
*/
|
||||
void enter() const noexcept;
|
||||
|
||||
/** Attempts to lock the message manager and exits if abort is called.
|
||||
|
||||
This method behaves identically to enter, except that it will abort waiting for
|
||||
the lock if the abort method is called.
|
||||
|
||||
Unlike other JUCE critical sections, this method **will** block waiting for the lock.
|
||||
|
||||
To ensure predictable behaviour, you should re-check your abort condition if tryEnter
|
||||
returns false.
|
||||
|
||||
This method can be used if you want to do some work while waiting for the
|
||||
MessageManagerLock:
|
||||
|
||||
void doWorkWhileWaitingForMessageManagerLock()
|
||||
{
|
||||
MessageManager::Lock::ScopedTryLockType mmLock (messageManagerLock);
|
||||
|
||||
while (! mmLock.isLocked())
|
||||
{
|
||||
while (workQueue.size() > 0)
|
||||
{
|
||||
auto work = workQueue.pop();
|
||||
doSomeWork (work);
|
||||
}
|
||||
|
||||
// this will block until we either have the lock or there is work
|
||||
mmLock.retryLock();
|
||||
}
|
||||
|
||||
// we have the mmlock
|
||||
// do some message manager stuff like resizing and painting components
|
||||
}
|
||||
|
||||
// called from another thread
|
||||
void addWorkToDo (Work work)
|
||||
{
|
||||
queue.push (work);
|
||||
messageManagerLock.abort();
|
||||
}
|
||||
|
||||
@returns false if waiting for a lock was aborted, true if the lock was acquired.
|
||||
@see enter, abort, ScopedTryLock
|
||||
*/
|
||||
bool tryEnter() const noexcept;
|
||||
|
||||
/** Releases the message manager lock.
|
||||
@see enter, ScopedLock
|
||||
*/
|
||||
void exit() const noexcept;
|
||||
|
||||
/** Unblocks a thread which is waiting in tryEnter
|
||||
Call this method if you want to unblock a thread which is waiting for the
|
||||
MessageManager lock in tryEnter.
|
||||
This method does not have any effect on a thread waiting for a lock in enter.
|
||||
@see tryEnter
|
||||
*/
|
||||
void abort() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Provides the type of scoped lock to use with a CriticalSection. */
|
||||
using ScopedLockType = GenericScopedLock<Lock>;
|
||||
|
||||
/** Provides the type of scoped unlocker to use with a CriticalSection. */
|
||||
using ScopedUnlockType = GenericScopedUnlock<Lock>;
|
||||
|
||||
/** Provides the type of scoped try-locker to use with a CriticalSection. */
|
||||
using ScopedTryLockType = GenericScopedTryLock<Lock>;
|
||||
|
||||
private:
|
||||
struct BlockingMessage;
|
||||
friend class ReferenceCountedObjectPtr<BlockingMessage>;
|
||||
|
||||
bool tryAcquire (bool) const noexcept;
|
||||
void messageCallback() const;
|
||||
|
||||
//==============================================================================
|
||||
mutable ReferenceCountedObjectPtr<BlockingMessage> blockingMessage;
|
||||
WaitableEvent lockedEvent;
|
||||
mutable Atomic<int> abortWait, lockGained;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// Internal methods - do not use!
|
||||
void deliverBroadcastMessage (const String&);
|
||||
~MessageManager() noexcept;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
MessageManager() noexcept;
|
||||
|
||||
static MessageManager* instance;
|
||||
|
||||
friend class MessageBase;
|
||||
class QuitMessage;
|
||||
friend class QuitMessage;
|
||||
friend class MessageManagerLock;
|
||||
|
||||
std::unique_ptr<ActionBroadcaster> broadcaster;
|
||||
Atomic<int> quitMessagePosted { 0 }, quitMessageReceived { 0 };
|
||||
Thread::ThreadID messageThreadId;
|
||||
Atomic<Thread::ThreadID> threadWithLock;
|
||||
|
||||
static bool postMessageToSystemQueue (MessageBase*);
|
||||
static void* exitModalLoopCallback (void*);
|
||||
static void doPlatformSpecificInitialisation();
|
||||
static void doPlatformSpecificShutdown();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageManager)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Used to make sure that the calling thread has exclusive access to the message loop.
|
||||
|
||||
Because it's not thread-safe to call any of the Component or other UI classes
|
||||
from threads other than the message thread, one of these objects can be used to
|
||||
lock the message loop and allow this to be done. The message thread will be
|
||||
suspended for the lifetime of the MessageManagerLock object, so create one on
|
||||
the stack like this: @code
|
||||
void MyThread::run()
|
||||
{
|
||||
someData = 1234;
|
||||
|
||||
const MessageManagerLock mmLock;
|
||||
// the event loop will now be locked so it's safe to make a few calls..
|
||||
|
||||
myComponent->setBounds (newBounds);
|
||||
myComponent->repaint();
|
||||
|
||||
// ..the event loop will now be unlocked as the MessageManagerLock goes out of scope
|
||||
}
|
||||
@endcode
|
||||
|
||||
Obviously be careful not to create one of these and leave it lying around, or
|
||||
your app will grind to a halt!
|
||||
|
||||
MessageManagerLocks are re-entrant, so can be safely nested if the current thread
|
||||
already has the lock.
|
||||
|
||||
Another caveat is that using this in conjunction with other CriticalSections
|
||||
can create lots of interesting ways of producing a deadlock! In particular, if
|
||||
your message thread calls stopThread() for a thread that uses these locks,
|
||||
you'll get an (occasional) deadlock..
|
||||
|
||||
@see MessageManager, MessageManager::currentThreadHasLockedMessageManager
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API MessageManagerLock : private Thread::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Tries to acquire a lock on the message manager.
|
||||
|
||||
The constructor attempts to gain a lock on the message loop, and the lock will be
|
||||
kept for the lifetime of this object.
|
||||
|
||||
Optionally, you can pass a thread object here, and while waiting to obtain the lock,
|
||||
this method will keep checking whether the thread has been given the
|
||||
Thread::signalThreadShouldExit() signal. If this happens, then it will return
|
||||
without gaining the lock. If you pass a thread, you must check whether the lock was
|
||||
successful by calling lockWasGained(). If this is false, your thread is being told to
|
||||
die, so you should take evasive action.
|
||||
|
||||
If you pass nullptr for the thread object, it will wait indefinitely for the lock - be
|
||||
careful when doing this, because it's very easy to deadlock if your message thread
|
||||
attempts to call stopThread() on a thread just as that thread attempts to get the
|
||||
message lock.
|
||||
|
||||
If the calling thread already has the lock, nothing will be done, so it's safe and
|
||||
quick to use these locks recursively.
|
||||
|
||||
E.g.
|
||||
@code
|
||||
void run()
|
||||
{
|
||||
...
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
MessageManagerLock mml (Thread::getCurrentThread());
|
||||
|
||||
if (! mml.lockWasGained())
|
||||
return; // another thread is trying to kill us!
|
||||
|
||||
..do some locked stuff here..
|
||||
}
|
||||
|
||||
..and now the MM is now unlocked..
|
||||
}
|
||||
@endcode
|
||||
|
||||
*/
|
||||
MessageManagerLock (Thread* threadToCheckForExitSignal = nullptr);
|
||||
|
||||
//==============================================================================
|
||||
/** This has the same behaviour as the other constructor, but takes a ThreadPoolJob
|
||||
instead of a thread.
|
||||
|
||||
See the MessageManagerLock (Thread*) constructor for details on how this works.
|
||||
*/
|
||||
MessageManagerLock (ThreadPoolJob* jobToCheckForExitSignal);
|
||||
|
||||
//==============================================================================
|
||||
/** Releases the current thread's lock on the message manager.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
~MessageManagerLock() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the lock was successfully acquired.
|
||||
(See the constructor that takes a Thread for more info).
|
||||
*/
|
||||
bool lockWasGained() const noexcept { return locked; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
MessageManager::Lock mmLock;
|
||||
bool locked;
|
||||
|
||||
//==============================================================================
|
||||
bool attemptLock (Thread*, ThreadPoolJob*);
|
||||
void exitSignalSent() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (MessageManagerLock)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This macro is used to catch unsafe use of functions which expect to only be called
|
||||
on the message thread, or when a MessageManagerLock is in place.
|
||||
It will also fail if you try to use the function before the message manager has been
|
||||
created, which could happen if you accidentally invoke it during a static constructor.
|
||||
*/
|
||||
#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED \
|
||||
jassert (juce::MessageManager::existsAndIsLockedByCurrentThread());
|
||||
|
||||
/** This macro is used to catch unsafe use of functions which expect to only be called
|
||||
on the message thread.
|
||||
It will also fail if you try to use the function before the message manager has been
|
||||
created, which could happen if you accidentally invoke it during a static constructor.
|
||||
*/
|
||||
#define JUCE_ASSERT_MESSAGE_THREAD \
|
||||
jassert (juce::MessageManager::existsAndIsCurrentThread());
|
||||
|
||||
/** This macro is used to catch unsafe use of functions which expect to not be called
|
||||
outside the lifetime of the MessageManager.
|
||||
*/
|
||||
#define JUCE_ASSERT_MESSAGE_MANAGER_EXISTS \
|
||||
jassert (juce::MessageManager::getInstanceWithoutCreating() != nullptr);
|
||||
|
||||
|
||||
} // namespace juce
|
58
deps/juce/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h
vendored
Normal file
58
deps/juce/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_MAC || JUCE_WINDOWS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An instance of this class will provide callbacks when drives are
|
||||
mounted or unmounted on the system.
|
||||
|
||||
Just inherit from this class and implement the pure virtual method
|
||||
to get the callbacks, there's no need to do anything else.
|
||||
|
||||
@see File::findFileSystemRoots()
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API MountedVolumeListChangeDetector
|
||||
{
|
||||
public:
|
||||
MountedVolumeListChangeDetector();
|
||||
virtual ~MountedVolumeListChangeDetector();
|
||||
|
||||
/** This method is called when a volume is mounted or unmounted. */
|
||||
virtual void mountedVolumeListChanged() = 0;
|
||||
|
||||
private:
|
||||
JUCE_PUBLIC_IN_DLL_BUILD (struct Pimpl)
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MountedVolumeListChangeDetector)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
39
deps/juce/modules/juce_events/messages/juce_NotificationType.h
vendored
Normal file
39
deps/juce/modules/juce_events/messages/juce_NotificationType.h
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
These enums are used in various classes to indicate whether a notification
|
||||
event should be sent out.
|
||||
*/
|
||||
enum NotificationType
|
||||
{
|
||||
dontSendNotification = 0, /**< No notification message should be sent. */
|
||||
sendNotification = 1, /**< Requests a notification message, either synchronous or not. */
|
||||
sendNotificationSync, /**< Requests a synchronous notification. */
|
||||
sendNotificationAsync, /**< Requests an asynchronous notification. */
|
||||
};
|
||||
|
||||
} // namespace juce
|
54
deps/juce/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.cpp
vendored
Normal file
54
deps/juce/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.cpp
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_MAC
|
||||
|
||||
class ScopedLowPowerModeDisabler::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl() = default;
|
||||
~Pimpl() { [[NSProcessInfo processInfo] endActivity: activity]; }
|
||||
|
||||
private:
|
||||
id activity { [[NSProcessInfo processInfo] beginActivityWithOptions: NSActivityUserInitiatedAllowingIdleSystemSleep
|
||||
reason: @"App must remain in high-power mode"] };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
||||
JUCE_DECLARE_NON_MOVEABLE (Pimpl)
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class ScopedLowPowerModeDisabler::Pimpl {};
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
ScopedLowPowerModeDisabler::ScopedLowPowerModeDisabler()
|
||||
: pimpl (std::make_unique<Pimpl>()) {}
|
||||
|
||||
ScopedLowPowerModeDisabler::~ScopedLowPowerModeDisabler() = default;
|
||||
|
||||
} // namespace juce
|
49
deps/juce/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.h
vendored
Normal file
49
deps/juce/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.h
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Disables low-power-mode for the duration of an instance's lifetime.
|
||||
|
||||
Currently this is only implemented on macOS, where it will disable the
|
||||
"App Nap" power-saving feature.
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class ScopedLowPowerModeDisabler
|
||||
{
|
||||
public:
|
||||
ScopedLowPowerModeDisabler();
|
||||
~ScopedLowPowerModeDisabler();
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ScopedLowPowerModeDisabler)
|
||||
JUCE_DECLARE_NON_MOVEABLE (ScopedLowPowerModeDisabler)
|
||||
};
|
||||
|
||||
} // namespace juce
|
301
deps/juce/modules/juce_events/native/juce_android_Messaging.cpp
vendored
Normal file
301
deps/juce/modules/juce_events/native/juce_android_Messaging.cpp
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
namespace Android
|
||||
{
|
||||
class Runnable : public juce::AndroidInterfaceImplementer
|
||||
{
|
||||
public:
|
||||
virtual void run() = 0;
|
||||
|
||||
private:
|
||||
jobject invoke (jobject proxy, jobject method, jobjectArray args) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
|
||||
|
||||
if (methodName == "run")
|
||||
{
|
||||
run();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// invoke base class
|
||||
return AndroidInterfaceImplementer::invoke (proxy, method, args);
|
||||
}
|
||||
};
|
||||
|
||||
struct Handler
|
||||
{
|
||||
Handler() : nativeHandler (LocalRef<jobject> (getEnv()->NewObject (AndroidHandler, AndroidHandler.constructor))) {}
|
||||
~Handler() { clearSingletonInstance(); }
|
||||
|
||||
JUCE_DECLARE_SINGLETON (Handler, false)
|
||||
|
||||
bool post (jobject runnable)
|
||||
{
|
||||
return (getEnv()->CallBooleanMethod (nativeHandler.get(), AndroidHandler.post, runnable) != 0);
|
||||
}
|
||||
|
||||
GlobalRef nativeHandler;
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (Handler)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct AndroidMessageQueue : private Android::Runnable
|
||||
{
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED (AndroidMessageQueue, true)
|
||||
|
||||
AndroidMessageQueue()
|
||||
: self (CreateJavaInterface (this, "java/lang/Runnable"))
|
||||
{
|
||||
}
|
||||
|
||||
~AndroidMessageQueue() override
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
bool post (MessageManager::MessageBase::Ptr&& message)
|
||||
{
|
||||
queue.add (std::move (message));
|
||||
|
||||
// this will call us on the message thread
|
||||
return handler.post (self.get());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void run() override
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
MessageManager::MessageBase::Ptr message (queue.removeAndReturn (0));
|
||||
|
||||
if (message == nullptr)
|
||||
break;
|
||||
|
||||
message->messageCallback();
|
||||
}
|
||||
}
|
||||
|
||||
// the this pointer to this class in Java land
|
||||
GlobalRef self;
|
||||
|
||||
ReferenceCountedArray<MessageManager::MessageBase, CriticalSection> queue;
|
||||
Android::Handler handler;
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (AndroidMessageQueue)
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation() { AndroidMessageQueue::getInstance(); }
|
||||
void MessageManager::doPlatformSpecificShutdown() { AndroidMessageQueue::deleteInstance(); }
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
return AndroidMessageQueue::getInstance()->post (message);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::broadcastMessage (const String&)
|
||||
{
|
||||
}
|
||||
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
struct QuitCallback : public CallbackMessage
|
||||
{
|
||||
QuitCallback() {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> activity (getCurrentActivity());
|
||||
|
||||
if (activity != nullptr)
|
||||
{
|
||||
jmethodID quitMethod = env->GetMethodID (AndroidActivity, "finishAndRemoveTask", "()V");
|
||||
|
||||
if (quitMethod != nullptr)
|
||||
{
|
||||
env->CallVoidMethod (activity.get(), quitMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
quitMethod = env->GetMethodID (AndroidActivity, "finish", "()V");
|
||||
jassert (quitMethod != nullptr);
|
||||
env->CallVoidMethod (activity.get(), quitMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(new QuitCallback())->post();
|
||||
quitMessagePosted = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JuceAppLifecycle : public ActivityLifecycleCallbacks
|
||||
{
|
||||
public:
|
||||
JuceAppLifecycle (juce::JUCEApplicationBase* (*initSymbolAddr)())
|
||||
: createApplicationSymbol (initSymbolAddr)
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
|
||||
}
|
||||
}
|
||||
|
||||
~JuceAppLifecycle() override
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr && myself != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
clear();
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
|
||||
myself.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityCreated (jobject, jobject) override
|
||||
{
|
||||
checkCreated();
|
||||
}
|
||||
|
||||
void onActivityDestroyed (jobject activity) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
// if the main activity is being destroyed, only then tear-down JUCE
|
||||
if (env->IsSameObject (getMainActivity().get(), activity) != 0)
|
||||
{
|
||||
JUCEApplicationBase::appWillTerminateByForce();
|
||||
JNIClassBase::releaseAllClasses (env);
|
||||
|
||||
jclass systemClass = (jclass) env->FindClass ("java/lang/System");
|
||||
jmethodID exitMethod = env->GetStaticMethodID (systemClass, "exit", "(I)V");
|
||||
env->CallStaticVoidMethod (systemClass, exitMethod, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityStarted (jobject) override
|
||||
{
|
||||
checkCreated();
|
||||
}
|
||||
|
||||
void onActivityPaused (jobject) override
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->suspended();
|
||||
}
|
||||
|
||||
void onActivityResumed (jobject) override
|
||||
{
|
||||
checkInitialised();
|
||||
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->resumed();
|
||||
}
|
||||
|
||||
static JuceAppLifecycle& getInstance (juce::JUCEApplicationBase* (*initSymbolAddr)())
|
||||
{
|
||||
static JuceAppLifecycle juceAppLifecycle (initSymbolAddr);
|
||||
return juceAppLifecycle;
|
||||
}
|
||||
|
||||
private:
|
||||
void checkCreated()
|
||||
{
|
||||
if (JUCEApplicationBase::getInstance() == nullptr)
|
||||
{
|
||||
DBG (SystemStats::getJUCEVersion());
|
||||
|
||||
JUCEApplicationBase::createInstance = createApplicationSymbol;
|
||||
|
||||
initialiseJuce_GUI();
|
||||
|
||||
if (! JUCEApplicationBase::createInstance())
|
||||
jassertfalse; // you must supply an application object for an android app!
|
||||
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
}
|
||||
}
|
||||
|
||||
void checkInitialised()
|
||||
{
|
||||
checkCreated();
|
||||
|
||||
if (! hasBeenInitialised)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
hasBeenInitialised = app->initialiseApp();
|
||||
|
||||
if (! hasBeenInitialised)
|
||||
exit (app->shutdownApp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalRef myself;
|
||||
juce::JUCEApplicationBase* (*createApplicationSymbol)();
|
||||
bool hasBeenInitialised = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
File juce_getExecutableFile();
|
||||
|
||||
void juce_juceEventsAndroidStartApp()
|
||||
{
|
||||
auto dllPath = juce_getExecutableFile().getFullPathName();
|
||||
auto addr = reinterpret_cast<juce::JUCEApplicationBase*(*)()> (DynamicLibrary (dllPath)
|
||||
.getFunction ("juce_CreateApplication"));
|
||||
|
||||
if (addr != nullptr)
|
||||
JuceAppLifecycle::getInstance (addr);
|
||||
}
|
||||
|
||||
} // namespace juce
|
103
deps/juce/modules/juce_events/native/juce_ios_MessageManager.mm
vendored
Normal file
103
deps/juce/modules/juce_events/native/juce_ios_MessageManager.mm
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
while (quitMessagePosted.get() == 0)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
|
||||
beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
if (! SystemStats::isRunningInAppExtensionSandbox())
|
||||
[[[UIApplication sharedApplication] delegate] applicationWillTerminate: [UIApplication sharedApplication]];
|
||||
|
||||
exit (0); // iOS apps get no mercy..
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
uint32 startTime = Time::getMillisecondCounter();
|
||||
NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001];
|
||||
|
||||
while (quitMessagePosted.get() == 0)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
|
||||
beforeDate: endDate];
|
||||
|
||||
if (millisecondsToRunFor >= 0
|
||||
&& Time::getMillisecondCounter() >= startTime + (uint32) millisecondsToRunFor)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return quitMessagePosted.get() == 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
static std::unique_ptr<MessageQueue> messageQueue;
|
||||
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (messageQueue == nullptr)
|
||||
messageQueue.reset (new MessageQueue());
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
messageQueue = nullptr;
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
if (messageQueue != nullptr)
|
||||
messageQueue->post (message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String&)
|
||||
{
|
||||
// N/A on current iOS
|
||||
}
|
||||
|
||||
} // namespace juce
|
50
deps/juce/modules/juce_events/native/juce_linux_EventLoop.h
vendored
Normal file
50
deps/juce/modules/juce_events/native/juce_linux_EventLoop.h
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace LinuxEventLoop
|
||||
{
|
||||
/** Registers a callback that will be called when a file descriptor is ready for I/O.
|
||||
|
||||
This will add the given file descriptor to the internal set of file descriptors
|
||||
that will be passed to the poll() call. When this file descriptor has data to read
|
||||
the readCallback will be called.
|
||||
|
||||
@param fd the file descriptor to be monitored
|
||||
@param readCallback a callback that will be called when the file descriptor has
|
||||
data to read. The file descriptor will be passed as an argument
|
||||
@param eventMask a bit mask specifying the events you are interested in for the
|
||||
file descriptor. The possible values for this are defined in
|
||||
<poll.h>
|
||||
*/
|
||||
void registerFdCallback (int fd, std::function<void (int)> readCallback, short eventMask = 1 /*POLLIN*/);
|
||||
|
||||
/** Unregisters a previously registered file descriptor.
|
||||
|
||||
@see registerFdCallback
|
||||
*/
|
||||
void unregisterFdCallback (int fd);
|
||||
}
|
||||
|
||||
} // namespace juce
|
336
deps/juce/modules/juce_events/native/juce_linux_Messaging.cpp
vendored
Normal file
336
deps/juce/modules/juce_events/native/juce_linux_Messaging.cpp
vendored
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class InternalMessageQueue
|
||||
{
|
||||
public:
|
||||
InternalMessageQueue()
|
||||
{
|
||||
auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe);
|
||||
jassertquiet (err == 0);
|
||||
|
||||
LinuxEventLoop::registerFdCallback (getReadHandle(),
|
||||
[this] (int fd)
|
||||
{
|
||||
while (auto msg = popNextMessage (fd))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
msg->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~InternalMessageQueue()
|
||||
{
|
||||
LinuxEventLoop::unregisterFdCallback (getReadHandle());
|
||||
|
||||
close (getReadHandle());
|
||||
close (getWriteHandle());
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void postMessage (MessageManager::MessageBase* const msg) noexcept
|
||||
{
|
||||
ScopedLock sl (lock);
|
||||
queue.add (msg);
|
||||
|
||||
if (bytesInSocket < maxBytesInSocketQueue)
|
||||
{
|
||||
bytesInSocket++;
|
||||
|
||||
ScopedUnlock ul (lock);
|
||||
unsigned char x = 0xff;
|
||||
auto numBytes = write (getWriteHandle(), &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON (InternalMessageQueue, false)
|
||||
|
||||
private:
|
||||
CriticalSection lock;
|
||||
ReferenceCountedArray <MessageManager::MessageBase> queue;
|
||||
|
||||
int msgpipe[2];
|
||||
int bytesInSocket = 0;
|
||||
static constexpr int maxBytesInSocketQueue = 128;
|
||||
|
||||
int getWriteHandle() const noexcept { return msgpipe[0]; }
|
||||
int getReadHandle() const noexcept { return msgpipe[1]; }
|
||||
|
||||
MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (bytesInSocket > 0)
|
||||
{
|
||||
--bytesInSocket;
|
||||
|
||||
ScopedUnlock ul (lock);
|
||||
unsigned char x;
|
||||
auto numBytes = read (fd, &x, 1);
|
||||
ignoreUnused (numBytes);
|
||||
}
|
||||
|
||||
return queue.removeAndReturn (0);
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue)
|
||||
|
||||
//==============================================================================
|
||||
struct InternalRunLoop
|
||||
{
|
||||
public:
|
||||
InternalRunLoop()
|
||||
{
|
||||
fdReadCallbacks.reserve (16);
|
||||
}
|
||||
|
||||
void registerFdCallback (int fd, std::function<void (int)>&& cb, short eventMask)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (shouldDeferModifyingReadCallbacks)
|
||||
{
|
||||
deferredReadCallbackModifications.emplace_back ([this, fd, cb, eventMask]() mutable
|
||||
{
|
||||
registerFdCallback (fd, std::move (cb), eventMask);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
fdReadCallbacks.push_back ({ fd, std::move (cb) });
|
||||
pfds.push_back ({ fd, eventMask, 0 });
|
||||
}
|
||||
|
||||
void unregisterFdCallback (int fd)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (shouldDeferModifyingReadCallbacks)
|
||||
{
|
||||
deferredReadCallbackModifications.emplace_back ([this, fd] { unregisterFdCallback (fd); });
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto removePredicate = [=] (const std::pair<int, std::function<void (int)>>& cb) { return cb.first == fd; };
|
||||
|
||||
fdReadCallbacks.erase (std::remove_if (std::begin (fdReadCallbacks), std::end (fdReadCallbacks), removePredicate),
|
||||
std::end (fdReadCallbacks));
|
||||
}
|
||||
|
||||
{
|
||||
auto removePredicate = [=] (const pollfd& pfd) { return pfd.fd == fd; };
|
||||
|
||||
pfds.erase (std::remove_if (std::begin (pfds), std::end (pfds), removePredicate),
|
||||
std::end (pfds));
|
||||
}
|
||||
}
|
||||
|
||||
bool dispatchPendingEvents()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (poll (&pfds.front(), static_cast<nfds_t> (pfds.size()), 0) == 0)
|
||||
return false;
|
||||
|
||||
bool eventWasSent = false;
|
||||
|
||||
for (auto& pfd : pfds)
|
||||
{
|
||||
if (pfd.revents == 0)
|
||||
continue;
|
||||
|
||||
pfd.revents = 0;
|
||||
|
||||
auto fd = pfd.fd;
|
||||
|
||||
for (auto& fdAndCallback : fdReadCallbacks)
|
||||
{
|
||||
if (fdAndCallback.first == fd)
|
||||
{
|
||||
{
|
||||
ScopedValueSetter<bool> insideFdReadCallback (shouldDeferModifyingReadCallbacks, true);
|
||||
fdAndCallback.second (fd);
|
||||
}
|
||||
|
||||
if (! deferredReadCallbackModifications.empty())
|
||||
{
|
||||
for (auto& deferredRegisterEvent : deferredReadCallbackModifications)
|
||||
deferredRegisterEvent();
|
||||
|
||||
deferredReadCallbackModifications.clear();
|
||||
|
||||
// elements may have been removed from the fdReadCallbacks/pfds array so we really need
|
||||
// to call poll again
|
||||
return true;
|
||||
}
|
||||
|
||||
eventWasSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eventWasSent;
|
||||
}
|
||||
|
||||
void sleepUntilNextEvent (int timeoutMs)
|
||||
{
|
||||
poll (&pfds.front(), static_cast<nfds_t> (pfds.size()), timeoutMs);
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, std::function<void (int)>>> getFdReadCallbacks()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return fdReadCallbacks;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON (InternalRunLoop, false)
|
||||
|
||||
private:
|
||||
CriticalSection lock;
|
||||
|
||||
std::vector<std::pair<int, std::function<void (int)>>> fdReadCallbacks;
|
||||
std::vector<pollfd> pfds;
|
||||
|
||||
bool shouldDeferModifyingReadCallbacks = false;
|
||||
std::vector<std::function<void()>> deferredReadCallbackModifications;
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalRunLoop)
|
||||
|
||||
//==============================================================================
|
||||
namespace LinuxErrorHandling
|
||||
{
|
||||
static bool keyboardBreakOccurred = false;
|
||||
|
||||
void keyboardBreakSignalHandler (int sig)
|
||||
{
|
||||
if (sig == SIGINT)
|
||||
keyboardBreakOccurred = true;
|
||||
}
|
||||
|
||||
void installKeyboardBreakHandler()
|
||||
{
|
||||
struct sigaction saction;
|
||||
sigset_t maskSet;
|
||||
sigemptyset (&maskSet);
|
||||
saction.sa_handler = keyboardBreakSignalHandler;
|
||||
saction.sa_mask = maskSet;
|
||||
saction.sa_flags = 0;
|
||||
sigaction (SIGINT, &saction, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
LinuxErrorHandling::installKeyboardBreakHandler();
|
||||
|
||||
InternalRunLoop::getInstance();
|
||||
InternalMessageQueue::getInstance();
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
InternalMessageQueue::deleteInstance();
|
||||
InternalRunLoop::deleteInstance();
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
{
|
||||
queue->postMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String&)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
// this function expects that it will NEVER be called simultaneously for two concurrent threads
|
||||
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (LinuxErrorHandling::keyboardBreakOccurred)
|
||||
JUCEApplicationBase::quit();
|
||||
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
{
|
||||
if (runLoop->dispatchPendingEvents())
|
||||
break;
|
||||
|
||||
if (returnIfNoPendingMessages)
|
||||
return false;
|
||||
|
||||
runLoop->sleepUntilNextEvent (2000);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void LinuxEventLoop::registerFdCallback (int fd, std::function<void (int)> readCallback, short eventMask)
|
||||
{
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
runLoop->registerFdCallback (fd, std::move (readCallback), eventMask);
|
||||
}
|
||||
|
||||
void LinuxEventLoop::unregisterFdCallback (int fd)
|
||||
{
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
runLoop->unregisterFdCallback (fd);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
||||
JUCE_API std::vector<std::pair<int, std::function<void (int)>>> getFdReadCallbacks()
|
||||
{
|
||||
using namespace juce;
|
||||
|
||||
if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
|
||||
return runLoop->getFdReadCallbacks();
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
539
deps/juce/modules/juce_events/native/juce_mac_MessageManager.mm
vendored
Normal file
539
deps/juce/modules/juce_events/native/juce_mac_MessageManager.mm
vendored
Normal file
@ -0,0 +1,539 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
using AppFocusChangeCallback = void (*)();
|
||||
AppFocusChangeCallback appFocusChangeCallback = nullptr;
|
||||
|
||||
using CheckEventBlockedByModalComps = bool (*)(NSEvent*);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
using MenuTrackingChangedCallback = void (*)(bool);
|
||||
MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
struct AppDelegateClass : public ObjCClass<NSObject>
|
||||
{
|
||||
AppDelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_")
|
||||
{
|
||||
addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@");
|
||||
addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@");
|
||||
addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@");
|
||||
addMethod (@selector (application:openFile:), application_openFile, "c@:@@");
|
||||
addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@");
|
||||
addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@");
|
||||
addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@");
|
||||
addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@");
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@");
|
||||
addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@");
|
||||
addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@");
|
||||
addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@");
|
||||
addMethod (@selector (dummyMethod), dummyMethod, "v@:");
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
//==============================================================================
|
||||
addIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate");
|
||||
|
||||
addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@");
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@");
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@");
|
||||
addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@");
|
||||
addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@");
|
||||
#endif
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
private:
|
||||
static void applicationWillFinishLaunching (id self, SEL, NSNotification*)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[[NSAppleEventManager sharedAppleEventManager] setEventHandler: self
|
||||
andSelector: @selector (getUrl:withReplyEvent:)
|
||||
forEventClass: kInternetEventClass
|
||||
andEventID: kAEGetURL];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification)
|
||||
{
|
||||
if (notification.userInfo != nil)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
// NSUserNotification is deprecated from macOS 11, but there doesn't seem to be a
|
||||
// replacement for NSApplicationLaunchUserNotificationKey returning a non-deprecated type
|
||||
NSUserNotification* userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
if (userNotification != nil && userNotification.userInfo != nil)
|
||||
didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
app->systemRequestedQuit();
|
||||
|
||||
if (! MessageManager::getInstance()->hasStopMessageBeenSent())
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
static void applicationWillTerminate (id /*self*/, SEL, NSNotification*)
|
||||
{
|
||||
JUCEApplicationBase::appWillTerminateByForce();
|
||||
}
|
||||
|
||||
static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
{
|
||||
StringArray files;
|
||||
|
||||
for (NSString* f in filenames)
|
||||
files.add (quotedIfContainsSpaces (f));
|
||||
|
||||
if (files.size() > 0)
|
||||
app->anotherInstanceStarted (files.joinIntoString (" "));
|
||||
}
|
||||
}
|
||||
|
||||
static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
||||
static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
||||
static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); }
|
||||
|
||||
static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n)
|
||||
{
|
||||
NSDictionary* dict = (NSDictionary*) [n userInfo];
|
||||
auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]);
|
||||
MessageManager::getInstance()->deliverBroadcastMessage (messageString);
|
||||
}
|
||||
|
||||
static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*)
|
||||
{
|
||||
if (menuTrackingChangedCallback != nullptr)
|
||||
menuTrackingChangedCallback (true);
|
||||
}
|
||||
|
||||
static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*)
|
||||
{
|
||||
if (menuTrackingChangedCallback != nullptr)
|
||||
menuTrackingChangedCallback (false);
|
||||
}
|
||||
|
||||
static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread)
|
||||
|
||||
static void focusChanged()
|
||||
{
|
||||
if (appFocusChangeCallback != nullptr)
|
||||
(*appFocusChangeCallback)();
|
||||
}
|
||||
|
||||
static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue]));
|
||||
}
|
||||
|
||||
static String quotedIfContainsSpaces (NSString* file)
|
||||
{
|
||||
String s (nsStringToJuce (file));
|
||||
s = s.unquoted().replace ("\"", "\\\"");
|
||||
|
||||
if (s.containsChar (' '))
|
||||
s = s.quoted();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
//==============================================================================
|
||||
static void setPushNotificationsDelegate (id self, SEL, NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate)
|
||||
{
|
||||
object_setInstanceVariable (self, "pushNotificationsDelegate", delegate);
|
||||
}
|
||||
|
||||
static NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self)
|
||||
{
|
||||
return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (self, "pushNotificationsDelegate");
|
||||
}
|
||||
|
||||
static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken)
|
||||
{
|
||||
auto* delegate = getPushNotificationsDelegate (self);
|
||||
|
||||
SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
|
||||
|
||||
if (delegate != nil && [delegate respondsToSelector: selector])
|
||||
{
|
||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
||||
[invocation setSelector: selector];
|
||||
[invocation setTarget: delegate];
|
||||
[invocation setArgument: &application atIndex:2];
|
||||
[invocation setArgument: &deviceToken atIndex:3];
|
||||
|
||||
[invocation invoke];
|
||||
}
|
||||
}
|
||||
|
||||
static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error)
|
||||
{
|
||||
auto* delegate = getPushNotificationsDelegate (self);
|
||||
|
||||
SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
|
||||
|
||||
if (delegate != nil && [delegate respondsToSelector: selector])
|
||||
{
|
||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
||||
[invocation setSelector: selector];
|
||||
[invocation setTarget: delegate];
|
||||
[invocation setArgument: &application atIndex:2];
|
||||
[invocation setArgument: &error atIndex:3];
|
||||
|
||||
[invocation invoke];
|
||||
}
|
||||
}
|
||||
|
||||
static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo)
|
||||
{
|
||||
auto* delegate = getPushNotificationsDelegate (self);
|
||||
|
||||
SEL selector = @selector (application:didReceiveRemoteNotification:);
|
||||
|
||||
if (delegate != nil && [delegate respondsToSelector: selector])
|
||||
{
|
||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]];
|
||||
[invocation setSelector: selector];
|
||||
[invocation setTarget: delegate];
|
||||
[invocation setArgument: &application atIndex:2];
|
||||
[invocation setArgument: &userInfo atIndex:3];
|
||||
|
||||
[invocation invoke];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// This is declared at file scope, so that it's guaranteed to be
|
||||
// constructed before and destructed after `appDelegate` (below)
|
||||
static AppDelegateClass appDelegateClass;
|
||||
|
||||
//==============================================================================
|
||||
struct AppDelegate
|
||||
{
|
||||
public:
|
||||
AppDelegate()
|
||||
{
|
||||
delegate = [appDelegateClass.createInstance() init];
|
||||
|
||||
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[center addObserver: delegate selector: @selector (mainMenuTrackingBegan:)
|
||||
name: NSMenuDidBeginTrackingNotification object: nil];
|
||||
[center addObserver: delegate selector: @selector (mainMenuTrackingEnded:)
|
||||
name: NSMenuDidEndTrackingNotification object: nil];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
[NSApp setDelegate: delegate];
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[[NSDistributedNotificationCenter defaultCenter] addObserver: delegate
|
||||
selector: @selector (broadcastMessageCallback:)
|
||||
name: getBroadcastEventName()
|
||||
object: nil
|
||||
suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
else
|
||||
{
|
||||
[center addObserver: delegate selector: @selector (applicationDidResignActive:)
|
||||
name: NSApplicationDidResignActiveNotification object: NSApp];
|
||||
|
||||
[center addObserver: delegate selector: @selector (applicationDidBecomeActive:)
|
||||
name: NSApplicationDidBecomeActiveNotification object: NSApp];
|
||||
|
||||
[center addObserver: delegate selector: @selector (applicationWillUnhide:)
|
||||
name: NSApplicationWillUnhideNotification object: NSApp];
|
||||
}
|
||||
}
|
||||
|
||||
~AppDelegate()
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: delegate];
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
{
|
||||
[NSApp setDelegate: nil];
|
||||
|
||||
[[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate
|
||||
name: getBroadcastEventName()
|
||||
object: nil];
|
||||
}
|
||||
|
||||
[delegate release];
|
||||
}
|
||||
|
||||
static NSString* getBroadcastEventName()
|
||||
{
|
||||
return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64()));
|
||||
}
|
||||
|
||||
MessageQueue messageQueue;
|
||||
id delegate;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::runDispatchLoop()
|
||||
{
|
||||
if (quitMessagePosted.get() == 0) // check that the quit message wasn't already posted..
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
// must only be called by the message thread!
|
||||
jassert (isThisTheMessageThread());
|
||||
|
||||
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
|
||||
@try
|
||||
{
|
||||
[NSApp run];
|
||||
}
|
||||
@catch (NSException* e)
|
||||
{
|
||||
// An AppKit exception will kill the app, but at least this provides a chance to log it.,
|
||||
std::runtime_error ex (std::string ("NSException: ") + [[e name] UTF8String] + ", Reason:" + [[e reason] UTF8String]);
|
||||
JUCEApplicationBase::sendUnhandledException (&ex, __FILE__, __LINE__);
|
||||
}
|
||||
@finally
|
||||
{
|
||||
}
|
||||
#else
|
||||
[NSApp run];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdownNSApp()
|
||||
{
|
||||
[NSApp stop: nil];
|
||||
[NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1];
|
||||
}
|
||||
|
||||
void MessageManager::stopDispatchLoop()
|
||||
{
|
||||
if (isThisTheMessageThread())
|
||||
{
|
||||
quitMessagePosted = true;
|
||||
shutdownNSApp();
|
||||
}
|
||||
else
|
||||
{
|
||||
struct QuitCallback : public CallbackMessage
|
||||
{
|
||||
QuitCallback() {}
|
||||
void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); }
|
||||
};
|
||||
|
||||
(new QuitCallback())->post();
|
||||
}
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
|
||||
{
|
||||
jassert (millisecondsToRunFor >= 0);
|
||||
jassert (isThisTheMessageThread()); // must only be called by the message thread
|
||||
|
||||
auto endTime = Time::currentTimeMillis() + millisecondsToRunFor;
|
||||
|
||||
while (quitMessagePosted.get() == 0)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
auto msRemaining = endTime - Time::currentTimeMillis();
|
||||
|
||||
if (msRemaining <= 0)
|
||||
break;
|
||||
|
||||
CFRunLoopRunInMode (kCFRunLoopDefaultMode, jmin (1.0, msRemaining * 0.001), true);
|
||||
|
||||
if (NSEvent* e = [NSApp nextEventMatchingMask: NSEventMaskAny
|
||||
untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]
|
||||
inMode: NSDefaultRunLoopMode
|
||||
dequeue: YES])
|
||||
if (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e))
|
||||
[NSApp sendEvent: e];
|
||||
}
|
||||
}
|
||||
|
||||
return quitMessagePosted.get() == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
void initialiseNSApplication();
|
||||
void initialiseNSApplication()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[NSApplication sharedApplication];
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<AppDelegate> appDelegate;
|
||||
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
if (appDelegate == nil)
|
||||
appDelegate.reset (new AppDelegate());
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
appDelegate = nullptr;
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageBase* message)
|
||||
{
|
||||
jassert (appDelegate != nil);
|
||||
appDelegate->messageQueue.post (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String& message)
|
||||
{
|
||||
NSDictionary* info = [NSDictionary dictionaryWithObject: juceStringToNS (message)
|
||||
forKey: nsStringLiteral ("message")];
|
||||
|
||||
[[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegate::getBroadcastEventName()
|
||||
object: nil
|
||||
userInfo: info];
|
||||
}
|
||||
|
||||
// Special function used by some plugin classes to re-post carbon events
|
||||
void __attribute__ ((visibility("default"))) repostCurrentNSEvent();
|
||||
void __attribute__ ((visibility("default"))) repostCurrentNSEvent()
|
||||
{
|
||||
struct EventReposter : public CallbackMessage
|
||||
{
|
||||
EventReposter() : e ([[NSApp currentEvent] retain]) {}
|
||||
~EventReposter() override { [e release]; }
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
[NSApp postEvent: e atStart: YES];
|
||||
}
|
||||
|
||||
NSEvent* e;
|
||||
};
|
||||
|
||||
(new EventReposter())->post();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
struct MountedVolumeListChangeDetector::Pimpl
|
||||
{
|
||||
Pimpl (MountedVolumeListChangeDetector& d) : owner (d)
|
||||
{
|
||||
static ObserverClass cls;
|
||||
delegate = [cls.createInstance() init];
|
||||
ObserverClass::setOwner (delegate, this);
|
||||
|
||||
NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidMountNotification object: nil];
|
||||
[nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidUnmountNotification object: nil];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: delegate];
|
||||
[delegate release];
|
||||
}
|
||||
|
||||
private:
|
||||
MountedVolumeListChangeDetector& owner;
|
||||
id delegate;
|
||||
|
||||
struct ObserverClass : public ObjCClass<NSObject>
|
||||
{
|
||||
ObserverClass() : ObjCClass<NSObject> ("JUCEDriveObserver_")
|
||||
{
|
||||
addIvar<Pimpl*> ("owner");
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (changed:), changed, "v@:@");
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
addProtocol (@protocol (NSTextInput));
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static Pimpl* getOwner (id self) { return getIvar<Pimpl*> (self, "owner"); }
|
||||
static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); }
|
||||
|
||||
static void changed (id self, SEL, NSNotification*)
|
||||
{
|
||||
getOwner (self)->owner.mountedVolumeListChanged();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl.reset (new Pimpl (*this)); }
|
||||
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
104
deps/juce/modules/juce_events/native/juce_osx_MessageQueue.h
vendored
Normal file
104
deps/juce/modules/juce_events/native/juce_osx_MessageQueue.h
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/* An internal message pump class used in OSX and iOS. */
|
||||
class MessageQueue
|
||||
{
|
||||
public:
|
||||
MessageQueue()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
runLoop = CFRunLoopGetCurrent();
|
||||
#else
|
||||
runLoop = CFRunLoopGetMain();
|
||||
#endif
|
||||
|
||||
CFRunLoopSourceContext sourceContext;
|
||||
zerostruct (sourceContext); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
|
||||
sourceContext.info = this;
|
||||
sourceContext.perform = runLoopSourceCallback;
|
||||
runLoopSource.reset (CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext));
|
||||
CFRunLoopAddSource (runLoop, runLoopSource.get(), kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
~MessageQueue() noexcept
|
||||
{
|
||||
CFRunLoopRemoveSource (runLoop, runLoopSource.get(), kCFRunLoopCommonModes);
|
||||
CFRunLoopSourceInvalidate (runLoopSource.get());
|
||||
}
|
||||
|
||||
void post (MessageManager::MessageBase* const message)
|
||||
{
|
||||
messages.add (message);
|
||||
wakeUp();
|
||||
}
|
||||
|
||||
private:
|
||||
ReferenceCountedArray<MessageManager::MessageBase, CriticalSection> messages;
|
||||
CFRunLoopRef runLoop;
|
||||
CFUniquePtr<CFRunLoopSourceRef> runLoopSource;
|
||||
|
||||
void wakeUp() noexcept
|
||||
{
|
||||
CFRunLoopSourceSignal (runLoopSource.get());
|
||||
CFRunLoopWakeUp (runLoop);
|
||||
}
|
||||
|
||||
bool deliverNextMessage()
|
||||
{
|
||||
const MessageManager::MessageBase::Ptr nextMessage (messages.removeAndReturn (0));
|
||||
|
||||
if (nextMessage == nullptr)
|
||||
return false;
|
||||
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
nextMessage->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void runLoopCallback() noexcept
|
||||
{
|
||||
for (int i = 4; --i >= 0;)
|
||||
if (! deliverNextMessage())
|
||||
return;
|
||||
|
||||
wakeUp();
|
||||
}
|
||||
|
||||
static void runLoopSourceCallback (void* info) noexcept
|
||||
{
|
||||
static_cast<MessageQueue*> (info)->runLoopCallback();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
134
deps/juce/modules/juce_events/native/juce_win32_HiddenMessageWindow.h
vendored
Normal file
134
deps/juce/modules/juce_events/native/juce_win32_HiddenMessageWindow.h
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class HiddenMessageWindow
|
||||
{
|
||||
public:
|
||||
HiddenMessageWindow (const TCHAR* const messageWindowName, WNDPROC wndProc)
|
||||
{
|
||||
String className ("JUCE_");
|
||||
className << String::toHexString (Time::getHighResolutionTicks());
|
||||
|
||||
HMODULE moduleHandle = (HMODULE) Process::getCurrentModuleInstanceHandle();
|
||||
|
||||
WNDCLASSEX wc = {};
|
||||
wc.cbSize = sizeof (wc);
|
||||
wc.lpfnWndProc = wndProc;
|
||||
wc.cbWndExtra = 4;
|
||||
wc.hInstance = moduleHandle;
|
||||
wc.lpszClassName = className.toWideCharPointer();
|
||||
|
||||
atom = RegisterClassEx (&wc);
|
||||
jassert (atom != 0);
|
||||
|
||||
hwnd = CreateWindow (getClassNameFromAtom(), messageWindowName,
|
||||
0, 0, 0, 0, 0,
|
||||
nullptr, nullptr, moduleHandle, nullptr);
|
||||
jassert (hwnd != nullptr);
|
||||
}
|
||||
|
||||
~HiddenMessageWindow()
|
||||
{
|
||||
DestroyWindow (hwnd);
|
||||
UnregisterClass (getClassNameFromAtom(), nullptr);
|
||||
}
|
||||
|
||||
inline HWND getHWND() const noexcept { return hwnd; }
|
||||
|
||||
private:
|
||||
ATOM atom;
|
||||
HWND hwnd;
|
||||
|
||||
LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) (pointer_sized_uint) atom; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class JuceWindowIdentifier
|
||||
{
|
||||
public:
|
||||
static bool isJUCEWindow (HWND hwnd) noexcept
|
||||
{
|
||||
return GetWindowLongPtr (hwnd, GWLP_USERDATA) == getImprobableWindowNumber();
|
||||
}
|
||||
|
||||
static void setAsJUCEWindow (HWND hwnd, bool isJuceWindow) noexcept
|
||||
{
|
||||
SetWindowLongPtr (hwnd, GWLP_USERDATA, isJuceWindow ? getImprobableWindowNumber() : 0);
|
||||
}
|
||||
|
||||
private:
|
||||
static LONG_PTR getImprobableWindowNumber() noexcept
|
||||
{
|
||||
static auto number = (LONG_PTR) Random().nextInt64();
|
||||
return number;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class DeviceChangeDetector : private Timer
|
||||
{
|
||||
public:
|
||||
DeviceChangeDetector (const wchar_t* const name)
|
||||
: messageWindow (name, (WNDPROC) deviceChangeEventCallback)
|
||||
{
|
||||
SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this);
|
||||
}
|
||||
|
||||
virtual void systemDeviceChanged() = 0;
|
||||
|
||||
void triggerAsyncDeviceChangeCallback()
|
||||
{
|
||||
// We'll pause before sending a message, because on device removal, the OS hasn't always updated
|
||||
// its device lists correctly at this point. This also helps avoid repeated callbacks.
|
||||
startTimer (500);
|
||||
}
|
||||
|
||||
private:
|
||||
HiddenMessageWindow messageWindow;
|
||||
|
||||
static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message,
|
||||
const WPARAM wParam, const LPARAM lParam)
|
||||
{
|
||||
if (message == WM_DEVICECHANGE
|
||||
&& (wParam == 0x8000 /*DBT_DEVICEARRIVAL*/
|
||||
|| wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/
|
||||
|| wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/))
|
||||
{
|
||||
((DeviceChangeDetector*) GetWindowLongPtr (h, GWLP_USERDATA))
|
||||
->triggerAsyncDeviceChangeCallback();
|
||||
}
|
||||
|
||||
return DefWindowProc (h, message, wParam, lParam);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
systemDeviceChanged();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
328
deps/juce/modules/juce_events/native/juce_win32_Messaging.cpp
vendored
Normal file
328
deps/juce/modules/juce_events/native/juce_win32_Messaging.cpp
vendored
Normal file
@ -0,0 +1,328 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
extern HWND juce_messageWindowHandle;
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity
|
||||
bool juce_isRunningInUnity();
|
||||
#endif
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
LRESULT juce_offerEventToActiveXControl (::MSG&);
|
||||
#endif
|
||||
|
||||
using CheckEventBlockedByModalComps = bool (*)(const MSG&);
|
||||
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
|
||||
|
||||
using SettingChangeCallbackFunc = void (*)(void);
|
||||
SettingChangeCallbackFunc settingChangeCallback = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
class InternalMessageQueue
|
||||
{
|
||||
public:
|
||||
InternalMessageQueue()
|
||||
{
|
||||
messageWindow = std::make_unique<HiddenMessageWindow> (messageWindowName, (WNDPROC) messageWndProc);
|
||||
juce_messageWindowHandle = messageWindow->getHWND();
|
||||
}
|
||||
|
||||
~InternalMessageQueue()
|
||||
{
|
||||
juce_messageWindowHandle = nullptr;
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_SINGLETON (InternalMessageQueue, false)
|
||||
|
||||
//==============================================================================
|
||||
void broadcastMessage (const String& message)
|
||||
{
|
||||
auto localCopy = message;
|
||||
|
||||
Array<HWND> windows;
|
||||
EnumWindows (&broadcastEnumWindowProc, (LPARAM) &windows);
|
||||
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
{
|
||||
COPYDATASTRUCT data;
|
||||
data.dwData = broadcastMessageMagicNumber;
|
||||
data.cbData = (DWORD) (((size_t) localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType));
|
||||
data.lpData = (void*) localCopy.toUTF32().getAddress();
|
||||
|
||||
DWORD_PTR result;
|
||||
SendMessageTimeout (windows.getUnchecked (i), WM_COPYDATA,
|
||||
(WPARAM) juce_messageWindowHandle,
|
||||
(LPARAM) &data,
|
||||
SMTO_BLOCK | SMTO_ABORTIFHUNG, 8000, &result);
|
||||
}
|
||||
}
|
||||
|
||||
void postMessage (MessageManager::MessageBase* message)
|
||||
{
|
||||
bool shouldTriggerMessageQueueDispatch = false;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
shouldTriggerMessageQueueDispatch = messageQueue.isEmpty();
|
||||
messageQueue.add (message);
|
||||
}
|
||||
|
||||
if (! shouldTriggerMessageQueueDispatch)
|
||||
return;
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity
|
||||
if (juce_isRunningInUnity())
|
||||
{
|
||||
SendNotifyMessage (juce_messageWindowHandle, customMessageID, 0, 0);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
PostMessage (juce_messageWindowHandle, customMessageID, 0, 0);
|
||||
}
|
||||
|
||||
bool dispatchNextMessage (bool returnIfNoPendingMessages)
|
||||
{
|
||||
MSG m;
|
||||
|
||||
if (returnIfNoPendingMessages && ! PeekMessage (&m, nullptr, 0, 0, PM_NOREMOVE))
|
||||
return false;
|
||||
|
||||
if (GetMessage (&m, nullptr, 0, 0) >= 0)
|
||||
{
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
if (juce_offerEventToActiveXControl (m) != S_FALSE)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
if (m.message == customMessageID && m.hwnd == juce_messageWindowHandle)
|
||||
{
|
||||
dispatchMessages();
|
||||
}
|
||||
else if (m.message == WM_QUIT)
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
app->systemRequestedQuit();
|
||||
}
|
||||
else if (isEventBlockedByModalComps == nullptr || ! isEventBlockedByModalComps (m))
|
||||
{
|
||||
if ((m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN)
|
||||
&& ! JuceWindowIdentifier::isJUCEWindow (m.hwnd))
|
||||
{
|
||||
// if it's someone else's window being clicked on, and the focus is
|
||||
// currently on a juce window, pass the kb focus over..
|
||||
auto currentFocus = GetFocus();
|
||||
|
||||
if (currentFocus == nullptr || JuceWindowIdentifier::isJUCEWindow (currentFocus))
|
||||
SetFocus (m.hwnd);
|
||||
}
|
||||
|
||||
TranslateMessage (&m);
|
||||
DispatchMessage (&m);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static LRESULT CALLBACK messageWndProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) noexcept
|
||||
{
|
||||
if (h == juce_messageWindowHandle)
|
||||
{
|
||||
if (message == customMessageID)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->dispatchMessages();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (message == WM_COPYDATA)
|
||||
{
|
||||
handleBroadcastMessage (reinterpret_cast<const COPYDATASTRUCT*> (lParam));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (message == WM_SETTINGCHANGE)
|
||||
if (settingChangeCallback != nullptr)
|
||||
settingChangeCallback();
|
||||
}
|
||||
|
||||
return DefWindowProc (h, message, wParam, lParam);
|
||||
}
|
||||
|
||||
static BOOL CALLBACK broadcastEnumWindowProc (HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
if (hwnd != juce_messageWindowHandle)
|
||||
{
|
||||
TCHAR windowName[64] = { 0 }; // no need to read longer strings than this
|
||||
GetWindowText (hwnd, windowName, 63);
|
||||
|
||||
if (String (windowName) == messageWindowName)
|
||||
reinterpret_cast<Array<HWND>*> (lParam)->add (hwnd);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void dispatchMessage (MessageManager::MessageBase* message)
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
message->messageCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
message->decReferenceCount();
|
||||
}
|
||||
|
||||
static void handleBroadcastMessage (const COPYDATASTRUCT* data)
|
||||
{
|
||||
if (data != nullptr && data->dwData == broadcastMessageMagicNumber)
|
||||
{
|
||||
struct BroadcastMessage : public CallbackMessage
|
||||
{
|
||||
BroadcastMessage (CharPointer_UTF32 text, size_t length) : message (text, length) {}
|
||||
void messageCallback() override { MessageManager::getInstance()->deliverBroadcastMessage (message); }
|
||||
|
||||
String message;
|
||||
};
|
||||
|
||||
(new BroadcastMessage (CharPointer_UTF32 ((const CharPointer_UTF32::CharType*) data->lpData),
|
||||
data->cbData / sizeof (CharPointer_UTF32::CharType)))
|
||||
->post();
|
||||
}
|
||||
}
|
||||
|
||||
void dispatchMessages()
|
||||
{
|
||||
ReferenceCountedArray<MessageManager::MessageBase> messagesToDispatch;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (messageQueue.isEmpty())
|
||||
return;
|
||||
|
||||
messagesToDispatch.swapWith (messageQueue);
|
||||
}
|
||||
|
||||
for (int i = 0; i < messagesToDispatch.size(); ++i)
|
||||
{
|
||||
auto message = messagesToDispatch.getUnchecked (i);
|
||||
message->incReferenceCount();
|
||||
dispatchMessage (message.get());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static constexpr unsigned int customMessageID = WM_USER + 123;
|
||||
static constexpr unsigned int broadcastMessageMagicNumber = 0xc403;
|
||||
static const TCHAR messageWindowName[];
|
||||
|
||||
std::unique_ptr<HiddenMessageWindow> messageWindow;
|
||||
|
||||
CriticalSection lock;
|
||||
ReferenceCountedArray<MessageManager::MessageBase> messageQueue;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalMessageQueue)
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue)
|
||||
|
||||
const TCHAR InternalMessageQueue::messageWindowName[] = _T("JUCEWindow");
|
||||
|
||||
//==============================================================================
|
||||
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
return queue->dispatchNextMessage (returnIfNoPendingMessages);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
{
|
||||
queue->postMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageManager::broadcastMessage (const String& value)
|
||||
{
|
||||
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
|
||||
queue->broadcastMessage (value);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MessageManager::doPlatformSpecificInitialisation()
|
||||
{
|
||||
ignoreUnused (OleInitialize (nullptr));
|
||||
InternalMessageQueue::getInstance();
|
||||
}
|
||||
|
||||
void MessageManager::doPlatformSpecificShutdown()
|
||||
{
|
||||
InternalMessageQueue::deleteInstance();
|
||||
OleUninitialize();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct MountedVolumeListChangeDetector::Pimpl : private DeviceChangeDetector
|
||||
{
|
||||
Pimpl (MountedVolumeListChangeDetector& d) : DeviceChangeDetector (L"MountedVolumeList"), owner (d)
|
||||
{
|
||||
File::findFileSystemRoots (lastVolumeList);
|
||||
}
|
||||
|
||||
void systemDeviceChanged() override
|
||||
{
|
||||
Array<File> newList;
|
||||
File::findFileSystemRoots (newList);
|
||||
|
||||
if (lastVolumeList != newList)
|
||||
{
|
||||
lastVolumeList = newList;
|
||||
owner.mountedVolumeListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
MountedVolumeListChangeDetector& owner;
|
||||
Array<File> lastVolumeList;
|
||||
};
|
||||
|
||||
MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl.reset (new Pimpl (*this)); }
|
||||
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
|
||||
|
||||
} // namespace juce
|
82
deps/juce/modules/juce_events/native/juce_win32_WinRTWrapper.cpp
vendored
Normal file
82
deps/juce/modules/juce_events/native/juce_win32_WinRTWrapper.cpp
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
WinRTWrapper::WinRTWrapper()
|
||||
{
|
||||
winRTHandle = ::LoadLibraryA ("api-ms-win-core-winrt-l1-1-0");
|
||||
|
||||
if (winRTHandle == nullptr)
|
||||
return;
|
||||
|
||||
roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (winRTHandle, "RoInitialize");
|
||||
createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsCreateString");
|
||||
deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsDeleteString");
|
||||
getHStringRawBuffer = (WindowsGetStringRawBufferFuncPtr) ::GetProcAddress (winRTHandle, "WindowsGetStringRawBuffer");
|
||||
roActivateInstance = (RoActivateInstanceFuncPtr) ::GetProcAddress (winRTHandle, "RoActivateInstance");
|
||||
roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (winRTHandle, "RoGetActivationFactory");
|
||||
|
||||
if (roInitialize == nullptr || createHString == nullptr || deleteHString == nullptr
|
||||
|| getHStringRawBuffer == nullptr || roActivateInstance == nullptr || roGetActivationFactory == nullptr)
|
||||
return;
|
||||
|
||||
HRESULT status = roInitialize (1);
|
||||
initialised = ! (status != S_OK && status != S_FALSE && status != 0x80010106L);
|
||||
}
|
||||
|
||||
WinRTWrapper::~WinRTWrapper()
|
||||
{
|
||||
if (winRTHandle != nullptr)
|
||||
::FreeLibrary (winRTHandle);
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
WinRTWrapper::ScopedHString::ScopedHString (String str)
|
||||
{
|
||||
if (WinRTWrapper::getInstance()->isInitialised())
|
||||
WinRTWrapper::getInstance()->createHString (str.toWideCharPointer(),
|
||||
static_cast<uint32_t> (str.length()),
|
||||
&hstr);
|
||||
}
|
||||
|
||||
WinRTWrapper::ScopedHString::~ScopedHString()
|
||||
{
|
||||
if (WinRTWrapper::getInstance()->isInitialised() && hstr != nullptr)
|
||||
WinRTWrapper::getInstance()->deleteHString (hstr);
|
||||
}
|
||||
|
||||
String WinRTWrapper::hStringToString (HSTRING hstr)
|
||||
{
|
||||
if (isInitialised())
|
||||
if (const wchar_t* str = getHStringRawBuffer (hstr, nullptr))
|
||||
return String (str);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (WinRTWrapper)
|
||||
|
||||
}
|
112
deps/juce/modules/juce_events/native/juce_win32_WinRTWrapper.h
vendored
Normal file
112
deps/juce/modules/juce_events/native/juce_win32_WinRTWrapper.h
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class WinRTWrapper : public DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
~WinRTWrapper();
|
||||
bool isInitialised() const noexcept { return initialised; }
|
||||
|
||||
JUCE_DECLARE_SINGLETON (WinRTWrapper, true)
|
||||
|
||||
//==============================================================================
|
||||
template <class ComClass>
|
||||
ComSmartPtr<ComClass> activateInstance (const wchar_t* runtimeClassID, REFCLSID classUUID)
|
||||
{
|
||||
ComSmartPtr<ComClass> result;
|
||||
|
||||
if (isInitialised())
|
||||
{
|
||||
ComSmartPtr<IInspectable> inspectable;
|
||||
ScopedHString runtimeClass (runtimeClassID);
|
||||
auto hr = roActivateInstance (runtimeClass.get(), inspectable.resetAndGetPointerAddress());
|
||||
|
||||
if (SUCCEEDED (hr))
|
||||
inspectable->QueryInterface (classUUID, (void**) result.resetAndGetPointerAddress());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class ComClass>
|
||||
ComSmartPtr<ComClass> getWRLFactory (const wchar_t* runtimeClassID)
|
||||
{
|
||||
ComSmartPtr<ComClass> comPtr;
|
||||
|
||||
if (isInitialised())
|
||||
{
|
||||
ScopedHString classID (runtimeClassID);
|
||||
|
||||
if (classID.get() != nullptr)
|
||||
roGetActivationFactory (classID.get(), __uuidof (ComClass), (void**) comPtr.resetAndGetPointerAddress());
|
||||
}
|
||||
|
||||
return comPtr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ScopedHString
|
||||
{
|
||||
public:
|
||||
ScopedHString (String);
|
||||
~ScopedHString();
|
||||
|
||||
HSTRING get() const noexcept { return hstr; }
|
||||
|
||||
private:
|
||||
HSTRING hstr = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedHString)
|
||||
};
|
||||
|
||||
String hStringToString (HSTRING);
|
||||
|
||||
private:
|
||||
WinRTWrapper();
|
||||
|
||||
//==============================================================================
|
||||
HMODULE winRTHandle = nullptr;
|
||||
bool initialised = false;
|
||||
|
||||
typedef HRESULT (WINAPI* RoInitializeFuncPtr) (int);
|
||||
typedef HRESULT (WINAPI* WindowsCreateStringFuncPtr) (LPCWSTR, UINT32, HSTRING*);
|
||||
typedef HRESULT (WINAPI* WindowsDeleteStringFuncPtr) (HSTRING);
|
||||
typedef PCWSTR (WINAPI* WindowsGetStringRawBufferFuncPtr) (HSTRING, UINT32*);
|
||||
typedef HRESULT (WINAPI* RoActivateInstanceFuncPtr) (HSTRING, IInspectable**);
|
||||
typedef HRESULT (WINAPI* RoGetActivationFactoryFuncPtr) (HSTRING, REFIID, void**);
|
||||
|
||||
RoInitializeFuncPtr roInitialize = nullptr;
|
||||
WindowsCreateStringFuncPtr createHString = nullptr;
|
||||
WindowsDeleteStringFuncPtr deleteHString = nullptr;
|
||||
WindowsGetStringRawBufferFuncPtr getHStringRawBuffer = nullptr;
|
||||
RoActivateInstanceFuncPtr roActivateInstance = nullptr;
|
||||
RoGetActivationFactoryFuncPtr roGetActivationFactory = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTWrapper)
|
||||
};
|
||||
|
||||
} // namespace juce
|
108
deps/juce/modules/juce_events/timers/juce_MultiTimer.cpp
vendored
Normal file
108
deps/juce/modules/juce_events/timers/juce_MultiTimer.cpp
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct MultiTimerCallback : public Timer
|
||||
{
|
||||
MultiTimerCallback (const int tid, MultiTimer& mt) noexcept
|
||||
: owner (mt), timerID (tid)
|
||||
{
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
owner.timerCallback (timerID);
|
||||
}
|
||||
|
||||
MultiTimer& owner;
|
||||
const int timerID;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiTimerCallback)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
MultiTimer::MultiTimer() noexcept {}
|
||||
MultiTimer::MultiTimer (const MultiTimer&) noexcept {}
|
||||
|
||||
MultiTimer::~MultiTimer()
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (timerListLock);
|
||||
timers.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Timer* MultiTimer::getCallback (int timerID) const noexcept
|
||||
{
|
||||
for (int i = timers.size(); --i >= 0;)
|
||||
{
|
||||
MultiTimerCallback* const t = static_cast<MultiTimerCallback*> (timers.getUnchecked(i));
|
||||
|
||||
if (t->timerID == timerID)
|
||||
return t;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MultiTimer::startTimer (const int timerID, const int intervalInMilliseconds) noexcept
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (timerListLock);
|
||||
|
||||
Timer* timer = getCallback (timerID);
|
||||
|
||||
if (timer == nullptr)
|
||||
timers.add (timer = new MultiTimerCallback (timerID, *this));
|
||||
|
||||
timer->startTimer (intervalInMilliseconds);
|
||||
}
|
||||
|
||||
void MultiTimer::stopTimer (const int timerID) noexcept
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (timerListLock);
|
||||
|
||||
if (Timer* const t = getCallback (timerID))
|
||||
t->stopTimer();
|
||||
}
|
||||
|
||||
bool MultiTimer::isTimerRunning (const int timerID) const noexcept
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (timerListLock);
|
||||
|
||||
if (Timer* const t = getCallback (timerID))
|
||||
return t->isTimerRunning();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int MultiTimer::getTimerInterval (const int timerID) const noexcept
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (timerListLock);
|
||||
|
||||
if (Timer* const t = getCallback (timerID))
|
||||
return t->getTimerInterval();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace juce
|
125
deps/juce/modules/juce_events/timers/juce_MultiTimer.h
vendored
Normal file
125
deps/juce/modules/juce_events/timers/juce_MultiTimer.h
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A type of timer class that can run multiple timers with different frequencies,
|
||||
all of which share a single callback.
|
||||
|
||||
This class is very similar to the Timer class, but allows you run multiple
|
||||
separate timers, where each one has a unique ID number. The methods in this
|
||||
class are exactly equivalent to those in Timer, but with the addition of
|
||||
this ID number.
|
||||
|
||||
To use it, you need to create a subclass of MultiTimer, implementing the
|
||||
timerCallback() method. Then you can start timers with startTimer(), and
|
||||
each time the callback is triggered, it passes in the ID of the timer that
|
||||
caused it.
|
||||
|
||||
@see Timer
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API MultiTimer
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates a MultiTimer.
|
||||
|
||||
When created, no timers are running, so use startTimer() to start things off.
|
||||
*/
|
||||
MultiTimer() noexcept;
|
||||
|
||||
/** Creates a copy of another timer.
|
||||
|
||||
Note that this timer will not contain any running timers, even if the one you're
|
||||
copying from was running.
|
||||
*/
|
||||
MultiTimer (const MultiTimer&) noexcept;
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~MultiTimer();
|
||||
|
||||
//==============================================================================
|
||||
/** The user-defined callback routine that actually gets called by each of the
|
||||
timers that are running.
|
||||
|
||||
It's perfectly ok to call startTimer() or stopTimer() from within this
|
||||
callback to change the subsequent intervals.
|
||||
*/
|
||||
virtual void timerCallback (int timerID) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts a timer and sets the length of interval required.
|
||||
|
||||
If the timer is already started, this will reset it, so the
|
||||
time between calling this method and the next timer callback
|
||||
will not be less than the interval length passed in.
|
||||
|
||||
@param timerID a unique Id number that identifies the timer to
|
||||
start. This is the id that will be passed back
|
||||
to the timerCallback() method when this timer is
|
||||
triggered
|
||||
@param intervalInMilliseconds the interval to use (any values less than 1 will be
|
||||
rounded up to 1)
|
||||
*/
|
||||
void startTimer (int timerID, int intervalInMilliseconds) noexcept;
|
||||
|
||||
/** Stops a timer.
|
||||
|
||||
If a timer has been started with the given ID number, it will be cancelled.
|
||||
No more callbacks will be made for the specified timer after this method returns.
|
||||
|
||||
If this is called from a different thread, any callbacks that may
|
||||
be currently executing may be allowed to finish before the method
|
||||
returns.
|
||||
*/
|
||||
void stopTimer (int timerID) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Checks whether a timer has been started for a specified ID.
|
||||
@returns true if a timer with the given ID is running.
|
||||
*/
|
||||
bool isTimerRunning (int timerID) const noexcept;
|
||||
|
||||
/** Returns the interval for a specified timer ID.
|
||||
@returns the timer's interval in milliseconds if it's running, or 0 if no
|
||||
timer was running for the ID number specified.
|
||||
*/
|
||||
int getTimerInterval (int timerID) const noexcept;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
SpinLock timerListLock;
|
||||
OwnedArray<Timer> timers;
|
||||
|
||||
Timer* getCallback (int) const noexcept;
|
||||
MultiTimer& operator= (const MultiTimer&);
|
||||
};
|
||||
|
||||
} // namespace juce
|
397
deps/juce/modules/juce_events/timers/juce_Timer.cpp
vendored
Normal file
397
deps/juce/modules/juce_events/timers/juce_Timer.cpp
vendored
Normal file
@ -0,0 +1,397 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class Timer::TimerThread : private Thread,
|
||||
private DeletedAtShutdown,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
using LockType = CriticalSection; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
|
||||
|
||||
TimerThread() : Thread ("JUCE Timer")
|
||||
{
|
||||
timers.reserve (32);
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
~TimerThread() override
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
callbackArrived.signal();
|
||||
stopThread (4000);
|
||||
jassert (instance == this || instance == nullptr);
|
||||
|
||||
if (instance == this)
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
auto lastTime = Time::getMillisecondCounter();
|
||||
ReferenceCountedObjectPtr<CallTimersMessage> messageToSend (new CallTimersMessage());
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
auto now = Time::getMillisecondCounter();
|
||||
auto elapsed = (int) (now >= lastTime ? (now - lastTime)
|
||||
: (std::numeric_limits<uint32>::max() - (lastTime - now)));
|
||||
lastTime = now;
|
||||
|
||||
auto timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed);
|
||||
|
||||
if (timeUntilFirstTimer <= 0)
|
||||
{
|
||||
if (callbackArrived.wait (0))
|
||||
{
|
||||
// already a message in flight - do nothing..
|
||||
}
|
||||
else
|
||||
{
|
||||
messageToSend->post();
|
||||
|
||||
if (! callbackArrived.wait (300))
|
||||
{
|
||||
// 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
|
||||
// message has been lost and trying again.
|
||||
messageToSend->post();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// don't wait for too long because running this loop also helps keep the
|
||||
// Time::getApproximateMillisecondTimer value stay up-to-date
|
||||
wait (jlimit (1, 100, timeUntilFirstTimer));
|
||||
}
|
||||
}
|
||||
|
||||
void callTimers()
|
||||
{
|
||||
auto timeout = Time::getMillisecondCounter() + 100;
|
||||
|
||||
const LockType::ScopedLockType sl (lock);
|
||||
|
||||
while (! timers.empty())
|
||||
{
|
||||
auto& first = timers.front();
|
||||
|
||||
if (first.countdownMs > 0)
|
||||
break;
|
||||
|
||||
auto* timer = first.timer;
|
||||
first.countdownMs = timer->timerPeriodMs;
|
||||
shuffleTimerBackInQueue (0);
|
||||
notify();
|
||||
|
||||
const LockType::ScopedUnlockType ul (lock);
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
timer->timerCallback();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
// avoid getting stuck in a loop if a timer callback repeatedly takes too long
|
||||
if (Time::getMillisecondCounter() > timeout)
|
||||
break;
|
||||
}
|
||||
|
||||
callbackArrived.signal();
|
||||
}
|
||||
|
||||
void callTimersSynchronously()
|
||||
{
|
||||
if (! isThreadRunning())
|
||||
{
|
||||
// (This is relied on by some plugins in cases where the MM has
|
||||
// had to restart and the async callback never started)
|
||||
cancelPendingUpdate();
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
callTimers();
|
||||
}
|
||||
|
||||
static void add (Timer* tim) noexcept
|
||||
{
|
||||
if (instance == nullptr)
|
||||
instance = new TimerThread();
|
||||
|
||||
instance->addTimer (tim);
|
||||
}
|
||||
|
||||
static void remove (Timer* tim) noexcept
|
||||
{
|
||||
if (instance != nullptr)
|
||||
instance->removeTimer (tim);
|
||||
}
|
||||
|
||||
static void resetCounter (Timer* tim) noexcept
|
||||
{
|
||||
if (instance != nullptr)
|
||||
instance->resetTimerCounter (tim);
|
||||
}
|
||||
|
||||
static TimerThread* instance;
|
||||
static LockType lock;
|
||||
|
||||
private:
|
||||
struct TimerCountdown
|
||||
{
|
||||
Timer* timer;
|
||||
int countdownMs;
|
||||
};
|
||||
|
||||
std::vector<TimerCountdown> timers;
|
||||
|
||||
WaitableEvent callbackArrived;
|
||||
|
||||
struct CallTimersMessage : public MessageManager::MessageBase
|
||||
{
|
||||
CallTimersMessage() {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (instance != nullptr)
|
||||
instance->callTimers();
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void addTimer (Timer* t)
|
||||
{
|
||||
// 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 (std::find_if (timers.begin(), timers.end(),
|
||||
[t] (TimerCountdown i) { return i.timer == t; }) == timers.end());
|
||||
|
||||
auto pos = timers.size();
|
||||
|
||||
timers.push_back ({ t, t->timerPeriodMs });
|
||||
t->positionInQueue = pos;
|
||||
shuffleTimerForwardInQueue (pos);
|
||||
notify();
|
||||
}
|
||||
|
||||
void removeTimer (Timer* t)
|
||||
{
|
||||
auto pos = t->positionInQueue;
|
||||
auto lastIndex = timers.size() - 1;
|
||||
|
||||
jassert (pos <= lastIndex);
|
||||
jassert (timers[pos].timer == t);
|
||||
|
||||
for (auto i = pos; i < lastIndex; ++i)
|
||||
{
|
||||
timers[i] = timers[i + 1];
|
||||
timers[i].timer->positionInQueue = i;
|
||||
}
|
||||
|
||||
timers.pop_back();
|
||||
}
|
||||
|
||||
void resetTimerCounter (Timer* t) noexcept
|
||||
{
|
||||
auto pos = t->positionInQueue;
|
||||
|
||||
jassert (pos < timers.size());
|
||||
jassert (timers[pos].timer == t);
|
||||
|
||||
auto lastCountdown = timers[pos].countdownMs;
|
||||
auto newCountdown = t->timerPeriodMs;
|
||||
|
||||
if (newCountdown != lastCountdown)
|
||||
{
|
||||
timers[pos].countdownMs = newCountdown;
|
||||
|
||||
if (newCountdown > lastCountdown)
|
||||
shuffleTimerBackInQueue (pos);
|
||||
else
|
||||
shuffleTimerForwardInQueue (pos);
|
||||
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
void shuffleTimerBackInQueue (size_t pos)
|
||||
{
|
||||
auto numTimers = timers.size();
|
||||
|
||||
if (pos < numTimers - 1)
|
||||
{
|
||||
auto t = timers[pos];
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto next = pos + 1;
|
||||
|
||||
if (next == numTimers || timers[next].countdownMs >= t.countdownMs)
|
||||
break;
|
||||
|
||||
timers[pos] = timers[next];
|
||||
timers[pos].timer->positionInQueue = pos;
|
||||
|
||||
++pos;
|
||||
}
|
||||
|
||||
timers[pos] = t;
|
||||
t.timer->positionInQueue = pos;
|
||||
}
|
||||
}
|
||||
|
||||
void shuffleTimerForwardInQueue (size_t pos)
|
||||
{
|
||||
if (pos > 0)
|
||||
{
|
||||
auto t = timers[pos];
|
||||
|
||||
while (pos > 0)
|
||||
{
|
||||
auto& prev = timers[(size_t) pos - 1];
|
||||
|
||||
if (prev.countdownMs <= t.countdownMs)
|
||||
break;
|
||||
|
||||
timers[pos] = prev;
|
||||
timers[pos].timer->positionInQueue = pos;
|
||||
|
||||
--pos;
|
||||
}
|
||||
|
||||
timers[pos] = t;
|
||||
t.timer->positionInQueue = pos;
|
||||
}
|
||||
}
|
||||
|
||||
int getTimeUntilFirstTimer (int numMillisecsElapsed)
|
||||
{
|
||||
const LockType::ScopedLockType sl (lock);
|
||||
|
||||
if (timers.empty())
|
||||
return 1000;
|
||||
|
||||
for (auto& t : timers)
|
||||
t.countdownMs -= numMillisecsElapsed;
|
||||
|
||||
return timers.front().countdownMs;
|
||||
}
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
startThread (7);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
|
||||
};
|
||||
|
||||
Timer::TimerThread* Timer::TimerThread::instance = nullptr;
|
||||
Timer::TimerThread::LockType Timer::TimerThread::lock;
|
||||
|
||||
//==============================================================================
|
||||
Timer::Timer() noexcept {}
|
||||
Timer::Timer (const Timer&) noexcept {}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
// If you're destroying a timer on a background thread, make sure the timer has
|
||||
// been stopped before execution reaches this point. A simple way to achieve this
|
||||
// is to add a call to `stopTimer()` to the destructor of your class which inherits
|
||||
// from Timer.
|
||||
jassert (! isTimerRunning()
|
||||
|| MessageManager::getInstanceWithoutCreating() == nullptr
|
||||
|| MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
|
||||
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
void Timer::startTimer (int interval) noexcept
|
||||
{
|
||||
// If you're calling this before (or after) the MessageManager is
|
||||
// running, then you're not going to get any timer callbacks!
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
|
||||
|
||||
const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
|
||||
|
||||
bool wasStopped = (timerPeriodMs == 0);
|
||||
timerPeriodMs = jmax (1, interval);
|
||||
|
||||
if (wasStopped)
|
||||
TimerThread::add (this);
|
||||
else
|
||||
TimerThread::resetCounter (this);
|
||||
}
|
||||
|
||||
void Timer::startTimerHz (int timerFrequencyHz) noexcept
|
||||
{
|
||||
if (timerFrequencyHz > 0)
|
||||
startTimer (1000 / timerFrequencyHz);
|
||||
else
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
void Timer::stopTimer() noexcept
|
||||
{
|
||||
const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
|
||||
|
||||
if (timerPeriodMs > 0)
|
||||
{
|
||||
TimerThread::remove (this);
|
||||
timerPeriodMs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE Timer::callPendingTimersSynchronously()
|
||||
{
|
||||
if (TimerThread::instance != nullptr)
|
||||
TimerThread::instance->callTimersSynchronously();
|
||||
}
|
||||
|
||||
struct LambdaInvoker : private Timer
|
||||
{
|
||||
LambdaInvoker (int milliseconds, std::function<void()> f) : function (f)
|
||||
{
|
||||
startTimer (milliseconds);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
auto f = function;
|
||||
delete this;
|
||||
f();
|
||||
}
|
||||
|
||||
std::function<void()> function;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LambdaInvoker)
|
||||
};
|
||||
|
||||
void JUCE_CALLTYPE Timer::callAfterDelay (int milliseconds, std::function<void()> f)
|
||||
{
|
||||
new LambdaInvoker (milliseconds, f);
|
||||
}
|
||||
|
||||
} // namespace juce
|
137
deps/juce/modules/juce_events/timers/juce_Timer.h
vendored
Normal file
137
deps/juce/modules/juce_events/timers/juce_Timer.h
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Makes repeated callbacks to a virtual method at a specified time interval.
|
||||
|
||||
A Timer's timerCallback() method will be repeatedly called at a given
|
||||
interval. When you create a Timer object, it will do nothing until the
|
||||
startTimer() method is called, which will cause the message thread to
|
||||
start making callbacks at the specified interval, until stopTimer() is called
|
||||
or the object is deleted.
|
||||
|
||||
The time interval isn't guaranteed to be precise to any more than maybe
|
||||
10-20ms, and the intervals may end up being much longer than requested if the
|
||||
system is busy. Because the callbacks are made by the main message thread,
|
||||
anything that blocks the message queue for a period of time will also prevent
|
||||
any timers from running until it can carry on.
|
||||
|
||||
If you need to have a single callback that is shared by multiple timers with
|
||||
different frequencies, then the MultiTimer class allows you to do that - its
|
||||
structure is very similar to the Timer class, but contains multiple timers
|
||||
internally, each one identified by an ID number.
|
||||
|
||||
@see HighResolutionTimer, MultiTimer
|
||||
|
||||
@tags{Events}
|
||||
*/
|
||||
class JUCE_API Timer
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates a Timer.
|
||||
When created, the timer is stopped, so use startTimer() to get it going.
|
||||
*/
|
||||
Timer() noexcept;
|
||||
|
||||
/** Creates a copy of another timer.
|
||||
|
||||
Note that this timer won't be started, even if the one you're copying
|
||||
is running.
|
||||
*/
|
||||
Timer (const Timer&) noexcept;
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~Timer();
|
||||
|
||||
//==============================================================================
|
||||
/** The user-defined callback routine that actually gets called periodically.
|
||||
|
||||
It's perfectly ok to call startTimer() or stopTimer() from within this
|
||||
callback to change the subsequent intervals.
|
||||
*/
|
||||
virtual void timerCallback() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts the timer and sets the length of interval required.
|
||||
|
||||
If the timer is already started, this will reset it, so the
|
||||
time between calling this method and the next timer callback
|
||||
will not be less than the interval length passed in.
|
||||
|
||||
@param intervalInMilliseconds the interval to use (any value less
|
||||
than 1 will be rounded up to 1)
|
||||
*/
|
||||
void startTimer (int intervalInMilliseconds) noexcept;
|
||||
|
||||
/** Starts the timer with an interval specified in Hertz.
|
||||
This is effectively the same as calling startTimer (1000 / timerFrequencyHz).
|
||||
*/
|
||||
void startTimerHz (int timerFrequencyHz) noexcept;
|
||||
|
||||
/** Stops the timer.
|
||||
|
||||
No more timer callbacks will be triggered after this method returns.
|
||||
|
||||
Note that if you call this from a background thread while the message-thread
|
||||
is already in the middle of your callback, then this method will cancel any
|
||||
future timer callbacks, but it will return without waiting for the current one
|
||||
to finish. The current callback will continue, possibly still running some of
|
||||
your timer code after this method has returned.
|
||||
*/
|
||||
void stopTimer() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the timer is currently running. */
|
||||
bool isTimerRunning() const noexcept { return timerPeriodMs > 0; }
|
||||
|
||||
/** Returns the timer's interval.
|
||||
@returns the timer's interval in milliseconds if it's running, or 0 if it's not.
|
||||
*/
|
||||
int getTimerInterval() const noexcept { return timerPeriodMs; }
|
||||
|
||||
//==============================================================================
|
||||
/** Invokes a lambda after a given number of milliseconds. */
|
||||
static void JUCE_CALLTYPE callAfterDelay (int milliseconds, std::function<void()> functionToCall);
|
||||
|
||||
//==============================================================================
|
||||
/** For internal use only: invokes any timers that need callbacks.
|
||||
Don't call this unless you really know what you're doing!
|
||||
*/
|
||||
static void JUCE_CALLTYPE callPendingTimersSynchronously();
|
||||
|
||||
private:
|
||||
class TimerThread;
|
||||
friend class TimerThread;
|
||||
size_t positionInQueue = (size_t) -1;
|
||||
int timerPeriodMs = 0;
|
||||
|
||||
Timer& operator= (const Timer&) = delete;
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user