// Copyright Epic Games, Inc. All Rights Reserved. #include "BuildStorageTool.h" #include "Experimental/ZenServerInterface.h" #include "Features/IModularFeatures.h" #include "Framework/Application/SlateApplication.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Framework/Notifications/NotificationManager.h" #include "GenericPlatform/GenericPlatformFile.h" #include "HAL/PlatformProcess.h" #include "Misc/CompilationResult.h" #include "Misc/MessageDialog.h" #include "Misc/Optional.h" #include "Misc/Timespan.h" #include "Modules/ModuleInterface.h" #include "Modules/ModuleManager.h" #include "RequiredProgramMainCPPInclude.h" #include "SBuildActivity.h" #include "SBuildSelection.h" #include "SBuildLogin.h" #include "SMessageDialog.h" #include "StandaloneRenderer.h" #include "BuildStorageToolStyle.h" #include "Tasks/Task.h" #include "Templates/SharedPointer.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Notifications/SNotificationList.h" #include "Widgets/Text/STextBlock.h" #include "ZenServiceInstanceManager.h" #include "BuildServiceInstanceManager.h" #if PLATFORM_WINDOWS #include "Runtime/Launch/Resources/Windows/Resource.h" #include "Windows/WindowsApplication.h" #include "Windows/AllowWindowsPlatformTypes.h" #include #include "Windows/HideWindowsPlatformTypes.h" #elif PLATFORM_LINUX #include "UnixCommonStartup.h" #elif PLATFORM_MAC #include "Mac/MacProgramDelegate.h" #include "LaunchEngineLoop.h" #else #error "Unsupported platform!" #endif #define LOCTEXT_NAMESPACE "BuildStorageTool" // These macros are not properly defined by UBT in the case of an engine program with bTreatAsEngineModule=true // So define them here as a workaround #define IMPLEMENT_ENCRYPTION_KEY_REGISTRATION() #define IMPLEMENT_SIGNING_KEY_REGISTRATION() IMPLEMENT_APPLICATION(BuildStorageTool, "BuildStorageTool"); static void OnRequestExit() { RequestEngineExit(TEXT("BuildStorageTool Closed")); } static void HideOnCloseOverride(const TSharedRef& WindowBeingClosed) { WindowBeingClosed->HideWindow(); } class FBuildStorageToolApp { FCriticalSection CriticalSection; FSlateApplication& Slate; TSharedPtr Window; TSharedPtr CompileNotification; TSharedPtr ZenServiceInstanceManager; TSharedPtr BuildServiceInstanceManager; std::atomic bLatentExclusiveOperationActive = false; void ExitTool() { FSlateApplication::Get().RequestDestroyWindow(Window.ToSharedRef()); } bool CanExecuteExclusiveAction() { return !bLatentExclusiveOperationActive; } TSharedRef< SWidget > MakeMainMenu() { FMenuBarBuilder MenuBuilder( nullptr ); { // Control MenuBuilder.AddPullDownMenu( LOCTEXT( "FileMenu", "File" ), LOCTEXT( "FileMenu_ToolTip", "Opens the file menu" ), FOnGetContent::CreateRaw( this, &FBuildStorageToolApp::FillFileMenu ) ); } // Create the menu bar TSharedRef< SWidget > MenuBarWidget = MenuBuilder.MakeWidget(); MenuBarWidget->SetVisibility( EVisibility::Visible ); // Work around for menu bar not showing on Mac return MenuBarWidget; } TSharedRef FillFileMenu() { const bool bCloseSelfOnly = false; const bool bSearchable = false; const bool bRecursivelySearchable = false; FMenuBuilder MenuBuilder(true, nullptr, TSharedPtr(), bCloseSelfOnly, &FCoreStyle::Get(), bSearchable, NAME_None, bRecursivelySearchable); MenuBuilder.AddMenuEntry( LOCTEXT("Exit", "Exit"), LOCTEXT("Exit_ToolTip", "Exits the Storage Server UI"), FSlateIcon(), FUIAction( FExecuteAction::CreateRaw( this, &FBuildStorageToolApp::ExitTool ), FCanExecuteAction::CreateRaw(this, &FBuildStorageToolApp::CanExecuteExclusiveAction) ), NAME_None, EUserInterfaceActionType::Button ); return MenuBuilder.MakeWidget(); } public: FBuildStorageToolApp(FSlateApplication& InSlate) : Slate(InSlate) { ZenServiceInstanceManager = MakeShared(); BuildServiceInstanceManager = MakeShared(); InstallMessageDialogOverride(); } virtual ~FBuildStorageToolApp() { RemoveMessageDialogOverride(); } void Run() { Window = SNew(SWindow) .Title(GetWindowTitle()) .ClientSize(FVector2D(900.0f, 600.0f)) .ActivationPolicy(EWindowActivationPolicy::Always) .SizingRule(ESizingRule::UserSized) .IsTopmostWindow(false) .FocusWhenFirstShown(false) .SupportsMaximize(true) .SupportsMinimize(true) .HasCloseButton(true) [ SNew(SBorder) .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) [ SNew(SVerticalBox) // Menu + SVerticalBox::Slot() .AutoHeight() [ MakeMainMenu() ] // Login panel + SVerticalBox::Slot() .AutoHeight() .Padding(0.0f, 10.0f, 5.0f, 0.0f) .HAlign(HAlign_Fill) .VAlign(VAlign_Top) [ SNew(SBuildLogin) .ZenServiceInstance(ZenServiceInstanceManager.ToSharedRef(), &UE::Zen::FServiceInstanceManager::GetZenServiceInstance) .BuildServiceInstance(BuildServiceInstanceManager.ToSharedRef(), &UE::Zen::Build::FServiceInstanceManager::GetBuildServiceInstance) ] // Selection panel + SVerticalBox::Slot() .Padding(0.0f, 10.0f, 5.0f, 0.0f) .HAlign(HAlign_Fill) .VAlign(VAlign_Top) [ SNew(SBuildSelection) .ZenServiceInstance(ZenServiceInstanceManager.ToSharedRef(), &UE::Zen::FServiceInstanceManager::GetZenServiceInstance) .BuildServiceInstance(BuildServiceInstanceManager.ToSharedRef(), &UE::Zen::Build::FServiceInstanceManager::GetBuildServiceInstance) .OnBuildTransferStarted_Lambda([this] (UE::Zen::Build::FBuildServiceInstance::FBuildTransfer Transfer, FStringView Name, FStringView Platform) { BuildActivity->AddBuildTransfer(Transfer, Name, Platform); }) ] // Activity panel + SVerticalBox::Slot() .AutoHeight() .Padding(0.0f, 10.0f, 5.0f, 0.0f) .HAlign(HAlign_Fill) .VAlign(VAlign_Bottom) [ SAssignNew(BuildActivity, SBuildActivity) .ZenServiceInstance(ZenServiceInstanceManager.ToSharedRef(), &UE::Zen::FServiceInstanceManager::GetZenServiceInstance) .BuildServiceInstance(BuildServiceInstanceManager.ToSharedRef(), &UE::Zen::Build::FServiceInstanceManager::GetBuildServiceInstance) ] ] ]; Slate.AddWindow(Window.ToSharedRef(), true); // Setting focus seems to have to happen after the Window has been added Slate.ClearKeyboardFocus(EFocusCause::Cleared); // loop until the app is ready to quit while (!IsEngineExitRequested()) { BeginExitIfRequested(); FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread); FTSTicker::GetCoreTicker().Tick(FApp::GetDeltaTime()); Slate.PumpMessages(); Slate.Tick(); FPlatformProcess::Sleep(1.0f / 30.0f); } // Make sure the window is hidden, because it might take a while for the background thread to finish. Window->HideWindow(); } private: FText GetWindowTitle() { return LOCTEXT("WindowTitle", "Unreal Build Storage Tool"); } EAppReturnType::Type OnModalMessageDialog(EAppMsgCategory InMessageCategory, EAppMsgType::Type InMessage, const FText& InText, const FText& InTitle) { if (IsInGameThread() && FSlateApplication::IsInitialized() && FSlateApplication::Get().CanAddModalWindow()) { return OpenModalMessageDialog_Internal(InMessageCategory, InMessage, InText, InTitle, Window); } else { return FPlatformMisc::MessageBoxExt(InMessage, *InText.ToString(), *InTitle.ToString()); } } void InstallMessageDialogOverride() { FCoreDelegates::ModalMessageDialog.BindRaw(this, &FBuildStorageToolApp::OnModalMessageDialog); } void RemoveMessageDialogOverride() { FCoreDelegates::ModalMessageDialog.Unbind(); } TSharedPtr BuildActivity; }; int BuildStorageToolMain(const TCHAR* CmdLine) { ON_SCOPE_EXIT { RequestEngineExit(TEXT("Exiting")); FEngineLoop::AppPreExit(); FModuleManager::Get().UnloadModulesAtShutdown(); FEngineLoop::AppExit(); }; const FTaskTagScope Scope(ETaskTag::EGameThread); // start up the main loop GEngineLoop.PreInit(CmdLine); FSystemWideCriticalSection SystemWideBuildStorageToolCritSec(TEXT("BuildStorageTool")); if (!SystemWideBuildStorageToolCritSec.IsValid()) { return true; } check(GConfig && GConfig->IsReadyForUse()); // Initialize high DPI mode FSlateApplication::InitHighDPI(true); { // Create the platform slate application (what FSlateApplication::Get() returns) TSharedRef Slate = FSlateApplication::Create(MakeShareable(FPlatformApplicationMisc::CreateApplication())); { // Initialize renderer TSharedRef SlateRenderer = GetStandardStandaloneRenderer(); // Try to initialize the renderer. It's possible that we launched when the driver crashed so try a few times before giving up. bool bRendererInitialized = Slate->InitializeRenderer(SlateRenderer, true); if (!bRendererInitialized) { // Close down the Slate application FSlateApplication::Shutdown(); return false; } // Set the normal UE IsEngineExitRequested() when outer frame is closed Slate->SetExitRequestedHandler(FSimpleDelegate::CreateStatic(&OnRequestExit)); // Prepare the custom Slate styles FBuildStorageToolStyle::Initialize(); // Set the icon FAppStyle::SetAppStyleSet(FBuildStorageToolStyle::Get()); // Run the inner application loop FBuildStorageToolApp App(Slate.Get()); App.Run(); // Clean up the custom styles FBuildStorageToolStyle::Shutdown(); } // Close down the Slate application FSlateApplication::Shutdown(); } return true; } #if PLATFORM_WINDOWS int WINAPI WinMain(_In_ HINSTANCE hCurrInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) { hInstance = hCurrInstance; return BuildStorageToolMain(GetCommandLineW())? 0 : 1; } #elif PLATFORM_LINUX int main(int argc, char* argv[]) { return CommonUnixMain(argc, argv, &BuildStorageToolMain); } #elif PLATFORM_MAC int main(int argc, char* argv[]) { [MacProgramDelegate mainWithArgc : argc argv : argv programMain : BuildStorageToolMain programExit : FEngineLoop::AppExit] ; } #else #error "Unsupported platform!" #endif #undef LOCTEXT_NAMESPACE