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.

214 lines
5.1KB

  1. #include <common.hpp>
  2. #include <random.hpp>
  3. #include <asset.hpp>
  4. #include <midi.hpp>
  5. #include <rtmidi.hpp>
  6. #include <keyboard.hpp>
  7. #include <gamepad.hpp>
  8. #include <bridge.hpp>
  9. #include <settings.hpp>
  10. #include <engine/Engine.hpp>
  11. #include <app/common.hpp>
  12. #include <app/Scene.hpp>
  13. #include <plugin.hpp>
  14. #include <app.hpp>
  15. #include <patch.hpp>
  16. #include <ui.hpp>
  17. #include <system.hpp>
  18. #include <string.hpp>
  19. #include <updater.hpp>
  20. #include <osdialog.h>
  21. #include <thread>
  22. #include <unistd.h> // for getopt
  23. #include <signal.h> // for signal
  24. #if defined ARCH_WIN
  25. #include <windows.h> // for CreateMutex
  26. #endif
  27. using namespace rack;
  28. static void fatalSignalHandler(int sig) {
  29. // Ignore this signal to avoid recursion.
  30. signal(sig, NULL);
  31. // Ignore abort() since we call it below.
  32. signal(SIGABRT, NULL);
  33. FATAL("Fatal signal %d. Stack trace:\n%s", sig, system::getStackTrace().c_str());
  34. // This might fail because we might not be in the main thread.
  35. // But oh well, we're crashing anyway.
  36. std::string text = app::APP_NAME + " has crashed. See " + asset::logPath + " for details.";
  37. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, text.c_str());
  38. abort();
  39. }
  40. int main(int argc, char *argv[]) {
  41. #if defined ARCH_WIN
  42. // Windows global mutex to prevent multiple instances
  43. // Handle will be closed by Windows when the process ends
  44. HANDLE instanceMutex = CreateMutexA(NULL, true, app::APP_NAME.c_str());
  45. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  46. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Rack is already running. Multiple Rack instances are not supported.");
  47. exit(1);
  48. }
  49. (void) instanceMutex;
  50. // Don't display "Assertion failed!" dialog message.
  51. _set_error_mode(_OUT_TO_STDERR);
  52. #endif
  53. std::string patchPath;
  54. bool screenshot = false;
  55. float screenshotZoom = 1.f;
  56. // Parse command line arguments
  57. int c;
  58. opterr = 0;
  59. while ((c = getopt(argc, argv, "dhp:s:u:")) != -1) {
  60. switch (c) {
  61. case 'd': {
  62. settings::devMode = true;
  63. } break;
  64. case 'h': {
  65. settings::headless = true;
  66. } break;
  67. // Due to Mac app translocation and Apple adding a -psn... flag when launched, disable screenshots on Mac for now.
  68. #if !defined ARCH_MAC
  69. case 'p': {
  70. screenshot = true;
  71. // If parsing number failed, use default value
  72. sscanf(optarg, "%f", &screenshotZoom);
  73. } break;
  74. #endif
  75. case 's': {
  76. asset::systemDir = optarg;
  77. } break;
  78. case 'u': {
  79. asset::userDir = optarg;
  80. } break;
  81. default: break;
  82. }
  83. }
  84. if (optind < argc) {
  85. patchPath = argv[optind];
  86. }
  87. asset::init();
  88. logger::init();
  89. // We can now install a signal handler and log the output
  90. if (!settings::devMode) {
  91. signal(SIGABRT, fatalSignalHandler);
  92. signal(SIGFPE, fatalSignalHandler);
  93. signal(SIGILL, fatalSignalHandler);
  94. signal(SIGSEGV, fatalSignalHandler);
  95. signal(SIGTERM, fatalSignalHandler);
  96. }
  97. // Log environment
  98. INFO("%s v%s", app::APP_NAME.c_str(), app::APP_VERSION.c_str());
  99. INFO("%s", system::getOperatingSystemInfo().c_str());
  100. std::string argsList;
  101. for (int i = 0; i < argc; i++) {
  102. argsList += argv[i];
  103. argsList += " ";
  104. }
  105. INFO("Args: %s", argsList.c_str());
  106. if (settings::devMode)
  107. INFO("Development mode");
  108. INFO("System directory: %s", asset::systemDir.c_str());
  109. INFO("User directory: %s", asset::userDir.c_str());
  110. // Load settings
  111. try {
  112. settings::load(asset::settingsPath);
  113. }
  114. catch (UserException &e) {
  115. std::string msg = e.what();
  116. msg += "\n\nReset settings to default?";
  117. if (!osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK_CANCEL, msg.c_str())) {
  118. exit(1);
  119. }
  120. }
  121. // Check existence of the system res/ directory
  122. std::string resDir = asset::system("res");
  123. if (!system::isDirectory(resDir)) {
  124. std::string message = string::f("Rack's resource directory \"%s\" does not exist. Make sure Rack is correctly installed and launched.", resDir.c_str());
  125. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, message.c_str());
  126. exit(1);
  127. }
  128. INFO("Initializing environment");
  129. random::init();
  130. midi::init();
  131. rtmidiInit();
  132. bridgeInit();
  133. keyboard::init();
  134. gamepad::init();
  135. plugin::init();
  136. updater::init();
  137. if (!settings::headless) {
  138. ui::init();
  139. windowInit();
  140. }
  141. // Initialize app
  142. INFO("Initializing app");
  143. appInit();
  144. const char *openedFilename = glfwGetOpenedFilename();
  145. if (openedFilename) {
  146. patchPath = openedFilename;
  147. }
  148. if (!settings::headless) {
  149. APP->patch->init(patchPath);
  150. }
  151. INFO("Starting engine");
  152. APP->engine->start();
  153. if (settings::headless) {
  154. // TEMP Prove that the app doesn't crash
  155. std::this_thread::sleep_for(std::chrono::seconds(2));
  156. }
  157. else if (screenshot) {
  158. INFO("Taking screenshots of all modules at %gx zoom", screenshotZoom);
  159. APP->window->screenshot(screenshotZoom);
  160. }
  161. else {
  162. INFO("Running window");
  163. APP->window->run();
  164. INFO("Stopped window");
  165. }
  166. INFO("Stopping engine");
  167. APP->engine->stop();
  168. // Destroy app
  169. if (!settings::headless) {
  170. APP->patch->save(asset::autosavePath);
  171. }
  172. INFO("Destroying app");
  173. appDestroy();
  174. settings::save(asset::settingsPath);
  175. // Destroy environment
  176. INFO("Destroying environment");
  177. if (!settings::headless) {
  178. windowDestroy();
  179. ui::destroy();
  180. }
  181. plugin::destroy();
  182. bridgeDestroy();
  183. midi::destroy();
  184. INFO("Destroying logger");
  185. logger::destroy();
  186. return 0;
  187. }