ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
main.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 // LOCAL
9 #include "MainWindow.h"
10 #include "ecvApplication.h"
11 #include "ecvCommandLineParser.h"
12 #include "ecvPersistentSettings.h"
13 #include "ecvSettingManager.h"
14 #include "ecvUIManager.h"
15 
16 // CV_CORE_LIB
17 #include <CPUInfo.h>
18 #include <CVLog.h>
19 #include <CVTools.h>
20 #include <MemoryInfo.h>
21 
22 // CV_DB_LIB
23 #include <ecvColorScalesManager.h>
24 #include <ecvGuiParameters.h>
25 #include <ecvNormalVectors.h>
26 
27 // CV_IO_LIB
28 #include <FileIOFilter.h>
29 #include <ecvGlobalShiftManager.h>
30 
31 // QT
32 #include <QDir>
33 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
34 #include <QSurfaceFormat>
35 #else
36 #include <QGLFormat>
37 #endif
38 #include <QMessageBox>
39 #include <QOffscreenSurface>
40 #include <QOpenGLContext>
41 #include <QOpenGLFunctions>
42 #include <QPixmap>
43 #include <QSplashScreen>
44 #include <QStorageInfo>
45 #include <QSysInfo>
46 #include <QTime>
47 #include <QTimer>
48 #include <QTranslator>
49 #include <QtWidgets/QApplication>
50 #ifdef CC_GAMEPAD_SUPPORT
51 #include <QGamepadManager>
52 #endif
53 
54 // System-specific includes
55 #ifdef _WIN32
56 #include <windows.h> // For GetFileType, GetStdHandle, AttachConsole
57 
58 #include <cstdio> // For freopen
59 #endif
60 #ifdef Q_OS_MAC
61 #include <cstdlib> // For putenv
62 #endif
63 
64 // COMMON
65 #include "CommonSettings.h"
66 #include "ecvTranslationManager.h"
67 
68 // PLUGINS
69 #include "ecvPluginInterface.h"
70 #include "ecvPluginManager.h"
71 
72 #ifdef USE_VLD
73 #include <vld.h>
74 #endif
75 
76 // #if defined(_MSC_VER) && (_MSC_VER >= 1600)
77 // #pragma execution_character_set("utf-8")
78 // #endif
79 
80 // Function to get total system memory in GB
81 QString GetTotalMemoryInfo() {
82  using namespace cloudViewer::system;
83 
84  MemoryInfo memInfo = getMemoryInfo();
85 
86  if (memInfo.totalRam > 0) {
87  double totalGB = static_cast<double>(memInfo.totalRam) /
88  (1024.0 * 1024.0 * 1024.0);
89  return QString("RAM: %1 GB").arg(totalGB, 0, 'f', 2);
90  }
91 
92  return QString("RAM: Unable to detect");
93 }
94 
95 // Function to get storage information
96 QString GetStorageInfo() {
97  QStorageInfo storage = QStorageInfo::root();
98 
99  if (storage.isValid() && storage.isReady()) {
100  qint64 totalBytes = storage.bytesTotal();
101  qint64 availableBytes = storage.bytesAvailable();
102 
103  double totalGB = totalBytes / (1024.0 * 1024.0 * 1024.0);
104  double availableGB = availableBytes / (1024.0 * 1024.0 * 1024.0);
105  double usedGB = totalGB - availableGB;
106 
107  return QString("Storage: %1 GB total, %2 GB used, %3 GB available")
108  .arg(totalGB, 0, 'f', 2)
109  .arg(usedGB, 0, 'f', 2)
110  .arg(availableGB, 0, 'f', 2);
111  }
112 
113  return QString("Storage: Unable to detect");
114 }
115 
116 // Function to get CPU information
117 QString GetCPUInfo() {
118  using namespace cloudViewer::utility;
119 
120  const CPUInfo& cpuInfo = CPUInfo::GetInstance();
121  int numCores = cpuInfo.NumCores();
122  int numThreads = cpuInfo.NumThreads();
123  std::string modelName = cpuInfo.ModelName();
124 
125  if (numCores <= 0 && numThreads <= 0) {
126  return QString("CPU: Unable to detect");
127  }
128 
129  QString result;
130  if (!modelName.empty()) {
131  // Show model name with cores and threads
132  if (numCores > 0) {
133  result = QString("CPU: %1 (%2 cores, %3 threads)")
134  .arg(QString::fromStdString(modelName))
135  .arg(numCores)
136  .arg(numThreads);
137  } else {
138  result = QString("CPU: %1 (%2 threads)")
139  .arg(QString::fromStdString(modelName))
140  .arg(numThreads);
141  }
142  } else {
143  // Fallback: show only cores and threads
144  if (numCores > 0) {
145  result = QString("CPU: %1 cores, %2 threads")
146  .arg(numCores)
147  .arg(numThreads);
148  } else {
149  result = QString("CPU: %1 threads").arg(numThreads);
150  }
151  }
152 
153  return result;
154 }
155 
156 // Function to get GPU information
157 QString GetGPUInfo() {
158  QString gpuInfo = "GPU: ";
159 
160  // Create a temporary OpenGL context to query GPU info
161  QOffscreenSurface surface;
162  surface.create();
163 
164  QOpenGLContext context;
165  if (context.create() && context.makeCurrent(&surface)) {
166  QOpenGLFunctions* functions = context.functions();
167  if (functions) {
168  const GLubyte* vendor = functions->glGetString(GL_VENDOR);
169  const GLubyte* renderer = functions->glGetString(GL_RENDERER);
170  const GLubyte* version = functions->glGetString(GL_VERSION);
171 
172  if (vendor && renderer && version) {
173  gpuInfo += QString("%1 %2 (OpenGL %3)")
174  .arg(reinterpret_cast<const char*>(vendor))
175  .arg(reinterpret_cast<const char*>(renderer))
176  .arg(reinterpret_cast<const char*>(version));
177  } else {
178  gpuInfo += "Unable to query details";
179  }
180  }
181  context.doneCurrent();
182  } else {
183  gpuInfo += "Unable to create OpenGL context";
184  }
185 
186  return gpuInfo;
187 }
188 
189 // Function to print all system hardware information
191  CVLog::Print(
192  "=================================================================="
193  "==============");
194  CVLog::Print("System Hardware Information");
195  CVLog::Print(
196  "=================================================================="
197  "==============");
198 
199  // Operating System
200  CVLog::Print(QString("OS: %1 %2 (%3)")
201  .arg(QSysInfo::productType())
202  .arg(QSysInfo::productVersion())
203  .arg(QSysInfo::currentCpuArchitecture()));
204 
205  // Kernel version
206  CVLog::Print(QString("Kernel: %1").arg(QSysInfo::kernelVersion()));
207 
208  // CPU Information
210 
211  // Memory Information
213 
214  // Storage Information
216 
217  // GPU Information
219 
220  CVLog::Print(
221  "=================================================================="
222  "==============");
223 }
224 
226  // fix OMP: Error #15: Initializing libomp.dylib, but found libomp.dylib
227  // already initialized.
228 #ifdef Q_OS_MAC
229  char ompEnv[] = "KMP_DUPLICATE_LIB_OK=True";
230  putenv(ompEnv);
231 #endif
232 
233  // store the log message until a valid logging instance is registered
235 
236  // global structures initialization
237  FileIOFilter::InitInternalFilters(); // load all known I/O filters
238 
239  // force pre-computed normals array initialization
241  // force pre-computed color tables initialization
243 
244  // load the plugins
246 
247  ecvSettingManager::Init(Settings::CONFIG_PATH); // init setting manager for
248  // persistent settings
249  { // restore some global parameters
250  double maxAbsCoord =
254  .toDouble();
255  double maxAbsDiag =
259  .toDouble();
260 
261  CVLog::Print(QObject::tr("Restore [Global Shift] Max abs. coord = %1 / "
262  "max abs. diag = %2")
263  .arg(maxAbsCoord, 0, 'e', 0)
264  .arg(maxAbsDiag, 0, 'e', 0));
265 
268  }
269 }
270 
271 int main(int argc, char* argv[]) {
272 #ifdef _WIN32 // This will allow printf to function on windows when opened from
273  // command line
274  DWORD stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
275  if (AttachConsole(ATTACH_PARENT_PROCESS)) {
276  if (stdout_type ==
277  FILE_TYPE_UNKNOWN) // this will allow std redirection (./executable
278  // > out.txt)
279  {
280  freopen("CONOUT$", "w", stdout);
281  freopen("CONOUT$", "w", stderr);
282  }
283  }
284 #endif
285 
286 #ifdef Q_OS_MAC
287  // On macOS, when double-clicking the application, the Finder (sometimes!)
288  // adds a command-line parameter like "-psn_0_582385" which is a "process
289  // serial number". We need to recognize this and discount it when
290  // determining if we are running on the command line or not.
291 
292  int numRealArgs = argc;
293 
294  for (int i = 1; i < argc; ++i) {
295  if (strncmp(argv[i], "-psn_", 5) == 0) {
296  --numRealArgs;
297  }
298  }
299 
300  bool commandLine = (numRealArgs > 1) && (argv[1][0] == '-');
301 #else
302  bool commandLine = (argc > 1) && (argv[1][0] == '-');
303 #endif
304 
306 
307 #ifdef CC_GAMEPAD_SUPPORT
308  QGamepadManager::instance(); // potential workaround to bug
309  // https://bugreports.qt.io/browse/QTBUG-61553
310 #endif
311 
312  ecvApplication app(argc, argv, commandLine);
313 
314  // QApplication docs suggest resetting to "C" after the QApplication is
315  // initialized.
316  setlocale(LC_NUMERIC, "C");
317 
318  // However, this is needed to address BUG #17225, #17226.
319  QLocale::setDefault(QLocale::c());
320 
321  // specific commands
322  int lastArgumentIndex = 1;
323  QTranslator translator;
324  if (commandLine) {
325  // translation file selection
326  if (QString(argv[lastArgumentIndex]).toUpper() == "-LANG") {
327  QString langFilename = QString::fromLocal8Bit(argv[2]);
328 
330  commandLine = false;
331  lastArgumentIndex += 2;
332  }
333  }
334 
335  // splash screen
336  QScopedPointer<QSplashScreen> splash(nullptr);
337  QTimer splashTimer;
338  // standard mode
339  if (!commandLine) {
340 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
341  QOpenGLContext context;
342  QSurfaceFormat format;
343  format.setVersion(2, 1);
344  context.setFormat(format);
345  if (!context.create() || !context.isValid()) {
346  QMessageBox::critical(nullptr, QObject::tr("Error"),
347  QObject::tr("This application needs OpenGL "
348  "2.1 at least to run!"));
349  return EXIT_FAILURE;
350  }
351 #else
352  if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_1) ==
353  0) {
354  QMessageBox::critical(nullptr, QObject::tr("Error"),
355  QObject::tr("This application needs OpenGL "
356  "2.1 at least to run!"));
357  return EXIT_FAILURE;
358  }
359 #endif
360 
361  // splash screen
362  QPixmap pixmap(QString::fromUtf8(
364  splash.reset(new QSplashScreen(pixmap, Qt::WindowStaysOnTopHint));
365  splash->show();
366  QApplication::processEvents();
367  }
368 
369  // init environment
370  InitEnvironment();
371 
372  int result = 0;
373  // UI settings
374  QUIWidget* qui = nullptr;
375 
376  // command line mode
377  if (commandLine) {
378  // command line processing (no GUI)
380  argc, argv, ccPluginManager::get().pluginList());
381  } else {
382  // main window init.
383  MainWindow* mainWindow = MainWindow::TheInstance();
384 
385  if (!mainWindow) {
386  QMessageBox::critical(nullptr, QObject::tr("Error"),
387  QObject::tr("Failed to initialize the main "
388  "application window?!"));
389  return EXIT_FAILURE;
390  }
391 
392  mainWindow->initPlugins();
393 
394  // Print system hardware information
396 
397  if (Settings::UI_WRAPPER) {
398  // use UIManager instead
400  qui = new QUIWidget();
401  // set main weindow
402  mainWindow->setUiManager(qui);
403  qui->setMainWidget(mainWindow);
404 
406 
407  // set align center - center the window title text
408  qui->setAlignment(Qt::AlignCenter);
409 
410  // set dragable
411  qui->setSizeGripEnabled(true);
412 
413  // set icon visibility
414  qui->setVisible(QUIWidget::Lab_Ico, true);
415  qui->setVisible(QUIWidget::BtnMenu, true);
416 
417  // persistent Theme settings
418  QString qssfile =
422  .toString();
423  qui->setStyle(qssfile);
424 
425  // qui.setIconMain(QChar(0xf099), 11);
426 
428  qui->setWindowIcon(QIcon(Settings::APP_LOGO));
429 
430  qui->createTrayMenu();
431 
432  qui->show();
433  } else {
434  // use default ui
435  mainWindow->setWindowIcon(QIcon(Settings::APP_LOGO));
436  mainWindow->setWindowTitle(Settings::APP_TITLE);
437  // persistent Theme settings
438  QString qssfile =
442  .toString();
443  MainWindow::ChangeStyle(qssfile);
444  mainWindow->show();
445  }
446 
447  // close start logo according timer
448  QApplication::processEvents();
449 
450  // show current Global Shift parameters in Console
451  {
452  CVLog::Print(
453  QObject::tr("Current [Global Shift] Max abs. coord = %1 / "
454  "max abs. diag = %2")
456  0, 'e', 0)
458  0, 'e', 0));
459  }
460 
461  if (argc > lastArgumentIndex) {
462  if (splash) {
463  splash->close();
464  }
465 
466  // any additional argument is assumed to be a filename --> we try to
467  // load it/them
468  QStringList filenames;
469  for (int i = lastArgumentIndex; i < argc; ++i) {
470  QString arg(argv[i]);
471  // special command: auto start a plugin
472  if (arg.startsWith(":start-plugin:")) {
473  QString pluginName = arg.mid(14);
474  QString pluginNameUpper = pluginName.toUpper();
475  // look for this plugin
476  bool found = false;
477  for (ccPluginInterface* plugin :
478  ccPluginManager::get().pluginList()) {
479  if (plugin->getName().replace(' ', '_').toUpper() ==
480  pluginNameUpper) {
481  found = true;
482  bool success = plugin->start();
483  if (!success) {
484  CVLog::Error(QObject::tr("Failed to start the "
485  "plugin '%1'")
486  .arg(plugin->getName()));
487  }
488  break;
489  }
490  }
491 
492  if (!found) {
493  CVLog::Error(
494  QObject::tr("Couldn't find the plugin '%1'")
495  .arg(pluginName.replace('_', ' ')));
496  }
497  } else {
498  filenames << QString::fromLocal8Bit(argv[i]);
499  }
500  }
501 
502  mainWindow->addToDB(filenames);
503  } else if (splash) {
504  // count-down to hide the timer (only effective once the application
505  // will have actually started!)
506  QObject::connect(&splashTimer, &QTimer::timeout, [&]() {
507  if (splash) splash->close();
508  QCoreApplication::processEvents();
509  splash.reset();
510  });
511  splashTimer.setInterval(1000);
512  splashTimer.start();
513  }
514 
515  // change the default path to the application one (do this AFTER
516  // processing the command line)
517  QDir workingDir = QCoreApplication::applicationDirPath();
518 
519 #ifdef Q_OS_MAC
520  // This makes sure that our "working directory" is not within the
521  // application bundle
522  if (workingDir.dirName() == "MacOS") {
523  workingDir.cdUp();
524  workingDir.cdUp();
525  workingDir.cdUp();
526  }
527 #endif
528 
529  QDir::setCurrent(workingDir.absolutePath());
530 
531  // let's rock!
532  try {
533  // mainWindow->drawWidgets();
534  result = app.exec();
535  } catch (const std::exception& e) {
536  QMessageBox::warning(
537  nullptr, QObject::tr("ECV crashed!"),
538  QObject::tr("Hum, it seems that ECV has crashed... Sorry "
539  "about that :)\n") +
540  e.what());
541  } catch (...) {
542  QMessageBox::warning(nullptr, QObject::tr("ECV crashed!"),
543  QObject::tr("Hum, it seems that ECV has "
544  "crashed... Sorry about that :)"));
545  }
546 
547  // release the plugins
548  for (ccPluginInterface* plugin : ccPluginManager::get().pluginList()) {
549  plugin->stop(); // just in case
550  }
551  }
552 
557  // release main window
559 
560  // release io filters
562 
563  // release setting manager
565 
566  // release ui manager in this case
567  if (qui != nullptr) {
568  delete qui;
569  qui = nullptr;
570  }
571 
572 #ifdef CV_TRACK_ALIVE_SHARED_OBJECTS
573  // for debug purposes
574  unsigned alive = CCShareable::GetAliveCount();
575  if (alive > 1) {
576  printf("Error: some shared objects (%u) have not been released on "
577  "program end!",
578  alive);
579  system("PAUSE");
580  }
581 #endif
582 
583  return result;
584 }
filament::Texture::InternalFormat format
std::string version
core::Tensor result
Definition: VtkUtils.cpp:76
int main(int argc, char *argv[])
Definition: main.cpp:271
QString GetGPUInfo()
Definition: main.cpp:157
void PrintSystemHardwareInfo()
Definition: main.cpp:190
void InitEnvironment()
Definition: main.cpp:225
QString GetTotalMemoryInfo()
Definition: main.cpp:81
QString GetStorageInfo()
Definition: main.cpp:96
QString GetCPUInfo()
Definition: main.cpp:117
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static void EnableMessageBackup(bool state)
Enables the message backup system.
Definition: CVLog.cpp:55
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
static std::string FromQString(const QString &qs)
Definition: CVTools.cpp:100
static void UnregisterAll()
Unregisters all filters.
static MainWindow * TheInstance()
Returns the unique instance of this object.
void setUiManager(QUIWidget *uiManager)
void addToDB(const QStringList &filenames, QString fileFilter=QString(), bool displayDialog=true)
static void ChangeStyle(const QString &qssFile)
void initPlugins()
Sets up the UI (menus and toolbars) based on loaded plugins.
static void DestroyInstance()
Deletes current main window instance.
void setMainWidget(MainWindow *mainWidget)
static void setStyle(QUIWidget::Style style)
void setPixmap(QUIWidget::Widget widget, const QString &file, const QSize &size=QSize(32, 32))
static void setCode()
void createTrayMenu()
void setVisible(QUIWidget::Widget widget, bool visible=true)
void setTitle(const QString &title)
void setAlignment(Qt::Alignment alignment)
static ccColorScalesManager * GetUniqueInstance()
Returns unique instance.
static int Parse(int nargs, char **args, ccPluginInterfaceList &plugins)
Parses the input command.
static ccNormalVectors * GetUniqueInstance()
Returns unique instance.
Standard ECV plugin interface.
static ccPluginManager & get()
static ccTranslationManager & get()
void loadTranslation(QString language)
CPU information.
Definition: CPUInfo.h:19
static CPUInfo & GetInstance()
Definition: CPUInfo.cpp:174
const std::string & ModelName() const
Definition: CPUInfo.cpp:183
static void InitOpenGL()
static double MaxCoordinateAbsValue()
Returns the max coordinate (absolute) value.
static void SetMaxCoordinateAbsValue(double value)
Sets the max coordinate (absolute) value.
static void SetMaxBoundgBoxDiagonal(double value)
Sets the max bounding-box diagonal.
static double MaxBoundgBoxDiagonal()
Returns max bounding-box diagonal.
static const QString CurrentTheme()
static const QString GlobalShift()
static const QString MaxAbsCoord()
static const QString MaxAbsDiag()
static const QString ThemeSettings()
static void ReleaseInstance()
Releases unique instance.
static QVariant getValue(const QString &section, const QString &key, const QVariant &defaultValue=QVariant())
static void Init(const QString &path)
ImGuiContext * context
Definition: Window.cpp:76
static const QString APP_START_LOGO
static const QString DEFAULT_STYLE
static const QString APP_TITLE
static bool UI_WRAPPER
static QString CONFIG_PATH
static const QString APP_LOGO
MemoryInfo getMemoryInfo()
Definition: MemoryInfo.cpp:63