mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-09 20:29:10 +02:00
Make the GUI embedding work
There's still a few things that need fixing.
This commit is contained in:
+2
-1
@@ -42,6 +42,7 @@ class DefaultDataConverter {
|
|||||||
* whether the host supports these callbacks before sending them!
|
* whether the host supports these callbacks before sending them!
|
||||||
*/
|
*/
|
||||||
virtual std::optional<EventPayload> read(const int /*opcode*/,
|
virtual std::optional<EventPayload> read(const int /*opcode*/,
|
||||||
|
const int /*index*/,
|
||||||
const intptr_t /*value*/,
|
const intptr_t /*value*/,
|
||||||
const void* data) {
|
const void* data) {
|
||||||
if (data == nullptr) {
|
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
|
// some VST hsots will outright crash if they receive them, please let me
|
||||||
// know if there's a better way to do this.
|
// know if there's a better way to do this.
|
||||||
const std::optional<EventPayload> payload =
|
const std::optional<EventPayload> payload =
|
||||||
data_converter.read(opcode, value, data);
|
data_converter.read(opcode, index, value, data);
|
||||||
if (!payload.has_value()) {
|
if (!payload.has_value()) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ class DispatchDataConverter : DefaultDataConverter {
|
|||||||
: chunk(chunk_data), rect(editor_rectangle) {}
|
: chunk(chunk_data), rect(editor_rectangle) {}
|
||||||
|
|
||||||
std::optional<EventPayload> read(const int opcode,
|
std::optional<EventPayload> read(const int opcode,
|
||||||
|
const int index,
|
||||||
const intptr_t value,
|
const intptr_t value,
|
||||||
const void* data) {
|
const void* data) {
|
||||||
// There are some events that need specific structs that we can't simply
|
// There are some events that need specific structs that we can't simply
|
||||||
@@ -206,7 +207,7 @@ class DispatchDataConverter : DefaultDataConverter {
|
|||||||
return DynamicVstEvents(*static_cast<const VstEvents*>(data));
|
return DynamicVstEvents(*static_cast<const VstEvents*>(data));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return DefaultDataConverter::read(opcode, value, data);
|
return DefaultDataConverter::read(opcode, index, value, data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,17 @@ Editor::Editor(std::string window_class_name)
|
|||||||
x11_connection(xcb_connect(nullptr, nullptr), &xcb_disconnect) {}
|
x11_connection(xcb_connect(nullptr, nullptr), &xcb_disconnect) {}
|
||||||
|
|
||||||
HWND Editor::open() {
|
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 =
|
win32_handle =
|
||||||
std::unique_ptr<std::remove_pointer_t<HWND>, decltype(&DestroyWindow)>(
|
std::unique_ptr<std::remove_pointer_t<HWND>, decltype(&DestroyWindow)>(
|
||||||
CreateWindowEx(WS_EX_TOOLWINDOW,
|
CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_ACCEPTFILES,
|
||||||
reinterpret_cast<LPCSTR>(window_class),
|
reinterpret_cast<LPCSTR>(window_class),
|
||||||
"yabridge plugin", 0, 0, 0, 256, 256, nullptr,
|
"yabridge plugin", WS_POPUP, CW_USEDEFAULT,
|
||||||
nullptr, GetModuleHandle(nullptr), nullptr),
|
CW_USEDEFAULT, 256, 256, nullptr, nullptr,
|
||||||
|
GetModuleHandle(nullptr), nullptr),
|
||||||
&DestroyWindow);
|
&DestroyWindow);
|
||||||
|
|
||||||
return win32_handle->get();
|
return win32_handle->get();
|
||||||
@@ -26,28 +31,73 @@ void Editor::close() {
|
|||||||
// everything for us?
|
// everything for us?
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <iostream>
|
// 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) {
|
bool Editor::embed_into(const size_t parent_window_handle) {
|
||||||
if (!win32_handle.has_value()) {
|
if (!win32_handle.has_value()) {
|
||||||
return false;
|
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();
|
const size_t child_window_handle = get_x11_handle().value();
|
||||||
|
|
||||||
// TODO: Reparenting works, but the Wine window is not updating so that
|
// TODO: Reparenting works, but the Wine window is not updating so that
|
||||||
// might cause it to look like it doesn't work
|
// might cause it to look like it doesn't work
|
||||||
xcb_reparent_window(x11_connection.get(), child_window_handle,
|
xcb_reparent_window(x11_connection.get(), child_window_handle,
|
||||||
parent_window_handle, 0, 0);
|
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?
|
// TODO: Is this map needed?
|
||||||
xcb_map_window(x11_connection.get(), child_window_handle);
|
xcb_map_window(x11_connection.get(), child_window_handle);
|
||||||
xcb_flush(x11_connection.get());
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#define NOMCX
|
#define NOMCX
|
||||||
#define NOIMM
|
#define NOIMM
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <vestige/aeffectx.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -35,6 +36,20 @@ class Editor {
|
|||||||
HWND open();
|
HWND open();
|
||||||
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);
|
||||||
|
|
||||||
|
// 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.
|
* Embed the (open) window into a parent window.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -198,6 +198,14 @@ intptr_t PluginBridge::dispatch_wrapper(AEffect* plugin,
|
|||||||
// We have to intercept GUI open calls since we can't use
|
// We have to intercept GUI open calls since we can't use
|
||||||
// the X11 window handle passed by the host
|
// the X11 window handle passed by the host
|
||||||
switch (opcode) {
|
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: {
|
case effEditOpen: {
|
||||||
const auto win32_handle = editor.open();
|
const auto win32_handle = editor.open();
|
||||||
|
|
||||||
@@ -240,10 +248,13 @@ void PluginBridge::wait() {
|
|||||||
|
|
||||||
class HostCallbackDataConverter : DefaultDataConverter {
|
class HostCallbackDataConverter : DefaultDataConverter {
|
||||||
public:
|
public:
|
||||||
HostCallbackDataConverter(AEffect* plugin, VstTimeInfo& time_info)
|
HostCallbackDataConverter(AEffect* plugin,
|
||||||
: plugin(plugin), time_info(time_info) {}
|
Editor& editor,
|
||||||
|
VstTimeInfo& time_info)
|
||||||
|
: plugin(plugin), editor(editor), time_info(time_info) {}
|
||||||
|
|
||||||
std::optional<EventPayload> read(const int opcode,
|
std::optional<EventPayload> read(const int opcode,
|
||||||
|
const int index,
|
||||||
const intptr_t value,
|
const intptr_t value,
|
||||||
const void* data) {
|
const void* data) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
@@ -271,8 +282,18 @@ class HostCallbackDataConverter : DefaultDataConverter {
|
|||||||
// done inside of `passthrough_event`.
|
// done inside of `passthrough_event`.
|
||||||
return AEffect(*plugin);
|
return AEffect(*plugin);
|
||||||
break;
|
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<short>(value),
|
||||||
|
static_cast<short>(index)});
|
||||||
|
|
||||||
|
return DefaultDataConverter::read(opcode, index, value, data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DefaultDataConverter::read(opcode, value, data);
|
return DefaultDataConverter::read(opcode, index, value, data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,6 +326,7 @@ class HostCallbackDataConverter : DefaultDataConverter {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
AEffect* plugin;
|
AEffect* plugin;
|
||||||
|
Editor& editor;
|
||||||
VstTimeInfo& time_info;
|
VstTimeInfo& time_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -314,7 +336,7 @@ intptr_t PluginBridge::host_callback(AEffect* /*plugin*/,
|
|||||||
intptr_t value,
|
intptr_t value,
|
||||||
void* data,
|
void* data,
|
||||||
float option) {
|
float option) {
|
||||||
HostCallbackDataConverter converter(plugin, time_info);
|
HostCallbackDataConverter converter(plugin, editor, time_info);
|
||||||
return send_event(vst_host_callback, host_callback_semaphore, converter,
|
return send_event(vst_host_callback, host_callback_semaphore, converter,
|
||||||
std::nullopt, opcode, index, value, data, option);
|
std::nullopt, opcode, index, value, data, option);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user