GUIApplicationManager.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "native/utilities/GUIApplicationManager.h"
  9. #include "native/connection/connectionManager.h"
  10. #include "native/utilities/IniConfiguration.h"
  11. #include "native/utilities/GUIApplicationServer.h"
  12. #include "native/resourcecompiler/rccontroller.h"
  13. #include "native/FileServer/fileServer.h"
  14. #include "native/AssetManager/assetScanner.h"
  15. #include <native/utilities/PlatformConfiguration.h>
  16. #include <QApplication>
  17. #include <QDialogButtonBox>
  18. #include <QLabel>
  19. #include <QMessageBox>
  20. #include <QSettings>
  21. #include <AzQtComponents/Components/StyleManager.h>
  22. #include <AzQtComponents/Components/WindowDecorationWrapper.h>
  23. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  24. #include <AzCore/Utils/Utils.h>
  25. #if defined(EXTERNAL_CRASH_REPORTING)
  26. #include <ToolsCrashHandler.h>
  27. #endif
  28. #if defined(AZ_PLATFORM_MAC)
  29. #include <native/utilities/MacDockIconHandler.h>
  30. #include <ApplicationServices/ApplicationServices.h>
  31. #endif
  32. #include <QJsonArray>
  33. #include <QJsonDocument>
  34. #include <QJsonObject>
  35. #include <QSettings>
  36. #include <ui/MessageWindow.h>
  37. using namespace AssetProcessor;
  38. namespace
  39. {
  40. static const int s_errorMessageBoxDelay = 5000;
  41. void RemoveTemporaries()
  42. {
  43. // get currently running app
  44. QFileInfo moduleFileInfo;
  45. char executableDirectory[AZ_MAX_PATH_LEN];
  46. AZ::Utils::GetExecutablePathReturnType result = AZ::Utils::GetExecutablePath(executableDirectory, AZStd::size(executableDirectory));
  47. if (result.m_pathStored == AZ::Utils::ExecutablePathResult::Success)
  48. {
  49. moduleFileInfo.setFile(executableDirectory);
  50. }
  51. QDir binaryDir = moduleFileInfo.absoluteDir();
  52. // strip extension
  53. QString applicationBase = moduleFileInfo.completeBaseName();
  54. // add wildcard filter
  55. applicationBase.append("*_tmp");
  56. // set to qt
  57. binaryDir.setNameFilters(QStringList() << applicationBase);
  58. binaryDir.setFilter(QDir::Files);
  59. // iterate all matching
  60. foreach(QString tempFile, binaryDir.entryList())
  61. {
  62. binaryDir.remove(tempFile);
  63. }
  64. }
  65. }
  66. ErrorCollector::~ErrorCollector()
  67. {
  68. if (!m_errorMessages.isEmpty())
  69. {
  70. MessageWindow messageWindow(m_parent);
  71. messageWindow.SetHeaderText("The following errors occurred during startup:");
  72. messageWindow.SetMessageText(m_errorMessages);
  73. messageWindow.SetTitleText("Startup Errors");
  74. messageWindow.exec();
  75. }
  76. }
  77. void ErrorCollector::AddError(AZStd::string message)
  78. {
  79. QString qMessage(message.c_str());
  80. qMessage = qMessage.trimmed();
  81. m_errorMessages << qMessage;
  82. }
  83. GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, QObject* parent)
  84. : GUIApplicationManager(argc, argv, parent, {})
  85. {
  86. }
  87. GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, AZ::ComponentApplicationSettings componentAppSettings)
  88. : GUIApplicationManager(argc, argv, nullptr, AZStd::move(componentAppSettings))
  89. {
  90. }
  91. GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, QObject* parent, AZ::ComponentApplicationSettings componentAppSettings)
  92. : ApplicationManagerBase(argc, argv, parent, AZStd::move(componentAppSettings))
  93. {
  94. #if defined(AZ_PLATFORM_MAC)
  95. // Since AP is not shipped as a '.app' package, it will not receive keyboard focus
  96. // unless we tell the OS specifically to treat it as a foreground application.
  97. ProcessSerialNumber psn = { 0, kCurrentProcess };
  98. TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  99. #endif
  100. }
  101. GUIApplicationManager::~GUIApplicationManager()
  102. {
  103. Destroy();
  104. }
  105. ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun()
  106. {
  107. AssetProcessor::MessageInfoBus::Handler::BusConnect();
  108. ApplicationManager::BeforeRunStatus status = ApplicationManagerBase::BeforeRun();
  109. if (status != ApplicationManager::BeforeRunStatus::Status_Success)
  110. {
  111. return status;
  112. }
  113. // The build process may leave behind some temporaries, try to delete them
  114. RemoveTemporaries();
  115. QDir projectAssetRoot;
  116. AssetUtilities::ComputeAssetRoot(projectAssetRoot);
  117. #if defined(EXTERNAL_CRASH_REPORTING)
  118. CrashHandler::ToolsCrashHandler::InitCrashHandler("AssetProcessor", projectAssetRoot.absolutePath().toStdString());
  119. #endif
  120. // we have to monitor both the cache folder and the database file and restart AP if either of them gets deleted
  121. // It is important to note that we are monitoring the parent folder and not the actual cache folder itself since
  122. // we want to handle the use case on Mac OS if the user moves the cache folder to the trash.
  123. m_qtFileWatcher.addPath(projectAssetRoot.absolutePath());
  124. QDir projectCacheRoot;
  125. AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
  126. QString assetDbPath = projectCacheRoot.filePath("assetdb.sqlite");
  127. m_qtFileWatcher.addPath(assetDbPath);
  128. // if our Gems file changes, make sure we watch that, too.
  129. QString projectPath = AssetUtilities::ComputeProjectPath();
  130. QObject::connect(&m_qtFileWatcher, &QFileSystemWatcher::fileChanged, this, &GUIApplicationManager::FileChanged);
  131. QObject::connect(&m_qtFileWatcher, &QFileSystemWatcher::directoryChanged, this, &GUIApplicationManager::DirectoryChanged);
  132. return ApplicationManager::BeforeRunStatus::Status_Success;
  133. }
  134. void GUIApplicationManager::Destroy()
  135. {
  136. m_startupErrorCollector = nullptr;
  137. if (m_mainWindow)
  138. {
  139. delete m_mainWindow;
  140. m_mainWindow = nullptr;
  141. }
  142. AssetProcessor::MessageInfoBus::Handler::BusDisconnect();
  143. ApplicationManagerBase::Destroy();
  144. DestroyIniConfiguration();
  145. DestroyFileServer();
  146. }
  147. bool GUIApplicationManager::Run()
  148. {
  149. qRegisterMetaType<AZ::u32>("AZ::u32");
  150. qRegisterMetaType<AZ::Uuid>("AZ::Uuid");
  151. AZ::IO::FixedMaxPath engineRootPath;
  152. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  153. {
  154. settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  155. }
  156. AzQtComponents::StyleManager* styleManager = new AzQtComponents::StyleManager(qApp);
  157. styleManager->initialize(qApp, engineRootPath);
  158. QDir engineRoot;
  159. AssetUtilities::ComputeAssetRoot(engineRoot);
  160. AssetUtilities::ComputeEngineRoot(engineRoot);
  161. AzQtComponents::StyleManager::addSearchPaths(
  162. QStringLiteral("style"),
  163. engineRoot.filePath(QStringLiteral("Code/Tools/AssetProcessor/native/ui/style")),
  164. QStringLiteral(":/AssetProcessor/style"),
  165. engineRootPath);
  166. m_mainWindow = new MainWindow(this);
  167. auto wrapper = new AzQtComponents::WindowDecorationWrapper(
  168. #if defined(AZ_PLATFORM_WINDOWS)
  169. // On windows we do want our custom title bar
  170. AzQtComponents::WindowDecorationWrapper::OptionAutoAttach
  171. #else
  172. // On others platforms we don't want our custom title bar (ie, use native title bars).
  173. AzQtComponents::WindowDecorationWrapper::OptionDisabled
  174. #endif
  175. );
  176. wrapper->setGuest(m_mainWindow);
  177. // Use this variant of the enableSaveRestoreGeometry because the global QCoreApplication::setOrganization and setApplicationName
  178. // are called in ApplicationManager::Activate, which happens much later on in this function.
  179. // ApplicationManager::Activate is shared between the Batch version of the AP and the GUI version,
  180. // and there are thing
  181. const bool restoreOnFirstShow = true;
  182. wrapper->enableSaveRestoreGeometry(GetOrganizationName(), GetApplicationName(), "MainWindow", restoreOnFirstShow);
  183. AzQtComponents::StyleManager::setStyleSheet(m_mainWindow, QStringLiteral("style:AssetProcessor.qss"));
  184. auto refreshStyleSheets = [styleManager]()
  185. {
  186. styleManager->Refresh();
  187. };
  188. // CheckForRegistryProblems can pop up a dialog, so we need to check after
  189. // we initialize the stylesheet
  190. bool showErrorMessageOnRegistryProblem = true;
  191. RegistryCheckInstructions registryCheckInstructions = CheckForRegistryProblems(m_mainWindow, showErrorMessageOnRegistryProblem);
  192. if (registryCheckInstructions != RegistryCheckInstructions::Continue)
  193. {
  194. if (registryCheckInstructions == RegistryCheckInstructions::Restart)
  195. {
  196. Restart();
  197. }
  198. return false;
  199. }
  200. bool startHidden = QApplication::arguments().contains("--start-hidden", Qt::CaseInsensitive);
  201. if (!startHidden)
  202. {
  203. wrapper->show();
  204. }
  205. else
  206. {
  207. #ifdef AZ_PLATFORM_WINDOWS
  208. // Qt / Windows has issues if the main window isn't shown once
  209. // so we show it then hide it
  210. wrapper->show();
  211. // Have a delay on the hide, to make sure that the show is entirely processed
  212. // first
  213. QTimer::singleShot(0, wrapper, &QWidget::hide);
  214. #endif
  215. }
  216. #ifdef AZ_PLATFORM_MAC
  217. connect(new MacDockIconHandler(this), &MacDockIconHandler::dockIconClicked, m_mainWindow, &MainWindow::ShowWindow);
  218. #endif
  219. QAction* quitAction = new QAction(QObject::tr("Quit"), m_mainWindow);
  220. quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
  221. quitAction->setMenuRole(QAction::QuitRole);
  222. m_mainWindow->addAction(quitAction);
  223. m_mainWindow->connect(quitAction, SIGNAL(triggered()), this, SLOT(QuitRequested()));
  224. QAction* refreshAction = new QAction(QObject::tr("Refresh Stylesheet"), m_mainWindow);
  225. refreshAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
  226. m_mainWindow->addAction(refreshAction);
  227. m_mainWindow->connect(refreshAction, &QAction::triggered, this, refreshStyleSheets);
  228. QObject::connect(this, &GUIApplicationManager::ShowWindow, m_mainWindow, &MainWindow::ShowWindow);
  229. if (QSystemTrayIcon::isSystemTrayAvailable())
  230. {
  231. QAction* showAction = new QAction(QObject::tr("Show"), m_mainWindow);
  232. QObject::connect(showAction, &QAction::triggered, m_mainWindow, &MainWindow::ShowWindow);
  233. QAction* hideAction = new QAction(QObject::tr("Hide"), m_mainWindow);
  234. QObject::connect(hideAction, &QAction::triggered, wrapper, &AzQtComponents::WindowDecorationWrapper::hide);
  235. QMenu* trayIconMenu = new QMenu();
  236. trayIconMenu->addAction(showAction);
  237. trayIconMenu->addAction(hideAction);
  238. trayIconMenu->addSeparator();
  239. #if AZ_TRAIT_OS_PLATFORM_APPLE
  240. QAction* systemTrayQuitAction = new QAction(QObject::tr("Quit"), m_mainWindow);
  241. systemTrayQuitAction->setMenuRole(QAction::NoRole);
  242. m_mainWindow->connect(systemTrayQuitAction, SIGNAL(triggered()), this, SLOT(QuitRequested()));
  243. trayIconMenu->addAction(systemTrayQuitAction);
  244. #else
  245. trayIconMenu->addAction(quitAction);
  246. #endif
  247. m_trayIcon = new QSystemTrayIcon(QIcon(":/o3de_assetprocessor_taskbar.svg"), m_mainWindow);
  248. m_trayIcon->setContextMenu(trayIconMenu);
  249. m_trayIcon->setToolTip(QObject::tr("O3DE Asset Processor"));
  250. m_trayIcon->show();
  251. QObject::connect(m_trayIcon, &QSystemTrayIcon::activated, m_mainWindow, [&, wrapper](QSystemTrayIcon::ActivationReason reason)
  252. {
  253. if (reason == QSystemTrayIcon::DoubleClick)
  254. {
  255. if (wrapper->isVisible())
  256. {
  257. wrapper->hide();
  258. }
  259. else
  260. {
  261. m_mainWindow->ShowWindow();
  262. }
  263. }
  264. });
  265. QObject::connect(m_trayIcon, &QSystemTrayIcon::messageClicked, m_mainWindow, &MainWindow::ShowWindow);
  266. if (startHidden)
  267. {
  268. m_trayIcon->showMessage(
  269. QCoreApplication::translate("Tray Icon", "O3DE Asset Processor has started"),
  270. QCoreApplication::translate("Tray Icon", "The O3DE Asset Processor monitors raw project assets and converts those assets into runtime-ready data."),
  271. QSystemTrayIcon::Information, 3000);
  272. }
  273. }
  274. connect(this, &GUIApplicationManager::AssetProcessorStatusChanged, m_mainWindow, &MainWindow::OnAssetProcessorStatusChanged);
  275. if (!Activate())
  276. {
  277. return false;
  278. }
  279. m_mainWindow->Activate();
  280. connect(GetAssetScanner(), &AssetProcessor::AssetScanner::AssetScanningStatusChanged, this, [this](AssetScanningStatus status)
  281. {
  282. if (status == AssetScanningStatus::Started)
  283. {
  284. AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Scanning_Started);
  285. m_mainWindow->OnAssetProcessorStatusChanged(entry);
  286. }
  287. });
  288. connect(GetRCController(), &AssetProcessor::RCController::ActiveJobsCountChanged, this, &GUIApplicationManager::OnActiveJobsCountChanged);
  289. connect(this, &GUIApplicationManager::ConnectionStatusMsg, this, &GUIApplicationManager::ShowTrayIconMessage);
  290. qApp->setQuitOnLastWindowClosed(false);
  291. m_duringStartup = false;
  292. m_startedSuccessfully = true;
  293. int resultCode = qApp->exec(); // this blocks until the last window is closed.
  294. if(!InitiatedShutdown())
  295. {
  296. // if we are here it implies that AP did not stop the Qt event loop and is shutting down prematurely
  297. // we need to call QuitRequested and start the event loop once again so that AP shuts down correctly
  298. QuitRequested();
  299. resultCode = qApp->exec();
  300. }
  301. if (m_trayIcon)
  302. {
  303. m_trayIcon->hide();
  304. delete m_trayIcon;
  305. }
  306. m_mainWindow->SaveLogPanelState();
  307. // mainWindow indirectly uses some UserSettings so clean it up before we clean those up
  308. delete m_mainWindow;
  309. m_mainWindow = nullptr;
  310. AZ::SerializeContext* context = nullptr;
  311. AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  312. AZ_Assert(context, "No serialize context");
  313. QDir projectCacheRoot;
  314. AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
  315. m_localUserSettings.Save(projectCacheRoot.filePath("AssetProcessorUserSettings.xml").toUtf8().data(), context);
  316. m_localUserSettings.Deactivate();
  317. if (NeedRestart())
  318. {
  319. bool launched = Restart();
  320. if (!launched)
  321. {
  322. return false;
  323. }
  324. }
  325. Destroy();
  326. return !resultCode && m_startedSuccessfully;
  327. }
  328. void GUIApplicationManager::NegotiationFailed()
  329. {
  330. QString message = QCoreApplication::translate("error", "An attempt to connect to the game or editor has failed. The game or editor appears to be running from a different folder or a different project. Please restart the asset processor from the correct branch or make sure the game/editor is running the same project as the asset processor.");
  331. QMetaObject::invokeMethod(this, "ShowMessageBox", Qt::QueuedConnection, Q_ARG(QString, QString("Negotiation Failed")), Q_ARG(QString, message), Q_ARG(bool, false));
  332. }
  333. void GUIApplicationManager::OnAssetFailed(const AZStd::string& sourceFileName)
  334. {
  335. QString message = tr("Error : %1 failed to compile\nPlease check the Asset Processor for more information.").arg(QString::fromUtf8(sourceFileName.c_str()));
  336. QMetaObject::invokeMethod(this, "ShowTrayIconErrorMessage", Qt::QueuedConnection, Q_ARG(QString, message));
  337. }
  338. void GUIApplicationManager::OnErrorMessage(const char* error)
  339. {
  340. QMessageBox msgBox;
  341. msgBox.setText(QCoreApplication::translate("errors", error));
  342. msgBox.setStandardButtons(QMessageBox::Ok);
  343. msgBox.setDefaultButton(QMessageBox::Ok);
  344. msgBox.exec();
  345. }
  346. void GUIApplicationManager::ShowMessageBox(QString title, QString msg, bool isCritical)
  347. {
  348. if (!m_messageBoxIsVisible)
  349. {
  350. // Only show the message box if it is not visible
  351. m_messageBoxIsVisible = true;
  352. QMessageBox msgBox(m_mainWindow);
  353. msgBox.setWindowTitle(title);
  354. msgBox.setText(msg);
  355. msgBox.setStandardButtons(QMessageBox::Ok);
  356. msgBox.setDefaultButton(QMessageBox::Ok);
  357. if (isCritical)
  358. {
  359. msgBox.setIcon(QMessageBox::Critical);
  360. }
  361. msgBox.exec();
  362. m_messageBoxIsVisible = false;
  363. }
  364. }
  365. bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
  366. {
  367. // if we're in a worker thread, errors must not pop up a dialog box
  368. if (AssetProcessor::GetThreadLocalJobId() != 0)
  369. {
  370. // just absorb the error, do not perform default op
  371. return true;
  372. }
  373. if (m_startupErrorCollector)
  374. {
  375. m_startupErrorCollector->AddError(message);
  376. return true;
  377. }
  378. if (!InitiatedShutdown())
  379. {
  380. // During quitting, we don't pop up error message boxes.
  381. // instead, we're going to return true, which will cause it to
  382. // process to the log file instead.
  383. return true;
  384. }
  385. // If we're the main thread, then consider showing the message box directly.
  386. // note that all other threads will PAUSE if they emit a message while the main thread is showing this box
  387. // due to the way the trace system EBUS is mutex-protected.
  388. Qt::ConnectionType connection = Qt::DirectConnection;
  389. if (QThread::currentThread() != qApp->thread())
  390. {
  391. connection = Qt::QueuedConnection;
  392. }
  393. QMetaObject::invokeMethod(this, "ShowMessageBox", connection, Q_ARG(QString, QString("Error")), Q_ARG(QString, QString(message)), Q_ARG(bool, true));
  394. return true;
  395. }
  396. bool GUIApplicationManager::OnAssert(const char* message)
  397. {
  398. if (!OnError(nullptr, message))
  399. {
  400. return false;
  401. }
  402. // Asserts should be severe enough for data corruption,
  403. // so the process should quit to avoid that happening for users.
  404. if (!AZ::Debug::Trace::Instance().IsDebuggerPresent())
  405. {
  406. QuitRequested();
  407. return true;
  408. }
  409. AZ::Debug::Trace::Instance().Break();
  410. return true;
  411. }
  412. WId GUIApplicationManager::GetWindowId() const
  413. {
  414. return m_mainWindow->effectiveWinId();
  415. }
  416. bool GUIApplicationManager::Activate()
  417. {
  418. m_startupErrorCollector = AZStd::make_unique<ErrorCollector>(m_mainWindow);
  419. AZ::SerializeContext* context = nullptr;
  420. AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  421. AZ_Assert(context, "No serialize context");
  422. QDir projectCacheRoot;
  423. AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
  424. m_localUserSettings.Load(projectCacheRoot.filePath("AssetProcessorUserSettings.xml").toUtf8().data(), context);
  425. m_localUserSettings.Activate(AZ::UserSettings::CT_LOCAL);
  426. InitIniConfiguration();
  427. InitFileServer();
  428. //activate the base stuff.
  429. if (!ApplicationManagerBase::Activate())
  430. {
  431. return false;
  432. }
  433. return true;
  434. }
  435. bool GUIApplicationManager::PostActivate()
  436. {
  437. if (!ApplicationManagerBase::PostActivate())
  438. {
  439. m_startupErrorCollector = nullptr;
  440. m_startedSuccessfully = false;
  441. return false;
  442. }
  443. m_fileWatcher->StartWatching();
  444. m_startupErrorCollector = nullptr;
  445. return true;
  446. }
  447. void GUIApplicationManager::CreateQtApplication()
  448. {
  449. QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
  450. QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
  451. QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
  452. QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
  453. // Qt actually modifies the argc and argv, you must pass the real ones in as ref so it can.
  454. m_qApp = new QApplication(*m_frameworkApp.GetArgC(), *m_frameworkApp.GetArgV());
  455. }
  456. void GUIApplicationManager::DirectoryChanged([[maybe_unused]] QString path)
  457. {
  458. QDir projectCacheRoot;
  459. AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
  460. if (!projectCacheRoot.exists() || !projectCacheRoot.exists("assetdb.sqlite"))
  461. {
  462. // If either the Cache directory or database file has been removed, we need to restart
  463. QTimer::singleShot(200, this, [this]()
  464. {
  465. QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection);
  466. });
  467. }
  468. }
  469. void GUIApplicationManager::FileChanged(QString path)
  470. {
  471. QDir projectCacheRoot;
  472. AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
  473. QString assetDbPath = projectCacheRoot.filePath("assetdb.sqlite");
  474. if (QString::compare(AssetUtilities::NormalizeFilePath(path), assetDbPath, Qt::CaseInsensitive) == 0)
  475. {
  476. if (!QFile::exists(assetDbPath))
  477. {
  478. // if the database file is deleted we need to restart
  479. QTimer::singleShot(200, this, [this]()
  480. {
  481. QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection);
  482. });
  483. }
  484. }
  485. }
  486. bool GUIApplicationManager::InitApplicationServer()
  487. {
  488. m_applicationServer = new GUIApplicationServer();
  489. return true;
  490. }
  491. void GUIApplicationManager::InitConnectionManager()
  492. {
  493. ApplicationManagerBase::InitConnectionManager();
  494. using namespace std::placeholders;
  495. using namespace AzFramework::AssetSystem;
  496. using namespace AzToolsFramework::AssetSystem;
  497. //File Server related
  498. m_connectionManager->RegisterService(FileOpenRequest::MessageType, std::bind(&FileServer::ProcessOpenRequest, m_fileServer, _1, _2, _3, _4));
  499. m_connectionManager->RegisterService(FileCloseRequest::MessageType, std::bind(&FileServer::ProcessCloseRequest, m_fileServer, _1, _2, _3, _4));
  500. m_connectionManager->RegisterService(FileReadRequest::MessageType, std::bind(&FileServer::ProcessReadRequest, m_fileServer, _1, _2, _3, _4));
  501. m_connectionManager->RegisterService(FileWriteRequest::MessageType, std::bind(&FileServer::ProcessWriteRequest, m_fileServer, _1, _2, _3, _4));
  502. m_connectionManager->RegisterService(FileSeekRequest::MessageType, std::bind(&FileServer::ProcessSeekRequest, m_fileServer, _1, _2, _3, _4));
  503. m_connectionManager->RegisterService(FileTellRequest::MessageType, std::bind(&FileServer::ProcessTellRequest, m_fileServer, _1, _2, _3, _4));
  504. m_connectionManager->RegisterService(FileIsReadOnlyRequest::MessageType, std::bind(&FileServer::ProcessIsReadOnlyRequest, m_fileServer, _1, _2, _3, _4));
  505. m_connectionManager->RegisterService(PathIsDirectoryRequest::MessageType, std::bind(&FileServer::ProcessIsDirectoryRequest, m_fileServer, _1, _2, _3, _4));
  506. m_connectionManager->RegisterService(FileSizeRequest::MessageType, std::bind(&FileServer::ProcessSizeRequest, m_fileServer, _1, _2, _3, _4));
  507. m_connectionManager->RegisterService(FileModTimeRequest::MessageType, std::bind(&FileServer::ProcessModificationTimeRequest, m_fileServer, _1, _2, _3, _4));
  508. m_connectionManager->RegisterService(FileExistsRequest::MessageType, std::bind(&FileServer::ProcessExistsRequest, m_fileServer, _1, _2, _3, _4));
  509. m_connectionManager->RegisterService(FileFlushRequest::MessageType, std::bind(&FileServer::ProcessFlushRequest, m_fileServer, _1, _2, _3, _4));
  510. m_connectionManager->RegisterService(PathCreateRequest::MessageType, std::bind(&FileServer::ProcessCreatePathRequest, m_fileServer, _1, _2, _3, _4));
  511. m_connectionManager->RegisterService(PathDestroyRequest::MessageType, std::bind(&FileServer::ProcessDestroyPathRequest, m_fileServer, _1, _2, _3, _4));
  512. m_connectionManager->RegisterService(FileRemoveRequest::MessageType, std::bind(&FileServer::ProcessRemoveRequest, m_fileServer, _1, _2, _3, _4));
  513. m_connectionManager->RegisterService(FileCopyRequest::MessageType, std::bind(&FileServer::ProcessCopyRequest, m_fileServer, _1, _2, _3, _4));
  514. m_connectionManager->RegisterService(FileRenameRequest::MessageType, std::bind(&FileServer::ProcessRenameRequest, m_fileServer, _1, _2, _3, _4));
  515. m_connectionManager->RegisterService(FindFilesRequest::MessageType, std::bind(&FileServer::ProcessFindFileNamesRequest, m_fileServer, _1, _2, _3, _4));
  516. m_connectionManager->RegisterService(FileTreeRequest::MessageType, std::bind(&FileServer::ProcessFileTreeRequest, m_fileServer, _1, _2, _3, _4));
  517. QObject::connect(m_connectionManager, SIGNAL(connectionAdded(uint, Connection*)), m_fileServer, SLOT(ConnectionAdded(unsigned int, Connection*)));
  518. QObject::connect(m_connectionManager, SIGNAL(ConnectionDisconnected(unsigned int)), m_fileServer, SLOT(ConnectionRemoved(unsigned int)));
  519. QObject::connect(m_fileServer, SIGNAL(AddBytesReceived(unsigned int, qint64, bool)), m_connectionManager, SLOT(AddBytesReceived(unsigned int, qint64, bool)));
  520. QObject::connect(m_fileServer, SIGNAL(AddBytesSent(unsigned int, qint64, bool)), m_connectionManager, SLOT(AddBytesSent(unsigned int, qint64, bool)));
  521. QObject::connect(m_fileServer, SIGNAL(AddBytesRead(unsigned int, qint64, bool)), m_connectionManager, SLOT(AddBytesRead(unsigned int, qint64, bool)));
  522. QObject::connect(m_fileServer, SIGNAL(AddBytesWritten(unsigned int, qint64, bool)), m_connectionManager, SLOT(AddBytesWritten(unsigned int, qint64, bool)));
  523. QObject::connect(m_fileServer, SIGNAL(AddOpenRequest(unsigned int, bool)), m_connectionManager, SLOT(AddOpenRequest(unsigned int, bool)));
  524. QObject::connect(m_fileServer, SIGNAL(AddCloseRequest(unsigned int, bool)), m_connectionManager, SLOT(AddCloseRequest(unsigned int, bool)));
  525. QObject::connect(m_fileServer, SIGNAL(AddOpened(unsigned int, bool)), m_connectionManager, SLOT(AddOpened(unsigned int, bool)));
  526. QObject::connect(m_fileServer, SIGNAL(AddClosed(unsigned int, bool)), m_connectionManager, SLOT(AddClosed(unsigned int, bool)));
  527. QObject::connect(m_fileServer, SIGNAL(AddReadRequest(unsigned int, bool)), m_connectionManager, SLOT(AddReadRequest(unsigned int, bool)));
  528. QObject::connect(m_fileServer, SIGNAL(AddWriteRequest(unsigned int, bool)), m_connectionManager, SLOT(AddWriteRequest(unsigned int, bool)));
  529. QObject::connect(m_fileServer, SIGNAL(AddTellRequest(unsigned int, bool)), m_connectionManager, SLOT(AddTellRequest(unsigned int, bool)));
  530. QObject::connect(m_fileServer, SIGNAL(AddSeekRequest(unsigned int, bool)), m_connectionManager, SLOT(AddSeekRequest(unsigned int, bool)));
  531. QObject::connect(m_fileServer, SIGNAL(AddIsReadOnlyRequest(unsigned int, bool)), m_connectionManager, SLOT(AddIsReadOnlyRequest(unsigned int, bool)));
  532. QObject::connect(m_fileServer, SIGNAL(AddIsDirectoryRequest(unsigned int, bool)), m_connectionManager, SLOT(AddIsDirectoryRequest(unsigned int, bool)));
  533. QObject::connect(m_fileServer, SIGNAL(AddSizeRequest(unsigned int, bool)), m_connectionManager, SLOT(AddSizeRequest(unsigned int, bool)));
  534. QObject::connect(m_fileServer, SIGNAL(AddModificationTimeRequest(unsigned int, bool)), m_connectionManager, SLOT(AddModificationTimeRequest(unsigned int, bool)));
  535. QObject::connect(m_fileServer, SIGNAL(AddExistsRequest(unsigned int, bool)), m_connectionManager, SLOT(AddExistsRequest(unsigned int, bool)));
  536. QObject::connect(m_fileServer, SIGNAL(AddFlushRequest(unsigned int, bool)), m_connectionManager, SLOT(AddFlushRequest(unsigned int, bool)));
  537. QObject::connect(m_fileServer, SIGNAL(AddCreatePathRequest(unsigned int, bool)), m_connectionManager, SLOT(AddCreatePathRequest(unsigned int, bool)));
  538. QObject::connect(m_fileServer, SIGNAL(AddDestroyPathRequest(unsigned int, bool)), m_connectionManager, SLOT(AddDestroyPathRequest(unsigned int, bool)));
  539. QObject::connect(m_fileServer, SIGNAL(AddRemoveRequest(unsigned int, bool)), m_connectionManager, SLOT(AddRemoveRequest(unsigned int, bool)));
  540. QObject::connect(m_fileServer, SIGNAL(AddCopyRequest(unsigned int, bool)), m_connectionManager, SLOT(AddCopyRequest(unsigned int, bool)));
  541. QObject::connect(m_fileServer, SIGNAL(AddRenameRequest(unsigned int, bool)), m_connectionManager, SLOT(AddRenameRequest(unsigned int, bool)));
  542. QObject::connect(m_fileServer, SIGNAL(AddFindFileNamesRequest(unsigned int, bool)), m_connectionManager, SLOT(AddFindFileNamesRequest(unsigned int, bool)));
  543. QObject::connect(m_fileServer, SIGNAL(UpdateConnectionMetrics()), m_connectionManager, SLOT(UpdateConnectionMetrics()));
  544. m_connectionManager->RegisterService(ShowAssetProcessorRequest::MessageType,
  545. std::bind([this](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray /*payload*/)
  546. {
  547. Q_EMIT ShowWindow();
  548. }, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)
  549. );
  550. m_connectionManager->RegisterService(ShowAssetInAssetProcessorRequest::MessageType,
  551. std::bind([this](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray payload)
  552. {
  553. ShowAssetInAssetProcessorRequest request;
  554. bool readFromStream = AZ::Utils::LoadObjectFromBufferInPlace(payload.data(), payload.size(), request);
  555. AZ_Assert(readFromStream, "GUIApplicationManager::ShowAssetInAssetProcessorRequest: Could not deserialize from stream");
  556. if (readFromStream)
  557. {
  558. m_mainWindow->HighlightAsset(request.m_assetPath.c_str());
  559. Q_EMIT ShowWindow();
  560. }
  561. }, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)
  562. );
  563. }
  564. void GUIApplicationManager::InitIniConfiguration()
  565. {
  566. m_iniConfiguration = new IniConfiguration();
  567. m_iniConfiguration->readINIConfigFile();
  568. m_iniConfiguration->parseCommandLine();
  569. }
  570. void GUIApplicationManager::DestroyIniConfiguration()
  571. {
  572. if (m_iniConfiguration)
  573. {
  574. delete m_iniConfiguration;
  575. m_iniConfiguration = nullptr;
  576. }
  577. }
  578. void GUIApplicationManager::InitFileServer()
  579. {
  580. m_fileServer = new FileServer();
  581. m_fileServer->SetSystemRoot(GetSystemRoot());
  582. }
  583. void GUIApplicationManager::DestroyFileServer()
  584. {
  585. if (m_fileServer)
  586. {
  587. delete m_fileServer;
  588. m_fileServer = nullptr;
  589. }
  590. }
  591. IniConfiguration* GUIApplicationManager::GetIniConfiguration() const
  592. {
  593. return m_iniConfiguration;
  594. }
  595. FileServer* GUIApplicationManager::GetFileServer() const
  596. {
  597. return m_fileServer;
  598. }
  599. void GUIApplicationManager::ShowTrayIconErrorMessage(QString msg)
  600. {
  601. AZStd::chrono::steady_clock::time_point currentTime = AZStd::chrono::steady_clock::now();
  602. if (m_trayIcon && m_mainWindow)
  603. {
  604. if((currentTime - m_timeWhenLastWarningWasShown) >= AZStd::chrono::milliseconds(s_errorMessageBoxDelay))
  605. {
  606. m_timeWhenLastWarningWasShown = currentTime;
  607. m_trayIcon->showMessage(
  608. QCoreApplication::translate("Tray Icon", "O3DE Asset Processor"),
  609. QCoreApplication::translate("Tray Icon", msg.toUtf8().data()),
  610. QSystemTrayIcon::Critical, 3000);
  611. }
  612. }
  613. }
  614. void GUIApplicationManager::QuitRequested()
  615. {
  616. m_startupErrorCollector = nullptr;
  617. ApplicationManagerBase::QuitRequested();
  618. }
  619. void GUIApplicationManager::ShowTrayIconMessage(QString msg)
  620. {
  621. if (m_trayIcon && m_mainWindow && !m_mainWindow->isVisible())
  622. {
  623. m_trayIcon->showMessage(
  624. QCoreApplication::translate("Tray Icon", "O3DE Asset Processor"),
  625. QCoreApplication::translate("Tray Icon", msg.toUtf8().data()),
  626. QSystemTrayIcon::Information, 3000);
  627. }
  628. }
  629. bool GUIApplicationManager::Restart()
  630. {
  631. bool launched = QProcess::startDetached(QCoreApplication::applicationFilePath(), QCoreApplication::arguments());
  632. if (!launched)
  633. {
  634. QMessageBox::critical(nullptr,
  635. QCoreApplication::translate("application", "Unable to launch Asset Processor"),
  636. QCoreApplication::translate("application", "Unable to launch Asset Processor"));
  637. }
  638. return launched;
  639. }
  640. void GUIApplicationManager::Reflect()
  641. {
  642. ApplicationManagerBase::Reflect();
  643. AZ::SerializeContext* context = nullptr;
  644. AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  645. AZ_Assert(context, "No serialize context");
  646. AzToolsFramework::LogPanel::BaseLogPanel::Reflect(context);
  647. AssetProcessor::PlatformConfiguration::Reflect(context);
  648. }
  649. const char* GUIApplicationManager::GetLogBaseName()
  650. {
  651. return "AP_GUI";
  652. }
  653. ApplicationManager::RegistryCheckInstructions GUIApplicationManager::PopupRegistryProblemsMessage(QString warningText)
  654. {
  655. warningText = warningText.arg(tr("Click the Restart button"));
  656. // Doing all of this as a custom dialog because QMessageBox
  657. // has a fixed width, which doesn't display the extremely large
  658. // block of warning text well.
  659. QDialog dialog(nullptr, Qt::WindowCloseButtonHint | Qt::WindowTitleHint);
  660. dialog.setWindowTitle("Asset Processor Error");
  661. QVBoxLayout* layout = new QVBoxLayout(&dialog);
  662. layout->addSpacing(16);
  663. QHBoxLayout* messageLayout = new QHBoxLayout(&dialog);
  664. QLabel* icon = new QLabel("", &dialog);
  665. QPixmap errorIcon(":/stylesheet/img/lineedit-invalid.png");
  666. errorIcon = errorIcon.scaled(errorIcon.size() * 4);
  667. icon->setPixmap(errorIcon);
  668. icon->setMaximumSize(errorIcon.size());
  669. QLabel* label = new QLabel(warningText, &dialog);
  670. messageLayout->addWidget(icon);
  671. messageLayout->addSpacing(16);
  672. messageLayout->addWidget(label);
  673. layout->addLayout(messageLayout);
  674. layout->addSpacing(16);
  675. QDialogButtonBox* buttons = new QDialogButtonBox(&dialog);
  676. QPushButton* exitButton = buttons->addButton(tr("Exit"), QDialogButtonBox::RejectRole);
  677. connect(exitButton, &QPushButton::pressed, &dialog, &QDialog::reject);
  678. QPushButton* restartButton = buttons->addButton(tr("Restart"), QDialogButtonBox::AcceptRole);
  679. connect(restartButton, &QPushButton::pressed, &dialog, &QDialog::accept);
  680. layout->addWidget(buttons);
  681. if (dialog.exec() == QDialog::Accepted)
  682. {
  683. return RegistryCheckInstructions::Restart;
  684. }
  685. else
  686. {
  687. return RegistryCheckInstructions::Exit;
  688. }
  689. }
  690. void GUIApplicationManager::InitSourceControl()
  691. {
  692. // Look in the editor's settings for the Source Control value
  693. constexpr AZStd::string_view enableSourceControlKey = "/Amazon/Settings/EnableSourceControl";
  694. bool enableSourceControl = false;
  695. if (const auto* registry = AZ::SettingsRegistry::Get())
  696. {
  697. bool potentialValue;
  698. if (registry->Get(potentialValue, enableSourceControlKey))
  699. {
  700. enableSourceControl = AZStd::move(potentialValue);
  701. }
  702. }
  703. const AzFramework::CommandLine* commandLine = nullptr;
  704. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  705. if (commandLine->HasSwitch("enablescm"))
  706. {
  707. enableSourceControl = true;
  708. }
  709. AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(&AzToolsFramework::SourceControlConnectionRequestBus::Events::EnableSourceControl, enableSourceControl);
  710. if (!enableSourceControl)
  711. {
  712. // Source control is disabled, emit the SourceControlReady signal immediately since the source control system will not emit it
  713. Q_EMIT SourceControlReady();
  714. }
  715. // Register the source control status request - whenever it comes in, we need to reset our source control
  716. // to follow that state:
  717. if (m_connectionManager)
  718. {
  719. auto refreshSourceControl = [](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray payload, QString /*platform*/)
  720. {
  721. AzFramework::AssetSystem::UpdateSourceControlStatusRequest request;
  722. bool readFromStream = AZ::Utils::LoadObjectFromBufferInPlace(payload.data(), payload.size(), request);
  723. AZ_Assert(readFromStream, "GUIApplicationManager::UpdateSourceControlStatusRequest: Could not deserialize from stream");
  724. if (readFromStream)
  725. {
  726. AzToolsFramework::SourceControlState state = AzToolsFramework::SourceControlState::Disabled;
  727. AzToolsFramework::SourceControlConnectionRequestBus::BroadcastResult(state, &AzToolsFramework::SourceControlConnectionRequestBus::Events::GetSourceControlState);
  728. bool wasEnabled = state != AzToolsFramework::SourceControlState::Disabled;
  729. bool isEnabled = request.m_sourceControlEnabled;
  730. if (wasEnabled != isEnabled)
  731. {
  732. AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(&AzToolsFramework::SourceControlConnectionRequestBus::Events::EnableSourceControl, isEnabled);
  733. }
  734. }
  735. };
  736. m_connectionManager->RegisterService(AzFramework::AssetSystem::UpdateSourceControlStatusRequest::MessageType, refreshSourceControl);
  737. }
  738. }
  739. bool GUIApplicationManager::GetShouldExitOnIdle() const
  740. {
  741. bool shouldExit = false;
  742. const AzFramework::CommandLine* commandLine = nullptr;
  743. AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
  744. if (commandLine->HasSwitch("quitonidle"))
  745. {
  746. shouldExit = true;
  747. }
  748. return shouldExit;
  749. }