Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

jackappdialog.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * Carla plugin host
  3. * Copyright (C) 2011-2023 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "jackappdialog.hpp"
  18. #ifdef __clang__
  19. # pragma clang diagnostic push
  20. # pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy"
  21. # pragma clang diagnostic ignored "-Wdeprecated-register"
  22. #elif defined(__GNUC__) && __GNUC__ >= 8
  23. # pragma GCC diagnostic push
  24. # pragma GCC diagnostic ignored "-Wclass-memaccess"
  25. # pragma GCC diagnostic ignored "-Wdeprecated-copy"
  26. #endif
  27. #include "ui_jackappdialog.h"
  28. #include <QtCore/QFileInfo>
  29. #include <QtCore/QVector>
  30. #include <QtWidgets/QPushButton>
  31. #ifdef __clang__
  32. # pragma clang diagnostic pop
  33. #elif defined(__GNUC__) && __GNUC__ >= 8
  34. # pragma GCC diagnostic pop
  35. #endif
  36. #include "qsafesettings.hpp"
  37. #include "CarlaFrontend.h"
  38. #include "CarlaLibJackHints.h"
  39. #include "CarlaString.hpp"
  40. // --------------------------------------------------------------------------------------------------------------------
  41. // Jack Application Dialog
  42. enum {
  43. UI_SESSION_NONE = 0,
  44. UI_SESSION_LADISH = 1,
  45. UI_SESSION_NSM = 2,
  46. };
  47. struct JackAppDialog::Self {
  48. Ui_JackAppDialog ui;
  49. const QString fProjectFilename;
  50. Self(const char* const projectFilename)
  51. : fProjectFilename(projectFilename) {}
  52. static Self& create(const char* const projectFilename)
  53. {
  54. Self* const self = new Self(projectFilename);
  55. return *self;
  56. }
  57. };
  58. JackAppDialog::JackAppDialog(QWidget* const parent, const char* const projectFilename)
  59. : QDialog(parent),
  60. self(Self::create(projectFilename))
  61. {
  62. self.ui.setupUi(this);
  63. // -------------------------------------------------------------------------------------------------------------
  64. // UI setup
  65. self.ui.group_error->setVisible(false);
  66. adjustSize();
  67. setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
  68. #ifdef CARLA_OS_MAC
  69. if (parent != nullptr)
  70. setWindowModality(Qt::WindowModal);
  71. #endif
  72. // -------------------------------------------------------------------------------------------------------------
  73. // Load settings
  74. loadSettings();
  75. // -------------------------------------------------------------------------------------------------------------
  76. // Set-up connections
  77. connect(this, &QDialog::finished,
  78. this, &JackAppDialog::slot_saveSettings);
  79. connect(self.ui.cb_session_mgr, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
  80. this, &JackAppDialog::slot_sessionManagerChanged);
  81. connect(self.ui.le_command, &QLineEdit::textChanged,
  82. this, &JackAppDialog::slot_commandChanged);
  83. }
  84. JackAppDialog::~JackAppDialog()
  85. {
  86. delete &self;
  87. }
  88. // -----------------------------------------------------------------------------------------------------------------
  89. // public methods
  90. JackAppDialog::CommandAndFlags JackAppDialog::getCommandAndFlags() const
  91. {
  92. const QString command = self.ui.le_command->text();
  93. QString name = self.ui.le_name->text();
  94. if (name.isEmpty())
  95. {
  96. name = QFileInfo(command.split(' ').first()).baseName();
  97. name[0] = name[0].toTitleCase();
  98. }
  99. SessionManager smgr;
  100. switch (self.ui.cb_session_mgr->currentIndex())
  101. {
  102. case UI_SESSION_LADISH:
  103. smgr = LIBJACK_SESSION_MANAGER_LADISH;
  104. break;
  105. case UI_SESSION_NSM:
  106. smgr = LIBJACK_SESSION_MANAGER_NSM;
  107. break;
  108. default:
  109. smgr = LIBJACK_SESSION_MANAGER_NONE;
  110. break;
  111. }
  112. uint flags = 0x0;
  113. if (self.ui.cb_manage_window->isChecked())
  114. flags |= LIBJACK_FLAG_CONTROL_WINDOW;
  115. if (self.ui.cb_capture_first_window->isChecked())
  116. flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW;
  117. if (self.ui.cb_buffers_addition_mode->isChecked())
  118. flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION;
  119. if (self.ui.cb_out_midi_mixdown->isChecked())
  120. flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN;
  121. if (self.ui.cb_external_start->isChecked())
  122. flags |= LIBJACK_FLAG_EXTERNAL_START;
  123. const QString labelSetup(QString("%1%2%3%4%5%6").arg(QChar('0' + self.ui.sb_audio_ins->value()))
  124. .arg(QChar('0' + self.ui.sb_audio_outs->value()))
  125. .arg(QChar('0' + self.ui.sb_midi_ins->value()))
  126. .arg(QChar('0' + self.ui.sb_midi_outs->value()))
  127. .arg(QChar('0' + smgr))
  128. .arg(QChar('0' + flags)));
  129. return {command, name, labelSetup};
  130. }
  131. // -----------------------------------------------------------------------------------------------------------------
  132. // private methods
  133. void JackAppDialog::checkIfButtonBoxShouldBeEnabled(const int index, const QCarlaString& command)
  134. {
  135. bool enabled = command.isNotEmpty();
  136. QCarlaString showErr;
  137. // NSM applications must not be abstract or absolute paths, and must not contain arguments
  138. if (enabled and index == UI_SESSION_NSM)
  139. {
  140. if (QVector<QChar>{'.', '/'}.contains(command[0]))
  141. showErr = tr("NSM applications cannot use abstract or absolute paths");
  142. else if (command.contains(' ') or command.contains(';') or command.contains('&'))
  143. showErr = tr("NSM applications cannot use CLI arguments");
  144. else if (self.fProjectFilename.isEmpty())
  145. showErr = tr("You need to save the current Carla project before NSM can be used");
  146. }
  147. if (showErr.isNotEmpty())
  148. {
  149. enabled = false;
  150. self.ui.l_error->setText(showErr);
  151. self.ui.group_error->setVisible(true);
  152. }
  153. else
  154. {
  155. self.ui.group_error->setVisible(false);
  156. }
  157. if (QPushButton* const button = self.ui.buttonBox->button(QDialogButtonBox::Ok))
  158. button->setEnabled(enabled);
  159. }
  160. void JackAppDialog::loadSettings()
  161. {
  162. const QSafeSettings settings("falkTX", "CarlaAddJackApp");
  163. const QString smName = settings.valueString("SessionManager", "");
  164. if (smName == "LADISH (SIGUSR1)")
  165. self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH);
  166. else if (smName == "NSM")
  167. self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM);
  168. else
  169. self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE);
  170. self.ui.le_command->setText(settings.valueString("Command", ""));
  171. self.ui.le_name->setText(settings.valueString("Name", ""));
  172. self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
  173. self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
  174. self.ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2));
  175. self.ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0));
  176. self.ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0));
  177. self.ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true));
  178. self.ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false));
  179. self.ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false));
  180. checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(),
  181. self.ui.le_command->text());
  182. }
  183. // -----------------------------------------------------------------------------------------------------------------
  184. // private slots
  185. void JackAppDialog::slot_commandChanged(const QString& command)
  186. {
  187. checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), command);
  188. }
  189. void JackAppDialog::slot_sessionManagerChanged(const int index)
  190. {
  191. checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command->text());
  192. }
  193. void JackAppDialog::slot_saveSettings()
  194. {
  195. QSafeSettings settings("falkTX", "CarlaAddJackApp");
  196. settings.setValue("Command", self.ui.le_command->text());
  197. settings.setValue("Name", self.ui.le_name->text());
  198. settings.setValue("SessionManager", self.ui.cb_session_mgr->currentText());
  199. settings.setValue("NumAudioIns", self.ui.sb_audio_ins->value());
  200. settings.setValue("NumAudioOuts", self.ui.sb_audio_outs->value());
  201. settings.setValue("NumMidiIns", self.ui.sb_midi_ins->value());
  202. settings.setValue("NumMidiOuts", self.ui.sb_midi_outs->value());
  203. settings.setValue("ManageWindow", self.ui.cb_manage_window->isChecked());
  204. settings.setValue("CaptureFirstWindow", self.ui.cb_capture_first_window->isChecked());
  205. settings.setValue("MidiOutMixdown", self.ui.cb_out_midi_mixdown->isChecked());
  206. }
  207. // --------------------------------------------------------------------------------------------------------------------
  208. const JackAppDialogResults*
  209. carla_frontend_createAndExecJackAppDialog(void* const parent, const char* const projectFilename)
  210. {
  211. JackAppDialog gui(reinterpret_cast<QWidget*>(parent), projectFilename);
  212. if (gui.exec())
  213. {
  214. static JackAppDialogResults ret = {};
  215. static CarlaString retCommand;
  216. static CarlaString retName;
  217. static CarlaString retLabelSetup;
  218. const JackAppDialog::CommandAndFlags cafs = gui.getCommandAndFlags();
  219. retCommand = cafs.command.toUtf8().constData();
  220. retName = cafs.name.toUtf8().constData();
  221. retLabelSetup = cafs.labelSetup.toUtf8().constData();
  222. ret.command = retCommand;
  223. ret.name = retName;
  224. ret.labelSetup = retLabelSetup;
  225. return &ret;
  226. }
  227. return nullptr;
  228. }
  229. #if 0
  230. // --------------------------------------------------------------------------------------------------------------------
  231. // Testing
  232. #include "../utils/qsafesettings.cpp"
  233. int main(int argc, char* argv[])
  234. {
  235. QApplication app(argc, argv);
  236. if (JackAppDialogResults* const res = carla_frontend_createAndExecJackAppDialog(nullptr, ""))
  237. {
  238. printf("Results:\n");
  239. printf("\tCommand: %s\n", res->command);
  240. printf("\tName: %s\n", res->name);
  241. printf("\tLabelSetup: %s\n", res->labelSetup);
  242. }
  243. return 0;
  244. }
  245. #endif
  246. // --------------------------------------------------------------------------------------------------------------------