Signed-off-by: falkTX <falktx@gmail.com>tags/v2.1-alpha2
| @@ -6,8 +6,8 @@ | |||||
| <rect> | <rect> | ||||
| <x>0</x> | <x>0</x> | ||||
| <y>0</y> | <y>0</y> | ||||
| <width>471</width> | |||||
| <height>369</height> | |||||
| <width>496</width> | |||||
| <height>438</height> | |||||
| </rect> | </rect> | ||||
| </property> | </property> | ||||
| <property name="windowTitle"> | <property name="windowTitle"> | ||||
| @@ -27,39 +27,23 @@ | |||||
| <string>Application</string> | <string>Application</string> | ||||
| </property> | </property> | ||||
| <layout class="QGridLayout" name="gridLayout"> | <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"> | <property name="orientation"> | ||||
| <enum>Qt::Horizontal</enum> | <enum>Qt::Horizontal</enum> | ||||
| </property> | </property> | ||||
| <property name="sizeType"> | <property name="sizeType"> | ||||
| <enum>QSizePolicy::Ignored</enum> | |||||
| <enum>QSizePolicy::Fixed</enum> | |||||
| </property> | </property> | ||||
| <property name="sizeHint" stdset="0"> | <property name="sizeHint" stdset="0"> | ||||
| <size> | <size> | ||||
| <width>87</width> | |||||
| <height>1</height> | |||||
| <width>20</width> | |||||
| <height>60</height> | |||||
| </size> | </size> | ||||
| </property> | </property> | ||||
| </spacer> | </spacer> | ||||
| </item> | </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"> | <widget class="QLabel" name="label_10"> | ||||
| <property name="text"> | <property name="text"> | ||||
| <string>Name:</string> | <string>Name:</string> | ||||
| @@ -69,22 +53,124 @@ | |||||
| </property> | </property> | ||||
| </widget> | </widget> | ||||
| </item> | </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"> | <property name="orientation"> | ||||
| <enum>Qt::Horizontal</enum> | <enum>Qt::Horizontal</enum> | ||||
| </property> | </property> | ||||
| <property name="sizeType"> | <property name="sizeType"> | ||||
| <enum>QSizePolicy::Ignored</enum> | |||||
| <enum>QSizePolicy::Fixed</enum> | |||||
| </property> | </property> | ||||
| <property name="sizeHint" stdset="0"> | <property name="sizeHint" stdset="0"> | ||||
| <size> | <size> | ||||
| <width>1</width> | |||||
| <height>1</height> | |||||
| <width>20</width> | |||||
| <height>60</height> | |||||
| </size> | </size> | ||||
| </property> | </property> | ||||
| </spacer> | </spacer> | ||||
| </item> | </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> | </layout> | ||||
| </widget> | </widget> | ||||
| </item> | </item> | ||||
| @@ -122,7 +208,7 @@ | |||||
| <item> | <item> | ||||
| <widget class="QComboBox" name="cb_session_mgr"> | <widget class="QComboBox" name="cb_session_mgr"> | ||||
| <property name="currentIndex"> | <property name="currentIndex"> | ||||
| <number>1</number> | |||||
| <number>0</number> | |||||
| </property> | </property> | ||||
| <item> | <item> | ||||
| <property name="text"> | <property name="text"> | ||||
| @@ -131,12 +217,12 @@ | |||||
| </item> | </item> | ||||
| <item> | <item> | ||||
| <property name="text"> | <property name="text"> | ||||
| <string>Auto</string> | |||||
| <string>LADISH (SIGUSR1)</string> | |||||
| </property> | </property> | ||||
| </item> | </item> | ||||
| <item> | <item> | ||||
| <property name="text"> | <property name="text"> | ||||
| <string>LADISH (SIGUSR1)</string> | |||||
| <string>NSM</string> | |||||
| </property> | </property> | ||||
| </item> | </item> | ||||
| </widget> | </widget> | ||||
| @@ -895,12 +895,24 @@ public: | |||||
| * Load a project file. | * Load a project file. | ||||
| * @note Already loaded plugins are not removed; call removeAllPlugins() first if needed. | * @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. | * 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) | // Information (base) | ||||
| @@ -410,6 +410,11 @@ CARLA_EXPORT bool carla_load_project(const char* filename); | |||||
| CARLA_EXPORT bool carla_save_project(const char* filename); | CARLA_EXPORT bool carla_save_project(const char* filename); | ||||
| #ifndef BUILD_BRIDGE | #ifndef BUILD_BRIDGE | ||||
| /*! | |||||
| * Clear the currently set project filename. | |||||
| */ | |||||
| CARLA_EXPORT void carla_clear_project_filename(); | |||||
| /*! | /*! | ||||
| * Connect two patchbay ports. | * Connect two patchbay ports. | ||||
| * @param groupIdA Output group | * @param groupIdA Output group | ||||
| @@ -812,7 +812,7 @@ bool carla_load_project(const char* filename) | |||||
| carla_debug("carla_load_project(\"%s\")", 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) | 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); | carla_debug("carla_save_project(\"%s\")", filename); | ||||
| return gStandalone.engine->saveProject(filename); | |||||
| return gStandalone.engine->saveProject(filename, true); | |||||
| } | } | ||||
| #ifndef BUILD_BRIDGE | #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) | 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!! | // NOTE: please keep in sync with carla_get_supported_file_extensions!! | ||||
| if (extension == "carxp" || extension == "carxs") | 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; | 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(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"); | 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); | File file(jfilename); | ||||
| CARLA_SAFE_ASSERT_RETURN_ERR(file.existsAsFile(), "Requested file does not exist or is not a readable file"); | 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); | XmlDocument xml(file); | ||||
| return loadProjectInternal(xml); | 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_SAFE_ASSERT_RETURN_ERR(filename != nullptr && filename[0] != '\0', "Invalid filename"); | ||||
| carla_debug("CarlaEngine::saveProject(\"%s\")", 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)); | const String jfilename = String(CharPointer_UTF8(filename)); | ||||
| File file(jfilename); | File file(jfilename); | ||||
| if (setAsCurrentProject) | |||||
| { | |||||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||||
| pData->currentProjectFilename = filename; | |||||
| #endif | |||||
| } | |||||
| if (file.replaceWithData(out.getData(), out.getDataSize())) | if (file.replaceWithData(out.getData(), out.getDataSize())) | ||||
| return true; | return true; | ||||
| @@ -1158,6 +1172,18 @@ bool CarlaEngine::saveProject(const char* const filename) | |||||
| return false; | 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) | // Information (base) | ||||
| @@ -375,6 +375,7 @@ CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept | |||||
| actionCanceled(false), | actionCanceled(false), | ||||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | ||||
| loadingProject(false), | loadingProject(false), | ||||
| currentProjectFilename(), | |||||
| #endif | #endif | ||||
| hints(0x0), | hints(0x0), | ||||
| bufferSize(0), | bufferSize(0), | ||||
| @@ -223,6 +223,7 @@ struct CarlaEngine::ProtectedData { | |||||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | ||||
| bool loadingProject; | bool loadingProject; | ||||
| CarlaString currentProjectFilename; | |||||
| #endif | #endif | ||||
| uint hints; | uint hints; | ||||
| @@ -175,7 +175,7 @@ protected: | |||||
| CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(filename), true); | CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(filename), true); | ||||
| try { | try { | ||||
| ok = fEngine->loadProject(filename); | |||||
| ok = fEngine->loadProject(filename, true); | |||||
| } CARLA_SAFE_EXCEPTION("loadProject"); | } CARLA_SAFE_EXCEPTION("loadProject"); | ||||
| delete[] filename; | delete[] filename; | ||||
| @@ -187,11 +187,15 @@ protected: | |||||
| CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(filename), true); | CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(filename), true); | ||||
| try { | try { | ||||
| ok = fEngine->saveProject(filename); | |||||
| ok = fEngine->saveProject(filename, true); | |||||
| } CARLA_SAFE_EXCEPTION("saveProject"); | } CARLA_SAFE_EXCEPTION("saveProject"); | ||||
| delete[] filename; | delete[] filename; | ||||
| } | } | ||||
| else if (std::strcmp(msg, "clear_project_filename") == 0) | |||||
| { | |||||
| fEngine->clearCurrentProjectFilename(); | |||||
| } | |||||
| else if (std::strcmp(msg, "patchbay_connect") == 0) | else if (std::strcmp(msg, "patchbay_connect") == 0) | ||||
| { | { | ||||
| uint32_t groupA, portA, groupB, portB; | uint32_t groupA, portA, groupB, portB; | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * Carla Plugin JACK | * 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 | * This program is free software; you can redistribute it and/or | ||||
| * modify it under the terms of the GNU General Public License as | * modify it under the terms of the GNU General Public License as | ||||
| @@ -28,6 +28,11 @@ | |||||
| #include "CarlaShmUtils.hpp" | #include "CarlaShmUtils.hpp" | ||||
| #include "CarlaThread.hpp" | #include "CarlaThread.hpp" | ||||
| #ifdef HAVE_LIBLO | |||||
| # include "CarlaOscUtils.hpp" | |||||
| #endif | |||||
| #include "water/files/File.h" | |||||
| #include "water/misc/Time.h" | #include "water/misc/Time.h" | ||||
| #include "water/text/StringArray.h" | #include "water/text/StringArray.h" | ||||
| #include "water/threads/ChildProcess.h" | #include "water/threads/ChildProcess.h" | ||||
| @@ -47,6 +52,28 @@ using water::Time; | |||||
| CARLA_BACKEND_START_NAMESPACE | 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 | // Fallback data | ||||
| @@ -62,17 +89,22 @@ public: | |||||
| kEngine(engine), | kEngine(engine), | ||||
| kPlugin(plugin), | kPlugin(plugin), | ||||
| fShmIds(), | fShmIds(), | ||||
| fNumPorts(), | |||||
| fSetupLabel(), | |||||
| #ifdef HAVE_LIBLO | |||||
| fOscClientAddress(nullptr), | |||||
| fOscServer(nullptr), | |||||
| fProject(), | |||||
| #endif | |||||
| fProcess() {} | 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(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()); | CARLA_SAFE_ASSERT(! isThreadRunning()); | ||||
| fShmIds = shmIds; | |||||
| fNumPorts = numPorts; | |||||
| fShmIds = shmIds; | |||||
| fSetupLabel = setupLabel; | |||||
| } | } | ||||
| uintptr_t getProcessID() const noexcept | uintptr_t getProcessID() const noexcept | ||||
| @@ -82,9 +114,156 @@ public: | |||||
| return (uintptr_t)fProcess->getPID(); | 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: | 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() | 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) | if (fProcess == nullptr) | ||||
| { | { | ||||
| fProcess = new ChildProcess(); | fProcess = new ChildProcess(); | ||||
| @@ -127,6 +306,7 @@ protected: | |||||
| const ScopedEngineEnvironmentLocker _seel(kEngine); | const ScopedEngineEnvironmentLocker _seel(kEngine); | ||||
| const ScopedEnvVar sev3("NSM_URL", lo_server_get_url(fOscServer)); | |||||
| const ScopedEnvVar sev2("LD_LIBRARY_PATH", libjackdir.buffer()); | const ScopedEnvVar sev2("LD_LIBRARY_PATH", libjackdir.buffer()); | ||||
| const ScopedEnvVar sev1("LD_PRELOAD", ldpreload.isNotEmpty() ? ldpreload.buffer() : nullptr); | const ScopedEnvVar sev1("LD_PRELOAD", ldpreload.isNotEmpty() ? ldpreload.buffer() : nullptr); | ||||
| @@ -135,7 +315,7 @@ protected: | |||||
| else | else | ||||
| carla_unsetenv("CARLA_FRONTEND_WIN_ID"); | 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()); | carla_setenv("CARLA_SHM_IDS", fShmIds.buffer()); | ||||
| started = fProcess->start(arguments); | started = fProcess->start(arguments); | ||||
| @@ -149,7 +329,32 @@ protected: | |||||
| } | } | ||||
| for (; fProcess->isRunning() && ! shouldThreadExit();) | 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 | // we only get here if bridge crashed or thread asked to exit | ||||
| if (fProcess->isRunning() && shouldThreadExit()) | if (fProcess->isRunning() && shouldThreadExit()) | ||||
| @@ -184,7 +389,42 @@ private: | |||||
| CarlaPlugin* const kPlugin; | CarlaPlugin* const kPlugin; | ||||
| CarlaString fShmIds; | 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; | ScopedPointer<ChildProcess> fProcess; | ||||
| @@ -247,6 +487,8 @@ public: | |||||
| fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientQuit); | fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientQuit); | ||||
| fShmNonRtClientControl.commitWrite(); | fShmNonRtClientControl.commitWrite(); | ||||
| fBridgeThread.sendTerminate(); | |||||
| if (! fTimedOut) | if (! fTimedOut) | ||||
| waitForClient("stopping", 3000); | waitForClient("stopping", 3000); | ||||
| } | } | ||||
| @@ -326,6 +568,13 @@ public: | |||||
| void prepareForSave() noexcept override | void prepareForSave() noexcept override | ||||
| { | { | ||||
| #ifdef HAVE_LIBLO | |||||
| if (fInfo.setupLabel.length() == 6) | |||||
| setupUniqueProjectID(); | |||||
| fBridgeThread.nsmSave(fInfo.setupLabel); | |||||
| #endif | |||||
| { | { | ||||
| const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | ||||
| @@ -402,6 +651,10 @@ public: | |||||
| CARLA_SAFE_ASSERT_RETURN(restartBridgeThread(),); | CARLA_SAFE_ASSERT_RETURN(restartBridgeThread(),); | ||||
| } | } | ||||
| #ifdef HAVE_LIBLO | |||||
| fBridgeThread.nsmShowGui(yesNo); | |||||
| #endif | |||||
| const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | ||||
| fShmNonRtClientControl.writeOpcode(yesNo ? kPluginBridgeNonRtClientShowUI : kPluginBridgeNonRtClientHideUI); | fShmNonRtClientControl.writeOpcode(yesNo ? kPluginBridgeNonRtClientShowUI : kPluginBridgeNonRtClientHideUI); | ||||
| @@ -1236,7 +1489,7 @@ public: | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // check setup | // check setup | ||||
| if (std::strlen(label) != 6) | |||||
| if (std::strlen(label) < 6) | |||||
| { | { | ||||
| pData->engine->setLastError("invalid application setup received"); | pData->engine->setLastError("invalid application setup received"); | ||||
| return false; | return false; | ||||
| @@ -1256,7 +1509,11 @@ public: | |||||
| fInfo.setupLabel = label; | fInfo.setupLabel = label; | ||||
| const int setupHints = label[5] - '0'; | |||||
| // --------------------------------------------------------------- | |||||
| // set project unique id | |||||
| if (label[6] == '\0') | |||||
| setupUniqueProjectID(); | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // set info | // set info | ||||
| @@ -1306,6 +1563,8 @@ public: | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // setup hints and options | // setup hints and options | ||||
| const int setupHints = label[5] - '0'; | |||||
| // FIXME dryWet broken | // FIXME dryWet broken | ||||
| pData->hints = PLUGIN_IS_BRIDGE | PLUGIN_OPTION_FIXED_BUFFERS; | pData->hints = PLUGIN_IS_BRIDGE | PLUGIN_OPTION_FIXED_BUFFERS; | ||||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | ||||
| @@ -1313,7 +1572,7 @@ public: | |||||
| #endif | #endif | ||||
| //fInfo.optionsAvailable = optionAv; | //fInfo.optionsAvailable = optionAv; | ||||
| if (setupHints & 0x1) | |||||
| if (setupHints & FLAG_CONTROL_WINDOW) | |||||
| pData->hints |= PLUGIN_HAS_CUSTOM_UI; | 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*2, &fShmNonRtClientControl.filename[fShmNonRtClientControl.filename.length()-6], 6); | ||||
| std::strncpy(shmIdsStr+6*3, &fShmNonRtServerControl.filename[fShmNonRtServerControl.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()) | if (! restartBridgeThread()) | ||||
| @@ -1415,16 +1674,45 @@ private: | |||||
| waitForClient("resize-pool", 5000); | 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; | 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() | bool restartBridgeThread() | ||||
| @@ -1515,6 +1803,18 @@ private: | |||||
| return true; | 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) | CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginJack) | ||||
| }; | }; | ||||
| @@ -93,7 +93,7 @@ public: | |||||
| using water::File; | using water::File; | ||||
| const File pluginFile(File::getSpecialLocation(File::currentExecutableFile).withFileExtension("xml")); | 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()); | carla_stderr2("Failed to init plugin, possible reasons: %s", getLastError()); | ||||
| return; | return; | ||||
| @@ -2,7 +2,7 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # Carla Backend code | # 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 | # This program is free software; you can redistribute it and/or | ||||
| # modify it under the terms of the GNU General Public License as | # modify it under the terms of the GNU General Public License as | ||||
| @@ -1386,6 +1386,11 @@ class CarlaHostMeta(object): | |||||
| def save_project(self, filename): | def save_project(self, filename): | ||||
| raise NotImplementedError | raise NotImplementedError | ||||
| # Clear the currently set project filename. | |||||
| @abstractmethod | |||||
| def clear_project_filename(self): | |||||
| raise NotImplementedError | |||||
| # Connect two patchbay ports. | # Connect two patchbay ports. | ||||
| # @param groupIdA Output group | # @param groupIdA Output group | ||||
| # @param portIdA Output port | # @param portIdA Output port | ||||
| @@ -1968,6 +1973,9 @@ class CarlaHostNull(CarlaHostMeta): | |||||
| def save_project(self, filename): | def save_project(self, filename): | ||||
| return False | return False | ||||
| def clear_project_filename(self): | |||||
| return | |||||
| def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB): | def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB): | ||||
| return False | return False | ||||
| @@ -2256,6 +2264,9 @@ class CarlaHostDLL(CarlaHostMeta): | |||||
| self.lib.carla_save_project.argtypes = [c_char_p] | self.lib.carla_save_project.argtypes = [c_char_p] | ||||
| self.lib.carla_save_project.restype = c_bool | 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.argtypes = [c_uint, c_uint, c_uint, c_uint] | ||||
| self.lib.carla_patchbay_connect.restype = c_bool | self.lib.carla_patchbay_connect.restype = c_bool | ||||
| @@ -2536,6 +2547,9 @@ class CarlaHostDLL(CarlaHostMeta): | |||||
| def save_project(self, filename): | def save_project(self, filename): | ||||
| return bool(self.lib.carla_save_project(filename.encode("utf-8"))) | 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): | def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB): | ||||
| return bool(self.lib.carla_patchbay_connect(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): | def save_project(self, filename): | ||||
| return self.sendMsgAndSetError(["save_project", 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): | def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB): | ||||
| return self.sendMsgAndSetError(["patchbay_connect", 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_LADISH = 3 | ||||
| SESSION_MGR_NSM = 4 | SESSION_MGR_NSM = 4 | ||||
| UI_SESSION_NONE = 0 | |||||
| UI_SESSION_LADISH = 1 | |||||
| UI_SESSION_NSM = 2 | |||||
| FLAG_CONTROL_WINDOW = 0x01 | FLAG_CONTROL_WINDOW = 0x01 | ||||
| FLAG_CAPTURE_FIRST_WINDOW = 0x02 | FLAG_CAPTURE_FIRST_WINDOW = 0x02 | ||||
| FLAG_BUFFERS_ADDITION_MODE = 0x10 | FLAG_BUFFERS_ADDITION_MODE = 0x10 | ||||
| @@ -1959,6 +1963,7 @@ class JackApplicationW(QDialog): | |||||
| # Set-up connections | # Set-up connections | ||||
| self.finished.connect(self.slot_saveSettings) | 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) | self.ui.le_command.textChanged.connect(self.slot_commandChanged) | ||||
| # ------------------------------------------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------------------------------------------ | ||||
| @@ -1970,16 +1975,13 @@ class JackApplicationW(QDialog): | |||||
| flags = 0x0 | flags = 0x0 | ||||
| if not name: | 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() | 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 | 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(): | if self.ui.cb_manage_window.isChecked(): | ||||
| flags |= self.FLAG_CONTROL_WINDOW | flags |= self.FLAG_CONTROL_WINDOW | ||||
| @@ -1997,6 +1999,15 @@ class JackApplicationW(QDialog): | |||||
| chr(baseIntVal+flags)) | chr(baseIntVal+flags)) | ||||
| return (command, name, labelSetup) | 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): | def loadSettings(self): | ||||
| settings = QSettings("falkTX", "CarlaAddJackApp") | settings = QSettings("falkTX", "CarlaAddJackApp") | ||||
| @@ -2015,7 +2026,11 @@ class JackApplicationW(QDialog): | |||||
| @pyqtSlot(str) | @pyqtSlot(str) | ||||
| def slot_commandChanged(self, text): | 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() | @pyqtSlot() | ||||
| def slot_saveSettings(self): | def slot_saveSettings(self): | ||||
| @@ -733,6 +733,7 @@ class HostWindow(QMainWindow): | |||||
| self.pluginRemoveAll() | self.pluginRemoveAll() | ||||
| self.fProjectFilename = "" | self.fProjectFilename = "" | ||||
| self.setProperWindowTitle() | self.setProperWindowTitle() | ||||
| self.host.clear_project_filename() | |||||
| @pyqtSlot() | @pyqtSlot() | ||||
| def slot_fileOpen(self): | 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,); | 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")); | 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 | // make sure we don't get loaded again | ||||
| carla_unsetenv("CARLA_SHM_IDS"); | carla_unsetenv("CARLA_SHM_IDS"); | ||||
| @@ -943,7 +943,7 @@ bool CarlaJackAppClient::handleNonRtData() | |||||
| case kPluginBridgeNonRtClientPrepareForSave: | case kPluginBridgeNonRtClientPrepareForSave: | ||||
| { | { | ||||
| if (fSessionManager == 1) // auto | |||||
| if (fSessionManager == 1 && std::getenv("NSM_URL") == nullptr) // auto | |||||
| { | { | ||||
| struct sigaction sig; | struct sigaction sig; | ||||
| carla_zeroStruct(sig); | carla_zeroStruct(sig); | ||||
| @@ -37,6 +37,7 @@ | |||||
| #endif | #endif | ||||
| #include <cerrno> | #include <cerrno> | ||||
| #include <map> | |||||
| #ifdef __SSE2_MATH__ | #ifdef __SSE2_MATH__ | ||||
| # include <xmmintrin.h> | # include <xmmintrin.h> | ||||
| @@ -164,6 +165,8 @@ struct JackClientState { | |||||
| LinkedList<JackPortState*> midiIns; | LinkedList<JackPortState*> midiIns; | ||||
| LinkedList<JackPortState*> midiOuts; | LinkedList<JackPortState*> midiOuts; | ||||
| std::map<std::string, JackPortState*> portNameMapping; | |||||
| JackShutdownCallback shutdownCb; | JackShutdownCallback shutdownCb; | ||||
| void* shutdownCbPtr; | void* shutdownCbPtr; | ||||
| @@ -195,6 +198,7 @@ struct JackClientState { | |||||
| audioOuts(), | audioOuts(), | ||||
| midiIns(), | midiIns(), | ||||
| midiOuts(), | midiOuts(), | ||||
| portNameMapping(), | |||||
| shutdownCb(nullptr), | shutdownCb(nullptr), | ||||
| shutdownCbPtr(nullptr), | shutdownCbPtr(nullptr), | ||||
| infoShutdownCb(nullptr), | infoShutdownCb(nullptr), | ||||
| @@ -104,26 +104,26 @@ const char** jack_get_ports(jack_client_t* client, const char* a, const char* b, | |||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| jack_port_t* jack_port_by_name(jack_client_t* client, const char* name) | 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; | JackClientState* const jclient = (JackClientState*)client; | ||||
| CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0); | 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) | 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); | std::free(retPort.fullname); | ||||
| retPort.fullname = strdup(name); | 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; | 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); | carla_stderr2("jack_port_by_name: invalid port name '%s'", name); | ||||
| return nullptr; | 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->audioIns.append(port); | ||||
| } | } | ||||
| jclient->portNameMapping[port->fullname] = port; | |||||
| return (jack_port_t*)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->audioOuts.append(port); | ||||
| } | } | ||||
| jclient->portNameMapping[port->fullname] = port; | |||||
| return (jack_port_t*)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->midiIns.append(port); | ||||
| } | } | ||||
| jclient->portNameMapping[port->fullname] = port; | |||||
| return (jack_port_t*)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->midiOuts.append(port); | ||||
| } | } | ||||
| jclient->portNameMapping[port->fullname] = port; | |||||
| return (jack_port_t*)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) | if (jport->flags & JackPortIsInput) | ||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(jclient->midiIns.removeOne(jport), ENOENT); | CARLA_SAFE_ASSERT_RETURN(jclient->midiIns.removeOne(jport), ENOENT); | ||||
| jclient->portNameMapping.erase(jport->fullname); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| if (jport->flags & JackPortIsOutput) | if (jport->flags & JackPortIsOutput) | ||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(jclient->midiOuts.removeOne(jport), ENOENT); | CARLA_SAFE_ASSERT_RETURN(jclient->midiOuts.removeOne(jport), ENOENT); | ||||
| jclient->portNameMapping.erase(jport->fullname); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| } | } | ||||
| @@ -144,12 +150,14 @@ int jack_port_unregister(jack_client_t* client, jack_port_t* port) | |||||
| if (jport->flags & JackPortIsInput) | if (jport->flags & JackPortIsInput) | ||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(jclient->audioIns.removeOne(jport), ENOENT); | CARLA_SAFE_ASSERT_RETURN(jclient->audioIns.removeOne(jport), ENOENT); | ||||
| jclient->portNameMapping.erase(jport->fullname); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| if (jport->flags & JackPortIsOutput) | if (jport->flags & JackPortIsOutput) | ||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(jclient->audioOuts.removeOne(jport), ENOENT); | CARLA_SAFE_ASSERT_RETURN(jclient->audioOuts.removeOne(jport), ENOENT); | ||||
| jclient->portNameMapping.erase(jport->fullname); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| } | } | ||||
| @@ -325,8 +333,37 @@ int jack_port_set_name(jack_port_t *port, const char *port_name) | |||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| int jack_port_rename(jack_client_t* client, jack_port_t *port, const char *port_name) | 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 | CARLA_EXPORT | ||||
| @@ -125,6 +125,11 @@ public: | |||||
| return TerminateProcess (processInfo.hProcess, 0) != FALSE; | return TerminateProcess (processInfo.hProcess, 0) != FALSE; | ||||
| } | } | ||||
| bool terminateProcess() const noexcept | |||||
| { | |||||
| return TerminateProcess (processInfo.hProcess, 0) != FALSE; | |||||
| } | |||||
| uint32 getExitCode() const noexcept | uint32 getExitCode() const noexcept | ||||
| { | { | ||||
| DWORD exitCode = 0; | DWORD exitCode = 0; | ||||
| @@ -238,6 +243,11 @@ public: | |||||
| return ::kill (childPID, SIGKILL) == 0; | return ::kill (childPID, SIGKILL) == 0; | ||||
| } | } | ||||
| bool terminateProcess() const noexcept | |||||
| { | |||||
| return ::kill (childPID, SIGTERM) == 0; | |||||
| } | |||||
| uint32 getExitCode() const noexcept | uint32 getExitCode() const noexcept | ||||
| { | { | ||||
| if (childPID != 0) | if (childPID != 0) | ||||
| @@ -287,6 +297,11 @@ bool ChildProcess::kill() | |||||
| return activeProcess == nullptr || activeProcess->killProcess(); | return activeProcess == nullptr || activeProcess->killProcess(); | ||||
| } | } | ||||
| bool ChildProcess::terminate() | |||||
| { | |||||
| return activeProcess == nullptr || activeProcess->terminateProcess(); | |||||
| } | |||||
| uint32 ChildProcess::getExitCode() const | uint32 ChildProcess::getExitCode() const | ||||
| { | { | ||||
| return activeProcess != nullptr ? activeProcess->getExitCode() : 0; | return activeProcess != nullptr ? activeProcess->getExitCode() : 0; | ||||
| @@ -107,6 +107,7 @@ public: | |||||
| result in undefined behaviour. | result in undefined behaviour. | ||||
| */ | */ | ||||
| bool kill(); | bool kill(); | ||||
| bool terminate(); | |||||
| uint32 getPID() const noexcept; | uint32 getPID() const noexcept; | ||||