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,