From de712ca02e2bca8a7b66e1f4c99ff246dd6a7c47 Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 15 Nov 2019 19:12:00 +0000 Subject: [PATCH] Linux: Added support for building and hosting VST3 plug-ins --- .../VST/juce_VST_Wrapper.cpp | 4 +- .../VST3/juce_VST3_Wrapper.cpp | 191 +++++++++---- .../utility/juce_CheckSettingMacros.h | 2 +- .../utility/juce_PluginUtilities.cpp | 6 +- .../format/juce_AudioPluginFormatManager.cpp | 4 +- .../VST3_SDK/base/source/fstring.cpp | 10 +- .../VST3_SDK/pluginterfaces/base/funknown.cpp | 1 - .../format_types/juce_VST3Common.h | 4 +- .../format_types/juce_VST3Headers.h | 33 ++- .../format_types/juce_VST3PluginFormat.cpp | 252 +++++++++++++++--- .../format_types/juce_VST3PluginFormat.h | 2 +- .../juce_audio_processors.cpp | 5 +- .../native/juce_linux_Messaging.cpp | 17 ++ .../embedding/juce_XEmbedComponent.h | 3 + .../native/juce_linux_XEmbedComponent.cpp | 3 + 15 files changed, 416 insertions(+), 121 deletions(-) diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp index f87b910996..d0c1884dbe 100644 --- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp @@ -1847,8 +1847,8 @@ private: if (handleManufacturerSpecificVST2Opcode (args.index, args.value, args.ptr, args.opt)) return 1; - if (args.index == JUCE_MULTICHAR_CONSTANT ('P', 'r', 'e', 'S') - && args.value == JUCE_MULTICHAR_CONSTANT ('A', 'e', 'C', 's')) + if (args.index == (int32) JUCE_MULTICHAR_CONSTANT ('P', 'r', 'e', 'S') + && args.value == (int32) JUCE_MULTICHAR_CONSTANT ('A', 'e', 'C', 's')) return handleSetContentScaleFactor (args.opt); if (args.index == Vst2::effGetParamDisplay) 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 41b78fa907..4a62a262b8 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -20,9 +20,9 @@ #include "../../juce_core/system/juce_TargetPlatform.h" //============================================================================== -#if JucePlugin_Build_VST3 && (__APPLE_CPP__ || __APPLE_CC__ || _WIN32 || _WIN64) +#if JucePlugin_Build_VST3 && (__APPLE_CPP__ || __APPLE_CC__ || _WIN32 || _WIN64 || LINUX || __linux__) -#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS) +#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) #undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY #define JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY 1 #endif @@ -30,9 +30,11 @@ #include "../../juce_audio_processors/format_types/juce_VST3Headers.h" #undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY +#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 #include "../utility/juce_CheckSettingMacros.h" #include "../utility/juce_IncludeModuleHeaders.h" +#include "../utility/juce_IncludeSystemHeaders.h" #include "../utility/juce_WindowsHooks.h" #include "../utility/juce_FakeMouseMoveGenerator.h" #include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" @@ -55,6 +57,10 @@ namespace Vst2 #endif #endif +#if JUCE_LINUX + std::vector>> getFdReadCallbacks(); +#endif + namespace juce { @@ -62,14 +68,14 @@ using namespace Steinberg; //============================================================================== #if JUCE_MAC - extern void initialiseMacVST(); + extern void initialiseMacVST(); #if ! JUCE_64BIT extern void updateEditorCompBoundsVST (Component*); #endif - extern JUCE_API void* attachComponentToWindowRefVST (Component*, void* parentWindowOrView, bool isNSView); - extern JUCE_API void detachComponentFromWindowRefVST (Component*, void* nsWindow, bool isNSView); + extern JUCE_API void* attachComponentToWindowRefVST (Component*, void* parentWindowOrView, bool isNSView); + extern JUCE_API void detachComponentFromWindowRefVST (Component*, void* nsWindow, bool isNSView); #endif #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE @@ -426,7 +432,7 @@ public: jassert (info.defaultNormalizedValue >= 0 && info.defaultNormalizedValue <= 1.0f); // Is this a meter? - if (((param.getCategory() & 0xffff0000) >> 16) == 2) + if ((((unsigned int) param.getCategory() & 0xffff0000) >> 16) == 2) info.flags = Vst::ParameterInfo::kIsReadOnly; else info.flags = param.isAutomatable() ? Vst::ParameterInfo::kCanAutomate : 0; @@ -1011,6 +1017,9 @@ private: //============================================================================== class JuceVST3Editor : public Vst::EditorView, public Steinberg::IPlugViewContentScaleSupport, + #if JUCE_LINUX + public Steinberg::Linux::IEventHandler, + #endif private Timer { public: @@ -1037,6 +1046,17 @@ private: REFCOUNT_METHODS (Vst::EditorView) + //============================================================================== + #if JUCE_LINUX + void PLUGIN_API onFDIsSet (Steinberg::Linux::FileDescriptor fd) override + { + auto it = fdCallbackMap.find (fd); + + if (it != fdCallbackMap.end()) + it->second (fd); + } + #endif + //============================================================================== tresult PLUGIN_API isPlatformTypeSupported (FIDString type) override { @@ -1044,8 +1064,10 @@ private: { #if JUCE_WINDOWS if (strcmp (type, kPlatformTypeHWND) == 0) - #else + #elif JUCE_MAC if (strcmp (type, kPlatformTypeNSView) == 0 || strcmp (type, kPlatformTypeHIView) == 0) + #elif JUCE_LINUX + if (strcmp (type, kPlatformTypeX11EmbedWindowID) == 0) #endif return kResultTrue; } @@ -1061,10 +1083,20 @@ private: if (component == nullptr) component.reset (new ContentWrapperComponent (*this, pluginInstance)); - #if JUCE_WINDOWS + #if JUCE_WINDOWS || JUCE_LINUX component->addToDesktop (0, parent); component->setOpaque (true); component->setVisible (true); + #if JUCE_LINUX + 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); @@ -1085,8 +1117,14 @@ private: { if (component != nullptr) { - #if JUCE_WINDOWS + #if JUCE_WINDOWS || JUCE_LINUX component->removeFromDesktop(); + #if JUCE_LINUX + fdCallbackMap.clear(); + + if (auto* runLoop = getHostRunLoop()) + runLoop->unregisterEventHandler (this); + #endif #else if (macHostWindow != nullptr) { @@ -1465,6 +1503,8 @@ private: }; //============================================================================== + ScopedJuceInitialiser_GUI libraryInitialiser; + ComSmartPtr owner; AudioProcessor& pluginInstance; @@ -1497,7 +1537,22 @@ private: #if JUCE_WINDOWS WindowsHooks hooks; + #elif JUCE_LINUX + 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 //============================================================================== @@ -1766,7 +1821,7 @@ public: void setStateInformation (const void* data, int sizeAsInt) { - int64 size = sizeAsInt; + auto size = (uint64) sizeAsInt; // Check if this data was written with a newer JUCE version // and if it has the JUCE private data magic code at the end @@ -1798,7 +1853,7 @@ public: } } - if (size >= 0) + if (size > 0) pluginInstance->setStateInformation (data, static_cast (size)); } @@ -2833,61 +2888,87 @@ bool shutdownModule() #undef JUCE_EXPORTED_FUNCTION #if JUCE_WINDOWS - extern "C" __declspec (dllexport) bool InitDll() { return initModule(); } - extern "C" __declspec (dllexport) bool ExitDll() { return shutdownModule(); } - #define JUCE_EXPORTED_FUNCTION - + extern "C" __declspec (dllexport) bool InitDll() { return initModule(); } + extern "C" __declspec (dllexport) bool ExitDll() { return shutdownModule(); } + #define JUCE_EXPORTED_FUNCTION #else - #define JUCE_EXPORTED_FUNCTION extern "C" __attribute__ ((visibility ("default"))) + #define JUCE_EXPORTED_FUNCTION extern "C" __attribute__ ((visibility ("default"))) +#endif - CFBundleRef globalBundleInstance = nullptr; - juce::uint32 numBundleRefs = 0; - juce::Array bundleRefs; +#if JUCE_LINUX + void* moduleHandle = nullptr; + int moduleEntryCounter = 0; - enum { MaxPathLength = 2048 }; - char modulePath[MaxPathLength] = { 0 }; - void* moduleHandle = nullptr; + JUCE_EXPORTED_FUNCTION bool ModuleEntry (void* sharedLibraryHandle) + { + if (++moduleEntryCounter == 1) + { + moduleHandle = sharedLibraryHandle; + return initModule(); + } - JUCE_EXPORTED_FUNCTION bool bundleEntry (CFBundleRef ref) - { - if (ref != nullptr) - { - ++numBundleRefs; - CFRetain (ref); + return true; + } - bundleRefs.add (ref); + JUCE_EXPORTED_FUNCTION bool ModuleExit() + { + if (--moduleEntryCounter == 0) + { + moduleHandle = nullptr; + return shutdownModule(); + } - if (moduleHandle == nullptr) - { - globalBundleInstance = ref; - moduleHandle = ref; + return true; + } +#elif JUCE_MAC + CFBundleRef globalBundleInstance = nullptr; + juce::uint32 numBundleRefs = 0; + juce::Array bundleRefs; - CFURLRef tempURL = CFBundleCopyBundleURL (ref); - CFURLGetFileSystemRepresentation (tempURL, true, (UInt8*) modulePath, MaxPathLength); - CFRelease (tempURL); - } - } + enum { MaxPathLength = 2048 }; + char modulePath[MaxPathLength] = { 0 }; + void* moduleHandle = nullptr; - return initModule(); - } + JUCE_EXPORTED_FUNCTION bool bundleEntry (CFBundleRef ref) + { + if (ref != nullptr) + { + ++numBundleRefs; + CFRetain (ref); - JUCE_EXPORTED_FUNCTION bool bundleExit() - { - if (shutdownModule()) - { - if (--numBundleRefs == 0) - { - for (int i = 0; i < bundleRefs.size(); ++i) - CFRelease (bundleRefs.getUnchecked (i)); + bundleRefs.add (ref); - bundleRefs.clear(); - } + if (moduleHandle == nullptr) + { + globalBundleInstance = ref; + moduleHandle = ref; - return true; - } + CFURLRef tempURL = CFBundleCopyBundleURL (ref); + CFURLGetFileSystemRepresentation (tempURL, true, (UInt8*) modulePath, MaxPathLength); + CFRelease (tempURL); + } + } - return false; - } + return initModule(); + } + + JUCE_EXPORTED_FUNCTION bool bundleExit() + { + if (shutdownModule()) + { + if (--numBundleRefs == 0) + { + for (int i = 0; i < bundleRefs.size(); ++i) + CFRelease (bundleRefs.getUnchecked (i)); + + bundleRefs.clear(); + } + + return true; + } + + return false; + } #endif //============================================================================== @@ -3091,7 +3172,7 @@ private: if (entry->isUnicode) return kResultFalse; - memcpy (info, &entry->info2, sizeof (PClassInfoType)); + memcpy (info, (PClassInfoType*) &entry->info2, sizeof (PClassInfoType)); return kResultOk; } } diff --git a/modules/juce_audio_plugin_client/utility/juce_CheckSettingMacros.h b/modules/juce_audio_plugin_client/utility/juce_CheckSettingMacros.h index 6523fc4a6d..2868772d1d 100644 --- a/modules/juce_audio_plugin_client/utility/juce_CheckSettingMacros.h +++ b/modules/juce_audio_plugin_client/utility/juce_CheckSettingMacros.h @@ -66,7 +66,7 @@ #define JucePlugin_Build_RTAS 0 #endif -#if ! (defined (_MSC_VER) || defined (__APPLE_CPP__) || defined (__APPLE_CC__)) +#if ! (defined (_MSC_VER) || defined (__APPLE_CPP__) || defined (__APPLE_CC__) || defined (LINUX) || defined (__linux__)) #undef JucePlugin_Build_VST3 #define JucePlugin_Build_VST3 0 #endif diff --git a/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp b/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp index 8898db7090..beedbfa1e7 100644 --- a/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp +++ b/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp @@ -24,8 +24,6 @@ #include "../utility/juce_CheckSettingMacros.h" #include "juce_IncludeModuleHeaders.h" -using namespace juce; - namespace juce { @@ -40,7 +38,7 @@ std::function PluginHostType::jucePlugInIsRunningInAudioS #define JUCE_VST3_CAN_REPLACE_VST2 1 #endif -#if JucePlugin_Build_VST3 && (__APPLE_CPP__ || __APPLE_CC__ || _WIN32 || _WIN64) && JUCE_VST3_CAN_REPLACE_VST2 +#if JucePlugin_Build_VST3 && (__APPLE_CPP__ || __APPLE_CC__ || _WIN32 || _WIN64 || LINUX || __linux__) && JUCE_VST3_CAN_REPLACE_VST2 #define VST3_REPLACEMENT_AVAILABLE 1 // NB: Nasty old-fashioned code in here because it's copied from the Steinberg example code. @@ -141,6 +139,8 @@ bool JUCE_API handleManufacturerSpecificVST2Opcode (int32 index, pointer_sized_i } // namespace juce +using namespace juce; + //============================================================================== #if JucePlugin_Enable_IAA && JucePlugin_Build_Standalone && JUCE_IOS && (! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP) extern bool JUCE_CALLTYPE juce_isInterAppAudioConnected(); diff --git a/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp b/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp index 1613d61873..1121fd060a 100644 --- a/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp +++ b/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp @@ -35,7 +35,7 @@ void AudioPluginFormatManager::addDefaultFormats() jassert (dynamic_cast (format) == nullptr); #endif - #if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS) + #if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) jassert (dynamic_cast (format) == nullptr); #endif @@ -57,7 +57,7 @@ void AudioPluginFormatManager::addDefaultFormats() formats.add (new VSTPluginFormat()); #endif - #if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS) + #if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) formats.add (new VST3PluginFormat()); #endif diff --git a/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.cpp b/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.cpp index e41cbae122..1f294c1ab7 100644 --- a/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.cpp +++ b/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.cpp @@ -287,7 +287,6 @@ static inline int strnicmp16 (const Steinberg::char16* s1, const Steinberg::char //----------------------------------------------------------------------------- static inline int sprintf16 (Steinberg::char16* wcs, const Steinberg::char16* format, ...) { -#warning DEPRECATED No Linux implementation assert(false && "DEPRECATED No Linux implementation"); return 0; } @@ -311,7 +310,6 @@ static inline int vsnwprintf (Steinberg::char16* wcs, size_t maxlen, //----------------------------------------------------------------------------- static inline Steinberg::char16* strrchr16 (const Steinberg::char16* str, Steinberg::char16 c) { -#warning DEPRECATED No Linux implementation assert(false && "DEPRECATED No Linux implementation"); return nullptr; } @@ -1586,7 +1584,6 @@ char16 ConstString::toLower (char16 c) } return c; #elif SMTG_OS_LINUX - #warning DEPRECATED No Linux implementation assert(false && "DEPRECATED No Linux implementation"); return c; #else @@ -1615,7 +1612,6 @@ char16 ConstString::toUpper (char16 c) } return c; #elif SMTG_OS_LINUX - #warning DEPRECATED No Linux implementation assert(false && "DEPRECATED No Linux implementation"); return c; #else @@ -1913,8 +1909,7 @@ int32 ConstString::multiByteToWideString (char16* dest, const char8* source, int } } else - { -#warning DEPRECATED No Linux implementation + { assert(false && "DEPRECATED No Linux implementation"); } @@ -1994,8 +1989,7 @@ int32 ConstString::wideStringToMultiByte (char8* dest, const char16* wideString, } } else - { -#warning DEPRECATED No Linux implementation + { assert(false && "DEPRECATED No Linux implementation"); } return result; diff --git a/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp b/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp index 0419bbc4bb..206a64afcf 100644 --- a/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp +++ b/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp @@ -148,7 +148,6 @@ bool FUID::generate () return false; #else -#warning implement me! return false; #endif } diff --git a/modules/juce_audio_processors/format_types/juce_VST3Common.h b/modules/juce_audio_processors/format_types/juce_VST3Common.h index ddec01e172..9a59d2ba3a 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -85,8 +85,10 @@ inline Steinberg::Vst::TChar* toString (const juce::String& source) noexcept #if JUCE_WINDOWS static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; -#else +#elif JUCE_MAC static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; +#elif JUCE_LINUX + static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeX11EmbedWindowID; #endif diff --git a/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/modules/juce_audio_processors/format_types/juce_VST3Headers.h index 0d4b1d1ae4..209538a570 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Headers.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -43,7 +43,11 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnon-virtual-dtor", "-Winconsistent-missing-destructor-override", "-Wcast-align", "-Wignored-qualifiers", - "-Wmissing-field-initializers") + "-Wmissing-field-initializers", + "-Wformat=", + "-Wpedantic", + "-Wextra", + "-Wclass-memaccess") #undef DEVELOPMENT #define DEVELOPMENT 0 // This avoids a Clang warning in Steinberg code about unused values @@ -93,12 +97,14 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnon-virtual-dtor", #include #include #include -#if VST_VERSION >= 0x030608 - #include - #include -#else - #include -#endif + + #if VST_VERSION >= 0x030608 + #include + #include + #else + #include + #endif + #include #include #include @@ -118,9 +124,10 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnon-virtual-dtor", #include #include #include -#if VST_VERSION >= 0x03060c // 3.6.12 - #include -#endif + + #if VST_VERSION >= 0x03060c // 3.6.12 + #include + #endif //============================================================================== namespace Steinberg @@ -138,8 +145,12 @@ namespace Steinberg DEF_CLASS_IID (IPlugView) DEF_CLASS_IID (IPlugFrame) DEF_CLASS_IID (IPlugViewContentScaleSupport) + + #if JUCE_LINUX + DEF_CLASS_IID (Linux::IRunLoop) + #endif } -#endif //JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY +#endif // JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY JUCE_END_IGNORE_WARNINGS_MSVC JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 5040407dc8..ccddd4f12a 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -16,7 +16,7 @@ ============================================================================== */ -#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS) +#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) #include "juce_VST3Headers.h" #include "juce_VST3Common.h" @@ -573,7 +573,7 @@ private: tresult PLUGIN_API setBinary (AttrID id, const void* data, Steinberg::uint32 size) override { - jassert (size >= 0 && (data != nullptr || size == 0)); + jassert (data != nullptr || size == 0); addMessageToQueue (id, MemoryBlock (data, (size_t) size)); return kResultTrue; } @@ -821,15 +821,18 @@ struct DLLHandle { typedef bool (PLUGIN_API *ExitModuleFn) (); - #if JUCE_WINDOWS + #if JUCE_WINDOWS || JUCE_LINUX releaseFactory(); - if (auto exitFn = (ExitModuleFn) getFunction ("ExitDll")) + #if JUCE_WINDOWS + if (auto exitFn = (ExitModuleFn) getFunction ("ExitDll")) + #else + if (auto exitFn = (ExitModuleFn) getFunction ("ModuleExit")) + #endif exitFn(); library.close(); - - #else + #elif JUCE_MAC if (bundleRef != nullptr) { releaseFactory(); @@ -846,11 +849,11 @@ struct DLLHandle void open (const PluginDescription& description) { - #if JUCE_WINDOWS + #if JUCE_WINDOWS || JUCE_LINUX jassert (description.fileOrIdentifier.isNotEmpty()); jassert (File (description.fileOrIdentifier).existsAsFile()); library.open (description.fileOrIdentifier); - #else + #elif JUCE_MAC open (description.fileOrIdentifier); #endif } @@ -878,9 +881,9 @@ struct DLLHandle void* getFunction (const char* functionName) { - #if JUCE_WINDOWS + #if JUCE_WINDOWS || JUCE_LINUX return library.getFunction (functionName); - #else + #elif JUCE_MAC if (bundleRef == nullptr) return nullptr; @@ -924,8 +927,7 @@ private: return false; } - - #else + #elif JUCE_MAC CFBundleRef bundleRef; bool open (const String& filePath) @@ -973,6 +975,55 @@ private: } } + return false; + } + #elif JUCE_LINUX + DynamicLibrary library; + + String getMachineName() + { + struct utsname unameData; + auto res = uname (&unameData); + + if (res != 0) + return {}; + + return unameData.machine; + } + + bool open (const String& bundlePath) + { + File file (bundlePath); + + if (! file.exists() || ! file.isDirectory()) + return false; + + auto pluginName = file.getFileNameWithoutExtension(); + + file = file.getChildFile ("Contents") + .getChildFile (getMachineName() + "-linux") + .getChildFile (pluginName + ".so"); + + if (! file.exists()) + return false; + + if (library.open (file.getFullPathName())) + { + typedef bool (PLUGIN_API *InitModuleProc) (void*); + + if (auto* proc = (InitModuleProc) getFunction ("ModuleEntry")) + { + if (proc (library.getNativeHandle())) + return true; + } + else + { + return true; + } + + library.close(); + } + return false; } #endif @@ -1091,7 +1142,7 @@ private: //============================================================================== struct VST3PluginWindow : public AudioProcessorEditor, public ComponentMovementWatcher, - #if ! JUCE_MAC + #if JUCE_WINDOWS || JUCE_LINUX public ComponentPeer::ScaleFactorListener, #endif public IPlugFrame @@ -1121,6 +1172,10 @@ struct VST3PluginWindow : public AudioProcessorEditor, scaleInterface->release(); removeScaleFactorListeners(); + + #if JUCE_LINUX + embeddedComponent.removeClient(); + #endif #endif warnOnFailure (view->removed()); @@ -1135,8 +1190,118 @@ struct VST3PluginWindow : public AudioProcessorEditor, view = nullptr; } - JUCE_DECLARE_VST3_COM_REF_METHODS + #if JUCE_LINUX + struct RunLoop final : public Steinberg::Linux::IRunLoop + { + ~RunLoop() + { + for (const auto& h : eventHandlers) + LinuxEventLoop::unregisterFdCallback (h.first); + } + + tresult PLUGIN_API registerEventHandler (Linux::IEventHandler* handler, + Linux::FileDescriptor fd) override + { + if (handler == nullptr || eventHandlers.find (fd) != eventHandlers.end()) + return kInvalidArgument; + + LinuxEventLoop::registerFdCallback (fd, [handler] (int descriptor) + { + handler->onFDIsSet (descriptor); + return true; + }); + + eventHandlers.emplace (fd, handler); + return kResultTrue; + } + + tresult PLUGIN_API unregisterEventHandler (Linux::IEventHandler* handler) override + { + if (handler == nullptr) + return kInvalidArgument; + + for (auto it = eventHandlers.begin(), end = eventHandlers.end(); it != end; ++it) + { + if (it->second == handler) + { + LinuxEventLoop::unregisterFdCallback (it->first); + eventHandlers.erase (it); + return kResultTrue; + } + } + + return kResultFalse; + } + + tresult PLUGIN_API registerTimer (Linux::ITimerHandler* handler, Linux::TimerInterval milliseconds) override + { + if (handler == nullptr || milliseconds == 0) + return kInvalidArgument; + + timerHandlers.push_back (std::make_unique (handler, (int) milliseconds)); + return kResultTrue; + } + + tresult PLUGIN_API unregisterTimer (Linux::ITimerHandler* handler) override + { + if (handler == nullptr) + return kInvalidArgument; + + for (auto it = timerHandlers.begin(), end = timerHandlers.end(); it != end; ++it) + { + if (it->get()->handler == handler) + { + timerHandlers.erase (it); + return kResultTrue; + } + } + + return kNotImplemented; + } + + uint32 PLUGIN_API addRef() override { return 1000; } + uint32 PLUGIN_API release() override { return 1000; } + tresult PLUGIN_API queryInterface (const TUID, void**) override { return kNoInterface; } + + std::unordered_map eventHandlers; + + struct TimerCaller : public Timer + { + TimerCaller (Linux::ITimerHandler* h, int interval) : handler (h) + { + startTimer (interval); + } + + void timerCallback() override + { + handler->onTimer(); + } + + Linux::ITimerHandler* handler; + }; + std::vector> timerHandlers; + }; + + RunLoop runLoop; + + Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID iid, void** obj) override + { + if (doUIDsMatch (iid, Steinberg::Linux::IRunLoop::iid)) + { + *obj = &runLoop; + return kResultTrue; + } + + jassertfalse; + *obj = nullptr; + + return Steinberg::kNotImplemented; + } + #else JUCE_DECLARE_VST3_COM_QUERY_METHODS + #endif + + JUCE_DECLARE_VST3_COM_REF_METHODS void paint (Graphics& g) override { @@ -1205,7 +1370,7 @@ struct VST3PluginWindow : public AudioProcessorEditor, SetWindowPos (pluginHandle, 0, pos.x, pos.y, rect.getWidth(), rect.getHeight(), isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW); - #elif JUCE_MAC + #else embeddedComponent.setBounds (getLocalBounds()); #endif @@ -1223,7 +1388,7 @@ struct VST3PluginWindow : public AudioProcessorEditor, SetWindowPos (pluginHandle, 0, pos.x, pos.y, rect.getWidth(), rect.getHeight(), isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW); - #elif JUCE_MAC + #else embeddedComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight()); #endif } @@ -1233,6 +1398,8 @@ struct VST3PluginWindow : public AudioProcessorEditor, } } + using ComponentMovementWatcher::componentMovedOrResized; + void componentVisibilityChanged() override { attachPluginWindow(); @@ -1243,10 +1410,12 @@ struct VST3PluginWindow : public AudioProcessorEditor, componentMovedOrResized (true, true); } - #if ! JUCE_MAC + using ComponentMovementWatcher::componentVisibilityChanged; + + #if JUCE_WINDOWS || JUCE_LINUX void nativeScaleFactorChanged (double newScaleFactor) override { - if (pluginHandle == nullptr || approximatelyEqual ((float) newScaleFactor, nativeScaleFactor)) + if (pluginHandle == 0 || approximatelyEqual ((float) newScaleFactor, nativeScaleFactor)) return; nativeScaleFactor = (float) newScaleFactor; @@ -1291,7 +1460,11 @@ private: void attachPluginWindow() { - if (pluginHandle == nullptr) + #if JUCE_MAC + if (pluginHandle == nil) + #else + if (pluginHandle == 0) + #endif { #if JUCE_WINDOWS if (auto* topComp = getTopLevelComponent()) @@ -1300,25 +1473,33 @@ private: pluginHandle = (HandleFormat) peer->getNativeHandle(); nativeScaleFactor = (float) peer->getPlatformScaleFactor(); } - #elif JUCE_MAC + #else embeddedComponent.setBounds (getLocalBounds()); addAndMakeVisible (embeddedComponent); - pluginHandle = (NSView*) embeddedComponent.getView(); + #if JUCE_MAC + pluginHandle = (HandleFormat) embeddedComponent.getView(); + jassert (pluginHandle != nil); + #elif JUCE_LINUX + pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID(); + jassert (pluginHandle != 0); + #endif #endif - if (pluginHandle != nullptr) - warnOnFailure (view->attached (pluginHandle, defaultVST3WindowType)); - else - jassertfalse; - - #if ! JUCE_MAC - if (auto* topPeer = getTopLevelComponent()->getPeer()) - { - nativeScaleFactor = 1.0f; // force update - nativeScaleFactorChanged ((float) topPeer->getPlatformScaleFactor()); - } + #if JUCE_MAC + if (pluginHandle != nil) + #else + if (pluginHandle != 0) #endif + warnOnFailure (view->attached ((void*) pluginHandle, defaultVST3WindowType)); } + + #if ! JUCE_MAC + if (auto* topPeer = getTopLevelComponent()->getPeer()) + { + nativeScaleFactor = 1.0f; // force update + nativeScaleFactorChanged ((float) topPeer->getPlatformScaleFactor()); + } + #endif } #if ! JUCE_MAC @@ -1350,6 +1531,9 @@ private: #elif JUCE_MAC AutoResizingNSViewComponentWithParent embeddedComponent; using HandleFormat = NSView*; + #elif JUCE_LINUX + XEmbedComponent embeddedComponent { true, false }; + using HandleFormat = Window; #else Component embeddedComponent; using HandleFormat = void*; @@ -3141,7 +3325,7 @@ bool VST3PluginFormat::fileMightContainThisPluginType (const String& fileOrIdent auto f = File::createFileWithoutCheckingPath (fileOrIdentifier); return f.hasFileExtension (".vst3") - #if JUCE_MAC + #if JUCE_MAC || JUCE_LINUX && f.exists(); #else && f.existsAsFile(); @@ -3199,7 +3383,7 @@ FileSearchPath VST3PluginFormat::getDefaultLocationsToSearch() #elif JUCE_MAC return FileSearchPath ("/Library/Audio/Plug-Ins/VST3;~/Library/Audio/Plug-Ins/VST3"); #else - return FileSearchPath(); + return FileSearchPath ("/usr/lib/vst3/;/usr/local/lib/vst3/;~/.vst3/"); #endif } diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h index c795449fd1..e57cfaa8c7 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h @@ -19,7 +19,7 @@ namespace juce { -#if (JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS)) || DOXYGEN +#if (JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX)) || DOXYGEN /** Implements a plugin format for VST3s. diff --git a/modules/juce_audio_processors/juce_audio_processors.cpp b/modules/juce_audio_processors/juce_audio_processors.cpp index aa1e360f9d..c468c944c1 100644 --- a/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/modules/juce_audio_processors/juce_audio_processors.cpp @@ -40,13 +40,14 @@ #endif #endif -#if JUCE_PLUGINHOST_VST && JUCE_LINUX +#if (JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_VST3) && JUCE_LINUX #include #include + #include #undef KeyPress #endif -#if ! JUCE_WINDOWS && ! JUCE_MAC +#if ! JUCE_WINDOWS && ! JUCE_MAC && ! JUCE_LINUX #undef JUCE_PLUGINHOST_VST3 #define JUCE_PLUGINHOST_VST3 0 #endif diff --git a/modules/juce_events/native/juce_linux_Messaging.cpp b/modules/juce_events/native/juce_linux_Messaging.cpp index 1ef5c5f7f4..cdd6ae0f73 100644 --- a/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/modules/juce_events/native/juce_linux_Messaging.cpp @@ -211,6 +211,12 @@ public: poll (&pfds.front(), static_cast (pfds.size()), timeoutMs); } + std::vector>> getFdReadCallbacks() + { + const ScopedLock sl (lock); + return fdReadCallbacks; + } + //============================================================================== JUCE_DECLARE_SINGLETON (InternalRunLoop, false) @@ -318,3 +324,14 @@ void LinuxEventLoop::unregisterFdCallback (int fd) } } // namespace juce + +JUCE_API std::vector>> getFdReadCallbacks() +{ + using namespace juce; + + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + return runLoop->getFdReadCallbacks(); + + jassertfalse; + return {}; +} diff --git a/modules/juce_gui_extra/embedding/juce_XEmbedComponent.h b/modules/juce_gui_extra/embedding/juce_XEmbedComponent.h index c4c6500ba1..85a7d02eb7 100644 --- a/modules/juce_gui_extra/embedding/juce_XEmbedComponent.h +++ b/modules/juce_gui_extra/embedding/juce_XEmbedComponent.h @@ -86,6 +86,9 @@ public: */ unsigned long getHostWindowID(); + /** Removes the client window from the host. */ + void removeClient(); + protected: //============================================================================== /** @internal */ diff --git a/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp b/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp index 58cead6635..150ab0b8af 100644 --- a/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp +++ b/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp @@ -345,6 +345,8 @@ private: X11Symbols::getInstance()->xReparentWindow (dpy, client, root, 0, 0); client = 0; + + X11Symbols::getInstance()->xSync (dpy, False); } } @@ -681,6 +683,7 @@ void XEmbedComponent::focusGained (FocusChangeType changeType) { pimpl->focu void XEmbedComponent::focusLost (FocusChangeType changeType) { pimpl->focusLost (changeType); } void XEmbedComponent::broughtToFront() { pimpl->broughtToFront(); } unsigned long XEmbedComponent::getHostWindowID() { return pimpl->getHostWindowID(); } +void XEmbedComponent::removeClient() { pimpl->setClient (0, true); } //============================================================================== bool juce_handleXEmbedEvent (ComponentPeer* p, void* e)