pluginbridge / Source /Helper /HelperIPC.cpp
RAM2118's picture
Fix: Remove MSG_NOSIGNAL from HelperIPC.cpp, fix include paths for macOS
0f12a9a verified
Raw History Blame
6.63 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);
}
HelperIPC::~HelperIPC()
{
disconnect();
}
bool HelperIPC::connect()
{
auto socketPath = IPC::getSocketPath(parentPid);
socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socketFd < 0) return false;
// Set SO_NOSIGPIPE (macOS)
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::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 == "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},
{"n", p.name.toStdString()},
{"v", std::round(p.value * 1000.0f) / 1000.0f},
{"t", p.displayText.toStdString()}
});
}
json response = {{"ok", true}, {"results", arr}};
sendMessage(juce::String(response.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;
json response = {{"ok", true}, {"values", values}};
sendMessage(juce::String(response.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 == "show_gui")
{
host.showGui();
sendMessage("{\"ok\":true}");
}
else if (cmd == "hide_gui")
{
host.hideGui();
sendMessage("{\"ok\":true}");
}
else if (cmd == "get_state")
{
auto state = host.getStateBase64();
json response = {{"ok", true}, {"state", state.toStdString()}};
sendMessage(juce::String(response.dump()));
}
else if (cmd == "set_state")
{
auto state = j.value("state", "");
bool ok = host.setStateBase64(juce::String(state));
sendMessage(ok ? "{\"ok\":true}" : "{\"ok\":false}");
}
else if (cmd == "configure")
{
double sr = j.value("sampleRate", 44100.0);
int bs = j.value("blockSize", 512);
int ch = j.value("channels", 2);
host.configure(sr, bs, ch);
sendMessage("{\"ok\":true}");
}
else if (cmd == "quit")
{
juce::JUCEApplication::getInstance()->systemRequestedQuit();
}
}
catch (const std::exception& e)
{
DBG("HelperIPC: Error handling message: " + juce::String(e.what()));
sendMessage("{\"ok\":false,\"error\":\"parse error\"}");
}
}