M7-M16: VST FX GUI native embed hoàn thiện — fix click-2-lần chuyển slot, anchor lần mở đầu, taskbar icon
- app.jsx: masterGuiOpenRef sync truoc setState — guard chuyen slot doc ref, het race closure (click 1 lan chuyen slot) - JuceFxGuiHost.cpp: M14 applyAnchor truoc setVisible (het flash giua man hinh); M15 clear WS_EX_APPWINDOW + set TOOLWINDOW (het taskbar icon — JUCE set APPWINDOW 0x40000 thang TOOLWINDOW); M9 owner DAW + M9d skip applyOwner khi an - JuceFxLoop.cpp: M16 bo clearAnchor khi ed.open (retry ed.open ~700ms xoa rect fresh -> window moi khong anchor duoc lan dau mo panel); chi clear khi Close - JuceFxEngine/app.jsx/plugins.py/fx_realtime.py: editor session realtime, anchor rect routing, retry ed.open - Rebuild engine PyInstaller + juce_fx_bridge cmake; binaries dong bo 5 noi (bridge 7464a7ef)
This commit is contained in:
+97
-4
@@ -668,7 +668,10 @@ class FxGuiRequest(BaseModel):
|
||||
# đã chỉnh (trước đây GUI mở lại load default → mất settings khi
|
||||
# hide/show).
|
||||
preset_b64: str = ""
|
||||
|
||||
# C: master=True — GUI master chỉ mở qua session realtime (native editor
|
||||
# của instance DSP, shm). JS warm-ensure session trước; master mà không có
|
||||
# session/slot -> 409 (fallback embed đã ngưng cho master).
|
||||
master: bool = False
|
||||
|
||||
class FxGuiCloseRequest(BaseModel):
|
||||
"""Đóng bridge fx-gui đã mở cho plugin (embed mode)."""
|
||||
@@ -719,6 +722,16 @@ async def open_fx_gui(req: FxGuiRequest, current_user: dict = Depends(get_curren
|
||||
# này). Vẫn spawn fx_vst_bridge khi: không shm / session không tồn tại /
|
||||
# plugin không phải slot chain (instrument, offline, fallback an toàn).
|
||||
_sess = fx_realtime.get_session((req.shm or "").strip()) if (req.shm or "").strip() else None
|
||||
# C: master + session sống nhưng plugin chưa phải slot chain (module vừa add —
|
||||
# JS set_chain async / watchdog respawn) -> chờ tối đa 15s cho slot xuất hiện;
|
||||
# session chết giữa chừng -> bỏ (fallback embed bị chặn bên dưới cho master).
|
||||
if req.master and _sess is not None and _sess.alive() and _sess.chain_slot_for_path(plugin_path) is None:
|
||||
for _ in range(150):
|
||||
if not _sess.alive():
|
||||
break
|
||||
if _sess.chain_slot_for_path(plugin_path) is not None:
|
||||
break
|
||||
_time.sleep(0.1)
|
||||
if _sess is not None and _sess.alive():
|
||||
_slot = _sess.chain_slot_for_path(plugin_path)
|
||||
if _slot is not None:
|
||||
@@ -727,11 +740,45 @@ async def open_fx_gui(req: FxGuiRequest, current_user: dict = Depends(get_curren
|
||||
# trước, nếu không 1 plugin hiện 2 GUI (1 floating native + 1 embed)
|
||||
# và floating không tắt được qua UI. Terminate proc cũ → frontend
|
||||
# embed panel của nó onDead → tự đóng.
|
||||
_terminate_fx_gui_for_path(plugin_path)
|
||||
# M8: set_chain (juce bridge) reload async (watchdog respawn) —
|
||||
# cho chain moi ap tren bridge truoc khi ed.open, khong thi C++
|
||||
# bao "slot ngoai chain (M) — bo qua". Khong co reload dang chay
|
||||
# -> tra ngay (khong them latency mo GUI).
|
||||
_ready = _sess.wait_chain_ready(timeout=15)
|
||||
logger.info("[fxgui] open native path=%s shm=%s slot=%s ready=%s",
|
||||
plugin_path, _sess.name, _slot, _ready)
|
||||
if not _ready:
|
||||
# M13: chua READY (proc respawn / chain reload cham) — cho them
|
||||
# 5s; van chet thi ed.open chi vao ctrl ring (C++ KHONG reset
|
||||
# ring khi respawn → proc moi doc duoc) nen van gui duoc.
|
||||
_sess.wait_chain_ready(timeout=5)
|
||||
if not _sess.alive():
|
||||
logger.warning("[fxgui] open native bo — session chet path=%s", plugin_path)
|
||||
raise HTTPException(status_code=409,
|
||||
detail="Session FX realtime đã đóng — bật lại FX PWR rồi thử mở GUI.")
|
||||
_killed = _terminate_fx_gui_for_path(plugin_path)
|
||||
_already = fx_realtime.editor_find(plugin_path)
|
||||
_same = _already is not None and _already[0] == _sess.name
|
||||
_sess.send_editor_cmd("open", slot=_slot)
|
||||
_sent = _sess.send_editor_cmd("open", slot=_slot)
|
||||
logger.info("[fxgui] open sent=%s killed_fallback=%s already=%s same=%s proc_pid=%s state=%s",
|
||||
_sent, _killed, bool(_already), _same,
|
||||
_sess.proc.pid if getattr(_sess, "proc", None) else None,
|
||||
getattr(getattr(_sess, "h", None), "state", "?"))
|
||||
fx_realtime.editor_register(plugin_path, _sess.name, _slot)
|
||||
# M13 workaround click-1: gui doi khi khong hien window ngay (owner
|
||||
# gan sau show → Win32 an window). Gửi lai ed.open sau 700ms = ban
|
||||
# sao click-2 — C++ log "ALREADY open — re-show" thi re-show chinh
|
||||
# la dieu can. ponytail: bo block nay khi log A (prefix p/t) xac
|
||||
# nhan root cause that (show truoc owner).
|
||||
def _retry_open(delay):
|
||||
_time.sleep(delay)
|
||||
try:
|
||||
if _sess.alive():
|
||||
_sess.send_editor_cmd("open", slot=_slot)
|
||||
logger.info("[fxgui] retry ed.open path=%s slot=%s", plugin_path, _slot)
|
||||
except Exception:
|
||||
pass
|
||||
threading.Thread(target=_retry_open, args=(0.7,), daemon=True).start()
|
||||
return {"success": True, "started": not _same,
|
||||
"already_running": bool(_same), "native": True,
|
||||
"embed_url": None, "session_id": _sess.name, "slot": _slot}
|
||||
@@ -749,6 +796,11 @@ async def open_fx_gui(req: FxGuiRequest, current_user: dict = Depends(get_curren
|
||||
for proc in _FX_GUI_PROCESSES:
|
||||
if proc is not None and proc.poll() is None and getattr(proc, "_sf_plugin_path", None) == plugin_path:
|
||||
return {"success": True, "started": False, "already_running": True, "cmd": [], "embed_url": getattr(proc, "_sf_embed_url", None) or None}
|
||||
# C: tới đây mà master vẫn không có session/slot (native không mở được) ->
|
||||
# từ chối spawn fallback embed (instance VST thứ 2, preset lệch, GUI không
|
||||
# anchor preset master). Track GUI (không master) giữ fallback embed cũ.
|
||||
if req.master:
|
||||
raise HTTPException(status_code=409, detail="Mastering GUI chỉ mở qua session realtime (bật FX PWR hoặc để session warm) — fallback embed đã ngưng cho master.")
|
||||
job = {"path": plugin_path, "name": req.name or os.path.basename(plugin_path)}
|
||||
if req.shm:
|
||||
job["shm"] = req.shm
|
||||
@@ -867,6 +919,20 @@ class FxGuiNativeRequest(BaseModel):
|
||||
path: str
|
||||
|
||||
|
||||
class FxGuiRectBody(BaseModel):
|
||||
"""Rect màn hình (px vật lý, gốc trên-trái; x/y âm = màn hình trái)."""
|
||||
x: int = 0
|
||||
y: int = 0
|
||||
w: int = 0
|
||||
h: int = 0
|
||||
|
||||
|
||||
class FxGuiRectRequest(BaseModel):
|
||||
"""M7: anchor rect cho GUI native — rect panel nơi GUI embed xuất hiện."""
|
||||
path: str
|
||||
rect: FxGuiRectBody = FxGuiRectBody()
|
||||
|
||||
|
||||
def _fxrt_native_editor(plugin_path):
|
||||
"""(session, slot) nếu plugin đang mở native trong session fxrt sống."""
|
||||
entry = fx_realtime.editor_find(plugin_path)
|
||||
@@ -889,8 +955,13 @@ async def hide_fx_gui(req: FxGuiNativeRequest,
|
||||
sess, slot = _fxrt_native_editor((req.path or "").strip())
|
||||
if sess is None:
|
||||
return {"success": True, "applied": False}
|
||||
# M13: capture preset TRUOC khi an — dong mastering panel / save project
|
||||
# giua luc chinh GUI native khong mat preset moi (truoc day chi capture o
|
||||
# /fx-gui/close; hide chi an -> chain luu preset cu/rong sau khi reload).
|
||||
_preset = sess.capture_editor_preset(slot)
|
||||
sess.send_editor_cmd("hide", slot=slot)
|
||||
return {"success": True, "applied": True, "native": True}
|
||||
return {"success": True, "applied": True, "native": True,
|
||||
"preset_b64": _preset}
|
||||
|
||||
|
||||
@router.post("/fx-gui/show")
|
||||
@@ -905,6 +976,28 @@ async def show_fx_gui(req: FxGuiNativeRequest,
|
||||
return {"success": True, "applied": True, "native": True}
|
||||
|
||||
|
||||
@router.post("/fx-gui/rect")
|
||||
async def fx_gui_rect(req: FxGuiRectRequest,
|
||||
current_user: dict = Depends(get_current_user)):
|
||||
"""M7: anchor PluginWindow native vào rect panel embed (SetWindowPos phía
|
||||
C++ khi rect đổi — mirror FxGuiServer /show của float/embed). Frontend đo
|
||||
rect panel mỗi 250ms và gửi; chỉ áp dụng editor in-process fxrt — GUI spawn
|
||||
mode (HTTP bridge) tự có /show riêng, trả applied=False không lỗi."""
|
||||
enforce_password_changed(current_user)
|
||||
sess, slot = _fxrt_native_editor((req.path or "").strip())
|
||||
r = req.rect
|
||||
if sess is None:
|
||||
logger.info("[fxgui] rect path=%s editor=NONE rect=(%s,%s,%s,%s) — "
|
||||
"chua mo native / registry mat", (req.path or "").strip(),
|
||||
r.x, r.y, r.w, r.h)
|
||||
return {"success": True, "applied": False}
|
||||
_sent = sess.send_editor_rect(slot, r.x, r.y, r.w, r.h)
|
||||
logger.info("[fxgui] rect path=%s editor=%s slot=%s rect=(%s,%s,%s,%s) sent=%s",
|
||||
(req.path or "").strip(), sess.name, slot,
|
||||
r.x, r.y, r.w, r.h, _sent)
|
||||
return {"success": True, "applied": True, "native": True}
|
||||
|
||||
|
||||
_FX_GUI_PROCESSES = [] # list[subprocess.Popen] — các bridge fx-gui do app spawn
|
||||
|
||||
def _register_fx_gui_process(proc):
|
||||
|
||||
Reference in New Issue
Block a user