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.
This commit is contained in:
Juuso Kaitila
2026-01-16 20:21:50 +02:00
committed by Robbert van der Helm
parent 945528cd7f
commit 604375b756
3 changed files with 67 additions and 1 deletions
+30 -1
View File
@@ -806,9 +806,38 @@ void Vst3Bridge::run() {
initial_size.emplace(size.getWidth(), initial_size.emplace(size.getWidth(),
size.getHeight()); 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( Editor& editor_instance = instance.editor.emplace(
main_context_, config_, generic_logger_, x11_handle, main_context_, config_, generic_logger_, x11_handle,
std::nullopt, initial_size); std::move(resize_watchdog), initial_size);
const tresult result = const tresult result =
instance.plug_view_instance->plug_view->attached( instance.plug_view_instance->plug_view->attached(
editor_instance.win32_handle(), type.c_str()); editor_instance.win32_handle(), type.c_str());
+28
View File
@@ -427,6 +427,34 @@ void Editor::resize(uint16_t width, uint16_t height) {
wrapper_window_size_.height = height; wrapper_window_size_.height = height;
} }
std::optional<Size> 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<xcb_get_geometry_reply_t> 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 { void Editor::show() noexcept {
ShowWindow(win32_window_.handle_, SW_SHOWNORMAL); ShowWindow(win32_window_.handle_, SW_SHOWNORMAL);
} }
+9
View File
@@ -204,6 +204,15 @@ class Editor {
*/ */
void resize(uint16_t width, uint16_t height); 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<Size> check_size_mismatch();
/** /**
* Show the window, should be called after the plugin has embedded itself. * 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, * There's absolutely zero reason why this can't be done in the constructor,