From e7e1b264559bb42f96a8fbda305bfd37121905c7 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 17 Mar 2020 01:50:50 +0100 Subject: [PATCH] Add the base for GUI handling Still need to embed the opened window into the window provided by the host. --- src/common/events.h | 12 ++++++++- src/common/logging.cpp | 8 ++++-- src/common/serialization.h | 48 +++++++++++++++++++-------------- src/plugin/host-bridge.cpp | 23 ++++++++++++++-- src/wine-host/plugin-bridge.cpp | 24 +++++++++++++++-- src/wine-host/plugin-bridge.h | 3 +++ 6 files changed, 91 insertions(+), 27 deletions(-) diff --git a/src/common/events.h b/src/common/events.h index c03707ed..89c85977 100644 --- a/src/common/events.h +++ b/src/common/events.h @@ -208,7 +208,8 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket, }, [&](WantsChunkBuffer&) -> void* { return string_buffer.data(); }, [&](const WantsVstTimeInfo&) -> void* { return nullptr; }, - [&](WantsString&) -> void* { return string_buffer.data(); }}, + [&](WantsString&) -> void* { return string_buffer.data(); }, + [&](WantsWindowHandle&) -> void* { return string_buffer.data(); }}, event.payload); const intptr_t return_value = callback(plugin, event.opcode, event.index, @@ -263,6 +264,15 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket, }, [&](WantsString&) -> EventResposnePayload { return std::string(static_cast(data)); + }, + [&](WantsWindowHandle&) -> EventResposnePayload { + // This is a bit of a hack, but I couldn't think of a nicer + // way to do this since it's only needed for the + // `effEditOpen` event. We override the callback function + // to create a Win32 window, pass that to the plugin, and + // then write the corresponding X11 window handle to the + // data pointer. + return *reinterpret_cast(data); }}, event.payload); diff --git a/src/common/logging.cpp b/src/common/logging.cpp index 6dac0676..383af21a 100644 --- a/src/common/logging.cpp +++ b/src/common/logging.cpp @@ -175,7 +175,8 @@ void Logger::log_event(bool is_dispatch, message << ""; }, [&](const WantsVstTimeInfo&) { message << ""; }, - [&](const WantsString&) { message << ""; }}, + [&](const WantsString&) { message << ""; }, + [&](const WantsWindowHandle&) { message << ""; }}, payload); message << ")"; @@ -209,7 +210,10 @@ void Logger::log_event_response(bool is_dispatch, } }, [&](const AEffect&) { message << ", "; }, - [&](const VstTimeInfo&) { message << ", "; }}, + [&](const VstTimeInfo&) { message << ", "; }, + [&](const intptr_t& window_handle) { + message << ", window #" << window_handle; + }}, payload); log(message.str()); diff --git a/src/common/serialization.h b/src/common/serialization.h index bb3d8f30..37540fbf 100644 --- a/src/common/serialization.h +++ b/src/common/serialization.h @@ -164,6 +164,12 @@ struct WantsVstTimeInfo {}; */ struct WantsString {}; +/** + * The event handler (the Wine VST host) should create a Winn32 window, pass + * that to the plugin, and return an X11 window handle. + */ +struct WantsWindowHandle {}; + /** * VST events are passed a void pointer that can contain a variety of different * data types depending on the event's opcode. This is typically either: @@ -197,27 +203,27 @@ using EventPayload = std::variant; + WantsString, + WantsWindowHandle>; template void serialize(S& s, EventPayload& payload) { - s.ext(payload, bitsery::ext::StdVariant{ - [](S&, std::nullptr_t&) {}, - [](S& s, std::string& string) { - // `binary_buffer_size` and not `max_string_length` - // because we also use this to send back large chunk - // data - s.text1b(string, binary_buffer_size); - }, - [](S& s, AEffect& effect) { s.object(effect); }, - [](S& s, DynamicVstEvents& events) { - s.container(events.events, max_midi_events, - [](S& s, VstEvent& event) { - s.container1b(event.dump); - }); - }, - [](S&, WantsChunkBuffer&) {}, - [](S&, WantsVstTimeInfo&) {}, [](S&, WantsString&) {}}); + s.ext(payload, + bitsery::ext::StdVariant{ + [](S&, std::nullptr_t&) {}, + [](S& s, std::string& string) { + // `binary_buffer_size` and not `max_string_length` because we + // also use this to send back large chunk data + s.text1b(string, binary_buffer_size); + }, + [](S& s, AEffect& effect) { s.object(effect); }, + [](S& s, DynamicVstEvents& events) { + s.container( + events.events, max_midi_events, + [](S& s, VstEvent& event) { s.container1b(event.dump); }); + }, + [](S&, WantsChunkBuffer&) {}, [](S&, WantsVstTimeInfo&) {}, + [](S&, WantsString&) {}, [](S&, WantsWindowHandle&) {}}); } /** @@ -270,9 +276,10 @@ struct Event { * instenad. * - A specific struct in response to an event such as `audioMasterGetTime` or * `audioMasterIOChanged`. + * - An X11 window pointer for the editor window. */ using EventResposnePayload = - std::variant; + std::variant; template void serialize(S& s, EventResposnePayload& payload) { @@ -286,7 +293,8 @@ void serialize(S& s, EventResposnePayload& payload) { s.text1b(string, binary_buffer_size); }, [](S& s, AEffect& effect) { s.object(effect); }, - [](S& s, VstTimeInfo& time_info) { s.object(time_info); }}); + [](S& s, VstTimeInfo& time_info) { s.object(time_info); }, + [](S& s, intptr_t& v) { s.value8b(v); }}); } /** diff --git a/src/plugin/host-bridge.cpp b/src/plugin/host-bridge.cpp index 965e82c5..6a66d7d3 100644 --- a/src/plugin/host-bridge.cpp +++ b/src/plugin/host-bridge.cpp @@ -183,7 +183,6 @@ class DispatchDataConverter : DefaultDataConverter { switch (opcode) { // TODO: Add GUI support. These events are just disabled for now to // ensure everything else works first. - case effEditOpen: case effEditTop: case effEditIdle: case effEditClose: @@ -195,6 +194,9 @@ class DispatchDataConverter : DefaultDataConverter { return std::nullopt; break; + case effEditOpen: + return WantsWindowHandle(); + break; case effGetChunk: return WantsChunkBuffer(); break; @@ -214,6 +216,10 @@ class DispatchDataConverter : DefaultDataConverter { void write(const int opcode, void* data, const EventResult& response) { switch (opcode) { + case effEditOpen: + // TODO: Write the returned window handle somewhere + DefaultDataConverter::write(opcode, data, response); + break; case effGetChunk: // Write the chunk data to some publically accessible place in // `HostBridge` and write a pointer to that struct to the data @@ -253,8 +259,21 @@ intptr_t HostBridge::dispatch(AEffect* /*plugin*/, DispatchDataConverter converter(chunk_data); // Some events need some extra handling - // TODO: Handle other things such as GUI itneraction + // TODO: Handle GUI closing? switch (opcode) { + case effEditOpen: + // TODO: Should work as follows: + // 1. Create a window in the Wine host using the Windows APIs + // 2. Return the X11 window handle + // 4. Use XEmbed to embmed the Wine window inside the parent + // window stored in the data void pointer + // 5. Return a handle to the X11 window + send_event(host_vst_dispatch, dispatch_semaphore, converter, + std::pair(logger, true), opcode, index, + value, data, option); + + return 0; + break; case effClose: // TODO: Gracefully close the editor? // TODO: Check whether the sockets and the endpoint are closed diff --git a/src/wine-host/plugin-bridge.cpp b/src/wine-host/plugin-bridge.cpp index 44c44bd8..9db39043 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -123,8 +123,28 @@ PluginBridge::PluginBridge(std::string plugin_dll_path, // lockstep anyway dispatch_handler = std::thread([&]() { while (true) { - passthrough_event(host_vst_dispatch, std::nullopt, plugin, - plugin->dispatcher); + passthrough_event( + host_vst_dispatch, std::nullopt, plugin, + [&](AEffect* plugin, int opcode, int index, intptr_t value, + void* data, float option) -> intptr_t { + // TODO: editEffClose + // We have to intercept GUI open calls since we can't use + // the X11 window handle passed by the host + if (opcode == effEditOpen) { + const auto handle = editor.open(); + const intptr_t return_value = plugin->dispatcher( + plugin, opcode, index, value, handle, option); + if (return_value == 0) { + return 0; + } + + // TODO: Return X11 handle + return return_value; + } + + return plugin->dispatcher(plugin, opcode, index, value, + data, option); + }); } }); diff --git a/src/wine-host/plugin-bridge.h b/src/wine-host/plugin-bridge.h index 6d9701fe..ee219fe9 100644 --- a/src/wine-host/plugin-bridge.h +++ b/src/wine-host/plugin-bridge.h @@ -31,6 +31,7 @@ #include #include "../common/logging.h" +#include "win32-editor.h" /** * This handles the communication between the Linux native VST plugin and the @@ -128,4 +129,6 @@ class PluginBridge { * `processReplacing` calls. */ std::vector process_buffer; + + Win32Editor editor; };