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.

450 lines
14KB

  1. /*
  2. * Common Carla code
  3. * Copyright (C) 2011-2019 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 "carla_shared.hpp"
  18. //---------------------------------------------------------------------------------------------------------------------
  19. // Imports (Global)
  20. #include <QtGui/QFontMetrics>
  21. #include <QtWidgets/QFileDialog>
  22. #include <QtWidgets/QGridLayout>
  23. #include <QtWidgets/QLineEdit>
  24. #ifdef CARLA_OS_UNIX
  25. # include <signal.h>
  26. #endif
  27. //---------------------------------------------------------------------------------------------------------------------
  28. // Imports (Custom)
  29. #include "carla_host.hpp"
  30. #include "CarlaUtils.h"
  31. #include "CarlaMathUtils.hpp"
  32. //---------------------------------------------------------------------------------------------------------------------
  33. // Global Carla object
  34. CarlaObject::CarlaObject() noexcept
  35. : host(nullptr),
  36. gui(nullptr),
  37. nogui(false),
  38. term(false) {}
  39. CarlaObject gCarla;
  40. //---------------------------------------------------------------------------------------------------------------------
  41. // Get Icon from user theme, using our own as backup (Oxygen)
  42. QIcon getIcon(const QString icon, const int size)
  43. {
  44. return QIcon::fromTheme(icon, QIcon(QString(":/%1x%1/%2.png").arg(size).arg(icon)));
  45. }
  46. //---------------------------------------------------------------------------------------------------------------------
  47. // Handle some basic command-line arguments shared between all carla variants
  48. QString handleInitialCommandLineArguments(const int argc, char* argv[])
  49. {
  50. static const QStringList listArgsNoGUI = { "-n", "--n", "-no-gui", "--no-gui", "-nogui", "--nogui" };
  51. static const QStringList listArgsHelp = { "-h", "--h", "-help", "--help" };
  52. static const QStringList listArgsVersion = { "-v", "--v", "-version", "--version" };
  53. QString initName(argv[0]); // = os.path.basename(file) if (file is not None and os.path.dirname(file) in PATH) else sys.argv[0]
  54. // libPrefix = None
  55. for (int i=1; i<argc; ++i)
  56. {
  57. const QString arg(argv[i]);
  58. if (arg.startsWith("--with-appname="))
  59. {
  60. // initName = os.path.basename(arg.replace("--with-appname=", ""));
  61. }
  62. else if (arg.startsWith("--with-libprefix=") || arg == "--gdb")
  63. {
  64. pass();
  65. }
  66. else if (listArgsNoGUI.contains(arg))
  67. {
  68. gCarla.nogui = true;
  69. }
  70. else if (listArgsHelp.contains(arg))
  71. {
  72. carla_stdout("Usage: %s [OPTION]... [FILE|URL]", initName);
  73. carla_stdout("");
  74. carla_stdout(" where FILE can be a Carla project or preset file to be loaded, or URL if using Carla-Control");
  75. carla_stdout("");
  76. carla_stdout(" and OPTION can be one or more of the following:");
  77. carla_stdout("");
  78. carla_stdout(" --gdb \t Run Carla inside gdb.");
  79. carla_stdout(" -n,--no-gui \t Run Carla headless, don't show UI.");
  80. carla_stdout("");
  81. carla_stdout(" -h,--help \t Print this help text and exit.");
  82. carla_stdout(" -v,--version\t Print version information and exit.");
  83. carla_stdout("");
  84. std::exit(0);
  85. }
  86. else if (listArgsVersion.contains(arg))
  87. {
  88. /*
  89. QString pathBinaries, pathResources = getPaths();
  90. */
  91. carla_stdout("Using Carla version %s", CARLA_VERSION_STRING);
  92. /*
  93. carla_stdout(" Qt version: %s", QT_VERSION_STR);
  94. carla_stdout(" Binary dir: %s", pathBinaries.toUtf8());
  95. carla_stdout(" Resources dir: %s", pathResources.toUtf8());
  96. */
  97. std::exit(0);
  98. }
  99. }
  100. return initName;
  101. }
  102. //---------------------------------------------------------------------------------------------------------------------
  103. // Get initial project file (as passed in the command-line parameters)
  104. QString getInitialProjectFile(bool)
  105. {
  106. // TODO
  107. return "";
  108. }
  109. //---------------------------------------------------------------------------------------------------------------------
  110. // Get paths (binaries, resources)
  111. bool getPaths(QString& pathBinaries, QString& pathResources)
  112. {
  113. const QString libFolder(carla_get_library_folder());
  114. QDir dir(libFolder);
  115. // FIXME need to completely rework this in C++ mode, so check if all cases are valid
  116. if (libFolder.endsWith("bin"))
  117. {
  118. CARLA_SAFE_ASSERT_RETURN(dir.cd("resources"), false);
  119. pathBinaries = libFolder;
  120. pathResources = dir.absolutePath();
  121. return true;
  122. }
  123. else if (libFolder.endsWith("carla"))
  124. {
  125. for (int i=2; --i>=0;)
  126. {
  127. CARLA_SAFE_ASSERT_INT_RETURN(dir.cdUp(), i, false);
  128. CARLA_SAFE_ASSERT_INT_RETURN(dir.cd("share"), i, false);
  129. if (dir.exists())
  130. {
  131. CARLA_SAFE_ASSERT_INT_RETURN(dir.cd("carla"), i, false);
  132. CARLA_SAFE_ASSERT_INT_RETURN(dir.cd("resources"), i, false);
  133. pathBinaries = libFolder;
  134. pathResources = dir.absolutePath();
  135. return true;
  136. }
  137. }
  138. }
  139. return false;
  140. }
  141. //---------------------------------------------------------------------------------------------------------------------
  142. // Signal handler
  143. static void signalHandler(const int sig)
  144. {
  145. switch (sig)
  146. {
  147. case SIGINT:
  148. case SIGTERM:
  149. gCarla.term = true;
  150. if (gCarla.host != nullptr)
  151. emit gCarla.host->SignalTerminate();
  152. break;
  153. case SIGUSR1:
  154. if (gCarla.host != nullptr)
  155. emit gCarla.host->SignalSave();
  156. break;
  157. }
  158. }
  159. #ifdef CARLA_OS_WIN
  160. static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept
  161. {
  162. if (dwCtrlType == CTRL_C_EVENT)
  163. {
  164. signalHandler(SIGINT);
  165. return TRUE;
  166. }
  167. return FALSE;
  168. }
  169. #endif
  170. void setUpSignals()
  171. {
  172. #if defined(CARLA_OS_UNIX)
  173. struct sigaction sig;
  174. carla_zeroStruct(sig);
  175. sig.sa_handler = signalHandler;
  176. sig.sa_flags = SA_RESTART;
  177. sigemptyset(&sig.sa_mask);
  178. sigaction(SIGTERM, &sig, nullptr);
  179. sigaction(SIGINT, &sig, nullptr);
  180. sigaction(SIGUSR1, &sig, nullptr);
  181. #elif defined(CARLA_OS_WIN)
  182. SetConsoleCtrlHandler(winSignalHandler, TRUE);
  183. #endif
  184. }
  185. //---------------------------------------------------------------------------------------------------------------------
  186. // QLineEdit and QPushButton combo
  187. QString getAndSetPath(QWidget* const parent, QLineEdit* const lineEdit)
  188. {
  189. const QCarlaString newPath = QFileDialog::getExistingDirectory(parent, parent->tr("Set Path"), lineEdit->text(), QFileDialog::ShowDirsOnly);
  190. if (newPath.isNotEmpty())
  191. lineEdit->setText(newPath);
  192. return newPath;
  193. }
  194. //---------------------------------------------------------------------------------------------------------------------
  195. // fill up a qlists from a C arrays
  196. void fillQStringListFromStringArray(QStringList& list, const char* const* const stringArray)
  197. {
  198. uint count = 0;
  199. // count number of strings first
  200. for (; stringArray[count] != nullptr; ++count) {}
  201. // allocate list
  202. list.reserve(count);
  203. // fill in strings
  204. for (count = 0; stringArray[count] != nullptr; ++count)
  205. list.append(stringArray[count]);
  206. }
  207. void fillQDoubleListFromDoubleArray(QList<double>& list, const double* const doubleArray)
  208. {
  209. uint count = 0;
  210. // count number of strings first
  211. for (; carla_isNotZero(doubleArray[count]); ++count) {}
  212. // allocate list
  213. list.reserve(count);
  214. // fill in strings
  215. for (count = 0; carla_isNotZero(doubleArray[count]); ++count)
  216. list.append(doubleArray[count]);
  217. }
  218. void fillQUIntListFromUIntArray(QList<uint>& list, const uint* const uintArray)
  219. {
  220. uint count = 0;
  221. // count number of strings first
  222. for (; uintArray[count] != 0; ++count) {}
  223. // allocate list
  224. list.reserve(count);
  225. // fill in strings
  226. for (count = 0; uintArray[count] != 0; ++count)
  227. list.append(uintArray[count]);
  228. }
  229. //---------------------------------------------------------------------------------------------------------------------
  230. // Backwards-compatible horizontalAdvance/width call, depending on Qt version
  231. int fontMetricsHorizontalAdvance(const QFontMetrics& fm, const QString& s)
  232. {
  233. #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
  234. return fm.horizontalAdvance(s);
  235. #else
  236. return fm.width(s);
  237. #endif
  238. }
  239. //---------------------------------------------------------------------------------------------------------------------
  240. // Check if a string array contains a string
  241. bool stringArrayContainsString(const char* const* const stringArray, const char* const string) noexcept
  242. {
  243. for (uint i=0; stringArray[i] != nullptr; ++i)
  244. {
  245. if (std::strcmp(stringArray[i], string) == 0)
  246. return true;
  247. }
  248. return false;
  249. }
  250. //---------------------------------------------------------------------------------------------------------------------
  251. // Custom QMessageBox which resizes itself to fit text
  252. void QMessageBoxWithBetterWidth::showEvent(QShowEvent* const event)
  253. {
  254. const QFontMetrics metrics(fontMetrics());
  255. const QStringList lines(text().trimmed().split("\n") + informativeText().trimmed().split("\n"));
  256. if (lines.size() > 0)
  257. {
  258. int width = 0;
  259. for (const QString& line : lines)
  260. width = std::max(fontMetricsHorizontalAdvance(metrics, line), width);
  261. if (QGridLayout* const layout_ = dynamic_cast<QGridLayout*>(layout()))
  262. layout_->setColumnMinimumWidth(2, width + 12);
  263. }
  264. QMessageBox::showEvent(event);
  265. }
  266. //---------------------------------------------------------------------------------------------------------------------
  267. // Safer QSettings class, which does not throw if type mismatches
  268. bool QSafeSettings::valueBool(const QString key, const bool defaultValue) const
  269. {
  270. QVariant var(value(key, defaultValue));
  271. if (var.isNull())
  272. return defaultValue;
  273. CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::Bool), defaultValue);
  274. return var.isValid() ? var.toBool() : defaultValue;
  275. }
  276. Qt::CheckState QSafeSettings::valueCheckState(const QString key, const Qt::CheckState defaultValue) const
  277. {
  278. QVariant var(value(key, defaultValue));
  279. if (var.isNull())
  280. return defaultValue;
  281. CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::UInt), defaultValue);
  282. if (! var.isValid())
  283. return defaultValue;
  284. const uint value = var.toUInt();
  285. switch (value)
  286. {
  287. case Qt::Unchecked:
  288. case Qt::PartiallyChecked:
  289. case Qt::Checked:
  290. return static_cast<Qt::CheckState>(value);
  291. default:
  292. return defaultValue;
  293. }
  294. }
  295. uint QSafeSettings::valueUInt(const QString key, const uint defaultValue) const
  296. {
  297. QVariant var(value(key, defaultValue));
  298. if (var.isNull())
  299. return defaultValue;
  300. CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::UInt), defaultValue);
  301. return var.isValid() ? var.toUInt() : defaultValue;
  302. }
  303. double QSafeSettings::valueDouble(const QString key, const double defaultValue) const
  304. {
  305. QVariant var(value(key, defaultValue));
  306. if (var.isNull())
  307. return defaultValue;
  308. CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::Double), defaultValue);
  309. return var.isValid() ? var.toDouble() : defaultValue;
  310. }
  311. QString QSafeSettings::valueString(const QString key, const QString defaultValue) const
  312. {
  313. QVariant var(value(key, defaultValue));
  314. if (var.isNull())
  315. return defaultValue;
  316. CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::String), defaultValue);
  317. return var.isValid() ? var.toString() : defaultValue;
  318. }
  319. QByteArray QSafeSettings::valueByteArray(const QString key, const QByteArray defaultValue) const
  320. {
  321. QVariant var(value(key, defaultValue));
  322. if (var.isNull())
  323. return defaultValue;
  324. CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::ByteArray), defaultValue);
  325. return var.isValid() ? var.toByteArray() : defaultValue;
  326. }
  327. QStringList QSafeSettings::valueStringList(const QString key, const QStringList defaultValue) const
  328. {
  329. QVariant var(value(key, defaultValue));
  330. if (var.isNull())
  331. return defaultValue;
  332. CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::StringList), defaultValue);
  333. return var.isValid() ? var.toStringList() : defaultValue;
  334. }
  335. //---------------------------------------------------------------------------------------------------------------------
  336. // Custom MessageBox
  337. int CustomMessageBox(QWidget* const parent,
  338. const QMessageBox::Icon icon,
  339. const QString title,
  340. const QString text,
  341. const QString extraText,
  342. const QMessageBox::StandardButtons buttons,
  343. const QMessageBox::StandardButton defButton)
  344. {
  345. QMessageBoxWithBetterWidth msgBox(parent);
  346. msgBox.setIcon(icon);
  347. msgBox.setWindowTitle(title);
  348. msgBox.setText(text);
  349. msgBox.setInformativeText(extraText);
  350. msgBox.setStandardButtons(buttons);
  351. msgBox.setDefaultButton(defButton);
  352. return msgBox.exec();
  353. }
  354. //---------------------------------------------------------------------------------------------------------------------