From d763ebde83c879ce44e9c6761137d3b96f96725e Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 12 Mar 2020 00:41:35 +0100 Subject: [PATCH] Bitwig workarounds for VST --- vst/CMakeLists.txt | 5 +- vst/SfizzVstEditor.cpp | 39 ++++++- vst/SfizzVstEditor.h | 10 ++ vst/X11RunLoop.cpp | 145 ++++++++++++++++++++++++ vst/X11RunLoop.h | 47 ++++++++ vst/external/steinberg/LICENSE | 27 ----- vst/external/steinberg/src/x11runloop.h | 110 ------------------ 7 files changed, 241 insertions(+), 142 deletions(-) create mode 100644 vst/X11RunLoop.cpp create mode 100644 vst/X11RunLoop.h delete mode 100644 vst/external/steinberg/LICENSE delete mode 100644 vst/external/steinberg/src/x11runloop.h diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt index 83c51154..3ac5467b 100644 --- a/vst/CMakeLists.txt +++ b/vst/CMakeLists.txt @@ -4,6 +4,8 @@ set (VSTPLUGIN_BUNDLE_NAME "${PROJECT_NAME}.vst3") set (VST3SDK_BASEDIR "${CMAKE_CURRENT_SOURCE_DIR}/external/VST_SDK/VST3_SDK") set (VST3SDK_ARCHIVE "vst-sdk_3.6.14_build-24_2019-11-29.zip") +file (MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external") + if (NOT EXISTS "${VST3SDK_BASEDIR}") message (STATUS "VST3 SDK is not found, downloading") @@ -41,7 +43,8 @@ add_library(${VSTPLUGIN_PRJ_NAME} MODULE SfizzVstEditor.cpp SfizzVstState.cpp GUIComponents.cpp - VstPluginFactory.cpp) + VstPluginFactory.cpp + X11RunLoop.cpp) if(WIN32) target_sources(${VSTPLUGIN_PRJ_NAME} PRIVATE vst3.def) diff --git a/vst/SfizzVstEditor.cpp b/vst/SfizzVstEditor.cpp index cd683d3b..4205c4f6 100644 --- a/vst/SfizzVstEditor.cpp +++ b/vst/SfizzVstEditor.cpp @@ -8,7 +8,7 @@ #include "SfizzVstState.h" #include "GUIComponents.h" #if !defined(__APPLE__) && !defined(_WIN32) -#include "x11runloop.h" +#include "X11RunLoop.h" #endif using namespace VSTGUI; @@ -39,7 +39,9 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p #if !defined(__APPLE__) && !defined(_WIN32) X11::FrameConfig x11config; - x11config.runLoop = VSTGUI::owned(new RunLoop(plugFrame)); + if (!_runLoop) + _runLoop = new RunLoop(plugFrame); + x11config.runLoop = _runLoop; config = &x11config; #endif @@ -58,8 +60,13 @@ void PLUGIN_API SfizzVstEditor::close() { CFrame *frame = this->frame; if (frame) { - frame->forget(); - this->frame = nullptr; + frame->removeAll(); + if (frame->getNbReference() != 1) + frame->forget (); + else { + frame->close(); + this->frame = nullptr; + } } } @@ -136,6 +143,30 @@ void SfizzVstEditor::controlEndEdit(CControl* ctl) enterOrLeaveEdit(ctl, false); } +CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) +{ + CMessageResult result = VSTGUIEditor::notify(sender, message); + + if (result != kMessageNotified) + return result; + +#if !defined(__APPLE__) && !defined(_WIN32) + if (message == CVSTGUITimer::kMsgTimer) { + SharedPointer runLoop = RunLoop::get(); + if (runLoop) { + // note(jpc) I don't find a reliable way to check if the host + // notifier of X11 events is working. If there is, remove this and + // avoid polluting Linux hosts which implement the loop correctly. + runLoop->processSomeEvents(); + + runLoop->cleanupDeadHandlers(); + } + } +#endif + + return result; +} + void SfizzVstEditor::onStateChanged() { updateStateDisplay(); diff --git a/vst/SfizzVstEditor.h b/vst/SfizzVstEditor.h index 7ed9a4dd..3b0244b7 100644 --- a/vst/SfizzVstEditor.h +++ b/vst/SfizzVstEditor.h @@ -7,6 +7,9 @@ #pragma once #include "SfizzVstController.h" #include "public.sdk/source/vst/vstguieditor.h" +#if !defined(__APPLE__) && !defined(_WIN32) +namespace VSTGUI { class RunLoop; } +#endif using namespace Steinberg; using namespace VSTGUI; @@ -30,6 +33,9 @@ public: void controlBeginEdit(CControl* ctl) override; void controlEndEdit(CControl* ctl) override; + // VSTGUIEditor + CMessageResult notify(CBaseObject* sender, const char* message) override; + // SfizzVstController::StateListener void onStateChanged() override; @@ -75,4 +81,8 @@ private: CSliderBase *_numVoicesSlider = nullptr; CSliderBase *_oversamplingSlider = nullptr; CSliderBase *_preloadSizeSlider = nullptr; + +#if !defined(__APPLE__) && !defined(_WIN32) + SharedPointer _runLoop; +#endif }; diff --git a/vst/X11RunLoop.cpp b/vst/X11RunLoop.cpp new file mode 100644 index 00000000..99077846 --- /dev/null +++ b/vst/X11RunLoop.cpp @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-3.0 + +#if !defined(__APPLE__) && !defined(_WIN32) +#include "X11RunLoop.h" +#include "vstgui/lib/platform/linux/x11platform.h" +#include "base/source/fobject.h" + +namespace VSTGUI { + +RunLoop::RunLoop(Steinberg::FUnknown* runLoop) + : runLoop(runLoop) +{ +} + +RunLoop::~RunLoop() {} + +SharedPointer RunLoop::get() +{ + return X11::RunLoop::get().cast(); +} + +struct RunLoop::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject { + X11::IEventHandler* handler { nullptr }; + bool alive { false }; + + void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override + { + if (alive && handler) + handler->onEvent(); + } + + DELEGATE_REFCOUNT(Steinberg::FObject) + DEFINE_INTERFACES + DEF_INTERFACE(Steinberg::Linux::IEventHandler) + END_DEFINE_INTERFACES(Steinberg::FObject) +}; + +struct RunLoop::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject { + X11::ITimerHandler* handler { nullptr }; + bool alive { false }; + + void PLUGIN_API onTimer() override + { + if (alive && handler) + handler->onTimer(); + } + + DELEGATE_REFCOUNT(Steinberg::FObject) + DEFINE_INTERFACES + DEF_INTERFACE(Steinberg::Linux::ITimerHandler) + END_DEFINE_INTERFACES(Steinberg::FObject) +}; + + +void RunLoop::processSomeEvents() +{ + for (size_t i = 0; i < eventHandlers.size(); ++i) { + const auto& eh = eventHandlers[i]; + if (eh->alive && eh->handler) { + eh->handler->onEvent(); + } + } +} + +void RunLoop::cleanupDeadHandlers() +{ + for (size_t i = 0; i < eventHandlers.size(); ++i) { + const auto& eh = eventHandlers[i]; + if (!eh->alive) { + runLoop->unregisterEventHandler(eh); + eventHandlers.erase(eventHandlers.begin() + i--); + } + } + for (size_t i = 0; i < timerHandlers.size(); ++i) { + const auto& th = timerHandlers[i]; + if (!th->alive) { + runLoop->unregisterTimer(th); + timerHandlers.erase(timerHandlers.begin() + i--); + } + } +} + +bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler) +{ + if (!runLoop) + return false; + + auto smtgHandler = Steinberg::owned(new EventHandler()); + smtgHandler->handler = handler; + smtgHandler->alive = true; + if (runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) { + eventHandlers.push_back(smtgHandler); + return true; + } + return false; +} + +bool RunLoop::unregisterEventHandler(X11::IEventHandler* handler) +{ + if (!runLoop) + return false; + + for (size_t i = 0; i < eventHandlers.size(); ++i) { + const auto& eh = eventHandlers[i]; + if (eh->alive && eh->handler == handler) { + eh->alive = false; + return true; + } + } + return false; +} + +bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler) +{ + if (!runLoop) + return false; + + auto smtgHandler = Steinberg::owned(new TimerHandler()); + smtgHandler->handler = handler; + smtgHandler->alive = true; + if (runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) { + timerHandlers.push_back(smtgHandler); + return true; + } + return false; +} + +bool RunLoop::unregisterTimer(X11::ITimerHandler* handler) +{ + if (!runLoop) + return false; + + for (size_t i = 0; i < timerHandlers.size(); ++i) { + const auto& th = timerHandlers[i]; + if (th->alive && th->handler == handler) { + th->alive = false; + return true; + } + } + return false; +} + +} // namespace VSTGUI + +#endif diff --git a/vst/X11RunLoop.h b/vst/X11RunLoop.h new file mode 100644 index 00000000..cb47b34c --- /dev/null +++ b/vst/X11RunLoop.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0 +/* + This is a modified version the X11 run loop from vst3editor.cpp. + + This version is edited to add more safeguards to protect against host bugs. + It also permits to call event processing externally in case the host has a + defective X11 event loop notifier. +*/ + +#pragma once +#if !defined(__APPLE__) && !defined(_WIN32) +#include "vstgui/lib/platform/linux/x11frame.h" +#include "pluginterfaces/gui/iplugview.h" + +namespace VSTGUI { + +class RunLoop final : public X11::IRunLoop, public AtomicReferenceCounted { +public: + explicit RunLoop(Steinberg::FUnknown* runLoop); + ~RunLoop(); + + static SharedPointer get(); + + void processSomeEvents(); + void cleanupDeadHandlers(); + + // X11::IRunLoop + bool registerEventHandler(int fd, X11::IEventHandler* handler); + bool unregisterEventHandler(X11::IEventHandler* handler); + bool registerTimer(uint64_t interval, X11::ITimerHandler* handler); + bool unregisterTimer(X11::ITimerHandler* handler); + +private: + struct EventHandler; + struct TimerHandler; + +private: + using EventHandlers = std::vector>; + using TimerHandlers = std::vector>; + EventHandlers eventHandlers; + TimerHandlers timerHandlers; + Steinberg::FUnknownPtr runLoop; +}; + +} // namespace VSTGUI + +#endif diff --git a/vst/external/steinberg/LICENSE b/vst/external/steinberg/LICENSE deleted file mode 100644 index a75f1d5b..00000000 --- a/vst/external/steinberg/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -//----------------------------------------------------------------------------- -// VSTGUI LICENSE -// (c) 2018, Steinberg Media Technologies, All Rights Reserved -//----------------------------------------------------------------------------- -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Steinberg Media Technologies nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -// OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- diff --git a/vst/external/steinberg/src/x11runloop.h b/vst/external/steinberg/src/x11runloop.h deleted file mode 100644 index 7cece455..00000000 --- a/vst/external/steinberg/src/x11runloop.h +++ /dev/null @@ -1,110 +0,0 @@ -#include "vstgui/lib/platform/linux/x11frame.h" -#include "pluginterfaces/gui/iplugview.h" -#include "base/source/fstring.h" - -namespace VSTGUI { - -// Map Steinberg Vst Interface to VSTGUI Interface -class RunLoop : public X11::IRunLoop, public AtomicReferenceCounted -{ -public: - struct EventHandler : Steinberg::Linux::IEventHandler, public Steinberg::FObject - { - X11::IEventHandler* handler {nullptr}; - - void PLUGIN_API onFDIsSet (Steinberg::Linux::FileDescriptor) override - { - if (handler) - handler->onEvent (); - } - DELEGATE_REFCOUNT (Steinberg::FObject) - DEFINE_INTERFACES - DEF_INTERFACE (Steinberg::Linux::IEventHandler) - END_DEFINE_INTERFACES (Steinberg::FObject) - }; - struct TimerHandler : Steinberg::Linux::ITimerHandler, public Steinberg::FObject - { - X11::ITimerHandler* handler {nullptr}; - - void PLUGIN_API onTimer () final - { - if (handler) - handler->onTimer (); - } - DELEGATE_REFCOUNT (Steinberg::FObject) - DEFINE_INTERFACES - DEF_INTERFACE (Steinberg::Linux::ITimerHandler) - END_DEFINE_INTERFACES (Steinberg::FObject) - }; - - bool registerEventHandler (int fd, X11::IEventHandler* handler) final - { - if(!runLoop) - return false; - - auto smtgHandler = Steinberg::owned (new EventHandler ()); - smtgHandler->handler = handler; - if (runLoop->registerEventHandler (smtgHandler, fd) == Steinberg::kResultTrue) - { - eventHandlers.push_back (smtgHandler); - return true; - } - return false; - } - bool unregisterEventHandler (X11::IEventHandler* handler) final - { - if(!runLoop) - return false; - - for (auto it = eventHandlers.begin (), end = eventHandlers.end (); it != end; ++it) - { - if ((*it)->handler == handler) - { - runLoop->unregisterEventHandler ((*it)); - eventHandlers.erase (it); - return true; - } - } - return false; - } - bool registerTimer (uint64_t interval, X11::ITimerHandler* handler) final - { - if(!runLoop) - return false; - - auto smtgHandler = Steinberg::owned (new TimerHandler ()); - smtgHandler->handler = handler; - if (runLoop->registerTimer (smtgHandler, interval) == Steinberg::kResultTrue) - { - timerHandlers.push_back (smtgHandler); - return true; - } - return false; - } - bool unregisterTimer (X11::ITimerHandler* handler) final - { - if(!runLoop) - return false; - - for (auto it = timerHandlers.begin (), end = timerHandlers.end (); it != end; ++it) - { - if ((*it)->handler == handler) - { - runLoop->unregisterTimer ((*it)); - timerHandlers.erase (it); - return true; - } - } - return false; - } - - RunLoop (Steinberg::FUnknown* runLoop) : runLoop (runLoop) {} -private: - using EventHandlers = std::vector>; - using TimerHandlers = std::vector>; - EventHandlers eventHandlers; - TimerHandlers timerHandlers; - Steinberg::FUnknownPtr runLoop; -}; - -} // namespace