diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index dc1f422cb1..afe957fedd 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -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 #include @@ -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 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; + + std::atomic refCount { 1 }; + + HostRunLoopInterfaces hostRunLoops; + std::unordered_map> 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; + SharedResourcePointer eventHandler; + #endif + VSTComSmartPtr owner; AudioProcessor& pluginInstance; @@ -1723,20 +1876,6 @@ private: #if JUCE_WINDOWS WindowsHooks hooks; - #elif JUCE_LINUX || JUCE_BSD - std::unordered_map> 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; + #endif + std::atomic refCount { 1 }; AudioProcessor* pluginInstance; @@ -3269,6 +3420,10 @@ struct JucePluginFactory : public IPluginFactory3 { ScopedJuceInitialiser_GUI libraryInitialiser; + #if JUCE_LINUX || JUCE_BSD + SharedResourcePointer messageThread; + #endif + *obj = nullptr; TUID tuid; diff --git a/modules/juce_audio_plugin_client/utility/juce_LinuxMessageThread.h b/modules/juce_audio_plugin_client/utility/juce_LinuxMessageThread.h index e0e2bc5f3d..1e3fbc1ad5 100644 --- a/modules/juce_audio_plugin_client/utility/juce_LinuxMessageThread.h +++ b/modules/juce_audio_plugin_client/utility/juce_LinuxMessageThread.h @@ -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; diff --git a/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/modules/juce_audio_processors/format_types/juce_VST3Headers.h index 0daa0e2905..a2434bd10a 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Headers.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -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