From 4b84f663ab5a99fe57cabd8787518ab84bc11352 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 27 Apr 2020 18:45:20 +0200 Subject: [PATCH] Always run the event loop, fixing processing issue Also remove any special `effEditIdle` handling. Apparently plugins rely on the message loop for their internal tasks, even for things that have nothing to do with GUIs, such as deferring initialization. --- README.md | 2 -- src/wine-host/editor.cpp | 13 +++++++------ src/wine-host/editor.h | 4 ++-- src/wine-host/plugin-bridge.cpp | 31 ++++++++++++++++++++----------- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 29cb853f..ff70ed6b 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ There are a few things that should be done before releasing this, including: - Fix implementation bugs: - Serum crashed and audio engine froze while browsing through Serum presets in the browser? - - Plugins from certain manufacturers, such as KiloHearts or Image-Line, don't - do any audio processing until the editor has been opened. - KiloHearts plugins create a ridiculous amount of file descriptor leaks in wineserver when esync is enabled. I haven't come across any other plugins that do this. Not sure if this is fixable in yabridge. diff --git a/src/wine-host/editor.cpp b/src/wine-host/editor.cpp index d9bbdb87..60554b3d 100644 --- a/src/wine-host/editor.cpp +++ b/src/wine-host/editor.cpp @@ -131,18 +131,18 @@ void Editor::send_idle_event() { } void Editor::handle_events() { - send_idle_event(); + MSG msg; // The null value for the second argument is needed to handle interaction // with child GUI components - MSG msg; while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { // This timer would periodically send `effEditIdle` events so the editor // remains responsive even during blocking GUI operations such as open - // dropdowns or message boxes. We filter it out here because we will - // send sent the event manually every time the host calls - // `effEditIdle()`. It will still be fired implicitely when the GUI - // thread gets blocked. + // dropdowns or message boxes. This is only needed when the GUI is + // actually blocked and it will be dispatched by the messaging loop of + // the blocking GUI component. Since we're not touching the + // `effEditIdle` event sent by the host we can always filter this timer + // event out in this event loop. if (msg.message == WM_TIMER && msg.wParam == idle_timer_id && msg.hwnd == win32_handle.get()) { continue; @@ -201,6 +201,7 @@ void Editor::handle_events() { xcb_flush(x11_connection.get()); } break; } + free(generic_event); } } diff --git a/src/wine-host/editor.h b/src/wine-host/editor.h index 6342b6f8..f97871c2 100644 --- a/src/wine-host/editor.h +++ b/src/wine-host/editor.h @@ -88,14 +88,14 @@ class Editor { /** * Send a single `effEditIdle` event to the plugin to allow it to update its * GUI state. This is called periodically from a timer while the GUI is - * being blocked. + * being blocked, and also called explicitly by the host on a timer. */ void send_idle_event(); /** * Pump messages from the editor GUI's event loop until all events are * process. Must be run from the same thread the GUI was created in because - * of Win32 limitations. I guess that's what `effEditIdle` is for. + * of Win32 limitations. */ void handle_events(); diff --git a/src/wine-host/plugin-bridge.cpp b/src/wine-host/plugin-bridge.cpp index 708d8ee8..830a5fbb 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -150,6 +150,26 @@ void PluginBridge::handle_dispatch() { passthrough_event(host_vst_dispatch, std::nullopt, plugin, std::bind(&PluginBridge::dispatch_wrapper, this, _1, _2, _3, _4, _5, _6)); + + // Because of the way the Win32 API works we have to process events + // on the same thread as the one the window was created on, and that + // thread is the thread that's handling dispatcher calls. + if (editor.has_value()) { + // This will handle Win32 events similar to the loop below, and + // it will also handle any X11 events. + editor->handle_events(); + } else { + MSG msg; + + // Since some plugins rely on the Win32 message API even for + // non-editor related tasks (such as deferring the loading of + // presets using a timer), we have to run a message loop even + // when the editor is closed. + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } } } catch (const boost::system::system_error&) { // The plugin has cut off communications, so we can shut down this host @@ -286,17 +306,6 @@ intptr_t PluginBridge::dispatch_wrapper(AEffect* plugin, // We have to intercept GUI open calls since we can't use // the X11 window handle passed by the host switch (opcode) { - case effEditIdle: - // Because of the way the Win32 API works we have to process events - // on the same thread the window was created, and that thread is the - // thread that's handling dispatcher calls - // To allow the GUI to update even when this thread gets blocked - // (e.g. when a dropdown is open), the actual `effEditIdle` event - // gets sent to the plugin on a timer. - editor->handle_events(); - - return 1; - break; case effEditOpen: { // Create a Win32 window through Wine, embed it into the window // provided by the host, and let the plugin embed itself into the