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.

229 lines
5.6KB

  1. #include <arch.hpp>
  2. #if defined ARCH_MAC
  3. #include <CoreFoundation/CoreFoundation.h>
  4. #include <CoreServices/CoreServices.h>
  5. #include <pwd.h>
  6. #endif
  7. #if defined ARCH_WIN
  8. #include <Windows.h>
  9. #include <Shlobj.h>
  10. #include <Shlwapi.h>
  11. #endif
  12. #if defined ARCH_LIN
  13. #include <unistd.h>
  14. #include <sys/types.h>
  15. #include <pwd.h>
  16. #endif
  17. #include <asset.hpp>
  18. #include <system.hpp>
  19. #include <settings.hpp>
  20. #include <string.hpp>
  21. #include <plugin/Plugin.hpp>
  22. #include <engine/Module.hpp>
  23. #include <app/common.hpp>
  24. #include <osdialog.h>
  25. namespace rack {
  26. namespace asset {
  27. #if defined ARCH_MAC
  28. std::string getApplicationSupportDir();
  29. #endif
  30. static void initSystemDir() {
  31. if (!systemDir.empty())
  32. return;
  33. if (settings::devMode) {
  34. systemDir = system::getWorkingDirectory();
  35. return;
  36. }
  37. // Environment variable overrides
  38. const char* envSystem = getenv("RACK_SYSTEM_DIR");
  39. if (envSystem) {
  40. systemDir = envSystem;
  41. return;
  42. }
  43. #if defined ARCH_MAC
  44. CFBundleRef bundle = CFBundleGetMainBundle();
  45. assert(bundle);
  46. // Check if we're running as a command-line program or an app bundle.
  47. CFURLRef bundleUrl = CFBundleCopyBundleURL(bundle);
  48. // Thanks Ken Thomases! https://stackoverflow.com/a/58369256/272642
  49. CFStringRef uti;
  50. if (CFURLCopyResourcePropertyForKey(bundleUrl, kCFURLTypeIdentifierKey, &uti, NULL) && uti && UTTypeConformsTo(uti, kUTTypeApplicationBundle)) {
  51. char bundleBuf[PATH_MAX];
  52. Boolean success = CFURLGetFileSystemRepresentation(bundleUrl, TRUE, (UInt8*) bundleBuf, sizeof(bundleBuf));
  53. assert(success);
  54. bundlePath = bundleBuf;
  55. }
  56. CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(bundle);
  57. char resourcesBuf[PATH_MAX];
  58. Boolean success = CFURLGetFileSystemRepresentation(resourcesUrl, TRUE, (UInt8*) resourcesBuf, sizeof(resourcesBuf));
  59. assert(success);
  60. CFRelease(resourcesUrl);
  61. systemDir = resourcesBuf;
  62. #endif
  63. #if defined ARCH_WIN
  64. // Get path to executable
  65. wchar_t moduleBufW[MAX_PATH] = L"";
  66. DWORD length = GetModuleFileNameW(NULL, moduleBufW, LENGTHOF(moduleBufW));
  67. assert(length > 0);
  68. // Get directory of executable
  69. PathRemoveFileSpecW(moduleBufW);
  70. systemDir = string::UTF16toUTF8(moduleBufW);
  71. #endif
  72. #if defined ARCH_LIN
  73. // Use the current working directory as the default path on Linux.
  74. systemDir = system::getWorkingDirectory();
  75. #endif
  76. }
  77. static void initUserDir() {
  78. if (!userDir.empty())
  79. return;
  80. if (settings::devMode) {
  81. userDir = systemDir;
  82. return;
  83. }
  84. // Environment variable overrides
  85. const char* envUser = getenv("RACK_USER_DIR");
  86. if (envUser) {
  87. userDir = envUser;
  88. return;
  89. }
  90. #if defined ARCH_WIN
  91. // Get AppData/Local path
  92. WCHAR localBufW[MAX_PATH] = {};
  93. HRESULT localH = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, localBufW);
  94. assert(SUCCEEDED(localH));
  95. std::string localDir = string::UTF16toUTF8(localBufW);
  96. // Usually C:/Users/<username>/AppData/Local/Rack2
  97. userDir = system::join(localDir, "Rack" + APP_VERSION_MAJOR);
  98. // Get Documents path
  99. WCHAR documentsBufW[MAX_PATH] = {};
  100. HRESULT documentsH = SHGetFolderPathW(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, documentsBufW);
  101. assert(SUCCEEDED(documentsH));
  102. std::string documentsDir = string::UTF16toUTF8(documentsBufW);
  103. // Rack <2.5.0 used "My Documents/Rack2"
  104. oldUserDir = system::join(documentsDir, "Rack" + APP_VERSION_MAJOR);
  105. #endif
  106. #if defined ARCH_MAC
  107. // Usually ~/Library/Application Support/Rack2
  108. userDir = system::join(getApplicationSupportDir(), "Rack" + APP_VERSION_MAJOR);
  109. // Get home directory
  110. struct passwd* pw = getpwuid(getuid());
  111. assert(pw);
  112. std::string homeDir = pw->pw_dir;
  113. // Rack <2.5.0 used ~/Documents/Rack2
  114. oldUserDir = system::join(homeDir, "Documents", "Rack" + APP_VERSION_MAJOR);
  115. #endif
  116. #if defined ARCH_LIN
  117. // Get home path
  118. const char* homeBuf = getenv("HOME");
  119. if (!homeBuf) {
  120. struct passwd* pw = getpwuid(getuid());
  121. assert(pw);
  122. homeBuf = pw->pw_dir;
  123. }
  124. std::string homeDir = homeBuf;
  125. // Get XDG data path
  126. const char* envData = getenv("XDG_DATA_HOME");
  127. if (envData) {
  128. userDir = envData;
  129. }
  130. else {
  131. userDir = system::join(homeDir, ".local", "share");
  132. }
  133. // Usually ~/.local/share/Rack2
  134. userDir = system::join(userDir, "Rack" + APP_VERSION_MAJOR);
  135. // Rack <2.5.0 used ~/.Rack2
  136. oldUserDir = system::join(homeDir, ".Rack" + APP_VERSION_MAJOR);
  137. #endif
  138. // If userDir doesn't exist but oldUserDir does, attempt to move it and inform user.
  139. if (oldUserDir != "" && !system::isDirectory(userDir) && system::isDirectory(oldUserDir)) {
  140. if (system::rename(oldUserDir, userDir)) {
  141. std::string msg = APP_NAME + "'s user folder has been moved from";
  142. msg += "\n" + oldUserDir;
  143. msg += "\nto";
  144. msg += "\n" + userDir;
  145. osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, msg.c_str());
  146. }
  147. else {
  148. std::string msg = "Failed to move " + APP_NAME + "'s user folder from";
  149. msg += "\n" + oldUserDir;
  150. msg += "\nto";
  151. msg += "\n" + userDir;
  152. msg += "\nConsider moving this folder manually to ensure compatibility with future versions.";
  153. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, msg.c_str());
  154. // Move failed, just use the old dir instead
  155. userDir = oldUserDir;
  156. oldUserDir = "";
  157. }
  158. }
  159. else {
  160. oldUserDir = "";
  161. }
  162. // Create user dir if it doesn't exist
  163. system::createDirectory(userDir);
  164. }
  165. void init() {
  166. initSystemDir();
  167. initUserDir();
  168. }
  169. std::string system(std::string filename) {
  170. return system::join(systemDir, filename);
  171. }
  172. std::string user(std::string filename) {
  173. return system::join(userDir, filename);
  174. }
  175. std::string plugin(plugin::Plugin* plugin, std::string filename) {
  176. assert(plugin);
  177. return system::join(plugin->path, filename);
  178. }
  179. std::string systemDir;
  180. std::string userDir;
  181. std::string oldUserDir;
  182. std::string bundlePath;
  183. } // namespace asset
  184. } // namespace rack