diff --git a/native_bridge/src/main.cpp b/native_bridge/src/main.cpp index 6e67f93..517f1c3 100644 --- a/native_bridge/src/main.cpp +++ b/native_bridge/src/main.cpp @@ -1096,7 +1096,7 @@ int main(int argc, char* argv[]) { // attached -> GUI da mo, khong post attach job lan nua. Re-attach // tren view dang attached lam plugin loi (Nexus createView null). if (instruments.withInstrument(guiCh, [](INativeInstrument* i0) { - return i0 && i0->hasAttachedView(); + return i0 && i0->hasAttachedView() && !i0->ownsGuiWindow(); })) { return; } @@ -1120,7 +1120,7 @@ int main(int argc, char* argv[]) { return; } if (auto* inst0 = instruments.get(guiCh)) { - if (inst0->hasAttachedView()) { + if (inst0->hasAttachedView() && !inst0->ownsGuiWindow()) { // Dedupe race: main da silence (setReloading) truoc khi post — // restore de channel khong cam vinh vien. std::vector sp = same_plugin_channels(guiCh); diff --git a/native_bridge/src/plugin_host_main.cpp b/native_bridge/src/plugin_host_main.cpp index 41eddb3..bf340ce 100644 --- a/native_bridge/src/plugin_host_main.cpp +++ b/native_bridge/src/plugin_host_main.cpp @@ -38,10 +38,41 @@ static bool parent_alive(uint32_t pid) { #endif } +// G4.7: the child owns the host window, so the user's X click lands HERE, not +// on the bridge. DefWindowProc destroys the window on WM_CLOSE while the VST2 +// instrument still thinks its editor is attached (guiAttached_ stale true) - +// a second OPEN_GUI then can never re-show the GUI (bridge dedupes on +// hasAttachedView, and the child's openGUI guard skips too). Intercept: +// WM_CLOSE -> effEditClose + hide (keep the window alive, reopen works); +// WM_DESTROY (external destroy) -> forget the hwnd so the next OPEN_GUI +// creates a fresh window instead of attaching to a dead one. +static INativeInstrument* g_wndInst = nullptr; +static HWND g_hostHwnd = nullptr; + +static LRESULT CALLBACK PluginHostWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_CLOSE: + if (g_wndInst) { + try { g_wndInst->closeGUI(); } catch (...) {} + } + if (g_hostHwnd) ShowWindow(g_hostHwnd, SW_HIDE); + return 0; // swallow: do NOT let DefWindowProc destroy the window + case WM_DESTROY: + if (g_wndInst) { + try { g_wndInst->closeGUI(); } catch (...) {} + } + g_hostHwnd = nullptr; + break; + default: + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + static HWND make_host_window() { WNDCLASSEX wc = {}; wc.cbSize = sizeof(wc); - wc.lpfnWndProc = DefWindowProc; + wc.lpfnWndProc = PluginHostWndProc; wc.hInstance = GetModuleHandle(nullptr); wc.lpszClassName = "PluginHostClass"; RegisterClassEx(&wc); @@ -106,6 +137,7 @@ int main(int argc, char* argv[]) { } } INativeInstrument& inst = vst2 ? (INativeInstrument&)instV2 : (INativeInstrument&)instV3; + g_wndInst = &inst; // WM_CLOSE (user X) needs the live instrument // G4.6 auto-play fix: a loaded chunk can embed a stuck/sustained note // (factory state, seed, or a patch captured mid-note). Release everything @@ -145,12 +177,12 @@ int main(int argc, char* argv[]) { // VST3: GUI on the main thread at startup (editorhost pattern: attach then // pump below). VST2: deferred until control 4 (OPEN_GUI) so loading a Qin // does not pop a window for every track. - HWND hwnd = nullptr; + g_hostHwnd = nullptr; if (!vst2) { - hwnd = make_host_window(); - printf("[plugin_host] ownHwnd=%p openGUI...\n", (void*)hwnd); + g_hostHwnd = make_host_window(); + printf("[plugin_host] ownHwnd=%p openGUI...\n", (void*)g_hostHwnd); fflush(stdout); - bool gui = inst.openGUI(hwnd); + bool gui = inst.openGUI(g_hostHwnd); printf("[plugin_host] openGUI=%d\n", gui ? 1 : 0); fflush(stdout); } @@ -208,16 +240,16 @@ int main(int argc, char* argv[]) { for (uint32_t i = 0; i < nctl; ++i) { const auto& c = ipc->base.controlQueue[i]; if (c.type == 4) { - if (!hwnd) hwnd = make_host_window(); - if (hwnd && !inst.hasAttachedView()) { + if (!g_hostHwnd) g_hostHwnd = make_host_window(); + if (g_hostHwnd && !inst.hasAttachedView()) { printf("[plugin_host] OPEN_GUI ch=%u\n", channel); fflush(stdout); - inst.openGUI(hwnd); - ShowWindow(hwnd, SW_SHOW); + inst.openGUI(g_hostHwnd); + ShowWindow(g_hostHwnd, SW_SHOW); } } else if (c.type == 5) { inst.closeGUI(); - if (hwnd) ShowWindow(hwnd, SW_HIDE); + if (g_hostHwnd) ShowWindow(g_hostHwnd, SW_HIDE); } else if (c.type == 6) { quit = true; }