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:
@@ -25,6 +25,7 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
@@ -97,10 +98,97 @@ static std::string base64Encode(const std::string& in) {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
// M13: prefix moi dong std::cerr bang [p=<pid> t=<ms-tu-start>] - log cua
|
||||
// nhieu bridge process tron 1 file (FXRT_BRIDGE_LOG default) nen khong tach
|
||||
// duoc event theo process/thoi gian. Wrapper stream buffer: phat prefix o dau
|
||||
// dong, khong buffer ca dong (cerr unitbuf flush tung token - emit o sync()
|
||||
// se tao prefix gia); mutex bao an toan thread (main/hb/ctrl/gui thread cung
|
||||
// cerr). Cai dau run_juce_fx_loop truoc khi thread khac chay.
|
||||
class PfxBuf : public std::streambuf {
|
||||
public:
|
||||
explicit PfxBuf(std::streambuf* sink)
|
||||
: sink_(sink), lineStart_(true), t0_(std::chrono::steady_clock::now()) {
|
||||
pid_ = (unsigned long)::GetCurrentProcessId();
|
||||
}
|
||||
~PfxBuf() override {
|
||||
if (sink_) std::cerr.rdbuf(sink_);
|
||||
}
|
||||
|
||||
protected:
|
||||
int_type overflow(int_type c) override {
|
||||
if (traits_type::eq_int_type(c, traits_type::eof()))
|
||||
return traits_type::not_eof(c);
|
||||
char ch = traits_type::to_char_type(c);
|
||||
std::lock_guard<std::mutex> lk(mu_);
|
||||
if (!writeOne(ch)) return traits_type::eof();
|
||||
return c;
|
||||
}
|
||||
|
||||
std::streamsize xsputn(const char* s, std::streamsize n) override {
|
||||
std::lock_guard<std::mutex> lk(mu_);
|
||||
std::streamsize done = 0;
|
||||
while (done < n) {
|
||||
const char c = s[done];
|
||||
if (c == '\n') {
|
||||
if (sink_->sputc('\n') == traits_type::eof()) break;
|
||||
++done;
|
||||
lineStart_ = true;
|
||||
continue;
|
||||
}
|
||||
if (!emitPrefix()) break;
|
||||
std::streamsize j = done;
|
||||
while (j < n && s[j] != '\n') ++j;
|
||||
const std::streamsize chunk = j - done;
|
||||
if (chunk > 0) {
|
||||
const std::streamsize w = sink_->sputn(s + done, chunk);
|
||||
if (w != chunk) break;
|
||||
done += chunk;
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
int sync() override {
|
||||
std::lock_guard<std::mutex> lk(mu_);
|
||||
return sink_->pubsync() == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
private:
|
||||
bool emitPrefix() {
|
||||
if (!lineStart_) return true;
|
||||
lineStart_ = false;
|
||||
const long long ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - t0_).count();
|
||||
char buf[64];
|
||||
const int len = std::snprintf(buf, sizeof(buf), "[p=%lu t=%lld] ",
|
||||
pid_, ms);
|
||||
if (len <= 0) return true;
|
||||
return sink_->sputn(buf, len) == (std::streamsize)len;
|
||||
}
|
||||
|
||||
bool writeOne(char c) {
|
||||
if (c == '\n') {
|
||||
if (sink_->sputc('\n') == traits_type::eof()) return false;
|
||||
lineStart_ = true;
|
||||
return true;
|
||||
}
|
||||
if (!emitPrefix()) return false;
|
||||
return sink_->sputc(c) != traits_type::eof();
|
||||
}
|
||||
|
||||
std::streambuf* sink_;
|
||||
std::mutex mu_;
|
||||
bool lineStart_;
|
||||
unsigned long pid_;
|
||||
std::chrono::steady_clock::time_point t0_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
|
||||
uint32_t parentPid) {
|
||||
PfxBuf pfx(std::cerr.rdbuf());
|
||||
std::cerr.rdbuf(&pfx);
|
||||
std::cerr << "[JuceFxLoop] start job=" << jobPath << " shm=" << shmName
|
||||
<< " parent=" << parentPid << std::endl;
|
||||
const std::string job = readFile(jobPath);
|
||||
@@ -220,6 +308,16 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
|
||||
ipc->h.ctrlRead++; // consume trước dispatch — không block ring
|
||||
if (std::strncmp(key, "ed.", 3) != 0)
|
||||
continue; // SET_PARAM cũ — no-op (xem comment trên)
|
||||
// M7: ed.rx/ry/rw/rh = anchor rect (frontend đo panel embed,
|
||||
// gửi 4 key mỗi 250ms) → guiHost.setAnchorField (thread-safe,
|
||||
// không qua engine queue); reconcile GUI thread SetWindowPos
|
||||
// khi rect đổi → window native nằm đúng rect panel.
|
||||
if (key[3] == 'r' && (key[4] == 'x' || key[4] == 'y' ||
|
||||
key[4] == 'w' || key[4] == 'h') &&
|
||||
key[5] == '\0') {
|
||||
guiHost.setAnchorField(slot, key[4], (int)value);
|
||||
continue;
|
||||
}
|
||||
JuceFxEngine::EditorAction act;
|
||||
bool known = true;
|
||||
if (std::strcmp(key, "ed.open") == 0)
|
||||
@@ -247,6 +345,11 @@ int run_juce_fx_loop(const std::string& jobPath, const std::string& shmName,
|
||||
w = v / 4096;
|
||||
h = v % 4096;
|
||||
}
|
||||
// M16: KHÔNG clearAnchor khi Open — backend retry ed.open sau
|
||||
// ~700ms (plugins.py) clear rect fresh JS vừa gửi → window mới
|
||||
// tạo không anchor được (lần đầu mở panel). Rect cũ vẫn đúng khi
|
||||
// panel yên (M7 dedupe); panel dời → JS gửi rect mới ≤250ms sửa.
|
||||
// Chỉ clear khi Close (window xoá — rect cũ vô dụng).
|
||||
engine.requestEditor(slot, act, w, h);
|
||||
std::cerr << "[JuceFxLoop] ctrl " << key << " slot=" << slot
|
||||
<< " w=" << w << " h=" << h << std::endl;
|
||||
|
||||
Reference in New Issue
Block a user