| @@ -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) | |||
| @@ -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<std::pair<int, std::function<void(int)>>> 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<JuceVST3EditController> owner; | |||
| AudioProcessor& pluginInstance; | |||
| @@ -1497,7 +1537,22 @@ private: | |||
| #if JUCE_WINDOWS | |||
| WindowsHooks hooks; | |||
| #elif JUCE_LINUX | |||
| 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 | |||
| //============================================================================== | |||
| @@ -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<int> (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<CFBundleRef> 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<CFBundleRef> 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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -24,8 +24,6 @@ | |||
| #include "../utility/juce_CheckSettingMacros.h" | |||
| #include "juce_IncludeModuleHeaders.h" | |||
| using namespace juce; | |||
| namespace juce | |||
| { | |||
| @@ -40,7 +38,7 @@ std::function<bool(AudioProcessor&)> 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(); | |||
| @@ -35,7 +35,7 @@ void AudioPluginFormatManager::addDefaultFormats() | |||
| jassert (dynamic_cast<VSTPluginFormat*> (format) == nullptr); | |||
| #endif | |||
| #if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS) | |||
| #if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) | |||
| jassert (dynamic_cast<VST3PluginFormat*> (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 | |||
| @@ -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; | |||
| @@ -148,7 +148,6 @@ bool FUID::generate () | |||
| return false; | |||
| #else | |||
| #warning implement me! | |||
| return false; | |||
| #endif | |||
| } | |||
| @@ -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 | |||
| @@ -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 <base/source/fobject.cpp> | |||
| #include <base/source/fstreamer.cpp> | |||
| #include <base/source/fstring.cpp> | |||
| #if VST_VERSION >= 0x030608 | |||
| #include <base/thread/source/flock.cpp> | |||
| #include <pluginterfaces/base/coreiids.cpp> | |||
| #else | |||
| #include <base/source/flock.cpp> | |||
| #endif | |||
| #if VST_VERSION >= 0x030608 | |||
| #include <base/thread/source/flock.cpp> | |||
| #include <pluginterfaces/base/coreiids.cpp> | |||
| #else | |||
| #include <base/source/flock.cpp> | |||
| #endif | |||
| #include <base/source/updatehandler.cpp> | |||
| #include <pluginterfaces/base/conststringtable.cpp> | |||
| #include <pluginterfaces/base/funknown.cpp> | |||
| @@ -118,9 +124,10 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnon-virtual-dtor", | |||
| #include <public.sdk/source/vst/vstparameters.cpp> | |||
| #include <public.sdk/source/vst/vstpresetfile.cpp> | |||
| #include <public.sdk/source/vst/hosting/hostclasses.cpp> | |||
| #if VST_VERSION >= 0x03060c // 3.6.12 | |||
| #include <public.sdk/source/vst/hosting/pluginterfacesupport.cpp> | |||
| #endif | |||
| #if VST_VERSION >= 0x03060c // 3.6.12 | |||
| #include <public.sdk/source/vst/hosting/pluginterfacesupport.cpp> | |||
| #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 | |||
| @@ -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<TimerCaller> (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<Linux::FileDescriptor, Linux::IEventHandler*> 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<std::unique_ptr<TimerCaller>> 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 | |||
| } | |||
| @@ -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. | |||
| @@ -40,13 +40,14 @@ | |||
| #endif | |||
| #endif | |||
| #if JUCE_PLUGINHOST_VST && JUCE_LINUX | |||
| #if (JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_VST3) && JUCE_LINUX | |||
| #include <X11/Xlib.h> | |||
| #include <X11/Xutil.h> | |||
| #include <sys/utsname.h> | |||
| #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 | |||
| @@ -211,6 +211,12 @@ public: | |||
| poll (&pfds.front(), static_cast<nfds_t> (pfds.size()), timeoutMs); | |||
| } | |||
| std::vector<std::pair<int, std::function<void(int)>>> 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<std::pair<int, std::function<void(int)>>> getFdReadCallbacks() | |||
| { | |||
| using namespace juce; | |||
| if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) | |||
| return runLoop->getFdReadCallbacks(); | |||
| jassertfalse; | |||
| return {}; | |||
| } | |||
| @@ -86,6 +86,9 @@ public: | |||
| */ | |||
| unsigned long getHostWindowID(); | |||
| /** Removes the client window from the host. */ | |||
| void removeClient(); | |||
| protected: | |||
| //============================================================================== | |||
| /** @internal */ | |||
| @@ -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) | |||