// yabridge: a Wine plugin bridge // Copyright (C) 2020-2022 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 "common.h" #include #include "../../common/process.h" #include "../editor.h" /** * The maximum number of Win32 messages to handle per message loop. This is * needed because otherwise some plugins can run into an infinite loop. I've * observed this with: * * - Waves plugins * - Melda plugins when having multiple editor windows open within a single * plugin group */ constexpr int max_win32_messages = 20; /** * Some JUCE based plugins however send thousands of `WM_USER+123` events * at once from the GUI. So while the limit from `win32_message_limit` * needs to exist, it also causes some other plugins to feel sluggish. * When we encounter these events, we'll assume we're dealing with a JUCE * plugin and increase the limit. Examples of affected plugins are: * * - Thermal by Output */ constexpr int extended_max_win32_messages = 8192; /** * The Win32 message ID that needs to trigger the behaviour described for * `juce_win32_message_limit`. */ constexpr unsigned int juce_message_id = WM_USER + 123; HostBridge::HostBridge(MainContext& main_context, ghc::filesystem::path plugin_path, pid_t parent_pid) : plugin_path_(plugin_path), main_context_(main_context), generic_logger_(Logger::create_wine_stderr()), parent_pid_(parent_pid), watchdog_guard_(main_context.register_watchdog(*this)) {} void HostBridge::handle_events() noexcept { MSG msg; int limit = max_win32_messages; for (int i = 0; i < limit && PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE); i++) { // HACK: See the docstring on `juce_win32_message_limit` if (msg.message == juce_message_id) { limit = extended_max_win32_messages; } TranslateMessage(&msg); DispatchMessage(&msg); } } void HostBridge::shutdown_if_dangling() { // If the parent process has exited and this plugin bridge instance is // outliving the process it's supposed to be connected to (because in some // situations sockets won't get closed when this happens so we'd hang on // `recv()`), then we'll close the sockets here so that the plugin bridge // exits gracefully. This will be periodically called from `MainContext`'s // watchdog thread. if (!pid_running(parent_pid_)) { std::cerr << "WARNING: The native plugin host seems to have died." << std::endl; std::cerr << " This bridge will shut down now." << std::endl; // FIXME: Closing the sockets should work fine, but it still leaves some // background threads hanging around. For now we'll just // terminate the entire process instead since we'll probably be // left in a bad state anyways. The only thing this could // potentially break would be sharing a plugin group across two // different DAWs, but you really shouldn't be doing that. :D // // Check this commit for another now-unnecessary change we // reverted here. // close_sockets(); TerminateProcess(GetCurrentProcess(), 0); } }