Merge plugin-editor drop: a card dragged onto a third-party plugin's editor is handed to the OS as a file drop
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "shell/instrument/editor_internal.h" // (transitively: lice + the kit, Windows only)
|
||||
#include "shell/instrument/reasampler_processor.h"
|
||||
#include "core/version/app_version.h" // vstOutputName — channel-forked window class name
|
||||
|
||||
using namespace Steinberg;
|
||||
|
||||
@@ -20,7 +21,32 @@ namespace reasampler::vst {
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace {
|
||||
constexpr const wchar_t* kChildClassName = L"ReaSampler9000VstEditor";
|
||||
|
||||
// Channel-forked so stable and beta never fight over one class registration (a second
|
||||
// RegisterClassW against an already-taken name silently fails, leaving that channel's editors
|
||||
// running the FIRST-registered channel's wndProc). vstOutputName() is ASCII (CMake OUTPUT_NAME),
|
||||
// so a plain widen is safe.
|
||||
const std::wstring& childClassName() {
|
||||
static const std::wstring name = [] {
|
||||
const std::string& base = version::vstOutputName();
|
||||
return std::wstring(base.begin(), base.end()) + L"VstEditor";
|
||||
}();
|
||||
return name;
|
||||
}
|
||||
|
||||
// The module holding a given code address — used to resolve THIS wndProc's own module rather
|
||||
// than the parent's hInstance REAPER hands us. Registering against the parent would put the class
|
||||
// in REAPER's own module while the wndproc still points into our DLL — a dangling procedure
|
||||
// pointer once our DLL unloads but REAPER's class registration outlives it. Takes the address
|
||||
// rather than naming wndProc directly so a free function in this anonymous namespace doesn't need
|
||||
// access to the private member.
|
||||
HMODULE moduleHandleFor(void* codeAddress) {
|
||||
HMODULE mod = nullptr;
|
||||
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
reinterpret_cast<LPCWSTR>(codeAddress), &mod);
|
||||
return mod;
|
||||
}
|
||||
|
||||
// The change-detection poll (WM_TIMER on the child window). A low-frequency UI-thread
|
||||
// timer: responsive enough that a recapture/ingest/assign refreshes within a bounded
|
||||
@@ -105,8 +131,9 @@ void ReaSamplerEditor::attachedToParent() {
|
||||
HWND parent = static_cast<HWND>(systemWindow);
|
||||
if (!parent) return;
|
||||
|
||||
HINSTANCE hInst =
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parent, GWLP_HINSTANCE));
|
||||
// Own module, not the parent's hInstance — see moduleHandleFor() above.
|
||||
HINSTANCE hInst = reinterpret_cast<HINSTANCE>(
|
||||
moduleHandleFor(reinterpret_cast<void*>(&ReaSamplerEditor::wndProc)));
|
||||
if (!hInst) hInst = GetModuleHandle(nullptr);
|
||||
|
||||
static bool classRegistered = false;
|
||||
@@ -114,7 +141,7 @@ void ReaSamplerEditor::attachedToParent() {
|
||||
WNDCLASSW wc{};
|
||||
wc.lpfnWndProc = &ReaSamplerEditor::wndProc;
|
||||
wc.hInstance = hInst;
|
||||
wc.lpszClassName = kChildClassName;
|
||||
wc.lpszClassName = childClassName().c_str();
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
// CS_DBLCLKS is what makes WM_LBUTTONDBLCLK arrive at all. It also REPLACES the second
|
||||
// WM_LBUTTONDOWN of a double-click, so every surface that counted two downs (Browse's
|
||||
@@ -134,7 +161,7 @@ void ReaSamplerEditor::attachedToParent() {
|
||||
refreshFromBank();
|
||||
|
||||
const ViewRect& r = getRect();
|
||||
childHwnd_ = CreateWindowExW(0, kChildClassName, L"", WS_CHILD | WS_VISIBLE, 0, 0,
|
||||
childHwnd_ = CreateWindowExW(0, childClassName().c_str(), L"", WS_CHILD | WS_VISIBLE, 0, 0,
|
||||
r.getWidth(), r.getHeight(), parent, nullptr, hInst, nullptr);
|
||||
if (childHwnd_) {
|
||||
SetWindowLongPtr(childHwnd_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
|
||||
|
||||
Reference in New Issue
Block a user