Browse Source

Add initial CarlaPipeUtils.hpp file, misc changes

tags/1.9.4
falkTX 11 years ago
parent
commit
02fdcee874
2 changed files with 57 additions and 346 deletions
  1. +54
    -343
      source/modules/carla_native/distrho/DistrhoPluginCarla.cpp
  2. +3
    -3
      source/modules/carla_native/resources/externalui.py

+ 54
- 343
source/modules/carla_native/distrho/DistrhoPluginCarla.cpp View File

@@ -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

// ---------------------------------------------


+ 3
- 3
source/modules/carla_native/resources/externalui.py View File

@@ -34,9 +34,9 @@ class ExternalUI(object):
self.fQuitReceived = False

self.fSampleRate = float(argv[1])
self.fPipeRecvFd = int(argv[2])
self.fPipeSendFd = int(argv[3])
self.fUiName = argv[4]
self.fUiName = argv[2]
self.fPipeRecvFd = int(argv[3])
self.fPipeSendFd = int(argv[4])

fcntl(self.fPipeRecvFd, F_SETFL, fcntl(self.fPipeRecvFd, F_GETFL) | O_NONBLOCK)



Loading…
Cancel
Save