From 994f3c9e3853684cfbe356d83c2c1cc909c9905a Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 17 May 2020 15:07:20 +0200 Subject: [PATCH] Add a plugin group host application --- README.md | 6 +++ meson.build | 54 +++++++++++++++++---- src/common/config/config.h.in | 29 ++++++++--- src/common/config/meson.build | 6 ++- src/plugin/utils.cpp | 5 +- src/wine-host/group-host.cpp | 81 +++++++++++++++++++++++++++++++ src/wine-host/individual-host.cpp | 15 ++++-- 7 files changed, 171 insertions(+), 25 deletions(-) create mode 100644 src/wine-host/group-host.cpp diff --git a/README.md b/README.md index cd4ab42e..68e03ae2 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,10 @@ yabridge is also able to load 32-bit VST plugins. The installation procedure for automatically detect whether a plugin is 32-bit or 64-bit on startup and it will handle it accordingly. +### Plugin groups + +TODO: Document + ### Wine prefixes It is also possible to use yabridge with multiple Wine prefixes. Yabridge will @@ -431,3 +435,5 @@ as the _Windows VST plugin_. The whole process works as follows: from the plugin's `AEffect` struct to the Linux native VST plugin over the `dispatcher()` socket. This is only done once at startup. After this point the plugin will stop blocking and has finished loading. + +TODO: Document plugin groups diff --git a/meson.build b/meson.build index fd511f5a..81b87057 100644 --- a/meson.build +++ b/meson.build @@ -15,15 +15,17 @@ if not meson.get_compiler('cpp').compiles(winelib_check) error('You need to set up a cross compiler, check the README for compilation instructions.') endif -# Depending on the `use-bitbridge` flag we'll enable building a second 32-bit -# host application that can act as a bit bridge for using 32-bit Windows plugins +# Depending on the `use-bitbridge` flag we'll enable building secondary 32-bit +# host applications that can act as a bit bridge for using 32-bit Windows plugins # in 64-bit Linux VST hosts. The plugin will determine which host application to # use based on the `.dll` file it's trying to load. # This setup is necessary until Meson provides a way to have multiple # cross-builds for a single build directory: # https://github.com/mesonbuild/meson/issues/5125 -host_name_64bit = 'yabridge-host' -host_name_32bit = 'yabridge-host-32' +individual_host_name_64bit = 'yabridge-host' +individual_host_name_32bit = 'yabridge-host-32' +group_host_name_64bit = 'yabridge-group' +group_host_name_32bit = 'yabridge-group-32' # This provides an easy way to start the Wine VST host using winedbg since it # can be quite a pain to set up @@ -87,15 +89,33 @@ host_sources = [ 'src/common/serialization.cpp', 'src/wine-host/editor.cpp', 'src/wine-host/editor.cpp', - 'src/wine-host/individual-host.cpp', 'src/wine-host/utils.cpp', 'src/wine-host/wine-bridge.cpp', version_header, ] +individual_host_sources = host_sources + ['src/wine-host/individual-host.cpp'] +group_host_sources = host_sources + ['src/wine-host/group-host.cpp'] + executable( - host_name_64bit, - host_sources, + individual_host_name_64bit, + individual_host_sources, + native : false, + include_directories : include_dir, + dependencies : [ + boost_dep, + bitsery_dep, + tomlplusplus_dep, + wine_threads_dep, + xcb_dep + ], + cpp_args : compiler_options + ['-m64'], + link_args : ['-m64'] +) + +executable( + group_host_name_64bit, + group_host_sources, native : false, include_directories : include_dir, dependencies : [ @@ -118,8 +138,8 @@ if get_option('use-bitbridge') xcb_dep = winegcc.find_library('xcb') executable( - host_name_32bit, - host_sources, + individual_host_name_32bit, + individual_host_sources, native : false, include_directories : include_dir, dependencies : [ @@ -139,4 +159,20 @@ if get_option('use-bitbridge') cpp_args : compiler_options + ['-m32', '-Wno-ignored-attributes'], link_args : ['-m32'] ) + + executable( + group_host_name_32bit, + group_host_sources, + native : false, + include_directories : include_dir, + dependencies : [ + boost_dep, + bitsery_dep, + tomlplusplus_dep, + wine_threads_dep, + xcb_dep + ], + cpp_args : compiler_options + ['-m32', '-Wno-ignored-attributes'], + link_args : ['-m32'] + ) endif diff --git a/src/common/config/config.h.in b/src/common/config/config.h.in index 8f225016..358e9073 100644 --- a/src/common/config/config.h.in +++ b/src/common/config/config.h.in @@ -17,14 +17,29 @@ #pragma once /** - * The name of the wine host name, e.g. `yabridge-host.exe` for the regular 64 - * bit build. + * The name of the Wine VST host application, e.g. `yabridge-host.exe` for the + * regular 64-bit build. */ -constexpr char yabridge_wine_host_name[] = "@host_binary_64bit@"; +constexpr char yabridge_individual_host_name[] = + "@individual_host_binary_64bit@"; /** - * The name of the 32-bit wine host name, e.g. `yabridge-host-32.exe`.` This is - * used as a bitbridge to be able to load legacy 32-bit only Windows plugins - * from a 64-bit Linux host. + * The name of the group host application, e.g. `yabridge-group.exe` for the + * regular 64-bit build. */ -constexpr char yabridge_wine_host_name_32bit[] = "@host_binary_32bit@"; +constexpr char yabridge_group_host_name[] = "@group_host_binary_64bit@"; + +/** + * The name of the 32-bit Wine VST host application, e.g. + * `yabridge-host-32.exe`.` This is used as a bitbridge to be able to load + * legacy 32-bit only Windows plugins from a 64-bit Linux host. + */ +constexpr char yabridge_individual_host_name_32bit[] = + "@individual_host_binary_32bit@"; + +/** + * The name of the 32-bit group host application, e.g. `yabridge-group-32.exe`.` + * This is used as a bitbridge to be able to load legacy 32-bit only Windows + * plugins from a 64-bit Linux host. + */ +constexpr char yabridge_group_host_name_32bit[] = "@group_host_binary_32bit@"; diff --git a/src/common/config/meson.build b/src/common/config/meson.build index eb2cd4f6..6c88ee17 100644 --- a/src/common/config/meson.build +++ b/src/common/config/meson.build @@ -5,8 +5,10 @@ config_header = configure_file( output : 'config.h', configuration : configuration_data( { - 'host_binary_32bit': host_name_32bit + '.exe', - 'host_binary_64bit': host_name_64bit + '.exe', + 'individual_host_binary_32bit': individual_host_name_32bit + '.exe', + 'individual_host_binary_64bit': individual_host_name_64bit + '.exe', + 'group_host_binary_32bit': group_host_name_32bit + '.exe', + 'group_host_binary_64bit': group_host_name_64bit + '.exe', } ) ) diff --git a/src/plugin/utils.cpp b/src/plugin/utils.cpp index 896b0313..2fa192fa 100644 --- a/src/plugin/utils.cpp +++ b/src/plugin/utils.cpp @@ -112,9 +112,10 @@ PluginArchitecture find_vst_architecture(fs::path plugin_path) { } fs::path find_vst_host(PluginArchitecture plugin_arch) { - auto host_name = yabridge_wine_host_name; + // TODO: Take plugin group settings into account + auto host_name = yabridge_individual_host_name; if (plugin_arch == PluginArchitecture::vst_32) { - host_name = yabridge_wine_host_name_32bit; + host_name = yabridge_individual_host_name_32bit; } fs::path host_path = diff --git a/src/wine-host/group-host.cpp b/src/wine-host/group-host.cpp new file mode 100644 index 00000000..afd3f03c --- /dev/null +++ b/src/wine-host/group-host.cpp @@ -0,0 +1,81 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +// Generated inside of build directory +#include +#include + +#include "wine-bridge.h" + +/** + * This works very similar to the host application defined in + * `individual-host.cpp`, but instead of just loading a single plugin this will + * act as a daemon that can host multiple 'grouped' plugins. This works by + * allowing the `libyabridge.so` instance to connect this this process over a + * socket to ask this process to host a VST `.dll` file using a provided socket. + * After that initialization step both the regular individual plugin host and + * this group plugin host will function identically on both the plugin and the + * Wine VST host side. + * + * The explicit calling convention is needed to work around a bug introduced in + * Wine 5.7: https://bugs.winehq.org/show_bug.cgi?id=49138 + */ +int __cdecl main(int argc, char* argv[]) { + // Instead of directly hosting a plugin, this process will receive a UNIX + // domain socket endpoint path that it should listen on to allow yabridge + // instances to spawn plugins in this process. + if (argc < 3) { + std::cerr << "Usage: " +#ifdef __i386__ + << yabridge_group_host_name_32bit +#else + << yabridge_group_host_name +#endif + << " " << std::endl; + + return 1; + } + + const std::string group_name(argv[1]); + const std::string group_socket_endpoint_path(argv[2]); + + // TODO: Before doing anything, try listening on the socket and fail + // silently (or log a message?) if another application is already + // listening on the socket. This way we don't need any complicated + // inter-process synchronization to ensure that there is a single + // active group host listening for this group. + // TODO: We should somehow try and redirect this process's STDOUT and STDERR + // streams to the logger so we can forward Wine's debug messages to + // the log even the yabridge plugin instance that initially spawned + // this group host process has exited. The only way I can think of + // doing this would be using some kind of in memory file and the + // `dup2()` system call. + + Logger logger = Logger::create_from_environment(group_name); + logger.log("Initializing yabridge group host version " + + std::string(yabridge_git_version) +#ifdef __i386__ + + " (32-bit compatibility mode)" +#endif + ); + + // TODO: After initializing, listen for connections and spawn plugins + // the exact same way as what happens in `individual-host.cpp` + // TODO: Allow this process to exit when the last plugin exits. Make sure + // that that doesn't cause any race conditions. +} diff --git a/src/wine-host/individual-host.cpp b/src/wine-host/individual-host.cpp index 2d6e059c..1d8149a3 100644 --- a/src/wine-host/individual-host.cpp +++ b/src/wine-host/individual-host.cpp @@ -22,9 +22,14 @@ #include "wine-bridge.h" -// This explicit calling convention is needed to work around a bug introduced in -// Wine 5.7 -// https://bugs.winehq.org/show_bug.cgi?id=49138 +/** + * This is the default VST host application. It will load the specified VST2 + * plugin, and then connect back to the `libyabridge.so` instace that spawned + * this over the socket. + * + * The explicit calling convention is needed to work around a bug introduced in + * Wine 5.7: https://bugs.winehq.org/show_bug.cgi?id=49138 + */ int __cdecl main(int argc, char* argv[]) { // We pass the name of the VST plugin .dll file to load and the Unix domain // socket to connect to in plugin/bridge.cpp as the first two arguments of @@ -32,9 +37,9 @@ int __cdecl main(int argc, char* argv[]) { if (argc < 3) { std::cerr << "Usage: " #ifdef __i386__ - << yabridge_wine_host_name_32bit + << yabridge_individual_host_name_32bit #else - << yabridge_wine_host_name + << yabridge_individual_host_name #endif << " " << std::endl;