feat(native): realtime master FX chain + fx-gui fast start, hide/show, non-topmost
- FxGuiServer: HWND_TOP (no longer always-on-top), /hide + /show endpoints, onStarted callback prints SF_FXGUI_PORT before plugin load (fast connect) - RenderFxJob: RealtimeFxChain (mutex+worker, SEH-guarded, bypass slots, deferred Vst3Fx dtor); --fx-gui split into parse/create/load-attach stages - main: FreeConsole unless SF_KEEP_CONSOLE; control type 6 SET_FX_CHAIN feeds g_fxChain on the audio loop; shutdown before CoUninitialize - plugins.py: CREATE_NO_WINDOW on both Popen paths - lib.rs: set_fx_chain command -> push_control(6) - app.jsx/nativeBridgeService: setFxChain sync from MasteringModal; panel close uses /hide + refs to fix closure bug; VstGuiEmbed rect refresh -> /show
This commit is contained in:
@@ -283,7 +283,7 @@ void danceStart(HWND hwnd, HWND target, int x, int y) {
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
|
||||
SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
||||
}
|
||||
SetWindowPos(hwnd, HWND_TOPMOST, g_embedRect.left, g_embedRect.top,
|
||||
SetWindowPos(hwnd, HWND_TOP, 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
|
||||
@@ -306,7 +306,7 @@ void danceStart(HWND hwnd, HWND target, int x, int y) {
|
||||
const int nx = wr.left + (cur.x - pt.x);
|
||||
const int ny = wr.top + (cur.y - pt.y);
|
||||
GetCursorPos(&g_savedCursor);
|
||||
SetWindowPos(hwnd, HWND_TOPMOST, nx, ny, 0, 0,
|
||||
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;
|
||||
@@ -516,6 +516,50 @@ void runHttpServer(HWND hwnd, std::atomic<bool>& stop) {
|
||||
if (root) std::free(root);
|
||||
sendResponse(c, 400, "application/json", "{\"error\":\"bad json\"}", 20);
|
||||
}
|
||||
} else if (req.path == "/hide") {
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
sendResponse(c, 200, "application/json", "{\"ok\":true}", 11);
|
||||
} else if (req.path == "/show") {
|
||||
// Optional body {rect:{x,y,w,h}} (screen px) — anchored placement
|
||||
// over the embed panel; without a rect just show + top (never
|
||||
// topmost — the GUI must not stay above other apps).
|
||||
json_parse_result_s spres = {};
|
||||
json_value_s* sroot = json_parse_ex(req.body.data(), req.body.size(),
|
||||
json_parse_flags_default, nullptr, nullptr, &spres);
|
||||
bool rectOk = false;
|
||||
if (sroot && sroot->type == json_type_object) {
|
||||
const json_object_s* so = static_cast<const json_object_s*>(sroot->payload);
|
||||
const json_value_s* srv = memberValue(so, "rect");
|
||||
if (srv && srv->type == json_type_object) {
|
||||
const json_object_s* sro = static_cast<const json_object_s*>(srv->payload);
|
||||
const LONG srw = (LONG)memberInt(sro, "w", 0);
|
||||
const LONG srh = (LONG)memberInt(sro, "h", 0);
|
||||
if (srw > 0 && srh > 0) {
|
||||
g_embedRect.left = (LONG)memberInt(sro, "x", 0);
|
||||
g_embedRect.top = (LONG)memberInt(sro, "y", 0);
|
||||
g_embedRect.right = g_embedRect.left + srw;
|
||||
g_embedRect.bottom = g_embedRect.top + srh;
|
||||
rectOk = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sroot) std::free(sroot);
|
||||
if (rectOk) {
|
||||
SetWindowPos(hwnd, HWND_TOP, g_embedRect.left, g_embedRect.top,
|
||||
g_embedRect.right - g_embedRect.left,
|
||||
g_embedRect.bottom - g_embedRect.top,
|
||||
SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
fxGuiRequestResize(hwnd, g_embedRect.right - g_embedRect.left,
|
||||
g_embedRect.bottom - g_embedRect.top);
|
||||
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);
|
||||
}
|
||||
sendResponse(c, 200, "application/json", "{\"ok\":true}", 11);
|
||||
} else if (req.path == "/close") {
|
||||
sendResponse(c, 200, "application/json", "{\"ok\":true}", 11);
|
||||
stop.store(true);
|
||||
@@ -534,9 +578,13 @@ void runHttpServer(HWND hwnd, std::atomic<bool>& stop) {
|
||||
// Runs the embedded GUI server: starts the HTTP worker thread, then pumps
|
||||
// messages on this (STA) thread until the host window is destroyed (user
|
||||
// closed it or POST /close). Returns 0.
|
||||
int fxGuiServerLoop(HWND hwnd) {
|
||||
int fxGuiServerLoop(HWND hwnd, const std::function<void()>& onStarted) {
|
||||
std::atomic<bool> stop{false};
|
||||
std::thread http(runHttpServer, hwnd, std::ref(stop));
|
||||
// onStarted runs on the pump (STA) thread AFTER the HTTP server is up and
|
||||
// the port printed — used to defer the slow plugin load/attach in server
|
||||
// mode so the caller (plugins.py) doesn't block on VST load.
|
||||
if (onStarted) onStarted();
|
||||
MSG msg;
|
||||
while (IsWindow(hwnd)) {
|
||||
BOOL r = GetMessageA(&msg, nullptr, 0, 0);
|
||||
|
||||
Reference in New Issue
Block a user