RAM2118 commited on
Commit
eabb3b4
·
verified ·
1 Parent(s): 68398ab

Out-of-process rebuild: Add Helper/HelperIPC.cpp — handles commands from parent via Unix socket

Browse files
Files changed (1) hide show
  1. Source/Helper/HelperIPC.cpp +240 -0
Source/Helper/HelperIPC.cpp ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "HelperIPC.h"
2
+ #include "HelperPluginHost.h"
3
+ #include "../Shared/IPCProtocol.h"
4
+
5
+ #include <sys/socket.h>
6
+ #include <sys/un.h>
7
+ #include <unistd.h>
8
+ #include <poll.h>
9
+
10
+ // Using nlohmann json (shared with parent)
11
+ #include "json.hpp"
12
+ using json = nlohmann::json;
13
+
14
+ HelperIPC::HelperIPC(int parentPid, HelperPluginHost& host)
15
+ : parentPid(parentPid), host(host)
16
+ {
17
+ }
18
+
19
+ HelperIPC::~HelperIPC()
20
+ {
21
+ disconnect();
22
+ }
23
+
24
+ bool HelperIPC::connect()
25
+ {
26
+ auto socketPath = IPC::getSocketPath(parentPid);
27
+
28
+ socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
29
+ if (socketFd < 0) return false;
30
+
31
+ struct sockaddr_un addr;
32
+ memset(&addr, 0, sizeof(addr));
33
+ addr.sun_family = AF_UNIX;
34
+ strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
35
+
36
+ // Try to connect (parent should have the socket listening already)
37
+ int retries = 50; // 5 seconds total
38
+ while (retries-- > 0)
39
+ {
40
+ if (::connect(socketFd, (struct sockaddr*)&addr, sizeof(addr)) == 0)
41
+ {
42
+ // Connected — start listening
43
+ running.store(true);
44
+ listenThread = std::thread([this]() { listenLoop(); });
45
+ return true;
46
+ }
47
+ usleep(100000); // 100ms
48
+ }
49
+
50
+ close(socketFd);
51
+ socketFd = -1;
52
+ return false;
53
+ }
54
+
55
+ void HelperIPC::disconnect()
56
+ {
57
+ running.store(false);
58
+ if (socketFd >= 0)
59
+ {
60
+ shutdown(socketFd, SHUT_RDWR);
61
+ close(socketFd);
62
+ socketFd = -1;
63
+ }
64
+ if (listenThread.joinable())
65
+ listenThread.join();
66
+ }
67
+
68
+ void HelperIPC::sendReady()
69
+ {
70
+ json msg = {{"notify", "ready"}};
71
+ sendMessage(msg.dump());
72
+ }
73
+
74
+ void HelperIPC::sendParamChanged(int index, float value)
75
+ {
76
+ json msg = {{"notify", "param_changed"}, {"index", index}, {"value", value}};
77
+ sendMessage(msg.dump());
78
+ }
79
+
80
+ void HelperIPC::sendMessage(const juce::String& jsonStr)
81
+ {
82
+ if (socketFd < 0) return;
83
+
84
+ auto data = jsonStr + "\n";
85
+ auto bytes = data.toRawUTF8();
86
+ auto len = data.getNumBytesAsUTF8();
87
+ ::send(socketFd, bytes, len, MSG_NOSIGNAL);
88
+ }
89
+
90
+ void HelperIPC::listenLoop()
91
+ {
92
+ std::string buffer;
93
+ char chunk[4096];
94
+
95
+ while (running.load())
96
+ {
97
+ struct pollfd pfd;
98
+ pfd.fd = socketFd;
99
+ pfd.events = POLLIN;
100
+
101
+ int ret = poll(&pfd, 1, 200); // 200ms timeout
102
+ if (ret <= 0) continue;
103
+
104
+ ssize_t n = recv(socketFd, chunk, sizeof(chunk) - 1, 0);
105
+ if (n <= 0)
106
+ {
107
+ // Parent disconnected or error
108
+ DBG("HelperIPC: Parent disconnected");
109
+ juce::JUCEApplication::getInstance()->systemRequestedQuit();
110
+ return;
111
+ }
112
+
113
+ chunk[n] = '\0';
114
+ buffer += chunk;
115
+
116
+ // Process complete messages (newline-delimited)
117
+ size_t pos;
118
+ while ((pos = buffer.find('\n')) != std::string::npos)
119
+ {
120
+ auto msg = buffer.substr(0, pos);
121
+ buffer.erase(0, pos + 1);
122
+
123
+ if (!msg.empty())
124
+ handleMessage(juce::String(msg));
125
+ }
126
+ }
127
+ }
128
+
129
+ void HelperIPC::handleMessage(const juce::String& message)
130
+ {
131
+ try
132
+ {
133
+ auto j = json::parse(message.toStdString());
134
+ auto cmd = j.value("cmd", "");
135
+
136
+ if (cmd == "load")
137
+ {
138
+ auto path = j.value("path", "");
139
+ double sr = j.value("sampleRate", 44100.0);
140
+ int bs = j.value("blockSize", 512);
141
+
142
+ bool ok = host.loadPlugin(juce::String(path), sr, bs);
143
+
144
+ json response;
145
+ if (ok)
146
+ response = {{"ok", true}, {"name", host.getPluginName().toStdString()}, {"params", host.getParameterCount()}};
147
+ else
148
+ response = {{"ok", false}, {"error", "Failed to load plugin"}};
149
+
150
+ sendMessage(juce::String(response.dump()));
151
+ }
152
+ else if (cmd == "unload")
153
+ {
154
+ host.unloadPlugin();
155
+ sendMessage("{\"ok\":true}");
156
+ }
157
+ else if (cmd == "search_param")
158
+ {
159
+ auto keyword = j.value("keyword", "");
160
+ auto results = host.searchParams(juce::String(keyword));
161
+
162
+ json arr = json::array();
163
+ for (auto& p : results)
164
+ {
165
+ arr.push_back({
166
+ {"i", p.index},
167
+ {"n", p.name.toStdString()},
168
+ {"v", std::round(p.value * 1000.0f) / 1000.0f},
169
+ {"t", p.displayText.toStdString()}
170
+ });
171
+ }
172
+
173
+ json response = {{"ok", true}, {"results", arr}};
174
+ sendMessage(juce::String(response.dump()));
175
+ }
176
+ else if (cmd == "get_params")
177
+ {
178
+ std::vector<int> ids;
179
+ for (auto& id : j["ids"])
180
+ ids.push_back(id.get<int>());
181
+
182
+ auto results = host.getParams(ids);
183
+
184
+ json values = json::object();
185
+ for (auto& [idx, val] : results)
186
+ values[std::to_string(idx)] = std::round(val * 1000.0f) / 1000.0f;
187
+
188
+ json response = {{"ok", true}, {"values", values}};
189
+ sendMessage(juce::String(response.dump()));
190
+ }
191
+ else if (cmd == "set_params")
192
+ {
193
+ std::map<int, float> values;
194
+ for (auto& [key, val] : j["values"].items())
195
+ values[std::stoi(key)] = val.get<float>();
196
+
197
+ bool ok = host.setParams(values);
198
+ sendMessage(ok ? "{\"ok\":true}" : "{\"ok\":false}");
199
+ }
200
+ else if (cmd == "show_gui")
201
+ {
202
+ host.showGui();
203
+ sendMessage("{\"ok\":true}");
204
+ }
205
+ else if (cmd == "hide_gui")
206
+ {
207
+ host.hideGui();
208
+ sendMessage("{\"ok\":true}");
209
+ }
210
+ else if (cmd == "get_state")
211
+ {
212
+ auto state = host.getStateBase64();
213
+ json response = {{"ok", true}, {"state", state.toStdString()}};
214
+ sendMessage(juce::String(response.dump()));
215
+ }
216
+ else if (cmd == "set_state")
217
+ {
218
+ auto state = j.value("state", "");
219
+ bool ok = host.setStateBase64(juce::String(state));
220
+ sendMessage(ok ? "{\"ok\":true}" : "{\"ok\":false}");
221
+ }
222
+ else if (cmd == "configure")
223
+ {
224
+ double sr = j.value("sampleRate", 44100.0);
225
+ int bs = j.value("blockSize", 512);
226
+ int ch = j.value("channels", 2);
227
+ host.configure(sr, bs, ch);
228
+ sendMessage("{\"ok\":true}");
229
+ }
230
+ else if (cmd == "quit")
231
+ {
232
+ juce::JUCEApplication::getInstance()->systemRequestedQuit();
233
+ }
234
+ }
235
+ catch (const std::exception& e)
236
+ {
237
+ DBG("HelperIPC: Error handling message: " + juce::String(e.what()));
238
+ sendMessage("{\"ok\":false,\"error\":\"parse error\"}");
239
+ }
240
+ }