diff --git a/source/modules/distrho/DistrhoPluginUtils.hpp b/source/modules/distrho/DistrhoPluginUtils.hpp index aea47cc26..c3fa3ce67 100644 --- a/source/modules/distrho/DistrhoPluginUtils.hpp +++ b/source/modules/distrho/DistrhoPluginUtils.hpp @@ -38,33 +38,36 @@ START_NAMESPACE_DISTRHO const char* getBinaryFilename(); /** - Get an OS-specific directory intended to store persistent configuration data about the plugin.@n - Calling this function will ensure the dictory exists on the filesystem.@n - The returned path already includes DISTRHO_PLUGIN_NAME and final OS separator. -*/ -const char* getConfigDir(); + Get a string representation of the current plugin format we are building against.@n + This can be "AudioUnit", "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2", "VST3" or "CLAP".@n + This string is purely informational and must not be used to tweak plugin behaviour. -/** - Get an OS-specific directory intended to store "documents" for the plugin.@n - Calling this function will ensure the dictory exists on the filesystem.@n - The returned path already includes DISTRHO_PLUGIN_NAME and final OS separator. + @note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT. */ -const char* getDocumentsDir(); +const char* getPluginFormatName() noexcept; /** - Get the user "home" directory.@n - This function is provided only for convenience, it should not be needed under normal circunstances. + List of supported OS-specific directories to be used for getSpecialDir. */ -const char* getHomeDir(); +enum SpecialDir { + /** The user "home" directory */ + kSpecialDirHome, + /** Directory intended to store persistent configuration data (in general) */ + kSpecialDirConfig, + /** Directory intended to store persistent configuration data for the current plugin */ + kSpecialDirConfigForPlugin, + /** Directory intended to store "documents" (in general) */ + kSpecialDirDocuments, + /** Directory intended to store "documents" for the current plugin */ + kSpecialDirDocumentsForPlugin, +}; /** - Get a string representation of the current plugin format we are building against.@n - This can be "AudioUnit", "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2" or "VST3" or "CLAP".@n - This string is purely informational and must not be used to tweak plugin behaviour. - - @note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT. + Get an OS-specific directory.@n + Calling this function will ensure the dictory exists on the filesystem.@n + The returned path always includes a final OS separator. */ -const char* getPluginFormatName() noexcept; +const char* getSpecialDir(SpecialDir dir); /** Get the path to where resources are stored within the plugin bundle.@n @@ -82,7 +85,13 @@ const char* getPluginFormatName() noexcept; The other non-mentioned formats do not support bundles.@n @note For CLAP and VST2 on non-macOS systems, this assumes you have your plugin inside a dedicated directory - rather than only shipping with the binary (e.g. /myplugin.dll) + rather than only shipping with the binary, like so: + @code + + myplugin.vst/ + - myplugin.dll + + resources/ + - image1.png + @endcode */ const char* getResourcePath(const char* bundlePath) noexcept; diff --git a/source/modules/distrho/DistrhoUIMain.cpp b/source/modules/distrho/DistrhoUIMain.cpp index 194e9844f..efc5d9f7e 100644 --- a/source/modules/distrho/DistrhoUIMain.cpp +++ b/source/modules/distrho/DistrhoUIMain.cpp @@ -23,11 +23,7 @@ #if DISTRHO_PLUGIN_HAS_UI -#if defined(DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT) -# if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS -# warning Using single/monolithic LV2 target while DISTRHO_PLUGIN_WANT_DIRECT_ACCESS is 0 -# endif -#elif defined(DISTRHO_PLUGIN_TARGET_AU) +#if defined(DISTRHO_PLUGIN_TARGET_AU) # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 # import "src/DistrhoUIAU.mm" #elif defined(DISTRHO_PLUGIN_TARGET_CARLA) @@ -40,7 +36,13 @@ # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 0 # include "src/DistrhoUIDSSI.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_LV2) -# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +# if defined(DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT) +# if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +# warning Using single/monolithic LV2 target while DISTRHO_PLUGIN_WANT_DIRECT_ACCESS is 0 +# endif +# else +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +# endif # include "src/DistrhoUILV2.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_VST2) # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 diff --git a/source/modules/distrho/extra/String.hpp b/source/modules/distrho/extra/String.hpp index 8a34e981b..b7fda9273 100644 --- a/source/modules/distrho/extra/String.hpp +++ b/source/modules/distrho/extra/String.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -1030,13 +1030,14 @@ public: } // we have some data ourselves, reallocate to add the new stuff - char* const newBuf = static_cast(std::realloc(fBuffer, fBufferLen + strBufLen + 1)); + char* const newBuf = static_cast(std::realloc(fBufferAlloc ? fBuffer : nullptr, fBufferLen + strBufLen + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this); std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); fBuffer = newBuf; fBufferLen += strBufLen; + fBufferAlloc = true; return *this; } diff --git a/source/modules/distrho/src/DistrhoUtils.cpp b/source/modules/distrho/src/DistrhoUtils.cpp index 37083c920..ee0f5efa8 100644 --- a/source/modules/distrho/src/DistrhoUtils.cpp +++ b/source/modules/distrho/src/DistrhoUtils.cpp @@ -82,65 +82,122 @@ const char* getBinaryFilename() return filename; } -const char* getConfigDir() +const char* getPluginFormatName() noexcept +{ +#if defined(DISTRHO_PLUGIN_TARGET_AU) + return "AudioUnit"; +#elif defined(DISTRHO_PLUGIN_TARGET_CARLA) + return "Carla"; +#elif defined(DISTRHO_PLUGIN_TARGET_JACK) + #if defined(DISTRHO_OS_WASM) + return "Wasm/Standalone"; + #elif defined(HAVE_JACK) + return "JACK/Standalone"; + #else + return "Standalone"; + #endif +#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA) + return "LADSPA"; +#elif defined(DISTRHO_PLUGIN_TARGET_DSSI) + return "DSSI"; +#elif defined(DISTRHO_PLUGIN_TARGET_LV2) + return "LV2"; +#elif defined(DISTRHO_PLUGIN_TARGET_VST2) + return "VST2"; +#elif defined(DISTRHO_PLUGIN_TARGET_VST3) + return "VST3"; +#elif defined(DISTRHO_PLUGIN_TARGET_CLAP) + return "CLAP"; +#elif defined(DISTRHO_PLUGIN_TARGET_STATIC) && defined(DISTRHO_PLUGIN_TARGET_STATIC_NAME) + return DISTRHO_PLUGIN_TARGET_STATIC_NAME; +#else + return "Unknown"; +#endif +} + +#ifndef DISTRHO_OS_WINDOWS +static inline +void _createDirIfNeeded(const char* const dir) +{ + if (access(dir, F_OK) != 0) + mkdir(dir, 0755); +} +#endif + +static const char* _getDocumentsDir(); +static const char* _getDocumentsDirForPlugin(); +static const char* _getHomeDir(); + +static const char* _getConfigDir() { #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) - return getDocumentsDir(); + return _getDocumentsDir(); #else static String dir; if (dir.isEmpty()) { if (const char* const xdgEnv = getenv("XDG_CONFIG_HOME")) + { dir = xdgEnv; + if (dir.isNotEmpty() && ! dir.endsWith('/')) + dir += "/"; + } + if (dir.isEmpty()) { - dir = getHomeDir(); - dir += "/.config"; + dir = _getHomeDir(); + dir += ".config/"; } - // ensure main config dir exists - if (access(dir, F_OK) != 0) - mkdir(dir, 0755); + _createDirIfNeeded(dir); + } + + return dir; + #endif +} + +static const char* _getConfigDirForPlugin() +{ + #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) + return _getDocumentsDirForPlugin(); + #else + static String dir; - // and also our custom subdir - dir += "/" DISTRHO_PLUGIN_NAME "/"; - if (access(dir, F_OK) != 0) - mkdir(dir, 0755); + if (dir.isEmpty()) + { + dir = _getConfigDir(); + dir += DISTRHO_PLUGIN_NAME DISTRHO_OS_SEP_STR; + _createDirIfNeeded(dir); } return dir; #endif } -const char* getDocumentsDir() +static const char* _getDocumentsDir() { static String dir; if (dir.isEmpty()) { #if defined(DISTRHO_OS_MAC) - dir = getHomeDir(); - dir += "/Documents/" DISTRHO_PLUGIN_NAME "/"; + dir = _getHomeDir(); + dir += "Documents/"; #elif defined(DISTRHO_OS_WASM) - dir = getHomeDir(); - dir += "/"; + dir = _getHomeDir(); #elif defined(DISTRHO_OS_WINDOWS) WCHAR wpath[MAX_PATH]; if (SHGetFolderPathW(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, wpath) == S_OK) { CHAR apath[MAX_PATH]; if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, apath, MAX_PATH, nullptr, nullptr) != 0) - { dir = apath; - dir += "\\" DISTRHO_PLUGIN_NAME "\\"; - wcscat(wpath, L"\\" DISTRHO_PLUGIN_NAME "\\"); - } } #else - String xdgDirsConfigPath(getConfigDir()); - xdgDirsConfigPath += "/user-dirs.dirs"; + String xdgDirsConfigPath(_getConfigDir()); + xdgDirsConfigPath += "user-dirs.dirs"; if (FILE* const f = std::fopen(xdgDirsConfigPath, "r")) { @@ -178,17 +235,13 @@ const char* getDocumentsDir() if (sdir.startsWith("$HOME")) { - dir = getHomeDir(); - dir += sdir.buffer() + 5; + dir = _getHomeDir(); + dir += sdir.buffer() + 6; } else { dir = sdir; } - - // ensure main config dir exists - if (access(dir, F_OK) != 0) - mkdir(dir, 0755); } } @@ -202,28 +255,46 @@ const char* getDocumentsDir() // ${XDG_CONFIG_HOME}/user-dirs.dirs does not exist or has bad data if (dir.isEmpty()) - { - dir = getDocumentsDir(); - dir += DISTRHO_PLUGIN_NAME "/"; - } + dir = _getDocumentsDir(); + + _createDirIfNeeded(dir); #endif - // ensure our custom subdir exists - if (dir.isNotEmpty()) + if (dir.isNotEmpty() && ! dir.endsWith(DISTRHO_OS_SEP)) + dir += DISTRHO_OS_SEP_STR; + } + + return dir; +} + +static const char* _getDocumentsDirForPlugin() +{ + static String dir; + + if (dir.isEmpty()) + { + #ifdef DISTRHO_OS_WINDOWS + WCHAR wpath[MAX_PATH]; + if (SHGetFolderPathW(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, wpath) == S_OK) { - #ifdef DISTRHO_OS_WINDOWS + wcscat(wpath, L"\\" DISTRHO_PLUGIN_NAME "\\"); _wmkdir(wpath); - #else - if (access(dir, F_OK) != 0) - mkdir(dir, 0755); - #endif + + CHAR apath[MAX_PATH]; + if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, apath, MAX_PATH, nullptr, nullptr) != 0) + dir = apath; } + #else + dir = _getDocumentsDir(); + dir += DISTRHO_PLUGIN_NAME DISTRHO_OS_SEP_STR; + _createDirIfNeeded(dir); + #endif } return dir; } -const char* getHomeDir() +static const char* _getHomeDir() { static String dir; @@ -245,45 +316,32 @@ const char* getHomeDir() if (struct passwd* const pwd = getpwuid(getuid())) dir = pwd->pw_dir; - if (dir.isNotEmpty() && ! dir.endsWith('/')) - dir += "/"; + _createDirIfNeeded(dir); #endif + + if (dir.isNotEmpty() && ! dir.endsWith(DISTRHO_OS_SEP)) + dir += DISTRHO_OS_SEP_STR; } return dir; } -const char* getPluginFormatName() noexcept +const char* getSpecialDir(const SpecialDir dir) { -#if defined(DISTRHO_PLUGIN_TARGET_AU) - return "AudioUnit"; -#elif defined(DISTRHO_PLUGIN_TARGET_CARLA) - return "Carla"; -#elif defined(DISTRHO_PLUGIN_TARGET_JACK) - #if defined(DISTRHO_OS_WASM) - return "Wasm/Standalone"; - #elif defined(HAVE_JACK) - return "JACK/Standalone"; - #else - return "Standalone"; - #endif -#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA) - return "LADSPA"; -#elif defined(DISTRHO_PLUGIN_TARGET_DSSI) - return "DSSI"; -#elif defined(DISTRHO_PLUGIN_TARGET_LV2) - return "LV2"; -#elif defined(DISTRHO_PLUGIN_TARGET_VST2) - return "VST2"; -#elif defined(DISTRHO_PLUGIN_TARGET_VST3) - return "VST3"; -#elif defined(DISTRHO_PLUGIN_TARGET_CLAP) - return "CLAP"; -#elif defined(DISTRHO_PLUGIN_TARGET_STATIC) && defined(DISTRHO_PLUGIN_TARGET_STATIC_NAME) - return DISTRHO_PLUGIN_TARGET_STATIC_NAME; -#else - return "Unknown"; -#endif + switch (dir) + { + case kSpecialDirHome: + return _getHomeDir(); + case kSpecialDirConfig: + return _getConfigDir(); + case kSpecialDirConfigForPlugin: + return _getConfigDirForPlugin(); + case kSpecialDirDocuments: + return _getDocumentsDir(); + case kSpecialDirDocumentsForPlugin: + return _getDocumentsDirForPlugin(); + } + return nullptr; } const char* getResourcePath(const char* const bundlePath) noexcept