2019-12-26 23:06:02 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-06-03 15:32:00 -04:00
# include "UserInterfaceCommand.h"
# include "Async/TaskGraphInterfaces.h"
2020-01-14 04:50:47 -05:00
//#include "Brushes/SlateImageBrush.h"
2019-06-03 15:32:00 -04:00
# include "Containers/Ticker.h"
2020-10-08 05:07:59 -04:00
# include "CoreGlobals.h"
2022-05-09 13:12:28 -04:00
# include "Styling/AppStyle.h"
2019-06-03 15:32:00 -04:00
# include "Framework/Application/SlateApplication.h"
# include "Framework/Docking/LayoutService.h"
# include "Framework/Docking/TabManager.h"
# include "HAL/PlatformApplicationMisc.h"
# include "HAL/PlatformProcess.h"
# include "Interfaces/IPluginManager.h"
# include "ISlateReflectorModule.h"
# include "ISourceCodeAccessModule.h"
# include "Misc/CommandLine.h"
# include "Misc/ConfigCacheIni.h"
2020-01-14 04:50:47 -05:00
# include "Misc/Parse.h"
2019-06-03 15:32:00 -04:00
# include "Modules/ModuleManager.h"
# include "StandaloneRenderer.h"
# include "Widgets/Docking/SDockTab.h"
// Insights
# include "Insights/IUnrealInsightsModule.h"
2020-01-14 04:50:47 -05:00
# include "Insights/Version.h"
2019-06-03 15:32:00 -04:00
2020-01-16 07:28:54 -05:00
# if PLATFORM_WINDOWS
# include "Windows/AllowWindowsPlatformTypes.h"
# include "Windows/MinWindows.h"
# include "Windows/HideWindowsPlatformTypes.h"
# endif
# if PLATFORM_UNIX
# include <sys/file.h>
# include <errno.h>
# endif
2019-06-03 15:32:00 -04:00
////////////////////////////////////////////////////////////////////////////////////////////////////
# define IDEAL_FRAMERATE 60
2021-04-14 09:17:09 -04:00
# define BACKGROUND_FRAMERATE 4
# define IDLE_INPUT_SECONDS 5.0f
2019-06-03 15:32:00 -04:00
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace UserInterfaceCommand
{
TSharedRef < FWorkspaceItem > DeveloperTools = FWorkspaceItem : : NewGroup ( NSLOCTEXT ( " UnrealInsights " , " DeveloperToolsMenu " , " Developer Tools " ) ) ;
2021-04-14 09:17:09 -04:00
bool IsApplicationBackground ( )
{
return ! FPlatformApplicationMisc : : IsThisApplicationForeground ( ) & & ( FPlatformTime : : Seconds ( ) - FSlateApplication : : Get ( ) . GetLastUserInteractionTime ( ) ) > IDLE_INPUT_SECONDS ;
}
void AdaptiveSleep ( float Seconds )
{
const double IdealFrameTime = 1.0 / IDEAL_FRAMERATE ;
if ( Seconds > IdealFrameTime )
{
// While in background, pump message at ideal frame time and get out of background as soon as input is received
const double WakeupTime = FPlatformTime : : Seconds ( ) + Seconds ;
while ( IsApplicationBackground ( ) & & FPlatformTime : : Seconds ( ) < WakeupTime )
{
FSlateApplication : : Get ( ) . PumpMessages ( ) ;
FPlatformProcess : : Sleep ( ( float ) FMath : : Clamp ( WakeupTime - FPlatformTime : : Seconds ( ) , 0.0 , IdealFrameTime ) ) ;
}
}
else
{
FPlatformProcess : : Sleep ( Seconds ) ;
}
}
2019-06-03 15:32:00 -04:00
}
////////////////////////////////////////////////////////////////////////////////////////////////////
2020-01-16 07:28:54 -05:00
bool CheckSessionBrowserSingleInstance ( )
{
# if PLATFORM_WINDOWS
2021-06-21 06:29:41 -04:00
// Create a named event that other processes can detect.
// It allows only a single instance of Unreal Insights (Browser Mode).
// The event is also used by runtime to choose when to try to auto-connect.
// See FTraceAuxiliary::TryAutoConnect() in \Runtime\Core\Private\ProfilingDebugging\TraceAuxiliary.cpp
HANDLE SessionBrowserEvent = CreateEvent ( NULL , true , false , TEXT ( " Local \\ UnrealInsightsBrowser " ) ) ;
2020-01-16 07:28:54 -05:00
if ( SessionBrowserEvent = = NULL | | GetLastError ( ) = = ERROR_ALREADY_EXISTS )
{
// Another Session Browser process is already running.
if ( SessionBrowserEvent ! = NULL )
{
CloseHandle ( SessionBrowserEvent ) ;
}
// Activate the respective window.
2021-12-10 17:46:06 -05:00
HWND Window = FindWindowW ( 0 , L " Unreal Insights Session Browser " ) ;
2020-01-16 07:28:54 -05:00
if ( Window )
{
ShowWindow ( Window , SW_SHOW ) ;
SetForegroundWindow ( Window ) ;
2020-02-05 14:26:36 -05:00
FLASHWINFO FlashInfo ;
FlashInfo . cbSize = sizeof ( FLASHWINFO ) ;
FlashInfo . hwnd = Window ;
FlashInfo . dwFlags = FLASHW_ALL ;
FlashInfo . uCount = 3 ;
FlashInfo . dwTimeout = 0 ;
FlashWindowEx ( & FlashInfo ) ;
2020-01-16 07:28:54 -05:00
}
return false ;
}
# endif // PLATFORM_WINDOWS
# if PLATFORM_UNIX
2021-06-21 06:29:41 -04:00
int FileHandle = open ( " /var/run/UnrealInsightsBrowser.pid " , O_CREAT | O_RDWR , 0666 ) ;
2020-01-16 07:28:54 -05:00
int Ret = flock ( FileHandle , LOCK_EX | LOCK_NB ) ;
if ( Ret & & EWOULDBLOCK = = errno )
{
// Another Session Browser process is already running.
// Activate the respective window.
//TODO: "wmctrl -a Insights"
return false ;
}
# endif
return true ;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
2019-06-03 15:32:00 -04:00
void FUserInterfaceCommand : : Run ( )
{
2020-09-24 00:43:27 -04:00
const uint32 MaxPath = FPlatformMisc : : GetMaxPathLength ( ) ;
TCHAR * TraceFile = new TCHAR [ MaxPath + 1 ] ;
TraceFile [ 0 ] = 0 ;
bool bOpenTraceFile = false ;
2020-01-16 07:28:54 -05:00
// Only a single instance of Session Browser window/process is allowed.
{
2020-01-23 05:11:17 -05:00
bool bBrowserMode = true ;
2020-01-16 07:28:54 -05:00
2020-01-23 05:11:17 -05:00
if ( bBrowserMode )
2020-01-16 07:28:54 -05:00
{
2020-02-22 18:11:49 -05:00
bBrowserMode = FCString : : Strifind ( FCommandLine : : Get ( ) , TEXT ( " -OpenTraceId= " ) ) = = nullptr ;
2020-01-23 05:11:17 -05:00
}
if ( bBrowserMode )
{
2020-09-24 00:43:27 -04:00
bOpenTraceFile = GetTraceFileFromCmdLine ( TraceFile , MaxPath ) ;
bBrowserMode = ! bOpenTraceFile ;
2020-01-23 05:11:17 -05:00
}
if ( bBrowserMode & & ! CheckSessionBrowserSingleInstance ( ) )
{
return ;
2020-01-16 07:28:54 -05:00
}
}
2019-06-03 15:32:00 -04:00
2020-05-26 10:28:30 -04:00
//FCoreStyle::ResetToDefault();
2019-06-03 15:32:00 -04:00
2020-06-23 18:40:00 -04:00
// Crank up a normal Slate application using the platform's standalone renderer.
FSlateApplication : : InitializeAsStandaloneApplication ( GetStandardStandaloneRenderer ( ) ) ;
2019-06-03 15:32:00 -04:00
// Load required modules.
FModuleManager : : Get ( ) . LoadModuleChecked ( " TraceInsights " ) ;
// Load plug-ins.
// @todo: allow for better plug-in support in standalone Slate applications
IPluginManager : : Get ( ) . LoadModulesForEnabledPlugins ( ELoadingPhase : : PreDefault ) ;
2020-01-24 09:34:25 -05:00
IPluginManager : : Get ( ) . LoadModulesForEnabledPlugins ( ELoadingPhase : : Default ) ;
2019-06-03 15:32:00 -04:00
// Load optional modules.
2020-06-24 13:38:03 -04:00
if ( FModuleManager : : Get ( ) . ModuleExists ( TEXT ( " SettingsEditor " ) ) )
{
FModuleManager : : Get ( ) . LoadModule ( " SettingsEditor " ) ;
}
2019-06-03 15:32:00 -04:00
2020-09-24 00:43:27 -04:00
InitializeSlateApplication ( bOpenTraceFile , TraceFile ) ;
delete [ ] TraceFile ;
TraceFile = nullptr ;
2019-06-03 15:32:00 -04:00
// Initialize source code access.
// Load the source code access module.
ISourceCodeAccessModule & SourceCodeAccessModule = FModuleManager : : LoadModuleChecked < ISourceCodeAccessModule > ( FName ( " SourceCodeAccess " ) ) ;
// Manually load in the source code access plugins, as standalone programs don't currently support plugins.
# if PLATFORM_MAC
2020-11-02 14:48:49 -04:00
IModuleInterface & XCodeSourceCodeAccessModule = FModuleManager : : LoadModuleChecked < IModuleInterface > ( FName ( " XCodeSourceCodeAccess " ) ) ;
SourceCodeAccessModule . SetAccessor ( FName ( " XCodeSourceCodeAccess " ) ) ;
2019-06-03 15:32:00 -04:00
# elif PLATFORM_WINDOWS
2020-11-02 14:48:49 -04:00
IModuleInterface & VisualStudioSourceCodeAccessModule = FModuleManager : : LoadModuleChecked < IModuleInterface > ( FName ( " VisualStudioSourceCodeAccess " ) ) ;
SourceCodeAccessModule . SetAccessor ( FName ( " VisualStudioSourceCodeAccess " ) ) ;
2019-06-03 15:32:00 -04:00
# endif
# if WITH_SHARED_POINTER_TESTS
2021-04-22 13:07:08 -04:00
SharedPointerTesting : : TestSharedPointer < ESPMode : : NotThreadSafe > ( ) ;
2019-06-03 15:32:00 -04:00
SharedPointerTesting : : TestSharedPointer < ESPMode : : ThreadSafe > ( ) ;
# endif
// Enter main loop.
double DeltaTime = 0.0 ;
double LastTime = FPlatformTime : : Seconds ( ) ;
const float IdealFrameTime = 1.0f / IDEAL_FRAMERATE ;
2021-04-14 09:17:09 -04:00
const float BackgroundFrameTime = 1.0f / BACKGROUND_FRAMERATE ;
2019-06-03 15:32:00 -04:00
2019-09-12 14:21:26 -04:00
while ( ! IsEngineExitRequested ( ) )
2019-06-03 15:32:00 -04:00
{
// Save the state of the tabs here rather than after close of application (the tabs are undesirably saved out with ClosedTab state on application close).
//UserInterfaceCommand::UserConfiguredNewLayout = FGlobalTabmanager::Get()->PersistLayout();
FTaskGraphInterface : : Get ( ) . ProcessThreadUntilIdle ( ENamedThreads : : GameThread ) ;
FSlateApplication : : Get ( ) . PumpMessages ( ) ;
FSlateApplication : : Get ( ) . Tick ( ) ;
2021-08-16 11:09:22 -04:00
FTSTicker : : GetCoreTicker ( ) . Tick ( DeltaTime ) ;
2019-06-03 15:32:00 -04:00
// Throttle frame rate.
2021-04-14 09:17:09 -04:00
const float FrameTime = UserInterfaceCommand : : IsApplicationBackground ( ) ? BackgroundFrameTime : IdealFrameTime ;
UserInterfaceCommand : : AdaptiveSleep ( FMath : : Max < float > ( 0.0f , FrameTime - ( FPlatformTime : : Seconds ( ) - LastTime ) ) ) ;
2019-06-03 15:32:00 -04:00
double CurrentTime = FPlatformTime : : Seconds ( ) ;
DeltaTime = CurrentTime - LastTime ;
LastTime = CurrentTime ;
FStats : : AdvanceFrame ( false ) ;
2020-01-20 10:25:29 -05:00
FCoreDelegates : : OnEndFrame . Broadcast ( ) ;
2019-06-03 15:32:00 -04:00
GLog - > FlushThreadedLogs ( ) ; //im: ???
2020-10-08 05:07:59 -04:00
GFrameCounter + + ;
2019-06-03 15:32:00 -04:00
}
//im: ??? FCoreDelegates::OnExit.Broadcast();
2020-01-14 04:50:47 -05:00
ShutdownSlateApplication ( ) ;
2019-06-03 15:32:00 -04:00
}
////////////////////////////////////////////////////////////////////////////////////////////////////
2020-09-24 00:43:27 -04:00
void FUserInterfaceCommand : : InitializeSlateApplication ( bool bOpenTraceFile , const TCHAR * TraceFile )
2019-06-03 15:32:00 -04:00
{
2021-07-19 08:55:22 -04:00
FSlateApplication : : InitHighDPI ( true ) ;
2019-10-03 16:26:48 -04:00
2020-01-14 04:50:47 -05:00
//const FSlateBrush* AppIcon = new FSlateImageBrush(FPaths::EngineContentDir() / "Editor/Slate/Icons/Insights/AppIcon_24x.png", FVector2D(24.0f, 24.0f));
//FSlateApplication::Get().SetAppIcon(AppIcon);
2019-06-03 15:32:00 -04:00
// Set the application name.
2020-01-14 04:50:47 -05:00
const FText ApplicationTitle = FText : : Format ( NSLOCTEXT ( " UnrealInsights " , " AppTitle " , " Unreal Insights {0} " ) , FText : : FromString ( TEXT ( UNREAL_INSIGHTS_VERSION_STRING_EX ) ) ) ;
FGlobalTabmanager : : Get ( ) - > SetApplicationTitle ( ApplicationTitle ) ;
2019-06-03 15:32:00 -04:00
// Load widget reflector.
const bool bAllowDebugTools = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " DebugTools " ) ) ;
if ( bAllowDebugTools )
{
FModuleManager : : LoadModuleChecked < ISlateReflectorModule > ( " SlateReflector " ) . RegisterTabSpawner ( UserInterfaceCommand : : DeveloperTools ) ;
}
IUnrealInsightsModule & TraceInsightsModule = FModuleManager : : LoadModuleChecked < IUnrealInsightsModule > ( " TraceInsights " ) ;
2020-01-23 05:11:17 -05:00
uint32 TraceId = 0 ;
2020-02-22 18:11:49 -05:00
bool bUseTraceId = FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -OpenTraceId= " ) , TraceId ) ;
2019-06-03 15:32:00 -04:00
2022-03-07 08:10:36 -05:00
const uint32 MaxPath = FPlatformMisc : : GetMaxPathLength ( ) ;
2020-02-05 14:26:36 -05:00
TCHAR * StoreHost = new TCHAR [ MaxPath + 1 ] ;
FCString : : Strcpy ( StoreHost , MaxPath , TEXT ( " 127.0.0.1 " ) ) ;
uint32 StorePort = 0 ;
bool bUseCustomStoreAddress = false ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -Store= " ) , StoreHost , MaxPath , true ) )
{
TCHAR * Port = FCString : : Strchr ( StoreHost , TEXT ( ' : ' ) ) ;
if ( Port )
{
* Port = 0 ;
Port + + ;
StorePort = FCString : : Atoi ( Port ) ;
}
bUseCustomStoreAddress = true ;
}
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -StoreHost= " ) , StoreHost , MaxPath , true ) )
{
bUseCustomStoreAddress = true ;
}
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -StorePort= " ) , StorePort ) )
{
bUseCustomStoreAddress = true ;
}
2020-09-24 00:43:27 -04:00
const bool bInitializeTesting = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " InsightsTest " ) ) ;
2022-03-07 08:10:36 -05:00
if ( bUseTraceId | | bOpenTraceFile ) // viewer mode
2020-01-14 04:50:47 -05:00
{
2022-03-07 08:10:36 -05:00
// This parameter will cause the application to close when analysis fails to start or completes successfully.
const bool bAutoQuit = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " AutoQuit " ) ) ;
2020-09-24 00:43:27 -04:00
2022-03-07 08:10:36 -05:00
if ( bInitializeTesting )
{
const bool bInitAutomationModules = true ;
TraceInsightsModule . InitializeTesting ( bInitAutomationModules , bAutoQuit ) ;
2020-09-24 00:43:27 -04:00
}
2022-03-07 08:10:36 -05:00
TCHAR Cmd [ 1024 ] ;
bool bExecuteCommand = false ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -ExecOnAnalysisCompleteCmd= " ) , Cmd , 1024 , false ) )
{
bExecuteCommand = true ;
}
if ( bExecuteCommand )
{
TraceInsightsModule . ScheduleCommand ( Cmd ) ;
}
const bool bNoUI = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " NoUI " ) ) ;
2021-07-29 08:37:01 -04:00
if ( ! bNoUI )
{
TraceInsightsModule . CreateSessionViewer ( bAllowDebugTools ) ;
}
2022-03-07 08:10:36 -05:00
if ( bUseTraceId )
2020-01-23 05:11:17 -05:00
{
2022-03-07 08:10:36 -05:00
TraceInsightsModule . ConnectToStore ( StoreHost , StorePort ) ;
TraceInsightsModule . StartAnalysisForTrace ( TraceId , bAutoQuit ) ;
2020-01-23 05:11:17 -05:00
}
else
{
2022-03-07 08:10:36 -05:00
TraceInsightsModule . StartAnalysisForTraceFile ( TraceFile , bAutoQuit ) ;
2020-01-23 05:11:17 -05:00
}
}
2022-03-07 08:10:36 -05:00
else // browser mode
{
if ( bUseCustomStoreAddress )
{
TraceInsightsModule . ConnectToStore ( StoreHost , StorePort ) ;
}
else
{
TraceInsightsModule . CreateDefaultStore ( ) ;
}
FCreateSessionBrowserParams Params ;
Params . bAllowDebugTools = bAllowDebugTools ;
Params . bInitializeTesting = bInitializeTesting ;
Params . bStartProcessWithStompMalloc = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " stompmalloc " ) ) ;
TraceInsightsModule . CreateSessionBrowser ( Params ) ;
}
2020-02-05 14:26:36 -05:00
delete [ ] StoreHost ;
2019-06-03 15:32:00 -04:00
}
////////////////////////////////////////////////////////////////////////////////////////////////////
2020-01-14 04:50:47 -05:00
void FUserInterfaceCommand : : ShutdownSlateApplication ( )
2019-06-03 15:32:00 -04:00
{
2020-01-14 04:50:47 -05:00
IUnrealInsightsModule & TraceInsightsModule = FModuleManager : : LoadModuleChecked < IUnrealInsightsModule > ( " TraceInsights " ) ;
TraceInsightsModule . ShutdownUserInterface ( ) ;
2019-06-03 15:32:00 -04:00
// Shut down application.
FSlateApplication : : Shutdown ( ) ;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
2020-09-24 00:43:27 -04:00
bool FUserInterfaceCommand : : GetTraceFileFromCmdLine ( TCHAR * OutTraceFile , uint32 MaxPath )
{
// Try getting the trace file from the -OpenTraceFile= paramter first.
bool bUseTraceFile = FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -OpenTraceFile= " ) , OutTraceFile , MaxPath , true ) ;
if ( bUseTraceFile )
{
return true ;
}
// Support opening a trace file by double clicking a .utrace file.
// In this case, the app will receive as the first parameter a utrace file path.
const TCHAR * CmdLine = FCommandLine : : Get ( ) ;
bool HasToken = FParse : : Token ( CmdLine , OutTraceFile , MaxPath , false ) ;
if ( HasToken )
{
FString Token = OutTraceFile ;
if ( Token . EndsWith ( TEXT ( " .utrace " ) ) )
{
bUseTraceFile = true ;
}
}
return bUseTraceFile ;
}
////////////////////////////////////////////////////////////////////////////////////////////////////