From 604375b7567f12f6137fd5ac34863f36be3e2193 Mon Sep 17 00:00:00 2001 From: Juuso Kaitila Date: Fri, 16 Jan 2026 20:21:50 +0200 Subject: [PATCH] VST3: Fix broken resizing in Ardour Adds timer_proc lambda for VST3 to check for size mismatches and trigger a new resize to correct it through eventual consistency. This is done to workaround an X11 sync issue where the plugin view would end up smaller or larger than its wrapper window. In Ardour this could result in the plugin becoming uninteractable. --- src/wine-host/bridges/vst3.cpp | 31 ++++++++++++++++++++++++++++++- src/wine-host/editor.cpp | 28 ++++++++++++++++++++++++++++ src/wine-host/editor.h | 9 +++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index eb004526..d2eec2f5 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -806,9 +806,38 @@ void Vst3Bridge::run() { initial_size.emplace(size.getWidth(), size.getHeight()); } + + // HACK: Create a resize watchdog that periodically + // verifies the wrapper window size matches the expected + // size. This works around VST3 resize issues (mostly) + // in Ardour during mutual recursion where X11 + // operations may not be applied and the wrapper window + // remains smaller or larger than the wine window. The + // goal here is eventual consistency + auto resize_watchdog = [&instance = instance] { + if (instance.editor) { + if (const auto expected = + instance.editor + ->check_size_mismatch()) { + // Resize the plugin view to propagate the + // target size everywhere. + if (instance.plug_view_instance) { + Steinberg::ViewRect rect{ + 0, 0, + (expected->width), + (expected->height)}; + instance.plug_frame_proxy->resizeView( + instance.plug_view_instance + ->plug_view, + &rect); + } + } + } + }; + Editor& editor_instance = instance.editor.emplace( main_context_, config_, generic_logger_, x11_handle, - std::nullopt, initial_size); + std::move(resize_watchdog), initial_size); const tresult result = instance.plug_view_instance->plug_view->attached( editor_instance.win32_handle(), type.c_str()); diff --git a/src/wine-host/editor.cpp b/src/wine-host/editor.cpp index 53e01c00..b7da3465 100644 --- a/src/wine-host/editor.cpp +++ b/src/wine-host/editor.cpp @@ -427,6 +427,34 @@ void Editor::resize(uint16_t width, uint16_t height) { wrapper_window_size_.height = height; } +std::optional Editor::check_size_mismatch() { + xcb_generic_error_t* error = nullptr; + const xcb_get_geometry_cookie_t cookie = + xcb_get_geometry(x11_connection_.get(), wrapper_window_.window_); + const std::unique_ptr geom( + xcb_get_geometry_reply(x11_connection_.get(), cookie, &error)); + + if (error) { + free(error); + return std::nullopt; + } + + if (geom && (geom->width != wrapper_window_size_.width || + geom->height != wrapper_window_size_.height)) { + logger_.log_editor_trace([&]() { + return "DEBUG: Size mismatch detected. Actual: " + + std::to_string(geom->width) + "x" + + std::to_string(geom->height) + ", Expected: " + + std::to_string(wrapper_window_size_.width) + "x" + + std::to_string(wrapper_window_size_.height); + }); + + return wrapper_window_size_; + } + + return std::nullopt; +} + void Editor::show() noexcept { ShowWindow(win32_window_.handle_, SW_SHOWNORMAL); } diff --git a/src/wine-host/editor.h b/src/wine-host/editor.h index cfeb950f..398d5d32 100644 --- a/src/wine-host/editor.h +++ b/src/wine-host/editor.h @@ -204,6 +204,15 @@ class Editor { */ void resize(uint16_t width, uint16_t height); + /** + * Check if the wrapper window's actual X11 size matches the expected size. + * Returns the expected size if there's a mismatch, or nullopt if sizes + * match. This is used as a workaround for VST3 plugins where rapid + * resizing during mutual recursion can cause the X11 window to get stuck + * at an intermediate size. + */ + std::optional check_size_mismatch(); + /** * Show the window, should be called after the plugin has embedded itself. * There's absolutely zero reason why this can't be done in the constructor,