/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include "native/utilities/GUIApplicationManager.h" #include "native/connection/connectionManager.h" #include "native/utilities/IniConfiguration.h" #include "native/utilities/GUIApplicationServer.h" #include "native/resourcecompiler/rccontroller.h" #include "native/FileServer/fileServer.h" #include "native/AssetManager/assetScanner.h" #include #include #include #include #include #include #include #include #include #include #if defined(EXTERNAL_CRASH_REPORTING) #include #endif #if defined(AZ_PLATFORM_MAC) #include #include #endif #include #include #include #include #include using namespace AssetProcessor; namespace { static const int s_errorMessageBoxDelay = 5000; void RemoveTemporaries() { // get currently running app QFileInfo moduleFileInfo; char executableDirectory[AZ_MAX_PATH_LEN]; AZ::Utils::GetExecutablePathReturnType result = AZ::Utils::GetExecutablePath(executableDirectory, AZStd::size(executableDirectory)); if (result.m_pathStored == AZ::Utils::ExecutablePathResult::Success) { moduleFileInfo.setFile(executableDirectory); } QDir binaryDir = moduleFileInfo.absoluteDir(); // strip extension QString applicationBase = moduleFileInfo.completeBaseName(); // add wildcard filter applicationBase.append("*_tmp"); // set to qt binaryDir.setNameFilters(QStringList() << applicationBase); binaryDir.setFilter(QDir::Files); // iterate all matching foreach(QString tempFile, binaryDir.entryList()) { binaryDir.remove(tempFile); } } } ErrorCollector::~ErrorCollector() { if (!m_errorMessages.isEmpty()) { MessageWindow messageWindow(m_parent); messageWindow.SetHeaderText("The following errors occurred during startup:"); messageWindow.SetMessageText(m_errorMessages); messageWindow.SetTitleText("Startup Errors"); messageWindow.exec(); } } void ErrorCollector::AddError(AZStd::string message) { QString qMessage(message.c_str()); qMessage = qMessage.trimmed(); m_errorMessages << qMessage; } GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, QObject* parent) : GUIApplicationManager(argc, argv, parent, {}) { } GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, AZ::ComponentApplicationSettings componentAppSettings) : GUIApplicationManager(argc, argv, nullptr, AZStd::move(componentAppSettings)) { } GUIApplicationManager::GUIApplicationManager(int* argc, char*** argv, QObject* parent, AZ::ComponentApplicationSettings componentAppSettings) : ApplicationManagerBase(argc, argv, parent, AZStd::move(componentAppSettings)) { #if defined(AZ_PLATFORM_MAC) // Since AP is not shipped as a '.app' package, it will not receive keyboard focus // unless we tell the OS specifically to treat it as a foreground application. ProcessSerialNumber psn = { 0, kCurrentProcess }; TransformProcessType(&psn, kProcessTransformToForegroundApplication); #endif } GUIApplicationManager::~GUIApplicationManager() { Destroy(); } ApplicationManager::BeforeRunStatus GUIApplicationManager::BeforeRun() { AssetProcessor::MessageInfoBus::Handler::BusConnect(); ApplicationManager::BeforeRunStatus status = ApplicationManagerBase::BeforeRun(); if (status != ApplicationManager::BeforeRunStatus::Status_Success) { return status; } // The build process may leave behind some temporaries, try to delete them RemoveTemporaries(); QDir projectAssetRoot; AssetUtilities::ComputeAssetRoot(projectAssetRoot); #if defined(EXTERNAL_CRASH_REPORTING) CrashHandler::ToolsCrashHandler::InitCrashHandler("AssetProcessor", projectAssetRoot.absolutePath().toStdString()); #endif // we have to monitor both the cache folder and the database file and restart AP if either of them gets deleted // It is important to note that we are monitoring the parent folder and not the actual cache folder itself since // we want to handle the use case on Mac OS if the user moves the cache folder to the trash. m_qtFileWatcher.addPath(projectAssetRoot.absolutePath()); QDir projectCacheRoot; AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot); QString assetDbPath = projectCacheRoot.filePath("assetdb.sqlite"); m_qtFileWatcher.addPath(assetDbPath); // if our Gems file changes, make sure we watch that, too. QString projectPath = AssetUtilities::ComputeProjectPath(); QObject::connect(&m_qtFileWatcher, &QFileSystemWatcher::fileChanged, this, &GUIApplicationManager::FileChanged); QObject::connect(&m_qtFileWatcher, &QFileSystemWatcher::directoryChanged, this, &GUIApplicationManager::DirectoryChanged); return ApplicationManager::BeforeRunStatus::Status_Success; } void GUIApplicationManager::Destroy() { m_startupErrorCollector = nullptr; if (m_mainWindow) { delete m_mainWindow; m_mainWindow = nullptr; } AssetProcessor::MessageInfoBus::Handler::BusDisconnect(); ApplicationManagerBase::Destroy(); DestroyIniConfiguration(); DestroyFileServer(); } bool GUIApplicationManager::Run() { qRegisterMetaType("AZ::u32"); qRegisterMetaType("AZ::Uuid"); AZ::IO::FixedMaxPath engineRootPath; if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); } AzQtComponents::StyleManager* styleManager = new AzQtComponents::StyleManager(qApp); styleManager->initialize(qApp, engineRootPath); QDir engineRoot; AssetUtilities::ComputeAssetRoot(engineRoot); AssetUtilities::ComputeEngineRoot(engineRoot); AzQtComponents::StyleManager::addSearchPaths( QStringLiteral("style"), engineRoot.filePath(QStringLiteral("Code/Tools/AssetProcessor/native/ui/style")), QStringLiteral(":/AssetProcessor/style"), engineRootPath); m_mainWindow = new MainWindow(this); auto wrapper = new AzQtComponents::WindowDecorationWrapper( #if defined(AZ_PLATFORM_WINDOWS) // On windows we do want our custom title bar AzQtComponents::WindowDecorationWrapper::OptionAutoAttach #else // On others platforms we don't want our custom title bar (ie, use native title bars). AzQtComponents::WindowDecorationWrapper::OptionDisabled #endif ); wrapper->setGuest(m_mainWindow); // Use this variant of the enableSaveRestoreGeometry because the global QCoreApplication::setOrganization and setApplicationName // are called in ApplicationManager::Activate, which happens much later on in this function. // ApplicationManager::Activate is shared between the Batch version of the AP and the GUI version, // and there are thing const bool restoreOnFirstShow = true; wrapper->enableSaveRestoreGeometry(GetOrganizationName(), GetApplicationName(), "MainWindow", restoreOnFirstShow); AzQtComponents::StyleManager::setStyleSheet(m_mainWindow, QStringLiteral("style:AssetProcessor.qss")); auto refreshStyleSheets = [styleManager]() { styleManager->Refresh(); }; // CheckForRegistryProblems can pop up a dialog, so we need to check after // we initialize the stylesheet bool showErrorMessageOnRegistryProblem = true; RegistryCheckInstructions registryCheckInstructions = CheckForRegistryProblems(m_mainWindow, showErrorMessageOnRegistryProblem); if (registryCheckInstructions != RegistryCheckInstructions::Continue) { if (registryCheckInstructions == RegistryCheckInstructions::Restart) { Restart(); } return false; } bool startHidden = QApplication::arguments().contains("--start-hidden", Qt::CaseInsensitive); if (!startHidden) { wrapper->show(); } else { #ifdef AZ_PLATFORM_WINDOWS // Qt / Windows has issues if the main window isn't shown once // so we show it then hide it wrapper->show(); // Have a delay on the hide, to make sure that the show is entirely processed // first QTimer::singleShot(0, wrapper, &QWidget::hide); #endif } #ifdef AZ_PLATFORM_MAC connect(new MacDockIconHandler(this), &MacDockIconHandler::dockIconClicked, m_mainWindow, &MainWindow::ShowWindow); #endif QAction* quitAction = new QAction(QObject::tr("Quit"), m_mainWindow); quitAction->setShortcut(QKeySequence(0x0 | Qt::CTRL | Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); m_mainWindow->addAction(quitAction); m_mainWindow->connect(quitAction, SIGNAL(triggered()), this, SLOT(QuitRequested())); QAction* refreshAction = new QAction(QObject::tr("Refresh Stylesheet"), m_mainWindow); refreshAction->setShortcut(QKeySequence(0x0 | Qt::CTRL | Qt::Key_R)); m_mainWindow->addAction(refreshAction); m_mainWindow->connect(refreshAction, &QAction::triggered, this, refreshStyleSheets); QObject::connect(this, &GUIApplicationManager::ShowWindow, m_mainWindow, &MainWindow::ShowWindow); if (QSystemTrayIcon::isSystemTrayAvailable()) { QAction* showAction = new QAction(QObject::tr("Show"), m_mainWindow); QObject::connect(showAction, &QAction::triggered, m_mainWindow, &MainWindow::ShowWindow); QAction* hideAction = new QAction(QObject::tr("Hide"), m_mainWindow); QObject::connect(hideAction, &QAction::triggered, wrapper, &AzQtComponents::WindowDecorationWrapper::hide); QMenu* trayIconMenu = new QMenu(); trayIconMenu->addAction(showAction); trayIconMenu->addAction(hideAction); trayIconMenu->addSeparator(); #if AZ_TRAIT_OS_PLATFORM_APPLE QAction* systemTrayQuitAction = new QAction(QObject::tr("Quit"), m_mainWindow); systemTrayQuitAction->setMenuRole(QAction::NoRole); m_mainWindow->connect(systemTrayQuitAction, SIGNAL(triggered()), this, SLOT(QuitRequested())); trayIconMenu->addAction(systemTrayQuitAction); #else trayIconMenu->addAction(quitAction); #endif m_trayIcon = new QSystemTrayIcon(QIcon(":/o3de_assetprocessor_taskbar.svg"), m_mainWindow); m_trayIcon->setContextMenu(trayIconMenu); m_trayIcon->setToolTip(QObject::tr("O3DE Asset Processor")); m_trayIcon->show(); QObject::connect(m_trayIcon, &QSystemTrayIcon::activated, m_mainWindow, [&, wrapper](QSystemTrayIcon::ActivationReason reason) { if (reason == QSystemTrayIcon::DoubleClick) { if (wrapper->isVisible()) { wrapper->hide(); } else { m_mainWindow->ShowWindow(); } } }); QObject::connect(m_trayIcon, &QSystemTrayIcon::messageClicked, m_mainWindow, &MainWindow::ShowWindow); if (startHidden) { m_trayIcon->showMessage( QCoreApplication::translate("Tray Icon", "O3DE Asset Processor has started"), QCoreApplication::translate("Tray Icon", "The O3DE Asset Processor monitors raw project assets and converts those assets into runtime-ready data."), QSystemTrayIcon::Information, 3000); } } connect(this, &GUIApplicationManager::AssetProcessorStatusChanged, m_mainWindow, &MainWindow::OnAssetProcessorStatusChanged); if (!Activate()) { return false; } m_mainWindow->Activate(); connect(GetAssetScanner(), &AssetProcessor::AssetScanner::AssetScanningStatusChanged, this, [this](AssetScanningStatus status) { if (status == AssetScanningStatus::Started) { AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Scanning_Started); m_mainWindow->OnAssetProcessorStatusChanged(entry); } }); connect(GetRCController(), &AssetProcessor::RCController::ActiveJobsCountChanged, this, &GUIApplicationManager::OnActiveJobsCountChanged); connect(this, &GUIApplicationManager::ConnectionStatusMsg, this, &GUIApplicationManager::ShowTrayIconMessage); qApp->setQuitOnLastWindowClosed(false); m_duringStartup = false; m_startedSuccessfully = true; int resultCode = qApp->exec(); // this blocks until the last window is closed. if(!InitiatedShutdown()) { // if we are here it implies that AP did not stop the Qt event loop and is shutting down prematurely // we need to call QuitRequested and start the event loop once again so that AP shuts down correctly QuitRequested(); resultCode = qApp->exec(); } if (m_trayIcon) { m_trayIcon->hide(); delete m_trayIcon; } m_mainWindow->SaveLogPanelState(); // mainWindow indirectly uses some UserSettings so clean it up before we clean those up delete m_mainWindow; m_mainWindow = nullptr; AZ::SerializeContext* context = nullptr; AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext); AZ_Assert(context, "No serialize context"); QDir projectCacheRoot; AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot); m_localUserSettings.Save(projectCacheRoot.filePath("AssetProcessorUserSettings.xml").toUtf8().data(), context); m_localUserSettings.Deactivate(); if (NeedRestart()) { bool launched = Restart(); if (!launched) { return false; } } Destroy(); return !resultCode && m_startedSuccessfully; } void GUIApplicationManager::NegotiationFailed() { 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."); QMetaObject::invokeMethod(this, "ShowMessageBox", Qt::QueuedConnection, Q_ARG(QString, QString("Negotiation Failed")), Q_ARG(QString, message), Q_ARG(bool, false)); } void GUIApplicationManager::OnAssetFailed(const AZStd::string& sourceFileName) { QString message = tr("Error : %1 failed to compile\nPlease check the Asset Processor for more information.").arg(QString::fromUtf8(sourceFileName.c_str())); QMetaObject::invokeMethod(this, "ShowTrayIconErrorMessage", Qt::QueuedConnection, Q_ARG(QString, message)); } void GUIApplicationManager::OnErrorMessage(const char* error) { QMessageBox msgBox; msgBox.setText(QCoreApplication::translate("errors", error)); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.exec(); } void GUIApplicationManager::ShowMessageBox(QString title, QString msg, bool isCritical) { if (!m_messageBoxIsVisible) { // Only show the message box if it is not visible m_messageBoxIsVisible = true; QMessageBox msgBox(m_mainWindow); msgBox.setWindowTitle(title); msgBox.setText(msg); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); if (isCritical) { msgBox.setIcon(QMessageBox::Critical); } msgBox.exec(); m_messageBoxIsVisible = false; } } bool GUIApplicationManager::OnError(const char* /*window*/, const char* message) { // if we're in a worker thread, errors must not pop up a dialog box if (AssetProcessor::GetThreadLocalJobId() != 0) { // just absorb the error, do not perform default op return true; } if (m_startupErrorCollector) { m_startupErrorCollector->AddError(message); return true; } if (!InitiatedShutdown()) { // During quitting, we don't pop up error message boxes. // instead, we're going to return true, which will cause it to // process to the log file instead. return true; } // If we're the main thread, then consider showing the message box directly. // note that all other threads will PAUSE if they emit a message while the main thread is showing this box // due to the way the trace system EBUS is mutex-protected. Qt::ConnectionType connection = Qt::DirectConnection; if (QThread::currentThread() != qApp->thread()) { connection = Qt::QueuedConnection; } QMetaObject::invokeMethod(this, "ShowMessageBox", connection, Q_ARG(QString, QString("Error")), Q_ARG(QString, QString(message)), Q_ARG(bool, true)); return true; } bool GUIApplicationManager::OnAssert(const char* message) { if (!OnError(nullptr, message)) { return false; } // Asserts should be severe enough for data corruption, // so the process should quit to avoid that happening for users. if (!AZ::Debug::Trace::Instance().IsDebuggerPresent()) { QuitRequested(); return true; } AZ::Debug::Trace::Instance().Break(); return true; } WId GUIApplicationManager::GetWindowId() const { return m_mainWindow->effectiveWinId(); } bool GUIApplicationManager::Activate() { m_startupErrorCollector = AZStd::make_unique(m_mainWindow); AZ::SerializeContext* context = nullptr; AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext); AZ_Assert(context, "No serialize context"); QDir projectCacheRoot; AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot); m_localUserSettings.Load(projectCacheRoot.filePath("AssetProcessorUserSettings.xml").toUtf8().data(), context); m_localUserSettings.Activate(AZ::UserSettings::CT_LOCAL); InitIniConfiguration(); InitFileServer(); //activate the base stuff. if (!ApplicationManagerBase::Activate()) { return false; } return true; } bool GUIApplicationManager::PostActivate() { if (!ApplicationManagerBase::PostActivate()) { m_startupErrorCollector = nullptr; m_startedSuccessfully = false; return false; } m_fileWatcher->StartWatching(); m_startupErrorCollector = nullptr; return true; } void GUIApplicationManager::CreateQtApplication() { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); // Qt actually modifies the argc and argv, you must pass the real ones in as ref so it can. m_qApp = new QApplication(*m_frameworkApp.GetArgC(), *m_frameworkApp.GetArgV()); } void GUIApplicationManager::DirectoryChanged([[maybe_unused]] QString path) { QDir projectCacheRoot; AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot); if (!projectCacheRoot.exists() || !projectCacheRoot.exists("assetdb.sqlite")) { // If either the Cache directory or database file has been removed, we need to restart QTimer::singleShot(200, this, [this]() { QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection); }); } } void GUIApplicationManager::FileChanged(QString path) { QDir projectCacheRoot; AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot); QString assetDbPath = projectCacheRoot.filePath("assetdb.sqlite"); if (QString::compare(AssetUtilities::NormalizeFilePath(path), assetDbPath, Qt::CaseInsensitive) == 0) { if (!QFile::exists(assetDbPath)) { // if the database file is deleted we need to restart QTimer::singleShot(200, this, [this]() { QMetaObject::invokeMethod(this, "Restart", Qt::QueuedConnection); }); } } } bool GUIApplicationManager::InitApplicationServer() { m_applicationServer = new GUIApplicationServer(); return true; } void GUIApplicationManager::InitConnectionManager() { ApplicationManagerBase::InitConnectionManager(); using namespace std::placeholders; using namespace AzFramework::AssetSystem; using namespace AzToolsFramework::AssetSystem; //File Server related m_connectionManager->RegisterService(FileOpenRequest::MessageType, std::bind(&FileServer::ProcessOpenRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileCloseRequest::MessageType, std::bind(&FileServer::ProcessCloseRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileReadRequest::MessageType, std::bind(&FileServer::ProcessReadRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileWriteRequest::MessageType, std::bind(&FileServer::ProcessWriteRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileSeekRequest::MessageType, std::bind(&FileServer::ProcessSeekRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileTellRequest::MessageType, std::bind(&FileServer::ProcessTellRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileIsReadOnlyRequest::MessageType, std::bind(&FileServer::ProcessIsReadOnlyRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(PathIsDirectoryRequest::MessageType, std::bind(&FileServer::ProcessIsDirectoryRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileSizeRequest::MessageType, std::bind(&FileServer::ProcessSizeRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileModTimeRequest::MessageType, std::bind(&FileServer::ProcessModificationTimeRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileExistsRequest::MessageType, std::bind(&FileServer::ProcessExistsRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileFlushRequest::MessageType, std::bind(&FileServer::ProcessFlushRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(PathCreateRequest::MessageType, std::bind(&FileServer::ProcessCreatePathRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(PathDestroyRequest::MessageType, std::bind(&FileServer::ProcessDestroyPathRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileRemoveRequest::MessageType, std::bind(&FileServer::ProcessRemoveRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileCopyRequest::MessageType, std::bind(&FileServer::ProcessCopyRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileRenameRequest::MessageType, std::bind(&FileServer::ProcessRenameRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FindFilesRequest::MessageType, std::bind(&FileServer::ProcessFindFileNamesRequest, m_fileServer, _1, _2, _3, _4)); m_connectionManager->RegisterService(FileTreeRequest::MessageType, std::bind(&FileServer::ProcessFileTreeRequest, m_fileServer, _1, _2, _3, _4)); QObject::connect(m_connectionManager, SIGNAL(connectionAdded(uint, Connection*)), m_fileServer, SLOT(ConnectionAdded(unsigned int, Connection*))); QObject::connect(m_connectionManager, SIGNAL(ConnectionDisconnected(unsigned int)), m_fileServer, SLOT(ConnectionRemoved(unsigned int))); QObject::connect(m_fileServer, SIGNAL(AddBytesReceived(unsigned int, qint64, bool)), m_connectionManager, SLOT(AddBytesReceived(unsigned int, qint64, bool))); QObject::connect(m_fileServer, SIGNAL(AddBytesSent(unsigned int, qint64, bool)), m_connectionManager, SLOT(AddBytesSent(unsigned int, qint64, bool))); QObject::connect(m_fileServer, SIGNAL(AddBytesRead(unsigned int, qint64, bool)), m_connectionManager, SLOT(AddBytesRead(unsigned int, qint64, bool))); QObject::connect(m_fileServer, SIGNAL(AddBytesWritten(unsigned int, qint64, bool)), m_connectionManager, SLOT(AddBytesWritten(unsigned int, qint64, bool))); QObject::connect(m_fileServer, SIGNAL(AddOpenRequest(unsigned int, bool)), m_connectionManager, SLOT(AddOpenRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddCloseRequest(unsigned int, bool)), m_connectionManager, SLOT(AddCloseRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddOpened(unsigned int, bool)), m_connectionManager, SLOT(AddOpened(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddClosed(unsigned int, bool)), m_connectionManager, SLOT(AddClosed(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddReadRequest(unsigned int, bool)), m_connectionManager, SLOT(AddReadRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddWriteRequest(unsigned int, bool)), m_connectionManager, SLOT(AddWriteRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddTellRequest(unsigned int, bool)), m_connectionManager, SLOT(AddTellRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddSeekRequest(unsigned int, bool)), m_connectionManager, SLOT(AddSeekRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddIsReadOnlyRequest(unsigned int, bool)), m_connectionManager, SLOT(AddIsReadOnlyRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddIsDirectoryRequest(unsigned int, bool)), m_connectionManager, SLOT(AddIsDirectoryRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddSizeRequest(unsigned int, bool)), m_connectionManager, SLOT(AddSizeRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddModificationTimeRequest(unsigned int, bool)), m_connectionManager, SLOT(AddModificationTimeRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddExistsRequest(unsigned int, bool)), m_connectionManager, SLOT(AddExistsRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddFlushRequest(unsigned int, bool)), m_connectionManager, SLOT(AddFlushRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddCreatePathRequest(unsigned int, bool)), m_connectionManager, SLOT(AddCreatePathRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddDestroyPathRequest(unsigned int, bool)), m_connectionManager, SLOT(AddDestroyPathRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddRemoveRequest(unsigned int, bool)), m_connectionManager, SLOT(AddRemoveRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddCopyRequest(unsigned int, bool)), m_connectionManager, SLOT(AddCopyRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddRenameRequest(unsigned int, bool)), m_connectionManager, SLOT(AddRenameRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(AddFindFileNamesRequest(unsigned int, bool)), m_connectionManager, SLOT(AddFindFileNamesRequest(unsigned int, bool))); QObject::connect(m_fileServer, SIGNAL(UpdateConnectionMetrics()), m_connectionManager, SLOT(UpdateConnectionMetrics())); m_connectionManager->RegisterService(ShowAssetProcessorRequest::MessageType, std::bind([this](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray /*payload*/) { Q_EMIT ShowWindow(); }, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4) ); m_connectionManager->RegisterService(ShowAssetInAssetProcessorRequest::MessageType, std::bind([this](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray payload) { ShowAssetInAssetProcessorRequest request; bool readFromStream = AZ::Utils::LoadObjectFromBufferInPlace(payload.data(), payload.size(), request); AZ_Assert(readFromStream, "GUIApplicationManager::ShowAssetInAssetProcessorRequest: Could not deserialize from stream"); if (readFromStream) { m_mainWindow->HighlightAsset(request.m_assetPath.c_str()); Q_EMIT ShowWindow(); } }, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4) ); } void GUIApplicationManager::InitIniConfiguration() { m_iniConfiguration = new IniConfiguration(); m_iniConfiguration->readINIConfigFile(); m_iniConfiguration->parseCommandLine(); } void GUIApplicationManager::DestroyIniConfiguration() { if (m_iniConfiguration) { delete m_iniConfiguration; m_iniConfiguration = nullptr; } } void GUIApplicationManager::InitFileServer() { m_fileServer = new FileServer(); m_fileServer->SetSystemRoot(GetSystemRoot()); } void GUIApplicationManager::DestroyFileServer() { if (m_fileServer) { delete m_fileServer; m_fileServer = nullptr; } } IniConfiguration* GUIApplicationManager::GetIniConfiguration() const { return m_iniConfiguration; } FileServer* GUIApplicationManager::GetFileServer() const { return m_fileServer; } void GUIApplicationManager::ShowTrayIconErrorMessage(QString msg) { AZStd::chrono::steady_clock::time_point currentTime = AZStd::chrono::steady_clock::now(); if (m_trayIcon && m_mainWindow) { if((currentTime - m_timeWhenLastWarningWasShown) >= AZStd::chrono::milliseconds(s_errorMessageBoxDelay)) { m_timeWhenLastWarningWasShown = currentTime; m_trayIcon->showMessage( QCoreApplication::translate("Tray Icon", "O3DE Asset Processor"), QCoreApplication::translate("Tray Icon", msg.toUtf8().data()), QSystemTrayIcon::Critical, 3000); } } } void GUIApplicationManager::QuitRequested() { m_startupErrorCollector = nullptr; ApplicationManagerBase::QuitRequested(); } void GUIApplicationManager::ShowTrayIconMessage(QString msg) { if (m_trayIcon && m_mainWindow && !m_mainWindow->isVisible()) { m_trayIcon->showMessage( QCoreApplication::translate("Tray Icon", "O3DE Asset Processor"), QCoreApplication::translate("Tray Icon", msg.toUtf8().data()), QSystemTrayIcon::Information, 3000); } } bool GUIApplicationManager::Restart() { bool launched = QProcess::startDetached(QCoreApplication::applicationFilePath(), QCoreApplication::arguments()); if (!launched) { QMessageBox::critical(nullptr, QCoreApplication::translate("application", "Unable to launch Asset Processor"), QCoreApplication::translate("application", "Unable to launch Asset Processor")); } return launched; } void GUIApplicationManager::Reflect() { ApplicationManagerBase::Reflect(); AZ::SerializeContext* context = nullptr; AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext); AZ_Assert(context, "No serialize context"); AzToolsFramework::LogPanel::BaseLogPanel::Reflect(context); AssetProcessor::PlatformConfiguration::Reflect(context); } const char* GUIApplicationManager::GetLogBaseName() { return "AP_GUI"; } ApplicationManager::RegistryCheckInstructions GUIApplicationManager::PopupRegistryProblemsMessage(QString warningText) { warningText = warningText.arg(tr("Click the Restart button")); // Doing all of this as a custom dialog because QMessageBox // has a fixed width, which doesn't display the extremely large // block of warning text well. QDialog dialog(nullptr, Qt::WindowCloseButtonHint | Qt::WindowTitleHint); dialog.setWindowTitle("Asset Processor Error"); QVBoxLayout* layout = new QVBoxLayout(&dialog); layout->addSpacing(16); QHBoxLayout* messageLayout = new QHBoxLayout(&dialog); QLabel* icon = new QLabel("", &dialog); QPixmap errorIcon(":/stylesheet/img/lineedit-invalid.png"); errorIcon = errorIcon.scaled(errorIcon.size() * 4); icon->setPixmap(errorIcon); icon->setMaximumSize(errorIcon.size()); QLabel* label = new QLabel(warningText, &dialog); messageLayout->addWidget(icon); messageLayout->addSpacing(16); messageLayout->addWidget(label); layout->addLayout(messageLayout); layout->addSpacing(16); QDialogButtonBox* buttons = new QDialogButtonBox(&dialog); QPushButton* exitButton = buttons->addButton(tr("Exit"), QDialogButtonBox::RejectRole); connect(exitButton, &QPushButton::pressed, &dialog, &QDialog::reject); QPushButton* restartButton = buttons->addButton(tr("Restart"), QDialogButtonBox::AcceptRole); connect(restartButton, &QPushButton::pressed, &dialog, &QDialog::accept); layout->addWidget(buttons); if (dialog.exec() == QDialog::Accepted) { return RegistryCheckInstructions::Restart; } else { return RegistryCheckInstructions::Exit; } } void GUIApplicationManager::InitSourceControl() { // Look in the editor's settings for the Source Control value constexpr AZStd::string_view enableSourceControlKey = "/Amazon/Settings/EnableSourceControl"; bool enableSourceControl = false; if (const auto* registry = AZ::SettingsRegistry::Get()) { bool potentialValue; if (registry->Get(potentialValue, enableSourceControlKey)) { enableSourceControl = AZStd::move(potentialValue); } } const AzFramework::CommandLine* commandLine = nullptr; AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine); if (commandLine->HasSwitch("enablescm")) { enableSourceControl = true; } AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(&AzToolsFramework::SourceControlConnectionRequestBus::Events::EnableSourceControl, enableSourceControl); if (!enableSourceControl) { // Source control is disabled, emit the SourceControlReady signal immediately since the source control system will not emit it Q_EMIT SourceControlReady(); } // Register the source control status request - whenever it comes in, we need to reset our source control // to follow that state: if (m_connectionManager) { auto refreshSourceControl = [](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray payload, QString /*platform*/) { AzFramework::AssetSystem::UpdateSourceControlStatusRequest request; bool readFromStream = AZ::Utils::LoadObjectFromBufferInPlace(payload.data(), payload.size(), request); AZ_Assert(readFromStream, "GUIApplicationManager::UpdateSourceControlStatusRequest: Could not deserialize from stream"); if (readFromStream) { AzToolsFramework::SourceControlState state = AzToolsFramework::SourceControlState::Disabled; AzToolsFramework::SourceControlConnectionRequestBus::BroadcastResult(state, &AzToolsFramework::SourceControlConnectionRequestBus::Events::GetSourceControlState); bool wasEnabled = state != AzToolsFramework::SourceControlState::Disabled; bool isEnabled = request.m_sourceControlEnabled; if (wasEnabled != isEnabled) { AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(&AzToolsFramework::SourceControlConnectionRequestBus::Events::EnableSourceControl, isEnabled); } } }; m_connectionManager->RegisterService(AzFramework::AssetSystem::UpdateSourceControlStatusRequest::MessageType, refreshSourceControl); } } bool GUIApplicationManager::GetShouldExitOnIdle() const { bool shouldExit = false; const AzFramework::CommandLine* commandLine = nullptr; AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine); if (commandLine->HasSwitch("quitonidle")) { shouldExit = true; } return shouldExit; }