Fix GUI Qin không hiện lại sau hide + âm thanh lỗi sau khi đóng cửa sổ

- plugin_host_main.cpp: chặn WM_CLOSE (đóng GUI nhưng giữ window sống, hide
  thay vì destroy), WM_DESTROY reset hwnd; dùng g_hostHwnd toàn cục thay
  hwnd local để control OPEN_GUI/CLOSE_GUI tái sử dụng đúng window.
- main.cpp: bỏ qua dedupe hasAttachedView cho instrument child-own
  (SandboxVst2Host) — bridge guiOpen_ stale sau khi child tự đóng nên
  OPEN_GUI lặp lại phải forward control 4; child tự chặn double-open.
- Verify: WM_CLOSE -> window ẩn còn sống; OPEN_GUI lần 2 -> window hiện lại,
  âm ch1 vẫn kêu (peak 0.15), không câm.
This commit is contained in:
2026-08-21 09:21:56 +07:00
parent a7b01d3484
commit e7e974b3e0
2 changed files with 44 additions and 12 deletions
+42 -10
View File
@@ -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;
}