mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-06 19:40:10 +02:00
Remove the editor_xembed option
This commit is contained in:
@@ -25,6 +25,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
### Removed
|
||||
|
||||
- The `editor_xembed` compatibility option has been removed. This option hasn't
|
||||
worked properly for the last couple major Wine releases.
|
||||
- Out of the box support for building a 32-bit version of yabridge for use in
|
||||
64-bit machines has been dropped as part of solving a compatibility issue with
|
||||
newer Meson versions
|
||||
|
||||
@@ -337,7 +337,6 @@ you load a new plugin._
|
||||
| `editor_coordinate_hack` | `{true,false}` | Compatibility option for plugins that rely on the absolute screen coordinates of the window they're embedded in. Since the Wine window gets embedded inside of a window provided by your DAW, these coordinates won't match up and the plugin would end up drawing in the wrong location without this option. Currently the only known plugins that require this option are _PSPaudioware E27_ and _Soundtoys Crystallizer_. Defaults to `false`. |
|
||||
| `editor_disable_host_scaling` | `{true,false}` | Disable host-driven HiDPI scaling for VST3 and CLAP plugins. Wine currently does not have proper fractional HiDPI support, so you might have to enable this option if you're using a HiDPI display. In most cases setting the font DPI in `winecfg`'s graphics tab to 192 will cause plugins to scale correctly at 200% size. Defaults to `false`. |
|
||||
| `editor_force_dnd` | `{true,false}` | This option forcefully enables drag-and-drop support in _REAPER_. Because REAPER's FX window supports drag-and-drop itself, dragging a file onto a plugin editor will cause the drop to be intercepted by the FX window. This makes it impossible to drag files onto plugins in REAPER under normal circumstances. Setting this option to `true` will strip drag-and-drop support from the FX window, thus allowing files to be dragged onto the plugin again. Defaults to `false`. |
|
||||
| `editor_xembed` | `{true,false}` | Use Wine's XEmbed implementation instead of yabridge's normal window embedding method. Some plugins will have redrawing issues when using XEmbed and editor resizing won't always work properly with it, but it could be useful in certain setups. You may need to use [this Wine patch](https://github.com/psycha0s/airwave/blob/master/fix-xembed-wine-windows.patch) if you're getting blank editor windows. Defaults to `false`. |
|
||||
| `frame_rate` | `<number>` | The rate at which Win32 events are being handled and usually also the refresh rate of a plugin's editor GUI. When using plugin groups all plugins share the same event handling loop, so in those the last loaded plugin will set the refresh rate. Defaults to `60`. |
|
||||
| `hide_daw` | `{true,false}` | Don't report the name of the actual DAW to the plugin. See the [known issues](#known-issues-and-fixes) section for a list of situations where this may be useful. This affects VST2, VST3, and CLAP plugins. Defaults to `false`. |
|
||||
| `vst3_prefer_32bit` | `{true,false}` | Use the 32-bit version of a VST3 plugin instead the 64-bit version if both are installed and they're in the same VST3 bundle inside of `~/.vst3/yabridge`. You likely won't need this. |
|
||||
@@ -368,9 +367,6 @@ group = "toneboosters"
|
||||
["PSPaudioware"]
|
||||
editor_coordinate_hack = true
|
||||
|
||||
["Analog Lab 3.so"]
|
||||
editor_xembed = true
|
||||
|
||||
["Chromaphone 3.so"]
|
||||
hide_daw = true
|
||||
|
||||
@@ -570,13 +566,6 @@ Aside from that, these are some known caveats:
|
||||
VST 2.4 has no way to let the host know that those labels have been updated.
|
||||
Deactivating and reactivating the plugin will cause these labels to be updated
|
||||
again for the current patch.
|
||||
- The Cinnamon desktop environment has some quirks with its window management
|
||||
that affect yabridge's plugin editor embedding. Most notably some plugins may
|
||||
flicker while dragging windows around, and there may be [rendering
|
||||
issues](https://github.com/robbert-vdh/yabridge/issues/89) when using multiple
|
||||
monitors depending on which screen has been set as primary. Enabling the
|
||||
XEmbed [compatibility option](#compatibility-options) may help, but Wine's
|
||||
XEmbed implementation also introduces other rendering issues.
|
||||
|
||||
There are also some (third party) plugin API extensions for that have not been
|
||||
implemented yet. See the [roadmap](./ROADMAP.md) for a list of future plans.
|
||||
|
||||
+8
-17
@@ -109,23 +109,14 @@ size or when the plugin resizes its own window. For embedding the Wine window
|
||||
into the host's window we support two different implementations:
|
||||
|
||||
- The main approach involves reparenting the Wine window to the host window, and
|
||||
then manually sending X11 `ConfigureNotify` events to the corresponding X11
|
||||
window whenever its size or position on the screen changes. This is needed
|
||||
because while the reparented Wine window is located at the (relative)
|
||||
coordinates `(0, 0)`, Wine willl think that these coordinates are absolute
|
||||
screen coordinates and without sending this event a lot of Windows
|
||||
applications will either render in the wrong location or have broken knobs and
|
||||
sliders. By manually sending the event instead of actually reconfiguring the
|
||||
window Wine will think the window is located at its actual screen coordinates
|
||||
and user interaction works as expected.
|
||||
- Alternatively there's an option to use Wine's own XEmbed implementation.
|
||||
XEmbed is the usual solution for embedding one application window into
|
||||
approach. However this sadly does have a few quirks, including flickering with
|
||||
some plugins that use VSTGUI and windows that don't properly rendering until
|
||||
they are reopened in some hosts. Because of that the above embedding behaviour
|
||||
that essentially fakes this XEmbed support is the default and XEmbed can be
|
||||
enabled separately on a plugin by plugin basis by setting a flag in a
|
||||
`yabridge.toml` config file.
|
||||
then acting as a minimal X11 window manager that sits between the host window
|
||||
and Wine's window. This is needed because while the reparented Wine window is
|
||||
located at the (relative) coordinates `(0, 0)` in the host's window, Wine will
|
||||
think that these coordinates are absolute screen coordinates and without
|
||||
sending this event a lot of Windows applications will either render in the
|
||||
wrong location or have broken knobs and sliders. We solve this by intercepting
|
||||
window configuration events to let the window know where on screen it is
|
||||
located.
|
||||
|
||||
Aside from embedding the window we also manage keyboard focus grabbing. Since
|
||||
it's not possible for us to know when the Windows plugin wants keyboard focus,
|
||||
|
||||
@@ -115,12 +115,6 @@ Configuration::Configuration(const fs::path& config_path,
|
||||
} else {
|
||||
invalid_options.emplace_back(key);
|
||||
}
|
||||
} else if (key == "editor_xembed") {
|
||||
if (const auto parsed_value = value.as_boolean()) {
|
||||
editor_xembed = parsed_value->get();
|
||||
} else {
|
||||
invalid_options.emplace_back(key);
|
||||
}
|
||||
} else if (key == "frame_rate") {
|
||||
if (const auto parsed_value = value.as_floating_point()) {
|
||||
frame_rate = parsed_value->get();
|
||||
|
||||
@@ -112,14 +112,6 @@ class Configuration {
|
||||
*/
|
||||
bool editor_force_dnd = false;
|
||||
|
||||
/**
|
||||
* Use XEmbed instead of yabridge's normal editor embedding method. Wine's
|
||||
* XEmbed support is not very polished yet and tends to lead to rendering
|
||||
* issues, so this is disabled by default. Also, editor resizing won't work
|
||||
* reliably when XEmbed is enabled.
|
||||
*/
|
||||
bool editor_xembed = false;
|
||||
|
||||
/**
|
||||
* The number of times per second we'll handle the event loop. In most
|
||||
* plugins this also controls the plugin editor GUI's refresh rate.
|
||||
@@ -199,7 +191,6 @@ class Configuration {
|
||||
[](S& s, auto& v) { s.ext(v, bitsery::ext::GhcPath{}); });
|
||||
s.value1b(editor_coordinate_hack);
|
||||
s.value1b(editor_force_dnd);
|
||||
s.value1b(editor_xembed);
|
||||
s.ext(frame_rate, bitsery::ext::InPlaceOptional(),
|
||||
[](S& s, auto& v) { s.value4b(v); });
|
||||
s.value1b(hide_daw);
|
||||
|
||||
@@ -298,9 +298,6 @@ class PluginBridge {
|
||||
if (config_.editor_force_dnd) {
|
||||
other_options.push_back("editor: force drag-and-drop");
|
||||
}
|
||||
if (config_.editor_xembed) {
|
||||
other_options.push_back("editor: XEmbed");
|
||||
}
|
||||
if (config_.frame_rate) {
|
||||
std::ostringstream option;
|
||||
option << "frame rate: " << std::setprecision(2)
|
||||
|
||||
+23
-120
@@ -54,8 +54,7 @@ constexpr size_t idle_timer_id = 1337;
|
||||
* The X11 event mask for the host window, which in most DAWs except for Ardour
|
||||
* and REAPER will be the same as `parent_window_`.
|
||||
*/
|
||||
constexpr uint32_t host_event_mask =
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_VISIBILITY_CHANGE;
|
||||
constexpr uint32_t host_event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||
|
||||
/**
|
||||
* The X11 event mask for the parent window. We'll use this for input focus
|
||||
@@ -64,7 +63,7 @@ constexpr uint32_t host_event_mask =
|
||||
* reparents.
|
||||
*/
|
||||
constexpr uint32_t parent_event_mask =
|
||||
host_event_mask | XCB_EVENT_MASK_FOCUS_CHANGE |
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE |
|
||||
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW;
|
||||
|
||||
/**
|
||||
@@ -109,21 +108,6 @@ constexpr uint32_t icccm_wm_state_normal = 1;
|
||||
// `xdnd_aware_property_name` was moved to `editor.h` so the unity build
|
||||
// succeeds
|
||||
|
||||
/**
|
||||
* Client message name for XEmbed messages. See
|
||||
* https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html.
|
||||
*/
|
||||
constexpr char xembed_message_name[] = "_XEMBED";
|
||||
|
||||
// Constants from the XEmbed spec
|
||||
constexpr uint32_t xembed_protocol_version = 0;
|
||||
|
||||
constexpr uint32_t xembed_embedded_notify_msg = 0;
|
||||
constexpr uint32_t xembed_window_activate_msg = 1;
|
||||
constexpr uint32_t xembed_focus_in_msg = 4;
|
||||
|
||||
constexpr uint32_t xembed_focus_first = 1;
|
||||
|
||||
/**
|
||||
* The default arrow cursor used in Windows.
|
||||
*/
|
||||
@@ -275,7 +259,6 @@ Editor::Editor(MainContext& main_context,
|
||||
std::optional<fu2::unique_function<void()>> timer_proc)
|
||||
: use_coordinate_hack_(config.editor_coordinate_hack),
|
||||
use_force_dnd_(config.editor_force_dnd),
|
||||
use_xembed_(config.editor_xembed),
|
||||
logger_(logger),
|
||||
x11_connection_(xcb_connect(nullptr, nullptr), xcb_disconnect),
|
||||
dnd_proxy_handle_(WineXdndProxy::get_handle()),
|
||||
@@ -370,24 +353,16 @@ Editor::Editor(MainContext& main_context,
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// When using XEmbed we'll need the atoms for the corresponding properties
|
||||
xcb_xembed_message_ =
|
||||
get_atom_by_name(*x11_connection_, xembed_message_name);
|
||||
|
||||
// When not using XEmbed, Wine will interpret any local coordinates as
|
||||
// global coordinates. To work around this we'll tell the Wine window it's
|
||||
// located at its actual coordinates on screen rather than somewhere within.
|
||||
// For robustness's sake this should be done both when the actual window the
|
||||
// Wine window is embedded in (which may not be the parent window) is moved
|
||||
// or resized, and when the user moves his mouse over the window because
|
||||
// this is sometimes needed for plugin groups. 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.
|
||||
// If we do enable XEmbed support, we'll also listen for visibility changes
|
||||
// and trigger the embedding when the window becomes visible
|
||||
// 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_,
|
||||
@@ -399,23 +374,12 @@ Editor::Editor(MainContext& main_context,
|
||||
// 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());
|
||||
|
||||
if (use_xembed_) {
|
||||
// This call alone doesn't do anything. We need to call this function a
|
||||
// second time on visibility change because Wine's XEmbed implementation
|
||||
// does not work properly (which is why we remvoed XEmbed support in the
|
||||
// first place).
|
||||
do_xembed();
|
||||
} else {
|
||||
// Embed the Win32 window into the window provided by the host. Instead
|
||||
// of using the XEmbed protocol, we'll register a few events and manage
|
||||
// the child window ourselves. This is a hack to work around the issue's
|
||||
// described in `Editor`'s docstring'.
|
||||
do_reparent(wine_window_, wrapper_window_.window_);
|
||||
}
|
||||
}
|
||||
|
||||
void Editor::resize(uint16_t width, uint16_t height) {
|
||||
logger_.log_editor_trace([&]() {
|
||||
@@ -635,25 +599,6 @@ void Editor::handle_x11_events() noexcept {
|
||||
xcb_wm_state_property_, 32, 2, values.data());
|
||||
xcb_flush(x11_connection_.get());
|
||||
} break;
|
||||
// Start the XEmbed procedure when the window becomes visible,
|
||||
// since most hosts will only show the window after the plugin
|
||||
// has embedded itself into it.
|
||||
case XCB_VISIBILITY_NOTIFY: {
|
||||
const auto event =
|
||||
reinterpret_cast<xcb_visibility_notify_event_t*>(
|
||||
generic_event.get());
|
||||
logger_.log_editor_trace([&]() {
|
||||
return "DEBUG: VisibilityNotify for window " +
|
||||
std::to_string(event->window);
|
||||
});
|
||||
|
||||
if (event->window == host_window_ ||
|
||||
event->window == parent_window_) {
|
||||
if (use_xembed_) {
|
||||
do_xembed();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
// We want to grab keyboard input focus when the user hovers
|
||||
// over our embedded Wine window AND that window is a child of
|
||||
// the currently active window. This ensures that the behavior
|
||||
@@ -1069,27 +1014,6 @@ bool Editor::supports_ewmh_active_window() const {
|
||||
return active_window_property_exists;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
||||
void Editor::send_xembed_message(xcb_window_t window,
|
||||
uint32_t message,
|
||||
uint32_t detail,
|
||||
uint32_t data1,
|
||||
uint32_t data2) const noexcept {
|
||||
xcb_client_message_event_t event{};
|
||||
event.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.type = xcb_xembed_message_;
|
||||
event.window = window;
|
||||
event.format = 32;
|
||||
event.data.data32[0] = XCB_CURRENT_TIME;
|
||||
event.data.data32[1] = message;
|
||||
event.data.data32[2] = detail;
|
||||
event.data.data32[3] = data1;
|
||||
event.data.data32[4] = data2;
|
||||
|
||||
xcb_send_event(x11_connection_.get(), false, window,
|
||||
XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<char*>(&event));
|
||||
}
|
||||
|
||||
void Editor::do_reparent(xcb_window_t child, xcb_window_t new_parent) const {
|
||||
const xcb_void_cookie_t reparent_cookie = xcb_reparent_window_checked(
|
||||
x11_connection_.get(), child, new_parent, 0, 0);
|
||||
@@ -1139,28 +1063,6 @@ void Editor::do_reparent(xcb_window_t child, xcb_window_t new_parent) const {
|
||||
xcb_flush(x11_connection_.get());
|
||||
}
|
||||
|
||||
void Editor::do_xembed() const {
|
||||
if (!use_xembed_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're embedding using XEmbed, then we'll have to go through the whole
|
||||
// XEmbed dance here. See the spec for more information on how this works:
|
||||
// https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html#lifecycle
|
||||
do_reparent(wine_window_, wrapper_window_.window_);
|
||||
|
||||
// Let the Wine window know it's being embedded into the parent window
|
||||
send_xembed_message(wine_window_, xembed_embedded_notify_msg, 0,
|
||||
wrapper_window_.window_, xembed_protocol_version);
|
||||
send_xembed_message(wine_window_, xembed_focus_in_msg, xembed_focus_first,
|
||||
0, 0);
|
||||
send_xembed_message(wine_window_, xembed_window_activate_msg, 0, 0, 0);
|
||||
xcb_flush(x11_connection_.get());
|
||||
|
||||
xcb_map_window(x11_connection_.get(), wine_window_);
|
||||
xcb_flush(x11_connection_.get());
|
||||
}
|
||||
|
||||
LRESULT CALLBACK window_proc(HWND handle,
|
||||
UINT message,
|
||||
WPARAM wParam,
|
||||
@@ -1415,15 +1317,16 @@ bool is_cursor_in_wine_window(
|
||||
|
||||
if (HWND windows_window = WindowFromPoint(*windows_pointer_pos);
|
||||
windows_window && windows_window != windows_desktop_window) {
|
||||
// NOTE: Because resizing reparented Wine windows without XEmbed is a
|
||||
// bit janky, yabridge creates windows with client areas large
|
||||
// enough to fit the entire screen, and the plugin then embeds its
|
||||
// own GUI in a portion of that. The result is that
|
||||
// `WindowFromPoint()` will still return that same huge window
|
||||
// when we hover over an area to the right or to the bottom of a
|
||||
// plugin GUI. We can easily detect by just checking the window
|
||||
// class name. Also check out the `WM_NCHITTEST` implementation in
|
||||
// the message loop above.
|
||||
// NOTE: It could happen that the reparented Wine window extends beyond
|
||||
// the host's window. In that case `WindowFromPoint()` will still
|
||||
// think we're hovering over the plugin's GUI when hovering over
|
||||
// an area to the right or to the bottom of a plugin GUI. We can
|
||||
// easily detect by just checking the window class name. Also
|
||||
// check out the `WM_NCHITTEST` implementation in the message loop
|
||||
// above.
|
||||
//
|
||||
// This may not be necessary anymore with the new window
|
||||
// configuration approach.
|
||||
std::array<char, 64> window_class_name{0};
|
||||
GetClassName(windows_window, window_class_name.data(),
|
||||
window_class_name.size());
|
||||
|
||||
+5
-31
@@ -126,6 +126,9 @@ class DeferredWin32Window {
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: This documentation needs to be updated with the recent changes to how
|
||||
* embedding is handled.
|
||||
*
|
||||
* A wrapper around the win32 windowing API to create and destroy editor
|
||||
* windows. We can embed this window into the window provided by the host, and a
|
||||
* VST plugin can then later embed itself in the window create here.
|
||||
@@ -202,8 +205,8 @@ class Editor {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* or in `do_xembed()`, but it needs to be. Thanks Waves.
|
||||
* There's absolutely zero reason why this can't be done in the constructor,
|
||||
* but it can't be. Thanks Waves.
|
||||
*/
|
||||
void show() noexcept;
|
||||
|
||||
@@ -277,12 +280,6 @@ class Editor {
|
||||
*/
|
||||
const bool use_force_dnd_;
|
||||
|
||||
/**
|
||||
* Whether to use XEmbed instead of yabridge's normal window embedded. Wine
|
||||
* with XEmbed tends to cause rendering issues, so it's disabled by default.
|
||||
*/
|
||||
const bool use_xembed_;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get the X11 event mask containing the current keyboard modifiers. Because
|
||||
@@ -320,29 +317,11 @@ class Editor {
|
||||
*/
|
||||
void redetect_host_window() noexcept;
|
||||
|
||||
/**
|
||||
* Send an XEmbed message to a window. This does not include a flush. See
|
||||
* the spec for more information:
|
||||
*
|
||||
* https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html#lifecycle
|
||||
*/
|
||||
void send_xembed_message(xcb_window_t window,
|
||||
uint32_t message,
|
||||
uint32_t detail,
|
||||
uint32_t data1,
|
||||
uint32_t data2) const noexcept;
|
||||
|
||||
/**
|
||||
* Reparent `child` to `new_parent`. This includes the flush.
|
||||
*/
|
||||
void do_reparent(xcb_window_t child, xcb_window_t new_parent) const;
|
||||
|
||||
/**
|
||||
* Start the XEmbed procedure when `use_xembed_` is enabled. This should be
|
||||
* rerun whenever visibility changes.
|
||||
*/
|
||||
void do_xembed() const;
|
||||
|
||||
/**
|
||||
* The logger instance we will print debug tracing information to.
|
||||
*/
|
||||
@@ -449,9 +428,4 @@ class Editor {
|
||||
* `supports_ewmh_active_window()`.
|
||||
*/
|
||||
mutable std::optional<bool> supports_ewmh_active_window_cache_;
|
||||
|
||||
/**
|
||||
* The atom corresponding to `_XEMBED`.
|
||||
*/
|
||||
xcb_atom_t xcb_xembed_message_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user