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.

245 lines
6.3KB

  1. #include <common.hpp>
  2. #include <random.hpp>
  3. #include <asset.hpp>
  4. #include <audio.hpp>
  5. #include <rtaudio.hpp>
  6. #include <midi.hpp>
  7. #include <rtmidi.hpp>
  8. #include <keyboard.hpp>
  9. #include <gamepad.hpp>
  10. #include <settings.hpp>
  11. #include <engine/Engine.hpp>
  12. #include <app/common.hpp>
  13. #include <app/Scene.hpp>
  14. #include <plugin.hpp>
  15. #include <context.hpp>
  16. #include <patch.hpp>
  17. #include <ui.hpp>
  18. #include <system.hpp>
  19. #include <string.hpp>
  20. #include <updater.hpp>
  21. #include <network.hpp>
  22. #include <osdialog.h>
  23. #include <thread>
  24. #include <unistd.h> // for getopt
  25. #include <signal.h> // for signal
  26. #if defined ARCH_WIN
  27. #include <windows.h> // for CreateMutex
  28. #endif
  29. #if defined ARCH_MAC
  30. #define GLFW_EXPOSE_NATIVE_COCOA
  31. #include <GLFW/glfw3native.h> // for glfwGetOpenedFilenames()
  32. #endif
  33. using namespace rack;
  34. static void fatalSignalHandler(int sig) {
  35. // Ignore this signal to avoid recursion.
  36. signal(sig, NULL);
  37. // Ignore abort() since we call it below.
  38. signal(SIGABRT, NULL);
  39. FATAL("Fatal signal %d. Stack trace:\n%s", sig, system::getStackTrace().c_str());
  40. // This might fail because we might not be in the main thread.
  41. // But oh well, we're crashing anyway.
  42. std::string text = APP_NAME + " has crashed. See " + asset::logPath + " for details.";
  43. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, text.c_str());
  44. abort();
  45. }
  46. int main(int argc, char* argv[]) {
  47. #if defined ARCH_WIN
  48. // Windows global mutex to prevent multiple instances
  49. // Handle will be closed by Windows when the process ends
  50. HANDLE instanceMutex = CreateMutexW(NULL, true, string::toWstring(APP_NAME).c_str());
  51. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  52. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Rack is already running. Multiple Rack instances are not supported.");
  53. exit(1);
  54. }
  55. (void) instanceMutex;
  56. // Don't display "Assertion failed!" dialog message.
  57. _set_error_mode(_OUT_TO_STDERR);
  58. #endif
  59. std::string patchPath;
  60. bool screenshot = false;
  61. float screenshotZoom = 1.f;
  62. // Parse command line arguments
  63. int c;
  64. opterr = 0;
  65. while ((c = getopt(argc, argv, "dht:s:u:p:")) != -1) {
  66. switch (c) {
  67. case 'd': {
  68. settings::devMode = true;
  69. } break;
  70. case 'h': {
  71. settings::headless = true;
  72. } break;
  73. case 't': {
  74. screenshot = true;
  75. // If parsing number failed, use default value
  76. std::sscanf(optarg, "%f", &screenshotZoom);
  77. } break;
  78. case 's': {
  79. asset::systemDir = optarg;
  80. } break;
  81. case 'u': {
  82. asset::userDir = optarg;
  83. } break;
  84. // Mac "app translocation" passes a nonsense -psn_... flag, so -p is reserved.
  85. case 'p': break;
  86. default: break;
  87. }
  88. }
  89. if (optind < argc) {
  90. patchPath = argv[optind];
  91. }
  92. // Initialize environment
  93. asset::init();
  94. bool loggerWasTruncated = logger::isTruncated();
  95. logger::init();
  96. // We can now install a signal handler and log the output
  97. if (!settings::devMode) {
  98. signal(SIGABRT, fatalSignalHandler);
  99. signal(SIGFPE, fatalSignalHandler);
  100. signal(SIGILL, fatalSignalHandler);
  101. signal(SIGSEGV, fatalSignalHandler);
  102. signal(SIGTERM, fatalSignalHandler);
  103. }
  104. // Log environment
  105. INFO("%s v%s", APP_NAME.c_str(), APP_VERSION.c_str());
  106. INFO("%s", system::getOperatingSystemInfo().c_str());
  107. std::string argsList;
  108. for (int i = 0; i < argc; i++) {
  109. argsList += argv[i];
  110. argsList += " ";
  111. }
  112. INFO("Args: %s", argsList.c_str());
  113. if (settings::devMode)
  114. INFO("Development mode");
  115. INFO("System directory: %s", asset::systemDir.c_str());
  116. INFO("User directory: %s", asset::userDir.c_str());
  117. #if defined ARCH_MAC
  118. INFO("Bundle path: %s", asset::bundlePath.c_str());
  119. #endif
  120. // Load settings
  121. try {
  122. settings::load(asset::settingsPath);
  123. }
  124. catch (Exception& e) {
  125. std::string msg = e.what();
  126. msg += "\n\nReset settings to default?";
  127. if (!osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK_CANCEL, msg.c_str())) {
  128. exit(1);
  129. }
  130. }
  131. // Check existence of the system res/ directory
  132. std::string resDir = asset::system("res");
  133. if (!system::isDirectory(resDir)) {
  134. std::string message = string::f("Rack's resource directory \"%s\" does not exist. Make sure Rack is correctly installed and launched.", resDir.c_str());
  135. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, message.c_str());
  136. exit(1);
  137. }
  138. INFO("Initializing environment");
  139. random::init();
  140. network::init();
  141. audio::init();
  142. rtaudioInit();
  143. midi::init();
  144. rtmidiInit();
  145. keyboard::init();
  146. gamepad::init();
  147. plugin::init();
  148. updater::init();
  149. if (!settings::headless) {
  150. ui::init();
  151. windowInit();
  152. }
  153. // Initialize context
  154. INFO("Initializing context");
  155. contextSet(new Context);
  156. APP->init();
  157. // On Mac, use a hacked-in GLFW addition to get the launched path.
  158. #if defined ARCH_MAC
  159. // For some reason, launching from the command line sets glfwGetOpenedFilenames(), so make sure we're running the app bundle.
  160. if (asset::bundlePath != "") {
  161. const char* const* openedFilenames = glfwGetOpenedFilenames();
  162. if (openedFilenames && openedFilenames[0]) {
  163. patchPath = openedFilenames[0];
  164. }
  165. }
  166. #endif
  167. // Initialize patch
  168. if (loggerWasTruncated && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack crashed during the last session, possibly due to a buggy module in your patch. Clear your patch and start over?")) {
  169. // This is the default state, but just call clear() to be explicit.
  170. APP->patch->clear();
  171. }
  172. else if (patchPath != "") {
  173. APP->patch->loadAction(patchPath);
  174. }
  175. else {
  176. APP->patch->path = settings::patchPath;
  177. APP->patch->loadAutosave();
  178. }
  179. // Run context
  180. if (settings::headless) {
  181. printf("Press enter to exit.\n");
  182. getchar();
  183. }
  184. else if (screenshot) {
  185. INFO("Taking screenshots of all modules at %gx zoom", screenshotZoom);
  186. APP->window->screenshot(screenshotZoom);
  187. }
  188. else {
  189. INFO("Running window");
  190. APP->window->run();
  191. INFO("Stopped window");
  192. }
  193. // Destroy context
  194. if (!settings::headless) {
  195. APP->patch->saveAutosave();
  196. // TODO If Rack crashes, the settings' patch path won't be changed although the autosave will. This could possibly cause the user to overwrite their old patch with the unrelated autosave.
  197. settings::patchPath = APP->patch->path;
  198. }
  199. INFO("Destroying context");
  200. delete APP;
  201. contextSet(NULL);
  202. if (!settings::headless) {
  203. settings::save(asset::settingsPath);
  204. }
  205. // Destroy environment
  206. INFO("Destroying environment");
  207. if (!settings::headless) {
  208. windowDestroy();
  209. ui::destroy();
  210. }
  211. plugin::destroy();
  212. midi::destroy();
  213. audio::destroy();
  214. INFO("Destroying logger");
  215. logger::destroy();
  216. return 0;
  217. }