Download Source/Helper/HelperIPC.cpp from RAM2118/pluginbridge: direct link, hf CLI and curl.
- Browser
- Download file 8.07 kB
-
https://huggingface.co/RAM2118/pluginbridge/resolve/fdc4e418d34e7f32ef31422f1e9aae1de08f4e86/Source/Helper/HelperIPC.cpp
- Command line
-
hf download hf://RAM2118/pluginbridge@fdc4e418d34e7f32ef31422f1e9aae1de08f4e86/Source/Helper/HelperIPC.cpp
-
curl -L -o HelperIPC.cpp https://huggingface.co/RAM2118/pluginbridge/resolve/fdc4e418d34e7f32ef31422f1e9aae1de08f4e86/Source/Helper/HelperIPC.cpp
8.07 kB
| 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\"}"); | |
| } | |
| } | |