|
|
@@ -24,29 +24,19 @@ |
|
|
|
#include "DistrhoPluginMain.cpp" |
|
|
|
|
|
|
|
#define DISTRHO_PLUGIN_HAS_UI 1 |
|
|
|
#define DISTRHO_UI_EXTERNAL |
|
|
|
|
|
|
|
#if DISTRHO_PLUGIN_HAS_UI |
|
|
|
# include "DistrhoUIMain.cpp" |
|
|
|
# ifdef DISTRHO_UI_OPENGL |
|
|
|
# include "dgl/App.hpp" |
|
|
|
# include "dgl/Window.hpp" |
|
|
|
# else |
|
|
|
# include "CarlaPipeUtils.hpp" |
|
|
|
# endif |
|
|
|
#endif |
|
|
|
|
|
|
|
#define WAIT_START_TIMEOUT 3000 /* ms */ |
|
|
|
#define WAIT_ZOMBIE_TIMEOUT 3000 /* ms */ |
|
|
|
#define WAIT_STEP 100 /* ms */ |
|
|
|
|
|
|
|
#include <fcntl.h> |
|
|
|
//#include <sys/types.h> |
|
|
|
#include <sys/wait.h> |
|
|
|
|
|
|
|
using juce::ChildProcess; |
|
|
|
using juce::NamedPipe; |
|
|
|
using juce::Random; |
|
|
|
using juce::ScopedPointer; |
|
|
|
using juce::String; |
|
|
|
using juce::StringArray; |
|
|
|
|
|
|
|
// ----------------------------------------------------------------------- |
|
|
|
|
|
|
@@ -56,32 +46,25 @@ START_NAMESPACE_DISTRHO |
|
|
|
// ----------------------------------------------------------------------- |
|
|
|
// Carla UI |
|
|
|
|
|
|
|
#ifdef DISTRHO_UI_EXTERNAL |
|
|
|
class UICarla : public CarlaPipeServer |
|
|
|
#else |
|
|
|
class UICarla |
|
|
|
#endif |
|
|
|
{ |
|
|
|
public: |
|
|
|
UICarla(const HostDescriptor* const host, PluginInternal* const plugin) |
|
|
|
: fHost(host), |
|
|
|
fPlugin(plugin), |
|
|
|
fUi(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback), |
|
|
|
fUi(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback) |
|
|
|
#ifdef DISTRHO_UI_OPENGL |
|
|
|
glApp(fUi.getApp()), |
|
|
|
glWindow(fUi.getWindow()) |
|
|
|
#else |
|
|
|
pipeRecv(-1), |
|
|
|
pipeSend(-1), |
|
|
|
pid(-1) |
|
|
|
, glWindow(fUi.getWindow()) |
|
|
|
#endif |
|
|
|
{ |
|
|
|
#ifdef DISTRHO_UI_OPENGL |
|
|
|
glWindow.setSize(fUi.getWidth(), fUi.getHeight()); |
|
|
|
glWindow.setWindowTitle(host->uiName); |
|
|
|
#else |
|
|
|
|
|
|
|
const char* argv[6]; |
|
|
|
|
|
|
|
//------------------------------------------ |
|
|
|
// argv[0] => filename |
|
|
|
|
|
|
|
CarlaString filename; |
|
|
|
filename += fHost->resourceDir; |
|
|
|
#ifdef CARLA_OS_WIN |
|
|
@@ -91,254 +74,59 @@ public: |
|
|
|
#endif |
|
|
|
filename += fUi.getExternalFilename(); |
|
|
|
|
|
|
|
argv[0] = (const char*)filename; |
|
|
|
|
|
|
|
//------------------------------------------ |
|
|
|
// argv[1] => sample rate |
|
|
|
|
|
|
|
char sampleRateStr[12+1]; |
|
|
|
std::snprintf(sampleRateStr, 12, "%g", host->get_sample_rate(host->handle)); |
|
|
|
sampleRateStr[12] = '\0'; |
|
|
|
std::snprintf(sampleRateStr, 12, "%g", host->get_sample_rate(host->handle)); |
|
|
|
|
|
|
|
argv[1] = sampleRateStr; |
|
|
|
|
|
|
|
//------------------------------------------ |
|
|
|
// argv[2-3] => pipes |
|
|
|
|
|
|
|
int pipe1[2]; /* written by host process, read by plugin UI process */ |
|
|
|
int pipe2[2]; /* written by plugin UI process, read by host process */ |
|
|
|
|
|
|
|
if (pipe(pipe1) != 0) |
|
|
|
{ |
|
|
|
fail("pipe1 creation failed"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (pipe(pipe2) != 0) |
|
|
|
{ |
|
|
|
fail("pipe2 creation failed"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
char uiPipeRecv[100+1]; |
|
|
|
char uiPipeSend[100+1]; |
|
|
|
|
|
|
|
std::snprintf(uiPipeRecv, 100, "%d", pipe1[0]); /* [0] means reading end */ |
|
|
|
std::snprintf(uiPipeSend, 100, "%d", pipe2[1]); /* [1] means writting end */ |
|
|
|
|
|
|
|
uiPipeRecv[100] = '\0'; |
|
|
|
uiPipeSend[100] = '\0'; |
|
|
|
|
|
|
|
argv[2] = uiPipeRecv; /* reading end */ |
|
|
|
argv[3] = uiPipeSend; /* writting end */ |
|
|
|
|
|
|
|
//------------------------------------------ |
|
|
|
// argv[4] => UI Name |
|
|
|
|
|
|
|
argv[4] = host->uiName; |
|
|
|
|
|
|
|
//------------------------------------------ |
|
|
|
// argv[5] => NULL |
|
|
|
|
|
|
|
argv[5] = nullptr; |
|
|
|
|
|
|
|
//------------------------------------------ |
|
|
|
// fork |
|
|
|
|
|
|
|
int ret = -1; |
|
|
|
|
|
|
|
if ((! fork_exec(argv, &ret)) || ret == -1) |
|
|
|
{ |
|
|
|
close(pipe1[0]); |
|
|
|
close(pipe1[1]); |
|
|
|
close(pipe2[0]); |
|
|
|
close(pipe2[1]); |
|
|
|
fail("fork_exec() failed"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
pid = ret; |
|
|
|
|
|
|
|
/* fork duplicated the handles, close pipe ends that are used by the child process */ |
|
|
|
close(pipe1[0]); |
|
|
|
close(pipe2[1]); |
|
|
|
|
|
|
|
pipeSend = pipe1[1]; /* [1] means writting end */ |
|
|
|
pipeRecv = pipe2[0]; /* [0] means reading end */ |
|
|
|
|
|
|
|
fcntl(pipeRecv, F_SETFL, fcntl(pipeRecv, F_GETFL) | O_NONBLOCK); |
|
|
|
|
|
|
|
//------------------------------------------ |
|
|
|
// wait a while for child process to confirm it is alive |
|
|
|
|
|
|
|
char ch; |
|
|
|
|
|
|
|
for (int i=0; ;) |
|
|
|
{ |
|
|
|
ret = read(pipeRecv, &ch, 1); |
|
|
|
|
|
|
|
switch (ret) |
|
|
|
{ |
|
|
|
case -1: |
|
|
|
if (errno == EAGAIN) |
|
|
|
{ |
|
|
|
if (i < WAIT_START_TIMEOUT / WAIT_STEP) |
|
|
|
{ |
|
|
|
carla_msleep(WAIT_STEP); |
|
|
|
i++; |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
carla_stderr("we have waited for child with pid %d to appear for %.1f seconds and we are giving up", (int)pid, (float)WAIT_START_TIMEOUT / 1000.0f); |
|
|
|
} |
|
|
|
else |
|
|
|
carla_stderr("read() failed: %s", strerror(errno)); |
|
|
|
break; |
|
|
|
|
|
|
|
case 1: |
|
|
|
if (ch == '\n') |
|
|
|
// success |
|
|
|
return; |
|
|
|
|
|
|
|
carla_stderr("read() wrong first char '%c'", ch); |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
carla_stderr("read() returned %d", ret); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
carla_stderr("force killing misbehaved child %d (start)", (int)pid); |
|
|
|
|
|
|
|
if (kill(pid, SIGKILL) == -1) |
|
|
|
{ |
|
|
|
carla_stderr("kill() failed: %s (start)\n", strerror(errno)); |
|
|
|
} |
|
|
|
|
|
|
|
/* wait a while child to exit, we dont like zombie processes */ |
|
|
|
wait_child(pid); |
|
|
|
CarlaPipeServer::start(filename, sampleRateStr, host->uiName); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void fail(const char* const error) |
|
|
|
#ifdef DISTRHO_UI_EXTERNAL |
|
|
|
~UICarla() override |
|
|
|
{ |
|
|
|
carla_stderr2(error); |
|
|
|
fHost->dispatcher(fHost->handle, HOST_OPCODE_UI_UNAVAILABLE, 0, 0, nullptr, 0.0f); |
|
|
|
CarlaPipeServer::stop(); |
|
|
|
} |
|
|
|
|
|
|
|
static bool fork_exec(const char* const argv[6], int* const retp) |
|
|
|
void fail(const char* const error) override |
|
|
|
{ |
|
|
|
int ret = *retp = vfork(); |
|
|
|
|
|
|
|
switch (ret) |
|
|
|
{ |
|
|
|
case 0: /* child process */ |
|
|
|
execvp(argv[0], (char* const*)argv); |
|
|
|
carla_stderr2("exec of UI failed: %s", strerror(errno)); |
|
|
|
return false; |
|
|
|
case -1: |
|
|
|
carla_stderr2("fork() failed to create new process for plugin UI"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
carla_stderr2(error); |
|
|
|
fHost->dispatcher(fHost->handle, HOST_OPCODE_UI_UNAVAILABLE, 0, 0, nullptr, 0.0f); |
|
|
|
} |
|
|
|
|
|
|
|
static bool wait_child(pid_t pid) |
|
|
|
void msgReceived(const char* const msg) override |
|
|
|
{ |
|
|
|
pid_t ret; |
|
|
|
int i; |
|
|
|
|
|
|
|
if (pid == -1) |
|
|
|
if (std::strcmp(msg, "control") == 0) |
|
|
|
{ |
|
|
|
carla_stderr2("Can't wait for pid -1"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
int index; |
|
|
|
float value; |
|
|
|
|
|
|
|
for (i = 0; i < WAIT_ZOMBIE_TIMEOUT / WAIT_STEP; ++i) |
|
|
|
if (readNextLineAsInt(index) && readNextLineAsFloat(value)) |
|
|
|
handleSetParameterValue(index, value); |
|
|
|
} |
|
|
|
else if (std::strcmp(msg, "configure") == 0) |
|
|
|
{ |
|
|
|
ret = waitpid(pid, NULL, WNOHANG); |
|
|
|
char* key; |
|
|
|
char* value; |
|
|
|
|
|
|
|
if (ret != 0) |
|
|
|
if (readNextLineAsString(key) && readNextLineAsString(value)) |
|
|
|
{ |
|
|
|
if (ret == pid) |
|
|
|
{ |
|
|
|
//printf("child zombie with pid %d was consumed.\n", (int)pid); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
if (ret == -1) |
|
|
|
{ |
|
|
|
carla_stderr2("waitpid(%d) failed: %s", (int)pid, strerror(errno)); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
carla_stderr2("we have waited for child pid %d to exit but we got pid %d instead", (int)pid, (int)ret); |
|
|
|
|
|
|
|
return false; |
|
|
|
handleSetState(key, value); |
|
|
|
std::free(key); |
|
|
|
std::free(value); |
|
|
|
} |
|
|
|
|
|
|
|
carla_msleep(WAIT_STEP); /* wait 100 ms */ |
|
|
|
} |
|
|
|
|
|
|
|
carla_stderr2("we have waited for child with pid %d to exit for %.1f seconds and we are giving up", (int)pid, (float)WAIT_START_TIMEOUT / 1000.0f); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
char* read_line() const |
|
|
|
{ |
|
|
|
char ch; |
|
|
|
ssize_t ret; |
|
|
|
|
|
|
|
char buf[0xff]; |
|
|
|
char* ptr = buf; |
|
|
|
|
|
|
|
for (int i=0; i < 0xff; ++i) |
|
|
|
else if (std::strcmp(msg, "exiting") == 0) |
|
|
|
{ |
|
|
|
ret = read(pipeRecv, &ch, 1); |
|
|
|
|
|
|
|
if (ret == 1 && ch != '\n') |
|
|
|
{ |
|
|
|
if (ch == '\r') |
|
|
|
ch = '\n'; |
|
|
|
|
|
|
|
*ptr++ = ch; |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (ptr != buf) |
|
|
|
{ |
|
|
|
*ptr = '\0'; |
|
|
|
return strdup(buf); |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
waitChildClose(); |
|
|
|
fHost->ui_closed(fHost->handle); |
|
|
|
} |
|
|
|
|
|
|
|
return nullptr; |
|
|
|
} |
|
|
|
|
|
|
|
~UICarla() |
|
|
|
{ |
|
|
|
printf("UI CARLA HERE 00END\n"); |
|
|
|
#ifdef DISTRHO_UI_EXTERNAL |
|
|
|
write(pipeSend, "quit\n", 5); |
|
|
|
|
|
|
|
/* for a while wait child to exit, we dont like zombie processes */ |
|
|
|
if (! wait_child(pid)) |
|
|
|
else |
|
|
|
{ |
|
|
|
carla_stderr2("force killing misbehaved child %d (exit)", (int)pid); |
|
|
|
|
|
|
|
if (kill(pid, SIGKILL) == -1) |
|
|
|
carla_stderr2("kill() failed: %s (exit)", strerror(errno)); |
|
|
|
else |
|
|
|
wait_child(pid); |
|
|
|
carla_stderr("unknown message HOST: \"%s\"", msg); |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
// --------------------------------------------- |
|
|
|
|
|
|
@@ -348,9 +136,9 @@ public: |
|
|
|
glWindow.setVisible(yesNo); |
|
|
|
#else |
|
|
|
if (yesNo) |
|
|
|
write(pipeSend, "show\n", 5); |
|
|
|
writeMsg("show\n", 5); |
|
|
|
else |
|
|
|
write(pipeSend, "hide\n", 5); |
|
|
|
writeMsg("hide\n", 5); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
@@ -358,73 +146,8 @@ public: |
|
|
|
{ |
|
|
|
fUi.idle(); |
|
|
|
|
|
|
|
#if 1//def DISTRHO_UI_EXTERNAL |
|
|
|
char* locale = strdup(setlocale(LC_NUMERIC, nullptr)); |
|
|
|
setlocale(LC_NUMERIC, "POSIX"); |
|
|
|
|
|
|
|
for (;;) |
|
|
|
{ |
|
|
|
char* const msg = read_line(); |
|
|
|
|
|
|
|
if (msg == nullptr) |
|
|
|
break; |
|
|
|
|
|
|
|
if (std::strcmp(msg, "control") == 0) |
|
|
|
{ |
|
|
|
int index; |
|
|
|
float value; |
|
|
|
char* indexStr = read_line(); |
|
|
|
char* valueStr = read_line(); |
|
|
|
|
|
|
|
index = atoi(indexStr); |
|
|
|
|
|
|
|
if (sscanf(valueStr, "%f", &value) == 1) |
|
|
|
fHost->ui_parameter_changed(fHost->handle, index, value); |
|
|
|
else |
|
|
|
fprintf(stderr, "failed to convert \"%s\" to float\n", valueStr); |
|
|
|
|
|
|
|
carla_stdout("PARAM CHANGE, %i %f", index, value); |
|
|
|
|
|
|
|
std::free(indexStr); |
|
|
|
std::free(valueStr); |
|
|
|
} |
|
|
|
else if (std::strcmp(msg, "configure") == 0) |
|
|
|
{ |
|
|
|
char* const key = read_line(); |
|
|
|
char* const value = read_line(); |
|
|
|
|
|
|
|
fHost->ui_custom_data_changed(fHost->handle, key, value); |
|
|
|
|
|
|
|
carla_stdout("STATE CHANGE, \"%s\" \"%s\"", key, value); |
|
|
|
|
|
|
|
std::free(key); |
|
|
|
std::free(value); |
|
|
|
} |
|
|
|
else if (std::strcmp(msg, "exiting") == 0) |
|
|
|
{ |
|
|
|
/* for a while wait child to exit, we dont like zombie processes */ |
|
|
|
if (! wait_child(pid)) |
|
|
|
{ |
|
|
|
fprintf(stderr, "force killing misbehaved child %d (exit)\n", (int)pid); |
|
|
|
|
|
|
|
if (kill(pid, SIGKILL) == -1) |
|
|
|
fprintf(stderr, "kill() failed: %s (exit)\n", strerror(errno)); |
|
|
|
else |
|
|
|
wait_child(pid); |
|
|
|
} |
|
|
|
|
|
|
|
fHost->ui_closed(fHost->handle); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
carla_stderr("unknown message HOST: \"%s\"", msg); |
|
|
|
} |
|
|
|
|
|
|
|
std::free(msg); |
|
|
|
} |
|
|
|
|
|
|
|
setlocale(LC_NUMERIC, locale); |
|
|
|
std::free(locale); |
|
|
|
#ifdef DISTRHO_UI_EXTERNAL |
|
|
|
CarlaPipeServer::idle(); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
@@ -436,15 +159,15 @@ public: |
|
|
|
char msgParamIndex[0xff+1]; |
|
|
|
char msgParamValue[0xff+1]; |
|
|
|
|
|
|
|
std::snprintf(msgParamIndex, 0xff, "%d", index); |
|
|
|
std::snprintf(msgParamValue, 0xff, "%f", value); |
|
|
|
std::snprintf(msgParamIndex, 0xff, "%d\n", index); |
|
|
|
std::snprintf(msgParamValue, 0xff, "%f\n", value); |
|
|
|
|
|
|
|
msgParamIndex[0xff] = '\0'; |
|
|
|
msgParamValue[0xff] = '\0'; |
|
|
|
|
|
|
|
write(pipeSend, "control\n", 8); |
|
|
|
write(pipeSend, msgParamIndex, std::strlen(msgParamIndex)); |
|
|
|
write(pipeSend, msgParamValue, std::strlen(msgParamValue)); |
|
|
|
writeMsg("control\n", 8); |
|
|
|
writeMsg(msgParamIndex); |
|
|
|
writeMsg(msgParamValue); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
@@ -456,12 +179,12 @@ public: |
|
|
|
#else |
|
|
|
char msgProgram[0xff+1]; |
|
|
|
|
|
|
|
std::snprintf(msgProgram, 0xff, "%d", realProgram); |
|
|
|
std::snprintf(msgProgram, 0xff, "%d\n", realProgram); |
|
|
|
|
|
|
|
msgProgram[0xff] = '\0'; |
|
|
|
|
|
|
|
write(pipeSend, "program\n", 8); |
|
|
|
write(pipeSend, msgProgram, std::strlen(msgProgram)); |
|
|
|
writeMsg("program\n", 8); |
|
|
|
writeMsg(msgProgram); |
|
|
|
#endif |
|
|
|
} |
|
|
|
#endif |
|
|
@@ -472,13 +195,9 @@ public: |
|
|
|
#ifdef DISTRHO_UI_OPENGL |
|
|
|
fUi.stateChanged(key, value); |
|
|
|
#else |
|
|
|
CarlaString skey(key), svalue(value); |
|
|
|
skey.replace('\n', '\r'); |
|
|
|
svalue.replace('\n', '\r'); |
|
|
|
|
|
|
|
write(pipeSend, "configure\n", 10); |
|
|
|
write(pipeSend, (const char*)skey, skey.length()); |
|
|
|
write(pipeSend, (const char*)svalue, svalue.length()); |
|
|
|
writeMsg("configure\n", 10); |
|
|
|
writeAndFixMsg(key); |
|
|
|
writeAndFixMsg(value); |
|
|
|
#endif |
|
|
|
} |
|
|
|
#endif |
|
|
@@ -488,11 +207,8 @@ public: |
|
|
|
#ifdef DISTRHO_UI_OPENGL |
|
|
|
glWindow.setWindowTitle(uiName); |
|
|
|
#else |
|
|
|
CarlaString stitle(uiTitle); |
|
|
|
stitle.replace('\n', '\r'); |
|
|
|
|
|
|
|
write(pipeSend, "uiTitle\n", 8); |
|
|
|
write(pipeSend, (const char*)stitle, stitle.length()); |
|
|
|
writeMsg("uiTitle\n", 8); |
|
|
|
writeAndFixMsg(uiTitle); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
@@ -536,12 +252,7 @@ private: |
|
|
|
|
|
|
|
#ifdef DISTRHO_UI_OPENGL |
|
|
|
// OpenGL stuff |
|
|
|
App& glApp; |
|
|
|
Window& glWindow; |
|
|
|
#else |
|
|
|
int pipeRecv; /* the pipe end that is used for receiving messages from UI */ |
|
|
|
int pipeSend; /* the pipe end that is used for sending messages to UI */ |
|
|
|
pid_t pid; |
|
|
|
#endif |
|
|
|
|
|
|
|
// --------------------------------------------- |
|
|
|