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:
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -208,6 +209,14 @@ struct JuceFxEngine::Impl {
|
||||
std::vector<EditorCmd> editorQueue; // lệnh chờ (requestEditor)
|
||||
mutable std::mutex editorMutex; // editors + captured
|
||||
std::mutex queueMutex; // editorQueue
|
||||
// M14: retry Open khi editor chưa mở (lần Open trước fail/trễ).
|
||||
struct RetryOpen {
|
||||
uint32_t slot = 0;
|
||||
int w = 0, h = 0;
|
||||
uint64_t dueMs = 0;
|
||||
int tries = 0;
|
||||
};
|
||||
std::vector<RetryOpen> retryOpens; // M14 GUI-thread-only
|
||||
};
|
||||
|
||||
JuceFxEngine::JuceFxEngine() : impl_(std::make_unique<Impl>()) {}
|
||||
@@ -224,11 +233,28 @@ void JuceFxEngine::requestEditor(uint32_t slot, EditorAction action, int w, int
|
||||
bool JuceFxEngine::pumpEditorQueue() {
|
||||
std::vector<Impl::EditorCmd> cmds;
|
||||
{
|
||||
// M14: retry drain cần chạy kể cả khi queue rỗng (retry pending)
|
||||
// — bỏ early-return, chỉ đổi queue nếu có lệnh.
|
||||
std::lock_guard<std::mutex> lk(impl_->queueMutex);
|
||||
if (impl_->editorQueue.empty()) return false;
|
||||
cmds.swap(impl_->editorQueue);
|
||||
if (!impl_->editorQueue.empty()) cmds.swap(impl_->editorQueue);
|
||||
}
|
||||
bool any = false;
|
||||
// M14: đồng hồ + retry-open helpers (GUI-thread-only).
|
||||
const auto nowMs = []() -> uint64_t {
|
||||
return static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
};
|
||||
const auto retryClearSlot = [this](uint32_t slot) {
|
||||
auto& v = impl_->retryOpens;
|
||||
for (auto it = v.begin(); it != v.end(); ++it) {
|
||||
if (it->slot == slot) { v.erase(it); return; }
|
||||
}
|
||||
};
|
||||
const auto retrySchedule = [this, &nowMs](uint32_t slot, int w, int h) {
|
||||
for (const auto& r : impl_->retryOpens)
|
||||
if (r.slot == slot) return; // không xếp chồng
|
||||
impl_->retryOpens.push_back({slot, w, h, nowMs() + 1000, 0});
|
||||
};
|
||||
for (const auto& c : cmds) {
|
||||
std::lock_guard<std::mutex> lk(impl_->editorMutex);
|
||||
if (c.slot >= impl_->editors.size()) {
|
||||
@@ -253,6 +279,7 @@ bool JuceFxEngine::pumpEditorQueue() {
|
||||
std::cerr << "[JuceFxEngine] slot " << c.slot
|
||||
<< " createEditorIfNeeded null (plugin không có UI)"
|
||||
<< std::endl;
|
||||
retrySchedule(c.slot, c.w, c.h); // M14: thử lại sau
|
||||
break;
|
||||
}
|
||||
se.editor.reset(ed); // engine sở hữu editor (M1)
|
||||
@@ -262,11 +289,15 @@ bool JuceFxEngine::pumpEditorQueue() {
|
||||
std::cerr << "[JuceFxEngine] slot " << c.slot << " editor open "
|
||||
<< se.editor->getWidth() << "x"
|
||||
<< se.editor->getHeight() << std::endl;
|
||||
retryClearSlot(c.slot); // M14: mở thành công = hết retry
|
||||
} else {
|
||||
// Đã mở — Show lại (Reaper: editor còn sống, mở window lại).
|
||||
se.visible = true;
|
||||
se.editor->setVisible(true);
|
||||
if (c.w > 0 && c.h > 0) se.editor->setSize(c.w, c.h);
|
||||
retryClearSlot(c.slot); // M14
|
||||
std::cerr << "[JuceFxEngine] slot " << c.slot
|
||||
<< " editor ALREADY open — re-show" << std::endl;
|
||||
}
|
||||
any = true;
|
||||
break;
|
||||
@@ -279,6 +310,7 @@ bool JuceFxEngine::pumpEditorQueue() {
|
||||
<< " editor closed" << std::endl;
|
||||
any = true;
|
||||
}
|
||||
retryClearSlot(c.slot); // M14: đóng = huỷ retry mở dang dở
|
||||
break;
|
||||
}
|
||||
case EditorAction::Show:
|
||||
@@ -309,6 +341,34 @@ bool JuceFxEngine::pumpEditorQueue() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// M14: Drain retry-open — editor chưa mở do lần Open trước fail/trễ.
|
||||
{
|
||||
auto& rv = impl_->retryOpens;
|
||||
for (auto it = rv.begin(); it != rv.end();) {
|
||||
bool gone;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(impl_->editorMutex);
|
||||
gone = (it->slot >= impl_->editors.size()) ||
|
||||
!impl_->editors[it->slot].proc ||
|
||||
impl_->editors[it->slot].open;
|
||||
}
|
||||
if (gone || it->tries >= 2) {
|
||||
it = rv.erase(it);
|
||||
continue;
|
||||
}
|
||||
if (nowMs() >= it->dueMs) {
|
||||
std::cerr << "[JuceFxEngine] M14 retry open slot " << it->slot
|
||||
<< " lần " << (it->tries + 1) << std::endl;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(impl_->queueMutex);
|
||||
impl_->editorQueue.push_back({it->slot, EditorAction::Open, it->w, it->h});
|
||||
}
|
||||
it->tries++;
|
||||
it->dueMs = nowMs() + 1000;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return any;
|
||||
}
|
||||
|
||||
@@ -382,6 +442,7 @@ void JuceFxEngine::shutdown() {
|
||||
}
|
||||
impl_->editors.clear();
|
||||
}
|
||||
impl_->retryOpens.clear(); // M14
|
||||
if (impl_->prepared) {
|
||||
impl_->graph.releaseResources();
|
||||
impl_->graph.clear();
|
||||
@@ -495,9 +556,16 @@ void JuceFxEngine::prepare(uint32_t sampleRate, uint32_t blockSize) {
|
||||
if (base64Decode(s.presetB64, bytes) && !bytes.empty()) {
|
||||
juce::MemoryBlock mb(bytes.data(), bytes.size());
|
||||
if (juce::VST3PluginFormat::setStateFromVSTPresetFile(inst.get(), mb))
|
||||
std::cerr << "[JuceFxEngine] slot " << i << " preset applied" << std::endl;
|
||||
else
|
||||
std::cerr << "[JuceFxEngine] slot " << i << " preset FAILED" << std::endl;
|
||||
std::cerr << "[JuceFxEngine] slot " << i << " preset applied (vstpreset)" << std::endl;
|
||||
else {
|
||||
// M13: preset_b64 tu editor capture (getStateInformation) la
|
||||
// JUCE VST3 state raw (4B len + "VstW" + payload), KHONG
|
||||
// phai file .vstpreset -> setStateFromVSTPresetFile luon FAIL
|
||||
// -> plugin mo default moi lan spawn (preset da luu mat).
|
||||
// Ap thang setStateInformation (doi xung capture - cung wrapper).
|
||||
inst->setStateInformation(mb.getData(), (int)mb.getSize());
|
||||
std::cerr << "[JuceFxEngine] slot " << i << " preset applied (state)" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto node = g.addNode(std::unique_ptr<juce::AudioProcessor>(std::move(inst)));
|
||||
|
||||
Reference in New Issue
Block a user