Files
SonicForgeStudio/native_bridge/juce_fx/JuceFxGuiHost.h
T
admin ef521d9981 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)
2026-09-06 19:45:14 +07:00

81 lines
3.9 KiB
C++

// native_bridge/juce_fx/JuceFxGuiHost.h
// M2: GUI thread + window host (Reaper-style) — host editor của ĐÚNG instance
// DSP trong graph ngay trong fxrt_bridge (juce_fx_bridge.exe), không instance
// 2 / feeder (nguyên nhân CPU 100% .24).
//
// GUI thread riêng (BELOW_NORMAL) chạy juce::MessageManager + tick 30ms:
// engine.pumpEditorQueue() → reconcile window theo editor state.
// PluginWindow (DocumentWindow native) bọc juce::AudioProcessorEditor mà
// ENGINE sở hữu — window KHÔNG sở hữu content (setContentNonOwned).
// Idle (không Open, không play) ≈ 0 CPU như Reaper.
#pragma once
#include <atomic>
#include <cstdint>
#include <functional>
#include <memory>
#include <thread>
class JuceFxEngine;
class JuceFxGuiHost {
public:
explicit JuceFxGuiHost(JuceFxEngine& engine);
~JuceFxGuiHost(); // .cpp (Impl incomplete ở header)
JuceFxGuiHost(const JuceFxGuiHost&) = delete;
JuceFxGuiHost& operator=(const JuceFxGuiHost&) = delete;
// Start GUI thread (thread này thành JUCE message thread). Gọi TRƯỚC mọi
// engine.prepare() — plugin instance phải tạo trên GUI thread (plugin JUCE
// MM gắn thread tạo instance; editor tạo thread khác → treo — M1 chỉ chạy
// vì main thread vừa tạo instance vừa là message thread).
void start();
// Chạy task đồng bộ trên GUI thread (chờ xong). Main/audio thread dùng để
// bọc engine.prepare()/shutdown() — instance + editor sống trên cùng thread
// như plugin yêu cầu. KHÔNG gọi từ GUI thread; không started → chạy inline.
template <typename F>
void runOnGui(F&& f) {
enqueueGuiTask([f = std::forward<F>(f)]() mutable { f(); });
}
// Đóng editor + dừng GUI thread + join (tối đa 2s, detach nếu GUI thread
// kẹt trong plugin editor). Gọi TRƯỚC engine.shutdown().
void stop();
// Main thread: enqueue Close cho mọi editor đang mở + chờ GUI thread pump
// xong (poll hasOpenEditor). Bắt buộc trước engine.prepare()/shutdown()
// khi GUI thread đang chạy (editor là JUCE Component — xoá trên message
// thread). Không started → return ngay.
void waitEditorsClosed(uint32_t timeoutMs);
// Thread-safe: số host window đang tồn tại (selftest).
uint32_t windowCount() const { return winCount_.load(); }
// GUI thread: X button của PluginWindow slot → engine Close (queue) — tick
// sau pump + reconcile xoá window.
void onWindowClose(uint32_t slot);
// M7: anchor rect (embed panel — frontend đo + gửi ed.rx/ry/rw/rh qua SHM
// ctrl ring). Thread-safe — ctrl thread của JuceFxLoop gọi setAnchorField
// từng field (KHÔNG qua engine queue — engine không sở hữu window); GUI
// thread tick reconcile áp dụng bằng SetWindowPos khi rect ĐỔI (dedupe,
// mirror FxGuiServer g_embedRect) → window native nằm ĐÚNG rect panel.
// field = 'x'|'y'|'w'|'h'. clearAnchor: quên rect cũ (gọi lúc ed.open/
// ed.close — mở mới chỉ anchor sau khi có rect fresh từ frontend).
void setAnchorField(uint32_t slot, char field, int value);
void clearAnchor(uint32_t slot);
// GUI thread tick 30ms (GuiTickTimer trong .cpp gọi): pump + reconcile.
void tick();
private:
// runOnGui: xếp task cho GUI thread + chờ hoàn thành (count-based).
void enqueueGuiTask(std::function<void()> task);
// GUI thread: tạo/xoá/ẩn-hiện PluginWindow theo engine editor state.
void reconcile();
// GUI thread entry.
void guiMain();
struct Impl;
std::unique_ptr<Impl> impl_; // windows_ (PluginWindow chỉ thấy trong .cpp)
JuceFxEngine& engine_;
std::thread thread_;
std::atomic<bool> started_{false};
std::atomic<bool> quit_{false};
std::atomic<bool> guiExited_{false};
std::atomic<uint32_t> winCount_{0};
};