main.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. //##########################################################################
  2. //# #
  3. //# CLOUDCOMPARE #
  4. //# #
  5. //# This program is free software; you can redistribute it and/or modify #
  6. //# it under the terms of the GNU General Public License as published by #
  7. //# the Free Software Foundation; version 2 or later of the License. #
  8. //# #
  9. //# This program is distributed in the hope that it will be useful, #
  10. //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
  11. //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
  12. //# GNU General Public License for more details. #
  13. //# #
  14. //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
  15. //# #
  16. //##########################################################################
  17. #include <ccIncludeGL.h>
  18. //Qt
  19. #include <QDir>
  20. #include <QMessageBox>
  21. #include <QPixmap>
  22. #include <QSettings>
  23. #include <QSplashScreen>
  24. #include <QTime>
  25. #include <QTimer>
  26. #include <QTranslator>
  27. #ifdef CC_GAMEPAD_SUPPORT
  28. #include <QGamepadManager>
  29. #endif
  30. //qCC_db
  31. #include <ccColorScalesManager.h>
  32. #include <ccLog.h>
  33. #include <ccNormalVectors.h>
  34. #include <ccPointCloud.h>
  35. //qCC_io
  36. #include <FileIOFilter.h>
  37. #include <ccGlobalShiftManager.h>
  38. //local
  39. #include "ccApplication.h"
  40. #include "ccCommandLineParser.h"
  41. #include "ccGuiParameters.h"
  42. #include "ccPersistentSettings.h"
  43. #include "mainwindow.h"
  44. #include "ccTranslationManager.h"
  45. //plugins
  46. #include "ccPluginInterface.h"
  47. #include "ccPluginManager.h"
  48. #ifdef USE_VLD
  49. #include <vld.h>
  50. #endif
  51. static bool IsCommandLine(int argc, char **argv)
  52. {
  53. #ifdef Q_OS_MAC
  54. // On macOS, when double-clicking the application, the Finder (sometimes!) adds a command-line parameter
  55. // like "-psn_0_582385" which is a "process serial number".
  56. // We need to recognize this and discount it when determining if we are running on the command line or not.
  57. int numRealArgs = argc;
  58. for ( int i = 1; i < argc; ++i )
  59. {
  60. if ( strncmp( argv[i], "-psn_", 5 ) == 0 )
  61. {
  62. --numRealArgs;
  63. }
  64. }
  65. return (numRealArgs > 1) && (argv[1][0] == '-');
  66. #else
  67. return (argc > 1) && (argv[1][0] == '-');
  68. #endif
  69. }
  70. int main(int argc, char **argv)
  71. {
  72. #ifdef _WIN32 //This will allow printf to function on windows when opened from command line
  73. DWORD stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
  74. if (AttachConsole(ATTACH_PARENT_PROCESS))
  75. {
  76. if (stdout_type == FILE_TYPE_UNKNOWN) // this will allow std redirection (./executable > out.txt)
  77. {
  78. freopen("CONOUT$", "w", stdout);
  79. freopen("CONOUT$", "w", stderr);
  80. }
  81. }
  82. #endif
  83. bool commandLine = IsCommandLine(argc, argv);
  84. // Convert the input arguments to QString before the application is initialized
  85. // (as it will force utf8, which might prevent from properly reading filenames from the command line)
  86. QStringList argumentsLocal8Bit;
  87. for (int i = 0; i < argc; ++i)
  88. {
  89. argumentsLocal8Bit << QString::fromLocal8Bit(argv[i]);
  90. }
  91. //specific commands
  92. int lastArgumentIndex = 1;
  93. if (commandLine)
  94. {
  95. //translation file selection
  96. if ( lastArgumentIndex < argumentsLocal8Bit.size()
  97. && argumentsLocal8Bit[lastArgumentIndex].toUpper() == "-LANG")
  98. {
  99. //remove verified local option
  100. argumentsLocal8Bit.removeAt(lastArgumentIndex);
  101. if (lastArgumentIndex >= argumentsLocal8Bit.size())
  102. {
  103. ccLog::Error(QObject::tr("Missing argument after %1: language file").arg("-LANG"));
  104. return EXIT_FAILURE;
  105. }
  106. //remove verified arguments so that -SILENT will be the first one (if present)...
  107. QString langFilename = argumentsLocal8Bit.takeAt(lastArgumentIndex);
  108. ccTranslationManager::Get().loadTranslation(langFilename);
  109. commandLine = false;
  110. }
  111. if ( lastArgumentIndex < argumentsLocal8Bit.size()
  112. && argumentsLocal8Bit[lastArgumentIndex].toUpper() == "-VERBOSITY")
  113. {
  114. //remove verified local option
  115. argumentsLocal8Bit.removeAt(lastArgumentIndex);
  116. if (lastArgumentIndex >= argumentsLocal8Bit.size())
  117. {
  118. ccLog::Error(QObject::tr("Missing argument after %1: verbosity level").arg("-VERBOSITY"));
  119. return EXIT_FAILURE;
  120. }
  121. //remove verified arguments so that -SILENT will be the first one (if present)...
  122. QString verbosityLevelStr = argumentsLocal8Bit.takeAt(lastArgumentIndex);
  123. bool ok = false;
  124. int verbosityLevel = verbosityLevelStr.toInt(&ok);
  125. if (!ok || verbosityLevel < 0)
  126. {
  127. ccLog::Warning(QObject::tr("Invalid verbosity level: %1").arg(verbosityLevelStr));
  128. }
  129. else
  130. {
  131. ccLog::SetVerbosityLevel(verbosityLevel);
  132. }
  133. }
  134. }
  135. #ifdef Q_OS_WIN
  136. //enables automatic scaling based on the monitor's pixel density
  137. ccApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
  138. #endif
  139. ccApplication::InitOpenGL();
  140. ccApplication app(argc, argv, commandLine);
  141. if (!commandLine)
  142. {
  143. // if not in CLI mode, we set the default log verbosity level
  144. ccLog::SetVerbosityLevel(ccGui::Parameters().logVerbosityLevel);
  145. }
  146. #ifdef CC_GAMEPAD_SUPPORT
  147. #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
  148. #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
  149. QGamepadManager::instance(); //potential workaround to bug https://bugreports.qt.io/browse/QTBUG-61553
  150. #endif
  151. #endif
  152. #endif
  153. //store the log message until a valid logging instance is registered
  154. ccLog::EnableMessageBackup(true);
  155. //splash screen
  156. QScopedPointer<QSplashScreen> splash(nullptr);
  157. //standard mode
  158. if (!commandLine)
  159. {
  160. if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_1) == 0)
  161. {
  162. QMessageBox::critical(nullptr, "Error", "This application needs OpenGL 2.1 at least to run!");
  163. return EXIT_FAILURE;
  164. }
  165. //init splash screen
  166. QPixmap pixmap(QString::fromUtf8(":/CC/images/imLogoV2Qt.png"));
  167. splash.reset(new QSplashScreen(pixmap, Qt::WindowStaysOnTopHint));
  168. splash->show();
  169. }
  170. //global structures initialization
  171. FileIOFilter::InitInternalFilters(); //load all known I/O filters (plugins will come later!)
  172. ccNormalVectors::GetUniqueInstance(); //force pre-computed normals array initialization
  173. ccColorScalesManager::GetUniqueInstance(); //force pre-computed color tables initialization
  174. //load the plugins
  175. ccPluginManager& pluginManager = ccPluginManager::Get();
  176. pluginManager.loadPlugins();
  177. //restore some global parameters
  178. {
  179. QSettings settings;
  180. settings.beginGroup(ccPS::GlobalShift());
  181. double maxAbsCoord = settings.value(ccPS::MaxAbsCoord(), ccGlobalShiftManager::MaxCoordinateAbsValue()).toDouble();
  182. double maxAbsDiag = settings.value(ccPS::MaxAbsDiag(), ccGlobalShiftManager::MaxBoundgBoxDiagonal()).toDouble();
  183. settings.endGroup();
  184. ccLog::Print(QString("[Global Shift] Max abs. coord = %1 / max abs. diag = %2").arg(maxAbsCoord, 0, 'e', 0).arg(maxAbsDiag, 0, 'e', 0));
  185. ccGlobalShiftManager::SetMaxCoordinateAbsValue(maxAbsCoord);
  186. ccGlobalShiftManager::SetMaxBoundgBoxDiagonal(maxAbsDiag);
  187. }
  188. int result = 0;
  189. //command line mode
  190. if (commandLine)
  191. {
  192. //command line processing (no GUI)
  193. result = ccCommandLineParser::Parse(argumentsLocal8Bit, pluginManager.pluginList());
  194. }
  195. else
  196. {
  197. //main window initialization
  198. MainWindow* mainWindow = MainWindow::TheInstance();
  199. if (!mainWindow)
  200. {
  201. QMessageBox::critical(nullptr, "Error", "Failed to initialize the main application window?!");
  202. return EXIT_FAILURE;
  203. }
  204. mainWindow->initPlugins();
  205. mainWindow->show();
  206. QCoreApplication::processEvents();
  207. //show current Global Shift parameters in Console
  208. {
  209. ccLog::Print(QString("[Global Shift] Max abs. coord = %1 / max abs. diag = %2")
  210. .arg(ccGlobalShiftManager::MaxCoordinateAbsValue(), 0, 'e', 0)
  211. .arg(ccGlobalShiftManager::MaxBoundgBoxDiagonal(), 0, 'e', 0));
  212. }
  213. if (splash)
  214. {
  215. splash->close();
  216. }
  217. if (argc > lastArgumentIndex)
  218. {
  219. //any additional argument is assumed to be a filename --> we try to load it/them
  220. QStringList filenames;
  221. for (int i = lastArgumentIndex; i < argc; ++i)
  222. {
  223. QString arg = argumentsLocal8Bit[i];
  224. //special command: auto start a plugin
  225. if (arg.startsWith(":start-plugin:"))
  226. {
  227. QString pluginName = arg.mid(14);
  228. QString pluginNameUpper = pluginName.toUpper();
  229. //look for this plugin
  230. bool found = false;
  231. for (ccPluginInterface* plugin : pluginManager.pluginList())
  232. {
  233. if (plugin->getName().replace(' ', '_').toUpper() == pluginNameUpper)
  234. {
  235. found = true;
  236. bool success = plugin->start();
  237. if (!success)
  238. {
  239. ccLog::Error(QString("Failed to start the plugin '%1'").arg(plugin->getName()));
  240. }
  241. break;
  242. }
  243. }
  244. if (!found)
  245. {
  246. ccLog::Error(QString("Couldn't find the plugin '%1'").arg(pluginName.replace('_', ' ')));
  247. }
  248. }
  249. else
  250. {
  251. filenames << arg;
  252. }
  253. }
  254. mainWindow->addToDB(filenames);
  255. }
  256. //change the default path to the application one (do this AFTER processing the command line)
  257. QDir workingDir = QCoreApplication::applicationDirPath();
  258. #ifdef Q_OS_MAC
  259. // This makes sure that our "working directory" is not within the application bundle
  260. if ( workingDir.dirName() == "MacOS" )
  261. {
  262. workingDir.cdUp();
  263. workingDir.cdUp();
  264. workingDir.cdUp();
  265. }
  266. #endif
  267. QDir::setCurrent(workingDir.absolutePath());
  268. //let's rock!
  269. try
  270. {
  271. result = QApplication::exec();
  272. }
  273. catch (const std::exception& e)
  274. {
  275. QMessageBox::warning(nullptr, "CC crashed!", QString("Hum, it seems that CC has crashed... Sorry about that :)\n") + e.what());
  276. }
  277. catch (...)
  278. {
  279. QMessageBox::warning(nullptr, "CC crashed!", "Hum, it seems that CC has crashed... Sorry about that :)");
  280. }
  281. //release the plugins
  282. for (ccPluginInterface* plugin : pluginManager.pluginList())
  283. {
  284. plugin->stop(); //just in case
  285. }
  286. }
  287. //release global structures
  288. ccPointCloud::ReleaseShaders(); // must be done before the OpenGL context is released (i.e. before the windows is destroyed)
  289. MainWindow::DestroyInstance();
  290. FileIOFilter::UnregisterAll();
  291. #ifdef CC_TRACK_ALIVE_SHARED_OBJECTS
  292. //for debug purposes
  293. unsigned alive = CCShareable::GetAliveCount();
  294. if (alive > 1)
  295. {
  296. printf("Error: some shared objects (%u) have not been released on program end!",alive);
  297. system("PAUSE");
  298. }
  299. #endif
  300. return result;
  301. }