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.

530 lines
16KB

  1. #include <jansson.h>
  2. #include <settings.hpp>
  3. #include <window/Window.hpp>
  4. #include <plugin.hpp>
  5. #include <app/Scene.hpp>
  6. #include <engine/Engine.hpp>
  7. #include <context.hpp>
  8. #include <patch.hpp>
  9. #include <asset.hpp>
  10. #include <system.hpp>
  11. namespace rack {
  12. namespace settings {
  13. std::string settingsPath;
  14. bool devMode = false;
  15. bool headless = false;
  16. bool isPlugin = false;
  17. bool safeMode = false;
  18. std::string token;
  19. bool windowMaximized = false;
  20. math::Vec windowSize = math::Vec(1024, 720);
  21. math::Vec windowPos = math::Vec(NAN, NAN);
  22. bool invertZoom = false;
  23. float pixelRatio = 0.0;
  24. std::string uiTheme = "default";
  25. float cableOpacity = 0.5;
  26. float cableTension = 1.0;
  27. float rackBrightness = 1.0;
  28. float haloBrightness = 0.25;
  29. bool allowCursorLock = true;
  30. KnobMode knobMode = KNOB_MODE_LINEAR;
  31. bool knobScroll = false;
  32. float knobLinearSensitivity = 0.001f;
  33. float knobScrollSensitivity = 0.001f;
  34. float sampleRate = 0;
  35. int threadCount = 1;
  36. bool tooltips = true;
  37. bool cpuMeter = false;
  38. bool lockModules = false;
  39. bool squeezeModules = true;
  40. #if defined ARCH_MAC
  41. // Most Mac GPUs can't handle rendering the screen every frame, so use 30 Hz by default.
  42. float frameRateLimit = 30.f;
  43. #else
  44. float frameRateLimit = 60.f;
  45. #endif
  46. float autosaveInterval = 15.0;
  47. bool skipLoadOnLaunch = false;
  48. std::list<std::string> recentPatchPaths;
  49. std::vector<NVGcolor> cableColors = {
  50. color::fromHexString("#f3374b"), // red
  51. color::fromHexString("#ffb437"), // yellow
  52. color::fromHexString("#00b56e"), // green
  53. color::fromHexString("#3695ef"), // blue
  54. color::fromHexString("#8b4ade"), // purple
  55. };
  56. bool autoCheckUpdates = true;
  57. bool showTipsOnLaunch = true;
  58. int tipIndex = -1;
  59. BrowserSort browserSort = BROWSER_SORT_UPDATED;
  60. float browserZoom = -1.f;
  61. json_t* pluginSettingsJ = NULL;
  62. std::map<std::string, std::map<std::string, ModuleInfo>> moduleInfos;
  63. std::map<std::string, PluginWhitelist> moduleWhitelist;
  64. ModuleInfo* getModuleInfo(const std::string& pluginSlug, const std::string& moduleSlug) {
  65. auto pluginIt = moduleInfos.find(pluginSlug);
  66. if (pluginIt == moduleInfos.end())
  67. return NULL;
  68. auto moduleIt = pluginIt->second.find(moduleSlug);
  69. if (moduleIt == pluginIt->second.end())
  70. return NULL;
  71. return &moduleIt->second;
  72. }
  73. bool isModuleWhitelisted(const std::string& pluginSlug, const std::string& moduleSlug) {
  74. auto pluginIt = moduleWhitelist.find(pluginSlug);
  75. // All modules in a plugin are visible if plugin set is empty.
  76. if (pluginIt == moduleWhitelist.end())
  77. return true;
  78. // All modules in a plugin are visible if plugin set is subscribed.
  79. const PluginWhitelist& plugin = pluginIt->second;
  80. if (plugin.subscribed)
  81. return true;
  82. // Check if plugin whitelist contains module
  83. auto moduleIt = plugin.moduleSlugs.find(moduleSlug);
  84. if (moduleIt == plugin.moduleSlugs.end())
  85. return false;
  86. return true;
  87. }
  88. void init() {
  89. settingsPath = asset::user("settings.json");
  90. }
  91. void destroy() {
  92. if (pluginSettingsJ) {
  93. json_decref(pluginSettingsJ);
  94. pluginSettingsJ = NULL;
  95. }
  96. }
  97. json_t* toJson() {
  98. json_t* rootJ = json_object();
  99. // Always disable safe mode when settings are saved.
  100. json_object_set_new(rootJ, "safeMode", json_boolean(false));
  101. json_object_set_new(rootJ, "token", json_string(token.c_str()));
  102. json_object_set_new(rootJ, "windowMaximized", json_boolean(windowMaximized));
  103. json_t* windowSizeJ = json_pack("[f, f]", windowSize.x, windowSize.y);
  104. json_object_set_new(rootJ, "windowSize", windowSizeJ);
  105. json_t* windowPosJ = json_pack("[f, f]", windowPos.x, windowPos.y);
  106. json_object_set_new(rootJ, "windowPos", windowPosJ);
  107. json_object_set_new(rootJ, "invertZoom", json_boolean(invertZoom));
  108. json_object_set_new(rootJ, "pixelRatio", json_real(pixelRatio));
  109. json_object_set_new(rootJ, "uiTheme", json_string(uiTheme.c_str()));
  110. json_object_set_new(rootJ, "cableOpacity", json_real(cableOpacity));
  111. json_object_set_new(rootJ, "cableTension", json_real(cableTension));
  112. json_object_set_new(rootJ, "rackBrightness", json_real(rackBrightness));
  113. json_object_set_new(rootJ, "haloBrightness", json_real(haloBrightness));
  114. json_object_set_new(rootJ, "allowCursorLock", json_boolean(allowCursorLock));
  115. json_object_set_new(rootJ, "knobMode", json_integer((int) knobMode));
  116. json_object_set_new(rootJ, "knobScroll", json_boolean(knobScroll));
  117. json_object_set_new(rootJ, "knobLinearSensitivity", json_real(knobLinearSensitivity));
  118. json_object_set_new(rootJ, "knobScrollSensitivity", json_real(knobScrollSensitivity));
  119. json_object_set_new(rootJ, "sampleRate", json_real(sampleRate));
  120. json_object_set_new(rootJ, "threadCount", json_integer(threadCount));
  121. json_object_set_new(rootJ, "tooltips", json_boolean(tooltips));
  122. json_object_set_new(rootJ, "cpuMeter", json_boolean(cpuMeter));
  123. json_object_set_new(rootJ, "lockModules", json_boolean(lockModules));
  124. json_object_set_new(rootJ, "squeezeModules", json_boolean(squeezeModules));
  125. json_object_set_new(rootJ, "frameRateLimit", json_real(frameRateLimit));
  126. json_object_set_new(rootJ, "autosaveInterval", json_real(autosaveInterval));
  127. if (skipLoadOnLaunch)
  128. json_object_set_new(rootJ, "skipLoadOnLaunch", json_boolean(true));
  129. json_t* recentPatchPathsJ = json_array();
  130. for (const std::string& path : recentPatchPaths) {
  131. json_array_append_new(recentPatchPathsJ, json_string(path.c_str()));
  132. }
  133. json_object_set_new(rootJ, "recentPatchPaths", recentPatchPathsJ);
  134. json_t* cableColorsJ = json_array();
  135. for (NVGcolor cableColor : cableColors) {
  136. std::string colorStr = color::toHexString(cableColor);
  137. json_array_append_new(cableColorsJ, json_string(colorStr.c_str()));
  138. }
  139. json_object_set_new(rootJ, "cableColors", cableColorsJ);
  140. json_object_set_new(rootJ, "autoCheckUpdates", json_boolean(autoCheckUpdates));
  141. json_object_set_new(rootJ, "showTipsOnLaunch", json_boolean(showTipsOnLaunch));
  142. json_object_set_new(rootJ, "tipIndex", json_integer(tipIndex));
  143. json_object_set_new(rootJ, "browserSort", json_integer((int) browserSort));
  144. json_object_set_new(rootJ, "browserZoom", json_real(browserZoom));
  145. // Merge pluginSettings instead of replace so plugins that fail to load don't cause their settings to be deleted.
  146. if (!pluginSettingsJ)
  147. pluginSettingsJ = json_object();
  148. plugin::settingsMergeJson(pluginSettingsJ);
  149. // Don't use *_set_new() here because we need to keep the reference to pluginSettingsJ.
  150. json_object_set(rootJ, "pluginSettings", pluginSettingsJ);
  151. // moduleInfos
  152. json_t* moduleInfosJ = json_object();
  153. for (const auto& pluginPair : moduleInfos) {
  154. json_t* pluginJ = json_object();
  155. for (const auto& modulePair : pluginPair.second) {
  156. const ModuleInfo& m = modulePair.second;
  157. json_t* moduleJ = json_object();
  158. {
  159. // To make setting.json smaller, only set properties if not default values.
  160. if (!m.enabled)
  161. json_object_set_new(moduleJ, "enabled", json_boolean(m.enabled));
  162. if (m.favorite)
  163. json_object_set_new(moduleJ, "favorite", json_boolean(m.favorite));
  164. if (m.added > 0)
  165. json_object_set_new(moduleJ, "added", json_integer(m.added));
  166. if (std::isfinite(m.lastAdded))
  167. json_object_set_new(moduleJ, "lastAdded", json_real(m.lastAdded));
  168. }
  169. if (json_object_size(moduleJ))
  170. json_object_set_new(pluginJ, modulePair.first.c_str(), moduleJ);
  171. else
  172. json_decref(moduleJ);
  173. }
  174. if (json_object_size(pluginJ))
  175. json_object_set_new(moduleInfosJ, pluginPair.first.c_str(), pluginJ);
  176. else
  177. json_decref(pluginJ);
  178. }
  179. json_object_set_new(rootJ, "moduleInfos", moduleInfosJ);
  180. // moduleWhitelist
  181. json_t* moduleWhitelistJ = json_object();
  182. for (const auto& pluginPair : moduleWhitelist) {
  183. const PluginWhitelist& plugin = pluginPair.second;
  184. json_t* pluginJ;
  185. // If plugin is subscribed, set to true, otherwise an array of module slugs.
  186. if (plugin.subscribed) {
  187. pluginJ = json_true();
  188. }
  189. else {
  190. pluginJ = json_array();
  191. for (const std::string& moduleSlug : plugin.moduleSlugs) {
  192. json_array_append_new(pluginJ, json_stringn(moduleSlug.c_str(), moduleSlug.size()));
  193. }
  194. }
  195. json_object_set_new(moduleWhitelistJ, pluginPair.first.c_str(), pluginJ);
  196. }
  197. json_object_set_new(rootJ, "moduleWhitelist", moduleWhitelistJ);
  198. return rootJ;
  199. }
  200. void fromJson(json_t* rootJ) {
  201. json_t* safeModeJ = json_object_get(rootJ, "safeMode");
  202. if (safeModeJ) {
  203. // If safe mode is enabled (e.g. by command line flag), don't disable it when loading.
  204. if (json_boolean_value(safeModeJ))
  205. safeMode = true;
  206. }
  207. json_t* tokenJ = json_object_get(rootJ, "token");
  208. if (tokenJ)
  209. token = json_string_value(tokenJ);
  210. json_t* windowMaximizedJ = json_object_get(rootJ, "windowMaximized");
  211. if (windowMaximizedJ)
  212. windowMaximized = json_boolean_value(windowMaximizedJ);
  213. json_t* windowSizeJ = json_object_get(rootJ, "windowSize");
  214. if (windowSizeJ) {
  215. double x, y;
  216. json_unpack(windowSizeJ, "[F, F]", &x, &y);
  217. windowSize = math::Vec(x, y);
  218. }
  219. json_t* windowPosJ = json_object_get(rootJ, "windowPos");
  220. if (windowPosJ) {
  221. double x, y;
  222. json_unpack(windowPosJ, "[F, F]", &x, &y);
  223. windowPos = math::Vec(x, y);
  224. }
  225. json_t* invertZoomJ = json_object_get(rootJ, "invertZoom");
  226. if (invertZoomJ)
  227. invertZoom = json_boolean_value(invertZoomJ);
  228. json_t* pixelRatioJ = json_object_get(rootJ, "pixelRatio");
  229. if (pixelRatioJ)
  230. pixelRatio = json_number_value(pixelRatioJ);
  231. json_t* uiThemeJ = json_object_get(rootJ, "uiTheme");
  232. if (uiThemeJ)
  233. uiTheme = json_string_value(uiThemeJ);
  234. json_t* cableOpacityJ = json_object_get(rootJ, "cableOpacity");
  235. if (cableOpacityJ)
  236. cableOpacity = json_number_value(cableOpacityJ);
  237. json_t* cableTensionJ = json_object_get(rootJ, "cableTension");
  238. if (cableTensionJ)
  239. cableTension = json_number_value(cableTensionJ);
  240. json_t* rackBrightnessJ = json_object_get(rootJ, "rackBrightness");
  241. if (rackBrightnessJ)
  242. rackBrightness = json_number_value(rackBrightnessJ);
  243. json_t* haloBrightnessJ = json_object_get(rootJ, "haloBrightness");
  244. if (haloBrightnessJ)
  245. haloBrightness = json_number_value(haloBrightnessJ);
  246. json_t* allowCursorLockJ = json_object_get(rootJ, "allowCursorLock");
  247. if (allowCursorLockJ)
  248. allowCursorLock = json_boolean_value(allowCursorLockJ);
  249. json_t* knobModeJ = json_object_get(rootJ, "knobMode");
  250. if (knobModeJ)
  251. knobMode = (KnobMode) json_integer_value(knobModeJ);
  252. json_t* knobScrollJ = json_object_get(rootJ, "knobScroll");
  253. if (knobScrollJ)
  254. knobScroll = json_boolean_value(knobScrollJ);
  255. json_t* knobLinearSensitivityJ = json_object_get(rootJ, "knobLinearSensitivity");
  256. if (knobLinearSensitivityJ)
  257. knobLinearSensitivity = json_number_value(knobLinearSensitivityJ);
  258. json_t* knobScrollSensitivityJ = json_object_get(rootJ, "knobScrollSensitivity");
  259. if (knobScrollSensitivityJ)
  260. knobScrollSensitivity = json_number_value(knobScrollSensitivityJ);
  261. json_t* sampleRateJ = json_object_get(rootJ, "sampleRate");
  262. if (sampleRateJ)
  263. sampleRate = json_number_value(sampleRateJ);
  264. json_t* threadCountJ = json_object_get(rootJ, "threadCount");
  265. if (threadCountJ)
  266. threadCount = json_integer_value(threadCountJ);
  267. json_t* tooltipsJ = json_object_get(rootJ, "tooltips");
  268. if (tooltipsJ)
  269. tooltips = json_boolean_value(tooltipsJ);
  270. json_t* cpuMeterJ = json_object_get(rootJ, "cpuMeter");
  271. if (cpuMeterJ)
  272. cpuMeter = json_boolean_value(cpuMeterJ);
  273. json_t* lockModulesJ = json_object_get(rootJ, "lockModules");
  274. if (lockModulesJ)
  275. lockModules = json_boolean_value(lockModulesJ);
  276. json_t* squeezeModulesJ = json_object_get(rootJ, "squeezeModules");
  277. if (squeezeModulesJ)
  278. squeezeModules = json_boolean_value(squeezeModulesJ);
  279. // Legacy setting in Rack <2.2
  280. json_t* frameSwapIntervalJ = json_object_get(rootJ, "frameSwapInterval");
  281. if (frameSwapIntervalJ) {
  282. // Assume 60 Hz monitor refresh rate.
  283. int frameSwapInterval = json_integer_value(frameSwapIntervalJ);
  284. if (frameSwapInterval > 0)
  285. frameRateLimit = 60.f / frameSwapInterval;
  286. else
  287. frameRateLimit = 0.f;
  288. }
  289. json_t* frameRateLimitJ = json_object_get(rootJ, "frameRateLimit");
  290. if (frameRateLimitJ)
  291. frameRateLimit = json_number_value(frameRateLimitJ);
  292. json_t* autosaveIntervalJ = json_object_get(rootJ, "autosaveInterval");
  293. if (autosaveIntervalJ)
  294. autosaveInterval = json_number_value(autosaveIntervalJ);
  295. json_t* skipLoadOnLaunchJ = json_object_get(rootJ, "skipLoadOnLaunch");
  296. if (skipLoadOnLaunchJ)
  297. skipLoadOnLaunch = json_boolean_value(skipLoadOnLaunchJ);
  298. recentPatchPaths.clear();
  299. json_t* recentPatchPathsJ = json_object_get(rootJ, "recentPatchPaths");
  300. if (recentPatchPathsJ) {
  301. size_t i;
  302. json_t* pathJ;
  303. json_array_foreach(recentPatchPathsJ, i, pathJ) {
  304. std::string path = json_string_value(pathJ);
  305. recentPatchPaths.push_back(path);
  306. }
  307. }
  308. cableColors.clear();
  309. json_t* cableColorsJ = json_object_get(rootJ, "cableColors");
  310. if (cableColorsJ) {
  311. size_t i;
  312. json_t* cableColorJ;
  313. json_array_foreach(cableColorsJ, i, cableColorJ) {
  314. std::string colorStr = json_string_value(cableColorJ);
  315. cableColors.push_back(color::fromHexString(colorStr));
  316. }
  317. }
  318. json_t* autoCheckUpdatesJ = json_object_get(rootJ, "autoCheckUpdates");
  319. if (autoCheckUpdatesJ)
  320. autoCheckUpdates = json_boolean_value(autoCheckUpdatesJ);
  321. json_t* showTipsOnLaunchJ = json_object_get(rootJ, "showTipsOnLaunch");
  322. if (showTipsOnLaunchJ)
  323. showTipsOnLaunch = json_boolean_value(showTipsOnLaunchJ);
  324. json_t* tipIndexJ = json_object_get(rootJ, "tipIndex");
  325. if (tipIndexJ)
  326. tipIndex = json_integer_value(tipIndexJ);
  327. json_t* browserSortJ = json_object_get(rootJ, "browserSort");
  328. if (browserSortJ)
  329. browserSort = (BrowserSort) json_integer_value(browserSortJ);
  330. json_t* browserZoomJ = json_object_get(rootJ, "browserZoom");
  331. if (browserZoomJ)
  332. browserZoom = json_number_value(browserZoomJ);
  333. // Delete previous pluginSettings object
  334. if (pluginSettingsJ) {
  335. json_decref(pluginSettingsJ);
  336. pluginSettingsJ = NULL;
  337. }
  338. pluginSettingsJ = json_object_get(rootJ, "pluginSettings");
  339. if (pluginSettingsJ)
  340. json_incref(pluginSettingsJ);
  341. moduleInfos.clear();
  342. json_t* moduleInfosJ = json_object_get(rootJ, "moduleInfos");
  343. if (moduleInfosJ) {
  344. const char* pluginSlug;
  345. json_t* pluginJ;
  346. json_object_foreach(moduleInfosJ, pluginSlug, pluginJ) {
  347. const char* moduleSlug;
  348. json_t* moduleJ;
  349. json_object_foreach(pluginJ, moduleSlug, moduleJ) {
  350. ModuleInfo m;
  351. json_t* enabledJ = json_object_get(moduleJ, "enabled");
  352. if (enabledJ)
  353. m.enabled = json_boolean_value(enabledJ);
  354. json_t* favoriteJ = json_object_get(moduleJ, "favorite");
  355. if (favoriteJ)
  356. m.favorite = json_boolean_value(favoriteJ);
  357. json_t* addedJ = json_object_get(moduleJ, "added");
  358. if (addedJ)
  359. m.added = json_integer_value(addedJ);
  360. json_t* lastAddedJ = json_object_get(moduleJ, "lastAdded");
  361. if (lastAddedJ)
  362. m.lastAdded = json_number_value(lastAddedJ);
  363. moduleInfos[pluginSlug][moduleSlug] = m;
  364. }
  365. }
  366. }
  367. moduleWhitelist.clear();
  368. json_t* moduleWhitelistJ = json_object_get(rootJ, "moduleWhitelist");
  369. if (moduleWhitelistJ) {
  370. const char* pluginSlug;
  371. json_t* pluginJ;
  372. json_object_foreach(moduleWhitelistJ, pluginSlug, pluginJ) {
  373. auto& plugin = moduleWhitelist[pluginSlug];
  374. if (json_is_true(pluginJ)) {
  375. plugin.subscribed = true;
  376. continue;
  377. }
  378. size_t moduleIndex;
  379. json_t* moduleJ;
  380. json_array_foreach(pluginJ, moduleIndex, moduleJ) {
  381. std::string moduleSlug = json_string_value(moduleJ);
  382. plugin.moduleSlugs.insert(moduleSlug);
  383. }
  384. }
  385. }
  386. }
  387. void save(std::string path) {
  388. if (path.empty())
  389. path = settingsPath;
  390. INFO("Saving settings %s", path.c_str());
  391. json_t* rootJ = toJson();
  392. if (!rootJ)
  393. return;
  394. DEFER({json_decref(rootJ);});
  395. std::string tmpPath = path + ".tmp";
  396. FILE* file = std::fopen(tmpPath.c_str(), "w");
  397. if (!file)
  398. return;
  399. json_dumpf(rootJ, file, JSON_INDENT(2));
  400. std::fclose(file);
  401. system::remove(path);
  402. system::rename(tmpPath, path);
  403. }
  404. void load(std::string path) {
  405. if (path.empty())
  406. path = settingsPath;
  407. INFO("Loading settings %s", path.c_str());
  408. FILE* file = std::fopen(path.c_str(), "r");
  409. if (!file)
  410. return;
  411. DEFER({std::fclose(file);});
  412. json_error_t error;
  413. json_t* rootJ = json_loadf(file, 0, &error);
  414. if (!rootJ)
  415. throw Exception("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text);
  416. DEFER({json_decref(rootJ);});
  417. fromJson(rootJ);
  418. }
  419. } // namespace settings
  420. } // namespace rack