fix(fx-gui): meter VST chạy đúng — bỏ điều kiện inRead chặn peek + chống race lúc load + ẩn cửa sổ native floating

This commit is contained in:
2026-08-22 17:36:31 +07:00
parent 8346bc3041
commit 4bb0db2b21
7 changed files with 60 additions and 90 deletions
+23 -77
View File
@@ -270,52 +270,14 @@ uint64_t frameHash(HWND hwnd) {
}
void danceStart(HWND hwnd, HWND target, int x, int y) {
// Anchored mode: place the host window's client area exactly over the embed
// panel rect (borderless + topmost) so real mouse events hit the plugin
// without moving the cursor, then ask the pump thread to size the plugin
// view to the panel. No park deadline -- the window stays until /close.
// Re-runs on every click so a scrolled/resized panel re-anchors.
if (g_embedRect.right > g_embedRect.left && g_embedRect.bottom > g_embedRect.top) {
const int cw = g_embedRect.right - g_embedRect.left;
const int ch = g_embedRect.bottom - g_embedRect.top;
LONG_PTR style = GetWindowLongPtrA(hwnd, GWL_STYLE);
if (style & (WS_CAPTION | WS_THICKFRAME | WS_SYSMENU |
WS_MINIMIZEBOX | WS_MAXIMIZEBOX)) {
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_SYSMENU |
WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
SetWindowLongPtrA(hwnd, GWL_STYLE, style);
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_NOACTIVATE | SWP_FRAMECHANGED);
}
SetWindowPos(hwnd, HWND_TOPMOST, g_embedRect.left, g_embedRect.top,
cw, ch, SWP_NOACTIVATE | SWP_SHOWWINDOW);
fxGuiRequestResize(hwnd, cw, ch);
Sleep(60); // let the compositor repaint so the editor sees itself active
g_dance = true;
g_anchored = true;
g_parkUntil = 0; // anchored: stay until /close
return;
}
if (g_dance) return;
// Dat window ngay tai vi tri cursor (dang nam tren panel embed trong DAW)
// chu khong nhay ra goc man hinh: shift window sao cho client point (x,y)
// cua editor trung voi screen point dang click -> GUI xuat hien dung cho
// panel, khong con floating ngoai cua so DAW.
POINT pt = { x, y };
ClientToScreen(target, &pt); // screen cua diem click (window con offscreen)
POINT cur;
GetCursorPos(&cur);
RECT wr = {0};
GetWindowRect(hwnd, &wr);
const int nx = wr.left + (cur.x - pt.x);
const int ny = wr.top + (cur.y - pt.y);
GetCursorPos(&g_savedCursor);
SetWindowPos(hwnd, HWND_TOP, nx, ny, 0, 0,
SWP_NOSIZE | SWP_SHOWWINDOW);
Sleep(60); // let the compositor repaint so the editor sees itself active
g_dance = true;
g_parkUntil = GetTickCount() + 3000;
// Embedded mode: the native window NEVER comes on-screen — showing it
// topmost over the panel was the "floating always-on-top" bug. Real
// hardware input would require the window on-screen, so it is disabled;
// relayInput always uses synthetic PostMessage.
// ponytail: re-enable real input only with a proper sibling placement
// (owned window, WS_EX_NOACTIVATE, no WS_EX_TOPMOST) if a plugin rejects
// synthetic clicks.
(void)hwnd; (void)target; (void)x; (void)y;
}
void danceEnd(HWND hwnd) {
@@ -382,12 +344,7 @@ void relayInput(HWND hwnd, const json_object_s* o) {
// responds (reset miss); two consecutive no-response clicks mean the
// editor rejects synthetic input — switch to real hardware input.
if (!g_realInput) {
if (g_embedRect.right > g_embedRect.left) {
// Anchored mode exists for plugins that need real input; the window
// appears exactly over the panel, so probing PostMessage first only
// wastes clicks.
g_realInput = true;
} else if (isDown) {
if (isDown) {
g_preClickHash = frameHash(hwnd);
} else if (isUp && g_preClickHash) {
Sleep(250);
@@ -396,8 +353,7 @@ void relayInput(HWND hwnd, const json_object_s* o) {
if (resp) {
g_miss = 0; // PostMessage responded — keep it
} else if (++g_miss >= 2) {
g_realInput = true; // synthetic input rejected — go real
g_miss = 0;
g_miss = 0; // real-input mode disabled: window must stay off-screen
}
g_preClickHash = 0;
}
@@ -556,29 +512,19 @@ void runHttpServer(HWND hwnd, std::atomic<bool>& stop) {
}
}
if (sroot) std::free(sroot);
if (rectOk) {
const int cw = g_embedRect.right - g_embedRect.left;
const int ch = g_embedRect.bottom - g_embedRect.top;
RECT cur = {};
GetWindowRect(hwnd, &cur);
const bool same = cur.left == g_embedRect.left && cur.top == g_embedRect.top &&
cur.right - cur.left == cw && cur.bottom - cur.top == ch;
// Frontend re-POSTs /show every ~500ms to re-anchor on scroll;
// skip both when nothing moved so the plugin view is not
// re-laid-out in a loop. Re-show after /hide regardless.
if (!same || !IsWindowVisible(hwnd))
SetWindowPos(hwnd, HWND_TOPMOST, g_embedRect.left, g_embedRect.top,
cw, ch, SWP_NOACTIVATE | SWP_SHOWWINDOW);
if (cur.right - cur.left != cw || cur.bottom - cur.top != ch)
fxGuiRequestResize(hwnd, cw, ch);
g_dance = true;
g_anchored = true;
g_parkUntil = 0;
} else {
ShowWindow(hwnd, SW_SHOW);
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
}
// Embedded mode: the native window NEVER comes on-screen — it stays
// parked off-screen at -20000,-20000 (GDI capture + synthetic input
// work regardless of position). The panel rect is still recorded
// for the (disabled) real-input fallback and to keep the API
// contract; /hide hid the window, so re-show it off-screen so
// /frame keeps capturing.
(void)rectOk;
if (!IsWindowVisible(hwnd))
SetWindowPos(hwnd, HWND_NOTOPMOST, -20000, -20000, 0, 0,
SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
g_dance = false;
g_anchored = false;
g_parkUntil = 0;
sendResponse(c, 200, "application/json", "{\"ok\":true}", 11);
} else if (req.path == "/preset") {
std::string b64;