pluginbridge / Source /Helper /HelperIPC.cpp
RAM2118's picture
fix: wire onGuiCreated callback in HelperIPC constructor to send gui_ready notification
df8bc97 verified
Raw
History Blame Contribute Delete
8.07 kB
#include "HelperIPC.h"
#include "HelperPluginHost.h"
#include "IPCProtocol.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include "json.hpp"
using json = nlohmann::json;
HelperIPC::HelperIPC(int parentPid, HelperPluginHost& host)
: parentPid(parentPid), host(host)
{
signal(SIGPIPE, SIG_IGN);
// Wire the GUI created callback so we send notification to host process
host.onGuiCreated = [this](const juce::String& portName, int width, int height)
{
sendGuiReady(portName, width, height);
};
}
HelperIPC::~HelperIPC()
{
host.onGuiCreated = nullptr;
disconnect();
}
bool HelperIPC::connect()
{
auto socketPath = IPC::getSocketPath(parentPid);
socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socketFd < 0) return false;
int nosigpipe = 1;
setsockopt(socketFd, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
int retries = 50;
while (retries-- > 0)
{
if (::connect(socketFd, (struct sockaddr*)&addr, sizeof(addr)) == 0)
{
running.store(true);
listenThread = std::thread([this]() { listenLoop(); });
return true;
}
usleep(100000);
}
close(socketFd);
socketFd = -1;
return false;
}
void HelperIPC::disconnect()
{
running.store(false);
if (socketFd >= 0)
{
shutdown(socketFd, SHUT_RDWR);
close(socketFd);
socketFd = -1;
}
if (listenThread.joinable())
listenThread.join();
}
void HelperIPC::sendReady()
{
json msg = {{"notify", "ready"}};
sendMessage(juce::String(msg.dump()));
}
void HelperIPC::sendGuiReady(const juce::String& portName, int width, int height)
{
json msg = {{"notify", "gui_ready"},
{"layerPortName", portName.toStdString()},
{"width", width},
{"height", height}};
sendMessage(juce::String(msg.dump()));
}
void HelperIPC::sendGuiResized(int width, int height)
{
json msg = {{"notify", "resized"}, {"width", width}, {"height", height}};
sendMessage(juce::String(msg.dump()));
}
void HelperIPC::sendParamChanged(int index, float value)
{
json msg = {{"notify", "param_changed"}, {"index", index}, {"value", value}};
sendMessage(juce::String(msg.dump()));
}
void HelperIPC::sendMessage(const juce::String& jsonStr)
{
if (socketFd < 0) return;
auto data = jsonStr + "\n";
auto bytes = data.toRawUTF8();
auto len = (size_t)data.getNumBytesAsUTF8();
::send(socketFd, bytes, len, 0);
}
void HelperIPC::listenLoop()
{
std::string buffer;
char chunk[4096];
while (running.load())
{
struct pollfd pfd;
pfd.fd = socketFd;
pfd.events = POLLIN;
int ret = poll(&pfd, 1, 200);
if (ret <= 0) continue;
ssize_t n = recv(socketFd, chunk, sizeof(chunk) - 1, 0);
if (n <= 0)
{
DBG("HelperIPC: Parent disconnected");
juce::JUCEApplication::getInstance()->systemRequestedQuit();
return;
}
chunk[n] = '\0';
buffer += chunk;
size_t pos;
while ((pos = buffer.find('\n')) != std::string::npos)
{
auto msg = buffer.substr(0, pos);
buffer.erase(0, pos + 1);
if (!msg.empty())
handleMessage(juce::String(msg));
}
}
}
void HelperIPC::handleMessage(const juce::String& message)
{
try
{
auto j = json::parse(message.toStdString());
auto cmd = j.value("cmd", "");
if (cmd == "load")
{
auto path = j.value("path", "");
double sr = j.value("sampleRate", 44100.0);
int bs = j.value("blockSize", 512);
bool ok = host.loadPlugin(juce::String(path), sr, bs);
json response;
if (ok)
response = {{"ok", true}, {"name", host.getPluginName().toStdString()}, {"params", host.getParameterCount()}};
else
response = {{"ok", false}, {"error", "Failed to load plugin"}};
sendMessage(juce::String(response.dump()));
}
else if (cmd == "unload")
{
host.unloadPlugin();
sendMessage("{\"ok\":true}");
}
else if (cmd == "show_gui")
{
host.showGui();
// gui_ready notification sent via onGuiCreated callback (async)
sendMessage("{\"ok\":true}");
}
else if (cmd == "hide_gui")
{
host.hideGui();
sendMessage("{\"ok\":true}");
}
else if (cmd == "search_param")
{
auto keyword = j.value("keyword", "");
auto results = host.searchParams(juce::String(keyword));
json arr = json::array();
for (auto& p : results)
arr.push_back({{"i",p.index},{"id",p.id.toStdString()},{"n",p.name.toStdString()},
{"v",std::round(p.value*1000.0f)/1000.0f},{"t",p.displayText.toStdString()}});
sendMessage(juce::String(json({{"ok",true},{"results",arr}}).dump()));
}
else if (cmd == "get_params")
{
std::vector<int> ids;
for (auto& id : j["ids"]) ids.push_back(id.get<int>());
auto results = host.getParams(ids);
json values = json::object();
for (auto& [idx, val] : results) values[std::to_string(idx)] = std::round(val*1000.0f)/1000.0f;
sendMessage(juce::String(json({{"ok",true},{"values",values}}).dump()));
}
else if (cmd == "set_params")
{
std::map<int, float> values;
for (auto& [key, val] : j["values"].items()) values[std::stoi(key)] = val.get<float>();
bool ok = host.setParams(values);
sendMessage(ok ? "{\"ok\":true}" : "{\"ok\":false}");
}
else if (cmd == "mouse")
{
auto type = j.value("type", "");
float x = j.value("x", 0.0f), y = j.value("y", 0.0f);
int button = j.value("button", 0);
if (type == "down") host.injectMouseDown(x, y, button);
else if (type == "up") host.injectMouseUp(x, y, button);
else if (type == "move") host.injectMouseMove(x, y);
else if (type == "drag") host.injectMouseDrag(x, y, button);
else if (type == "scroll") host.injectMouseScroll(x, y, j.value("dx",0.0f), j.value("dy",0.0f));
}
else if (cmd == "key")
{
auto type = j.value("type", "");
int keyCode = j.value("keyCode", 0);
auto chars = j.value("chars", "");
int modifiers = j.value("modifiers", 0);
if (type == "down") host.injectKeyDown(keyCode, juce::String(chars), modifiers);
else if (type == "up") host.injectKeyUp(keyCode, juce::String(chars), modifiers);
}
else if (cmd == "get_state")
{
auto state = host.getStateBase64();
sendMessage(juce::String(json({{"ok",true},{"state",state.toStdString()}}).dump()));
}
else if (cmd == "set_state")
{
bool ok = host.setStateBase64(juce::String(j.value("state", "")));
sendMessage(ok ? "{\"ok\":true}" : "{\"ok\":false}");
}
else if (cmd == "configure")
{
host.configure(j.value("sampleRate",44100.0), j.value("blockSize",512), j.value("channels",2));
sendMessage("{\"ok\":true}");
}
else if (cmd == "quit")
{
juce::JUCEApplication::getInstance()->systemRequestedQuit();
}
}
catch (const std::exception& e)
{
DBG("HelperIPC: Error: " + juce::String(e.what()));
sendMessage("{\"ok\":false,\"error\":\"parse error\"}");
}
}