Add a less hacky workaround for the XEmbed issues

This commit is contained in:
Robbert van der Helm
2020-04-09 18:21:16 +02:00
parent 1644d74d8d
commit e8fc990f0b
4 changed files with 37 additions and 69 deletions
+4 -9
View File
@@ -7,15 +7,10 @@ Yet Another way to use Windows VST2 plugins in Linux VST hosts.
There are a few things that should be done before releasing this, including: There are a few things that should be done before releasing this, including:
- Implement missing features: - Implement missing features:
- GUIs. The current basic implementation with XEmbed suffers from flickering - Small quality of life related GUI fixes. Wine's XEmbed implementation
during redrwas (in Serum, depends on the plugin) and it has the usual doesn't always update the reparented window's client area when the window
problems with resizing. Reparenting without XEmbed works great, but it gets resized. The current workaround works much better than not doing
breaks a lot of GUI elements because the plugin still thinks it's in the top anything at all, but it isn't fully reliably yet.
left corner of the screen. If that could be fixed that would be ideal.
- Fix implementation bugs:
- Closing Serum's editor takes a full second to execute `DestroyWindow`. After
fixing XEmbed it might be possible to at least make it feel responsive by
just hiding the window first.
- Add missing details if any to the architecture section. - Add missing details if any to the architecture section.
- Document what this has been tested on and what does or does not work. - Document what this has been tested on and what does or does not work.
- Document wine32 support. - Document wine32 support.
+25 -22
View File
@@ -62,9 +62,7 @@ HWND Editor::open(AEffect* effect, xcb_window_t parent_window_handle) {
// the plugin is not busy. // the plugin is not busy.
SetTimer(win32_handle->get(), idle_timer_id, 100, nullptr); SetTimer(win32_handle->get(), idle_timer_id, 100, nullptr);
// We'll only start the xembed procedure after the host has givne the window const uint32_t event_mask = XCB_EVENT_MASK_VISIBILITY_CHANGE;
// the correct size, otherwise Wine can't draw correctly
const uint32_t event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
xcb_change_window_attributes(x11_connection.get(), parent_window, xcb_change_window_attributes(x11_connection.get(), parent_window,
XCB_CW_EVENT_MASK, &event_mask); XCB_CW_EVENT_MASK, &event_mask);
xcb_flush(x11_connection.get()); xcb_flush(x11_connection.get());
@@ -80,19 +78,6 @@ void Editor::close() {
// everything for us? // everything for us?
} }
// TODO: I feel like this shouldn't necessary with xembed
bool Editor::resize(const VstRect& new_size) {
if (!win32_handle.has_value()) {
return false;
}
SetWindowPos(win32_handle->get(), HWND_TOP, new_size.left, new_size.top,
new_size.right - new_size.left, new_size.bottom - new_size.top,
0);
return true;
}
bool Editor::xembed() { bool Editor::xembed() {
if (!win32_handle.has_value()) { if (!win32_handle.has_value()) {
return false; return false;
@@ -152,15 +137,33 @@ void Editor::handle_events() {
} }
// Handle X11 events // Handle X11 events
// TODO: We don't listen for XEmbed property changes for mapping and
// unmapping the window as described in the spec. Is this
// something we should do?
// TODO: Also, increasing window size does not work reliably, any way we
// could make this work better?
xcb_generic_event_t* event; xcb_generic_event_t* event;
while ((event = xcb_poll_for_event(x11_connection.get())) != nullptr) { while ((event = xcb_poll_for_event(x11_connection.get())) != nullptr) {
if ((event->response_type & ~0x80) == XCB_CONFIGURE_NOTIFY) { // The most significant bit in an event's response type is used to
xcb_configure_notify_event_t configuration = // indicate whether the event source
*reinterpret_cast<xcb_configure_notify_event_t*>(event); switch (event->response_type & ((1 << 7) - 1)) {
case XCB_VISIBILITY_NOTIFY: {
xcb_visibility_notify_event_t visibility_event =
*reinterpret_cast<xcb_visibility_notify_event_t*>(
event);
// TODO: Only has to be done once, and the problems mentioned in // Wine's XEmbed implementation is a bit iffy when it comes
// the readme are still here. // to window size changes, including editor windows being
xembed(); // increased in size by the DAW when first opening the
// editor. Restarting the XEmbed protocol on visibility and
// size changes is an easy workaround, but it's not 100%
// reliable.
if (visibility_event.window == parent_window &&
visibility_event.state !=
XCB_VISIBILITY_FULLY_OBSCURED) {
xembed();
}
} break;
} }
free(event); free(event);
-10
View File
@@ -41,16 +41,6 @@ class Editor {
HWND open(AEffect* effect, xcb_window_t parent_window_handle); HWND open(AEffect* effect, xcb_window_t parent_window_handle);
void close(); void close();
/**
* Resize the window to match the given size, if open.
*
* @param new_size The rectangle with the plugin's current position.
*
* @return Whether the resizing was succesful. Will return false if the
* editor isn't open.
*/
bool resize(const VstRect& new_size);
/** /**
* Pump messages from the editor GUI's event loop until all events are * Pump messages from the editor GUI's event loop until all events are
* process. Must be run from the same thread the GUI was created in because * process. Must be run from the same thread the GUI was created in because
+8 -28
View File
@@ -101,10 +101,10 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
host_vst_process_replacing.connect(socket_endpoint); host_vst_process_replacing.connect(socket_endpoint);
vst_host_aeffect.connect(socket_endpoint); vst_host_aeffect.connect(socket_endpoint);
// Initialize after communication has been set up We'll try to do the same // Initialize after communication has been set up
// `get_bridge_isntance` trick as in `plugin/plugin.cpp`, but since the // We'll try to do the same `get_bridge_isntance` trick as in
// plugin will probably call the host callback while it's initializing we // `plugin/plugin.cpp`, but since the plugin will probably call the host
// sadly have to use a global here. // callback while it's initializing we sadly have to use a global here.
current_bridge_isntance = this; current_bridge_isntance = this;
plugin = vst_entry_point(host_callback_proxy); plugin = vst_entry_point(host_callback_proxy);
if (plugin == nullptr) { if (plugin == nullptr) {
@@ -112,14 +112,14 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
"' failed to initialize."); "' failed to initialize.");
} }
// Send the plugin's information to the Linux VST plugin. Any updates during
// runtime are handled using the `audioMasterIOChanged` host callback.
write_object(vst_host_aeffect, *plugin);
// We only needed this little hack during initialization // We only needed this little hack during initialization
current_bridge_isntance = nullptr; current_bridge_isntance = nullptr;
plugin->ptr1 = this; plugin->ptr1 = this;
// Send the plugin's information to the Linux VST plugin. Any updates during
// runtime are handled using the `audioMasterIOChanged` host callback.
write_object(vst_host_aeffect, *plugin);
// This works functionally identically to the `handle_dispatch()` function // This works functionally identically to the `handle_dispatch()` function
// below, but this socket will only handle midi events. This is needed // below, but this socket will only handle midi events. This is needed
// because of Win32 API limitations. // because of Win32 API limitations.
@@ -254,17 +254,6 @@ intptr_t PluginBridge::dispatch_wrapper(AEffect* plugin,
return return_value; return return_value;
} break; } break;
case effEditGetRect: {
const intptr_t return_value =
plugin->dispatcher(plugin, opcode, index, value, data, option);
// Intercept these calls to make sure that the window (embedded
// within the X11 window) is large enough.
const auto size = **static_cast<VstRect**>(data);
editor.resize(size);
return return_value;
} break;
default: default:
return plugin->dispatcher(plugin, opcode, index, value, data, return plugin->dispatcher(plugin, opcode, index, value, data,
option); option);
@@ -308,15 +297,6 @@ class HostCallbackDataConverter : DefaultDataConverter {
// done inside of `passthrough_event`. // done inside of `passthrough_event`.
return AEffect(*plugin); return AEffect(*plugin);
break; break;
case audioMasterSizeWindow:
// TODO: Does the plugin not do this automatically? Check Some
// plugins will the host that their size has changed, so
// we'll have to change the window for it.
editor.resize(VstRect{0, 0, static_cast<short>(value),
static_cast<short>(index)});
return DefaultDataConverter::read(opcode, index, value, data);
break;
default: default:
return DefaultDataConverter::read(opcode, index, value, data); return DefaultDataConverter::read(opcode, index, value, data);
break; break;