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.

613 lines
19KB

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