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.

419 lines
12KB

  1. --- ../Rack/src/plugin.cpp 2023-06-11 21:02:02.644002965 +0200
  2. +++ plugin.cpp 2023-06-11 20:56:09.476002797 +0200
  3. @@ -1,356 +1,46 @@
  4. -#include <thread>
  5. -#include <map>
  6. -#include <stdexcept>
  7. -#include <tuple>
  8. -
  9. -#include <sys/types.h>
  10. -#include <sys/stat.h>
  11. -#include <unistd.h>
  12. -#include <sys/param.h> // for MAXPATHLEN
  13. -#include <fcntl.h>
  14. -#if defined ARCH_WIN
  15. - #include <windows.h>
  16. - #include <direct.h>
  17. -#else
  18. - #include <dlfcn.h> // for dlopen
  19. -#endif
  20. -#include <dirent.h>
  21. +/*
  22. + * DISTRHO Cardinal Plugin
  23. + * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
  24. + *
  25. + * This program is free software; you can redistribute it and/or
  26. + * modify it under the terms of the GNU General Public License as
  27. + * published by the Free Software Foundation; either version 3 of
  28. + * the License, or any later version.
  29. + *
  30. + * This program is distributed in the hope that it will be useful,
  31. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  32. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  33. + * GNU General Public License for more details.
  34. + *
  35. + * For a full copy of the GNU General Public License see the LICENSE file.
  36. + */
  37. +
  38. +/**
  39. + * This file is an edited version of VCVRack's plugin.cpp
  40. + * Copyright (C) 2016-2023 VCV.
  41. + *
  42. + * This program is free software: you can redistribute it and/or
  43. + * modify it under the terms of the GNU General Public License as
  44. + * published by the Free Software Foundation; either version 3 of
  45. + * the License, or (at your option) any later version.
  46. + */
  47. -#include <osdialog.h>
  48. -#include <jansson.h>
  49. +#include <algorithm>
  50. +#include <map>
  51. #include <plugin.hpp>
  52. -#include <system.hpp>
  53. -#include <asset.hpp>
  54. -#include <string.hpp>
  55. -#include <context.hpp>
  56. -#include <plugin/callbacks.hpp>
  57. -#include <settings.hpp>
  58. namespace rack {
  59. -
  60. -namespace core {
  61. -void init(rack::plugin::Plugin* plugin);
  62. -} // namespace core
  63. -
  64. namespace plugin {
  65. -////////////////////
  66. -// private API
  67. -////////////////////
  68. -
  69. -
  70. -static void* getSymbol(void* handle, const char* name) {
  71. - if (!handle)
  72. - return NULL;
  73. -
  74. -#if defined ARCH_WIN
  75. - return (void*) GetProcAddress((HMODULE) handle, name);
  76. -#else
  77. - return dlsym(handle, name);
  78. -#endif
  79. -}
  80. -
  81. -/** Returns library handle */
  82. -static void* loadLibrary(std::string libraryPath) {
  83. -#if defined ARCH_WIN
  84. - SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  85. - std::wstring libraryFilenameW = string::UTF8toUTF16(libraryPath);
  86. - HINSTANCE handle = LoadLibraryW(libraryFilenameW.c_str());
  87. - SetErrorMode(0);
  88. - if (!handle) {
  89. - int error = GetLastError();
  90. - throw Exception("Failed to load library %s: code %d", libraryPath.c_str(), error);
  91. - }
  92. -#else
  93. - // Since Rack 2, plugins on Linux/Mac link to the absolute path /tmp/Rack2/libRack.<ext>
  94. - // Create a symlink at /tmp/Rack2 to the system dir containting libRack.
  95. - std::string systemDir = system::getAbsolute(asset::systemDir);
  96. - std::string linkPath = "/tmp/Rack2";
  97. - if (!settings::devMode) {
  98. - // Clean up old symbolic link in case a different edition was run earlier
  99. - system::remove(linkPath);
  100. - system::createSymbolicLink(systemDir, linkPath);
  101. - }
  102. - // Load library with dlopen
  103. - void* handle = NULL;
  104. - #if defined ARCH_LIN
  105. - handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL);
  106. - #elif defined ARCH_MAC
  107. - handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL);
  108. - #endif
  109. - if (!settings::devMode) {
  110. - system::remove(linkPath);
  111. - }
  112. - if (!handle)
  113. - throw Exception("Failed to load library %s: %s", libraryPath.c_str(), dlerror());
  114. -#endif
  115. - return handle;
  116. -}
  117. -
  118. -typedef void (*InitCallback)(Plugin*);
  119. -
  120. -static InitCallback loadPluginCallback(Plugin* plugin) {
  121. - // Load plugin library
  122. - std::string libraryExt;
  123. -#if defined ARCH_LIN
  124. - libraryExt = "so";
  125. -#elif defined ARCH_WIN
  126. - libraryExt = "dll";
  127. -#elif ARCH_MAC
  128. - libraryExt = "dylib";
  129. -#endif
  130. -
  131. -#if defined ARCH_X64
  132. - // Use `plugin.EXT` on x64 for backward compatibility.
  133. - // Change to `plugin-OS-CPU.EXT` in Rack 3.
  134. - std::string libraryFilename = "plugin." + libraryExt;
  135. -#else
  136. - // Use `plugin-CPU.EXT` on other CPUs like ARM64
  137. - std::string libraryFilename = "plugin-" + APP_CPU + "." + libraryExt;
  138. -#endif
  139. - std::string libraryPath = system::join(plugin->path, libraryFilename);
  140. -
  141. - // Check file existence
  142. - if (!system::isFile(libraryPath))
  143. - throw Exception("Plugin binary not found at %s", libraryPath.c_str());
  144. -
  145. - // Load dynamic/shared library
  146. - plugin->handle = loadLibrary(libraryPath);
  147. -
  148. - // Get plugin's init() function
  149. - InitCallback initCallback = (InitCallback) getSymbol(plugin->handle, "init");
  150. - if (!initCallback)
  151. - throw Exception("Failed to read init() symbol in %s", libraryPath.c_str());
  152. -
  153. - return initCallback;
  154. -}
  155. -
  156. -
  157. -/** If path is blank, loads Core */
  158. -static Plugin* loadPlugin(std::string path) {
  159. - if (path == "")
  160. - INFO("Loading Core plugin");
  161. - else
  162. - INFO("Loading plugin from %s", path.c_str());
  163. -
  164. - Plugin* plugin = new Plugin;
  165. - try {
  166. - // Set plugin path
  167. - plugin->path = (path == "") ? asset::systemDir : path;
  168. -
  169. - // Get modified timestamp
  170. - if (path != "") {
  171. - struct stat statbuf;
  172. - if (!stat(path.c_str(), &statbuf)) {
  173. -#if defined ARCH_MAC
  174. - plugin->modifiedTimestamp = (double) statbuf.st_mtimespec.tv_sec + statbuf.st_mtimespec.tv_nsec * 1e-9;
  175. -#elif defined ARCH_WIN
  176. - plugin->modifiedTimestamp = (double) statbuf.st_mtime;
  177. -#elif defined ARCH_LIN
  178. - plugin->modifiedTimestamp = (double) statbuf.st_mtim.tv_sec + statbuf.st_mtim.tv_nsec * 1e-9;
  179. -#endif
  180. - }
  181. - }
  182. -
  183. - // Load plugin.json
  184. - std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json");
  185. - FILE* file = std::fopen(manifestFilename.c_str(), "r");
  186. - if (!file)
  187. - throw Exception("Manifest file %s does not exist", manifestFilename.c_str());
  188. - DEFER({std::fclose(file);});
  189. -
  190. - json_error_t error;
  191. - json_t* rootJ = json_loadf(file, 0, &error);
  192. - if (!rootJ)
  193. - throw Exception("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text);
  194. - DEFER({json_decref(rootJ);});
  195. -
  196. - // Load manifest
  197. - plugin->fromJson(rootJ);
  198. -
  199. - // Reject plugin if slug already exists
  200. - Plugin* existingPlugin = getPlugin(plugin->slug);
  201. - if (existingPlugin)
  202. - throw Exception("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str());
  203. -
  204. - // Call init callback
  205. - InitCallback initCallback;
  206. - if (path == "") {
  207. - initCallback = core::init;
  208. - }
  209. - else {
  210. - initCallback = loadPluginCallback(plugin);
  211. - }
  212. - initCallback(plugin);
  213. -
  214. - // Load modules manifest
  215. - json_t* modulesJ = json_object_get(rootJ, "modules");
  216. - plugin->modulesFromJson(modulesJ);
  217. -
  218. - // Call settingsFromJson() if exists
  219. - // Returns NULL for Core.
  220. - auto settingsFromJson = (decltype(&::settingsFromJson)) getSymbol(plugin->handle, "settingsFromJson");
  221. - if (settingsFromJson) {
  222. - json_t* settingsJ = json_object_get(settings::pluginSettingsJ, plugin->slug.c_str());
  223. - if (settingsJ)
  224. - settingsFromJson(settingsJ);
  225. - }
  226. - }
  227. - catch (Exception& e) {
  228. - WARN("Could not load plugin %s: %s", path.c_str(), e.what());
  229. - delete plugin;
  230. - return NULL;
  231. - }
  232. -
  233. - INFO("Loaded %s %s", plugin->slug.c_str(), plugin->version.c_str());
  234. - plugins.push_back(plugin);
  235. - return plugin;
  236. -}
  237. -
  238. -
  239. -static void loadPlugins(std::string path) {
  240. - for (std::string pluginPath : system::getEntries(path)) {
  241. - if (!system::isDirectory(pluginPath))
  242. - continue;
  243. - if (!loadPlugin(pluginPath)) {
  244. - // Ignore bad plugins. They are reported in the log.
  245. - }
  246. - }
  247. -}
  248. -
  249. -
  250. -static void extractPackages(std::string path) {
  251. - std::string message;
  252. -
  253. - for (std::string packagePath : system::getEntries(path)) {
  254. - if (!system::isFile(packagePath))
  255. - continue;
  256. - if (system::getExtension(packagePath) != ".vcvplugin")
  257. - continue;
  258. -
  259. - // Extract package
  260. - INFO("Extracting package %s", packagePath.c_str());
  261. - try {
  262. - system::unarchiveToDirectory(packagePath, path);
  263. - }
  264. - catch (Exception& e) {
  265. - WARN("Plugin package %s failed to extract: %s", packagePath.c_str(), e.what());
  266. - message += string::f("Could not extract plugin package %s\n", packagePath.c_str());
  267. - continue;
  268. - }
  269. - // Remove package
  270. - system::remove(packagePath.c_str());
  271. - }
  272. - if (!message.empty()) {
  273. - osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  274. - }
  275. -}
  276. -
  277. -////////////////////
  278. -// public API
  279. -////////////////////
  280. -
  281. -void init() {
  282. - // Don't re-initialize
  283. - assert(plugins.empty());
  284. -
  285. - // Load Core
  286. - loadPlugin("");
  287. -
  288. - pluginsPath = asset::user("plugins");
  289. -
  290. - // Get user plugins directory
  291. - system::createDirectory(pluginsPath);
  292. -
  293. - // Don't load plugins if safe mode is enabled
  294. - if (settings::safeMode)
  295. - return;
  296. -
  297. - // Extract packages and load plugins
  298. - extractPackages(pluginsPath);
  299. - loadPlugins(pluginsPath);
  300. -
  301. - // If Fundamental wasn't loaded, copy the bundled Fundamental package and load it
  302. - if (!settings::devMode && !getPlugin("Fundamental")) {
  303. - std::string fundamentalSrc = asset::system("Fundamental.vcvplugin");
  304. - std::string fundamentalDir = system::join(pluginsPath, "Fundamental");
  305. - if (system::isFile(fundamentalSrc)) {
  306. - INFO("Extracting bundled Fundamental package");
  307. - try {
  308. - system::unarchiveToDirectory(fundamentalSrc.c_str(), pluginsPath.c_str());
  309. - loadPlugin(fundamentalDir);
  310. - }
  311. - catch (Exception& e) {
  312. - WARN("Could not extract Fundamental package: %s", e.what());
  313. - }
  314. - }
  315. - }
  316. -}
  317. -
  318. -
  319. -static void destroyPlugin(Plugin* plugin) {
  320. - void* handle = plugin->handle;
  321. -
  322. - // Call destroy() if defined in the plugin library
  323. - typedef void (*DestroyCallback)();
  324. - DestroyCallback destroyCallback = NULL;
  325. - if (handle) {
  326. - destroyCallback = (DestroyCallback) getSymbol(handle, "destroy");
  327. - }
  328. - if (destroyCallback) {
  329. - try {
  330. - destroyCallback();
  331. - }
  332. - catch (Exception& e) {
  333. - WARN("Could not destroy plugin %s", plugin->slug.c_str());
  334. - }
  335. - }
  336. -
  337. - // We must delete the Plugin instance *before* freeing the library, because the vtables of Model subclasses are defined in the library, which are needed in the Plugin destructor.
  338. - delete plugin;
  339. -
  340. - // Free library handle
  341. - if (handle) {
  342. -#if defined ARCH_WIN
  343. - FreeLibrary((HINSTANCE) handle);
  344. -#else
  345. - dlclose(handle);
  346. -#endif
  347. - }
  348. -}
  349. -
  350. -
  351. -void destroy() {
  352. - while (!plugins.empty()) {
  353. - Plugin* plugin = plugins.back();
  354. - INFO("Destroying plugin %s", plugin->name.c_str());
  355. - destroyPlugin(plugin);
  356. - plugins.pop_back();
  357. - }
  358. - assert(plugins.empty());
  359. -}
  360. -
  361. -
  362. -void settingsMergeJson(json_t* rootJ) {
  363. - for (Plugin* plugin : plugins) {
  364. - auto settingsToJson = (decltype(&::settingsToJson)) getSymbol(plugin->handle, "settingsToJson");
  365. - if (settingsToJson) {
  366. - json_t* settingsJ = settingsToJson();
  367. - json_object_set_new(rootJ, plugin->slug.c_str(), settingsJ);
  368. - }
  369. - else {
  370. - json_object_del(rootJ, plugin->slug.c_str());
  371. - }
  372. - }
  373. -}
  374. +void settingsMergeJson(json_t*) {}
  375. /** Given slug => fallback slug.
  376. Supports bidirectional fallbacks.
  377. -To request fallback slugs to be added to this list, contact VCV support.
  378. +To request fallback slugs to be added to this list, open a GitHub issue.
  379. */
  380. static const std::map<std::string, std::string> pluginSlugFallbacks = {
  381. {"VultModulesFree", "VultModules"},
  382. @@ -399,8 +89,19 @@
  383. */
  384. using PluginModuleSlug = std::tuple<std::string, std::string>;
  385. static const std::map<PluginModuleSlug, PluginModuleSlug> moduleSlugFallbacks = {
  386. + {{"Core", "AudioInterface2"}, {"Cardinal", "HostAudio2"}},
  387. + {{"Core", "AudioInterface"}, {"Cardinal", "HostAudio8"}},
  388. + {{"Core", "AudioInterface16"}, {"Cardinal", "HostAudio8"}},
  389. + {{"Core", "MIDIToCVInterface"}, {"Cardinal", "HostMIDI"}},
  390. + {{"Core", "MIDICCToCVInterface"}, {"Cardinal", "HostMIDICC"}},
  391. + {{"Core", "MIDITriggerToCVInterface"}, {"Cardinal", "HostMIDIGate"}},
  392. + {{"Core", "CV-MIDI"}, {"Cardinal", "HostMIDI"}},
  393. + {{"Core", "CV-CC"}, {"Cardinal", "HostMIDICC"}},
  394. + {{"Core", "CV-Gate"}, {"Cardinal", "HostMIDIGate"}},
  395. + {{"Core", "MIDI-Map"}, {"Cardinal", "HostMIDIMap"}},
  396. + {{"Core", "Notes"}, {"Cardinal", "TextEditor"}},
  397. + {{"Core", "Blank"}, {"Cardinal", "Blank"}},
  398. {{"MindMeld-ShapeMasterPro", "ShapeMasterPro"}, {"MindMeldModular", "ShapeMaster"}},
  399. - {{"MindMeldModular", "ShapeMaster"}, {"MindMeld-ShapeMasterPro", "ShapeMasterPro"}},
  400. // {{"", ""}, {"", ""}},
  401. };
  402. @@ -488,7 +189,6 @@
  403. }
  404. -std::string pluginsPath;
  405. std::vector<Plugin*> plugins;