Upload Source/Plugin/HelperConnection.cpp
Browse files
Source/Plugin/HelperConnection.cpp
CHANGED
|
@@ -17,7 +17,6 @@ using json = nlohmann::json;
|
|
| 17 |
HelperConnection::HelperConnection()
|
| 18 |
{
|
| 19 |
myPid = getpid();
|
| 20 |
-
// Ignore SIGPIPE globally (macOS doesn't have MSG_NOSIGNAL)
|
| 21 |
signal(SIGPIPE, SIG_IGN);
|
| 22 |
}
|
| 23 |
|
|
@@ -30,7 +29,6 @@ bool HelperConnection::spawnHelper()
|
|
| 30 |
{
|
| 31 |
killHelper();
|
| 32 |
|
| 33 |
-
// 1. Create shared memory
|
| 34 |
auto shmName = IPC::getShmName(myPid);
|
| 35 |
if (!sharedAudio.create(shmName))
|
| 36 |
{
|
|
@@ -38,7 +36,6 @@ bool HelperConnection::spawnHelper()
|
|
| 38 |
return false;
|
| 39 |
}
|
| 40 |
|
| 41 |
-
// 2. Create semaphores
|
| 42 |
if (!inputSem.create(IPC::getInputSemName(myPid)) ||
|
| 43 |
!outputSem.create(IPC::getOutputSemName(myPid)))
|
| 44 |
{
|
|
@@ -46,7 +43,6 @@ bool HelperConnection::spawnHelper()
|
|
| 46 |
return false;
|
| 47 |
}
|
| 48 |
|
| 49 |
-
// 3. Create Unix domain socket (server side)
|
| 50 |
auto socketPath = IPC::getSocketPath(myPid);
|
| 51 |
unlink(socketPath.c_str());
|
| 52 |
|
|
@@ -65,37 +61,42 @@ bool HelperConnection::spawnHelper()
|
|
| 65 |
if (bind(serverSocketFd, (struct sockaddr*)&addr, sizeof(addr)) < 0 ||
|
| 66 |
listen(serverSocketFd, 1) < 0)
|
| 67 |
{
|
| 68 |
-
DBG("HelperConnection: Failed to bind/listen
|
| 69 |
close(serverSocketFd);
|
| 70 |
serverSocketFd = -1;
|
| 71 |
return false;
|
| 72 |
}
|
| 73 |
|
| 74 |
-
//
|
| 75 |
-
auto
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
-
if (!
|
| 82 |
{
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
| 86 |
}
|
| 87 |
|
| 88 |
-
if (!
|
| 89 |
{
|
| 90 |
-
DBG("HelperConnection: Helper
|
| 91 |
return false;
|
| 92 |
}
|
| 93 |
|
| 94 |
-
// 5. Spawn helper process
|
| 95 |
state.store(State::Starting);
|
| 96 |
|
| 97 |
auto pidStr = juce::String(myPid).toStdString();
|
| 98 |
-
auto helperPathStr =
|
| 99 |
|
| 100 |
pid_t pid;
|
| 101 |
char* argv[] = {
|
|
@@ -108,7 +109,7 @@ bool HelperConnection::spawnHelper()
|
|
| 108 |
int spawnResult = posix_spawn(&pid, helperPathStr.c_str(), nullptr, nullptr, argv, environ);
|
| 109 |
if (spawnResult != 0)
|
| 110 |
{
|
| 111 |
-
DBG("HelperConnection: Failed to spawn
|
| 112 |
state.store(State::Disconnected);
|
| 113 |
return false;
|
| 114 |
}
|
|
@@ -116,7 +117,6 @@ bool HelperConnection::spawnHelper()
|
|
| 116 |
helperPid = pid;
|
| 117 |
helperRunning.store(true);
|
| 118 |
|
| 119 |
-
// 6. Accept connection from helper (with timeout)
|
| 120 |
struct pollfd pfd;
|
| 121 |
pfd.fd = serverSocketFd;
|
| 122 |
pfd.events = POLLIN;
|
|
@@ -131,23 +131,19 @@ bool HelperConnection::spawnHelper()
|
|
| 131 |
clientSocketFd = accept(serverSocketFd, nullptr, nullptr);
|
| 132 |
if (clientSocketFd < 0)
|
| 133 |
{
|
| 134 |
-
DBG("HelperConnection: Failed to accept
|
| 135 |
killHelper();
|
| 136 |
return false;
|
| 137 |
}
|
| 138 |
|
| 139 |
-
// Set SO_NOSIGPIPE on the accepted socket (macOS)
|
| 140 |
int nosigpipe = 1;
|
| 141 |
setsockopt(clientSocketFd, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
|
| 142 |
|
| 143 |
-
// 7. Start IPC listen thread
|
| 144 |
ipcRunning.store(true);
|
| 145 |
ipcThread = std::thread([this]() { ipcListenLoop(); });
|
| 146 |
|
| 147 |
-
// 8. Start crash detection timer
|
| 148 |
startTimerHz(4);
|
| 149 |
|
| 150 |
-
// Wait for "ready" notification
|
| 151 |
int waitMs = 0;
|
| 152 |
while (state.load() == State::Starting && waitMs < IPC::kHelperTimeoutMs)
|
| 153 |
{
|
|
@@ -161,8 +157,8 @@ bool HelperConnection::spawnHelper()
|
|
| 161 |
void HelperConnection::killHelper()
|
| 162 |
{
|
| 163 |
stopTimer();
|
| 164 |
-
|
| 165 |
ipcRunning.store(false);
|
|
|
|
| 166 |
if (clientSocketFd >= 0) { close(clientSocketFd); clientSocketFd = -1; }
|
| 167 |
if (serverSocketFd >= 0) { close(serverSocketFd); serverSocketFd = -1; }
|
| 168 |
if (ipcThread.joinable()) ipcThread.join();
|
|
@@ -224,7 +220,7 @@ void HelperConnection::timerCallback()
|
|
| 224 |
helperRunning.store(false);
|
| 225 |
state.store(State::Crashed);
|
| 226 |
stopTimer();
|
| 227 |
-
DBG("HelperConnection: Helper
|
| 228 |
if (onHelperCrashed)
|
| 229 |
onHelperCrashed();
|
| 230 |
}
|
|
@@ -297,6 +293,23 @@ void HelperConnection::handleNotification(const std::string& jsonStr)
|
|
| 297 |
if (onHelperReady)
|
| 298 |
juce::MessageManager::callAsync([this]() { if (onHelperReady) onHelperReady(); });
|
| 299 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
else if (notify == "param_changed")
|
| 301 |
{
|
| 302 |
int index = j.value("index", -1);
|
|
|
|
| 17 |
HelperConnection::HelperConnection()
|
| 18 |
{
|
| 19 |
myPid = getpid();
|
|
|
|
| 20 |
signal(SIGPIPE, SIG_IGN);
|
| 21 |
}
|
| 22 |
|
|
|
|
| 29 |
{
|
| 30 |
killHelper();
|
| 31 |
|
|
|
|
| 32 |
auto shmName = IPC::getShmName(myPid);
|
| 33 |
if (!sharedAudio.create(shmName))
|
| 34 |
{
|
|
|
|
| 36 |
return false;
|
| 37 |
}
|
| 38 |
|
|
|
|
| 39 |
if (!inputSem.create(IPC::getInputSemName(myPid)) ||
|
| 40 |
!outputSem.create(IPC::getOutputSemName(myPid)))
|
| 41 |
{
|
|
|
|
| 43 |
return false;
|
| 44 |
}
|
| 45 |
|
|
|
|
| 46 |
auto socketPath = IPC::getSocketPath(myPid);
|
| 47 |
unlink(socketPath.c_str());
|
| 48 |
|
|
|
|
| 61 |
if (bind(serverSocketFd, (struct sockaddr*)&addr, sizeof(addr)) < 0 ||
|
| 62 |
listen(serverSocketFd, 1) < 0)
|
| 63 |
{
|
| 64 |
+
DBG("HelperConnection: Failed to bind/listen");
|
| 65 |
close(serverSocketFd);
|
| 66 |
serverSocketFd = -1;
|
| 67 |
return false;
|
| 68 |
}
|
| 69 |
|
| 70 |
+
// Find helper .app bundle inside .vst3/Contents/Resources/
|
| 71 |
+
auto helperApp = juce::File::getSpecialLocation(juce::File::currentExecutableFile)
|
| 72 |
+
.getParentDirectory() // MacOS/
|
| 73 |
+
.getParentDirectory() // Contents/
|
| 74 |
+
.getChildFile("Resources")
|
| 75 |
+
.getChildFile("PluginBridgeHelper.app")
|
| 76 |
+
.getChildFile("Contents")
|
| 77 |
+
.getChildFile("MacOS")
|
| 78 |
+
.getChildFile(PluginBridgeConstants::kHelperBinaryName);
|
| 79 |
|
| 80 |
+
if (!helperApp.exists())
|
| 81 |
{
|
| 82 |
+
// Fallback: bare binary
|
| 83 |
+
helperApp = juce::File::getSpecialLocation(juce::File::currentExecutableFile)
|
| 84 |
+
.getParentDirectory()
|
| 85 |
+
.getParentDirectory()
|
| 86 |
+
.getChildFile("Resources")
|
| 87 |
+
.getChildFile(PluginBridgeConstants::kHelperBinaryName);
|
| 88 |
}
|
| 89 |
|
| 90 |
+
if (!helperApp.exists())
|
| 91 |
{
|
| 92 |
+
DBG("HelperConnection: Helper not found at: " + helperApp.getFullPathName());
|
| 93 |
return false;
|
| 94 |
}
|
| 95 |
|
|
|
|
| 96 |
state.store(State::Starting);
|
| 97 |
|
| 98 |
auto pidStr = juce::String(myPid).toStdString();
|
| 99 |
+
auto helperPathStr = helperApp.getFullPathName().toStdString();
|
| 100 |
|
| 101 |
pid_t pid;
|
| 102 |
char* argv[] = {
|
|
|
|
| 109 |
int spawnResult = posix_spawn(&pid, helperPathStr.c_str(), nullptr, nullptr, argv, environ);
|
| 110 |
if (spawnResult != 0)
|
| 111 |
{
|
| 112 |
+
DBG("HelperConnection: Failed to spawn: " + juce::String(strerror(spawnResult)));
|
| 113 |
state.store(State::Disconnected);
|
| 114 |
return false;
|
| 115 |
}
|
|
|
|
| 117 |
helperPid = pid;
|
| 118 |
helperRunning.store(true);
|
| 119 |
|
|
|
|
| 120 |
struct pollfd pfd;
|
| 121 |
pfd.fd = serverSocketFd;
|
| 122 |
pfd.events = POLLIN;
|
|
|
|
| 131 |
clientSocketFd = accept(serverSocketFd, nullptr, nullptr);
|
| 132 |
if (clientSocketFd < 0)
|
| 133 |
{
|
| 134 |
+
DBG("HelperConnection: Failed to accept connection");
|
| 135 |
killHelper();
|
| 136 |
return false;
|
| 137 |
}
|
| 138 |
|
|
|
|
| 139 |
int nosigpipe = 1;
|
| 140 |
setsockopt(clientSocketFd, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
|
| 141 |
|
|
|
|
| 142 |
ipcRunning.store(true);
|
| 143 |
ipcThread = std::thread([this]() { ipcListenLoop(); });
|
| 144 |
|
|
|
|
| 145 |
startTimerHz(4);
|
| 146 |
|
|
|
|
| 147 |
int waitMs = 0;
|
| 148 |
while (state.load() == State::Starting && waitMs < IPC::kHelperTimeoutMs)
|
| 149 |
{
|
|
|
|
| 157 |
void HelperConnection::killHelper()
|
| 158 |
{
|
| 159 |
stopTimer();
|
|
|
|
| 160 |
ipcRunning.store(false);
|
| 161 |
+
|
| 162 |
if (clientSocketFd >= 0) { close(clientSocketFd); clientSocketFd = -1; }
|
| 163 |
if (serverSocketFd >= 0) { close(serverSocketFd); serverSocketFd = -1; }
|
| 164 |
if (ipcThread.joinable()) ipcThread.join();
|
|
|
|
| 220 |
helperRunning.store(false);
|
| 221 |
state.store(State::Crashed);
|
| 222 |
stopTimer();
|
| 223 |
+
DBG("HelperConnection: Helper died");
|
| 224 |
if (onHelperCrashed)
|
| 225 |
onHelperCrashed();
|
| 226 |
}
|
|
|
|
| 293 |
if (onHelperReady)
|
| 294 |
juce::MessageManager::callAsync([this]() { if (onHelperReady) onHelperReady(); });
|
| 295 |
}
|
| 296 |
+
else if (notify == "gui_ready")
|
| 297 |
+
{
|
| 298 |
+
auto portName = juce::String(j.value("layerPortName", ""));
|
| 299 |
+
int width = j.value("width", 0);
|
| 300 |
+
int height = j.value("height", 0);
|
| 301 |
+
if (onGuiReady)
|
| 302 |
+
juce::MessageManager::callAsync([this, portName, width, height]()
|
| 303 |
+
{ if (onGuiReady) onGuiReady(portName, width, height); });
|
| 304 |
+
}
|
| 305 |
+
else if (notify == "resized")
|
| 306 |
+
{
|
| 307 |
+
int width = j.value("width", 0);
|
| 308 |
+
int height = j.value("height", 0);
|
| 309 |
+
if (onGuiResized)
|
| 310 |
+
juce::MessageManager::callAsync([this, width, height]()
|
| 311 |
+
{ if (onGuiResized) onGuiResized(width, height); });
|
| 312 |
+
}
|
| 313 |
else if (notify == "param_changed")
|
| 314 |
{
|
| 315 |
int index = j.value("index", -1);
|