1
0
Fork 0
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:
ed 2021-04-02 14:38:42 +01:00
parent 33f37847bc
commit efd52398d1
3 changed files with 217 additions and 55 deletions

View file

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

View file

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

View file

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