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.

201 lines
4.5KB

  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);
  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, app::APP_VERSION);
  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. if (!settings::headless) {
  125. ui::init();
  126. windowInit();
  127. }
  128. // Initialize app
  129. INFO("Initializing app");
  130. appInit();
  131. const char *openedFilename = glfwGetOpenedFilename();
  132. if (openedFilename) {
  133. patchPath = openedFilename;
  134. }
  135. if (!settings::headless) {
  136. APP->patch->init(patchPath);
  137. }
  138. INFO("Starting engine");
  139. APP->engine->start();
  140. if (settings::headless) {
  141. // TEMP Prove that the app doesn't crash
  142. std::this_thread::sleep_for(std::chrono::seconds(2));
  143. }
  144. else if (screenshot) {
  145. INFO("Taking screenshots of all modules at %gx zoom", screenshotZoom);
  146. APP->window->screenshot(screenshotZoom);
  147. }
  148. else {
  149. INFO("Running window");
  150. APP->window->run();
  151. INFO("Stopped window");
  152. }
  153. INFO("Stopping engine");
  154. APP->engine->stop();
  155. // Destroy app
  156. if (!settings::headless) {
  157. APP->patch->save(asset::user("autosave.vcv"));
  158. }
  159. INFO("Destroying app");
  160. appDestroy();
  161. settings::save(asset::user("settings.json"));
  162. // Destroy environment
  163. INFO("Destroying environment");
  164. if (!settings::headless) {
  165. windowDestroy();
  166. ui::destroy();
  167. }
  168. plugin::destroy();
  169. bridgeDestroy();
  170. midi::destroy();
  171. INFO("Destroying logger");
  172. logger::destroy();
  173. return 0;
  174. }