diff --git a/src/DPF b/src/DPF index 3aeed6e..ba985c6 160000 --- a/src/DPF +++ b/src/DPF @@ -1 +1 @@ -Subproject commit 3aeed6e3a31c546d52fd3244157dbca21097c4f6 +Subproject commit ba985c6578e55291671685edb7485f04dd0ac9fe diff --git a/src/mod-host b/src/mod-host index 8ac6a8f..c6a567c 160000 --- a/src/mod-host +++ b/src/mod-host @@ -1 +1 @@ -Subproject commit 8ac6a8f742bfebe7ece9f4122b8283dd3c073a16 +Subproject commit c6a567c36afe6aa59a19884580c54ccfc20c898d diff --git a/src/mod-ui b/src/mod-ui index 0595788..a1e043c 160000 --- a/src/mod-ui +++ b/src/mod-ui @@ -1 +1 @@ -Subproject commit 0595788f54ade32e58b897d4292d865fd805b972 +Subproject commit a1e043cb5886fb61d193fd97ec49b280e63f1a5e diff --git a/src/plugin/DesktopAudioDriver.cpp b/src/plugin/DesktopAudioDriver.cpp index 247751b..f26b581 100644 --- a/src/plugin/DesktopAudioDriver.cpp +++ b/src/plugin/DesktopAudioDriver.cpp @@ -13,12 +13,14 @@ # include # include # ifdef __APPLE__ +# include # include # include # include # else # include # include +# include # include # include # endif @@ -29,6 +31,33 @@ namespace Jack // ----------------------------------------------------------------------------------------------------------- +#ifdef __APPLE__ +static void terminateHandler(void*) +{ + printf("Desktop driver parent has died, terminating ourselves now\n"); + fflush(stdout); + kill(getpid(), SIGTERM); +} +#endif + +static void terminateOnParentExit() noexcept +{ +#if defined(__APPLE__) + const dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, + getppid(), + DISPATCH_PROC_EXIT, + nullptr); + + dispatch_source_set_event_handler_f(source, terminateHandler); + + dispatch_resume(source); +#elif !defined(_WIN32) + prctl(PR_SET_PDEATHSIG, SIGTERM); +#endif +} + +// ----------------------------------------------------------------------------------------------------------- + class DesktopAudioDriver : public JackAudioDriver { struct Data { @@ -516,6 +545,7 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke if (driver->Open(period, rate, true, true, 2, 2, false, "", "", 0, 0) == 0) { printf("%03d:%s OK\n", __LINE__, __FUNCTION__); + Jack::terminateOnParentExit(); return driver; } diff --git a/src/plugin/DesktopPlugin.cpp b/src/plugin/DesktopPlugin.cpp index b2f0693..0b12223 100644 --- a/src/plugin/DesktopPlugin.cpp +++ b/src/plugin/DesktopPlugin.cpp @@ -25,6 +25,7 @@ class DesktopPlugin : public Plugin, float* tmpBuffers[2] = {}; uint32_t numFramesInShmBuffer = 0; uint32_t numFramesInTmpBuffer = 0; + uint portBaseNum = 0; #ifdef DISTRHO_OS_WINDOWS const WCHAR* envp; @@ -40,7 +41,10 @@ class DesktopPlugin : public Plugin, if (isDummyInstance()) return; - envp = getEvironment(); + // TODO check available ports + portBaseNum = 1; + + envp = getEvironment(portBaseNum); if (shm.init() && run()) startRunner(500); @@ -91,15 +95,15 @@ class DesktopPlugin : public Plugin, const String appDir(getAppDir()); const String jackdStr(appDir + DISTRHO_OS_SEP_STR "jackd" APP_EXT); const String jacksessionStr(appDir + DISTRHO_OS_SEP_STR "jack" DISTRHO_OS_SEP_STR "jack-session.conf"); + const String servernameStr("mod-desktop-" + String(portBaseNum)); const String sampleRateStr(static_cast(getSampleRate())); const char* const jackd_args[] = { jackdStr.buffer(), "-R", "-S", - "-n", "mod-desktop", - "-C", - jacksessionStr.buffer(), + "-n", servernameStr.buffer(), + "-C", jacksessionStr.buffer(), "-d", "desktop", "-r", sampleRateStr.buffer(), nullptr @@ -129,7 +133,7 @@ class DesktopPlugin : public Plugin, return mod_ui.start(mod_ui_args, envp); } - parameters[kParameterBasePortNumber] = 1; + parameters[kParameterBasePortNumber] = portBaseNum; return true; } diff --git a/src/plugin/DesktopUI.cpp b/src/plugin/DesktopUI.cpp index 70d33bc..740ea3e 100644 --- a/src/plugin/DesktopUI.cpp +++ b/src/plugin/DesktopUI.cpp @@ -19,6 +19,7 @@ class DesktopUI : public UI, Button buttonOpenWebGui; Button buttonOpenUserFilesDir; String label; + uint port = 0; void* webview = nullptr; public: @@ -54,8 +55,6 @@ class DesktopUI : public UI, label += getPluginFormatName(); label += " v" VERSION; - webview = addWebView(getWindow().getNativeWindowHandle()); - if (d_isNotEqual(scaleFactor, 1.0)) { setGeometryConstraints((DISTRHO_UI_DEFAULT_WIDTH - 100) * scaleFactor, @@ -70,7 +69,8 @@ class DesktopUI : public UI, ~DesktopUI() override { - destroyWebView(webview); + if (webview != nullptr) + destroyWebView(webview); } protected: @@ -85,8 +85,17 @@ class DesktopUI : public UI, { if (index == kParameterBasePortNumber) { - // TODO set port - reloadWebView(webview); + if (webview != nullptr) + { + destroyWebView(webview); + webview = nullptr; + } + + port = d_roundToUnsignedInt(value); + DISTRHO_SAFE_ASSERT_RETURN(port != 0,); + + port += kPortNumOffset; + webview = addWebView(getWindow().getNativeWindowHandle(), port); } } @@ -120,10 +129,11 @@ class DesktopUI : public UI, switch (widget->getId()) { case 1: - reloadWebView(webview); + if (webview != nullptr) + reloadWebView(webview); break; case 2: - openWebGui(); + openWebGui(port); break; case 3: openUserFilesDir(); @@ -135,6 +145,9 @@ class DesktopUI : public UI, { UI::onResize(ev); + if (webview == nullptr) + return; + const double scaleFactor = getScaleFactor(); uint offset = kVerticalOffset * scaleFactor; diff --git a/src/plugin/DistrhoPluginInfo.h b/src/plugin/DistrhoPluginInfo.h index 275cfe5..e261900 100644 --- a/src/plugin/DistrhoPluginInfo.h +++ b/src/plugin/DistrhoPluginInfo.h @@ -25,6 +25,7 @@ #define DISTRHO_UI_USER_RESIZABLE 1 static const constexpr unsigned int kVerticalOffset = 30; +static const constexpr unsigned int kPortNumOffset = 18190; enum Parameters { kParameterBasePortNumber, diff --git a/src/plugin/WebView.hpp b/src/plugin/WebView.hpp index db4e735..1184f9d 100644 --- a/src/plugin/WebView.hpp +++ b/src/plugin/WebView.hpp @@ -7,7 +7,7 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- -void* addWebView(uintptr_t viewptr); +void* addWebView(uintptr_t parentWinId, uint port); void destroyWebView(void* webview); void reloadWebView(void* webview); void resizeWebView(void* webview, uint offset, uint width, uint height); diff --git a/src/plugin/WebView.mm b/src/plugin/WebView.mm index 9c4810c..017399b 100644 --- a/src/plugin/WebView.mm +++ b/src/plugin/WebView.mm @@ -12,9 +12,17 @@ // ----------------------------------------------------------------------------------------------------------- -void* addWebView(const uintptr_t viewptr) +struct WebViewImpl { + NSView* const view; + WKWebView* const webview; + NSURLRequest* const urlreq; +}; + +// ----------------------------------------------------------------------------------------------------------- + +void* addWebView(const uintptr_t parentWinId, const uint port) { - NSView* const view = reinterpret_cast(viewptr); + NSView* const view = reinterpret_cast(parentWinId); const CGRect rect = CGRectMake(0, kVerticalOffset, @@ -22,34 +30,47 @@ DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset); WKWebView* const webview = [[WKWebView alloc] initWithFrame: rect]; - [[[webview configuration] preferences] setValue: @(true) forKey: @"developerExtrasEnabled"]; - [view addSubview:webview]; + + char url[32]; + std::snprintf(url, 31, "http://127.0.0.1:%u/", port); + NSString* const nsurl = [[NSString alloc] + initWithBytes:url + length:std::strlen(url) + encoding:NSUTF8StringEncoding]; + NSURLRequest* const urlreq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: nsurl]]; + [nsurl release]; + + [webview loadRequest: urlreq]; [webview setHidden:NO]; - return webview; + return new WebViewImpl{view, webview, urlreq}; } -void destroyWebView(void* const webviewptr) +void destroyWebView(void* const webview) { - WKWebView* const webview = static_cast(webviewptr); + WebViewImpl* const impl = static_cast(webview); + + [impl->webview setHidden:YES]; + [impl->webview removeFromSuperview]; + [impl->urlreq release]; - [webview setHidden:YES]; + delete impl; } -void reloadWebView(void* const webviewptr) +void reloadWebView(void* const webview) { - WKWebView* const webview = static_cast(webviewptr); + WebViewImpl* const impl = static_cast(webview); - [webview loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString:@"http://127.0.0.1:18181/"]]]; + [impl->webview loadRequest: impl->urlreq]; } -void resizeWebView(void* const webviewptr, const uint offset, const uint width, const uint height) +void resizeWebView(void* const webview, const uint offset, const uint width, const uint height) { - WKWebView* const webview = static_cast(webviewptr); + WebViewImpl* const impl = static_cast(webview); - [webview setFrame:CGRectMake(0, offset, width, height)]; + [impl->webview setFrame:CGRectMake(0, offset, width, height)]; } // ----------------------------------------------------------------------------------------------------------- diff --git a/src/plugin/utils.cpp b/src/plugin/utils.cpp index 37f49c8..4f7a500 100644 --- a/src/plugin/utils.cpp +++ b/src/plugin/utils.cpp @@ -78,9 +78,45 @@ const char* getAppDir() #endif } -// ----------------------------------------------------------------------------------------------------------- +#ifdef _WIN32 +WCHAR char* getDataDirW() +{ + static WCHAR dataDir[MAX_PATH] = {}; + + if (dataDir[0] == 0) + { + SHGetSpecialFolderPathW(nullptr, dataDir, CSIDL_MYDOCUMENTS, false); + _wmkdir(dataDir); + + std::wcsncat(dataDir, L"\\MOD Desktop", MAX_PATH - 1); + _wmkdir(dataDir); + } + + return dataDir; +} +#else +const char* getDataDir() +{ + static char dataDir[PATH_MAX] = {}; + + if (dataDir[0] == 0) + { + // TODO + std::strncpy(dataDir, getenv("HOME"), PATH_MAX - 1); + mkdir(dataDir, 0777); + + std::strncat(dataDir, "/Documents", PATH_MAX - 1); + mkdir(dataDir, 0777); + + std::strncat(dataDir, "/MOD Desktop", PATH_MAX - 1); + mkdir(dataDir, 0777); + } + + return dataDir; +} +#endif -static const char* gMOD_USER_FILES_DIR = nullptr; +// ----------------------------------------------------------------------------------------------------------- static void set_envp_value(char** envp, const char* const fullvalue) { @@ -100,7 +136,7 @@ static void set_envp_value(char** envp, const char* const fullvalue) *envp = strdup(fullvalue); } -static const char* set_envp_value(char** envp, const char* const key, const char* const value) +static void set_envp_value(char** envp, const char* const key, const char* const value) { const size_t keylen = std::strlen(key); const size_t valuelen = std::strlen(value); @@ -111,7 +147,7 @@ static const char* set_envp_value(char** envp, const char* const key, const char { *envp = static_cast(std::realloc(*envp, keylen + valuelen + 2)); std::memcpy(*envp + (keylen + 1), value, valuelen + 1); - return *envp + (keylen + 1); + return; } } @@ -119,13 +155,12 @@ static const char* set_envp_value(char** envp, const char* const key, const char std::memcpy(*envp, key, keylen); std::memcpy(*envp + (keylen + 1), value, valuelen + 1); (*envp)[keylen] = '='; - return *envp + (keylen + 1); } // ----------------------------------------------------------------------------------------------------------- // NOTE this needs to match initEvironment from systray side -char* const* getEvironment() +char* const* getEvironment(const uint portBaseNum) { #ifdef DISTRHO_OS_MAC const char* const* const* const environptr = _NSGetEnviron(); @@ -147,12 +182,12 @@ char* const* getEvironment() while (environ[envsize] != nullptr) ++envsize; - char** const envp = new char*[envsize + 13]; + char** const envp = new char*[envsize + 32]; for (uint i = 0; i < envsize; ++i) envp[i] = strdup(environ[i]); - for (uint i = 0; i < 13; ++i) + for (uint i = 0; i < 32; ++i) envp[envsize + i] = nullptr; // base environment details @@ -171,28 +206,10 @@ char* const* getEvironment() // get and set directory to our documents and settings, under "user documents"; also make sure it exists #ifdef _WIN32 - WCHAR dataDir[MAX_PATH] = {}; - SHGetSpecialFolderPathW(nullptr, dataDir, CSIDL_MYDOCUMENTS, false); - - _wmkdir(dataDir); - std::wcsncat(dataDir, L"\\MOD Desktop", MAX_PATH - 1); - _wmkdir(dataDir); - + const WCHAR* const dataDir = getDataDirW(); set_envp_value(envp, L"MOD_DATA_DIR", dataDir); #else - char dataDir[PATH_MAX] = {}; - - // NOTE a generic implementation is a bit complex, let Qt do it for us this time - // const QByteArray docsDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation).toUtf8()); - // std::strncpy(dataDir, docsDir.constData(), PATH_MAX - 1); - // TODO - std::strncpy(dataDir, getenv("HOME"), PATH_MAX - 1); - std::strncat(dataDir, "/Documents", PATH_MAX - 1); - - mkdir(dataDir, 0777); - std::strncat(dataDir, "/MOD Desktop", PATH_MAX - 1); - mkdir(dataDir, 0777); - + const char* const dataDir = getDataDir(); set_envp_value(envp, "MOD_DATA_DIR", dataDir); #endif @@ -290,12 +307,12 @@ char* const* getEvironment() std::memcpy(path, dataDir, dataDirLen * sizeof(WCHAR)); std::wcsncpy(path + dataDirLen, L"\\user-files", MAX_PATH - dataDirLen - 1); _wmkdir(path); - gMOD_USER_FILES_DIR = set_envp_value(envp, L"MOD_USER_FILES_DIR", path); + set_envp_value(envp, L"MOD_USER_FILES_DIR", path); #else std::memcpy(path, dataDir, dataDirLen); std::strncpy(path + dataDirLen, "/user-files", PATH_MAX - dataDirLen - 1); mkdir(path, 0777); - gMOD_USER_FILES_DIR = set_envp_value(envp, "MOD_USER_FILES_DIR", path); + set_envp_value(envp, "MOD_USER_FILES_DIR", path); #endif // set path to MOD keys (plugin licenses) @@ -352,30 +369,61 @@ char* const* getEvironment() set_envp_value(envp, "JACK_DRIVER_DIR", path); #endif + // plugin-specific port details + #ifdef _WIN32 + #else + std::snprintf(path, PATH_MAX - 1, "MOD_DESKTOP_SERVER_NAME=mod-desktop-%u", portBaseNum); + set_envp_value(envp, path); + + std::snprintf(path, PATH_MAX - 1, "MOD_DEVICE_HOST_PORT=%u", kPortNumOffset + portBaseNum + 1); + set_envp_value(envp, path); + + std::snprintf(path, PATH_MAX - 1, "MOD_DEVICE_WEBSERVER_PORT=%u", kPortNumOffset + portBaseNum); + set_envp_value(envp, path); + #endif + return envp; } // ----------------------------------------------------------------------------------------------------------- -static void* _openWebGui(void*) +static void* _openWebGui(void* const arg) { - #if defined(_WIN32) - ShellExecuteW(nullptr, L"open", L"http://127.0.0.1:18181", nullptr, nullptr, SW_SHOWDEFAULT); - #elif defined(__APPLE__) - std::system("open \"http://127.0.0.1:18181\""); + const uint* const portptr = static_cast(arg); + const uint port = *portptr; + delete portptr; + + #ifdef _WIN32 + WCHAR url[32] = {}; + std::wsnprintf(url, 31, "http://127.0.0.1:%u", port); + ShellExecuteW(nullptr, L"open", url, nullptr, nullptr, SW_SHOWDEFAULT); + #else + #ifdef __APPLE__ + String cmd("open \"http://127.0.0.1:"); #else - std::system("xdg-open \"http://127.0.0.1:18181\""); + String cmd("xdg-open \"http://127.0.0.1:"); #endif + cmd += String(port); + cmd += "/\""; + std::system(cmd); + #endif + return nullptr; } -void openWebGui() +void openWebGui(const uint port) { + uint* const portptr = new uint; + *portptr = port; + pthread_t thread; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, 1); - pthread_create(&thread, &attr, _openWebGui, nullptr); + + if (pthread_create(&thread, &attr, _openWebGui, portptr) != 0) + delete portptr; + pthread_attr_destroy(&attr); } @@ -383,18 +431,19 @@ void openWebGui() static void* _openUserFilesDir(void*) { - #if defined(_WIN32) - ShellExecuteW(NULL, L"explore", gMOD_USER_FILES_DIR, nullptr, nullptr, SW_SHOWDEFAULT); - #elif defined(__APPLE__) + #ifdef _WIN32 + ShellExecuteW(NULL, L"explore", getDataDirW(), nullptr, nullptr, SW_SHOWDEFAULT); + #else + #ifdef __APPLE__ String cmd("open \""); - cmd += gMOD_USER_FILES_DIR; - cmd += "\""; - std::system(cmd); #else String cmd("xdg-open \""); - cmd += gMOD_USER_FILES_DIR; - cmd += "\""; #endif + cmd += getDataDir(); + cmd += "\""; + std::system(cmd); + #endif + return nullptr; } diff --git a/src/plugin/utils.hpp b/src/plugin/utils.hpp index 03fb5d4..392e522 100644 --- a/src/plugin/utils.hpp +++ b/src/plugin/utils.hpp @@ -15,11 +15,11 @@ const char* getAppDir(); /* Get environment to be used for a child process. */ -char* const* getEvironment(); +char* const* getEvironment(uint portBaseNum); /* Open a web browser with the mod-ui URL as address. */ -void openWebGui(); +void openWebGui(uint port); /* Open the "user files" directory in a file manager/explorer. */ diff --git a/src/systray/utils.cpp b/src/systray/utils.cpp index 7a4d852..bd6114c 100644 --- a/src/systray/utils.cpp +++ b/src/systray/utils.cpp @@ -55,11 +55,15 @@ void initEvironment() SetEnvironmentVariableW(L"JACK_NO_START_SERVER", L"1"); SetEnvironmentVariableW(L"LANG", L"en_US.UTF-8"); SetEnvironmentVariableW(L"MOD_DESKTOP", L"1"); + SetEnvironmentVariableW(L"MOD_DEVICE_HOST_PORT", L"18182"); + SetEnvironmentVariableW(L"MOD_DEVICE_WEBSERVER_PORT", L"18181"); SetEnvironmentVariableW(L"PYTHONUNBUFFERED", L"1"); #else setenv("JACK_NO_START_SERVER", "1", 1); setenv("LANG", "en_US.UTF-8", 1); setenv("MOD_DESKTOP", "1", 1); + setenv("MOD_DEVICE_HOST_PORT", "18182", 1); + setenv("MOD_DEVICE_WEBSERVER_PORT", "18181", 1); setenv("PYTHONUNBUFFERED", "1", 1); #endif diff --git a/utils/cxfreeze/mod-ui-setup.py b/utils/cxfreeze/mod-ui-setup.py index 0bba05d..17df057 100644 --- a/utils/cxfreeze/mod-ui-setup.py +++ b/utils/cxfreeze/mod-ui-setup.py @@ -29,8 +29,8 @@ os.environ['MOD_DEFAULT_PEDALBOARD'] = os.path.join(resdir, 'default.pedalboard') os.environ['MOD_DESKTOP'] = '1' os.environ['MOD_DEV_ENVIRONMENT'] = '0' -os.environ['MOD_DEVICE_HOST_PORT'] = '18182' -os.environ['MOD_DEVICE_WEBSERVER_PORT'] = '18181' +os.environ['MOD_DEVICE_HOST_PORT'] = os.environ.get("MOD_DEVICE_HOST_PORT", '18182') +os.environ['MOD_DEVICE_WEBSERVER_PORT'] = os.environ.get("MOD_DEVICE_WEBSERVER_PORT", '18181') os.environ['MOD_HARDWARE_DESC_FILE'] = os.path.join(resdir, 'mod-hardware-descriptor.json') os.environ['MOD_HTML_DIR'] = os.path.join(resdir, 'html') os.environ['MOD_IMAGE_VERSION_PATH'] = os.path.join(resdir, 'VERSION') diff --git a/utils/debug/jackd b/utils/debug/jackd index 555e80a..8c345e3 100755 --- a/utils/debug/jackd +++ b/utils/debug/jackd @@ -70,6 +70,8 @@ else fi export LV2_PATH +export MOD_DESKTOP=1 +export MOD_DEVICE_HOST_PORT=18182 export MOD_KEYS_PATH="$(convert_path "${DOCS_DIR}/MOD Desktop/keys/")" export MOD_USER_FILES_DIR="$(convert_path "${DOCS_DIR}/MOD Desktop/user-files")" diff --git a/utils/jack/jack-session-alsamidi.conf b/utils/jack/jack-session-alsamidi.conf index 43e6b19..c57309c 100644 --- a/utils/jack/jack-session-alsamidi.conf +++ b/utils/jack/jack-session-alsamidi.conf @@ -1,4 +1,4 @@ l system_midi alsa_midi l mod-midi-merger mod-midi-merger l mod-midi-broadcaster mod-midi-broadcaster -l mod-host mod-host 18182 +l mod-host mod-host diff --git a/utils/jack/jack-session.conf b/utils/jack/jack-session.conf index 76ac792..c37e69f 100644 --- a/utils/jack/jack-session.conf +++ b/utils/jack/jack-session.conf @@ -1,3 +1,3 @@ l mod-midi-merger mod-midi-merger l mod-midi-broadcaster mod-midi-broadcaster -l mod-host mod-host 18182 +l mod-host mod-host