Skip to content

Work around wrong offset in plugin windows - #462

Merged
robbert-vdh merged 4 commits into
robbert-vdh:new-wine10-embeddingfrom
trurli:new-wine10-embedding
Jan 11, 2026
Merged

Work around wrong offset in plugin windows#462
robbert-vdh merged 4 commits into
robbert-vdh:new-wine10-embeddingfrom
trurli:new-wine10-embedding

Conversation

@trurli

@trurli trurli commented Jan 3, 2026

Copy link
Copy Markdown
Contributor

This PR works around the issue of wrong mouse cursor positions as mentioned in #409 by getting the parent window offset via xcb and using that when no "synthetic" ConfigureNotify event with absolute coordinates is present.

Tested this with wine-10.20 and various (mostly VST3) plugins in Bitwig, Carla, Reaper and Ardour using gnome with Xwayland.

@patlach42

Copy link
Copy Markdown

Great job!

There is one small bug—in REAPER, when moving a window with a plugin, the offsets are not recalculated.

@Desidiosus

Copy link
Copy Markdown
Contributor

The moving a window bug was present in Ardour as well. I debugged it a bit and discovered that there wasn't a ConfigureNotify event when the window was moved. I also noticed that there was reparenting that didn't result in an event mask being set for the new host, which is why the ConfigureNotify event didn't come and the offset wasn't reapplied.

I created this patch to fix the offset bug when moving a window. It uses an alternative method of finding the host window when a reparenting occurs if the original method fails. Seemed to work in Reaper, Carla and Ardour with various plugins.
Fix_cursor_offset_after_moving_the_plugin_window.patch

diff --git a/src/wine-host/editor.cpp b/src/wine-host/editor.cpp
index 5d33f080..53e01c00 100644
--- a/src/wine-host/editor.cpp
+++ b/src/wine-host/editor.cpp
@@ -159,6 +159,20 @@ std::optional<xcb_window_t> find_host_window(xcb_connection_t& x11_connection,
                                              xcb_window_t starting_at,
                                              xcb_atom_t xcb_wm_state_property);
 
+/**
+ * Uses xcb_query_tree to find out what the parent of the given window is. This
+ * seems to be able to find the host if find_host_window fails.
+ *
+ * @param x11_connection The X11 connection to use.
+ * @param window_id The window we want to know the parent window of.
+ *
+ * @return The host's editor window, or a nullopt if we cannot find a valid
+ *   window.
+ */
+std::optional<xcb_window_t> find_host_window_from_query_tree(
+    xcb_connection_t& x11_connection,
+    xcb_window_t window_id);
+
 /**
  * Check whether `child` is a descendant of `parent` or the same window. Used
  * during focus checks to only grab focus when needed.
@@ -1003,7 +1017,10 @@ void Editor::redetect_host_window() noexcept {
     const xcb_window_t new_host_window =
         find_host_window(*x11_connection_, parent_window_,
                          xcb_wm_window_role_property_)
-            .value_or(parent_window_);
+            .value_or(find_host_window_from_query_tree(*x11_connection_,
+                                                       parent_window_)
+                          .value_or(parent_window_));
+
     if (new_host_window == host_window_) {
         return;
     }
@@ -1256,6 +1273,28 @@ std::optional<xcb_window_t> find_host_window(
     return std::nullopt;
 }
 
+std::optional<xcb_window_t> find_host_window_from_query_tree(
+    xcb_connection_t& x11_connection,
+    // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
+    const xcb_window_t window_id) {
+    const xcb_query_tree_cookie_t cookie =
+        xcb_query_tree(&x11_connection, window_id);
+    xcb_generic_error_t* error = nullptr;
+    const std::unique_ptr<xcb_query_tree_reply_t, decltype(&free)> reply(
+        xcb_query_tree_reply(&x11_connection, cookie, &error), free);
+
+    if (!error && reply) {
+        if (const xcb_window_t actual_parent = reply->parent;
+            actual_parent != XCB_NONE) {
+            return actual_parent;
+            }
+    } else {
+        free(error);
+    }
+
+    return std::nullopt;
+}
+
 bool is_child_window_or_same(
     xcb_connection_t& x11_connection,
     // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)

@trurli

trurli commented Jan 6, 2026

Copy link
Copy Markdown
Contributor Author

Great job!

Thanks!

There is one small bug—in REAPER, when moving a window with a plugin, the offsets are not recalculated.

I can confirm that I also have this effect in REAPER (but not Ardour for some reason) and that the patch from @Desidiosus fixes it for me.

@Schroedingers-Cat

Copy link
Copy Markdown

Thanks a lot for this PR!

With the patch from @Desidiosus applied onto @trurli 's branch, I'm noticing the following regressions in Reaper:

  • some plugins (like Madrona Labs Sumu still have an offset, however seemingly related to the size of Reaper's FX chain window around the plugin's window. This happens only when loading the plugin for the first time in a DAW process
  • several plugins (like NI Kontakt 5, various SoundHack plugins) don't open their GUI and Reaper's FX chain window stays gray
  • sometimes, Reaper's "Add FX to Track" window stops updating. While this sounds unrelated, it happened several times with the yabridge build from this branch but not even once over the last three years with the build from the Manjaro repo.

Is anyone else able to confirm these regressions?

@Desidiosus

Copy link
Copy Markdown
Contributor

@Schroedingers-Cat Could you provide some more information about your setup? I tested plugins by the companies you mentioned by name aside from Kontakt 5 and these were my findings:

  • There's something weird going on with Madrona Labs Sumu as yabridgectl wants to remove it immediately when syncing and it's not detect by anything. Is some plugin specific yabridge configuration needed to get it to work?
  • VST2 plugins from Madrona Labs work as expected. Their AaltoVerb plugin seems to have the JUCE Direct2D issue that prevents the GUI from being drawn.
  • All freeware Soundhack plugins worked as expected (both VST2 and VST3 versions).

If you export these env vars and run Reaper from the same terminal, you could send the log output if you can reproduce the issues easily.

export YABRIDGE_DEBUG_FILE=/tmp/yabridge.log
export YABRIDGE_DEBUG_LEVEL=1+editor

@thinkbeforecoding

Copy link
Copy Markdown

I tested it on Reaper with MeldaProd plugins and TDR nova and it's working ! Thank you so much !

@zx2c4

zx2c4 commented Jan 11, 2026

Copy link
Copy Markdown

@trurli do you intend to merge this patch from @Desidiosus into this PR so that @robbert-vdh can pick them both up?

@trurli

trurli commented Jan 11, 2026

Copy link
Copy Markdown
Contributor Author

@trurli do you intend to merge this patch from @Desidiosus into this PR so that @robbert-vdh can pick them both up?

Done.

@robbert-vdh

Copy link
Copy Markdown
Owner

Thanks so much for looking into this! Great find. I'm still seeing some issues with respect to sizing compared to the old Wine 9.x behavior with specific plugins , and from a very brief test run it seems like there are still some resizing issues in Ardour that need to be solved before finally tagging a release, but this is a massive improvement!

I'll merge this into new-wine10-embedding, and I'll try to find some time to finally merge that branch back into master sometime this week.

@robbert-vdh
robbert-vdh merged commit 945528c into robbert-vdh:new-wine10-embedding Jan 11, 2026
@drac-dot-gay

Copy link
Copy Markdown

i hate to be the bearer of bad news, but this pr doesn't work for me. vst2 plugins load fine, but vst3 and clap plugins give me a stack overflow and cause plugins to hang in bitwig studio.

[2026-1-13 19:35:35.286 BITWIG_PLUGIN_HOST info] PluginHost: Starting new audio thread for processing plugin 0
[2026-1-13 19:35:35.379 BITWIG_PLUGIN_HOST info] About to create a CLAP plugin instance for com.FabFilter.preset-discovery.Pro-C.2 (/home/draconium/.clap/yabridge/FabFilter Pro-C 2.clap)
ter Pro-C 2-01EFRWei] host:          '/usr/bin/yabridge-host.exe'
19:35:35 [FabFilter Pro-C 2-01EFRWei] plugin:        '/home/draconium/.wine/drive_c/Program Files/Common Files/CLAP/FabFilter Pro-C 2.clap'
19:35:35 [FabFilter Pro-C 2-01EFRWei] plugin type:   'CLAP'
19:35:35 [FabFilter Pro-C 2-01EFRWei] realtime:      'yes'
19:35:35 [FabFilter Pro-C 2-01EFRWei] sockets:       '/run/user/1000/yabridge-FabFilter Pro-C 2-01EFRWei'
19:35:35 [FabFilter Pro-C 2-01EFRWei] wine prefix:   '/home/draconium/.wine'
19:35:35 [FabFilter Pro-C 2-01EFRWei] wine version:  '10.20 (Staging)'
19:35:35 [FabFilter Pro-C 2-01EFRWei] 
19:35:35 [FabFilter Pro-C 2-01EFRWei] config from:   '<defaults>'
19:35:35 [FabFilter Pro-C 2-01EFRWei] hosting mode:  'individually, 64-bit'
19:35:35 [FabFilter Pro-C 2-01EFRWei] other options: '<none>'
19:35:35 [FabFilter Pro-C 2-01EFRWei] 
19:35:35 [FabFilter Pro-C 2-01EFRWei] Enabled features:
19:35:35 [FabFilter Pro-C 2-01EFRWei] - CLAP support
19:35:35 [FabFilter Pro-C 2-01EFRWei] - VST3 support
19:35:35 [FabFilter Pro-C 2-01EFRWei] 
19:35:35 [FabFilter Pro-C 2-01EFRWei] [Wine STDERR] 0664:fixme:winediag:loader_init wine-staging 10.20 is a testing version containing experimental patches.
19:35:35 [FabFilter Pro-C 2-01EFRWei] [Wine STDERR] 0664:fixme:winediag:loader_init Please mention your exact version when filing bug reports on winehq.org.
19:35:35 [FabFilter Pro-C 2-01EFRWei] [Wine STDERR] Initializing yabridge host version 5.1.1-37-g945528cd
19:35:35 [FabFilter Pro-C 2-01EFRWei] [Wine STDERR] Preparing to load CLAP plugin at '/home/draconium/.wine/drive_c/Program Files/Common Files/CLAP/FabFilter Pro-C 2.clap'
19:35:35 [FabFilter Pro-C 2-01EFRWei] [Wine STDERR] Finished initializing '/home/draconium/.wine/drive_c/Program Files/Common Files/CLAP/FabFilter Pro-C 2.clap'
19:35:35 [FabFilter Pro-C 2-01EFRWei] [Wine STDERR] 0664:err:virtual:virtual_setup_exception stack overflow 1024 bytes addr 0x6fffffc1b71f stack 0x10c00 (0x10000-0x11000-0x110000)
[2026-1-13 19:37:20.852 BITWIG_PLUGIN_HOST info] About to create a VST 3 plugin instance for 71FE697D00C0486F00492E140051E999 (/home/draconium/.vst3/yabridge/Kilohearts/kHs Bitcrush.vst3)
icyGetThreadInitializationType FFFFFFFFFFFFFFFA, 000000000275FF50
19:37:20 [kHs 3-Band EQ-9BFIOfly] [Wine STDERR] 0720:fixme:sync:SetWaitableTimerEx (00000000000000D4, 000000000275FDC8, 0, 0000000000000000, 0000000000000000, 0000000000000000, 32) semi-stub
19:37:20 [kHs 3-Band EQ-9BFIOfly] [Wine STDERR] Finished initializing '/home/draconium/.wine/drive_c/Program Files/Common Files/VST3/Kilohearts/kHs 3-Band EQ.vst3'
19:37:20 [kHs 3-Band EQ-9BFIOfly] [Wine STDERR] Thrift: Tue Jan 13 19:37:20 2026 TPipe::open ::CreateFile errored GLE=: errno = 2
19:37:20 [kHs 3-Band EQ-9BFIOfly] [Wine STDERR] 065c:err:virtual:virtual_setup_exception stack overflow 1152 bytes addr 0x6fffffc1b71f stack 0x10b80 (0x10000-0x11000-0x110000)
19:37:20 [kHs Bitcrush-1FLRkrvx] Initializing yabridge version 5.1.1-37-g945528cd
19:37:20 [kHs Bitcrush-1FLRkrvx] library:       '/usr/lib/libyabridge-vst3.so'
19:37:20 [kHs Bitcrush-1FLRkrvx] host:          '/usr/bin/yabridge-host.exe'
19:37:20 [kHs Bitcrush-1FLRkrvx] plugin:        '/home/draconium/.wine/drive_c/Program Files/Common Files/VST3/Kilohearts/kHs Bitcrush.vst3'
19:37:20 [kHs Bitcrush-1FLRkrvx] plugin type:   'VST3'
19:37:20 [kHs Bitcrush-1FLRkrvx] realtime:      'yes'
19:37:20 [kHs Bitcrush-1FLRkrvx] sockets:       '/run/user/1000/yabridge-kHs Bitcrush-1FLRkrvx'
19:37:20 [kHs Bitcrush-1FLRkrvx] wine prefix:   '/home/draconium/.wine'
19:37:20 [kHs Bitcrush-1FLRkrvx] wine version:  '10.20 (Staging)'
19:37:20 [kHs Bitcrush-1FLRkrvx] 
19:37:20 [kHs Bitcrush-1FLRkrvx] config from:   '<defaults>'
19:37:20 [kHs Bitcrush-1FLRkrvx] hosting mode:  'individually, 64-bit'
19:37:20 [kHs Bitcrush-1FLRkrvx] other options: '<none>'
19:37:20 [kHs Bitcrush-1FLRkrvx] 
19:37:20 [kHs Bitcrush-1FLRkrvx] Enabled features:
19:37:20 [kHs Bitcrush-1FLRkrvx] - CLAP support
19:37:20 [kHs Bitcrush-1FLRkrvx] - VST3 support
19:37:20 [kHs Bitcrush-1FLRkrvx] 
19:37:20 [kHs Bitcrush-1FLRkrvx] [Wine STDERR] Initializing yabridge host version 5.1.1-37-g945528cd
19:37:20 [kHs Bitcrush-1FLRkrvx] [Wine STDERR] Preparing to load VST3 plugin at '/home/draconium/.wine/drive_c/Program Files/Common Files/VST3/Kilohearts/kHs Bitcrush.vst3'
19:37:20 [kHs Bitcrush-1FLRkrvx] [Wine STDERR] 075c:fixme:kernelbase:AppPolicyGetThreadInitializationType FFFFFFFFFFFFFFFA, 000000000275FF50
19:37:20 [kHs Bitcrush-1FLRkrvx] [Wine STDERR] 0758:fixme:sync:SetWaitableTimerEx (00000000000000DC, 000000000285FDC8, 0, 0000000000000000, 0000000000000000, 0000000000000000, 32) semi-stub
19:37:20 [kHs Bitcrush-1FLRkrvx] [Wine STDERR] Finished initializing '/home/draconium/.wine/drive_c/Program Files/Common Files/VST3/Kilohearts/kHs Bitcrush.vst3'
19:37:20 [kHs Bitcrush-1FLRkrvx] [Wine STDERR] Thrift: Tue Jan 13 19:37:20 2026 TPipe::open ::CreateFile errored GLE=: errno = 2

@trurli

trurli commented Jan 14, 2026

Copy link
Copy Markdown
Contributor Author

i hate to be the bearer of bad news, but this pr doesn't work for me. vst2 plugins load fine, but vst3 and clap plugins give me a stack overflow and cause plugins to hang in bitwig studio.

What is the rest of your setup? Distro, desktop environment, X or Wayland?

@drac-dot-gay

Copy link
Copy Markdown

i hate to be the bearer of bad news, but this pr doesn't work for me. vst2 plugins load fine, but vst3 and clap plugins give me a stack overflow and cause plugins to hang in bitwig studio.

What is the rest of your setup? Distro, desktop environment, X or Wayland?

cachyos, kde plasma, xwayland. i actually was able to get a working build by building directly from the github repo, and not from the bespoke aur package that was set up for this branch. so, it's an issue with that and not this pr most likely.

@prgmatic

Copy link
Copy Markdown

Thank you so much! Massive quality of life improvement! Running on cachy, cosmic de, reaper.

@Schroedingers-Cat

Copy link
Copy Markdown

I think I found a regression when building yabridge from this branch. The following is happening with both, Wine 9.21 and Wine 11: Omnisphere 2 VST3 opens a blank UI in Reaper's FX Chain window but opens its actual UI as a borderless window behind Reaper at the top-left. This does not happen with Wine v9.21 and yabridge 5.1.1.

@Schroedingers-Cat Could you provide some more information about your setup? I tested plugins by the companies you mentioned by name aside from Kontakt 5 and these were my findings:

@Desidiosus thanks for looking into this! I wanted to catch yabridge regressions early on so my findings were made still with wine 9.21. I'm not sure if it is intentional or known that this branch breaks compatibility with that versions below 9.22?

The plugins showing a blank gray UI window was related to VST2 plugins on Wine 9.21 using this yabridge branch. When switching to Wine 11, those VST2 plugins work. So I guess these changes are simply incompatible with Wine 9.21?

* There's something weird going on with Madrona Labs Sumu as yabridgectl wants to remove it immediately when syncing and it's not detect by anything. Is some plugin specific yabridge configuration needed to get it to work?

There's a bug in yabridgectrl that makes it fail handling vst3s if the actual binary in the .vst3 directory has a different name than the .vst3 directory which keeps yabridgectrl in eternal "purging leftover vst3". So after renaming either the binary or the .vst3 directory to match each other, yabridgectrl handles that plugin correctly.

However, Sumu VST3 works fine this time with yabridge built from the tip of the new-wine10-embedding branch.

Also, native Win32 context menus work fine with both Wine v9.21 and Wine 11 as well as painted context menus such as UVI Falcon.

@Schroedingers-Cat

Copy link
Copy Markdown

More on the regression of this branch with Omnisphere 2, the log shows this during UI start:

09:16:19 [all] [STDERR] DEBUG: Reparenting 56623104 to 44042016 failed:
09:16:19 [all] [STDERR] Error code: 3
09:16:19 [all] [STDERR] Major code: 7
09:16:19 [all] [STDERR] Minor code: 0
09:16:19 [all] [STDERR] DEBUG: Could not query pointer location
09:16:19 [all] [STDERR] DEBUG: Reparenting 60817411 to 56623104 failed:
09:16:19 [all] [STDERR] Error code: 3
09:16:19 [all] [STDERR] Major code: 7
09:16:19 [all] [STDERR] Minor code: 0

The Wine version doesn't matter, happens with 9.21 and 11.0. The only thing that matters is yabridge stable vs this branch. With yabridge stable:

09:10:33 [all] [STDERR] DEBUG: Reparenting 56623104 to 44044830 succeeded
09:10:33 [all] [STDERR] DEBUG: Reparenting 60817411 to 56623104 succeeded

Reparenting twice is intentional?
omnisphere2-wine9-21-yabridge511-stable-.log
omnisphere2-wine9-21-yabridge511-wine10embedding.log

Her are the full logs:

@Desidiosus

Copy link
Copy Markdown
Contributor

@Schroedingers-Cat My latest commit in #463 should fix the regression with Omnisphere 2. Thanks for reporting it!

Reparenting twice is intentional and the code comments explain it well:

// If you naively reparent `wine_window_` to `parent_window_`, Wine will
// interpret any local coordinates as global coordinates. To work around
// this, we'll tell the Wine window where on screen it's located in the
// `XCB_CONFIGURE_NOTIFY` handler. This happens any time the window the Wine
// window is embedded in (which may not be the parent window) is moved or
// resized. We also listen for EnterNotify and LeaveNotify events on the
// Wine window so we can grab and release input focus as necessary. And
// lastly we'll look out for reparents, so we can make sure that the window
// does not get stolen by the window manager and that we correctly handle
// the host reparenting `parent_window_` themselves.
xcb_change_window_attributes(x11_connection_.get(), host_window_,
XCB_CW_EVENT_MASK, &host_event_mask);
xcb_change_window_attributes(x11_connection_.get(), parent_window_,
XCB_CW_EVENT_MASK, &parent_event_mask);
xcb_change_window_attributes(x11_connection_.get(), wrapper_window_.window_,
XCB_CW_EVENT_MASK, &wrapper_event_mask);
xcb_flush(x11_connection_.get());
// First reparent our dumb wrapper window to the host's window, and then
// embed the Wine window into our wrapper window
do_reparent(wrapper_window_.window_, parent_window_);
xcb_map_window(x11_connection_.get(), wrapper_window_.window_);
xcb_flush(x11_connection_.get());
do_reparent(wine_window_, wrapper_window_.window_);

@Schroedingers-Cat

Schroedingers-Cat commented Jan 20, 2026

Copy link
Copy Markdown

@Schroedingers-Cat My latest commit in #463 should fix the regression with Omnisphere 2. Thanks for reporting it!

Fantastic, can confirm it's working!

Reparenting twice is intentional and the code comments explain it well:

Interesting, thanks for sharing. Do you think wine devs would be up for making this workaround not necessary?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants