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.

416 lines
13KB

  1. #include <jansson.h>
  2. #include <settings.hpp>
  3. #include <Window.hpp>
  4. #include <plugin.hpp>
  5. #include <app/Scene.hpp>
  6. #include <app/ModuleBrowser.hpp>
  7. #include <engine/Engine.hpp>
  8. #include <context.hpp>
  9. #include <patch.hpp>
  10. #include <asset.hpp>
  11. namespace rack {
  12. namespace settings {
  13. std::string settingsPath;
  14. bool devMode = false;
  15. bool headless = false;
  16. bool isPlugin = false;
  17. std::string token;
  18. bool windowMaximized = false;
  19. math::Vec windowSize = math::Vec(1024, 768);
  20. math::Vec windowPos = math::Vec(NAN, NAN);
  21. float zoom = 0.0;
  22. bool invertZoom = false;
  23. float cableOpacity = 0.5;
  24. float cableTension = 0.5;
  25. float rackBrightness = 1.0;
  26. float haloBrightness = 0.25;
  27. bool allowCursorLock = true;
  28. KnobMode knobMode = KNOB_MODE_LINEAR;
  29. bool knobScroll = false;
  30. float knobLinearSensitivity = 0.001f;
  31. float knobScrollSensitivity = 0.001f;
  32. float sampleRate = 0;
  33. int threadCount = 1;
  34. bool tooltips = true;
  35. bool cpuMeter = false;
  36. bool lockModules = false;
  37. #if defined ARCH_MAC
  38. // Most Mac GPUs can't handle rendering the screen every frame, so use ~30 Hz by default.
  39. int frameSwapInterval = 2;
  40. #else
  41. int frameSwapInterval = 1;
  42. #endif
  43. float autosaveInterval = 15.0;
  44. bool skipLoadOnLaunch = false;
  45. std::list<std::string> recentPatchPaths;
  46. std::vector<NVGcolor> cableColors = {
  47. color::fromHexString("#fc2d5aff"), // red
  48. color::fromHexString("#f9b130ff"), // orange
  49. // color::fromHexString("#f7da31ff"), // yellow
  50. color::fromHexString("#67c12dff"), // green
  51. color::fromHexString("#0f8df4ff"), // blue
  52. color::fromHexString("#8c1889ff"), // purple
  53. };
  54. bool autoCheckUpdates = true;
  55. bool showTipsOnLaunch = true;
  56. int tipIndex = -1;
  57. ModuleBrowserSort moduleBrowserSort = MODULE_BROWSER_SORT_UPDATED;
  58. float moduleBrowserZoom = -1.f;
  59. std::map<std::string, std::set<std::string>> moduleWhitelist = {};
  60. std::map<std::string, std::map<std::string, ModuleUsage>> moduleUsages = {};
  61. void init() {
  62. if (devMode) {
  63. settingsPath = asset::user("settings.json");
  64. }
  65. else {
  66. settingsPath = asset::user("settings-v" + APP_VERSION_MAJOR + ".json");
  67. }
  68. }
  69. ModuleUsage* getModuleUsage(const std::string& pluginSlug, const std::string& moduleSlug) {
  70. auto it1 = moduleUsages.find(pluginSlug);
  71. if (it1 == moduleUsages.end())
  72. return NULL;
  73. auto it2 = it1->second.find(moduleSlug);
  74. if (it2 == it1->second.end())
  75. return NULL;
  76. return &it2->second;
  77. }
  78. json_t* toJson() {
  79. json_t* rootJ = json_object();
  80. json_object_set_new(rootJ, "token", json_string(token.c_str()));
  81. json_object_set_new(rootJ, "windowMaximized", json_boolean(windowMaximized));
  82. json_t* windowSizeJ = json_pack("[f, f]", windowSize.x, windowSize.y);
  83. json_object_set_new(rootJ, "windowSize", windowSizeJ);
  84. json_t* windowPosJ = json_pack("[f, f]", windowPos.x, windowPos.y);
  85. json_object_set_new(rootJ, "windowPos", windowPosJ);
  86. json_object_set_new(rootJ, "zoom", json_real(zoom));
  87. json_object_set_new(rootJ, "invertZoom", json_boolean(invertZoom));
  88. json_object_set_new(rootJ, "cableOpacity", json_real(cableOpacity));
  89. json_object_set_new(rootJ, "cableTension", json_real(cableTension));
  90. json_object_set_new(rootJ, "rackBrightness", json_real(rackBrightness));
  91. json_object_set_new(rootJ, "haloBrightness", json_real(haloBrightness));
  92. json_object_set_new(rootJ, "allowCursorLock", json_boolean(allowCursorLock));
  93. json_object_set_new(rootJ, "knobMode", json_integer((int) knobMode));
  94. json_object_set_new(rootJ, "knobScroll", json_boolean(knobScroll));
  95. json_object_set_new(rootJ, "knobLinearSensitivity", json_real(knobLinearSensitivity));
  96. json_object_set_new(rootJ, "knobScrollSensitivity", json_real(knobScrollSensitivity));
  97. json_object_set_new(rootJ, "sampleRate", json_real(sampleRate));
  98. json_object_set_new(rootJ, "threadCount", json_integer(threadCount));
  99. json_object_set_new(rootJ, "tooltips", json_boolean(tooltips));
  100. json_object_set_new(rootJ, "cpuMeter", json_boolean(cpuMeter));
  101. json_object_set_new(rootJ, "lockModules", json_boolean(lockModules));
  102. json_object_set_new(rootJ, "frameSwapInterval", json_integer(frameSwapInterval));
  103. json_object_set_new(rootJ, "autosaveInterval", json_real(autosaveInterval));
  104. if (skipLoadOnLaunch)
  105. json_object_set_new(rootJ, "skipLoadOnLaunch", json_boolean(true));
  106. json_t* recentPatchPathsJ = json_array();
  107. for (const std::string& path : recentPatchPaths) {
  108. json_array_append_new(recentPatchPathsJ, json_string(path.c_str()));
  109. }
  110. json_object_set_new(rootJ, "recentPatchPaths", recentPatchPathsJ);
  111. json_t* cableColorsJ = json_array();
  112. for (NVGcolor cableColor : cableColors) {
  113. std::string colorStr = color::toHexString(cableColor);
  114. json_array_append_new(cableColorsJ, json_string(colorStr.c_str()));
  115. }
  116. json_object_set_new(rootJ, "cableColors", cableColorsJ);
  117. json_object_set_new(rootJ, "autoCheckUpdates", json_boolean(autoCheckUpdates));
  118. json_object_set_new(rootJ, "showTipsOnLaunch", json_boolean(showTipsOnLaunch));
  119. json_object_set_new(rootJ, "tipIndex", json_integer(tipIndex));
  120. json_object_set_new(rootJ, "moduleBrowserSort", json_integer((int) moduleBrowserSort));
  121. json_object_set_new(rootJ, "moduleBrowserZoom", json_real(moduleBrowserZoom));
  122. json_t* moduleWhitelistJ = json_object();
  123. for (const auto& pair : moduleWhitelist) {
  124. json_t* moduleSlugsJ = json_array();
  125. for (const std::string& moduleSlug : pair.second) {
  126. json_array_append_new(moduleSlugsJ, json_string(moduleSlug.c_str()));
  127. }
  128. json_object_set_new(moduleWhitelistJ, pair.first.c_str(), moduleSlugsJ);
  129. }
  130. json_object_set_new(rootJ, "moduleWhitelist", moduleWhitelistJ);
  131. json_t* moduleUsagesJ = json_object();
  132. for (const auto& pair : moduleUsages) {
  133. json_t* modulesJ = json_object();
  134. for (const auto& modulePair : pair.second) {
  135. const ModuleUsage& mu = modulePair.second;
  136. if (mu.count <= 0 || !std::isfinite(mu.lastTime))
  137. continue;
  138. json_t* moduleUsagesJ = json_object();
  139. {
  140. json_object_set_new(moduleUsagesJ, "count", json_integer(mu.count));
  141. json_object_set_new(moduleUsagesJ, "lastTime", json_real(mu.lastTime));
  142. }
  143. json_object_set_new(modulesJ, modulePair.first.c_str(), moduleUsagesJ);
  144. }
  145. json_object_set_new(moduleUsagesJ, pair.first.c_str(), modulesJ);
  146. }
  147. json_object_set_new(rootJ, "moduleUsages", moduleUsagesJ);
  148. return rootJ;
  149. }
  150. void fromJson(json_t* rootJ) {
  151. json_t* tokenJ = json_object_get(rootJ, "token");
  152. if (tokenJ)
  153. token = json_string_value(tokenJ);
  154. json_t* windowMaximizedJ = json_object_get(rootJ, "windowMaximized");
  155. if (windowMaximizedJ)
  156. windowMaximized = json_boolean_value(windowMaximizedJ);
  157. json_t* windowSizeJ = json_object_get(rootJ, "windowSize");
  158. if (windowSizeJ) {
  159. double x, y;
  160. json_unpack(windowSizeJ, "[F, F]", &x, &y);
  161. windowSize = math::Vec(x, y);
  162. }
  163. json_t* windowPosJ = json_object_get(rootJ, "windowPos");
  164. if (windowPosJ) {
  165. double x, y;
  166. json_unpack(windowPosJ, "[F, F]", &x, &y);
  167. windowPos = math::Vec(x, y);
  168. }
  169. json_t* zoomJ = json_object_get(rootJ, "zoom");
  170. if (zoomJ)
  171. zoom = json_number_value(zoomJ);
  172. json_t* invertZoomJ = json_object_get(rootJ, "invertZoom");
  173. if (invertZoomJ)
  174. invertZoom = json_boolean_value(invertZoomJ);
  175. json_t* cableOpacityJ = json_object_get(rootJ, "cableOpacity");
  176. if (cableOpacityJ)
  177. cableOpacity = json_number_value(cableOpacityJ);
  178. json_t* cableTensionJ = json_object_get(rootJ, "cableTension");
  179. if (cableTensionJ)
  180. cableTension = json_number_value(cableTensionJ);
  181. json_t* rackBrightnessJ = json_object_get(rootJ, "rackBrightness");
  182. if (rackBrightnessJ)
  183. rackBrightness = json_number_value(rackBrightnessJ);
  184. json_t* haloBrightnessJ = json_object_get(rootJ, "haloBrightness");
  185. if (haloBrightnessJ)
  186. haloBrightness = json_number_value(haloBrightnessJ);
  187. json_t* allowCursorLockJ = json_object_get(rootJ, "allowCursorLock");
  188. if (allowCursorLockJ)
  189. allowCursorLock = json_boolean_value(allowCursorLockJ);
  190. json_t* knobModeJ = json_object_get(rootJ, "knobMode");
  191. if (knobModeJ)
  192. knobMode = (KnobMode) json_integer_value(knobModeJ);
  193. json_t* knobScrollJ = json_object_get(rootJ, "knobScroll");
  194. if (knobScrollJ)
  195. knobScroll = json_boolean_value(knobScrollJ);
  196. json_t* knobLinearSensitivityJ = json_object_get(rootJ, "knobLinearSensitivity");
  197. if (knobLinearSensitivityJ)
  198. knobLinearSensitivity = json_number_value(knobLinearSensitivityJ);
  199. json_t* knobScrollSensitivityJ = json_object_get(rootJ, "knobScrollSensitivity");
  200. if (knobScrollSensitivityJ)
  201. knobScrollSensitivity = json_number_value(knobScrollSensitivityJ);
  202. json_t* sampleRateJ = json_object_get(rootJ, "sampleRate");
  203. if (sampleRateJ)
  204. sampleRate = json_number_value(sampleRateJ);
  205. json_t* threadCountJ = json_object_get(rootJ, "threadCount");
  206. if (threadCountJ)
  207. threadCount = json_integer_value(threadCountJ);
  208. json_t* tooltipsJ = json_object_get(rootJ, "tooltips");
  209. if (tooltipsJ)
  210. tooltips = json_boolean_value(tooltipsJ);
  211. json_t* cpuMeterJ = json_object_get(rootJ, "cpuMeter");
  212. if (cpuMeterJ)
  213. cpuMeter = json_boolean_value(cpuMeterJ);
  214. json_t* lockModulesJ = json_object_get(rootJ, "lockModules");
  215. if (lockModulesJ)
  216. lockModules = json_boolean_value(lockModulesJ);
  217. json_t* frameSwapIntervalJ = json_object_get(rootJ, "frameSwapInterval");
  218. if (frameSwapIntervalJ)
  219. frameSwapInterval = json_integer_value(frameSwapIntervalJ);
  220. json_t* autosaveIntervalJ = json_object_get(rootJ, "autosaveInterval");
  221. if (autosaveIntervalJ)
  222. autosaveInterval = json_number_value(autosaveIntervalJ);
  223. json_t* skipLoadOnLaunchJ = json_object_get(rootJ, "skipLoadOnLaunch");
  224. if (skipLoadOnLaunchJ)
  225. skipLoadOnLaunch = json_boolean_value(skipLoadOnLaunchJ);
  226. recentPatchPaths.clear();
  227. json_t* recentPatchPathsJ = json_object_get(rootJ, "recentPatchPaths");
  228. if (recentPatchPathsJ) {
  229. size_t i;
  230. json_t* pathJ;
  231. json_array_foreach(recentPatchPathsJ, i, pathJ) {
  232. std::string path = json_string_value(pathJ);
  233. recentPatchPaths.push_back(path);
  234. }
  235. }
  236. cableColors.clear();
  237. json_t* cableColorsJ = json_object_get(rootJ, "cableColors");
  238. if (cableColorsJ) {
  239. size_t i;
  240. json_t* cableColorJ;
  241. json_array_foreach(cableColorsJ, i, cableColorJ) {
  242. std::string colorStr = json_string_value(cableColorJ);
  243. cableColors.push_back(color::fromHexString(colorStr));
  244. }
  245. }
  246. json_t* autoCheckUpdatesJ = json_object_get(rootJ, "autoCheckUpdates");
  247. if (autoCheckUpdatesJ)
  248. autoCheckUpdates = json_boolean_value(autoCheckUpdatesJ);
  249. json_t* showTipsOnLaunchJ = json_object_get(rootJ, "showTipsOnLaunch");
  250. if (showTipsOnLaunchJ)
  251. showTipsOnLaunch = json_boolean_value(showTipsOnLaunchJ);
  252. json_t* tipIndexJ = json_object_get(rootJ, "tipIndex");
  253. if (tipIndexJ)
  254. tipIndex = json_integer_value(tipIndexJ);
  255. json_t* moduleBrowserSortJ = json_object_get(rootJ, "moduleBrowserSort");
  256. if (moduleBrowserSortJ)
  257. moduleBrowserSort = (ModuleBrowserSort) json_integer_value(moduleBrowserSortJ);
  258. json_t* moduleBrowserZoomJ = json_object_get(rootJ, "moduleBrowserZoom");
  259. if (moduleBrowserZoomJ)
  260. moduleBrowserZoom = json_number_value(moduleBrowserZoomJ);
  261. moduleWhitelist.clear();
  262. json_t* moduleWhitelistJ = json_object_get(rootJ, "moduleWhitelist");
  263. if (moduleWhitelistJ) {
  264. const char* pluginSlug;
  265. json_t* moduleSlugsJ;
  266. json_object_foreach(moduleWhitelistJ, pluginSlug, moduleSlugsJ) {
  267. auto& moduleSlugs = moduleWhitelist[pluginSlug];
  268. size_t i;
  269. json_t* moduleSlugJ;
  270. json_array_foreach(moduleSlugsJ, i, moduleSlugJ) {
  271. std::string moduleSlug = json_string_value(moduleSlugJ);
  272. moduleSlugs.insert(moduleSlug);
  273. }
  274. }
  275. }
  276. moduleUsages.clear();
  277. json_t* moduleUsagesJ = json_object_get(rootJ, "moduleUsages");
  278. if (moduleUsagesJ) {
  279. const char* pluginSlug;
  280. json_t* modulesJ;
  281. json_object_foreach(moduleUsagesJ, pluginSlug, modulesJ) {
  282. const char* moduleSlug;
  283. json_t* moduleJ;
  284. json_object_foreach(modulesJ, moduleSlug, moduleJ) {
  285. ModuleUsage mu;
  286. json_t* countJ = json_object_get(moduleJ, "count");
  287. if (countJ)
  288. mu.count = json_integer_value(countJ);
  289. json_t* lastTimeJ = json_object_get(moduleJ, "lastTime");
  290. if (lastTimeJ)
  291. mu.lastTime = json_number_value(lastTimeJ);
  292. moduleUsages[pluginSlug][moduleSlug] = mu;
  293. }
  294. }
  295. }
  296. }
  297. void save(std::string path) {
  298. if (path.empty())
  299. path = settingsPath;
  300. INFO("Saving settings %s", path.c_str());
  301. json_t* rootJ = toJson();
  302. if (!rootJ)
  303. return;
  304. FILE* file = std::fopen(path.c_str(), "w");
  305. if (!file)
  306. return;
  307. DEFER({std::fclose(file);});
  308. // 11 is enough precision to handle double UNIX time values to 0.1 seconds.
  309. json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(11));
  310. json_decref(rootJ);
  311. }
  312. void load(std::string path) {
  313. if (path.empty())
  314. path = settingsPath;
  315. INFO("Loading settings %s", path.c_str());
  316. FILE* file = std::fopen(path.c_str(), "r");
  317. if (!file)
  318. return;
  319. DEFER({std::fclose(file);});
  320. json_error_t error;
  321. json_t* rootJ = json_loadf(file, 0, &error);
  322. if (!rootJ)
  323. throw Exception("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text);
  324. fromJson(rootJ);
  325. json_decref(rootJ);
  326. }
  327. } // namespace settings
  328. } // namespace rack