ef521d9981
- 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)
81 lines
3.9 KiB
C++
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};
|
|
};
|