mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
VST3: Use shared MessageThread on Linux to run the message loop when host run loop interface isn't available
This commit is contained in:
parent
33f37847bc
commit
efd52398d1
3 changed files with 217 additions and 55 deletions
|
|
@ -47,6 +47,7 @@
|
|||
#include "../utility/juce_IncludeModuleHeaders.h"
|
||||
#include "../utility/juce_WindowsHooks.h"
|
||||
#include "../utility/juce_FakeMouseMoveGenerator.h"
|
||||
#include "../utility/juce_LinuxMessageThread.h"
|
||||
#include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>
|
||||
#include <juce_audio_processors/format_types/juce_VST3Common.h>
|
||||
|
||||
|
|
@ -101,7 +102,165 @@ using namespace Steinberg;
|
|||
#endif
|
||||
|
||||
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
|
||||
extern JUCE_API double getScaleFactorForWindow (HWND);
|
||||
extern JUCE_API double getScaleFactorForWindow (HWND);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
|
||||
class EventHandler final : public Steinberg::Linux::IEventHandler
|
||||
{
|
||||
public:
|
||||
EventHandler() = default;
|
||||
|
||||
~EventHandler()
|
||||
{
|
||||
jassert (hostRunLoops.size() == 0);
|
||||
|
||||
if (! messageThread->isRunning())
|
||||
messageThread->start();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_VST3_COM_REF_METHODS
|
||||
|
||||
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
|
||||
{
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Steinberg::Linux::IEventHandler)
|
||||
|
||||
*obj = nullptr;
|
||||
return kNoInterface;
|
||||
}
|
||||
|
||||
void PLUGIN_API onFDIsSet (Steinberg::Linux::FileDescriptor fd) override
|
||||
{
|
||||
updateCurrentMessageThread();
|
||||
|
||||
auto it = fdCallbackMap.find (fd);
|
||||
|
||||
if (it != fdCallbackMap.end())
|
||||
it->second (fd);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void registerHandlerForFrame (IPlugFrame* plugFrame)
|
||||
{
|
||||
if (auto* runLoop = getRunLoopFromFrame (plugFrame))
|
||||
{
|
||||
if (hostRunLoops.contains (runLoop))
|
||||
runLoop->unregisterEventHandler (this);
|
||||
|
||||
hostRunLoops.add (runLoop);
|
||||
|
||||
fdCallbackMap.clear();
|
||||
|
||||
for (auto& cb : getFdReadCallbacks())
|
||||
{
|
||||
fdCallbackMap[cb.first] = cb.second;
|
||||
runLoop->registerEventHandler (this, cb.first);
|
||||
}
|
||||
|
||||
updateCurrentMessageThread();
|
||||
}
|
||||
}
|
||||
|
||||
void unregisterHandlerForFrame (IPlugFrame* plugFrame)
|
||||
{
|
||||
if (auto* runLoop = getRunLoopFromFrame (plugFrame))
|
||||
{
|
||||
hostRunLoops.remove (runLoop);
|
||||
|
||||
if (! hostRunLoops.contains (runLoop))
|
||||
runLoop->unregisterEventHandler (this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//=============================================================================
|
||||
class HostRunLoopInterfaces
|
||||
{
|
||||
public:
|
||||
HostRunLoopInterfaces() = default;
|
||||
|
||||
void add (Steinberg::Linux::IRunLoop* runLoop)
|
||||
{
|
||||
if (auto* refCountedRunLoop = find (runLoop))
|
||||
{
|
||||
++(refCountedRunLoop->refCount);
|
||||
return;
|
||||
}
|
||||
|
||||
runLoops.push_back ({ runLoop, 1 });
|
||||
}
|
||||
|
||||
void remove (Steinberg::Linux::IRunLoop* runLoop)
|
||||
{
|
||||
if (auto* refCountedRunLoop = find (runLoop))
|
||||
if (--(refCountedRunLoop->refCount) == 0)
|
||||
runLoops.erase (std::find (runLoops.begin(), runLoops.end(), runLoop));
|
||||
}
|
||||
|
||||
size_t size() const noexcept { return runLoops.size(); }
|
||||
bool contains (Steinberg::Linux::IRunLoop* runLoop) { return find (runLoop) != nullptr; }
|
||||
|
||||
private:
|
||||
struct RefCountedRunLoop
|
||||
{
|
||||
Steinberg::Linux::IRunLoop* runLoop = nullptr;
|
||||
int refCount = 0;
|
||||
|
||||
bool operator== (const Steinberg::Linux::IRunLoop* other) const noexcept { return runLoop == other; }
|
||||
};
|
||||
|
||||
RefCountedRunLoop* find (const Steinberg::Linux::IRunLoop* runLoop)
|
||||
{
|
||||
auto iter = std::find (runLoops.begin(), runLoops.end(), runLoop);
|
||||
|
||||
if (iter != runLoops.end())
|
||||
return &(*iter);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<RefCountedRunLoop> runLoops;
|
||||
|
||||
JUCE_DECLARE_NON_MOVEABLE (HostRunLoopInterfaces)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HostRunLoopInterfaces)
|
||||
};
|
||||
|
||||
static Steinberg::Linux::IRunLoop* getRunLoopFromFrame (IPlugFrame* plugFrame)
|
||||
{
|
||||
Steinberg::Linux::IRunLoop* runLoop = nullptr;
|
||||
|
||||
if (plugFrame != nullptr)
|
||||
plugFrame->queryInterface (Steinberg::Linux::IRunLoop::iid, (void**) &runLoop);
|
||||
|
||||
jassert (runLoop != nullptr);
|
||||
return runLoop;
|
||||
}
|
||||
|
||||
void updateCurrentMessageThread()
|
||||
{
|
||||
if (! MessageManager::getInstance()->isThisTheMessageThread())
|
||||
{
|
||||
if (messageThread->isRunning())
|
||||
messageThread->stop();
|
||||
|
||||
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
|
||||
}
|
||||
}
|
||||
|
||||
SharedResourcePointer<MessageThread> messageThread;
|
||||
|
||||
std::atomic<int> refCount { 1 };
|
||||
|
||||
HostRunLoopInterfaces hostRunLoops;
|
||||
std::unordered_map<int, std::function<void (int)>> fdCallbackMap;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_MOVEABLE (EventHandler)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventHandler)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -1165,16 +1324,18 @@ private:
|
|||
//==============================================================================
|
||||
class JuceVST3Editor : public Vst::EditorView,
|
||||
public Steinberg::IPlugViewContentScaleSupport,
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
public Steinberg::Linux::IEventHandler,
|
||||
#endif
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
JuceVST3Editor (JuceVST3EditController& ec, AudioProcessor& p)
|
||||
: Vst::EditorView (&ec, nullptr),
|
||||
owner (&ec), pluginInstance (p)
|
||||
owner (&ec),
|
||||
pluginInstance (p)
|
||||
{
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
const MessageManagerLock mmLock;
|
||||
#endif
|
||||
|
||||
createContentWrapperComponentIfNeeded();
|
||||
|
||||
#if JUCE_MAC
|
||||
|
|
@ -1186,6 +1347,15 @@ private:
|
|||
#endif
|
||||
}
|
||||
|
||||
~JuceVST3Editor() override
|
||||
{
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
const MessageManagerLock mmLock;
|
||||
#endif
|
||||
|
||||
component = nullptr;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
|
||||
{
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Steinberg::IPlugViewContentScaleSupport)
|
||||
|
|
@ -1194,20 +1364,6 @@ private:
|
|||
|
||||
REFCOUNT_METHODS (Vst::EditorView)
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
void PLUGIN_API onFDIsSet (Steinberg::Linux::FileDescriptor fd) override
|
||||
{
|
||||
if (plugFrame != nullptr)
|
||||
{
|
||||
auto it = fdCallbackMap.find (fd);
|
||||
|
||||
if (it != fdCallbackMap.end())
|
||||
it->second (fd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
tresult PLUGIN_API isPlatformTypeSupported (FIDString type) override
|
||||
{
|
||||
|
|
@ -1231,6 +1387,10 @@ private:
|
|||
if (parent == nullptr || isPlatformTypeSupported (type) == kResultFalse)
|
||||
return kResultFalse;
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
eventHandler->registerHandlerForFrame (plugFrame);
|
||||
#endif
|
||||
|
||||
systemWindow = parent;
|
||||
|
||||
createContentWrapperComponentIfNeeded();
|
||||
|
|
@ -1245,17 +1405,6 @@ private:
|
|||
component->startTimer (500);
|
||||
#endif
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
if (auto* runLoop = getHostRunLoop())
|
||||
{
|
||||
for (auto& cb : getFdReadCallbacks())
|
||||
{
|
||||
fdCallbackMap[cb.first] = cb.second;
|
||||
runLoop->registerEventHandler (this, cb.first);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
isNSView = (strcmp (type, kPlatformTypeNSView) == 0);
|
||||
macHostWindow = juce::attachComponentToWindowRefVST (component.get(), parent, isNSView);
|
||||
|
|
@ -1277,12 +1426,7 @@ private:
|
|||
{
|
||||
#if JUCE_WINDOWS
|
||||
component->removeFromDesktop();
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
fdCallbackMap.clear();
|
||||
|
||||
if (auto* runLoop = getHostRunLoop())
|
||||
runLoop->unregisterEventHandler (this);
|
||||
#else
|
||||
#elif JUCE_MAC
|
||||
if (macHostWindow != nullptr)
|
||||
{
|
||||
juce::detachComponentFromWindowRefVST (component.get(), macHostWindow, isNSView);
|
||||
|
|
@ -1293,6 +1437,10 @@ private:
|
|||
component = nullptr;
|
||||
}
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
eventHandler->unregisterHandlerForFrame (plugFrame);
|
||||
#endif
|
||||
|
||||
return CPluginView::removed();
|
||||
}
|
||||
|
||||
|
|
@ -1691,6 +1839,11 @@ private:
|
|||
//==============================================================================
|
||||
ScopedJuceInitialiser_GUI libraryInitialiser;
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
SharedResourcePointer<MessageThread> messageThread;
|
||||
SharedResourcePointer<EventHandler> eventHandler;
|
||||
#endif
|
||||
|
||||
VSTComSmartPtr<JuceVST3EditController> owner;
|
||||
AudioProcessor& pluginInstance;
|
||||
|
||||
|
|
@ -1723,20 +1876,6 @@ private:
|
|||
|
||||
#if JUCE_WINDOWS
|
||||
WindowsHooks hooks;
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
std::unordered_map<int, std::function<void (int)>> fdCallbackMap;
|
||||
|
||||
::Display* display = XWindowSystem::getInstance()->getDisplay();
|
||||
|
||||
Steinberg::Linux::IRunLoop* getHostRunLoop()
|
||||
{
|
||||
Steinberg::Linux::IRunLoop* runLoop = nullptr;
|
||||
|
||||
if (plugFrame != nullptr)
|
||||
plugFrame->queryInterface (Steinberg::Linux::IRunLoop::iid, (void**) &runLoop);
|
||||
|
||||
return runLoop;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1803,12 +1942,20 @@ public:
|
|||
|
||||
~JuceVST3Component() override
|
||||
{
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
const MessageManagerLock mmLock;
|
||||
#endif
|
||||
|
||||
if (juceVST3EditController != nullptr)
|
||||
juceVST3EditController->vst3IsPlaying = false;
|
||||
|
||||
if (pluginInstance != nullptr)
|
||||
if (pluginInstance->getPlayHead() == this)
|
||||
pluginInstance->setPlayHead (nullptr);
|
||||
|
||||
juceVST3EditController = nullptr;
|
||||
comPluginInstance = nullptr;
|
||||
host = nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -2990,6 +3137,10 @@ private:
|
|||
//==============================================================================
|
||||
ScopedJuceInitialiser_GUI libraryInitialiser;
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
SharedResourcePointer<MessageThread> messageThread;
|
||||
#endif
|
||||
|
||||
std::atomic<int> refCount { 1 };
|
||||
|
||||
AudioProcessor* pluginInstance;
|
||||
|
|
@ -3269,6 +3420,10 @@ struct JucePluginFactory : public IPluginFactory3
|
|||
{
|
||||
ScopedJuceInitialiser_GUI libraryInitialiser;
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
SharedResourcePointer<MessageThread> messageThread;
|
||||
#endif
|
||||
|
||||
*obj = nullptr;
|
||||
|
||||
TUID tuid;
|
||||
|
|
|
|||
|
|
@ -35,17 +35,20 @@ class MessageThread
|
|||
public:
|
||||
MessageThread()
|
||||
{
|
||||
startThread();
|
||||
start();
|
||||
}
|
||||
|
||||
~MessageThread()
|
||||
{
|
||||
MessageManager::getInstance()->stopDispatchLoop();
|
||||
stopThread();
|
||||
stop();
|
||||
}
|
||||
|
||||
void startThread()
|
||||
void start()
|
||||
{
|
||||
if (isRunning())
|
||||
stop();
|
||||
|
||||
shouldExit = false;
|
||||
|
||||
thread = std::thread { [this]
|
||||
|
|
@ -71,13 +74,16 @@ public:
|
|||
threadInitialised.wait();
|
||||
}
|
||||
|
||||
void stopThread()
|
||||
void stop()
|
||||
{
|
||||
if (! isRunning())
|
||||
return;
|
||||
|
||||
shouldExit = true;
|
||||
thread.join();
|
||||
}
|
||||
|
||||
bool isThreadRunning() const noexcept { return thread.joinable(); }
|
||||
bool isRunning() const noexcept { return thread.joinable(); }
|
||||
|
||||
private:
|
||||
WaitableEvent threadInitialised;
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ namespace Steinberg
|
|||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
DEF_CLASS_IID (Linux::IRunLoop)
|
||||
DEF_CLASS_IID (Linux::IEventHandler)
|
||||
#endif
|
||||
}
|
||||
#endif // JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue