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.

202 lines
4.6KB

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