diff --git a/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp b/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp index 2a300c64d25792cb5d0b3264fddb3859b9360368..01d0c4e25e05650df6ce6e9201c400bc0a8cedcd 100644 --- a/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp +++ b/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp @@ -1,337 +1,337 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -#include "NDisplayLaunchButton.h" - -#include "NDisplayLaunchButtonStyle.h" -#include "NDisplayLaunchButtonSettings.h" -#include "NDisplayLaunchButtonCommands.h" -#include "Misc/MessageDialog.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" -#include "Interfaces/IPluginManager.h" -#include "Misc/EngineVersion.h" -#include "Networking.h" -#include "SocketHelper.h" -#include "LevelEditor.h" -#include "FileHelpers.h" -#include "GameProjectGeneration/Public/GameProjectGenerationModule.h" -#include "Interfaces/IProjectManager.h" - -DEFINE_LOG_CATEGORY(LogNDisplayLaunchButton); - -void FNDisplayLaunchButtonModule::StartupModule() -{ - const UNDisplayLaunchButtonSettings* Settings = GetDefault<UNDisplayLaunchButtonSettings>(); - FNDisplayLaunchButtonStyle::Initialize(); - FNDisplayLaunchButtonStyle::ReloadTextures(); - FNDisplayLaunchButtonCommands::Register(); - - TSharedPtr<class FUICommandList> PluginCommands = MakeShareable(new FUICommandList); - PluginCommands->MapAction( - FNDisplayLaunchButtonCommands::Get().PluginAction, - FExecuteAction::CreateRaw(this, &FNDisplayLaunchButtonModule::PluginButtonClicked), - FCanExecuteAction()); - - FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor"); - TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender); - ToolbarExtender->AddToolBarExtension("Game", EExtensionHook::First, PluginCommands, FToolBarExtensionDelegate::CreateLambda([](FToolBarBuilder& Builder) - { - Builder.AddToolBarButton(FNDisplayLaunchButtonCommands::Get().PluginAction); - })); - LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); -} - -void FNDisplayLaunchButtonModule::ShutdownModule() -{ - FNDisplayLaunchButtonStyle::Shutdown(); - FNDisplayLaunchButtonCommands::Unregister(); -} - -/** - * Since some plugins interfere with nDisplay execution, this function can disable them - * @param PluginName - Name of the plugin to enable/disable - * @param NewState - The state to switch the plugin to - * @param OldState - Here the old state is stored AFTER the change - * @return true on Success - */ -bool FNDisplayLaunchButtonModule::ChangePluginStateAndStoreConfig(const FString PluginName, const bool NewState, bool& OldState) const -{ - const bool PreviousState = IPluginManager::Get().FindPlugin(PluginName)->IsEnabled(); - - FText FailMessage; - FGameProjectGenerationModule::Get().TryMakeProjectFileWriteable(FPaths::GetProjectFilePath()); - if (!IProjectManager::Get().SetPluginEnabled(PluginName, NewState, FailMessage)) - { - FMessageDialog::Open(EAppMsgType::Ok, FailMessage); - return false; - }else - { - if (!IProjectManager::Get().SaveCurrentProjectToDisk(FailMessage)) - { - FMessageDialog::Open(EAppMsgType::Ok, FailMessage); - return false; - } - } - - OldState = PreviousState; - return true; -} - -/** - * Get filename of the editor executable - */ -FString FNDisplayLaunchButtonModule::GetEditorExecutableName() -{ -#if PLATFORM_WINDOWS - return "UE4Editor.exe"; -#elif PLATFORM_LINUX - return "UE4Editor"; -#endif -} - -/** - * Get filename in the root dir of the project - */ -FString FNDisplayLaunchButtonModule::GetFilePathInProject(FString FileName) -{ - return FPaths::Combine(FPaths::ProjectDir(), FileName); -} - -/** - * Get the path to one of the bundled configs - * @param ConfigName - Name of the config without extension - * @return Path as FString - */ -FString FNDisplayLaunchButtonModule::GetConfigPath(FString ConfigName) -{ - return FPaths::Combine(IPluginManager::Get().FindPlugin("NDisplayLaunchButton")->GetBaseDir(), TEXT("LaunchConfig"), ConfigName + ".cfg"); -} - -/** - * Kill an Array of Process-handles after waiting for them for a few seconds - * @param Processes - Array of Processes to kill - * @param Num_Nodes - Number of Processes in the Array - */ -void FNDisplayLaunchButtonModule::KillProcesses(FProcHandle Processes[], const int Num_Nodes) -{ - float SecondsToWait = 5; - const float CheckInterval = 0.25; - FPlatformProcess::ConditionalSleep([Num_Nodes, Processes, CheckInterval, &SecondsToWait]() - { - SecondsToWait -= CheckInterval; - if(SecondsToWait <= 0) return true; - for (int i = 0; i < Num_Nodes; i++){ - if(FPlatformProcess::IsProcRunning(Processes[i])) return false; - } - return true; - }, CheckInterval); - for (int i = 0; i < Num_Nodes; i++) FPlatformProcess::TerminateProc(Processes[i]); -} - -/** - * Executed on click of the button in the toolbar - */ -void FNDisplayLaunchButtonModule::PluginButtonClicked() -{ - const UNDisplayLaunchButtonSettings* Settings = GetDefault<UNDisplayLaunchButtonSettings>(); - /* Check if we need to do something */ - if (Settings->LaunchType == ButtonLaunchType_NONE) - { - GEngine->AddOnScreenDebugMessage(-1, 3, FColor::White, TEXT("The Button is set to do nothing.")); - return; - } - - FString InsightParameters = ""; - if(Settings->bEnableInsights) - { - if (Settings->bStatNamedEvents) InsightParameters.Append("-statnamedevents"); - - TArray<FString> TraceChannels; - if (Settings->bLog) TraceChannels.Add("log"); - if (Settings->bBookmark) TraceChannels.Add("bookmark"); - if (Settings->bFrame) TraceChannels.Add("frame"); - if (Settings->bCPU) TraceChannels.Add("cpu"); - if (Settings->bGPU) TraceChannels.Add("gpu"); - if (Settings->bLoadTime) TraceChannels.Add("loadtime"); - if (Settings->bFile) TraceChannels.Add("file"); - if (Settings->bNet) TraceChannels.Add("net"); - if (TraceChannels.Num() > 0) InsightParameters.Append(FString::Printf(TEXT(" -trace=%s"), *FString::Join(TraceChannels, TEXT(",")))); - - if (Settings->TracehostIP.Len() > 0) InsightParameters.Append(FString::Printf(TEXT(" -tracehost=%s"), *Settings->TracehostIP)); - } - - /* Enable/Disable Plugins temporarily */ - if(!ChangePluginStateAndStoreConfig("SteamVR", false, SteamVRState)) return; - if(!ChangePluginStateAndStoreConfig("OculusVR", false, OculusVRState)) return; - - /* Trigger Editor save. Needed, else old version will be launched every time */ - if (!UEditorLoadingAndSavingUtils::SaveDirtyPackagesWithDialog(true, true)) return; - - /* minimize the root window to provide max performance for the previews. */ - TSharedPtr<SWindow> RootWindow = FGlobalTabmanager::Get()->GetRootWindow(); - if (RootWindow.IsValid()) RootWindow->Minimize(); - - /* Different Launch Types */ - if (Settings->LaunchType == ButtonLaunchType_ROLV) - { - FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *Settings->RolvConfig.FilePath, *((Settings->ROLVLogToProjectDir) ? ("-log ABSLOG=" + GetFilePathInProject("ROLV_Launch.log")) : "")); - - FProcHandle VRPN; - ProjectorDisplayType ModeFromBefore = DisplayType_Error; - if (Settings->StartVRPN) VRPN = FPlatformProcess::CreateProc(*Settings->VRPNPath.FilePath, *FString::Printf(TEXT("-f \"%s\" -millisleep 0"), *Settings->VRPNConfigPath.FilePath), false, false, false, nullptr, 0, nullptr, nullptr); - if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking start\0"); - if (Settings->SwitchProjector) ModeFromBefore = SwitchProjectorToState(Settings->ProjectorIP, Settings->ProjectorPort, Settings->ProjectorType); - - FProcHandle Instance = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Settings->ROLVLaunchParameters), true, false, false, nullptr, 0, nullptr, nullptr); - FPlatformProcess::WaitForProc(Instance); - - if (Settings->StartVRPN) FPlatformProcess::TerminateProc(VRPN); - FPlatformProcess::CloseProc(VRPN); - if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking stop\0"); - if (Settings->SwitchProjector && ModeFromBefore != DisplayType_Error) SwitchProjectorToState(Settings->ProjectorIP, Settings->ProjectorPort, ModeFromBefore); - } - else if (Settings->LaunchType == ButtonLaunchType_MiniCAVE) - { - const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("minicave"), *Settings->MiniCAVELaunchParameters); - - const int Num_Nodes = 5; - FString Windows_Node_Specific_Commands[Num_Nodes] = { - "dc_node=node_floor WinX=720 WinY=300 ResX=480 ResY=480" + FString((Settings->MiniCAVELogMasterWindow) ? " -log" : "") + ((Settings->MiniCAVELogToProjectDirFloor) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Floor_Master.log")) : "") + " " + Settings->MiniCAVEAdditionalLaunchParametersMaster, - "dc_node=node_front WinX=720 WinY=0 ResX=480 ResY=300" + ((Settings->MiniCAVELogToProjectDirFront) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Front.log")) : ""), - "dc_node=node_left WinX=420 WinY=300 ResX=300 ResY=480" + ((Settings->MiniCAVELogToProjectDirLeft) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Left.log" )) : ""), - "dc_node=node_right WinX=1200 WinY=300 ResX=300 ResY=480" + ((Settings->MiniCAVELogToProjectDirRight) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Right.log")) : ""), - "dc_node=node_back WinX=720 WinY=780 ResX=480 ResY=300" + ((Settings->MiniCAVELogToProjectDirBack) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Back.log" )) : "") - }; - - FProcHandle Processes[Num_Nodes]; - for (int i = 0; i < Num_Nodes; i++) - { - Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); - } - FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */ - - KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */ - } - else if (Settings->LaunchType == ButtonLaunchType_TWO_SCREEN) - { - const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("twoscreen"), *Settings->TwoScreenLaunchParameters); - - const int Num_Nodes = 2; - FString Windows_Node_Specific_Commands[Num_Nodes] = { - "dc_node=node_left WinX=200 WinY=200 ResX=480 ResY=480" + FString((Settings->TwoScreenLogMasterWindow) ? " -log" : "") + ((Settings->TwoScreenLogToProjectDirLeft) ? (" ABSLOG=" + GetFilePathInProject("TwoScreen_Left_Master.log")) : "") + " " + Settings->TwoScreenAdditionalLaunchParametersMaster, - "dc_node=node_right WinX=682 WinY=200 ResX=480 ResY=480" + ((Settings->TwoScreenLogToProjectDirRight) ? (" ABSLOG = " + GetFilePathInProject("TwoScreen_Right.log")) : "") - }; - - FProcHandle Processes[Num_Nodes]; - for (int i = 0; i < Num_Nodes; i++) - { - Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); - } - FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */ - - KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */ - } - else if (Settings->LaunchType == ButtonLaunchType_TDW) - { - const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("tileddisplaywall") ,*Settings->TiledDisplayWallLaunchParameters); - - const int Num_Nodes = 6; - FString Windows_Node_Specific_Commands[Num_Nodes] = { - "dc_node=node_tl WinX=200 WinY=200 ResX=480 ResY=272" + FString((Settings->TiledDisplayWallLogMasterWindow) ? " -log" : "") + ((Settings->TiledDisplayWallLogToProjectDirTL) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_TL_Master.log")) : "") + " " + Settings->TiledDisplayWallAdditionalLaunchParametersMaster, - "dc_node=node_tm WinX=693 WinY=200 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirTM) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_TM.log")) : ""), - "dc_node=node_tr WinX=1186 WinY=200 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirTR) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_TR.log")) : ""), - "dc_node=node_bl WinX=200 WinY=483 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBL) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_BL.log")) : ""), - "dc_node=node_bm WinX=693 WinY=483 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBM) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_BM.log")) : ""), - "dc_node=node_br WinX=1186 WinY=483 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBR) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_BR.log")) : "") - }; - - FProcHandle Processes[Num_Nodes]; - for (int i = 0; i < Num_Nodes; i++) - { - Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); - } - FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */ - - KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */ - } - else if (Settings->LaunchType == ButtonLaunchType_CAVE) - { - FProcHandle Instance = FPlatformProcess::CreateProc( - *Settings->CAVELaunchScriptPath.FilePath, - *("\"" + (FPaths::ConvertRelativePathToFull(".") + "/UE4Editor\"") - + " " + FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath()) - + " " + FString::FromInt(FEngineVersion::Current().GetMajor()) - + FString::FromInt(FEngineVersion::Current().GetMinor()) - + " " + InsightParameters), // Test this - true, false, false, nullptr, 0, nullptr, nullptr); - FPlatformProcess::WaitForProc(Instance); - } - - /* Re-maximize Editor Window */ - if (RootWindow.IsValid()) RootWindow->Maximize(); - - /* Restore Plugin States */ - ChangePluginStateAndStoreConfig("SteamVR", SteamVRState, SteamVRState); - ChangePluginStateAndStoreConfig("OculusVR", OculusVRState, OculusVRState); -} - -/** - * Send a command to the DTrack controller - * @param Address - IP address of DTrack controller - * @param Port - Port to contact DTrack controller - * @param Message - Message to send - */ -void FNDisplayLaunchButtonModule::SendToDTrack(FString Address, int Port, FString Message) -{ - FSocket* Socket = USocketHelper::OpenSocket(Address, Port, "DTrackSocket"); - if (!Socket) return; - if (USocketHelper::SendSocket(Socket, Message) <= 0) return; - FString Response = USocketHelper::ReceiveSocket<100>(Socket); - if (Response.Compare("dtrack2 ok") != 0) - { - UE_LOG(LogTemp, Error, TEXT("DTrack Command Failed. Response: '%s'"), *Response); - } - Socket->Shutdown(ESocketShutdownMode::ReadWrite); - Socket->Close(); -} - -/** - * Switch the display mode of the projector a 3D display mode or back - * @param Address - IP address of projector - * @param Port - Port to contact projector - * @param State - Switch to this state - * @return state of the projector before the change - */ -ProjectorDisplayType FNDisplayLaunchButtonModule::SwitchProjectorToState(FString Address, int Port, ProjectorDisplayType State) -{ - ProjectorDisplayType ModeBefore = ProjectorDisplayType::DisplayType_Error; - FSocket* Socket = USocketHelper::OpenSocket(Address, Port, "ProjectorSocket"); - if (!Socket) return ModeBefore; - - //Get mode from before - if (USocketHelper::SendSocket(Socket, ":TDSM ?\r") <= 0) return ModeBefore; - FString Response = USocketHelper::ReceiveSocket<100>(Socket); //Response looks like: %001 TDSM 000000 - - if (!Response.IsEmpty()) - { - int32 Position = 0; - Response.FindLastChar(' ', Position); - ModeBefore = static_cast<ProjectorDisplayType>(FCString::Atoi(*Response.RightChop(Position))); - } - - if (Response.IsEmpty() || USocketHelper::SendSocket(Socket, ":TDSM " + FString::FromInt(State) + "\r") <= 0) - { - Socket->Shutdown(ESocketShutdownMode::ReadWrite); - Socket->Close(); - return ModeBefore; - } - Response = USocketHelper::ReceiveSocket<100>(Socket); - if (Response.EndsWith(")")) - { - //Errors are marked like this - UE_LOG(LogTemp, Error, TEXT("Projector Type Change Failed. Response: '%s'"), *Response); - } - Socket->Shutdown(ESocketShutdownMode::ReadWrite); - Socket->Close(); - return ModeBefore; -} - -IMPLEMENT_MODULE(FNDisplayLaunchButtonModule, NDisplayLaunchButton) +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +#include "NDisplayLaunchButton.h" + +#include "NDisplayLaunchButtonStyle.h" +#include "NDisplayLaunchButtonSettings.h" +#include "NDisplayLaunchButtonCommands.h" +#include "Misc/MessageDialog.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Interfaces/IPluginManager.h" +#include "Misc/EngineVersion.h" +#include "Networking.h" +#include "SocketHelper.h" +#include "LevelEditor.h" +#include "FileHelpers.h" +#include "GameProjectGeneration/Public/GameProjectGenerationModule.h" +#include "Interfaces/IProjectManager.h" + +DEFINE_LOG_CATEGORY(LogNDisplayLaunchButton); + +void FNDisplayLaunchButtonModule::StartupModule() +{ + const UNDisplayLaunchButtonSettings* Settings = GetDefault<UNDisplayLaunchButtonSettings>(); + FNDisplayLaunchButtonStyle::Initialize(); + FNDisplayLaunchButtonStyle::ReloadTextures(); + FNDisplayLaunchButtonCommands::Register(); + + TSharedPtr<class FUICommandList> PluginCommands = MakeShareable(new FUICommandList); + PluginCommands->MapAction( + FNDisplayLaunchButtonCommands::Get().PluginAction, + FExecuteAction::CreateRaw(this, &FNDisplayLaunchButtonModule::PluginButtonClicked), + FCanExecuteAction()); + + FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor"); + TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender); + ToolbarExtender->AddToolBarExtension("Game", EExtensionHook::First, PluginCommands, FToolBarExtensionDelegate::CreateLambda([](FToolBarBuilder& Builder) + { + Builder.AddToolBarButton(FNDisplayLaunchButtonCommands::Get().PluginAction); + })); + LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); +} + +void FNDisplayLaunchButtonModule::ShutdownModule() +{ + FNDisplayLaunchButtonStyle::Shutdown(); + FNDisplayLaunchButtonCommands::Unregister(); +} + +/** + * Since some plugins interfere with nDisplay execution, this function can disable them + * @param PluginName - Name of the plugin to enable/disable + * @param NewState - The state to switch the plugin to + * @param OldState - Here the old state is stored AFTER the change + * @return true on Success + */ +bool FNDisplayLaunchButtonModule::ChangePluginStateAndStoreConfig(const FString PluginName, const bool NewState, bool& OldState) const +{ + const bool PreviousState = IPluginManager::Get().FindPlugin(PluginName)->IsEnabled(); + + FText FailMessage; + FGameProjectGenerationModule::Get().TryMakeProjectFileWriteable(FPaths::GetProjectFilePath()); + if (!IProjectManager::Get().SetPluginEnabled(PluginName, NewState, FailMessage)) + { + FMessageDialog::Open(EAppMsgType::Ok, FailMessage); + return false; + }else + { + if (!IProjectManager::Get().SaveCurrentProjectToDisk(FailMessage)) + { + FMessageDialog::Open(EAppMsgType::Ok, FailMessage); + return false; + } + } + + OldState = PreviousState; + return true; +} + +/** + * Get filename of the editor executable + */ +FString FNDisplayLaunchButtonModule::GetEditorExecutableName() +{ +#if PLATFORM_WINDOWS + return "UE4Editor.exe"; +#elif PLATFORM_LINUX + return "UE4Editor"; +#endif +} + +/** + * Get filename in the root dir of the project + */ +FString FNDisplayLaunchButtonModule::GetFilePathInProject(FString FileName) +{ + return FPaths::Combine(FPaths::ProjectDir(), FileName); +} + +/** + * Get the path to one of the bundled configs + * @param ConfigName - Name of the config without extension + * @return Path as FString + */ +FString FNDisplayLaunchButtonModule::GetConfigPath(FString ConfigName) +{ + return FPaths::Combine(IPluginManager::Get().FindPlugin("NDisplayLaunchButton")->GetBaseDir(), TEXT("LaunchConfig"), ConfigName + ".cfg"); +} + +/** + * Kill an Array of Process-handles after waiting for them for a few seconds + * @param Processes - Array of Processes to kill + * @param Num_Nodes - Number of Processes in the Array + */ +void FNDisplayLaunchButtonModule::KillProcesses(FProcHandle Processes[], const int Num_Nodes) +{ + float SecondsToWait = 5; + const float CheckInterval = 0.25; + FPlatformProcess::ConditionalSleep([Num_Nodes, Processes, CheckInterval, &SecondsToWait]() + { + SecondsToWait -= CheckInterval; + if(SecondsToWait <= 0) return true; + for (int i = 0; i < Num_Nodes; i++){ + if(FPlatformProcess::IsProcRunning(Processes[i])) return false; + } + return true; + }, CheckInterval); + for (int i = 0; i < Num_Nodes; i++) FPlatformProcess::TerminateProc(Processes[i]); +} + +/** + * Executed on click of the button in the toolbar + */ +void FNDisplayLaunchButtonModule::PluginButtonClicked() +{ + const UNDisplayLaunchButtonSettings* Settings = GetDefault<UNDisplayLaunchButtonSettings>(); + /* Check if we need to do something */ + if (Settings->LaunchType == ButtonLaunchType_NONE) + { + GEngine->AddOnScreenDebugMessage(-1, 3, FColor::White, TEXT("The Button is set to do nothing.")); + return; + } + + FString InsightParameters = ""; + if(Settings->bEnableInsights) + { + if (Settings->bStatNamedEvents) InsightParameters.Append("-statnamedevents"); + + TArray<FString> TraceChannels; + if (Settings->bLog) TraceChannels.Add("log"); + if (Settings->bBookmark) TraceChannels.Add("bookmark"); + if (Settings->bFrame) TraceChannels.Add("frame"); + if (Settings->bCPU) TraceChannels.Add("cpu"); + if (Settings->bGPU) TraceChannels.Add("gpu"); + if (Settings->bLoadTime) TraceChannels.Add("loadtime"); + if (Settings->bFile) TraceChannels.Add("file"); + if (Settings->bNet) TraceChannels.Add("net"); + if (TraceChannels.Num() > 0) InsightParameters.Append(FString::Printf(TEXT(" -trace=%s"), *FString::Join(TraceChannels, TEXT(",")))); + + if (Settings->TracehostIP.Len() > 0) InsightParameters.Append(FString::Printf(TEXT(" -tracehost=%s"), *Settings->TracehostIP)); + } + + /* Enable/Disable Plugins temporarily */ + if(!ChangePluginStateAndStoreConfig("SteamVR", false, SteamVRState)) return; + if(!ChangePluginStateAndStoreConfig("OculusVR", false, OculusVRState)) return; + + /* Trigger Editor save. Needed, else old version will be launched every time */ + if (!UEditorLoadingAndSavingUtils::SaveDirtyPackagesWithDialog(true, true)) return; + + /* minimize the root window to provide max performance for the previews. */ + TSharedPtr<SWindow> RootWindow = FGlobalTabmanager::Get()->GetRootWindow(); + if (RootWindow.IsValid()) RootWindow->Minimize(); + + /* Different Launch Types */ + if (Settings->LaunchType == ButtonLaunchType_ROLV) + { + FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *Settings->RolvConfig.FilePath, *((Settings->ROLVLogToProjectDir) ? ("-log ABSLOG=" + GetFilePathInProject("ROLV_Launch.log")) : "")); + + FProcHandle VRPN; + ProjectorDisplayType ModeFromBefore = DisplayType_Error; + if (Settings->StartVRPN) VRPN = FPlatformProcess::CreateProc(*Settings->VRPNPath.FilePath, *FString::Printf(TEXT("-f \"%s\" -millisleep 0"), *Settings->VRPNConfigPath.FilePath), false, false, false, nullptr, 0, nullptr, nullptr); + if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking start\0"); + if (Settings->SwitchProjector) ModeFromBefore = SwitchProjectorToState(Settings->ProjectorIP, Settings->ProjectorPort, Settings->ProjectorType); + + FProcHandle Instance = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Settings->ROLVLaunchParameters), true, false, false, nullptr, 0, nullptr, nullptr); + FPlatformProcess::WaitForProc(Instance); + + if (Settings->StartVRPN) FPlatformProcess::TerminateProc(VRPN); + FPlatformProcess::CloseProc(VRPN); + if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking stop\0"); + if (Settings->SwitchProjector && ModeFromBefore != DisplayType_Error) SwitchProjectorToState(Settings->ProjectorIP, Settings->ProjectorPort, ModeFromBefore); + } + else if (Settings->LaunchType == ButtonLaunchType_MiniCAVE) + { + const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("minicave"), *Settings->MiniCAVELaunchParameters); + + const int Num_Nodes = 5; + FString Windows_Node_Specific_Commands[Num_Nodes] = { + "dc_node=node_floor WinX=720 WinY=300 ResX=480 ResY=480" + FString((Settings->MiniCAVELogMasterWindow) ? " -log" : "") + ((Settings->MiniCAVELogToProjectDirFloor) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Floor_Master.log")) : "") + " " + Settings->MiniCAVEAdditionalLaunchParametersMaster, + "dc_node=node_front WinX=720 WinY=0 ResX=480 ResY=300" + ((Settings->MiniCAVELogToProjectDirFront) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Front.log")) : ""), + "dc_node=node_left WinX=420 WinY=300 ResX=300 ResY=480" + ((Settings->MiniCAVELogToProjectDirLeft) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Left.log" )) : ""), + "dc_node=node_right WinX=1200 WinY=300 ResX=300 ResY=480" + ((Settings->MiniCAVELogToProjectDirRight) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Right.log")) : ""), + "dc_node=node_back WinX=720 WinY=780 ResX=480 ResY=300" + ((Settings->MiniCAVELogToProjectDirBack) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Back.log" )) : "") + }; + + FProcHandle Processes[Num_Nodes]; + for (int i = 0; i < Num_Nodes; i++) + { + Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); + } + FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */ + + KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */ + } + else if (Settings->LaunchType == ButtonLaunchType_TWO_SCREEN) + { + const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("twoscreen"), *Settings->TwoScreenLaunchParameters); + + const int Num_Nodes = 2; + FString Windows_Node_Specific_Commands[Num_Nodes] = { + "dc_node=node_left WinX=200 WinY=200 ResX=480 ResY=480" + FString((Settings->TwoScreenLogMasterWindow) ? " -log" : "") + ((Settings->TwoScreenLogToProjectDirLeft) ? (" ABSLOG=" + GetFilePathInProject("TwoScreen_Left_Master.log")) : "") + " " + Settings->TwoScreenAdditionalLaunchParametersMaster, + "dc_node=node_right WinX=682 WinY=200 ResX=480 ResY=480" + ((Settings->TwoScreenLogToProjectDirRight) ? (" ABSLOG=" + GetFilePathInProject("TwoScreen_Right.log")) : "") + }; + + FProcHandle Processes[Num_Nodes]; + for (int i = 0; i < Num_Nodes; i++) + { + Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); + } + FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */ + + KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */ + } + else if (Settings->LaunchType == ButtonLaunchType_TDW) + { + const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("tileddisplaywall") ,*Settings->TiledDisplayWallLaunchParameters); + + const int Num_Nodes = 6; + FString Windows_Node_Specific_Commands[Num_Nodes] = { + "dc_node=node_tl WinX=200 WinY=200 ResX=480 ResY=272" + FString((Settings->TiledDisplayWallLogMasterWindow) ? " -log" : "") + ((Settings->TiledDisplayWallLogToProjectDirTL) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_TL_Master.log")) : "") + " " + Settings->TiledDisplayWallAdditionalLaunchParametersMaster, + "dc_node=node_tm WinX=693 WinY=200 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirTM) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_TM.log")) : ""), + "dc_node=node_tr WinX=1186 WinY=200 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirTR) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_TR.log")) : ""), + "dc_node=node_bl WinX=200 WinY=483 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBL) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_BL.log")) : ""), + "dc_node=node_bm WinX=693 WinY=483 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBM) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_BM.log")) : ""), + "dc_node=node_br WinX=1186 WinY=483 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBR) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_BR.log")) : "") + }; + + FProcHandle Processes[Num_Nodes]; + for (int i = 0; i < Num_Nodes; i++) + { + Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); + } + FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */ + + KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */ + } + else if (Settings->LaunchType == ButtonLaunchType_CAVE) + { + FProcHandle Instance = FPlatformProcess::CreateProc( + *Settings->CAVELaunchScriptPath.FilePath, + *("\"" + (FPaths::ConvertRelativePathToFull(".") + "/UE4Editor\"") + + " " + FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath()) + + " " + FString::FromInt(FEngineVersion::Current().GetMajor()) + + FString::FromInt(FEngineVersion::Current().GetMinor()) + + " " + InsightParameters), // Test this + true, false, false, nullptr, 0, nullptr, nullptr); + FPlatformProcess::WaitForProc(Instance); + } + + /* Re-maximize Editor Window */ + if (RootWindow.IsValid()) RootWindow->Maximize(); + + /* Restore Plugin States */ + ChangePluginStateAndStoreConfig("SteamVR", SteamVRState, SteamVRState); + ChangePluginStateAndStoreConfig("OculusVR", OculusVRState, OculusVRState); +} + +/** + * Send a command to the DTrack controller + * @param Address - IP address of DTrack controller + * @param Port - Port to contact DTrack controller + * @param Message - Message to send + */ +void FNDisplayLaunchButtonModule::SendToDTrack(FString Address, int Port, FString Message) +{ + FSocket* Socket = USocketHelper::OpenSocket(Address, Port, "DTrackSocket"); + if (!Socket) return; + if (USocketHelper::SendSocket(Socket, Message) <= 0) return; + FString Response = USocketHelper::ReceiveSocket<100>(Socket); + if (Response.Compare("dtrack2 ok") != 0) + { + UE_LOG(LogTemp, Error, TEXT("DTrack Command Failed. Response: '%s'"), *Response); + } + Socket->Shutdown(ESocketShutdownMode::ReadWrite); + Socket->Close(); +} + +/** + * Switch the display mode of the projector a 3D display mode or back + * @param Address - IP address of projector + * @param Port - Port to contact projector + * @param State - Switch to this state + * @return state of the projector before the change + */ +ProjectorDisplayType FNDisplayLaunchButtonModule::SwitchProjectorToState(FString Address, int Port, ProjectorDisplayType State) +{ + ProjectorDisplayType ModeBefore = ProjectorDisplayType::DisplayType_Error; + FSocket* Socket = USocketHelper::OpenSocket(Address, Port, "ProjectorSocket"); + if (!Socket) return ModeBefore; + + //Get mode from before + if (USocketHelper::SendSocket(Socket, ":TDSM ?\r") <= 0) return ModeBefore; + FString Response = USocketHelper::ReceiveSocket<100>(Socket); //Response looks like: %001 TDSM 000000 + + if (!Response.IsEmpty()) + { + int32 Position = 0; + Response.FindLastChar(' ', Position); + ModeBefore = static_cast<ProjectorDisplayType>(FCString::Atoi(*Response.RightChop(Position))); + } + + if (Response.IsEmpty() || USocketHelper::SendSocket(Socket, ":TDSM " + FString::FromInt(State) + "\r") <= 0) + { + Socket->Shutdown(ESocketShutdownMode::ReadWrite); + Socket->Close(); + return ModeBefore; + } + Response = USocketHelper::ReceiveSocket<100>(Socket); + if (Response.EndsWith(")")) + { + //Errors are marked like this + UE_LOG(LogTemp, Error, TEXT("Projector Type Change Failed. Response: '%s'"), *Response); + } + Socket->Shutdown(ESocketShutdownMode::ReadWrite); + Socket->Close(); + return ModeBefore; +} + +IMPLEMENT_MODULE(FNDisplayLaunchButtonModule, NDisplayLaunchButton)