From f1f7523248c876b947c0bdc48c3aee57dd8116ba Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 19 Mar 2020 17:29:30 +0100 Subject: [PATCH] Make the GUI embedding work There's still a few things that need fixing. --- src/common/events.h | 3 +- src/plugin/host-bridge.cpp | 3 +- src/wine-host/editor.cpp | 68 ++++++++++++++++++++++++++++----- src/wine-host/editor.h | 15 ++++++++ src/wine-host/plugin-bridge.cpp | 30 +++++++++++++-- 5 files changed, 104 insertions(+), 15 deletions(-) diff --git a/src/common/events.h b/src/common/events.h index 9a86507c..928ffcd5 100644 --- a/src/common/events.h +++ b/src/common/events.h @@ -42,6 +42,7 @@ class DefaultDataConverter { * whether the host supports these callbacks before sending them! */ virtual std::optional read(const int /*opcode*/, + const int /*index*/, const intptr_t /*value*/, const void* data) { if (data == nullptr) { @@ -132,7 +133,7 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, // some VST hsots will outright crash if they receive them, please let me // know if there's a better way to do this. const std::optional payload = - data_converter.read(opcode, value, data); + data_converter.read(opcode, index, value, data); if (!payload.has_value()) { return 1; } diff --git a/src/plugin/host-bridge.cpp b/src/plugin/host-bridge.cpp index 37bef267..a2ff51e9 100644 --- a/src/plugin/host-bridge.cpp +++ b/src/plugin/host-bridge.cpp @@ -177,6 +177,7 @@ class DispatchDataConverter : DefaultDataConverter { : chunk(chunk_data), rect(editor_rectangle) {} std::optional read(const int opcode, + const int index, const intptr_t value, const void* data) { // There are some events that need specific structs that we can't simply @@ -206,7 +207,7 @@ class DispatchDataConverter : DefaultDataConverter { return DynamicVstEvents(*static_cast(data)); break; default: - return DefaultDataConverter::read(opcode, value, data); + return DefaultDataConverter::read(opcode, index, value, data); break; } } diff --git a/src/wine-host/editor.cpp b/src/wine-host/editor.cpp index ac7f158f..610b29c4 100644 --- a/src/wine-host/editor.cpp +++ b/src/wine-host/editor.cpp @@ -7,12 +7,17 @@ Editor::Editor(std::string window_class_name) x11_connection(xcb_connect(nullptr, nullptr), &xcb_disconnect) {} HWND Editor::open() { + // Create a window without any decoratiosn for easy embedding. The + // combination of `WS_EX_TOOLWINDOW` and `WS_POPUP` causes the window to be + // drawn without any decorations (making resizes behave as you'd expect) and + // also causes mouse coordinates to be relative to the window itself. win32_handle = std::unique_ptr, decltype(&DestroyWindow)>( - CreateWindowEx(WS_EX_TOOLWINDOW, + CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_ACCEPTFILES, reinterpret_cast(window_class), - "yabridge plugin", 0, 0, 0, 256, 256, nullptr, - nullptr, GetModuleHandle(nullptr), nullptr), + "yabridge plugin", WS_POPUP, CW_USEDEFAULT, + CW_USEDEFAULT, 256, 256, nullptr, nullptr, + GetModuleHandle(nullptr), nullptr), &DestroyWindow); return win32_handle->get(); @@ -26,28 +31,73 @@ void Editor::close() { // everything for us? } -#include +// TODO: I feel like this should only have to be done once +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; +} + +// TODO: Below function shouldn't be needed +bool Editor::update() { + if (!win32_handle.has_value()) { + return false; + } + + // TODO: Doing this manually should not be needed + UpdateWindow(win32_handle->get()); + + // TODO: This should also be done somewhere else + // Pump events since the Win32 API won't do it for us + MSG msg; + while (PeekMessage(&msg, win32_handle->get(), 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return true; +} bool Editor::embed_into(const size_t parent_window_handle) { if (!win32_handle.has_value()) { return false; } - // TODO: Swap the order if that works once everything else works so you - // don't get to see the Wine window - ShowWindow(win32_handle->get(), SW_SHOW); - UpdateWindow(win32_handle->get()); - const size_t child_window_handle = get_x11_handle().value(); // TODO: Reparenting works, but the Wine window is not updating so that // might cause it to look like it doesn't work xcb_reparent_window(x11_connection.get(), child_window_handle, parent_window_handle, 0, 0); + + // TODO: Is this needed? + const int parent_events = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | + XCB_EVENT_MASK_STRUCTURE_NOTIFY; + const int child_events = + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE; + xcb_change_window_attributes(x11_connection.get(), parent_window_handle, + XCB_CW_EVENT_MASK, &parent_events); + xcb_change_window_attributes(x11_connection.get(), child_window_handle, + XCB_CW_EVENT_MASK, &child_events); + // TODO: Is this map needed? xcb_map_window(x11_connection.get(), child_window_handle); xcb_flush(x11_connection.get()); + ShowWindow(win32_handle->get(), SW_SHOWNORMAL); + // TODO: We should just immediatly resize the window to the right size + // isntead + UpdateWindow(win32_handle->get()); + return true; } diff --git a/src/wine-host/editor.h b/src/wine-host/editor.h index de1fea62..fc1cbaca 100644 --- a/src/wine-host/editor.h +++ b/src/wine-host/editor.h @@ -9,6 +9,7 @@ #define NOMCX #define NOIMM #define WIN32_LEAN_AND_MEAN +#include #include #include @@ -35,6 +36,20 @@ class Editor { HWND open(); 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); + + // TODO: This should not be needed, and is just a test to see if this works + // at all + bool update(); + /** * Embed the (open) window into a parent window. * diff --git a/src/wine-host/plugin-bridge.cpp b/src/wine-host/plugin-bridge.cpp index 4aadf2f5..405edafa 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -198,6 +198,14 @@ intptr_t PluginBridge::dispatch_wrapper(AEffect* plugin, // We have to intercept GUI open calls since we can't use // the X11 window handle passed by the host switch (opcode) { + case effEditIdle: + // TODO: Hack, shouldn't be needed. We'll just have to process + // events somewhere else. + editor.update(); + + return plugin->dispatcher(plugin, opcode, index, value, data, + option); + break; case effEditOpen: { const auto win32_handle = editor.open(); @@ -240,10 +248,13 @@ void PluginBridge::wait() { class HostCallbackDataConverter : DefaultDataConverter { public: - HostCallbackDataConverter(AEffect* plugin, VstTimeInfo& time_info) - : plugin(plugin), time_info(time_info) {} + HostCallbackDataConverter(AEffect* plugin, + Editor& editor, + VstTimeInfo& time_info) + : plugin(plugin), editor(editor), time_info(time_info) {} std::optional read(const int opcode, + const int index, const intptr_t value, const void* data) { switch (opcode) { @@ -271,8 +282,18 @@ class HostCallbackDataConverter : DefaultDataConverter { // done inside of `passthrough_event`. return AEffect(*plugin); break; + case audioMasterSizeWindow: + // TODO: Do all plugins send this? Or should we also use + // effEditGetRect`or add hooks int oresize events. + // The plugin sends it's own width and hieght in the index and + // value parameters + editor.resize(VstRect{0, 0, static_cast(value), + static_cast(index)}); + + return DefaultDataConverter::read(opcode, index, value, data); + break; default: - return DefaultDataConverter::read(opcode, value, data); + return DefaultDataConverter::read(opcode, index, value, data); break; } } @@ -305,6 +326,7 @@ class HostCallbackDataConverter : DefaultDataConverter { private: AEffect* plugin; + Editor& editor; VstTimeInfo& time_info; }; @@ -314,7 +336,7 @@ intptr_t PluginBridge::host_callback(AEffect* /*plugin*/, intptr_t value, void* data, float option) { - HostCallbackDataConverter converter(plugin, time_info); + HostCallbackDataConverter converter(plugin, editor, time_info); return send_event(vst_host_callback, host_callback_semaphore, converter, std::nullopt, opcode, index, value, data, option); }