diff --git a/src/common/linking.cpp b/src/common/linking.cpp index 16c2744e..c9de990b 100644 --- a/src/common/linking.cpp +++ b/src/common/linking.cpp @@ -20,7 +20,9 @@ #include -std::string get_this_file_location() { +namespace fs = ghc::filesystem; + +fs::path get_this_file_location() { // We'll try to find the library this function was defined in. When called // from a copy of `libyabridge-*.so` this will return that library. Because // the chainloader libraries load the plugin libraries from fixed locations, diff --git a/src/common/linking.h b/src/common/linking.h index 20f50e20..4eb59e90 100644 --- a/src/common/linking.h +++ b/src/common/linking.h @@ -19,7 +19,7 @@ // This header is completely standalone so the chainloading libraries can // retrieve their file path without pulling in a lot of additional dependencies -#include +#include /** * Return a path to this `.so` file. This can be used to find out from where @@ -29,4 +29,4 @@ * library using the provided exported functions since they can't detect the * path themselves. */ -std::string get_this_file_location(); +ghc::filesystem::path get_this_file_location(); diff --git a/src/plugin/bridges/common.h b/src/plugin/bridges/common.h index 5238d1d9..5f2610a4 100644 --- a/src/plugin/bridges/common.h +++ b/src/plugin/bridges/common.h @@ -66,23 +66,26 @@ class PluginBridge { * `connect_sockets_guarded()` themselves after their initialization list. * * @param plugin_type The type of the plugin we're handling. - * @param plugin_path The path to the plugin. For VST2 plugins this is the - * path to the `.dll` file, and for VST3 plugins this is the path to the - * module (either a `.vst3` DLL file or a bundle). + * @param plugin_path The path to the **native** plugin library `.so` file. + * This is used to determine the path to the Windows plugin library we + * should load. * @param create_socket_instance A function to create a socket instance. * Using a lambda here feels wrong, but I can't think of a better * solution right now. * * @throw std::runtime_error Thrown when the Wine plugin host could not be - * found, or if it could not locate and load a VST3 module. + * found, or if it could not locate and load a corresponding Windows + * plugin library. */ template < invocable_returning F> - PluginBridge(PluginType plugin_type, F&& create_socket_instance) + PluginBridge(PluginType plugin_type, + const ghc::filesystem::path& plugin_path, + F&& create_socket_instance) // This is still correct for VST3 plugins because we can configure an // entire directory (the module's bundle) at once - : config_(load_config_for(get_this_file_location())), - info_(plugin_type, config_.vst3_prefer_32bit), + : config_(load_config_for(plugin_path)), + info_(plugin_type, plugin_path, config_.vst3_prefer_32bit), io_context_(), sockets_(create_socket_instance(io_context_, info_)), generic_logger_(Logger::create_from_environment( @@ -179,7 +182,7 @@ class PluginBridge { "recommended to set up proper realtime privileges " "for your user. Check the readme for " "instructions on how to do that.", - false); + std::nullopt); } else { init_msg << "'yes'" << std::endl; } @@ -222,7 +225,7 @@ class PluginBridge { "realtime privileges for your user, and some plugins " "may cause your DAW to crash until you fix this. Check " "the readme for instructions on how to do that.", - false); + std::nullopt); } } else { init_msg @@ -390,7 +393,7 @@ class PluginBridge { "went wrong. You may need to rerun your DAW from a " "terminal and restart the plugin scanning process to " "see the error.", - true); + info_.native_library_path_); std::terminate(); } @@ -426,7 +429,7 @@ class PluginBridge { "Version mismatch", "If you just updated yabridge, then you may need " "to rerun 'yabridgectl sync' first to update your plugins.", - true); + info_.native_library_path_); } } diff --git a/src/plugin/bridges/vst2.cpp b/src/plugin/bridges/vst2.cpp index a2c4392f..76ab7338 100644 --- a/src/plugin/bridges/vst2.cpp +++ b/src/plugin/bridges/vst2.cpp @@ -35,9 +35,11 @@ Vst2PluginBridge& get_bridge_instance(const AEffect& plugin) noexcept { return *static_cast(plugin.ptr3); } -Vst2PluginBridge::Vst2PluginBridge(audioMasterCallback host_callback) +Vst2PluginBridge::Vst2PluginBridge(const ghc::filesystem::path& plugin_path, + audioMasterCallback host_callback) : PluginBridge( PluginType::vst2, + plugin_path, [](asio::io_context& io_context, const PluginInfo& info) { return Vst2Sockets( io_context, diff --git a/src/plugin/bridges/vst2.h b/src/plugin/bridges/vst2.h index 0e0f2bc3..5f897a31 100644 --- a/src/plugin/bridges/vst2.h +++ b/src/plugin/bridges/vst2.h @@ -40,13 +40,19 @@ class Vst2PluginBridge : PluginBridge> { * Initializes the Wine plugin bridge. This sets up the sockets for event * handling. * + * @param plugin_path The path to the **native** plugin library `.so` file. + * This is used to determine the path to the Windows plugin library we + * should load. For directly loaded bridges this should be + * `get_this_file_location()`. Chainloaded plugins should use the path of + * the chainloader copy instead. * @param host_callback The callback function passed to the VST plugin by * the host. * * @throw std::runtime_error Thrown when the VST host could not be found, or * if it could not locate and load a VST .dll file. */ - Vst2PluginBridge(audioMasterCallback host_callback); + Vst2PluginBridge(const ghc::filesystem::path& plugin_path, + audioMasterCallback host_callback); /** * Terminate the Wine plugin host process and drop all work when the module diff --git a/src/plugin/bridges/vst3.cpp b/src/plugin/bridges/vst3.cpp index 1e35d32f..00c9a2d5 100644 --- a/src/plugin/bridges/vst3.cpp +++ b/src/plugin/bridges/vst3.cpp @@ -24,9 +24,10 @@ using namespace std::literals::string_literals; -Vst3PluginBridge::Vst3PluginBridge() +Vst3PluginBridge::Vst3PluginBridge(const ghc::filesystem::path& plugin_path) : PluginBridge( PluginType::vst3, + plugin_path, [](asio::io_context& io_context, const PluginInfo& info) { return Vst3Sockets( io_context, diff --git a/src/plugin/bridges/vst3.h b/src/plugin/bridges/vst3.h index f6099b2c..34efa5f7 100644 --- a/src/plugin/bridges/vst3.h +++ b/src/plugin/bridges/vst3.h @@ -52,10 +52,16 @@ class Vst3PluginBridge : PluginBridge> { * Initializes the VST3 module by starting and setting up communicating with * the Wine plugin host. * + * @param plugin_path The path to the **native** plugin library `.so` file. + * This is used to determine the path to the Windows plugin library we + * should load. For directly loaded bridges this should be + * `get_this_file_location()`. Chainloaded plugins should use the path of + * the chainloader copy instead. + * * @throw std::runtime_error Thrown when the Wine plugin host could not be * found, or if it could not locate and load a VST3 module. */ - explicit Vst3PluginBridge(); + explicit Vst3PluginBridge(const ghc::filesystem::path& plugin_path); /** * Terminate the Wine plugin host process and drop all work when the module diff --git a/src/plugin/utils.cpp b/src/plugin/utils.cpp index 88ab1854..887058f1 100644 --- a/src/plugin/utils.cpp +++ b/src/plugin/utils.cpp @@ -43,9 +43,11 @@ fs::path normalize_plugin_path(const fs::path& windows_library_path, std::variant find_wine_prefix( fs::path windows_plugin_path); -PluginInfo::PluginInfo(PluginType plugin_type, bool prefer_32bit_vst3) +PluginInfo::PluginInfo(PluginType plugin_type, + const ghc::filesystem::path& plugin_path, + bool prefer_32bit_vst3) : plugin_type_(plugin_type), - native_library_path_(get_this_file_location()), + native_library_path_(plugin_path), // As explained in the docstring, this is the actual Windows library. For // VST3 plugins that come in a module we should be loading that module // instead of the `.vst3` file within in, which is where @@ -423,23 +425,24 @@ Configuration load_config_for(const fs::path& yabridge_path) { bool send_notification(const std::string& title, const std::string body, - bool append_origin) { + std::optional origin) { // I think there's a zero chance that we're going to call this function with // anything that even somewhat resembles HTML, but we should still do a // basic XML escape anyways. std::ostringstream formatted_body; formatted_body << xml_escape(body); - // If possible, append the path to this library file to the message. - if (append_origin) { + // If the path to the current library file is provided, then we'll append + // the path to that library file to the message. In earlier versions we + // would detect the library path right here, but that will not work with + // chainloaded plugins as they will load the actual plugin libraries from + // fixed locations. + if (origin) { try { - const fs::path this_library = get_this_file_location(); formatted_body << "\n" << "Source: " - << xml_escape(this_library.filename().string()) + << url_encode_path(origin->parent_path().string()) + << "\">" << xml_escape(origin->filename().string()) << ""; } catch (const std::system_error&) { // I don't think this can fail in the way we're using it, but the diff --git a/src/plugin/utils.h b/src/plugin/utils.h index c6840a98..ae05ee52 100644 --- a/src/plugin/utils.h +++ b/src/plugin/utils.h @@ -58,6 +58,9 @@ struct PluginInfo { * * @param plugin_type The type of the plugin we're going to load. The * detection works slightly differently depending on the plugin type. + * @param plugin_path The path to the **native** plugin library `.so` file. + * This is used to determine the path to the Windows plugin library we + * should load. * @param prefer_32bit_vst3 If there's both a 64-bit and a 32-bit Windows * VST3 module in the same bundle, then setting this to true will cause * the 32-bit version to be used instead of the 64-bit version. @@ -66,7 +69,9 @@ struct PluginInfo { * plugin. The error message contains a human readable description of what * went wrong. */ - PluginInfo(PluginType plugin_type, bool prefer_32bit_vst3 = false); + PluginInfo(PluginType plugin_type, + const ghc::filesystem::path& plugin_path, + bool prefer_32bit_vst3 = false); /** * Create the environment for the plugin host based on `wine_prefix_`. If @@ -267,15 +272,16 @@ Configuration load_config_for(const ghc::filesystem::path& yabridge_path); * @param body The message to display. This can contain line feeds, and it any * HTML tags and XML escape sequences will be automatically escaped. The * message can also be empty. - * @param Whether to append 'Source: ' to the body, where `` is - * a hyperlink to the directory this library is placed in. + * @param origin If this is set to the current plugin's path, then the + * notification will append a 'Source: ' hyperlink to the body so the + * user can more easily navigate to the plugin's path. * * @return Whether the notification was sent. This will be false if * `notify-send` is not available. */ bool send_notification(const std::string& title, const std::string body, - bool append_origin); + std::optional origin); /** * Starting from the starting file or directory, go up in the directory diff --git a/src/plugin/vst2-plugin.cpp b/src/plugin/vst2-plugin.cpp index 674bcbe0..dfb89ea8 100644 --- a/src/plugin/vst2-plugin.cpp +++ b/src/plugin/vst2-plugin.cpp @@ -24,6 +24,8 @@ using namespace std::literals::string_literals; +namespace fs = ghc::filesystem; + // The main entry point for VST2 plugins should be called `VSTPluginMain``. The // other one exist for legacy reasons since some old hosts might still use them // (EnergyXT being the only known host on Linux that uses the `main` entry @@ -40,11 +42,15 @@ using namespace std::literals::string_literals; */ extern "C" YABRIDGE_EXPORT AEffect* VSTPluginMain( audioMasterCallback host_callback) { + // FIXME: Update this for the chainloading + const fs::path plugin_path = get_this_file_location(); + try { // This is the only place where we have to use manual memory management. // The bridge's destructor is called when the `effClose` opcode is // received. - Vst2PluginBridge* bridge = new Vst2PluginBridge(host_callback); + Vst2PluginBridge* bridge = + new Vst2PluginBridge(plugin_path, host_callback); return &bridge->plugin_; } catch (const std::exception& error) { @@ -62,7 +68,7 @@ extern "C" YABRIDGE_EXPORT AEffect* VSTPluginMain( error.what() + "\nIf you just updated yabridge, then you may need to rerun " "'yabridgectl sync' first to update your plugins."s, - true); + plugin_path); return nullptr; } diff --git a/src/plugin/vst3-plugin.cpp b/src/plugin/vst3-plugin.cpp index bff4eb83..e8b2a733 100644 --- a/src/plugin/vst3-plugin.cpp +++ b/src/plugin/vst3-plugin.cpp @@ -29,6 +29,10 @@ using namespace std::literals::string_literals; // NOLINTNEXTLINE(bugprone-suspicious-include) #include +using namespace std::literals::string_literals; + +namespace fs = ghc::filesystem; + // Because VST3 plugins consist of completely independent components that have // to be initialized and connected by the host, hosting a VST3 plugin through // yabridge works very differently from hosting VST2 plugin. Even with @@ -48,8 +52,11 @@ std::unique_ptr bridge; bool InitModule() { assert(!bridge); + // FIXME: Update this for the chainloading + const fs::path plugin_path = get_this_file_location(); + try { - bridge = std::make_unique(); + bridge = std::make_unique(plugin_path); return true; } catch (const std::exception& error) { @@ -67,7 +74,7 @@ bool InitModule() { error.what() + "\nIf you just updated yabridge, then you may need to rerun " "'yabridgectl sync' first to update your plugins."s, - true); + plugin_path); return false; }