Signed-off-by: falkTX <falktx@gmail.com>tags/v2.1-alpha2
| @@ -6,8 +6,8 @@ | |||
| <rect> | |||
| <x>0</x> | |||
| <y>0</y> | |||
| <width>471</width> | |||
| <height>369</height> | |||
| <width>496</width> | |||
| <height>438</height> | |||
| </rect> | |||
| </property> | |||
| <property name="windowTitle"> | |||
| @@ -27,39 +27,23 @@ | |||
| <string>Application</string> | |||
| </property> | |||
| <layout class="QGridLayout" name="gridLayout"> | |||
| <item row="1" column="0"> | |||
| <widget class="QLabel" name="label"> | |||
| <property name="text"> | |||
| <string>Command:</string> | |||
| </property> | |||
| <property name="alignment"> | |||
| <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="1" column="1" colspan="3"> | |||
| <widget class="QLineEdit" name="le_command"/> | |||
| </item> | |||
| <item row="2" column="3"> | |||
| <spacer name="horizontalSpacer_3"> | |||
| <item row="0" column="0" rowspan="3"> | |||
| <spacer name="horizontalSpacer_4"> | |||
| <property name="orientation"> | |||
| <enum>Qt::Horizontal</enum> | |||
| </property> | |||
| <property name="sizeType"> | |||
| <enum>QSizePolicy::Ignored</enum> | |||
| <enum>QSizePolicy::Fixed</enum> | |||
| </property> | |||
| <property name="sizeHint" stdset="0"> | |||
| <size> | |||
| <width>87</width> | |||
| <height>1</height> | |||
| <width>20</width> | |||
| <height>60</height> | |||
| </size> | |||
| </property> | |||
| </spacer> | |||
| </item> | |||
| <item row="0" column="1" colspan="3"> | |||
| <widget class="QLineEdit" name="le_name"/> | |||
| </item> | |||
| <item row="0" column="0"> | |||
| <item row="0" column="1"> | |||
| <widget class="QLabel" name="label_10"> | |||
| <property name="text"> | |||
| <string>Name:</string> | |||
| @@ -69,22 +53,124 @@ | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="3" column="0"> | |||
| <spacer name="horizontalSpacer_4"> | |||
| <item row="0" column="2" colspan="2"> | |||
| <widget class="QLineEdit" name="le_name"/> | |||
| </item> | |||
| <item row="0" column="4" rowspan="3"> | |||
| <spacer name="horizontalSpacer_3"> | |||
| <property name="orientation"> | |||
| <enum>Qt::Horizontal</enum> | |||
| </property> | |||
| <property name="sizeType"> | |||
| <enum>QSizePolicy::Ignored</enum> | |||
| <enum>QSizePolicy::Fixed</enum> | |||
| </property> | |||
| <property name="sizeHint" stdset="0"> | |||
| <size> | |||
| <width>1</width> | |||
| <height>1</height> | |||
| <width>20</width> | |||
| <height>60</height> | |||
| </size> | |||
| </property> | |||
| </spacer> | |||
| </item> | |||
| <item row="1" column="1"> | |||
| <widget class="QLabel" name="label_5"> | |||
| <property name="text"> | |||
| <string>Application:</string> | |||
| </property> | |||
| <property name="alignment"> | |||
| <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="1" column="2"> | |||
| <widget class="QRadioButton" name="rb_template"> | |||
| <property name="enabled"> | |||
| <bool>false</bool> | |||
| </property> | |||
| <property name="sizePolicy"> | |||
| <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | |||
| <horstretch>0</horstretch> | |||
| <verstretch>0</verstretch> | |||
| </sizepolicy> | |||
| </property> | |||
| <property name="text"> | |||
| <string>From template</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="1" column="3"> | |||
| <widget class="QRadioButton" name="rb_custom"> | |||
| <property name="sizePolicy"> | |||
| <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | |||
| <horstretch>0</horstretch> | |||
| <verstretch>0</verstretch> | |||
| </sizepolicy> | |||
| </property> | |||
| <property name="text"> | |||
| <string>Custom</string> | |||
| </property> | |||
| <property name="checked"> | |||
| <bool>true</bool> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="2" column="1" colspan="3"> | |||
| <widget class="QStackedWidget" name="stackedWidget"> | |||
| <property name="currentIndex"> | |||
| <number>1</number> | |||
| </property> | |||
| <widget class="QWidget" name="page_template"> | |||
| <layout class="QHBoxLayout" name="horizontalLayout_2"> | |||
| <property name="margin"> | |||
| <number>0</number> | |||
| </property> | |||
| <item> | |||
| <widget class="QLabel" name="l_template"> | |||
| <property name="text"> | |||
| <string>Template:</string> | |||
| </property> | |||
| <property name="alignment"> | |||
| <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item> | |||
| <widget class="QComboBox" name="cb_template"> | |||
| <property name="sizePolicy"> | |||
| <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | |||
| <horstretch>0</horstretch> | |||
| <verstretch>0</verstretch> | |||
| </sizepolicy> | |||
| </property> | |||
| <property name="editable"> | |||
| <bool>false</bool> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| <widget class="QWidget" name="page_command"> | |||
| <layout class="QHBoxLayout" name="horizontalLayout_3"> | |||
| <property name="margin"> | |||
| <number>0</number> | |||
| </property> | |||
| <item> | |||
| <widget class="QLabel" name="l_command"> | |||
| <property name="text"> | |||
| <string>Command:</string> | |||
| </property> | |||
| <property name="alignment"> | |||
| <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item> | |||
| <widget class="QLineEdit" name="le_command"/> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| </item> | |||
| @@ -122,7 +208,7 @@ | |||
| <item> | |||
| <widget class="QComboBox" name="cb_session_mgr"> | |||
| <property name="currentIndex"> | |||
| <number>1</number> | |||
| <number>0</number> | |||
| </property> | |||
| <item> | |||
| <property name="text"> | |||
| @@ -131,12 +217,12 @@ | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>Auto</string> | |||
| <string>LADISH (SIGUSR1)</string> | |||
| </property> | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>LADISH (SIGUSR1)</string> | |||
| <string>NSM</string> | |||
| </property> | |||
| </item> | |||
| </widget> | |||
| @@ -895,12 +895,24 @@ public: | |||
| * Load a project file. | |||
| * @note Already loaded plugins are not removed; call removeAllPlugins() first if needed. | |||
| */ | |||
| bool loadProject(const char* const filename); | |||
| bool loadProject(const char* const filename, const bool setAsCurrentProject); | |||
| /*! | |||
| * Save current project to a file. | |||
| */ | |||
| bool saveProject(const char* const filename); | |||
| bool saveProject(const char* const filename, const bool setAsCurrentProject); | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| /*! | |||
| * Get the currently set project filename. | |||
| */ | |||
| const char* getCurrentProjectFilename() const noexcept; | |||
| /*! | |||
| * Clear the currently set project filename. | |||
| */ | |||
| void clearCurrentProjectFilename() noexcept; | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| // Information (base) | |||
| @@ -410,6 +410,11 @@ CARLA_EXPORT bool carla_load_project(const char* filename); | |||
| CARLA_EXPORT bool carla_save_project(const char* filename); | |||
| #ifndef BUILD_BRIDGE | |||
| /*! | |||
| * Clear the currently set project filename. | |||
| */ | |||
| CARLA_EXPORT void carla_clear_project_filename(); | |||
| /*! | |||
| * Connect two patchbay ports. | |||
| * @param groupIdA Output group | |||
| @@ -812,7 +812,7 @@ bool carla_load_project(const char* filename) | |||
| carla_debug("carla_load_project(\"%s\")", filename); | |||
| return gStandalone.engine->loadProject(filename); | |||
| return gStandalone.engine->loadProject(filename, true); | |||
| } | |||
| bool carla_save_project(const char* filename) | |||
| @@ -822,10 +822,19 @@ bool carla_save_project(const char* filename) | |||
| carla_debug("carla_save_project(\"%s\")", filename); | |||
| return gStandalone.engine->saveProject(filename); | |||
| return gStandalone.engine->saveProject(filename, true); | |||
| } | |||
| #ifndef BUILD_BRIDGE | |||
| void carla_clear_project_filename() | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr,); | |||
| carla_debug("carla_clear_project_filename()"); | |||
| gStandalone.engine->clearCurrentProjectFilename(); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| bool carla_patchbay_connect(uint groupIdA, uint portIdA, uint groupIdB, uint portIdB) | |||
| @@ -990,7 +990,7 @@ bool CarlaEngine::loadFile(const char* const filename) | |||
| // NOTE: please keep in sync with carla_get_supported_file_extensions!! | |||
| if (extension == "carxp" || extension == "carxs") | |||
| return loadProject(filename); | |||
| return loadProject(filename, false); | |||
| // ------------------------------------------------------------------- | |||
| @@ -1126,7 +1126,7 @@ bool CarlaEngine::loadFile(const char* const filename) | |||
| return false; | |||
| } | |||
| bool CarlaEngine::loadProject(const char* const filename) | |||
| bool CarlaEngine::loadProject(const char* const filename, const bool setAsCurrentProject) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(filename != nullptr && filename[0] != '\0', "Invalid filename"); | |||
| @@ -1136,11 +1136,18 @@ bool CarlaEngine::loadProject(const char* const filename) | |||
| File file(jfilename); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(file.existsAsFile(), "Requested file does not exist or is not a readable file"); | |||
| if (setAsCurrentProject) | |||
| { | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| pData->currentProjectFilename = filename; | |||
| #endif | |||
| } | |||
| XmlDocument xml(file); | |||
| return loadProjectInternal(xml); | |||
| } | |||
| bool CarlaEngine::saveProject(const char* const filename) | |||
| bool CarlaEngine::saveProject(const char* const filename, const bool setAsCurrentProject) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(filename != nullptr && filename[0] != '\0', "Invalid filename"); | |||
| carla_debug("CarlaEngine::saveProject(\"%s\")", filename); | |||
| @@ -1151,6 +1158,13 @@ bool CarlaEngine::saveProject(const char* const filename) | |||
| const String jfilename = String(CharPointer_UTF8(filename)); | |||
| File file(jfilename); | |||
| if (setAsCurrentProject) | |||
| { | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| pData->currentProjectFilename = filename; | |||
| #endif | |||
| } | |||
| if (file.replaceWithData(out.getData(), out.getDataSize())) | |||
| return true; | |||
| @@ -1158,6 +1172,18 @@ bool CarlaEngine::saveProject(const char* const filename) | |||
| return false; | |||
| } | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| const char* CarlaEngine::getCurrentProjectFilename() const noexcept | |||
| { | |||
| return pData->currentProjectFilename; | |||
| } | |||
| void CarlaEngine::clearCurrentProjectFilename() noexcept | |||
| { | |||
| pData->currentProjectFilename.clear(); | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Information (base) | |||
| @@ -375,6 +375,7 @@ CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept | |||
| actionCanceled(false), | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| loadingProject(false), | |||
| currentProjectFilename(), | |||
| #endif | |||
| hints(0x0), | |||
| bufferSize(0), | |||
| @@ -223,6 +223,7 @@ struct CarlaEngine::ProtectedData { | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| bool loadingProject; | |||
| CarlaString currentProjectFilename; | |||
| #endif | |||
| uint hints; | |||
| @@ -175,7 +175,7 @@ protected: | |||
| CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(filename), true); | |||
| try { | |||
| ok = fEngine->loadProject(filename); | |||
| ok = fEngine->loadProject(filename, true); | |||
| } CARLA_SAFE_EXCEPTION("loadProject"); | |||
| delete[] filename; | |||
| @@ -187,11 +187,15 @@ protected: | |||
| CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(filename), true); | |||
| try { | |||
| ok = fEngine->saveProject(filename); | |||
| ok = fEngine->saveProject(filename, true); | |||
| } CARLA_SAFE_EXCEPTION("saveProject"); | |||
| delete[] filename; | |||
| } | |||
| else if (std::strcmp(msg, "clear_project_filename") == 0) | |||
| { | |||
| fEngine->clearCurrentProjectFilename(); | |||
| } | |||
| else if (std::strcmp(msg, "patchbay_connect") == 0) | |||
| { | |||
| uint32_t groupA, portA, groupB, portB; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * Carla Plugin JACK | |||
| * Copyright (C) 2016-2018 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2016-2019 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| @@ -28,6 +28,11 @@ | |||
| #include "CarlaShmUtils.hpp" | |||
| #include "CarlaThread.hpp" | |||
| #ifdef HAVE_LIBLO | |||
| # include "CarlaOscUtils.hpp" | |||
| #endif | |||
| #include "water/files/File.h" | |||
| #include "water/misc/Time.h" | |||
| #include "water/text/StringArray.h" | |||
| #include "water/threads/ChildProcess.h" | |||
| @@ -47,6 +52,28 @@ using water::Time; | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| enum { | |||
| FLAG_CONTROL_WINDOW = 0x01, | |||
| FLAG_CAPTURE_FIRST_WINDOW = 0x02, | |||
| FLAG_BUFFERS_ADDITION_MODE = 0x10, | |||
| }; | |||
| enum { | |||
| SESSION_MGR_NONE = 0, | |||
| SESSION_MGR_AUTO = 1, | |||
| SESSION_MGR_JACK = 2, | |||
| SESSION_MGR_LADISH = 3, | |||
| SESSION_MGR_NSM = 4, | |||
| }; | |||
| static size_t safe_rand(const size_t limit) | |||
| { | |||
| const int r = std::rand(); | |||
| CARLA_SAFE_ASSERT_RETURN(r >= 0, 0); | |||
| return static_cast<uint>(r) % limit; | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------------------- | |||
| // Fallback data | |||
| @@ -62,17 +89,22 @@ public: | |||
| kEngine(engine), | |||
| kPlugin(plugin), | |||
| fShmIds(), | |||
| fNumPorts(), | |||
| fSetupLabel(), | |||
| #ifdef HAVE_LIBLO | |||
| fOscClientAddress(nullptr), | |||
| fOscServer(nullptr), | |||
| fProject(), | |||
| #endif | |||
| fProcess() {} | |||
| void setData(const char* const shmIds, const char* const numPorts) noexcept | |||
| void setData(const char* const shmIds, const char* const setupLabel) noexcept | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(shmIds != nullptr && shmIds[0] != '\0',); | |||
| CARLA_SAFE_ASSERT_RETURN(numPorts != nullptr && numPorts[0] != '\0',); | |||
| CARLA_SAFE_ASSERT_RETURN(setupLabel != nullptr && setupLabel[0] != '\0',); | |||
| CARLA_SAFE_ASSERT(! isThreadRunning()); | |||
| fShmIds = shmIds; | |||
| fNumPorts = numPorts; | |||
| fShmIds = shmIds; | |||
| fSetupLabel = setupLabel; | |||
| } | |||
| uintptr_t getProcessID() const noexcept | |||
| @@ -82,9 +114,156 @@ public: | |||
| return (uintptr_t)fProcess->getPID(); | |||
| } | |||
| void sendTerminate() const noexcept | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(fProcess != nullptr,); | |||
| fProcess->terminate(); | |||
| } | |||
| #ifdef HAVE_LIBLO | |||
| void nsmSave(const char* const setupLabel) | |||
| { | |||
| if (fOscClientAddress == nullptr) | |||
| return; | |||
| if (fSetupLabel != setupLabel) | |||
| fSetupLabel = setupLabel; | |||
| maybeOpenFirstTime(); | |||
| lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/save", ""); | |||
| } | |||
| void nsmShowGui(const bool yesNo) | |||
| { | |||
| if (fOscClientAddress == nullptr) | |||
| return; | |||
| lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, | |||
| yesNo ? "/nsm/client/show_optional_gui" | |||
| : "/nsm/client/hide_optional_gui", ""); | |||
| } | |||
| #endif | |||
| protected: | |||
| #ifdef HAVE_LIBLO | |||
| static void _osc_error_handler(int num, const char* msg, const char* path) | |||
| { | |||
| carla_stderr2("CarlaPluginJackThread::_osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); | |||
| } | |||
| static int _broadcast_handler(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(data != nullptr, 0); | |||
| carla_stdout("CarlaPluginJackThread::_broadcast_handler(%s, %s, %p, %i)", path, types, argv, argc); | |||
| return ((CarlaPluginJackThread*)data)->handleBroadcast(path, types, argv, msg); | |||
| } | |||
| void maybeOpenFirstTime() | |||
| { | |||
| if (fProject.path.isNotEmpty()) | |||
| return; | |||
| if (fSetupLabel.length() <= 6) | |||
| return; | |||
| if (fProject.init(kEngine->getCurrentProjectFilename(), &fSetupLabel[6])) | |||
| { | |||
| carla_stdout("Sending first open signal %s %s %s", | |||
| fProject.path.buffer(), fProject.display.buffer(), fProject.clientName.buffer()); | |||
| lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/open", "sss", | |||
| fProject.path.buffer(), fProject.display.buffer(), fProject.clientName.buffer()); | |||
| } | |||
| } | |||
| int handleBroadcast(const char* path, const char* types, lo_arg** argv, lo_message msg) | |||
| { | |||
| if (std::strcmp(path, "/nsm/server/announce") == 0) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sssiii") == 0, 0); | |||
| const lo_address msgAddress(lo_message_get_source(msg)); | |||
| CARLA_SAFE_ASSERT_RETURN(msgAddress != nullptr, 0); | |||
| char* const msgURL(lo_address_get_url(msgAddress)); | |||
| CARLA_SAFE_ASSERT_RETURN(msgURL != nullptr, 0); | |||
| if (fOscClientAddress != nullptr) | |||
| lo_address_free(fOscClientAddress); | |||
| fOscClientAddress = lo_address_new_from_url(msgURL); | |||
| CARLA_SAFE_ASSERT_RETURN(fOscClientAddress != nullptr, 0); | |||
| fProject.appName = &argv[0]->s; | |||
| static const char* const method = "/nsm/server/announce"; | |||
| static const char* const message = "Howdy, what took you so long?"; | |||
| static const char* const smName = "Carla"; | |||
| static const char* const features = ":server-control:optional-gui:"; | |||
| lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ssss", | |||
| method, message, smName, features); | |||
| maybeOpenFirstTime(); | |||
| } | |||
| else if (std::strcmp(path, "/reply") == 0) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ss") == 0, 0); | |||
| const char* const method = &argv[0]->s; | |||
| const char* const message = &argv[1]->s; | |||
| carla_stdout("Got reply of '%s' as '%s'", method, message); | |||
| if (std::strcmp(method, "/nsm/client/open") == 0) | |||
| { | |||
| carla_stdout("Sending 'Session is loaded' to %s", fProject.appName.buffer()); | |||
| lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/session_is_loaded", ""); | |||
| } | |||
| } | |||
| else if (std::strcmp(path, "/nsm/client/gui_is_shown") == 0) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "") == 0, 0); | |||
| kEngine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, kPlugin->getId(), 1, 0, 0.0f, nullptr); | |||
| } | |||
| else if (std::strcmp(path, "/nsm/client/gui_is_hidden") == 0) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "") == 0, 0); | |||
| kEngine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, kPlugin->getId(), 0, 0, 0.0f, nullptr); | |||
| } | |||
| return 0; | |||
| } | |||
| #endif | |||
| void run() | |||
| { | |||
| #ifdef HAVE_LIBLO | |||
| if (fOscClientAddress != nullptr) | |||
| { | |||
| lo_address_free(fOscClientAddress); | |||
| fOscClientAddress = nullptr; | |||
| } | |||
| const int sessionManager = fSetupLabel[4] - '0'; | |||
| if (sessionManager == SESSION_MGR_NSM) | |||
| { | |||
| // NSM support | |||
| fOscServer = lo_server_new_with_proto(nullptr, LO_UDP, _osc_error_handler); | |||
| CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr,); | |||
| lo_server_add_method(fOscServer, nullptr, nullptr, _broadcast_handler, this); | |||
| } | |||
| #endif | |||
| if (fProcess == nullptr) | |||
| { | |||
| fProcess = new ChildProcess(); | |||
| @@ -127,6 +306,7 @@ protected: | |||
| const ScopedEngineEnvironmentLocker _seel(kEngine); | |||
| const ScopedEnvVar sev3("NSM_URL", lo_server_get_url(fOscServer)); | |||
| const ScopedEnvVar sev2("LD_LIBRARY_PATH", libjackdir.buffer()); | |||
| const ScopedEnvVar sev1("LD_PRELOAD", ldpreload.isNotEmpty() ? ldpreload.buffer() : nullptr); | |||
| @@ -135,7 +315,7 @@ protected: | |||
| else | |||
| carla_unsetenv("CARLA_FRONTEND_WIN_ID"); | |||
| carla_setenv("CARLA_LIBJACK_SETUP", fNumPorts.buffer()); | |||
| carla_setenv("CARLA_LIBJACK_SETUP", fSetupLabel.buffer()); | |||
| carla_setenv("CARLA_SHM_IDS", fShmIds.buffer()); | |||
| started = fProcess->start(arguments); | |||
| @@ -149,7 +329,32 @@ protected: | |||
| } | |||
| for (; fProcess->isRunning() && ! shouldThreadExit();) | |||
| carla_msleep(50); | |||
| { | |||
| #ifdef HAVE_LIBLO | |||
| if (sessionManager == SESSION_MGR_NSM) | |||
| { | |||
| lo_server_recv_noblock(fOscServer, 50); | |||
| } | |||
| else | |||
| #endif | |||
| { | |||
| carla_msleep(50); | |||
| } | |||
| } | |||
| #ifdef HAVE_LIBLO | |||
| if (sessionManager == SESSION_MGR_NSM) | |||
| { | |||
| lo_server_free(fOscServer); | |||
| fOscServer = nullptr; | |||
| if (fOscClientAddress != nullptr) | |||
| { | |||
| lo_address_free(fOscClientAddress); | |||
| fOscClientAddress = nullptr; | |||
| } | |||
| } | |||
| #endif | |||
| // we only get here if bridge crashed or thread asked to exit | |||
| if (fProcess->isRunning() && shouldThreadExit()) | |||
| @@ -184,7 +389,42 @@ private: | |||
| CarlaPlugin* const kPlugin; | |||
| CarlaString fShmIds; | |||
| CarlaString fNumPorts; | |||
| CarlaString fSetupLabel; | |||
| #ifdef HAVE_LIBLO | |||
| lo_address fOscClientAddress; | |||
| lo_server fOscServer; | |||
| struct ProjectData { | |||
| CarlaString appName; | |||
| CarlaString path; | |||
| CarlaString display; | |||
| CarlaString clientName; | |||
| ProjectData() | |||
| : appName(), | |||
| path(), | |||
| display(), | |||
| clientName() {} | |||
| bool init(const char* const engineProjectFilename, const char* const uniqueCodeID) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(engineProjectFilename != nullptr && engineProjectFilename[0] != '\0', false); | |||
| CARLA_SAFE_ASSERT_RETURN(uniqueCodeID != nullptr && uniqueCodeID[0] != '\0', false); | |||
| CARLA_SAFE_ASSERT_RETURN(appName.isNotEmpty(), false); | |||
| const File file(File(engineProjectFilename).withFileExtension(uniqueCodeID)); | |||
| path = file.getFullPathName().toRawUTF8(); | |||
| display = file.getFileNameWithoutExtension().toRawUTF8(); | |||
| clientName = appName + "." + uniqueCodeID; | |||
| return true; | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT(ProjectData) | |||
| } fProject; | |||
| #endif | |||
| ScopedPointer<ChildProcess> fProcess; | |||
| @@ -247,6 +487,8 @@ public: | |||
| fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientQuit); | |||
| fShmNonRtClientControl.commitWrite(); | |||
| fBridgeThread.sendTerminate(); | |||
| if (! fTimedOut) | |||
| waitForClient("stopping", 3000); | |||
| } | |||
| @@ -326,6 +568,13 @@ public: | |||
| void prepareForSave() noexcept override | |||
| { | |||
| #ifdef HAVE_LIBLO | |||
| if (fInfo.setupLabel.length() == 6) | |||
| setupUniqueProjectID(); | |||
| fBridgeThread.nsmSave(fInfo.setupLabel); | |||
| #endif | |||
| { | |||
| const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | |||
| @@ -402,6 +651,10 @@ public: | |||
| CARLA_SAFE_ASSERT_RETURN(restartBridgeThread(),); | |||
| } | |||
| #ifdef HAVE_LIBLO | |||
| fBridgeThread.nsmShowGui(yesNo); | |||
| #endif | |||
| const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | |||
| fShmNonRtClientControl.writeOpcode(yesNo ? kPluginBridgeNonRtClientShowUI : kPluginBridgeNonRtClientHideUI); | |||
| @@ -1236,7 +1489,7 @@ public: | |||
| // --------------------------------------------------------------- | |||
| // check setup | |||
| if (std::strlen(label) != 6) | |||
| if (std::strlen(label) < 6) | |||
| { | |||
| pData->engine->setLastError("invalid application setup received"); | |||
| return false; | |||
| @@ -1256,7 +1509,11 @@ public: | |||
| fInfo.setupLabel = label; | |||
| const int setupHints = label[5] - '0'; | |||
| // --------------------------------------------------------------- | |||
| // set project unique id | |||
| if (label[6] == '\0') | |||
| setupUniqueProjectID(); | |||
| // --------------------------------------------------------------- | |||
| // set info | |||
| @@ -1306,6 +1563,8 @@ public: | |||
| // --------------------------------------------------------------- | |||
| // setup hints and options | |||
| const int setupHints = label[5] - '0'; | |||
| // FIXME dryWet broken | |||
| pData->hints = PLUGIN_IS_BRIDGE | PLUGIN_OPTION_FIXED_BUFFERS; | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| @@ -1313,7 +1572,7 @@ public: | |||
| #endif | |||
| //fInfo.optionsAvailable = optionAv; | |||
| if (setupHints & 0x1) | |||
| if (setupHints & FLAG_CONTROL_WINDOW) | |||
| pData->hints |= PLUGIN_HAS_CUSTOM_UI; | |||
| // --------------------------------------------------------------- | |||
| @@ -1328,7 +1587,7 @@ public: | |||
| std::strncpy(shmIdsStr+6*2, &fShmNonRtClientControl.filename[fShmNonRtClientControl.filename.length()-6], 6); | |||
| std::strncpy(shmIdsStr+6*3, &fShmNonRtServerControl.filename[fShmNonRtServerControl.filename.length()-6], 6); | |||
| fBridgeThread.setData(shmIdsStr, label); | |||
| fBridgeThread.setData(shmIdsStr, fInfo.setupLabel); | |||
| } | |||
| if (! restartBridgeThread()) | |||
| @@ -1415,16 +1674,45 @@ private: | |||
| waitForClient("resize-pool", 5000); | |||
| } | |||
| void waitForClient(const char* const action, const uint msecs) | |||
| void setupUniqueProjectID() | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(! fTimedOut,); | |||
| CARLA_SAFE_ASSERT_RETURN(! fTimedError,); | |||
| const char* const engineProjectFilename = pData->engine->getCurrentProjectFilename(); | |||
| carla_stdout("setupUniqueProjectID %s", engineProjectFilename); | |||
| if (fShmRtClientControl.waitForClient(msecs)) | |||
| if (engineProjectFilename == nullptr || engineProjectFilename[0] == '\0') | |||
| return; | |||
| fTimedOut = true; | |||
| carla_stderr2("waitForClient(%s) timed out", action); | |||
| const File file(engineProjectFilename); | |||
| CARLA_SAFE_ASSERT_RETURN(file.existsAsFile(),); | |||
| CARLA_SAFE_ASSERT_RETURN(file.getFileExtension().isNotEmpty(),); | |||
| char code[6]; | |||
| code[5] = '\0'; | |||
| for (;;) | |||
| { | |||
| static const char* const kValidChars = | |||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
| "abcdefghijklmnopqrstuvwxyz" | |||
| "0123456789"; | |||
| static const size_t kValidCharsLen(std::strlen(kValidChars)-1U); | |||
| code[0] = kValidChars[safe_rand(kValidCharsLen)]; | |||
| code[1] = kValidChars[safe_rand(kValidCharsLen)]; | |||
| code[2] = kValidChars[safe_rand(kValidCharsLen)]; | |||
| code[3] = kValidChars[safe_rand(kValidCharsLen)]; | |||
| code[4] = kValidChars[safe_rand(kValidCharsLen)]; | |||
| const File newFile(file.withFileExtension(code)); | |||
| if (newFile.existsAsFile()) | |||
| continue; | |||
| fInfo.setupLabel += code; | |||
| carla_stdout("new label %s", fInfo.setupLabel.buffer()); | |||
| break; | |||
| } | |||
| } | |||
| bool restartBridgeThread() | |||
| @@ -1515,6 +1803,18 @@ private: | |||
| return true; | |||
| } | |||
| void waitForClient(const char* const action, const uint msecs) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(! fTimedOut,); | |||
| CARLA_SAFE_ASSERT_RETURN(! fTimedError,); | |||
| if (fShmRtClientControl.waitForClient(msecs)) | |||
| return; | |||
| fTimedOut = true; | |||
| carla_stderr2("waitForClient(%s) timed out", action); | |||
| } | |||
| CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginJack) | |||
| }; | |||
| @@ -93,7 +93,7 @@ public: | |||
| using water::File; | |||
| const File pluginFile(File::getSpecialLocation(File::currentExecutableFile).withFileExtension("xml")); | |||
| if (! loadProject(pluginFile.getFullPathName().toRawUTF8())) | |||
| if (! loadProject(pluginFile.getFullPathName().toRawUTF8(), true)) | |||
| { | |||
| carla_stderr2("Failed to init plugin, possible reasons: %s", getLastError()); | |||
| return; | |||
| @@ -2,7 +2,7 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # Carla Backend code | |||
| # Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com> | |||
| # Copyright (C) 2011-2019 Filipe Coelho <falktx@falktx.com> | |||
| # | |||
| # This program is free software; you can redistribute it and/or | |||
| # modify it under the terms of the GNU General Public License as | |||
| @@ -1386,6 +1386,11 @@ class CarlaHostMeta(object): | |||
| def save_project(self, filename): | |||
| raise NotImplementedError | |||
| # Clear the currently set project filename. | |||
| @abstractmethod | |||
| def clear_project_filename(self): | |||
| raise NotImplementedError | |||
| # Connect two patchbay ports. | |||
| # @param groupIdA Output group | |||
| # @param portIdA Output port | |||
| @@ -1968,6 +1973,9 @@ class CarlaHostNull(CarlaHostMeta): | |||
| def save_project(self, filename): | |||
| return False | |||
| def clear_project_filename(self): | |||
| return | |||
| def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB): | |||
| return False | |||
| @@ -2256,6 +2264,9 @@ class CarlaHostDLL(CarlaHostMeta): | |||
| self.lib.carla_save_project.argtypes = [c_char_p] | |||
| self.lib.carla_save_project.restype = c_bool | |||
| self.lib.carla_clear_project_filename.argtypes = None | |||
| self.lib.carla_clear_project_filename.restype = None | |||
| self.lib.carla_patchbay_connect.argtypes = [c_uint, c_uint, c_uint, c_uint] | |||
| self.lib.carla_patchbay_connect.restype = c_bool | |||
| @@ -2536,6 +2547,9 @@ class CarlaHostDLL(CarlaHostMeta): | |||
| def save_project(self, filename): | |||
| return bool(self.lib.carla_save_project(filename.encode("utf-8"))) | |||
| def clear_project_filename(self): | |||
| self.lib.carla_clear_project_filename() | |||
| def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB): | |||
| return bool(self.lib.carla_patchbay_connect(groupIdA, portIdA, groupIdB, portIdB)) | |||
| @@ -2879,6 +2893,9 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
| def save_project(self, filename): | |||
| return self.sendMsgAndSetError(["save_project", filename]) | |||
| def clear_project_filename(self): | |||
| return self.sendMsgAndSetError(["clear_project_filename"]) | |||
| def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB): | |||
| return self.sendMsgAndSetError(["patchbay_connect", groupIdA, portIdA, groupIdB, portIdB]) | |||
| @@ -1934,6 +1934,10 @@ class JackApplicationW(QDialog): | |||
| SESSION_MGR_LADISH = 3 | |||
| SESSION_MGR_NSM = 4 | |||
| UI_SESSION_NONE = 0 | |||
| UI_SESSION_LADISH = 1 | |||
| UI_SESSION_NSM = 2 | |||
| FLAG_CONTROL_WINDOW = 0x01 | |||
| FLAG_CAPTURE_FIRST_WINDOW = 0x02 | |||
| FLAG_BUFFERS_ADDITION_MODE = 0x10 | |||
| @@ -1959,6 +1963,7 @@ class JackApplicationW(QDialog): | |||
| # Set-up connections | |||
| self.finished.connect(self.slot_saveSettings) | |||
| self.ui.cb_session_mgr.currentIndexChanged.connect(self.slot_sessionManagerChanged) | |||
| self.ui.le_command.textChanged.connect(self.slot_commandChanged) | |||
| # ------------------------------------------------------------------------------------------------------------------ | |||
| @@ -1970,16 +1975,13 @@ class JackApplicationW(QDialog): | |||
| flags = 0x0 | |||
| if not name: | |||
| name = os.path.basename(command.split(" ",1)[0]) | |||
| name = os.path.basename(command.split(" ",1)[0]).title() | |||
| # TODO finalize flag definitions | |||
| uiSessionMgrIndex = self.ui.cb_session_mgr.currentIndex() | |||
| if uiSessionMgrIndex == 1: | |||
| smgr = self.SESSION_MGR_AUTO | |||
| elif uiSessionMgrIndex == 2: | |||
| if uiSessionMgrIndex == self.UI_SESSION_LADISH: | |||
| smgr = self.SESSION_MGR_LADISH | |||
| #elif uiSessionMgrIndex == 2: | |||
| #smgr = self.SESSION_MGR_NSM | |||
| elif uiSessionMgrIndex == self.UI_SESSION_NSM: | |||
| smgr = self.SESSION_MGR_NSM | |||
| if self.ui.cb_manage_window.isChecked(): | |||
| flags |= self.FLAG_CONTROL_WINDOW | |||
| @@ -1997,6 +1999,15 @@ class JackApplicationW(QDialog): | |||
| chr(baseIntVal+flags)) | |||
| return (command, name, labelSetup) | |||
| def checkIfButtonBoxShouldBeEnabled(self, index, text): | |||
| enabled = len(text) > 0 | |||
| # NSM applications must not be abstract or absolute paths, and must not contain arguments | |||
| if enabled and index == self.UI_SESSION_NSM: | |||
| enabled = text[0] not in (".", "/") and " " not in text | |||
| self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enabled) | |||
| def loadSettings(self): | |||
| settings = QSettings("falkTX", "CarlaAddJackApp") | |||
| @@ -2015,7 +2026,11 @@ class JackApplicationW(QDialog): | |||
| @pyqtSlot(str) | |||
| def slot_commandChanged(self, text): | |||
| self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(len(text) > 0) | |||
| self.checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr.currentIndex(), text) | |||
| @pyqtSlot(int) | |||
| def slot_sessionManagerChanged(self, index): | |||
| self.checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command.text()) | |||
| @pyqtSlot() | |||
| def slot_saveSettings(self): | |||
| @@ -733,6 +733,7 @@ class HostWindow(QMainWindow): | |||
| self.pluginRemoveAll() | |||
| self.fProjectFilename = "" | |||
| self.setProperWindowTitle() | |||
| self.host.clear_project_filename() | |||
| @pyqtSlot() | |||
| def slot_fileOpen(self): | |||
| @@ -133,7 +133,7 @@ public: | |||
| CARLA_SAFE_ASSERT_INT2_RETURN(shmIds != nullptr && std::strlen(shmIds) == 6*4, std::strlen(shmIds), 6*4,); | |||
| const char* const libjackSetup(std::getenv("CARLA_LIBJACK_SETUP")); | |||
| CARLA_SAFE_ASSERT_RETURN(libjackSetup != nullptr && std::strlen(libjackSetup) == 6,); | |||
| CARLA_SAFE_ASSERT_RETURN(libjackSetup != nullptr && std::strlen(libjackSetup) >= 6,); | |||
| // make sure we don't get loaded again | |||
| carla_unsetenv("CARLA_SHM_IDS"); | |||
| @@ -943,7 +943,7 @@ bool CarlaJackAppClient::handleNonRtData() | |||
| case kPluginBridgeNonRtClientPrepareForSave: | |||
| { | |||
| if (fSessionManager == 1) // auto | |||
| if (fSessionManager == 1 && std::getenv("NSM_URL") == nullptr) // auto | |||
| { | |||
| struct sigaction sig; | |||
| carla_zeroStruct(sig); | |||
| @@ -37,6 +37,7 @@ | |||
| #endif | |||
| #include <cerrno> | |||
| #include <map> | |||
| #ifdef __SSE2_MATH__ | |||
| # include <xmmintrin.h> | |||
| @@ -164,6 +165,8 @@ struct JackClientState { | |||
| LinkedList<JackPortState*> midiIns; | |||
| LinkedList<JackPortState*> midiOuts; | |||
| std::map<std::string, JackPortState*> portNameMapping; | |||
| JackShutdownCallback shutdownCb; | |||
| void* shutdownCbPtr; | |||
| @@ -195,6 +198,7 @@ struct JackClientState { | |||
| audioOuts(), | |||
| midiIns(), | |||
| midiOuts(), | |||
| portNameMapping(), | |||
| shutdownCb(nullptr), | |||
| shutdownCbPtr(nullptr), | |||
| infoShutdownCb(nullptr), | |||
| @@ -104,26 +104,26 @@ const char** jack_get_ports(jack_client_t* client, const char* a, const char* b, | |||
| CARLA_EXPORT | |||
| jack_port_t* jack_port_by_name(jack_client_t* client, const char* name) | |||
| { | |||
| carla_stdout("%s(%p, %s) WIP", __FUNCTION__, client, name); | |||
| carla_debug("%s(%p, %s)", __FUNCTION__, client, name); | |||
| JackClientState* const jclient = (JackClientState*)client; | |||
| CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); | |||
| const JackServerState& jserver(jclient->server); | |||
| const int commonFlags = JackPortIsPhysical|JackPortIsTerminal; | |||
| static JackPortState retPort( | |||
| /* name */ nullptr, | |||
| /* fullname */ nullptr, | |||
| /* index */ 0, | |||
| /* flags */ 0x0, | |||
| /* isMidi */ false, | |||
| /* isSystem */ true, | |||
| /* isConnected */ false | |||
| ); | |||
| if (std::strncmp(name, "system:", 7) == 0) | |||
| { | |||
| static JackPortState retPort( | |||
| /* name */ nullptr, | |||
| /* fullname */ nullptr, | |||
| /* index */ 0, | |||
| /* flags */ 0x0, | |||
| /* isMidi */ false, | |||
| /* isSystem */ true, | |||
| /* isConnected */ false | |||
| ); | |||
| const JackServerState& jserver(jclient->server); | |||
| const int commonFlags = JackPortIsPhysical|JackPortIsTerminal; | |||
| std::free(retPort.fullname); | |||
| retPort.fullname = strdup(name); | |||
| @@ -188,6 +188,11 @@ jack_port_t* jack_port_by_name(jack_client_t* client, const char* name) | |||
| return (jack_port_t*)&retPort; | |||
| } | |||
| else | |||
| { | |||
| if (JackPortState* const port = jclient->portNameMapping[name]) | |||
| return (jack_port_t*)port; | |||
| } | |||
| carla_stderr2("jack_port_by_name: invalid port name '%s'", name); | |||
| return nullptr; | |||
| @@ -48,6 +48,7 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co | |||
| jclient->audioIns.append(port); | |||
| } | |||
| jclient->portNameMapping[port->fullname] = port; | |||
| return (jack_port_t*)port; | |||
| } | |||
| @@ -62,6 +63,7 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co | |||
| jclient->audioOuts.append(port); | |||
| } | |||
| jclient->portNameMapping[port->fullname] = port; | |||
| return (jack_port_t*)port; | |||
| } | |||
| @@ -82,6 +84,7 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co | |||
| jclient->midiIns.append(port); | |||
| } | |||
| jclient->portNameMapping[port->fullname] = port; | |||
| return (jack_port_t*)port; | |||
| } | |||
| @@ -96,6 +99,7 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co | |||
| jclient->midiOuts.append(port); | |||
| } | |||
| jclient->portNameMapping[port->fullname] = port; | |||
| return (jack_port_t*)port; | |||
| } | |||
| @@ -130,12 +134,14 @@ int jack_port_unregister(jack_client_t* client, jack_port_t* port) | |||
| if (jport->flags & JackPortIsInput) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(jclient->midiIns.removeOne(jport), ENOENT); | |||
| jclient->portNameMapping.erase(jport->fullname); | |||
| return 0; | |||
| } | |||
| if (jport->flags & JackPortIsOutput) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(jclient->midiOuts.removeOne(jport), ENOENT); | |||
| jclient->portNameMapping.erase(jport->fullname); | |||
| return 0; | |||
| } | |||
| } | |||
| @@ -144,12 +150,14 @@ int jack_port_unregister(jack_client_t* client, jack_port_t* port) | |||
| if (jport->flags & JackPortIsInput) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(jclient->audioIns.removeOne(jport), ENOENT); | |||
| jclient->portNameMapping.erase(jport->fullname); | |||
| return 0; | |||
| } | |||
| if (jport->flags & JackPortIsOutput) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(jclient->audioOuts.removeOne(jport), ENOENT); | |||
| jclient->portNameMapping.erase(jport->fullname); | |||
| return 0; | |||
| } | |||
| } | |||
| @@ -325,8 +333,37 @@ int jack_port_set_name(jack_port_t *port, const char *port_name) | |||
| CARLA_EXPORT | |||
| int jack_port_rename(jack_client_t* client, jack_port_t *port, const char *port_name) | |||
| { | |||
| carla_stderr2("%s(%p, %p, %s)", __FUNCTION__, client, port, port_name); | |||
| return ENOSYS; | |||
| carla_stderr2("%s(%p, %p, %s) WIP", __FUNCTION__, client, port, port_name); | |||
| JackClientState* const jclient = (JackClientState*)client; | |||
| CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, EINVAL); | |||
| CARLA_SAFE_ASSERT_RETURN(port_name != nullptr && port_name[0] != '\0', EINVAL); | |||
| JackPortState* const jport = (JackPortState*)port; | |||
| CARLA_SAFE_ASSERT_RETURN(jport != nullptr, EINVAL); | |||
| CARLA_SAFE_ASSERT_RETURN(! jport->isSystem, EINVAL); | |||
| // TODO: verify uniqueness | |||
| char* const name = strdup(port_name); | |||
| CARLA_SAFE_ASSERT_RETURN(name != nullptr, ENOMEM); | |||
| char* const fullname = (char*)std::malloc(STR_MAX); | |||
| CARLA_SAFE_ASSERT_RETURN(name != nullptr, ENOMEM); | |||
| std::free(jport->name); | |||
| jport->name = strdup(port_name); | |||
| std::snprintf(fullname, STR_MAX, "%s:%s", jclient->name, port_name); | |||
| fullname[STR_MAX-1] = '\0'; | |||
| std::free(jport->fullname); | |||
| jport->fullname = fullname; | |||
| // TODO: port rename callback | |||
| return 0; | |||
| } | |||
| CARLA_EXPORT | |||
| @@ -125,6 +125,11 @@ public: | |||
| return TerminateProcess (processInfo.hProcess, 0) != FALSE; | |||
| } | |||
| bool terminateProcess() const noexcept | |||
| { | |||
| return TerminateProcess (processInfo.hProcess, 0) != FALSE; | |||
| } | |||
| uint32 getExitCode() const noexcept | |||
| { | |||
| DWORD exitCode = 0; | |||
| @@ -238,6 +243,11 @@ public: | |||
| return ::kill (childPID, SIGKILL) == 0; | |||
| } | |||
| bool terminateProcess() const noexcept | |||
| { | |||
| return ::kill (childPID, SIGTERM) == 0; | |||
| } | |||
| uint32 getExitCode() const noexcept | |||
| { | |||
| if (childPID != 0) | |||
| @@ -287,6 +297,11 @@ bool ChildProcess::kill() | |||
| return activeProcess == nullptr || activeProcess->killProcess(); | |||
| } | |||
| bool ChildProcess::terminate() | |||
| { | |||
| return activeProcess == nullptr || activeProcess->terminateProcess(); | |||
| } | |||
| uint32 ChildProcess::getExitCode() const | |||
| { | |||
| return activeProcess != nullptr ? activeProcess->getExitCode() : 0; | |||
| @@ -107,6 +107,7 @@ public: | |||
| result in undefined behaviour. | |||
| */ | |||
| bool kill(); | |||
| bool terminate(); | |||
| uint32 getPID() const noexcept; | |||