3
0

GUIApplicationManager.cpp 36 KB

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