diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_NetImGui.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_NetImGui.cpp new file mode 100644 index 0000000..cc0a189 --- /dev/null +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_NetImGui.cpp @@ -0,0 +1,126 @@ +#include "CogEngineWindow_NetImGui.h" + +#include "CogWindowWidgets.h" +#include "imgui.h" +#include "imgui_internal.h" +#include "NetImgui_Api.h" + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_NetImGui::Initialize() +{ + Super::Initialize(); + bHasMenu = true; + Config = GetConfig(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_NetImGui::Shutdown() +{ +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_NetImGui::ResetConfig() +{ + Super::ResetConfig(); + Config->Reset(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_NetImGui::RenderHelp() +{ + ImGui::Text(""); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_NetImGui::RenderContent() +{ + Super::RenderContent(); + +#if NETIMGUI_ENABLED + + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Settings")) + { + { + static char Buffer[256] = ""; + ImStrncpy(Buffer, TCHAR_TO_ANSI(*Config->ServerName), IM_ARRAYSIZE(Buffer)); + if (ImGui::InputText("Server Name", Buffer, IM_ARRAYSIZE(Buffer))) + { + Config->ServerName = FString(Buffer); + } + } + ImGui::InputInt("Server Port", &Config->ServerPort); + + { + static char Buffer[256] = ""; + ImStrncpy(Buffer, TCHAR_TO_ANSI(*Config->ClientName), IM_ARRAYSIZE(Buffer)); + if (ImGui::InputText("Client Name", Buffer, IM_ARRAYSIZE(Buffer))) + { + Config->ClientName = FString(Buffer); + } + } + ImGui::InputInt("Client Port", &Config->ClientPort); + + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + + if (NetImgui::IsConnected()) + { + ImGui::TextUnformatted("Status: Connected"); + if (ImGui::Button("Disconnect")) + { + NetImgui::Disconnect(); + } + } + else if (NetImgui::IsConnectionPending()) + { + ImGui::TextUnformatted("Status: Waiting Server"); + if (ImGui::Button("Cancel")) + { + NetImgui::Disconnect(); + } + } + else // No connection + { + ImGui::TextUnformatted("Status: Not Connected"); + + if (ImGui::Button("Connect", ImVec2(ImGui::GetContentRegionAvail().x, 0))) + { + const auto clientName = StringCast(*Config->ClientName); + const auto serverName = StringCast(*Config->ServerName); + NetImgui::ConnectToApp(clientName.Get(), serverName.Get(), Config->ServerPort, nullptr, nullptr); + } + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Attempt a connection to a remote netImgui server at the provided address."); + } + + if (ImGui::Button("Listen", ImVec2(ImGui::GetContentRegionAvail().x, 0))) + { + const auto clientName = StringCast(*Config->ClientName); + NetImgui::ConnectFromApp(clientName.Get(), Config->ClientPort, nullptr, nullptr); + } + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Start listening for a connection request by a remote netImgui server, on the provided Port."); + } + } + +#endif // #if NETIMGUI_ENABLED +} + +void UCogEngineConfig_NetImGui::Reset() +{ + Super::Reset(); + +#if NETIMGUI_ENABLED + ClientName = FString("cog"); + ServerName = FString("localhost"); + ClientPort = NetImgui::kDefaultClientPort; + ServerPort = NetImgui::kDefaultServerPort; +#endif +} diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_NetImGui.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_NetImGui.h new file mode 100644 index 0000000..4b972a9 --- /dev/null +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_NetImGui.h @@ -0,0 +1,54 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogCommonConfig.h" +#include "CogWindow.h" +#include "CogEngineWindow_NetImGui.generated.h" + +class UCogEngineConfig_NetImGui; + +class COGENGINE_API FCogEngineWindow_NetImGui : public FCogWindow +{ + typedef FCogWindow Super; + +public: + + virtual void Initialize() override; + + virtual void Shutdown() override; + +protected: + + virtual void ResetConfig() override; + + virtual void RenderHelp() override; + + virtual void RenderContent() override; + +private: + + TWeakObjectPtr Config = nullptr; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UCLASS(Config = Cog) +class UCogEngineConfig_NetImGui : public UCogCommonConfig +{ + GENERATED_BODY() + +public: + + virtual void Reset() override; + + UPROPERTY(Config) + FString ClientName = FString("cog"); + + UPROPERTY(Config) + FString ServerName = FString("localhost"); + + UPROPERTY(Config) + int32 ServerPort = 8888; + + UPROPERTY(Config) + int32 ClientPort = 8889; +}; \ No newline at end of file diff --git a/Plugins/Cog/Source/CogImgui/CogImgui.Build.cs b/Plugins/Cog/Source/CogImgui/CogImgui.Build.cs index e1d0f31..4638f22 100644 --- a/Plugins/Cog/Source/CogImgui/CogImgui.Build.cs +++ b/Plugins/Cog/Source/CogImgui/CogImgui.Build.cs @@ -12,6 +12,7 @@ public class CogImgui : ModuleRules "Core", "ImGui", "ImPlot", + "NetImgui", }); PrivateDependencyModuleNames.AddRange(new[] @@ -21,7 +22,8 @@ public class CogImgui : ModuleRules "Engine", "InputCore", "Slate", - "SlateCore" + "SlateCore", + "Sockets" }); if (Target.bBuildEditor) diff --git a/Plugins/Cog/Source/CogImgui/Private/CogImguiConfig.cpp b/Plugins/Cog/Source/CogImgui/Private/CogImguiConfig.cpp index 50167d8..2dd03d5 100644 --- a/Plugins/Cog/Source/CogImgui/Private/CogImguiConfig.cpp +++ b/Plugins/Cog/Source/CogImgui/Private/CogImguiConfig.cpp @@ -1,12 +1,22 @@ #include "CogImguiConfig.h" THIRD_PARTY_INCLUDES_START + #include #include #include #include #include + #include #include #include + +#include "Private/NetImgui_Api.cpp" +#include "Private/NetImgui_Client.cpp" +#include "Private/NetImgui_CmdPackets_DrawFrame.cpp" +#include "Private/NetImgui_NetworkPosix.cpp" +#include "Private/NetImgui_NetworkUE4.cpp" +#include "Private/NetImgui_NetworkWin32.cpp" + THIRD_PARTY_INCLUDES_END \ No newline at end of file diff --git a/Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp b/Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp index 237a706..92bd73f 100644 --- a/Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp +++ b/Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp @@ -3,10 +3,10 @@ #include "CogDebugDrawImGui.h" #include "CogImguiHelper.h" #include "CogImguiInputHelper.h" -#include "CogWindowConsoleCommandManager.h" #include "CogWindow_Layouts.h" #include "CogWindow_Settings.h" #include "CogWindow_Spacing.h" +#include "CogWindowConsoleCommandManager.h" #include "CogWindowHelper.h" #include "CogWindowWidgets.h" #include "Engine/Engine.h" @@ -14,6 +14,7 @@ #include "HAL/IConsoleManager.h" #include "imgui_internal.h" #include "Misc/EngineVersionComparison.h" +#include "NetImgui_Api.h" FString UCogWindowManager::ToggleInputCommand = TEXT("Cog.ToggleInput"); FString UCogWindowManager::DisableInputCommand = TEXT("Cog.DisableInput"); @@ -21,6 +22,8 @@ FString UCogWindowManager::LoadLayoutCommand = TEXT("Cog.LoadLayout"); FString UCogWindowManager::SaveLayoutCommand = TEXT("Cog.SaveLayout"); FString UCogWindowManager::ResetLayoutCommand = TEXT("Cog.ResetLayout"); +bool UCogWindowManager::IsNetImguiInitialized = false; + //-------------------------------------------------------------------------------------------------------------------------- UCogWindowManager::UCogWindowManager() { @@ -128,7 +131,17 @@ void UCogWindowManager::InitializeInternal() } })); + +#if NETIMGUI_ENABLED + if (IsNetImguiInitialized == false) + { + NetImgui::Startup(); + IsNetImguiInitialized = true; + } +#endif + IsInitialized = true; + } //-------------------------------------------------------------------------------------------------------------------------- @@ -136,19 +149,30 @@ void UCogWindowManager::Shutdown() { FCogImGuiContextScope ImGuiContextScope(Context); - //------------------------------------------------------------ + //------------------------------------------------------------------ // Call PreSaveConfig before destroying imgui context // if PreSaveConfig needs to read ImGui IO for example - //------------------------------------------------------------ + //------------------------------------------------------------------ for (FCogWindow* Window : Windows) { Window->PreSaveConfig(); } - //------------------------------------------------------------ + //------------------------------------------------------------------ + // NetImgui must be shutdown before imgui as it uses context hooks + //------------------------------------------------------------------ +#if NETIMGUI_ENABLED + if (IsNetImguiInitialized) + { + NetImgui::Shutdown(); + IsNetImguiInitialized = false; + } +#endif + + //------------------------------------------------------------------ // Destroy ImGui before destroying the windows to make sure // imgui serialize their visibility state in imgui.ini - //------------------------------------------------------------ + //------------------------------------------------------------------ if (IsInitialized == true) { Context.Shutdown(); diff --git a/Plugins/Cog/Source/CogWindow/Public/CogWindowManager.h b/Plugins/Cog/Source/CogWindow/Public/CogWindowManager.h index ae12a92..2b8064c 100644 --- a/Plugins/Cog/Source/CogWindow/Public/CogWindowManager.h +++ b/Plugins/Cog/Source/CogWindow/Public/CogWindowManager.h @@ -159,6 +159,8 @@ protected: bool bHideAllWindows = false; bool IsInitialized = false; + + static bool IsNetImguiInitialized; }; //-------------------------------------------------------------------------------------------------------------------------- diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui.Build.cs b/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui.Build.cs new file mode 100644 index 0000000..ed3997e --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui.Build.cs @@ -0,0 +1,11 @@ +using UnrealBuildTool; +using System.IO; + +public class NetImgui : ModuleRules +{ + public NetImgui(ReadOnlyTargetRules Target) : base(Target) + { + Type = ModuleType.External; + PublicSystemIncludePaths.Add(ModuleDirectory); + } +} diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui_Api.h b/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui_Api.h new file mode 100644 index 0000000..6815ab5 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui_Api.h @@ -0,0 +1,285 @@ +#pragma once + +//================================================================================================= +//! @Name : NetImgui +//================================================================================================= +//! @author : Sammy Fatnassi +//! @date : 2024/12/10 +//! @version : v1.12.1 +//! @Details : For integration info : https://github.com/sammyfreg/netImgui/wiki +//================================================================================================= +#define NETIMGUI_VERSION "1.12.1" // Fixed disconnect thread contention and clipboard command +#define NETIMGUI_VERSION_NUM 11201 + + + + +//------------------------------------------------------------------------------------------------- +// Deactivate a few warnings to allow Imgui header include +// without generating warnings in maximum level '-Wall' +//------------------------------------------------------------------------------------------------- +#if defined (__clang__) + #pragma clang diagnostic push + // ImGui.h warnings(s) + #pragma clang diagnostic ignored "-Wunknown-warning-option" + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" + #pragma clang diagnostic ignored "-Wreserved-identifier" // Enum values using '__' or member starting with '_' in imgui.h + // NetImgui_Api.h Warning(s) + #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // Not using nullptr in case this file is used in pre C++11 +#elif defined(_MSC_VER) + #pragma warning (push) + // ImGui.h warnings(s) + #pragma warning (disable: 4514) // 'xxx': unreferenced inline function has been removed + #pragma warning (disable: 4710) // 'xxx': function not inlined + #pragma warning (disable: 4820) // 'xxx': 'yyy' bytes padding added after data member 'zzz' + #pragma warning (disable: 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified +#endif + +//================================================================================================= +// Include the user config file. It should contain the include for : +// 'imgui.h' : always +// 'imgui_internal.h' when 'NETIMGUI_INTERNAL_INCLUDE' is defined +//================================================================================================= +#ifdef NETIMGUI_IMPLEMENTATION + #define NETIMGUI_INTERNAL_INCLUDE + #include "NetImgui_Config.h" + #undef NETIMGUI_INTERNAL_INCLUDE +#else + #include "NetImgui_Config.h" +#endif + +//------------------------------------------------------------------------------------------------- +// If 'NETIMGUI_ENABLED' hasn't been defined yet (in project settings or NetImgui_Config.h') +// we define this library as 'Disabled' +//------------------------------------------------------------------------------------------------- +#ifndef NETIMGUI_ENABLED + #define NETIMGUI_ENABLED 0 +#endif + +//------------------------------------------------------------------------------------------------- +// NetImgui needs to detect Dear ImGui to be active, otherwise we disable it +// When including this header, make sure imgui.h is included first +// (either always included in NetImgui_config.h or have it included after Imgui.h in your cpp) +//------------------------------------------------------------------------------------------------- +#if !defined(IMGUI_VERSION) + #undef NETIMGUI_ENABLED + #define NETIMGUI_ENABLED 0 +#endif + +#if NETIMGUI_ENABLED + +#include + +//================================================================================================= +// Default Build settings defines values +// Assign default values when not set in user NetImgui_Config.h +//================================================================================================= + +//------------------------------------------------------------------------------------------------- +// Prepended to functions signature, for dll export/import +//------------------------------------------------------------------------------------------------- +#ifndef NETIMGUI_API + #define NETIMGUI_API IMGUI_API // Use same value as defined by Dear ImGui by default +#endif + +//------------------------------------------------------------------------------------------------- +// Enable TCP socket 'reuse port' option when opening it as a 'listener'. +// Note: Can help when unable to open a socket because it wasn't properly released after a crash. +//------------------------------------------------------------------------------------------------- +#ifndef NETIMGUI_FORCE_TCP_LISTEN_BINDING + #define NETIMGUI_FORCE_TCP_LISTEN_BINDING 0 // Doesn't seem to be needed on Window/Linux +#endif + +//------------------------------------------------------------------------------------------------- +// Enable Dear ImGui Callbacks support for BeginFrame/Render automatic interception. +// Note: Avoid having to replace ImGui::BeginFrame/ImGui::Render with in library user code, by +// 'NetImgui::NewFrame/NetImgui::EndFrame'. But prevent benefit of skipping frame draw +// when unneeded, that 'NetImgui::NewFrame' can provide. +// For more info, consult 'SampleNewFrame.cpp'. +// Needs Dear ImGui 1.81+ +//------------------------------------------------------------------------------------------------- +#ifndef NETIMGUI_IMGUI_CALLBACK_ENABLED + #define NETIMGUI_IMGUI_CALLBACK_ENABLED (IMGUI_VERSION_NUM >= 18100) // Not supported pre Dear ImGui 1.81 +#endif + + +namespace NetImgui +{ + +//================================================================================================= +// List of texture format supported +//================================================================================================= +enum eTexFormat { + kTexFmtA8, + kTexFmtRGBA8, + + // Support of 'user defined' texture format. + // Implementation must be added on both client and Server code. + // Search for TEXTURE_CUSTOM_SAMPLE for example implementation. + kTexFmtCustom, + + // + kTexFmt_Count, + kTexFmt_Invalid=kTexFmt_Count +}; + +//================================================================================================= +// Data Compression wanted status +//================================================================================================= +enum eCompressionMode { + kForceDisable, // Disable data compression for communications + kForceEnable, // Enable data compression for communications + kUseServerSetting // Use Server setting for compression (default) +}; + +//------------------------------------------------------------------------------------------------- +// Function typedefs +//------------------------------------------------------------------------------------------------- +typedef void (*ThreadFunctPtr)(void threadedFunction(void* pClientInfo), void* pClientInfo); +typedef void (*FontCreateFuncPtr)(float PreviousDPIScale, float NewDPIScale); + +//================================================================================================= +// Initialize the Network Library +//================================================================================================= +NETIMGUI_API bool Startup(void); + +//================================================================================================= +// Free Resources +// Wait until all communication threads have terminated before returning +//================================================================================================= +NETIMGUI_API void Shutdown(); + +//================================================================================================= +// Establish a connection between the NetImgui server application and this client. +// +// Can connect with NetImgui Server application by either reaching it directly +// using 'ConnectToApp' or waiting for Server to reach us after Client called 'ConnectFromApp'. +// +// Note: Start a new communication thread using std::Thread by default, but can receive custom +// thread start function instead (Look at ClientExample 'CustomCommunicationThread'). +//------------------------------------------------------------------------------------------------- +// clientName : Client name displayed in the Server's clients list +// serverHost : NetImgui Server Application address (Ex1: 127.0.0.2, Ex2: localhost) +// serverPort : PortID of the NetImgui Server application to connect to +// clientPort : PortID this Client should wait for connection from Server application +// threadFunction : User provided function to launch new networking thread. +// Use 'DefaultStartCommunicationThread' by default (uses 'std::thread'). +// fontCreateFunction : User provided function to call when the Server expect an update of +// the font atlas, because of a monitor DPI change. When left to nullptr, +// uses 'ImGuiIO.FontGlobalScale' instead to increase text size, +// with blurier results. +//================================================================================================= +NETIMGUI_API bool ConnectToApp(const char* clientName, const char* serverHost, uint32_t serverPort=kDefaultServerPort, ThreadFunctPtr threadFunction=0, FontCreateFuncPtr FontCreateFunction=0); +NETIMGUI_API bool ConnectFromApp(const char* clientName, uint32_t clientPort=kDefaultClientPort, ThreadFunctPtr threadFunction=0, FontCreateFuncPtr fontCreateFunction=0); + +//================================================================================================= +// Request a disconnect from the NetImguiServer application +//================================================================================================= +NETIMGUI_API void Disconnect(void); + +//================================================================================================= +// True if connected to the NetImguiServer application +//================================================================================================= +NETIMGUI_API bool IsConnected(void); + +//================================================================================================= +// True if connection request is waiting to be completed. For example, while waiting for +// Server to reach ud after having called 'ConnectFromApp()' +//================================================================================================= +NETIMGUI_API bool IsConnectionPending(void); + +//================================================================================================= +// True when Dear ImGui is currently expecting draw commands +// This means that we are between NewFrame() and EndFrame() +//================================================================================================= +NETIMGUI_API bool IsDrawing(void); + +//================================================================================================= +// True when we are currently drawing on the NetImguiServer application +// Means that we are between NewFrame() and EndFrame() of drawing for remote application +//================================================================================================= +NETIMGUI_API bool IsDrawingRemote(void); + +//================================================================================================= +// Send an updated texture used by imgui, to the NetImguiServer application +// Note: To remove a texture, set pData to nullptr +// Note: User needs to provide a valid 'dataSize' when using format 'kTexFmtCustom', +// can be ignored otherwise +//================================================================================================= +NETIMGUI_API void SendDataTexture(ImTextureID textureId, void* pData, uint16_t width, uint16_t height, eTexFormat format, uint32_t dataSize=0); + +//================================================================================================= +// Start a new Imgui Frame and wait for Draws commands, using ImContext that was active on connect. +// Returns true if we are awaiting a new ImGui frame. +// +// All ImGui drawing should be skipped when return is false. +// +// Note: This code can be used instead, to know if you should be drawing or not : +// 'if( !NetImgui::IsDrawing() )' +// +// Note: If your code cannot handle skipping a ImGui frame, leave 'bSupportFrameSkip=false', +// and this function will always call 'ImGui::NewFrame()' internally and return true +// +// Note: With Dear ImGui 1.81+, you can keep using the ImGui::BeginFrame()/Imgui::Render() +// without having to use NetImgui::NewFrame()/NetImgui::EndFrame() +// (unless wanting to support frame skip) +//================================================================================================= +NETIMGUI_API bool NewFrame(bool bSupportFrameSkip=false); + +//================================================================================================= +// Process all receives draws, send them to remote connection and restore the ImGui Context +//================================================================================================= +NETIMGUI_API void EndFrame(void); + +//================================================================================================= +// Return the context associated to this remote connection. Null when not connected. +//================================================================================================= +NETIMGUI_API ImGuiContext* GetContext(); + +//================================================================================================= +// Set the remote client background color and texture +// Note: If no TextureID is specified, will use the default server texture +//================================================================================================= +NETIMGUI_API void SetBackground(const ImVec4& bgColor); +NETIMGUI_API void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint ); +NETIMGUI_API void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint, ImTextureID bgTextureID); + +//================================================================================================= +// Control the data compression for communications between Client/Server +//================================================================================================= +NETIMGUI_API void SetCompressionMode(eCompressionMode eMode); +NETIMGUI_API eCompressionMode GetCompressionMode(); + +//================================================================================================= +// Helper functions +//================================================================================================= +NETIMGUI_API uint8_t GetTexture_BitsPerPixel (eTexFormat eFormat); +NETIMGUI_API uint32_t GetTexture_BytePerLine (eTexFormat eFormat, uint32_t pixelWidth); +NETIMGUI_API uint32_t GetTexture_BytePerImage (eTexFormat eFormat, uint32_t pixelWidth, uint32_t pixelHeight); +} + +//================================================================================================= +// Optional single include compiling option +// Note: User wanting to avoid adding the few NetImgui sources files to their project, +// can instead define 'NETIMGUI_IMPLEMENTATION' *once* before including 'NetImgui_Api.h' +// to pull all the needed cpp files alongside for compilation +//================================================================================================= +#if defined(NETIMGUI_IMPLEMENTATION) + #include "Private/NetImgui_Api.cpp" + #include "Private/NetImgui_Client.cpp" + #include "Private/NetImgui_CmdPackets_DrawFrame.cpp" + #include "Private/NetImgui_NetworkPosix.cpp" + #include "Private/NetImgui_NetworkUE4.cpp" + #include "Private/NetImgui_NetworkWin32.cpp" +#endif + +#endif // NETIMGUI_ENABLED + +//------------------------------------------------------------------------------------------------- +// Re-Enable the Deactivated warnings +//------------------------------------------------------------------------------------------------- +#if defined (__clang__) + #pragma clang diagnostic pop +#elif defined(_MSC_VER) + #pragma warning (pop) +#endif diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui_Config.h b/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui_Config.h new file mode 100644 index 0000000..25ade93 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/NetImgui_Config.h @@ -0,0 +1,62 @@ +#pragma once + +//================================================================================================= +// Enable code compilation for this library +// Note: Useful to disable 'netImgui' on unsupported builds while keeping functions declared +//================================================================================================= +#ifndef NETIMGUI_ENABLED + #define NETIMGUI_ENABLED 1 +#endif + +#if NETIMGUI_ENABLED + +#include + +#ifdef NETIMGUI_INTERNAL_INCLUDE +#include "Private/NetImgui_WarningDisableImgui.h" // Disable some extra warning generated by imgui_internal in '-Wall' +#include // Only needed when compiling NetImgui, not when using the NetImgui Api +#include "Private/NetImgui_WarningReenable.h" +#endif + +#endif // NETIMGUI_ENABLED + +//================================================================================================= +// Default Ports used to reach the Server or the Client (listen port for incoming connection) +//================================================================================================= +namespace NetImgui +{ + enum Constants{ + kDefaultServerPort = 8888, //!< Default port Server waits for a connection + kDefaultClientPort = 8889 //!< Default port Client waits for a connection + }; +} + +//================================================================================================= +// Enable default Win32/Posix networking code +// Note: By default, netImgui uses Winsock on Windows and Posix sockets on other platforms +// +// The use your own code, turn off both NETIMGUI_WINSOCKET_ENABLED, +// NETIMGUI_POSIX_SOCKETS_ENABLED and provide your own implementation of the functions +// declared in 'NetImgui_Network.h'. +// +// As an example, 'SampleCompression' disable default com implementation and use its own +//================================================================================================= +#if !defined(NETIMGUI_WINSOCKET_ENABLED) && !defined(__UNREAL__) + #ifdef _WIN32 + #define NETIMGUI_WINSOCKET_ENABLED 1 // Project needs 'ws2_32.lib' added to input libraries + #else + #define NETIMGUI_WINSOCKET_ENABLED 0 + #endif +#endif + +#if !defined(NETIMGUI_POSIX_SOCKETS_ENABLED) && !defined(__UNREAL__) + #define NETIMGUI_POSIX_SOCKETS_ENABLED !(NETIMGUI_WINSOCKET_ENABLED) +#endif + +//================================================================================================= +// Various build settings define +// Note: for more information, please look in 'NetImgui_Api.h' for description and default values +//================================================================================================= +//#define NETIMGUI_IMGUI_CALLBACK_ENABLED (IMGUI_VERSION_NUM >= 18100) // Not supported pre Dear ImGui 1.81 +//#define NETIMGUI_FORCE_TCP_LISTEN_BINDING 0 // Doesn't seem to be needed on Window/Linux +//#define NETIMGUI_API IMGUI_API // Use same value as defined by Dear ImGui by default diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Api.cpp b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Api.cpp new file mode 100644 index 0000000..c998218 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Api.cpp @@ -0,0 +1,795 @@ +#include "NetImgui_Shared.h" +#include "NetImgui_WarningDisable.h" + +#if NETIMGUI_ENABLED +#include +#include +#include "NetImgui_Client.h" +#include "NetImgui_Network.h" +#include "NetImgui_CmdPackets.h" + +using namespace NetImgui::Internal; + +namespace NetImgui { + +static Client::ClientInfo* gpClientInfo = nullptr; + +bool ProcessInputData(Client::ClientInfo& client); + +//================================================================================================= +void DefaultStartCommunicationThread(void ComFunctPtr(void*), void* pClient) +//================================================================================================= +{ +// Visual Studio 2017 generate this warning on std::thread, avoid the warning preventing build +#if defined(_MSC_VER) && (_MSC_VER < 1920) + #pragma warning (push) + #pragma warning (disable: 4625) // 'std::_LaunchPad<_Target>' : copy constructor was implicitly defined as deleted + #pragma warning (disable: 4626) // 'std::_LaunchPad<_Target>' : assignment operator was implicitly defined as deleted +#endif + + std::thread(ComFunctPtr, pClient).detach(); + +#if defined(_MSC_VER) && (_MSC_VER < 1920) + #pragma warning (pop) +#endif +} + + +//================================================================================================= +bool ConnectToApp(const char* clientName, const char* ServerHost, uint32_t serverPort, ThreadFunctPtr threadFunction, FontCreateFuncPtr FontCreateFunction) +//================================================================================================= +{ + if (!gpClientInfo) return false; + + Client::ClientInfo& client = *gpClientInfo; + Disconnect(); + + while (client.IsActive()) + std::this_thread::yield(); + + client.ContextRestore(); // Restore context setting override, after a disconnect + client.ContextRemoveHooks(); // Remove hooks callback only when completely disconnected + + StringCopy(client.mName, (clientName == nullptr || clientName[0] == 0 ? "Unnamed" : clientName)); + client.mpSocketPending = Network::Connect(ServerHost, serverPort); + client.mFontCreationFunction = FontCreateFunction; + if (client.mpSocketPending.load() != nullptr) + { + client.ContextInitialize(); + threadFunction = threadFunction == nullptr ? DefaultStartCommunicationThread : threadFunction; + threadFunction(Client::CommunicationsConnect, &client); + } + + return client.IsActive(); +} + +//================================================================================================= +bool ConnectFromApp(const char* clientName, uint32_t serverPort, ThreadFunctPtr threadFunction, FontCreateFuncPtr FontCreateFunction) +//================================================================================================= +{ + if (!gpClientInfo) return false; + + Client::ClientInfo& client = *gpClientInfo; + Disconnect(); + + while (client.IsActive()) + std::this_thread::yield(); + + client.ContextRestore(); // Restore context setting override, after a disconnect + client.ContextRemoveHooks(); // Remove hooks callback only when completly disconnected + + StringCopy(client.mName, (clientName == nullptr || clientName[0] == 0 ? "Unnamed" : clientName)); + client.mpSocketPending = Network::ListenStart(serverPort); + client.mFontCreationFunction = FontCreateFunction; + client.mThreadFunction = (threadFunction == nullptr) ? DefaultStartCommunicationThread : threadFunction; + if (client.mpSocketPending.load() != nullptr) + { + client.ContextInitialize(); + client.mSocketListenPort = serverPort; + client.mThreadFunction(Client::CommunicationsHost, &client); + } + + return client.IsActive(); +} + +//================================================================================================= +void Disconnect(void) +//================================================================================================= +{ + if (!gpClientInfo) return; + + // Attempt fake connection on local socket waiting for a Server connection, + // so the blocking operation can terminate and release the communication thread + Client::ClientInfo& client = *gpClientInfo; + client.mbDisconnectPending = true; + client.mbDisconnectListen = true; + if( client.mpSocketListen.load() != nullptr && client.mSocketListenPort != 0 ) + { + Network::SocketInfo* pFakeSocket = Network::Connect("127.0.0.1", client.mSocketListenPort); + client.mSocketListenPort = 0; + client.mbDisconnectPending = true; + + if(pFakeSocket){ + Network::Disconnect(pFakeSocket); + } + } + + // Wait for connection attempt to complete and fail + while( client.mbComInitActive || client.mbClientThreadActive ); + + // If fake connection to exit Listening failed, force disconnect socket directly + // even though it might potentially cause a race condition + Network::SocketInfo* pListenSocket = client.mpSocketListen.exchange(nullptr); + if( pListenSocket ){ + Network::Disconnect(pListenSocket); + } + + Network::SocketInfo* pPendingSocket = client.mpSocketPending.exchange(nullptr); + if( pPendingSocket ){ + Network::Disconnect(pPendingSocket); + } +} + +//================================================================================================= +bool IsConnected(void) +//================================================================================================= +{ + if (!gpClientInfo) return false; + + Client::ClientInfo& client = *gpClientInfo; + + // If disconnected in middle of a remote frame drawing, + // want to behave like it is still connected to finish frame properly + return client.IsConnected() || IsDrawingRemote(); +} + +//================================================================================================= +bool IsConnectionPending(void) +//================================================================================================= +{ + if (!gpClientInfo) return false; + + Client::ClientInfo& client = *gpClientInfo; + return client.IsConnectPending(); +} + +//================================================================================================= +bool IsDrawing(void) +//================================================================================================= +{ + if (!gpClientInfo) return false; + + Client::ClientInfo& client = *gpClientInfo; + return client.mbIsDrawing; +} + +//================================================================================================= +bool IsDrawingRemote(void) +//================================================================================================= +{ + if (!gpClientInfo) return false; + + Client::ClientInfo& client = *gpClientInfo; + return IsDrawing() && client.mbIsRemoteDrawing; +} + +//================================================================================================= +bool NewFrame(bool bSupportFrameSkip) +//================================================================================================= +{ + if (!gpClientInfo || gpClientInfo->mbIsDrawing) return false; + + Client::ClientInfo& client = *gpClientInfo; + ScopedBool scopedInside(client.mbInsideNewEnd, true); + + // ImGui Newframe handled by remote connection settings + if( NetImgui::IsConnected() ) + { + ImGui::SetCurrentContext(client.mpContext); + + // Save current context settings and override settings to fit our netImgui usage + if (!client.IsContextOverriden() ) + { + client.ContextOverride(); + } + + auto elapsedCheck = std::chrono::steady_clock::now() - client.mLastOutgoingDrawCheckTime; + auto elapsedDraw = std::chrono::steady_clock::now() - client.mLastOutgoingDrawTime; + auto elapsedCheckMs = static_cast(std::chrono::duration_cast(elapsedCheck).count()) / 1000.f; + auto elapsedDrawMs = static_cast(std::chrono::duration_cast(elapsedDraw).count()) / 1000.f; + client.mLastOutgoingDrawCheckTime = std::chrono::steady_clock::now(); + + // Update input and see if remote netImgui expect a new frame + client.mSavedDisplaySize = ImGui::GetIO().DisplaySize; + client.mbValidDrawFrame = false; + + // Take into account delay until next method call, for more precise fps + if( client.mDesiredFps > 0.f && (elapsedDrawMs + elapsedCheckMs/2.f) > (1000.f/client.mDesiredFps) ) + { + client.mLastOutgoingDrawTime = std::chrono::steady_clock::now(); + client.mbValidDrawFrame = true; + } + + ProcessInputData(client); + + // We are about to start drawing for remote context, check for font data update + const ImFontAtlas* pFonts = ImGui::GetIO().Fonts; + if( pFonts->TexPixelsAlpha8 && + (pFonts->TexPixelsAlpha8 != client.mpFontTextureData || client.mFontTextureID != pFonts->TexID )) + { + uint8_t* pPixelData(nullptr); int width(0), height(0); + ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pPixelData, &width, &height); + SendDataTexture(pFonts->TexID, pPixelData, static_cast(width), static_cast(height), eTexFormat::kTexFmtA8); + } + + // No font texture has been sent to the netImgui server, you can either + // 1. Leave font data available in ImGui (not call ImGui::ClearTexData) for netImgui to auto send it + // 2. Manually call 'NetImgui::SendDataTexture' with font texture data + assert(client.mbFontUploaded); + + // Update current active content with our time + ImGui::GetIO().DeltaTime = std::max(1.f / 1000.f, elapsedCheckMs/1000.f); + + // NetImgui isn't waiting for a new frame, try to skip drawing when caller supports it + if( !client.mbValidDrawFrame && bSupportFrameSkip ) + { + return false; + } + } + // Regular Imgui NewFrame + else + { + // Restore context setting override, after a disconnect + client.ContextRestore(); + + // Remove hooks callback only when completly disconnected + if (!client.IsConnectPending()) + { + client.ContextRemoveHooks(); + } + } + + // A new frame is expected, update the current time of the drawing context, and let Imgui know to prepare a new drawing frame + client.mbIsRemoteDrawing = NetImgui::IsConnected(); + client.mbIsDrawing = true; + + // This function can be called from a 'NewFrame' ImGui hook, we should not start a new frame again + if (!client.mbInsideHook) + { + ImGui::NewFrame(); + } + + return true; +} + +//================================================================================================= +void EndFrame(void) +//================================================================================================= +{ + if (!gpClientInfo) return; + + Client::ClientInfo& client = *gpClientInfo; + ScopedBool scopedInside(client.mbInsideNewEnd, true); + + if ( client.mbIsDrawing ) + { + // Must be fetched before 'Render' + ImGuiMouseCursor Cursor = ImGui::GetMouseCursor(); + + // This function can be called from a 'NewFrame' ImGui hook, in which case no need to call this again + if( !client.mbInsideHook ){ + ImGui::Render(); + } + + // Prepare the Dear Imgui DrawData for later tranmission to Server + client.ProcessDrawData(ImGui::GetDrawData(), Cursor); + + // Detect change to background settings by user, and forward them to server + if( client.mBGSetting != client.mBGSettingSent ) + { + CmdBackground* pCmdBackground = netImguiNew(); + *pCmdBackground = client.mBGSetting; + client.mBGSettingSent = client.mBGSetting; + client.mPendingBackgroundOut.Assign(pCmdBackground); + } + + // Restore display size, so we never lose original setting that may get updated after initial connection + if( client.mbIsRemoteDrawing ) { + ImGui::GetIO().DisplaySize = client.mSavedDisplaySize; + } + } + + client.mbIsRemoteDrawing = false; + client.mbIsDrawing = false; + client.mbValidDrawFrame = false; +} + +//================================================================================================= +ImGuiContext* GetContext() +//================================================================================================= +{ + if (!gpClientInfo) return nullptr; + + Client::ClientInfo& client = *gpClientInfo; + return client.mpContext; +} + +//================================================================================================= +void SendDataTexture(ImTextureID textureId, void* pData, uint16_t width, uint16_t height, eTexFormat format, uint32_t dataSize) +//================================================================================================= +{ + if (!gpClientInfo) return; + + Client::ClientInfo& client = *gpClientInfo; + CmdTexture* pCmdTexture = nullptr; + + // Makes sure even 32bits ImTextureID value are received properly as 64bits + uint64_t texId64(0); + static_assert(sizeof(uint64_t) >= sizeof(textureId), "ImTextureID is bigger than 64bits, CmdTexture::mTextureId needs to be updated to support it"); + reinterpret_cast(&texId64)[0] = textureId; + + // Add/Update a texture + if( pData != nullptr ) + { + if( format != eTexFormat::kTexFmtCustom ){ + dataSize = GetTexture_BytePerImage(format, width, height); + } + uint32_t SizeNeeded = dataSize + sizeof(CmdTexture); + pCmdTexture = netImguiSizedNew(SizeNeeded); + + pCmdTexture->mpTextureData.SetPtr(reinterpret_cast(&pCmdTexture[1])); + memcpy(pCmdTexture->mpTextureData.Get(), pData, dataSize); + + pCmdTexture->mSize = SizeNeeded; + pCmdTexture->mWidth = width; + pCmdTexture->mHeight = height; + pCmdTexture->mTextureId = texId64; + pCmdTexture->mFormat = static_cast(format); + pCmdTexture->mpTextureData.ToOffset(); + + // Detects when user is sending the font texture + ScopedImguiContext scopedCtx(client.mpContext ? client.mpContext : ImGui::GetCurrentContext()); + if( ImGui::GetIO().Fonts && ImGui::GetIO().Fonts->TexID == textureId ) + { + client.mbFontUploaded |= true; + client.mpFontTextureData = ImGui::GetIO().Fonts->TexPixelsAlpha8; + client.mFontTextureID = textureId; + } + } + // Texture to remove + else + { + pCmdTexture = netImguiNew(); + pCmdTexture->mTextureId = texId64; + pCmdTexture->mWidth = 0; + pCmdTexture->mHeight = 0; + pCmdTexture->mFormat = eTexFormat::kTexFmt_Invalid; + pCmdTexture->mpTextureData.SetOff(0); + } + + // In unlikely event of too many textures, wait for them to be processed + // (if connected) or Process them now (if not) + while( (client.mTexturesPendingCreated - client.mTexturesPendingSent) >= static_cast(ArrayCount(client.mTexturesPending)) ) + { + if( IsConnected() ) + std::this_thread::yield(); + else + client.ProcessTexturePending(); + } + uint32_t idx = client.mTexturesPendingCreated.fetch_add(1) % static_cast(ArrayCount(client.mTexturesPending)); + client.mTexturesPending[idx] = pCmdTexture; + + // If not connected to server yet, update all pending textures + if( !IsConnected() ) + client.ProcessTexturePending(); +} + +//================================================================================================= +void SetBackground(const ImVec4& bgColor) +//================================================================================================= +{ + if (!gpClientInfo) return; + + Client::ClientInfo& client = *gpClientInfo; + client.mBGSetting = NetImgui::Internal::CmdBackground(); + client.mBGSetting.mClearColor[0] = bgColor.x; + client.mBGSetting.mClearColor[1] = bgColor.y; + client.mBGSetting.mClearColor[2] = bgColor.z; + client.mBGSetting.mClearColor[3] = bgColor.w; +} + +//================================================================================================= +void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint ) +//================================================================================================= +{ + if (!gpClientInfo) return; + + Client::ClientInfo& client = *gpClientInfo; + client.mBGSetting.mClearColor[0] = bgColor.x; + client.mBGSetting.mClearColor[1] = bgColor.y; + client.mBGSetting.mClearColor[2] = bgColor.z; + client.mBGSetting.mClearColor[3] = bgColor.w; + client.mBGSetting.mTextureTint[0] = textureTint.x; + client.mBGSetting.mTextureTint[1] = textureTint.y; + client.mBGSetting.mTextureTint[2] = textureTint.z; + client.mBGSetting.mTextureTint[3] = textureTint.w; + client.mBGSetting.mTextureId = NetImgui::Internal::CmdBackground::kDefaultTexture; +} + +//================================================================================================= +void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint, ImTextureID bgTextureID) +//================================================================================================= +{ + if (!gpClientInfo) return; + + Client::ClientInfo& client = *gpClientInfo; + client.mBGSetting.mClearColor[0] = bgColor.x; + client.mBGSetting.mClearColor[1] = bgColor.y; + client.mBGSetting.mClearColor[2] = bgColor.z; + client.mBGSetting.mClearColor[3] = bgColor.w; + client.mBGSetting.mTextureTint[0] = textureTint.x; + client.mBGSetting.mTextureTint[1] = textureTint.y; + client.mBGSetting.mTextureTint[2] = textureTint.z; + client.mBGSetting.mTextureTint[3] = textureTint.w; + + uint64_t texId64(0); + reinterpret_cast(&texId64)[0] = bgTextureID; + client.mBGSetting.mTextureId = texId64; +} + +//================================================================================================= +void SetCompressionMode(eCompressionMode eMode) +//================================================================================================= +{ + if (!gpClientInfo) return; + + Client::ClientInfo& client = *gpClientInfo; + client.mClientCompressionMode = static_cast(eMode); +} +//================================================================================================= +eCompressionMode GetCompressionMode() +//================================================================================================= +{ + if (!gpClientInfo) return eCompressionMode::kUseServerSetting; + + Client::ClientInfo& client = *gpClientInfo; + return static_cast(client.mClientCompressionMode); +} + +//================================================================================================= +bool Startup(void) +//================================================================================================= +{ + if (!gpClientInfo) + { + gpClientInfo = netImguiNew(); + } + + return Network::Startup(); +} + +//================================================================================================= +void Shutdown() +//================================================================================================= +{ + if (!gpClientInfo) return; + + Disconnect(); + while( gpClientInfo->IsActive() ) + std::this_thread::yield(); + Network::Shutdown(); + + netImguiDeleteSafe(gpClientInfo); +} + + +//================================================================================================= +ImGuiContext* CloneContext(ImGuiContext* pSourceContext) +//================================================================================================= +{ + // Create a context duplicate + ScopedImguiContext scopedSourceCtx(pSourceContext); + ImGuiContext* pContextClone = ImGui::CreateContext(ImGui::GetIO().Fonts); + ImGuiIO& sourceIO = ImGui::GetIO(); + ImGuiStyle& sourceStyle = ImGui::GetStyle(); + { + ScopedImguiContext scopedCloneCtx(pContextClone); + ImGuiIO& newIO = ImGui::GetIO(); + ImGuiStyle& newStyle = ImGui::GetStyle(); + + // Import the style/options settings of current context, into this one + memcpy(&newStyle, &sourceStyle, sizeof(newStyle)); + memcpy(&newIO, &sourceIO, sizeof(newIO)); + //memcpy(newIO.KeyMap, sourceIO.KeyMap, sizeof(newIO.KeyMap)); + newIO.InputQueueCharacters.Data = nullptr; + newIO.InputQueueCharacters.Size = 0; + newIO.InputQueueCharacters.Capacity = 0; + } + return pContextClone; +} + +//================================================================================================= +uint8_t GetTexture_BitsPerPixel(eTexFormat eFormat) +//================================================================================================= +{ + switch(eFormat) + { + case eTexFormat::kTexFmtA8: return 8*1; + case eTexFormat::kTexFmtRGBA8: return 8*4; + case eTexFormat::kTexFmtCustom: return 0; + case eTexFormat::kTexFmt_Invalid: return 0; + } + return 0; +} + +//================================================================================================= +uint32_t GetTexture_BytePerLine(eTexFormat eFormat, uint32_t pixelWidth) +//================================================================================================= +{ + uint32_t bitsPerPixel = static_cast(GetTexture_BitsPerPixel(eFormat)); + return pixelWidth * bitsPerPixel / 8; + //Note: If adding support to BC compression format, have to take into account 4x4 size alignment +} + +//================================================================================================= +uint32_t GetTexture_BytePerImage(eTexFormat eFormat, uint32_t pixelWidth, uint32_t pixelHeight) +//================================================================================================= +{ + return GetTexture_BytePerLine(eFormat, pixelWidth) * pixelHeight; + //Note: If adding support to BC compression format, have to take into account 4x4 size alignement +} + +static inline void AddKeyEvent(const Client::ClientInfo& client, const CmdInput* pCmdInput, CmdInput::NetImguiKeys netimguiKey, ImGuiKey imguiKey) +{ + uint32_t valIndex = netimguiKey/64; + uint64_t valMask = 0x0000000000000001ull << (netimguiKey%64); +#if IMGUI_VERSION_NUM < 18700 + IM_UNUSED(client); + ImGui::GetIO().KeysDown[imguiKey] = (pCmdInput->mInputDownMask[valIndex] & valMask) != 0; +#else + bool bChanged = (pCmdInput->mInputDownMask[valIndex] ^ client.mPreviousInputState.mInputDownMask[valIndex]) & valMask; + if( bChanged ){ + ImGui::GetIO().AddKeyEvent(imguiKey, pCmdInput->mInputDownMask[valIndex] & valMask ); + } +#endif +} + +static inline void AddKeyAnalogEvent(const Client::ClientInfo& client, const CmdInput* pCmdInput, CmdInput::NetImguiKeys netimguiKey, ImGuiKey imguiKey) +{ + uint32_t valIndex = netimguiKey/64; + uint64_t valMask = 0x0000000000000001ull << (netimguiKey%64); + assert(CmdInput::kAnalog_First <= static_cast(netimguiKey) && static_cast(netimguiKey) <= CmdInput::kAnalog_Last); +#if IMGUI_VERSION_NUM < 18700 + IM_UNUSED(client); IM_UNUSED(pCmdInput); IM_UNUSED(netimguiKey); IM_UNUSED(imguiKey); +#else + int indexAnalog = netimguiKey - CmdInput::kAnalog_First; + indexAnalog = indexAnalog >= static_cast(CmdInput::kAnalog_Count) ? CmdInput::kAnalog_Count - 1 : indexAnalog; + float analogValue = pCmdInput->mInputAnalog[indexAnalog]; + bool bChanged = (pCmdInput->mInputDownMask[valIndex] ^ client.mPreviousInputState.mInputDownMask[valIndex]) & valMask; + bChanged |= abs(client.mPreviousInputState.mInputAnalog[indexAnalog] - analogValue) > 0.001f; + if(bChanged){ + ImGui::GetIO().AddKeyAnalogEvent(imguiKey, pCmdInput->mInputDownMask[valIndex] & valMask, analogValue); + } +#endif +} + +//================================================================================================= +bool ProcessInputData(Client::ClientInfo& client) +//================================================================================================= +{ + // Update the current clipboard data received from Server + CmdClipboard* pCmdClipboardNew = client.mPendingClipboardIn.Release(); + if( pCmdClipboardNew ){ + netImguiDeleteSafe(client.mpCmdClipboard); + client.mpCmdClipboard = pCmdClipboardNew; + } + + // Update the keyboard/mouse/gamepad inputs + CmdInput* pCmdInputNew = client.mPendingInputIn.Release(); + bool hasNewInput = pCmdInputNew != nullptr; + CmdInput* pCmdInput = hasNewInput ? pCmdInputNew : client.mpCmdInputPending; + ImGuiIO& io = ImGui::GetIO(); + + if (pCmdInput) + { + const float wheelY = pCmdInput->mMouseWheelVert - client.mPreviousInputState.mMouseWheelVertPrev; + const float wheelX = pCmdInput->mMouseWheelHoriz - client.mPreviousInputState.mMouseWheelHorizPrev; + io.DisplaySize = ImVec2(pCmdInput->mScreenSize[0], pCmdInput->mScreenSize[1]); + + // User assigned a function callback handling FontScaling, + // use it to request a Font update on DPI scaling change on the server + if (gpClientInfo->mFontCreationFunction != nullptr) + { + if(abs(gpClientInfo->mFontCreationScaling - pCmdInput->mFontDPIScaling) > 0.01f) + { + gpClientInfo->mFontCreationFunction(gpClientInfo->mFontCreationScaling, pCmdInput->mFontDPIScaling); + gpClientInfo->mFontCreationScaling = pCmdInput->mFontDPIScaling; + } + } + // Client doesn't support regenerating the font at new DPI + // Use FontGlobalScale to affect rendering size, resulting in blurrier result + else + { + io.FontGlobalScale = pCmdInput->mFontDPIScaling; + } + +#if IMGUI_VERSION_NUM < 18700 + io.MousePos = ImVec2(pCmdInput->mMousePos[0], pCmdInput->mMousePos[1]); + io.MouseWheel = wheelY; + io.MouseWheelH = wheelX; + for (uint32_t i(0); i < CmdInput::NetImguiMouseButton::ImGuiMouseButton_COUNT; ++i) { + io.MouseDown[i] = (pCmdInput->mMouseDownMask & (0x0000000000000001ull << i)) != 0; + } + + #define AddInputDown(KEYNAME) AddKeyEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey_::KEYNAME); + AddInputDown(ImGuiKey_Tab) + AddInputDown(ImGuiKey_LeftArrow) + AddInputDown(ImGuiKey_RightArrow) + AddInputDown(ImGuiKey_UpArrow) + AddInputDown(ImGuiKey_DownArrow) + AddInputDown(ImGuiKey_PageUp) + AddInputDown(ImGuiKey_PageDown) + AddInputDown(ImGuiKey_Home) + AddInputDown(ImGuiKey_End) + AddInputDown(ImGuiKey_Insert) + AddInputDown(ImGuiKey_Delete) + AddInputDown(ImGuiKey_Backspace) + AddInputDown(ImGuiKey_Space) + AddInputDown(ImGuiKey_Enter) + AddInputDown(ImGuiKey_Escape) + AddInputDown(ImGuiKey_A) // for text edit CTRL+A: select all + AddInputDown(ImGuiKey_C) // for text edit CTRL+C: copy + AddInputDown(ImGuiKey_V) // for text edit CTRL+V: paste + AddInputDown(ImGuiKey_X) // for text edit CTRL+X: cut + AddInputDown(ImGuiKey_Y) // for text edit CTRL+Y: redo + AddInputDown(ImGuiKey_Z) // for text edit CTRL+Z: undo + + io.KeyShift = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModShift); + io.KeyCtrl = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModCtrl); + io.KeyAlt = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModAlt); + io.KeySuper = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModSuper); +#else + #if IMGUI_VERSION_NUM < 18837 + #define ImGuiKey ImGuiKey_ + #endif + // At the moment All Dear Imgui version share the same ImGuiKey_ enum (with a 512 value offset), + // but could change in the future, so convert from our own enum version, to Dear ImGui. + #define AddInputDown(KEYNAME) AddKeyEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey::KEYNAME); + #define AddAnalogInputDown(KEYNAME) AddKeyAnalogEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey::KEYNAME); + AddInputDown(ImGuiKey_Tab) + AddInputDown(ImGuiKey_LeftArrow) + AddInputDown(ImGuiKey_RightArrow) + AddInputDown(ImGuiKey_UpArrow) + AddInputDown(ImGuiKey_DownArrow) + AddInputDown(ImGuiKey_PageUp) + AddInputDown(ImGuiKey_PageDown) + AddInputDown(ImGuiKey_Home) + AddInputDown(ImGuiKey_End) + AddInputDown(ImGuiKey_Insert) + AddInputDown(ImGuiKey_Delete) + AddInputDown(ImGuiKey_Backspace) + AddInputDown(ImGuiKey_Space) + AddInputDown(ImGuiKey_Enter) + AddInputDown(ImGuiKey_Escape) + + AddInputDown(ImGuiKey_LeftCtrl) AddInputDown(ImGuiKey_LeftShift) AddInputDown(ImGuiKey_LeftAlt) AddInputDown(ImGuiKey_LeftSuper) + AddInputDown(ImGuiKey_RightCtrl) AddInputDown(ImGuiKey_RightShift) AddInputDown(ImGuiKey_RightAlt) AddInputDown(ImGuiKey_RightSuper) + AddInputDown(ImGuiKey_Menu) + AddInputDown(ImGuiKey_0) AddInputDown(ImGuiKey_1) AddInputDown(ImGuiKey_2) AddInputDown(ImGuiKey_3) AddInputDown(ImGuiKey_4) AddInputDown(ImGuiKey_5) AddInputDown(ImGuiKey_6) AddInputDown(ImGuiKey_7) AddInputDown(ImGuiKey_8) AddInputDown(ImGuiKey_9) + AddInputDown(ImGuiKey_A) AddInputDown(ImGuiKey_B) AddInputDown(ImGuiKey_C) AddInputDown(ImGuiKey_D) AddInputDown(ImGuiKey_E) AddInputDown(ImGuiKey_F) AddInputDown(ImGuiKey_G) AddInputDown(ImGuiKey_H) AddInputDown(ImGuiKey_I) AddInputDown(ImGuiKey_J) + AddInputDown(ImGuiKey_K) AddInputDown(ImGuiKey_L) AddInputDown(ImGuiKey_M) AddInputDown(ImGuiKey_N) AddInputDown(ImGuiKey_O) AddInputDown(ImGuiKey_P) AddInputDown(ImGuiKey_Q) AddInputDown(ImGuiKey_R) AddInputDown(ImGuiKey_S) AddInputDown(ImGuiKey_T) + AddInputDown(ImGuiKey_U) AddInputDown(ImGuiKey_V) AddInputDown(ImGuiKey_W) AddInputDown(ImGuiKey_X) AddInputDown(ImGuiKey_Y) AddInputDown(ImGuiKey_Z) + AddInputDown(ImGuiKey_F1) AddInputDown(ImGuiKey_F2) AddInputDown(ImGuiKey_F3) AddInputDown(ImGuiKey_F4) AddInputDown(ImGuiKey_F5) AddInputDown(ImGuiKey_F6) + AddInputDown(ImGuiKey_F7) AddInputDown(ImGuiKey_F8) AddInputDown(ImGuiKey_F9) AddInputDown(ImGuiKey_F10) AddInputDown(ImGuiKey_F11) AddInputDown(ImGuiKey_F12) + + AddInputDown(ImGuiKey_Apostrophe) + AddInputDown(ImGuiKey_Comma) + AddInputDown(ImGuiKey_Minus) + AddInputDown(ImGuiKey_Period) + AddInputDown(ImGuiKey_Slash) + AddInputDown(ImGuiKey_Semicolon) + AddInputDown(ImGuiKey_Equal) + AddInputDown(ImGuiKey_LeftBracket) + AddInputDown(ImGuiKey_Backslash) + AddInputDown(ImGuiKey_RightBracket) + AddInputDown(ImGuiKey_GraveAccent) + AddInputDown(ImGuiKey_CapsLock) + AddInputDown(ImGuiKey_ScrollLock) + AddInputDown(ImGuiKey_NumLock) + AddInputDown(ImGuiKey_PrintScreen) + AddInputDown(ImGuiKey_Pause) + AddInputDown(ImGuiKey_Keypad0) AddInputDown(ImGuiKey_Keypad1) AddInputDown(ImGuiKey_Keypad2) AddInputDown(ImGuiKey_Keypad3) AddInputDown(ImGuiKey_Keypad4) + AddInputDown(ImGuiKey_Keypad5) AddInputDown(ImGuiKey_Keypad6) AddInputDown(ImGuiKey_Keypad7) AddInputDown(ImGuiKey_Keypad8) AddInputDown(ImGuiKey_Keypad9) + AddInputDown(ImGuiKey_KeypadDecimal) AddInputDown(ImGuiKey_KeypadDivide) AddInputDown(ImGuiKey_KeypadMultiply) + AddInputDown(ImGuiKey_KeypadSubtract) AddInputDown(ImGuiKey_KeypadAdd) AddInputDown(ImGuiKey_KeypadEnter) + AddInputDown(ImGuiKey_KeypadEqual) + +#if IMGUI_VERSION_NUM >= 19000 + AddInputDown(ImGuiKey_F13) AddInputDown(ImGuiKey_F14) AddInputDown(ImGuiKey_F15) AddInputDown(ImGuiKey_F16) AddInputDown(ImGuiKey_F17) AddInputDown(ImGuiKey_F18) + AddInputDown(ImGuiKey_F19) AddInputDown(ImGuiKey_F20) AddInputDown(ImGuiKey_F21) AddInputDown(ImGuiKey_F22) AddInputDown(ImGuiKey_F23) AddInputDown(ImGuiKey_F24) + AddInputDown(ImGuiKey_AppBack) + AddInputDown(ImGuiKey_AppForward) +#endif + // Gamepad + AddInputDown(ImGuiKey_GamepadStart) + AddInputDown(ImGuiKey_GamepadBack) + AddInputDown(ImGuiKey_GamepadFaceUp) + AddInputDown(ImGuiKey_GamepadFaceDown) + AddInputDown(ImGuiKey_GamepadFaceLeft) + AddInputDown(ImGuiKey_GamepadFaceRight) + AddInputDown(ImGuiKey_GamepadDpadUp) + AddInputDown(ImGuiKey_GamepadDpadDown) + AddInputDown(ImGuiKey_GamepadDpadLeft) + AddInputDown(ImGuiKey_GamepadDpadRight) + AddInputDown(ImGuiKey_GamepadL1) + AddInputDown(ImGuiKey_GamepadR1) + AddInputDown(ImGuiKey_GamepadL2) + AddInputDown(ImGuiKey_GamepadR2) + AddInputDown(ImGuiKey_GamepadL3) + AddInputDown(ImGuiKey_GamepadR3) + AddAnalogInputDown(ImGuiKey_GamepadLStickUp) + AddAnalogInputDown(ImGuiKey_GamepadLStickDown) + AddAnalogInputDown(ImGuiKey_GamepadLStickLeft) + AddAnalogInputDown(ImGuiKey_GamepadLStickRight) + AddAnalogInputDown(ImGuiKey_GamepadRStickUp) + AddAnalogInputDown(ImGuiKey_GamepadRStickDown) + AddAnalogInputDown(ImGuiKey_GamepadRStickLeft) + AddAnalogInputDown(ImGuiKey_GamepadRStickRight) + + #undef AddInputDown + #undef AddAnalogInputDown + #if IMGUI_VERSION_NUM < 18837 + #undef ImGuiKey + #endif + + #if IMGUI_VERSION_NUM < 18837 + #define ImGuiMod_Ctrl ImGuiKey_ModCtrl + #define ImGuiMod_Shift ImGuiKey_ModShift + #define ImGuiMod_Alt ImGuiKey_ModAlt + #define ImGuiMod_Super ImGuiKey_ModSuper + #endif + io.AddKeyEvent(ImGuiMod_Ctrl, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModCtrl)); + io.AddKeyEvent(ImGuiMod_Shift, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModShift)); + io.AddKeyEvent(ImGuiMod_Alt, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModAlt)); + io.AddKeyEvent(ImGuiMod_Super, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModSuper)); + + // Mouse + io.AddMouseWheelEvent(wheelX, wheelY); + io.AddMousePosEvent(pCmdInput->mMousePos[0], pCmdInput->mMousePos[1]); + for(int i(0); imMouseDownMask ^ client.mPreviousInputState.mMouseDownMask) & valMask){ + io.AddMouseButtonEvent(i, pCmdInput->mMouseDownMask & valMask); + } + } +#endif + uint16_t character; + io.InputQueueCharacters.resize(0); + while (client.mPendingKeyIn.ReadData(&character)){ + ImWchar ConvertedKey = static_cast(character); + io.AddInputCharacter(ConvertedKey); + } + + static_assert(sizeof(client.mPreviousInputState.mInputDownMask) == sizeof(pCmdInput->mInputDownMask), "Array size should match"); + static_assert(sizeof(client.mPreviousInputState.mInputAnalog) == sizeof(pCmdInput->mInputAnalog), "Array size should match"); + memcpy(client.mPreviousInputState.mInputDownMask, pCmdInput->mInputDownMask, sizeof(client.mPreviousInputState.mInputDownMask)); + memcpy(client.mPreviousInputState.mInputAnalog, pCmdInput->mInputAnalog, sizeof(client.mPreviousInputState.mInputAnalog)); + client.mPreviousInputState.mMouseDownMask = pCmdInput->mMouseDownMask; + client.mPreviousInputState.mMouseWheelVertPrev = pCmdInput->mMouseWheelVert; + client.mPreviousInputState.mMouseWheelHorizPrev = pCmdInput->mMouseWheelHoriz; + client.mServerCompressionEnabled = pCmdInput->mCompressionUse; + client.mServerCompressionSkip |= pCmdInput->mCompressionSkip; + } + + if( hasNewInput ){ + netImguiDeleteSafe(client.mpCmdInputPending); + client.mpCmdInputPending = pCmdInputNew; + } + return hasNewInput; +} + +} // namespace NetImgui + +#endif //NETIMGUI_ENABLED + +#include "NetImgui_WarningReenable.h" diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.cpp b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.cpp new file mode 100644 index 0000000..11866dd --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.cpp @@ -0,0 +1,716 @@ +#include "NetImgui_Shared.h" + +#if NETIMGUI_ENABLED +#include "NetImgui_WarningDisable.h" +#include "NetImgui_Client.h" +#include "NetImgui_Network.h" +#include "NetImgui_CmdPackets.h" + +namespace NetImgui { namespace Internal { namespace Client +{ + +//================================================================================================= +// SAVED IMGUI CONTEXT +// Because we overwrite some Imgui context IO values, we save them before makign any change +// and restore them after detecting a disconnection +//================================================================================================= +void SavedImguiContext::Save(ImGuiContext* copyFrom) +{ + ScopedImguiContext scopedContext(copyFrom); + ImGuiIO& sourceIO = ImGui::GetIO(); + mSavedContext = true; + mConfigFlags = sourceIO.ConfigFlags; + mBackendFlags = sourceIO.BackendFlags; + mBackendPlatformName = sourceIO.BackendPlatformName; + mBackendRendererName = sourceIO.BackendRendererName; + mDrawMouse = sourceIO.MouseDrawCursor; + mFontGlobalScale = sourceIO.FontGlobalScale; + mFontGeneratedSize = sourceIO.Fonts->Fonts.Size > 0 ? sourceIO.Fonts->Fonts[0]->FontSize : 13.f; // Save size to restore the font to original size +#if IMGUI_VERSION_NUM < 18700 + memcpy(mKeyMap, sourceIO.KeyMap, sizeof(mKeyMap)); +#endif +#if IMGUI_VERSION_NUM < 19110 + mGetClipboardTextFn = sourceIO.GetClipboardTextFn; + mSetClipboardTextFn = sourceIO.SetClipboardTextFn; + mClipboardUserData = sourceIO.ClipboardUserData; +#else + ImGuiPlatformIO& plaformIO = ImGui::GetPlatformIO(); + mGetClipboardTextFn = plaformIO.Platform_GetClipboardTextFn; + mSetClipboardTextFn = plaformIO.Platform_SetClipboardTextFn; + mClipboardUserData = plaformIO.Platform_ClipboardUserData; +#endif +} + +void SavedImguiContext::Restore(ImGuiContext* copyTo) +{ + ScopedImguiContext scopedContext(copyTo); + ImGuiIO& destIO = ImGui::GetIO(); + mSavedContext = false; + destIO.ConfigFlags = mConfigFlags; + destIO.BackendFlags = mBackendFlags; + destIO.BackendPlatformName = mBackendPlatformName; + destIO.BackendRendererName = mBackendRendererName; + destIO.MouseDrawCursor = mDrawMouse; + destIO.FontGlobalScale = mFontGlobalScale; +#if IMGUI_VERSION_NUM < 18700 + memcpy(destIO.KeyMap, mKeyMap, sizeof(destIO.KeyMap)); +#endif +#if IMGUI_VERSION_NUM < 19110 + destIO.GetClipboardTextFn = mGetClipboardTextFn; + destIO.SetClipboardTextFn = mSetClipboardTextFn; + destIO.ClipboardUserData = mClipboardUserData; +#else + ImGuiPlatformIO& plaformIO = ImGui::GetPlatformIO(); + plaformIO.Platform_GetClipboardTextFn = mGetClipboardTextFn; + plaformIO.Platform_SetClipboardTextFn = mSetClipboardTextFn; + plaformIO.Platform_ClipboardUserData = mClipboardUserData; +#endif +} + +//================================================================================================= +// GET CLIPBOARD +// Content received from the Server +//================================================================================================= +#if IMGUI_VERSION_NUM < 19110 +static const char* GetClipboardTextFn_NetImguiImpl(void* user_data_ctx) +{ + const ClientInfo* pClient = reinterpret_cast(user_data_ctx); + return pClient && pClient->mpCmdClipboard ? pClient->mpCmdClipboard->mContentUTF8.Get() : nullptr; +} +#else +static const char* GetClipboardTextFn_NetImguiImpl(ImGuiContext* ctx) +{ + ScopedImguiContext scopedContext(ctx); + const ClientInfo* pClient = reinterpret_cast(ImGui::GetPlatformIO().Platform_ClipboardUserData); + return pClient && pClient->mpCmdClipboard ? pClient->mpCmdClipboard->mContentUTF8.Get() : nullptr; +} +#endif + + +//================================================================================================= +// SET CLIPBOARD +//================================================================================================= +#if IMGUI_VERSION_NUM < 19110 +static void SetClipboardTextFn_NetImguiImpl(void* user_data_ctx, const char* text) +{ + if(user_data_ctx){ + ClientInfo* pClient = reinterpret_cast(user_data_ctx); + CmdClipboard* pClipboardOut = CmdClipboard::Create(text); + pClient->mPendingClipboardOut.Assign(pClipboardOut); + } +} +#else +static void SetClipboardTextFn_NetImguiImpl(ImGuiContext* ctx, const char* text) +{ + ScopedImguiContext scopedContext(ctx); + ClientInfo* pClient = reinterpret_cast(ImGui::GetPlatformIO().Platform_ClipboardUserData); + CmdClipboard* pClipboardOut = CmdClipboard::Create(text); + pClient->mPendingClipboardOut.Assign(pClipboardOut); +} +#endif + +//================================================================================================= +// INCOM: INPUT +// Receive new keyboard/mouse/screen resolution input to pass on to dearImgui +//================================================================================================= +void Communications_Incoming_Input(ClientInfo& client) +{ + auto pCmdInput = static_cast(client.mPendingRcv.pCommand); + client.mPendingRcv.bAutoFree = false; // Taking ownership of the data + client.mDesiredFps = pCmdInput->mDesiredFps > 0.f ? pCmdInput->mDesiredFps : 0.f; + size_t keyCount(pCmdInput->mKeyCharCount); + client.mPendingKeyIn.AddData(pCmdInput->mKeyChars, keyCount); + client.mPendingInputIn.Assign(pCmdInput); +} + +//================================================================================================= +// INCOM: CLIPBOARD +// Receive server new clipboard content, updating internal cache +//================================================================================================= +void Communications_Incoming_Clipboard(ClientInfo& client) +{ + auto pCmdClipboard = static_cast(client.mPendingRcv.pCommand); + client.mPendingRcv.bAutoFree = false; // Taking ownership of the data + pCmdClipboard->ToPointers(); + client.mPendingClipboardIn.Assign(pCmdClipboard); +} + +//================================================================================================= +// OUTCOM: TEXTURE +// Transmit all pending new/updated texture +//================================================================================================= +void Communications_Outgoing_Textures(ClientInfo& client) +{ + client.ProcessTexturePending(); + if( client.mbHasTextureUpdate ) + { + for(auto& cmdTexture : client.mTextures) + { + if( !cmdTexture.mbSent && cmdTexture.mpCmdTexture ) + { + client.mPendingSend.pCommand = cmdTexture.mpCmdTexture; + client.mPendingSend.bAutoFree = cmdTexture.mpCmdTexture->mFormat == eTexFormat::kTexFmt_Invalid; // Texture as been marked for deletion, can now free memory allocated for this + cmdTexture.mbSent = true; + return; // Exit as soon as we find a texture to send, so thread can proceed with transmitting it + } + } + client.mbHasTextureUpdate = false; // No pending texture detected, mark as 'all sent' + } +} + +//================================================================================================= +// OUTCOM: BACKGROUND +// Transmit the current client background settings +//================================================================================================= +void Communications_Outgoing_Background(ClientInfo& client) +{ + CmdBackground* pPendingBackground = client.mPendingBackgroundOut.Release(); + if( pPendingBackground ) + { + client.mPendingSend.pCommand = pPendingBackground; + client.mPendingSend.bAutoFree = false; + } +} + +//================================================================================================= +// OUTCOM: FRAME +// Transmit a new dearImgui frame to render +//================================================================================================= +void Communications_Outgoing_Frame(ClientInfo& client) +{ + CmdDrawFrame* pPendingDraw = client.mPendingFrameOut.Release(); + if( pPendingDraw ) + { + pPendingDraw->mFrameIndex = client.mFrameIndex++; + //--------------------------------------------------------------------- + // Apply delta compression to DrawCommand, when requested + if( pPendingDraw->mCompressed ) + { + // Create a new Compressed DrawFrame Command + if( client.mpCmdDrawLast && !client.mServerCompressionSkip ){ + client.mpCmdDrawLast->ToPointers(); + CmdDrawFrame* pDrawCompressed = CompressCmdDrawFrame(client.mpCmdDrawLast, pPendingDraw); + netImguiDeleteSafe(client.mpCmdDrawLast); + client.mpCmdDrawLast = pPendingDraw; // Keep original new command for next frame delta compression + pPendingDraw = pDrawCompressed; // Request compressed copy to be sent to server + } + // Save DrawCmd for next frame delta compression + else { + pPendingDraw->mCompressed = false; + client.mpCmdDrawLast = pPendingDraw; + } + } + client.mServerCompressionSkip = false; + + //--------------------------------------------------------------------- + // Ready to send command to server + pPendingDraw->ToOffsets(); + client.mPendingSend.pCommand = pPendingDraw; + client.mPendingSend.bAutoFree = client.mpCmdDrawLast != pPendingDraw; + } +} + +//================================================================================================= +// OUTCOM: Clipboard +// Send client 'Copy' clipboard content to Server +//================================================================================================= +void Communications_Outgoing_Clipboard(ClientInfo& client) +{ + CmdClipboard* pPendingClipboard = client.mPendingClipboardOut.Release(); + if( pPendingClipboard ){ + pPendingClipboard->ToOffsets(); + client.mPendingSend.pCommand = pPendingClipboard; + client.mPendingSend.bAutoFree = true; + } +} + +//================================================================================================= +// INCOMING COMMUNICATIONS +//================================================================================================= +void Communications_Incoming(ClientInfo& client) +{ + if( Network::DataReceivePending(client.mpSocketComs) ) + { + //----------------------------------------------------------------------------------------- + // 1. Ready to receive new command, starts the process by reading Header + //----------------------------------------------------------------------------------------- + if( client.mPendingRcv.IsReady() ) + { + client.mCmdPendingRead = CmdPendingRead(); + client.mPendingRcv.pCommand = &client.mCmdPendingRead; + client.mPendingRcv.bAutoFree= false; + } + + //----------------------------------------------------------------------------------------- + // 2. Read incoming command from server + //----------------------------------------------------------------------------------------- + if( client.mPendingRcv.IsPending() ) + { + Network::DataReceive(client.mpSocketComs, client.mPendingRcv); + + // Detected a new command bigger than header, allocate memory for it + if( client.mPendingRcv.pCommand->mSize > sizeof(CmdPendingRead) && + client.mPendingRcv.pCommand == &client.mCmdPendingRead ) + { + CmdPendingRead* pCmdHeader = reinterpret_cast(netImguiSizedNew(client.mPendingRcv.pCommand->mSize)); + *pCmdHeader = client.mCmdPendingRead; + client.mPendingRcv.pCommand = pCmdHeader; + client.mPendingRcv.bAutoFree= true; + } + } + + //----------------------------------------------------------------------------------------- + // 3. Command fully received from Server, process it + //----------------------------------------------------------------------------------------- + if( client.mPendingRcv.IsDone() ) + { + if( !client.mPendingRcv.IsError() ) + { + switch( client.mPendingRcv.pCommand->mType ) + { + case CmdHeader::eCommands::Input: Communications_Incoming_Input(client); break; + case CmdHeader::eCommands::Clipboard: Communications_Incoming_Clipboard(client); break; + // Commands not received in main loop, by Client + case CmdHeader::eCommands::Version: + case CmdHeader::eCommands::Texture: + case CmdHeader::eCommands::DrawFrame: + case CmdHeader::eCommands::Background: + case CmdHeader::eCommands::Count: break; + } + } + + // Cleanup after read completion + if( client.mPendingRcv.IsError() ){ + client.mbDisconnectPending = true; + } + if( client.mPendingRcv.bAutoFree ){ + netImguiDeleteSafe(client.mPendingRcv.pCommand); + } + client.mPendingRcv = PendingCom(); + } + } + // Prevent high CPU usage when waiting for new data + else + { + //std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::microseconds(250)); + } +} + +//================================================================================================= +// OUTGOING COMMUNICATIONS +//================================================================================================= +void Communications_Outgoing(ClientInfo& client) +{ + //--------------------------------------------------------------------------------------------- + // Try finishing sending a pending command to Server + //--------------------------------------------------------------------------------------------- + if( client.mPendingSend.IsPending() ) + { + Network::DataSend(client.mpSocketComs, client.mPendingSend); + + // Free allocated memory for command + if( client.mPendingSend.IsDone() ) + { + if( client.mPendingSend.IsError() ){ + client.mbDisconnectPending = true; + } + if( client.mPendingSend.bAutoFree ){ + netImguiDeleteSafe(client.mPendingSend.pCommand); + } + client.mPendingSend = PendingCom(); + } + } + //--------------------------------------------------------------------------------------------- + // Initiate sending next command to Server, when none are in flight + // Keep track of what command was last send to prevent 1 type to monopolize coms + //--------------------------------------------------------------------------------------------- + constexpr CmdHeader::eCommands kCommandsOrder[] = { + CmdHeader::eCommands::Texture, CmdHeader::eCommands::Background, + CmdHeader::eCommands::Clipboard, CmdHeader::eCommands::DrawFrame}; + constexpr uint32_t kCommandCounts = static_cast(sizeof(kCommandsOrder)/sizeof(CmdHeader::eCommands)); + + uint32_t Index(client.mPendingSendNext); + while( client.mPendingSend.IsReady() && (Index-client.mPendingSendNext)(&cmdVersionRcv); + while( !PendingRcv.IsDone() && cmdVersionRcv.mType == CmdHeader::eCommands::Version ) + { + while( !client.mbDisconnectPending && !Network::DataReceivePending(client.mpSocketPending) ){ + std::this_thread::yield(); // Idle until we receive the remote data + } + Network::DataReceive(client.mpSocketPending, PendingRcv); + } + + bool bForceConnect = client.mServerForceConnectEnabled && (cmdVersionRcv.mFlags & static_cast(CmdVersion::eFlags::ConnectForce)) != 0; + bool bCanConnect = !PendingRcv.IsError() && + cmdVersionRcv.mType == cmdVersionSend.mType && + cmdVersionRcv.mVersion == cmdVersionSend.mVersion && + cmdVersionRcv.mWCharSize == cmdVersionSend.mWCharSize && + (!client.IsConnected() || bForceConnect); + + StringCopy(cmdVersionSend.mClientName, client.mName); + cmdVersionSend.mFlags = client.IsConnected() && !bCanConnect ? static_cast(CmdVersion::eFlags::IsConnected): 0; + cmdVersionSend.mFlags |= client.IsConnected() && !client.mServerForceConnectEnabled ? static_cast(CmdVersion::eFlags::IsUnavailable) : 0; + + PendingSend.pCommand = reinterpret_cast(&cmdVersionSend); + while( !PendingSend.IsDone() ){ + Network::DataSend(client.mpSocketPending, PendingSend); + } + + //--------------------------------------------------------------------- + // Connection established, init client + //--------------------------------------------------------------------- + if( bCanConnect && !PendingSend.IsError() && (!client.IsConnected() || bForceConnect) ) + { + Network::SocketInfo* pNewComSocket = client.mpSocketPending.exchange(nullptr); + + // If we detect an active connection with Server and 'ForceConnect was requested, close it first + if( client.IsConnected() ) + { + client.mbDisconnectPending = true; + while( client.IsConnected() ); + } + + for(auto& texture : client.mTextures) + { + texture.mbSent = false; + } + + client.mpSocketComs = pNewComSocket; // Take ownerhip of socket + client.mbHasTextureUpdate = true; // Force sending the client textures + client.mBGSettingSent.mTextureId = client.mBGSetting.mTextureId-1u; // Force sending the Background settings (by making different than current settings) + client.mFrameIndex = 0; + client.mPendingSendNext = 0; + client.mServerForceConnectEnabled = (cmdVersionRcv.mFlags & static_cast(CmdVersion::eFlags::ConnectExclusive)) == 0; + client.mPendingRcv = PendingCom(); + client.mPendingSend = PendingCom(); + } + + // Disconnect pending socket if init failed + Network::SocketInfo* SocketPending = client.mpSocketPending.exchange(nullptr); + bool bValidConnection = SocketPending == nullptr; + if( SocketPending ){ + NetImgui::Internal::Network::Disconnect(SocketPending); + } + + client.mbComInitActive = false; + return bValidConnection; +} + +//================================================================================================= +// COMMUNICATIONS MAIN LOOP +//================================================================================================= +void Communications_Loop(void* pClientVoid) +{ + IM_ASSERT(pClientVoid != nullptr); + ClientInfo* pClient = reinterpret_cast(pClientVoid); + pClient->mbDisconnectPending = false; + pClient->mbClientThreadActive = true; + + while( !pClient->mbDisconnectPending ) + { + Communications_Outgoing(*pClient); + Communications_Incoming(*pClient); + } + + Network::SocketInfo* pSocket = pClient->mpSocketComs.exchange(nullptr); + if (pSocket){ + NetImgui::Internal::Network::Disconnect(pSocket); + } + pClient->mbClientThreadActive = false; +} + +//================================================================================================= +// COMMUNICATIONS CONNECT THREAD : Reach out and connect to a NetImGuiServer +//================================================================================================= +void CommunicationsConnect(void* pClientVoid) +{ + IM_ASSERT(pClientVoid != nullptr); + ClientInfo* pClient = reinterpret_cast(pClientVoid); + if( Communications_Initialize(*pClient) ) + { + Communications_Loop(pClientVoid); + } +} + +//================================================================================================= +// COMMUNICATIONS HOST THREAD : Waiting NetImGuiServer reaching out to us. +// Launch a new com loop when connection is established +//================================================================================================= +void CommunicationsHost(void* pClientVoid) +{ + ClientInfo* pClient = reinterpret_cast(pClientVoid); + pClient->mbListenThreadActive = true; + pClient->mbDisconnectListen = false; + pClient->mpSocketListen = pClient->mpSocketPending.exchange(nullptr); + + while( pClient->mpSocketListen.load() != nullptr && !pClient->mbDisconnectListen ) + { + pClient->mpSocketPending = Network::ListenConnect(pClient->mpSocketListen); + if( pClient->mpSocketPending.load() != nullptr && Communications_Initialize(*pClient) ) + { + pClient->mThreadFunction(Client::Communications_Loop, pClient); + } + std::this_thread::sleep_for(std::chrono::milliseconds(16)); // Prevents this thread from taking entire core, waiting on server connection requests + } + + Network::SocketInfo* pSocket = pClient->mpSocketListen.exchange(nullptr); + NetImgui::Internal::Network::Disconnect(pSocket); + pClient->mbListenThreadActive = false; +} + +//================================================================================================= +// Support of the Dear ImGui hooks +// (automatic call to NetImgui::BeginFrame()/EndFrame() on ImGui::BeginFrame()/Imgui::Render() +//================================================================================================= +#if NETIMGUI_IMGUI_CALLBACK_ENABLED +void HookBeginFrame(ImGuiContext*, ImGuiContextHook* hook) +{ + Client::ClientInfo& client = *reinterpret_cast(hook->UserData); + if (!client.mbInsideNewEnd) + { + ScopedBool scopedInside(client.mbInsideHook, true); + NetImgui::NewFrame(false); + } +} + +void HookEndFrame(ImGuiContext*, ImGuiContextHook* hook) +{ + Client::ClientInfo& client = *reinterpret_cast(hook->UserData); + if (!client.mbInsideNewEnd) + { + ScopedBool scopedInside(client.mbInsideHook, true); + NetImgui::EndFrame(); + } +} + +#endif // NETIMGUI_IMGUI_CALLBACK_ENABLED + +//================================================================================================= +// CLIENT INFO Constructor +//================================================================================================= +ClientInfo::ClientInfo() +: mpSocketPending(nullptr) +, mpSocketComs(nullptr) +, mpSocketListen(nullptr) +, mFontTextureID(TextureCastFromUInt(uint64_t(0u))) +, mTexturesPendingSent(0) +, mTexturesPendingCreated(0) +, mbClientThreadActive(false) +, mbListenThreadActive(false) +, mbComInitActive(false) +{ + memset(mTexturesPending, 0, sizeof(mTexturesPending)); +} + +//================================================================================================= +// CLIENT INFO Constructor +//================================================================================================= +ClientInfo::~ClientInfo() +{ + ContextRemoveHooks(); + + for( auto& texture : mTextures ){ + texture.Set(nullptr); + } + + for(size_t i(0); iFonts.size() > 0) + { + float noScaleSize = ImGui::GetIO().Fonts->Fonts[0]->FontSize / mFontCreationScaling; + float originalScale = mSavedContextValues.mFontGeneratedSize / noScaleSize; + mFontCreationFunction(mFontCreationScaling, originalScale); + } + mSavedContextValues.Restore(mpContext); + } +} + +//================================================================================================= +// Remove callback hooks, once we detect a disconnection +//================================================================================================= +void ClientInfo::ContextRemoveHooks() +{ +#if NETIMGUI_IMGUI_CALLBACK_ENABLED + if (mpContext && mhImguiHookNewframe != 0) + { + ImGui::RemoveContextHook(mpContext, mhImguiHookNewframe); + ImGui::RemoveContextHook(mpContext, mhImguiHookEndframe); + mhImguiHookNewframe = mhImguiHookNewframe = 0; + } +#endif +} + +//================================================================================================= +// Process textures waiting to be sent to server +// 1. New textures are added tp pending queue (Main Thread) +// 2. Pending textures are sent to Server and added to our active texture list (Com Thread) +//================================================================================================= +void ClientInfo::ProcessTexturePending() +{ + while( mTexturesPendingCreated != mTexturesPendingSent ) + { + mbHasTextureUpdate |= true; + uint32_t idx = mTexturesPendingSent.fetch_add(1) % static_cast(ArrayCount(mTexturesPending)); + CmdTexture* pCmdTexture = mTexturesPending[idx]; + mTexturesPending[idx] = nullptr; + if( pCmdTexture ) + { + // Find the TextureId from our list (or free slot) + int texIdx = 0; + int texFreeSlot = static_cast(mTextures.size()); + while( texIdx < mTextures.size() && ( !mTextures[texIdx].IsValid() || mTextures[texIdx].mpCmdTexture->mTextureId != pCmdTexture->mTextureId) ) + { + texFreeSlot = !mTextures[texIdx].IsValid() ? texIdx : texFreeSlot; + ++texIdx; + } + + if( texIdx == mTextures.size() ) + texIdx = texFreeSlot; + if( texIdx == mTextures.size() ) + mTextures.push_back(ClientTexture()); + + mTextures[texIdx].Set( pCmdTexture ); + mTextures[texIdx].mbSent = false; + } + } +} + +//================================================================================================= +// Create a new Draw Command from Dear Imgui draw data. +// 1. New ImGui frame has been completed, create a new draw command from draw data (Main Thread) +// 2. We see a pending Draw Command, take ownership of it and send it to Server (Com thread) +//================================================================================================= +void ClientInfo::ProcessDrawData(const ImDrawData* pDearImguiData, ImGuiMouseCursor mouseCursor) +{ + if( !mbValidDrawFrame ) + return; + + CmdDrawFrame* pDrawFrameNew = ConvertToCmdDrawFrame(pDearImguiData, mouseCursor); + pDrawFrameNew->mCompressed = mClientCompressionMode == eCompressionMode::kForceEnable || (mClientCompressionMode == eCompressionMode::kUseServerSetting && mServerCompressionEnabled); + mPendingFrameOut.Assign(pDrawFrameNew); +} + +}}} // namespace NetImgui::Internal::Client + +#include "NetImgui_WarningReenable.h" +#endif //#if NETIMGUI_ENABLED diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.h new file mode 100644 index 0000000..a5414e7 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.h @@ -0,0 +1,161 @@ +#pragma once + +#include "NetImgui_Shared.h" +#include "NetImgui_CmdPackets.h" + +//============================================================================= +// Forward Declares +//============================================================================= +namespace NetImgui { namespace Internal { namespace Network { struct SocketInfo; } } } + +namespace NetImgui { namespace Internal { namespace Client +{ + +//============================================================================= +// Keep a list of textures used by Imgui, needed by server +//============================================================================= +struct ClientTexture +{ + inline void Set( CmdTexture* pCmdTexture ); + inline bool IsValid()const; + CmdTexture* mpCmdTexture= nullptr; + bool mbSent = false; + uint8_t mPadding[7] = {}; +}; + +//============================================================================= +// Keeps a list of ImGui context values NetImgui overrides (to restore) +//============================================================================= +struct SavedImguiContext +{ + void Save(ImGuiContext* copyFrom); + void Restore(ImGuiContext* copyTo); + const char* mBackendPlatformName = nullptr; + const char* mBackendRendererName = nullptr; + void* mImeWindowHandle = nullptr; + float mFontGlobalScale = 1.f; + float mFontGeneratedSize = 0.f; + ImGuiBackendFlags mBackendFlags = 0; + ImGuiConfigFlags mConfigFlags = 0; + bool mDrawMouse = false; + bool mSavedContext = false; + char mPadding1[2] = {}; + void* mClipboardUserData = nullptr; +#if IMGUI_VERSION_NUM < 19110 + const char* (*mGetClipboardTextFn)(void*) = nullptr; + void (*mSetClipboardTextFn)(void*, const char*) = nullptr; +#else + const char* (*mGetClipboardTextFn)(ImGuiContext*) = nullptr; + void (*mSetClipboardTextFn)(ImGuiContext*, const char*) = nullptr; +#endif +#if IMGUI_VERSION_NUM < 18700 + int mKeyMap[ImGuiKey_COUNT] = {}; + char mPadding2[8 - (sizeof(mKeyMap) % 8)] = {}; +#endif +}; + +//============================================================================= +// Keep all Client infos needed for communication with server +//============================================================================= +struct ClientInfo +{ + using VecTexture = ImVector; + using BufferKeys = Ringbuffer; + using TimePoint = std::chrono::time_point; + + struct InputState + { + uint64_t mInputDownMask[(CmdInput::ImGuiKey_COUNT+63)/64] = {}; + float mInputAnalog[CmdInput::kAnalog_Count] = {}; + uint64_t mMouseDownMask = 0; + float mMouseWheelVertPrev = 0.f; + float mMouseWheelHorizPrev = 0.f; + }; + ClientInfo(); + ~ClientInfo(); + void ContextInitialize(); + void ContextOverride(); + void ContextRestore(); + void ContextRemoveHooks(); + inline bool IsContextOverriden()const; + + std::atomic mpSocketPending; // Hold socket info until communication is established + std::atomic mpSocketComs; // Socket used for communications with server + std::atomic mpSocketListen; // Socket used to wait for communication request from server + std::atomic_bool mbDisconnectPending; // Terminate Client/Server coms + std::atomic_bool mbDisconnectListen; // Terminate waiting connection from Server + uint32_t mSocketListenPort = 0; // Socket Port number used to wait for communication request from server + VecTexture mTextures; // List if textures created by this client (used un main thread) + char mName[64] = {}; + uint64_t mFrameIndex = 0; // Incremented everytime we send a DrawFrame Command + CmdTexture* mTexturesPending[16] = {}; + ExchangePtr mPendingFrameOut; + ExchangePtr mPendingBackgroundOut; + ExchangePtr mPendingInputIn; + ExchangePtr mPendingClipboardIn; // Clipboard content received from Server and waiting to be taken by client + ExchangePtr mPendingClipboardOut; // Clipboard content copied on Client and waiting to be sent to Server + ImGuiContext* mpContext = nullptr; // Context that the remote drawing should use (either the one active when connection request happened, or a clone) + PendingCom mPendingRcv; // Data being currently received from Server + PendingCom mPendingSend; // Data being currently sent to Server + uint32_t mPendingSendNext = 0; // Type of Cmd to next attempt sending, when channel is available + CmdPendingRead mCmdPendingRead; // Used to get info on the next incoming command from Server + CmdInput* mpCmdInputPending = nullptr; // Last Input Command from server, waiting to be processed by client + CmdClipboard* mpCmdClipboard = nullptr; // Last received clipboad command + CmdDrawFrame* mpCmdDrawLast = nullptr; // Last sent Draw Command. Used by data compression, to generate delta between previous and current frame + CmdBackground mBGSetting; // Current value assigned to background appearance by user + CmdBackground mBGSettingSent; // Last sent value to remote server + BufferKeys mPendingKeyIn; // Keys pressed received. Results of 2 CmdInputs are concatenated if received before being processed + TimePoint mLastOutgoingDrawCheckTime; // When we last checked if we have a pending draw command to send + TimePoint mLastOutgoingDrawTime; // When we last sent an updated draw command to the server + ImVec2 mSavedDisplaySize = {0, 0}; // Save original display size on 'NewFrame' and restore it on 'EndFrame' (making sure size is still valid after a disconnect) + const void* mpFontTextureData = nullptr; // Last font texture data send to server (used to detect if font was changed) + ImTextureID mFontTextureID; + SavedImguiContext mSavedContextValues; + std::atomic_uint32_t mTexturesPendingSent; + std::atomic_uint32_t mTexturesPendingCreated; + + std::atomic_bool mbClientThreadActive; // True when connected and communicating with Server + std::atomic_bool mbListenThreadActive; // True when listening from connection request from Server + std::atomic_bool mbComInitActive; // True when attempting to initialize a new connection + bool mbHasTextureUpdate = false; + bool mbIsDrawing = false; // We are inside a 'NetImgui::NewFrame' / 'NetImgui::EndFrame' (even if not for a remote draw) + bool mbIsRemoteDrawing = false; // True if the rendering it meant for the remote netImgui server + bool mbRestorePending = false; // Original context has had some settings overridden, original values stored in mRestoreXXX + bool mbFontUploaded = false; // Auto detect if font was sent to server + bool mbInsideHook = false; // Currently inside ImGui hook callback + bool mbInsideNewEnd = false; // Currently inside NetImgui::NewFrame() or NetImgui::EndFrame() (prevents recusrive hook call) + bool mbValidDrawFrame = false; // If we should forward the drawdata to the server at the end of ImGui::Render() + uint8_t mClientCompressionMode = eCompressionMode::kUseServerSetting; + bool mServerCompressionEnabled = false; // If Server would like compression to be enabled (mClientCompressionMode value can override this value) + bool mServerCompressionSkip = false; // Force ignore compression setting for 1 frame + bool mServerForceConnectEnabled = true; // If another NetImguiServer can take connection away from the one currently active + ThreadFunctPtr mThreadFunction = nullptr; // Function to use when laucnhing new threads + FontCreateFuncPtr mFontCreationFunction = nullptr; // Method to call to generate the remote ImGui font. By default, re-use the local font, but this doesn't handle native DPI scaling on remote server + float mFontCreationScaling = 1.f; // Last font scaling used when generating the NetImgui font + float mDesiredFps = 30.f; // How often we should update the remote drawing. Received from server + InputState mPreviousInputState; // Keeping track of last keyboard/mouse state + ImGuiID mhImguiHookNewframe = 0; + ImGuiID mhImguiHookEndframe = 0; + + void ProcessDrawData(const ImDrawData* pDearImguiData, ImGuiMouseCursor mouseCursor); + void ProcessTexturePending(); + inline bool IsConnected()const; + inline bool IsConnectPending()const; + inline bool IsActive()const; + +// Prevent warnings about implicitly created copy +protected: + ClientInfo(const ClientInfo&)=delete; + ClientInfo(const ClientInfo&&)=delete; + void operator=(const ClientInfo&)=delete; +}; + +//============================================================================= +// Main communication loop threads that are run in separate threads +//============================================================================= +void CommunicationsConnect(void* pClientVoid); +void CommunicationsHost(void* pClientVoid); + +}}} //namespace NetImgui::Internal::Client + +#include "NetImgui_Client.inl" diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.inl b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.inl new file mode 100644 index 0000000..c8daecb --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Client.inl @@ -0,0 +1,38 @@ + +#include "NetImgui_Network.h" + +namespace NetImgui { namespace Internal { namespace Client { + +void ClientTexture::Set( CmdTexture* pCmdTexture ) +{ + netImguiDeleteSafe(mpCmdTexture); + mpCmdTexture = pCmdTexture; + mbSent = pCmdTexture == nullptr; +} + +bool ClientTexture::IsValid()const +{ + return mpCmdTexture != nullptr; +} + +bool ClientInfo::IsConnected()const +{ + return mpSocketComs.load() != nullptr; +} + +bool ClientInfo::IsConnectPending()const +{ + return mbComInitActive || mpSocketPending.load() != nullptr || mpSocketListen.load() != nullptr; +} + +bool ClientInfo::IsActive()const +{ + return mbClientThreadActive || mbListenThreadActive; +} + +bool ClientInfo::IsContextOverriden()const +{ + return mSavedContextValues.mSavedContext; +} + +}}} // namespace NetImgui::Internal::Client diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets.h new file mode 100644 index 0000000..1c8d5c1 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets.h @@ -0,0 +1,276 @@ +#pragma once + +#include "NetImgui_Shared.h" +#include "NetImgui_CmdPackets_DrawFrame.h" + +namespace NetImgui { namespace Internal +{ + +//Note: If updating any of these commands data structure, increase 'CmdVersion::eVersion' +struct alignas(8) CmdHeader +{ + enum class eCommands : uint8_t { Version, Texture, Input, DrawFrame, Background, Clipboard, Count }; + CmdHeader(eCommands CmdType, uint16_t Size) : mSize(Size), mType(CmdType){} + uint32_t mSize = 0; + eCommands mType = eCommands::Count; + uint8_t mPadding[3] = {}; +}; + +// Used as step 1 of 2 of reading incoming transmission between Client/Server, to get header whose size we know +struct alignas(8) CmdPendingRead : public CmdHeader +{ + CmdPendingRead() : CmdHeader(eCommands::Count, sizeof(CmdPendingRead) ){} +}; + +struct alignas(8) CmdVersion : public CmdHeader +{ + enum class eVersion : uint32_t + { + Initial = 1, + NewTextureFormat = 2, + ImguiVersionInfo = 3, // Added Dear Imgui/ NetImgui version info to 'CmdVersion' + ServerRefactor = 4, // Change to 'CmdInput' and 'CmdVersion' store size of 'ImWchar' to make sure they are compatible + BackgroundCmd = 5, // Added new command to control background appearance + ClientName = 6, // Increase maximum allowed client name that a program can set + DataCompression = 7, // Adding support for data compression between client/server. Simple low cost delta compressor (only send difference from previous frame) + DataCompression2 = 8, // Improvement to data compression (save corner position and use SoA for vertices data) + VertexUVRange = 9, // Changed vertices UV value range to [0,1] for increased precision on large font texture + Imgui_1_87 = 10, // Added Dear ImGui Input refactor + OffetPointer = 11, // Updated the handling of OffsetPoint. Moved flag bit from last bit to first bit. Addresses and data are always at least 4 bytes aligned, so should never conflict with potential address space + CustomTexture = 12, // Added a 'custom' texture format to let user potentially handle their how format + DPIScale = 13, // Server now handle monitor DPI + Clipboard = 14, // Added clipboard support between server/client + ForceReconnect = 15, // Server can now take over the connection from another server + UpdatedComs = 16, // Faster protocol by removing blocking coms + RemDisconnect = 17, // Removed Disconnect command + // Insert new version here + + //-------------------------------- + _count, + _current = _count -1 + }; + enum class eFlags : uint8_t + { + IsUnavailable = 0x01, // Client telling Server it cannot be used + IsConnected = 0x02, // Client telling Server there's already a valid connection (can potentially be taken over if !IsUnavailable) + ConnectForce = 0x04, // Server telling Client it want to take over connection if there's already one + ConnectExclusive = 0x08, // Server telling Client that once connected, others servers should be denied access + }; + CmdVersion() : CmdHeader(CmdHeader::eCommands::Version, sizeof(CmdVersion)){} + char mClientName[64] = {}; + char mImguiVerName[16] = {IMGUI_VERSION}; + char mNetImguiVerName[16] = {NETIMGUI_VERSION}; + eVersion mVersion = eVersion::_current; + uint32_t mImguiVerID = IMGUI_VERSION_NUM; + uint32_t mNetImguiVerID = NETIMGUI_VERSION_NUM; + uint8_t mWCharSize = static_cast(sizeof(ImWchar)); + uint8_t mFlags = 0; + char PADDING[2]; +}; + +struct alignas(8) CmdInput : public CmdHeader +{ + // Identify a mouse button. + // Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience. + enum NetImguiMouseButton + { + ImGuiMouseButton_Left = 0, + ImGuiMouseButton_Right = 1, + ImGuiMouseButton_Middle = 2, + ImGuiMouseButton_Extra1 = 3, // Additional entry + ImGuiMouseButton_Extra2 = 4, // Additional entry + ImGuiMouseButton_COUNT = 5 + }; + + // Copy of Dear ImGui key enum + // We keep our own internal version, to make sure Client key is the same as Server Key (since they can have different Imgui version) + enum NetImguiKeys + { + // Keyboard + ImGuiKey_Tab, + ImGuiKey_LeftArrow, + ImGuiKey_RightArrow, + ImGuiKey_UpArrow, + ImGuiKey_DownArrow, + ImGuiKey_PageUp, + ImGuiKey_PageDown, + ImGuiKey_Home, + ImGuiKey_End, + ImGuiKey_Insert, + ImGuiKey_Delete, + ImGuiKey_Backspace, + ImGuiKey_Space, + ImGuiKey_Enter, + ImGuiKey_Escape, + ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper, + ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper, + ImGuiKey_Menu, + ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9, + ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J, + ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T, + ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z, + ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6, + ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12, + ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18, + ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24, + ImGuiKey_Apostrophe, // ' + ImGuiKey_Comma, // , + ImGuiKey_Minus, // - + ImGuiKey_Period, // . + ImGuiKey_Slash, // / + ImGuiKey_Semicolon, // ; + ImGuiKey_Equal, // = + ImGuiKey_LeftBracket, // [ + ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backslash) + ImGuiKey_RightBracket, // ] + ImGuiKey_GraveAccent, // ` + ImGuiKey_CapsLock, + ImGuiKey_ScrollLock, + ImGuiKey_NumLock, + ImGuiKey_PrintScreen, + ImGuiKey_Pause, + ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4, + ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9, + ImGuiKey_KeypadDecimal, + ImGuiKey_KeypadDivide, ImGuiKey_KeypadMultiply, ImGuiKey_KeypadSubtract, + ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, + ImGuiKey_AppBack, // Available on some keyboard/mouses. Often referred as "Browser Back" + ImGuiKey_AppForward, + + // Gamepad (some of those are analog values, 0.0f to 1.0f) // GAME NAVIGATION ACTION + // (download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets) + ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) + ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) + ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // Tap: Toggle Menu. Hold: Windowing mode (Focus/Move/Resize windows) + ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // Cancel / Close / Exit + ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // Text Input / On-screen Keyboard + ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // Activate / Open / Toggle / Tweak + ImGuiKey_GamepadDpadLeft, // D-pad Left // Move / Tweak / Resize Window (in Windowing mode) + ImGuiKey_GamepadDpadRight, // D-pad Right // Move / Tweak / Resize Window (in Windowing mode) + ImGuiKey_GamepadDpadUp, // D-pad Up // Move / Tweak / Resize Window (in Windowing mode) + ImGuiKey_GamepadDpadDown, // D-pad Down // Move / Tweak / Resize Window (in Windowing mode) + ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // Tweak Slower / Focus Previous (in Windowing mode) + ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // Tweak Faster / Focus Next (in Windowing mode) + ImGuiKey_GamepadL2, // L Trig. (Xbox) ZL (Switch) L2 (PS) [Analog] + ImGuiKey_GamepadR2, // R Trig. (Xbox) ZR (Switch) R2 (PS) [Analog] + ImGuiKey_GamepadL3, // L Stick (Xbox) L3 (Switch) L3 (PS) + ImGuiKey_GamepadR3, // R Stick (Xbox) R3 (Switch) R3 (PS) + + ImGuiKey_GamepadLStickLeft, // [Analog] // Move Window (in Windowing mode) + ImGuiKey_GamepadLStickRight, // [Analog] // Move Window (in Windowing mode) + ImGuiKey_GamepadLStickUp, // [Analog] // Move Window (in Windowing mode) + ImGuiKey_GamepadLStickDown, // [Analog] // Move Window (in Windowing mode) + ImGuiKey_GamepadRStickLeft, // [Analog] + ImGuiKey_GamepadRStickRight, // [Analog] + ImGuiKey_GamepadRStickUp, // [Analog] + ImGuiKey_GamepadRStickDown, // [Analog] + + // Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls) + // - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API. + ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY, + + // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls) + // - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing + // them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc. + // - Code polling every keys (e.g. an interface to detect a key press for input mapping) might want to ignore those + // and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiKey_ModCtrl). + // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys. + // In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and + // backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user... + ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper, + + // End of list + ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value + }; + + + static constexpr uint32_t kAnalog_First = ImGuiKey_GamepadLStickLeft; + static constexpr uint32_t kAnalog_Last = ImGuiKey_GamepadRStickDown; + static constexpr uint32_t kAnalog_Count = kAnalog_Last - kAnalog_First + 1; + + CmdInput() : CmdHeader(CmdHeader::eCommands::Input, sizeof(CmdInput)){} + uint16_t mScreenSize[2] = {}; + int16_t mMousePos[2] = {}; + float mMouseWheelVert = 0.f; + float mMouseWheelHoriz = 0.f; + uint16_t mKeyChars[256] = {}; // Input characters + uint16_t mKeyCharCount = 0; // Number of valid input characters + bool mCompressionUse = false; // Server would like client to compress the communication data + bool mCompressionSkip = false; // Server forcing next client's frame data to be uncompressed + float mFontDPIScaling = 1.f; // Font scaling request by Server accounting for monitor DPI + float mDesiredFps = 30.f; // Requested redraw speed + uint64_t mMouseDownMask = 0; + uint64_t mInputDownMask[(ImGuiKey_COUNT+63)/64]={}; + float mInputAnalog[kAnalog_Count] = {}; + inline bool IsKeyDown(NetImguiKeys netimguiKey) const; +}; + +struct alignas(8) CmdTexture : public CmdHeader +{ + CmdTexture() : CmdHeader(CmdHeader::eCommands::Texture, sizeof(CmdTexture)){} + OffsetPointer mpTextureData; + uint64_t mTextureId = 0; + uint16_t mWidth = 0; + uint16_t mHeight = 0; + uint8_t mFormat = eTexFormat::kTexFmt_Invalid; // eTexFormat + uint8_t PADDING[3] = {}; +}; + +struct alignas(8) CmdDrawFrame : public CmdHeader +{ + CmdDrawFrame() : CmdHeader(CmdHeader::eCommands::DrawFrame, sizeof(CmdDrawFrame)){} + uint64_t mFrameIndex = 0; + uint32_t mMouseCursor = 0; // ImGuiMouseCursor value + float mDisplayArea[4] = {}; + uint32_t mIndiceByteSize = 0; + uint32_t mDrawGroupCount = 0; + uint32_t mTotalVerticeCount = 0; + uint32_t mTotalIndiceCount = 0; + uint32_t mTotalDrawCount = 0; + uint32_t mUncompressedSize = 0; + uint8_t mCompressed = false; + uint8_t PADDING[3] = {}; + OffsetPointer mpDrawGroups; + inline void ToPointers(); + inline void ToOffsets(); +}; + +struct alignas(8) CmdBackground : public CmdHeader +{ + CmdBackground() : CmdHeader(CmdHeader::eCommands::Background, sizeof(CmdBackground)){} + static constexpr uint64_t kDefaultTexture = ~0u; + float mClearColor[4] = {0.2f, 0.2f, 0.2f, 1.f}; // Background color + float mTextureTint[4] = {1.f, 1.f, 1.f, 0.5f}; // Tint/alpha applied to texture + uint64_t mTextureId = kDefaultTexture; // Texture rendered in background, use server texture by default + inline bool operator==(const CmdBackground& cmp)const; + inline bool operator!=(const CmdBackground& cmp)const; +}; + +struct alignas(8) CmdClipboard : public CmdHeader +{ + CmdClipboard() : CmdHeader(CmdHeader::eCommands::Clipboard, sizeof(CmdClipboard)){} + size_t mByteSize = 0; + OffsetPointer mContentUTF8; + inline void ToPointers(); + inline void ToOffsets(); + inline static CmdClipboard* Create(const char* clipboard); +}; + +//============================================================================= +// Keeping track of partial incoming/outgoing transmissions +//============================================================================= +struct PendingCom +{ + size_t SizeCurrent = 0; // Amount of data sent or received so far + bool bAutoFree = false; // Need to free data buffer at the end of processing + bool bError = false; // If an error occurs during coms + CmdHeader* pCommand = nullptr; // Where to store incoming data or read to send data + inline bool IsError()const{ return bError; } + inline bool IsDone()const { return IsError() || (pCommand && pCommand->mSize == SizeCurrent); } + inline bool IsReady()const{ return !IsError() && pCommand == nullptr; } + inline bool IsPending()const{ return !IsError() && !IsDone() && !IsReady(); } +}; + +}} // namespace NetImgui::Internal + +#include "NetImgui_CmdPackets.inl" diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets.inl b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets.inl new file mode 100644 index 0000000..e03b622 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets.inl @@ -0,0 +1,101 @@ +#include "NetImgui_CmdPackets.h" + +namespace NetImgui { namespace Internal +{ + +void CmdDrawFrame::ToPointers() +{ + if( !mpDrawGroups.IsPointer() ) + { + mpDrawGroups.ToPointer(); + for (uint32_t i(0); i < mDrawGroupCount; ++i) { + mpDrawGroups[i].ToPointers(); + } + } +} + +void CmdDrawFrame::ToOffsets() +{ + if( !mpDrawGroups.IsOffset() ) + { + for (uint32_t i(0); i < mDrawGroupCount; ++i) { + mpDrawGroups[i].ToOffsets(); + } + mpDrawGroups.ToOffset(); + } +} + +void ImguiDrawGroup::ToPointers() +{ + if( !mpIndices.IsPointer() ) //Safer to test the first element after CmdHeader + { + mpIndices.ToPointer(); + mpVertices.ToPointer(); + mpDraws.ToPointer(); + } +} + +void ImguiDrawGroup::ToOffsets() +{ + if( !mpIndices.IsOffset() ) //Safer to test the first element after CmdHeader + { + mpIndices.ToOffset(); + mpVertices.ToOffset(); + mpDraws.ToOffset(); + } +} + +bool CmdInput::IsKeyDown( CmdInput::NetImguiKeys netimguiKey) const +{ + uint32_t valIndex = netimguiKey/64; + uint64_t valMask = 0x0000000000000001ull << (netimguiKey%64); + return mInputDownMask[valIndex] & valMask; +} + +bool CmdBackground::operator==(const CmdBackground& cmp)const +{ + bool sameValue(true); + for(size_t i(0); i(this)[i] == reinterpret_cast(&cmp)[i]; + } + return sameValue; +} + +bool CmdBackground::operator!=(const CmdBackground& cmp)const +{ + return (*this == cmp) == false; +} + + +void CmdClipboard::ToPointers() +{ + if( !mContentUTF8.IsPointer() ){ + mContentUTF8.ToPointer(); + } +} + +void CmdClipboard::ToOffsets() +{ + if( !mContentUTF8.IsOffset() ){ + mContentUTF8.ToOffset(); + } +} + +CmdClipboard* CmdClipboard::Create(const char* clipboard) +{ + if( clipboard ) + { + size_t clipboardByteSize(0); + while(clipboard[clipboardByteSize++] != 0); + size_t totalDataCount = sizeof(CmdClipboard) + DivUp(clipboardByteSize, ComDataSize); + auto pNewClipboard = NetImgui::Internal::netImguiSizedNew(totalDataCount*ComDataSize); + pNewClipboard->mSize = static_cast(totalDataCount*ComDataSize); + pNewClipboard->mByteSize = clipboardByteSize; + pNewClipboard->mContentUTF8.SetPtr(reinterpret_cast(&pNewClipboard[1])); + memcpy(pNewClipboard->mContentUTF8.Get(), clipboard, clipboardByteSize); + return pNewClipboard; + } + return nullptr; +} + +}} // namespace NetImgui::Internal diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets_DrawFrame.cpp b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets_DrawFrame.cpp new file mode 100644 index 0000000..e0f1173 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets_DrawFrame.cpp @@ -0,0 +1,381 @@ +#include "NetImgui_Shared.h" + +#if NETIMGUI_ENABLED +#include "NetImgui_WarningDisable.h" +#include "NetImgui_CmdPackets.h" + +namespace NetImgui { namespace Internal +{ + +template +inline void SetAndIncreaseDataPointer(OffsetPointer& dataPointer, uint32_t dataSize, ComDataType*& pDataOutput) +{ + dataPointer.SetComDataPtr(pDataOutput); + const size_t dataCount = DivUp(dataSize, ComDataSize); + pDataOutput[dataCount-1] = 0; + pDataOutput += dataCount; +} + +//============================================================================= +// Safely convert a pointer to a int value, even if int storage size > pointer +//============================================================================= +template +TInt PointerCast(TPointer* pointer) +{ + union CastHelperUnion + { + TInt ValueInt; + TPointer* ValuePointer; + }; + CastHelperUnion helperObject = {}; + helperObject.ValuePointer = pointer; + return helperObject.ValueInt; +} + +//================================================================================================= +// +//================================================================================================= +inline void ImGui_ExtractIndices(const ImDrawList& cmdList, ImguiDrawGroup& drawGroupOut, ComDataType*& pDataOutput) +{ + bool is16Bit = sizeof(ImDrawIdx) == 2 || cmdList.VtxBuffer.size() <= 0xFFFF; // When Dear Imgui is compiled with ImDrawIdx = uint16, we know for certain that there won't be any drawcall with index > 65k, even if Vertex buffer is bigger than 65k. + drawGroupOut.mBytePerIndex = is16Bit ? 2 : 4; + drawGroupOut.mIndiceCount = static_cast(cmdList.IdxBuffer.size()); + uint32_t sizeNeeded = drawGroupOut.mIndiceCount*drawGroupOut.mBytePerIndex; + SetAndIncreaseDataPointer(drawGroupOut.mpIndices, sizeNeeded, pDataOutput); + + // No conversion needed, straight copy + if( drawGroupOut.mBytePerIndex == sizeof(ImDrawIdx) ) + { + memcpy(drawGroupOut.mpIndices.Get(), &cmdList.IdxBuffer.front(), sizeNeeded); + } + // From 32bits to 16bits + else if(is16Bit) + { + for(int i(0); i < static_cast(drawGroupOut.mIndiceCount); ++i) + reinterpret_cast(drawGroupOut.mpIndices.Get())[i] = static_cast(cmdList.IdxBuffer[i]); + } + // From 16bits to 32bits + else + { + for(int i(0); i < static_cast(drawGroupOut.mIndiceCount); ++i) + reinterpret_cast(drawGroupOut.mpIndices.Get())[i] = static_cast(cmdList.IdxBuffer[i]); + } +} + +//================================================================================================= +// +//================================================================================================= +inline void ImGui_ExtractVertices(const ImDrawList& cmdList, ImguiDrawGroup& drawGroupOut, ComDataType*& pDataOutput) +{ + drawGroupOut.mVerticeCount = static_cast(cmdList.VtxBuffer.size()); + drawGroupOut.mReferenceCoord[0] = drawGroupOut.mVerticeCount > 0 ? cmdList.VtxBuffer[0].pos.x : 0.f; + drawGroupOut.mReferenceCoord[1] = drawGroupOut.mVerticeCount > 0 ? cmdList.VtxBuffer[0].pos.y : 0.f; + SetAndIncreaseDataPointer(drawGroupOut.mpVertices, drawGroupOut.mVerticeCount*sizeof(ImguiVert), pDataOutput); + ImguiVert* pVertices = drawGroupOut.mpVertices.Get(); + for(int i(0); i(drawGroupOut.mVerticeCount); ++i) + { + const auto& Vtx = cmdList.VtxBuffer[i]; + pVertices[i].mColor = Vtx.col; + pVertices[i].mUV[0] = static_cast((Vtx.uv.x - static_cast(ImguiVert::kUvRange_Min) + 0.5f/65535.f) * 0xFFFF / (ImguiVert::kUvRange_Max - ImguiVert::kUvRange_Min)); + pVertices[i].mUV[1] = static_cast((Vtx.uv.y - static_cast(ImguiVert::kUvRange_Min) + 0.5f/65535.f) * 0xFFFF / (ImguiVert::kUvRange_Max - ImguiVert::kUvRange_Min)); + pVertices[i].mPos[0] = static_cast((Vtx.pos.x - drawGroupOut.mReferenceCoord[0] - static_cast(ImguiVert::kPosRange_Min)) * 0xFFFF / (ImguiVert::kPosRange_Max - ImguiVert::kPosRange_Min)); + pVertices[i].mPos[1] = static_cast((Vtx.pos.y - drawGroupOut.mReferenceCoord[1] - static_cast(ImguiVert::kPosRange_Min)) * 0xFFFF / (ImguiVert::kPosRange_Max - ImguiVert::kPosRange_Min)); + } +} + +//================================================================================================= +// +//================================================================================================= +inline void ImGui_ExtractDraws(const ImDrawList& cmdList, ImguiDrawGroup& drawGroupOut, ComDataType*& pDataOutput) +{ + int maxDrawCount = static_cast(cmdList.CmdBuffer.size()); + uint32_t drawCount = 0; + ImguiDraw* pOutDraws = reinterpret_cast(pDataOutput); + for(int cmd_i = 0; cmd_i < maxDrawCount; ++cmd_i) + { + const ImDrawCmd* pCmd = &cmdList.CmdBuffer[cmd_i]; + if( pCmd->UserCallback == nullptr ) + { + #if IMGUI_VERSION_NUM >= 17100 + pOutDraws[drawCount].mVtxOffset = pCmd->VtxOffset; + pOutDraws[drawCount].mIdxOffset = pCmd->IdxOffset; + #else + pOutDraws[drawCount].mVtxOffset = 0; + pOutDraws[drawCount].mIdxOffset = 0; + #endif + + pOutDraws[drawCount].mTextureId = TextureCastFromID(pCmd->TextureId); + pOutDraws[drawCount].mIdxCount = pCmd->ElemCount; + pOutDraws[drawCount].mClipRect[0] = pCmd->ClipRect.x; + pOutDraws[drawCount].mClipRect[1] = pCmd->ClipRect.y; + pOutDraws[drawCount].mClipRect[2] = pCmd->ClipRect.z; + pOutDraws[drawCount].mClipRect[3] = pCmd->ClipRect.w; + ++drawCount; + } + } + drawGroupOut.mDrawCount = drawCount; + static_assert(sizeof(ImguiDraw) % ComDataSize == 0, "Need to support zero-ing the pending bytes, when not a size multiple of DataComType"); + drawGroupOut.mpDraws.SetComDataPtr(pDataOutput); + pDataOutput += drawGroupOut.mDrawCount * sizeof(ImguiDraw) / ComDataSize; +} + +//================================================================================================= +// Delta comress data. +// Take a data stream and output a version with only the difference from other stream is written +//================================================================================================= +void CompressData(const ComDataType* pDataPrev, size_t dataSizePrev, const ComDataType* pDataNew, size_t dataSizeNew, ComDataType*& pCommandMemoryInOut) +{ + static_assert(sizeof(uint32_t)*2 <= ComDataSize, "Need to adjust compression algorithm pointer calculation"); + const size_t elemCountPrev = static_cast(DivUp(dataSizePrev, sizeof(uint64_t))); + const size_t elemCountNew = static_cast(DivUp(dataSizeNew, sizeof(uint64_t))); + const size_t elemCount = elemCountPrev < elemCountNew ? elemCountPrev : elemCountNew; + size_t n = 0; + + if( pDataPrev ) + { + while(n < elemCount) + { + uint32_t* pBlockInfo = reinterpret_cast(pCommandMemoryInOut++); // Add a new block info to output + + // Find number of elements with same value as last frame + size_t startN = n; + while( n < elemCount && pDataPrev[n] == pDataNew[n] ) + ++n; + pBlockInfo[0] = static_cast(n - startN); + + // Find number of elements with different value as last frame, and save new value + while (n < elemCount && pDataPrev[n] != pDataNew[n]) { + *pCommandMemoryInOut = pDataNew[n++]; + ++pCommandMemoryInOut; + } + pBlockInfo[1] = static_cast(pCommandMemoryInOut - reinterpret_cast(pBlockInfo)) - 1; + } + } + + // New frame has more element than previous frame, add the remaining entries + if(elemCount < elemCountNew) + { + uint32_t* pBlockInfo = reinterpret_cast(pCommandMemoryInOut++); // Add a new block info to output + while (n < elemCountNew) { + *pCommandMemoryInOut = pDataNew[n++]; + ++pCommandMemoryInOut; + } + pBlockInfo[0] = 0; + pBlockInfo[1] = static_cast(pCommandMemoryInOut - reinterpret_cast(pBlockInfo)) - 1; + } +} + +//================================================================================================= +// Unpack a delta data compressed stream +//================================================================================================= +void DecompressData(const ComDataType* pDataPrev, size_t dataSizePrev, const ComDataType* pDataPack, size_t dataUnpackSize, ComDataType*& pCommandMemoryInOut) +{ + const size_t elemCountPrev = DivUp(dataSizePrev, ComDataSize); + const size_t elemCountUnpack = DivUp(dataUnpackSize, ComDataSize); + const size_t elemCountCopy = elemCountPrev < elemCountUnpack ? elemCountPrev : elemCountUnpack; + uint64_t* pCommandMemoryEnd = &pCommandMemoryInOut[elemCountUnpack]; + if( pDataPrev ){ + memcpy(pCommandMemoryInOut, pDataPrev, elemCountCopy * ComDataSize); + } + while(pCommandMemoryInOut < pCommandMemoryEnd) + { + const uint32_t* pBlockInfo = reinterpret_cast(pDataPack++); // Add a new block info to output + pCommandMemoryInOut += pBlockInfo[0]; + memcpy(pCommandMemoryInOut, pDataPack, pBlockInfo[1] * sizeof(uint64_t)); + pCommandMemoryInOut += pBlockInfo[1]; + pDataPack += pBlockInfo[1]; + } +} + +//================================================================================================= +// Take a regular NetImgui DrawFrame command and create a new compressed command +// It uses a basic delta compression method that works really well with Imgui data +// - Most of the drawing data do not change between 2 frames +// - Even if 1 window content changes, the others windows probably won't be changing at all +// - This means that for each window, we can only send the data that changed +// - This requires little cpu usage and generate good results +// - In 'SampleBasic' with 3 windows open (Main Window, ImGui Demo, ImGui Metric) at 30fps +// - Compression Off: 1650KB/sec of transfert +// - Compression On : 12KB/sec of transfert (130x less data) +//================================================================================================= +CmdDrawFrame* CompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const CmdDrawFrame* pDrawFrameNew) +{ + //----------------------------------------------------------------------------------------- + // Allocate memory for the new compressed command + //----------------------------------------------------------------------------------------- + // Allocate memory for worst case scenario (no compression possible) + // New DrawFrame size + 2 'compression block info' per data stream + size_t neededDataCount = DivUp(pDrawFrameNew->mSize, ComDataSize) + 6*static_cast(pDrawFrameNew->mDrawGroupCount); + CmdDrawFrame* pDrawFramePacked = netImguiSizedNew(neededDataCount*ComDataSize); + *pDrawFramePacked = *pDrawFrameNew; + pDrawFramePacked->mCompressed = true; + + ComDataType* pDataOutput = reinterpret_cast(&pDrawFramePacked[1]); + SetAndIncreaseDataPointer(pDrawFramePacked->mpDrawGroups, pDrawFramePacked->mDrawGroupCount * sizeof(ImguiDrawGroup), pDataOutput); + + //----------------------------------------------------------------------------------------- + // Copy draw data (vertices, indices, drawcall info, ...) + //----------------------------------------------------------------------------------------- + const uint32_t groupCountPrev = pDrawFramePrev->mDrawGroupCount; + for(uint32_t n = 0; n < pDrawFramePacked->mDrawGroupCount; n++) + { + // Look for the same drawgroup in previous frame + // Can usually avoid a search by checking same index in previous frame (drawgroup ordering shouldn't change often) + const ImguiDrawGroup& drawGroupNew = pDrawFrameNew->mpDrawGroups[n]; + ImguiDrawGroup& drawGroup = pDrawFramePacked->mpDrawGroups[n]; + drawGroup = drawGroupNew; + drawGroup.mDrawGroupIdxPrev = (n < groupCountPrev && drawGroup.mGroupID == pDrawFramePrev->mpDrawGroups[n].mGroupID) ? n : ImguiDrawGroup::kInvalidDrawGroup; + for(uint32_t j(0); jmpDrawGroups[j].mGroupID) ? j : ImguiDrawGroup::kInvalidDrawGroup; + } + + // Delta compress the 3 data streams + const uint64_t *pVerticePrev(nullptr), *pIndicePrev(nullptr), *pDrawsPrev(nullptr); + size_t verticeSizePrev(0), indiceSizePrev(0), drawSizePrev(0); + if (drawGroup.mDrawGroupIdxPrev < pDrawFramePrev->mDrawGroupCount) { + const ImguiDrawGroup& drawGroupPrev = pDrawFramePrev->mpDrawGroups[drawGroup.mDrawGroupIdxPrev]; + pVerticePrev = reinterpret_cast(drawGroupPrev.mpVertices.Get()); + pIndicePrev = reinterpret_cast(drawGroupPrev.mpIndices.Get()); + pDrawsPrev = reinterpret_cast(drawGroupPrev.mpDraws.Get()); + verticeSizePrev = drawGroupPrev.mVerticeCount * sizeof(ImguiVert); + indiceSizePrev = drawGroupPrev.mIndiceCount*static_cast(drawGroupPrev.mBytePerIndex); + drawSizePrev = drawGroupPrev.mDrawCount*sizeof(ImguiDraw); + } + + drawGroup.mpIndices.SetComDataPtr(pDataOutput); + CompressData( pIndicePrev, indiceSizePrev, + drawGroupNew.mpIndices.GetComData(), drawGroupNew.mIndiceCount*static_cast(drawGroupNew.mBytePerIndex), + pDataOutput); + + drawGroup.mpVertices.SetComDataPtr(pDataOutput); + CompressData( pVerticePrev, verticeSizePrev, + drawGroupNew.mpVertices.GetComData(), drawGroupNew.mVerticeCount * sizeof(ImguiVert), + pDataOutput); + + drawGroup.mpDraws.SetComDataPtr(pDataOutput); + CompressData( pDrawsPrev, drawSizePrev, + drawGroupNew.mpDraws.GetComData(), drawGroupNew.mDrawCount*sizeof(ImguiDraw), + pDataOutput); + } + + // Adjust data transfert amount to memory that has been actually needed + pDrawFramePacked->mSize = static_cast((pDataOutput - reinterpret_cast(pDrawFramePacked)))*static_cast(sizeof(uint64_t)); + return pDrawFramePacked; +} + +//================================================================================================= +// +//================================================================================================= +CmdDrawFrame* DecompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const CmdDrawFrame* pDrawFramePacked) +{ + //----------------------------------------------------------------------------------------- + // Allocate memory for the new uncompressed compressed command + //----------------------------------------------------------------------------------------- + CmdDrawFrame* pDrawFrameNew = netImguiSizedNew(pDrawFramePacked->mUncompressedSize); + *pDrawFrameNew = *pDrawFramePacked; + pDrawFrameNew->mCompressed = false; + ComDataType* pDataOutput = reinterpret_cast(&pDrawFrameNew[1]); + SetAndIncreaseDataPointer(pDrawFrameNew->mpDrawGroups, pDrawFrameNew->mDrawGroupCount * sizeof(ImguiDrawGroup), pDataOutput); + + for(uint32_t n = 0; n < pDrawFrameNew->mDrawGroupCount; n++) + { + const ImguiDrawGroup& drawGroupPack = pDrawFramePacked->mpDrawGroups[n]; + ImguiDrawGroup& drawGroup = pDrawFrameNew->mpDrawGroups[n]; + drawGroup = drawGroupPack; + + // Uncompress the 3 data streams + const ComDataType* pVerticePrev = nullptr; + const ComDataType* pIndicePrev = nullptr; + const ComDataType* pDrawsPrev = nullptr; + size_t verticeSizePrev(0), indiceSizePrev(0), drawSizePrev(0); + if (drawGroup.mDrawGroupIdxPrev < pDrawFramePrev->mDrawGroupCount) { + const ImguiDrawGroup& drawGroupPrev = pDrawFramePrev->mpDrawGroups[drawGroup.mDrawGroupIdxPrev]; + pVerticePrev = reinterpret_cast(drawGroupPrev.mpVertices.Get()); + pIndicePrev = reinterpret_cast(drawGroupPrev.mpIndices.Get()); + pDrawsPrev = reinterpret_cast(drawGroupPrev.mpDraws.Get()); + verticeSizePrev = drawGroupPrev.mVerticeCount * sizeof(ImguiVert); + indiceSizePrev = drawGroupPrev.mIndiceCount*static_cast(drawGroupPrev.mBytePerIndex); + drawSizePrev = drawGroupPrev.mDrawCount*sizeof(ImguiDraw); + } + + drawGroup.mpIndices.SetComDataPtr(pDataOutput); + DecompressData( pIndicePrev, indiceSizePrev, + drawGroupPack.mpIndices.GetComData(), drawGroupPack.mIndiceCount*static_cast(drawGroupPack.mBytePerIndex), + pDataOutput); + + drawGroup.mpVertices.SetComDataPtr(pDataOutput); + DecompressData( pVerticePrev, verticeSizePrev, + drawGroupPack.mpVertices.GetComData(), drawGroupPack.mVerticeCount*sizeof(ImguiVert), + pDataOutput); + + drawGroup.mpDraws.SetComDataPtr(pDataOutput); + DecompressData( pDrawsPrev, drawSizePrev, + drawGroupPack.mpDraws.GetComData(), drawGroupPack.mDrawCount*sizeof(ImguiDraw), + pDataOutput); + } + return pDrawFrameNew; +} + +//================================================================================================= +// Take a regular Dear Imgui Draw Data, and convert it to a NetImgui DrawFrame Command +// It involves saving each window draw group vertex/indices/draw buffers +// and packing their data a little bit, to reduce the bandwidth usage +//================================================================================================= +CmdDrawFrame* ConvertToCmdDrawFrame(const ImDrawData* pDearImguiData, ImGuiMouseCursor mouseCursor) +{ + //----------------------------------------------------------------------------------------- + // Find memory needed for entire DrawFrame Command + //----------------------------------------------------------------------------------------- + static_assert(sizeof(CmdDrawFrame) % ComDataSize == 0, "Make sure Command Data is aligned to com data type size"); + size_t neededDataCount = DivUp(sizeof(CmdDrawFrame), ComDataSize); + neededDataCount += DivUp(static_cast(pDearImguiData->CmdListsCount) * sizeof(ImguiDrawGroup), ComDataSize); + for(int n = 0; n < pDearImguiData->CmdListsCount; n++) + { + const ImDrawList* pCmdList = pDearImguiData->CmdLists[n]; + bool is16Bit = pCmdList->VtxBuffer.size() <= 0xFFFF; + neededDataCount += DivUp(static_cast(pCmdList->VtxBuffer.size()) * sizeof(ImguiVert), ComDataSize); + neededDataCount += DivUp(static_cast(pCmdList->IdxBuffer.size()) * (is16Bit ? 2 : 4), ComDataSize); + neededDataCount += DivUp(static_cast(pCmdList->CmdBuffer.size()) * sizeof(ImguiDraw), ComDataSize); + } + + //----------------------------------------------------------------------------------------- + // Allocate Data and initialize general frame information + //----------------------------------------------------------------------------------------- + CmdDrawFrame* pDrawFrame = netImguiSizedNew(neededDataCount*ComDataSize); + ComDataType* pDataOutput = reinterpret_cast(&pDrawFrame[1]); + pDrawFrame->mMouseCursor = static_cast(mouseCursor); + pDrawFrame->mDisplayArea[0] = pDearImguiData->DisplayPos.x; + pDrawFrame->mDisplayArea[1] = pDearImguiData->DisplayPos.y; + pDrawFrame->mDisplayArea[2] = pDearImguiData->DisplayPos.x + pDearImguiData->DisplaySize.x; + pDrawFrame->mDisplayArea[3] = pDearImguiData->DisplayPos.y + pDearImguiData->DisplaySize.y; + pDrawFrame->mDrawGroupCount = static_cast(pDearImguiData->CmdListsCount); + SetAndIncreaseDataPointer(pDrawFrame->mpDrawGroups, static_cast(pDrawFrame->mDrawGroupCount * sizeof(ImguiDrawGroup)), pDataOutput); + + //----------------------------------------------------------------------------------------- + // Copy draw data (vertices, indices, drawcall info, ...) + //----------------------------------------------------------------------------------------- + + + for(size_t n = 0; n < pDrawFrame->mDrawGroupCount; n++) + { + ImguiDrawGroup& drawGroup = pDrawFrame->mpDrawGroups[n]; + const ImDrawList* pCmdList = pDearImguiData->CmdLists[static_cast(n)]; + drawGroup = ImguiDrawGroup(); + drawGroup.mGroupID = PointerCast(pCmdList->_OwnerName); // Use the name string pointer as a unique ID (seems to remain the same between frame) + ImGui_ExtractIndices(*pCmdList, drawGroup, pDataOutput); + ImGui_ExtractVertices(*pCmdList,drawGroup, pDataOutput); + ImGui_ExtractDraws(*pCmdList, drawGroup, pDataOutput); + pDrawFrame->mTotalVerticeCount += drawGroup.mVerticeCount; + pDrawFrame->mTotalIndiceCount += drawGroup.mIndiceCount; + pDrawFrame->mTotalDrawCount += drawGroup.mDrawCount; + } + + pDrawFrame->mSize = static_cast(pDataOutput - reinterpret_cast(pDrawFrame)) * ComDataSize; + pDrawFrame->mUncompressedSize = pDrawFrame->mSize; // No compression with this item, so same value + return pDrawFrame; +} + +}} // namespace NetImgui::Internal + +#include "NetImgui_WarningReenable.h" +#endif //#if NETIMGUI_ENABLED diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets_DrawFrame.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets_DrawFrame.h new file mode 100644 index 0000000..16d1ef8 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_CmdPackets_DrawFrame.h @@ -0,0 +1,50 @@ +#pragma once + +#include "NetImgui_Shared.h" + +namespace NetImgui { namespace Internal +{ + +struct ImguiVert +{ + //Note: If updating this, increase 'CmdVersion::eVersion' + enum Constants{ kUvRange_Min=0, kUvRange_Max=1, kPosRange_Min=-8192, kPosRange_Max=8192}; + uint16_t mPos[2]; + uint16_t mUV[2]; + uint32_t mColor; +}; + +struct ImguiDraw +{ + uint64_t mTextureId; + uint32_t mIdxCount; // Drawcall number of indices (3 indices per triangles) + uint32_t mVtxOffset; // Drawcall start position in vertices buffer (considered index 0) + uint32_t mIdxOffset; // Drawcall start position in indices buffer + float mClipRect[4]; + uint8_t PADDING[4]={}; +}; + +// Each DearImgui window has its own vertex/index buffers with multiple drawcalls +struct alignas(8) ImguiDrawGroup +{ + static constexpr uint32_t kInvalidDrawGroup = 0xFFFFFFFF; + uint64_t mGroupID = 0; // Unique ID to recognize DrawGroup between 2 frames + uint32_t mVerticeCount = 0; + uint32_t mIndiceCount = 0; + uint32_t mDrawCount = 0; + uint32_t mDrawGroupIdxPrev = kInvalidDrawGroup;// Group index in previous DrawFrame (kInvalidDrawGroup when not using delta compression) + uint8_t mBytePerIndex = 2; // 2, 4 bytes + uint8_t PADDING[7] = {}; + float mReferenceCoord[2] = {}; // Reference position for the encoded vertices offsets (1st vertice top/left position) + OffsetPointer mpIndices; + OffsetPointer mpVertices; + OffsetPointer mpDraws; + inline void ToPointers(); + inline void ToOffsets(); +}; + +struct CmdDrawFrame* ConvertToCmdDrawFrame(const ImDrawData* pDearImguiData, ImGuiMouseCursor cursor); +struct CmdDrawFrame* CompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const CmdDrawFrame* pDrawFrameNew); +struct CmdDrawFrame* DecompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const CmdDrawFrame* pDrawFramePacked); + +}} // namespace NetImgui::Internal diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Network.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Network.h new file mode 100644 index 0000000..6aa3fb4 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Network.h @@ -0,0 +1,22 @@ +#pragma once + +namespace NetImgui { namespace Internal { struct PendingCom; }} + +namespace NetImgui { namespace Internal { namespace Network +{ + +struct SocketInfo; + +bool Startup (void); +void Shutdown (void); + +SocketInfo* Connect (const char* ServerHost, uint32_t ServerPort); // Communication Socket expected to be blocking +SocketInfo* ListenConnect (SocketInfo* ListenSocket); // Communication Socket expected to be blocking +SocketInfo* ListenStart (uint32_t ListenPort); // Listening Socket expected to be non blocking +void Disconnect (SocketInfo* pClientSocket); + +bool DataReceivePending (SocketInfo* pClientSocket); // True if some new data if waiting to be processed from remote connection +void DataReceive (SocketInfo* pClientSocket, PendingCom& PendingComRcv); // Try reading X amount of bytes from remote connection, but can fall short. +void DataSend (SocketInfo* pClientSocket, PendingCom& PendingComSend); // Try sending X amount of bytes to remote connection, but can fall short. + +}}} //namespace NetImgui::Internal::Network diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkPosix.cpp b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkPosix.cpp new file mode 100644 index 0000000..da51bdf --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkPosix.cpp @@ -0,0 +1,154 @@ +#include "NetImgui_Shared.h" + +#if defined(_MSC_VER) +#pragma warning (disable: 4221) +#endif + +#if NETIMGUI_ENABLED && NETIMGUI_POSIX_SOCKETS_ENABLED +#include +#include +#include +#include +#include + +//NOTE: The socket handling has been modified to improve speed, but the Posix version +// has not been updated. Please review the changes made to 'NetImgui_NetworkWin32.cpp' +// between version 1.11 and 1.12 and bring them over to this file. In particular : +// -Sockets set to non blocking and immediate sending +// -Added 'DataReceivePending' function +// -Reworked 'DataReceive' and 'DataSend' to be non blocking socket operation +static_assert(0) + +namespace NetImgui { namespace Internal { namespace Network +{ + +struct SocketInfo +{ + SocketInfo(int socket) : mSocket(socket){} + int mSocket; +}; + +bool Startup() +{ + return true; +} + +void Shutdown() +{ +} + +inline void SetNonBlocking(int Socket, bool bIsNonBlocking) +{ + int Flags = fcntl(Socket, F_GETFL, 0); + Flags = bIsNonBlocking ? Flags | O_NONBLOCK : Flags ^ (Flags & O_NONBLOCK); + fcntl(Socket, F_SETFL, Flags); +} + +SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort) +{ + int ConnectSocket = socket(AF_INET , SOCK_STREAM , 0 ); + if(ConnectSocket == -1) + return nullptr; + + char zPortName[32]; + addrinfo* pResults = nullptr; + SocketInfo* pSocketInfo = nullptr; + sprintf(zPortName, "%i", ServerPort); + getaddrinfo(ServerHost, zPortName, nullptr, &pResults); + addrinfo* pResultCur = pResults; + while( pResultCur && !pSocketInfo) + { + if( connect(ConnectSocket, pResultCur->ai_addr, static_cast(pResultCur->ai_addrlen)) == 0 ) + { + SetNonBlocking(ConnectSocket, false); + pSocketInfo = netImguiNew(ConnectSocket); + } + pResultCur = pResultCur->ai_next; + } + + freeaddrinfo(pResults); + if( !pSocketInfo ) + { + close(ConnectSocket); + } + return pSocketInfo; +} + +SocketInfo* ListenStart(uint32_t ListenPort) +{ + addrinfo hints; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + addrinfo* addrInfo; + getaddrinfo(nullptr, std::to_string(ListenPort).c_str(), &hints, &addrInfo); + + int ListenSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype, addrInfo->ai_protocol); + if( ListenSocket != -1 ) + { + #if NETIMGUI_FORCE_TCP_LISTEN_BINDING + int flag = 1; + setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); + setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag)); + #endif + if( bind(ListenSocket, addrInfo->ai_addr, addrInfo->ai_addrlen) != -1 && + listen(ListenSocket, 0) != -1) + { + SetNonBlocking(ListenSocket, false); + return netImguiNew(ListenSocket); + } + close(ListenSocket); + } + return nullptr; +} + +SocketInfo* ListenConnect(SocketInfo* ListenSocket) +{ + if( ListenSocket ) + { + sockaddr_storage ClientAddress; + socklen_t Size(sizeof(ClientAddress)); + int ServerSocket = accept(ListenSocket->mSocket, (sockaddr*)&ClientAddress, &Size) ; + if (ServerSocket != -1) + { + SetNonBlocking(ServerSocket, false); + return netImguiNew(ServerSocket); + } + } + return nullptr; +} + +void Disconnect(SocketInfo* pClientSocket) +{ + if( pClientSocket ) + { + shutdown(pClientSocket->mSocket, SHUT_RDWR); + close(pClientSocket->mSocket); + netImguiDelete(pClientSocket); + } +} + +bool DataReceive(SocketInfo* pClientSocket, void* pDataIn, size_t Size) +{ + int resultRcv = recv(pClientSocket->mSocket, static_cast(pDataIn), static_cast(Size), MSG_WAITALL); + return static_cast(Size) == resultRcv; +} + +bool DataSend(SocketInfo* pClientSocket, void* pDataOut, size_t Size) +{ + int resultSend = send(pClientSocket->mSocket, static_cast(pDataOut), static_cast(Size), 0); + return static_cast(Size) == resultSend; +} + +}}} // namespace NetImgui::Internal::Network +#else + +// Prevents Linker warning LNK4221 in Visual Studio (This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library) +extern int sSuppresstLNK4221_NetImgui_NetworkPosix; +int sSuppresstLNK4221_NetImgui_NetworkPosix(0); + +#endif // #if NETIMGUI_ENABLED && NETIMGUI_POSIX_SOCKETS_ENABLED + diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkUE4.cpp b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkUE4.cpp new file mode 100644 index 0000000..941776e --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkUE4.cpp @@ -0,0 +1,233 @@ +#include "NetImgui_Shared.h" + +// Tested with Unreal Engine 4.27, 5.3, 5.4, 5.5 + +#if NETIMGUI_ENABLED && defined(__UNREAL__) + +#include "CoreMinimal.h" +#include "Runtime/Launch/Resources/Version.h" +#include "Misc/OutputDeviceRedirector.h" +#include "SocketSubsystem.h" +#include "Sockets.h" +#include "HAL/PlatformProcess.h" +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 2 +#include "IPAddressAsyncResolve.h" +#endif + +namespace NetImgui { namespace Internal { namespace Network +{ + +//================================================================================================= +// Wrapper around native socket object and init some socket options +//================================================================================================= +struct SocketInfo +{ + SocketInfo(FSocket* pSocket) + : mpSocket(pSocket) + { + if( mpSocket ) + { + mpSocket->SetNonBlocking(true); + mpSocket->SetNoDelay(true); + + int32 NewSize(0); + while( !mpSocket->SetSendBufferSize(2*mSendSize, NewSize) ){ + mSendSize /= 2; + } + mSendSize = NewSize/2; + } + } + + ~SocketInfo() + { + Close(); + } + + void Close() + { + if(mpSocket ) + { + mpSocket->Close(); + ISocketSubsystem::Get()->DestroySocket(mpSocket); + mpSocket = nullptr; + } + } + FSocket* mpSocket = nullptr; + int32 mSendSize = 1024*1024; // Limit tx data to avoid socket error on large amount (texture) +}; + +bool Startup() +{ + return true; +} + +void Shutdown() +{ +} + +//================================================================================================= +// Try establishing a connection to a remote client at given address +//================================================================================================= +SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort) +{ + SocketInfo* pSocketInfo = nullptr; + ISocketSubsystem* SocketSubSystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); + TSharedPtr IpAddressFind = SocketSubSystem->GetAddressFromString((TCHAR*)StringCast(static_cast(ServerHost)).Get()); + if(IpAddressFind) + { + TSharedRef IpAddress = IpAddressFind->Clone(); + IpAddress->SetPort(ServerPort); + if (IpAddress->IsValid()) + { + FSocket* pNewSocket = SocketSubSystem->CreateSocket(NAME_Stream, "NetImgui", IpAddress->GetProtocolType()); + if (pNewSocket) + { + pNewSocket->SetNonBlocking(true); + if (pNewSocket->Connect(*IpAddress)) + { + bool bConnectionReady = false; + pNewSocket->WaitForPendingConnection(bConnectionReady, FTimespan::FromSeconds(1.0f)); + if( bConnectionReady ) + { + pSocketInfo = netImguiNew(pNewSocket); + return pSocketInfo; + } + } + } + } + } + netImguiDelete(pSocketInfo); + return nullptr; +} + +//================================================================================================= +// Start waiting for connection request on this socket +//================================================================================================= +SocketInfo* ListenStart(uint32_t ListenPort) +{ + ISocketSubsystem* PlatformSocketSub = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); + TSharedPtr IpAddress = PlatformSocketSub->GetLocalBindAddr(*GLog); + IpAddress->SetPort(ListenPort); + + FSocket* pNewListenSocket = PlatformSocketSub->CreateSocket(NAME_Stream, "NetImguiListen", IpAddress->GetProtocolType()); + if( pNewListenSocket ) + { + SocketInfo* pListenSocketInfo = netImguiNew(pNewListenSocket); + #if NETIMGUI_FORCE_TCP_LISTEN_BINDING + pNewListenSocket->SetReuseAddr(); + #endif + pNewListenSocket->SetNonBlocking(false); + pNewListenSocket->SetRecvErr(); + if (pNewListenSocket->Bind(*IpAddress)) + { + if (pNewListenSocket->Listen(1)) + { + return pListenSocketInfo; + } + } + netImguiDelete(pListenSocketInfo); + } + return nullptr; +} + +//================================================================================================= +// Establish a new connection to a remote request +//================================================================================================= +SocketInfo* ListenConnect(SocketInfo* pListenSocket) +{ + if (pListenSocket) + { + FSocket* pNewSocket = pListenSocket->mpSocket->Accept(FString("NetImgui")); + if( pNewSocket ) + { + SocketInfo* pSocketInfo = netImguiNew(pNewSocket); + return pSocketInfo; + } + } + return nullptr; +} + +//================================================================================================= +// Close a connection and free allocated object +//================================================================================================= +void Disconnect(SocketInfo* pClientSocket) +{ + netImguiDelete(pClientSocket); +} + +//================================================================================================= +// Return true if data has been received, or there's a connection error +//================================================================================================= +bool DataReceivePending(SocketInfo* pClientSocket) +{ + // Note: return true on a connection error, to exit code looping on the data wait. Will handle error after DataReceive() + uint32 PendingDataSize; + return !pClientSocket || pClientSocket->mpSocket->HasPendingData(PendingDataSize) || (pClientSocket->mpSocket->GetConnectionState() != ESocketConnectionState::SCS_Connected); +} + +//================================================================================================= +// Receive as much as possible a command and keep track of transfer status +//================================================================================================= +void DataReceive(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComRcv) +{ + // Invalid command + if( !pClientSocket || !PendingComRcv.pCommand || !pClientSocket->mpSocket || (pClientSocket->mpSocket->GetConnectionState() != ESocketConnectionState::SCS_Connected)){ + PendingComRcv.bError = true; + return; + } + + int32 sizeRcv(0); + if( pClientSocket->mpSocket->Recv( &reinterpret_cast(PendingComRcv.pCommand)[PendingComRcv.SizeCurrent], + static_cast(PendingComRcv.pCommand->mSize-PendingComRcv.SizeCurrent), + sizeRcv, + ESocketReceiveFlags::None) ) + { + PendingComRcv.SizeCurrent += static_cast(sizeRcv); + PendingComRcv.bError |= sizeRcv <= 0; // Error if no data read since DataReceivePending() said there was some available + } + else + { + // Connection error, abort transmission + const ESocketErrors SocketError = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode(); + PendingComRcv.bError = SocketError != SE_NO_ERROR && SocketError != ESocketErrors::SE_EWOULDBLOCK; + } +} + +//================================================================================================= +// Receive as much as possible a command and keep track of transfer status +//================================================================================================= +void DataSend(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComSend) +{ + // Invalid command + if( !pClientSocket || !PendingComSend.pCommand || !pClientSocket->mpSocket || (pClientSocket->mpSocket->GetConnectionState() != ESocketConnectionState::SCS_Connected) ){ + PendingComSend.bError = true; + return; + } + + int32 sizeSent = 0; + int32 sizeToSend = PendingComSend.pCommand->mSize-PendingComSend.SizeCurrent; + sizeToSend = sizeToSend > pClientSocket->mSendSize ? pClientSocket->mSendSize : sizeToSend; + + if( pClientSocket->mpSocket->Send( &reinterpret_cast(PendingComSend.pCommand)[PendingComSend.SizeCurrent], + static_cast(sizeToSend), + sizeSent) ) + { + PendingComSend.SizeCurrent += static_cast(sizeSent); + } + else + { + // Connection error, abort transmission + const ESocketErrors SocketError = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode(); + PendingComSend.bError = SocketError != SE_NO_ERROR && SocketError != ESocketErrors::SE_EWOULDBLOCK; + } +} + +}}} // namespace NetImgui::Internal::Network + +#else + +// Prevents Linker warning LNK4221 in Visual Studio (This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library) +extern int sSuppresstLNK4221_NetImgui_NetworkUE4; +int sSuppresstLNK4221_NetImgui_NetworkUE4(0); + +#endif // #if NETIMGUI_ENABLED && defined(__UNREAL__) diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkWin32.cpp b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkWin32.cpp new file mode 100644 index 0000000..6cf4e91 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_NetworkWin32.cpp @@ -0,0 +1,253 @@ +#include "NetImgui_Shared.h" + +#if NETIMGUI_ENABLED && NETIMGUI_WINSOCKET_ENABLED +#include "NetImgui_WarningDisableStd.h" +#include +#include + +#if defined(_MSC_VER) +#pragma comment(lib, "ws2_32") +#endif + +#include "NetImgui_CmdPackets.h" + +namespace NetImgui { namespace Internal { namespace Network +{ +//================================================================================================= +// Wrapper around native socket object and init some socket options +//================================================================================================= +struct SocketInfo +{ + SocketInfo(SOCKET socket) + : mSocket(socket) + { + u_long kNonBlocking = true; + ioctlsocket(mSocket, static_cast(FIONBIO), &kNonBlocking); + + constexpr DWORD kComsNoDelay = 1; + setsockopt(mSocket, SOL_SOCKET, TCP_NODELAY, reinterpret_cast(&kComsNoDelay), sizeof(kComsNoDelay)); + + const int kComsSendBuffer = 2*mSendSizeMax; + setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&kComsSendBuffer), sizeof(kComsSendBuffer)); + + //constexpr int kComsRcvBuffer = 1014*1024; + //setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&kComsRcvBuffer), sizeof(kComsRcvBuffer)); + } + + SOCKET mSocket; + int mSendSizeMax = 1024*1024; // Limit tx data to avoid socket error on large amount (texture) +}; + +bool Startup() +{ + WSADATA wsa; + if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) + return false; + + return true; +} + +void Shutdown() +{ + WSACleanup(); +} + +//================================================================================================= +// Try establishing a connection to a remote client at given address +//================================================================================================= +SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort) +{ + const timeval kConnectTimeout = {1, 0}; // Waiting 1 seconds before failing connection attempt + u_long kNonBlocking = true; + + SOCKET ClientSocket = socket(AF_INET , SOCK_STREAM , 0); + if(ClientSocket == INVALID_SOCKET) + return nullptr; + + char zPortName[32] = {}; + addrinfo* pResults = nullptr; + SocketInfo* pSocketInfo = nullptr; + NetImgui::Internal::StringFormat(zPortName, "%i", ServerPort); + getaddrinfo(ServerHost, zPortName, nullptr, &pResults); + addrinfo* pResultCur = pResults; + fd_set SocketSet; + + ioctlsocket(ClientSocket, static_cast(FIONBIO), &kNonBlocking); + while( pResultCur && !pSocketInfo ) + { + int Result = connect(ClientSocket, pResultCur->ai_addr, static_cast(pResultCur->ai_addrlen)); + bool Connected = Result != SOCKET_ERROR; + + // Not connected yet, wait some time before bailing out + if( Result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK ) + { + FD_ZERO(&SocketSet); + FD_SET(ClientSocket, &SocketSet); + Result = select(0, nullptr, &SocketSet, nullptr, &kConnectTimeout); + Connected = Result == 1; // when 1 socket ready for write, otherwise it's -1 or 0 + } + + if( Connected ) + { + pSocketInfo = netImguiNew(ClientSocket); + } + + pResultCur = pResultCur->ai_next; + } + freeaddrinfo(pResults); + if( !pSocketInfo ) + { + closesocket(ClientSocket); + } + return pSocketInfo; +} + +//================================================================================================= +// Start waiting for connection request on this socket +//================================================================================================= +SocketInfo* ListenStart(uint32_t ListenPort) +{ + SOCKET ListenSocket = INVALID_SOCKET; + if( (ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != INVALID_SOCKET ) + { + sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(static_cast(ListenPort)); + + #if NETIMGUI_FORCE_TCP_LISTEN_BINDING + constexpr BOOL ReUseAdrValue(true); + setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&ReUseAdrValue), sizeof(ReUseAdrValue)); + #endif + if( bind(ListenSocket, reinterpret_cast(&server), sizeof(server)) != SOCKET_ERROR && + listen(ListenSocket, 0) != SOCKET_ERROR ) + { + u_long kIsNonBlocking = false; + ioctlsocket(ListenSocket, static_cast(FIONBIO), &kIsNonBlocking); + return netImguiNew(ListenSocket); + } + closesocket(ListenSocket); + } + return nullptr; +} + +//================================================================================================= +// Establish a new connection to a remote request +//================================================================================================= +SocketInfo* ListenConnect(SocketInfo* ListenSocket) +{ + if( ListenSocket ) + { + sockaddr ClientAddress; + int Size(sizeof(ClientAddress)); + SOCKET ClientSocket = accept(ListenSocket->mSocket, &ClientAddress, &Size) ; + if (ClientSocket != INVALID_SOCKET) + { + return netImguiNew(ClientSocket); + } + } + return nullptr; +} + +//================================================================================================= +// Close a connection and free allocated object +//================================================================================================= +void Disconnect(SocketInfo* pClientSocket) +{ + if( pClientSocket ) + { + shutdown(pClientSocket->mSocket, SD_BOTH); + closesocket(pClientSocket->mSocket); + netImguiDelete(pClientSocket); + } +} + +//================================================================================================= +// Return true if data has been received, or there's a connection error +//================================================================================================= +bool DataReceivePending(SocketInfo* pClientSocket) +{ + const timeval kConnectTimeout = {0, 0}; // No wait time + if( pClientSocket ) + { + fd_set fdSetRead; + fd_set fdSetErr; + FD_ZERO(&fdSetRead); + FD_ZERO(&fdSetErr); + FD_SET(pClientSocket->mSocket, &fdSetRead); + FD_SET(pClientSocket->mSocket, &fdSetErr); + + // Note: return true if data ready or connection error (to exit parent loop waiting on data) + int result = select(0, &fdSetRead, nullptr, &fdSetErr, &kConnectTimeout); + return result != 0; + } + return true; +} + +//================================================================================================= +// Receive as much as possible a command and keep track of transfer status +//================================================================================================= +void DataReceive(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComRcv) +{ + // Invalid command + if( !pClientSocket || !PendingComRcv.pCommand || !pClientSocket->mSocket ){ + PendingComRcv.bError = true; + return; + } + + // Receive data from remote connection + int resultRcv = recv( pClientSocket->mSocket, + &reinterpret_cast(PendingComRcv.pCommand)[PendingComRcv.SizeCurrent], + static_cast(PendingComRcv.pCommand->mSize-PendingComRcv.SizeCurrent), + 0); + + // Note: 'DataReceive' is called after pending data has been confirm. + // 0 received data means connection lost + if( resultRcv != SOCKET_ERROR ){ + PendingComRcv.SizeCurrent += static_cast(resultRcv); + PendingComRcv.bError |= resultRcv <= 0; // Error if no data read since DataReceivePending() said there was some available + } + // Connection error, abort transmission + else if( WSAGetLastError() != WSAEWOULDBLOCK ){ + PendingComRcv.bError = true; + } +} + +//================================================================================================= +// Receive as much as possible a command and keep track of transfer status +//================================================================================================= +void DataSend(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComSend) +{ + // Invalid command + if( !pClientSocket || !PendingComSend.pCommand || !pClientSocket->mSocket ){ + PendingComSend.bError = true; + return; + } + + // Send data to remote connection + int sizeToSend = static_cast(PendingComSend.pCommand->mSize-PendingComSend.SizeCurrent); + sizeToSend = sizeToSend > pClientSocket->mSendSizeMax ? pClientSocket->mSendSizeMax : sizeToSend; + int resultSent = send( pClientSocket->mSocket, + &reinterpret_cast(PendingComSend.pCommand)[PendingComSend.SizeCurrent], + sizeToSend, + 0); + + if( resultSent != SOCKET_ERROR ){ + PendingComSend.SizeCurrent += static_cast(resultSent); + } + // Connection error, abort transmission + else if( WSAGetLastError() != WSAEWOULDBLOCK ){ + PendingComSend.bError = true; + } +} + +}}} // namespace NetImgui::Internal::Network + +#include "NetImgui_WarningReenable.h" +#else + +// Prevents Linker warning LNK4221 in Visual Studio (This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library) +extern int sSuppresstLNK4221_NetImgui_NetworkWin23; +int sSuppresstLNK4221_NetImgui_NetworkWin23(0); + +#endif // #if NETIMGUI_ENABLED && NETIMGUI_WINSOCKET_ENABLED diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Shared.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Shared.h new file mode 100644 index 0000000..f970e0e --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Shared.h @@ -0,0 +1,200 @@ +#pragma once + +//================================================================================================= +// Include NetImgui_Api.h with almost no warning suppression. +// this is to make sure library user does not need to suppress any +#if defined(_MSC_VER) +#pragma warning (disable: 4464) // warning C4464: relative include path contains '..' +#endif + +#ifndef NETIMGUI_INTERNAL_INCLUDE + #define NETIMGUI_INTERNAL_INCLUDE 1 + #include "NetImgui_Api.h" + #undef NETIMGUI_INTERNAL_INCLUDE +#else + #include "NetImgui_Api.h" +#endif + +#if NETIMGUI_ENABLED + +//================================================================================================= +// Include a few standard c++ header, with additional warning suppression +#include "NetImgui_WarningDisableStd.h" +#include +#include +#include +#include +#include "NetImgui_WarningReenable.h" +//================================================================================================= + + +//================================================================================================= +#include "NetImgui_WarningDisable.h" +namespace NetImgui { namespace Internal +{ + +using ComDataType = uint64_t; +constexpr size_t ComDataSize = sizeof(ComDataType); + +//============================================================================= +// All allocations made by netImgui goes through here. +// Relies in ImGui allocator +//============================================================================= +template TType* netImguiNew(Args... args); +template TType* netImguiSizedNew(size_t placementSize); +template void netImguiDelete(TType* pData); +template void netImguiDeleteSafe(TType*& pData); + +class ScopedImguiContext +{ +public: + ScopedImguiContext(ImGuiContext* pNewContext) : mpSavedContext(ImGui::GetCurrentContext()){ ImGui::SetCurrentContext(pNewContext); } + ~ScopedImguiContext() { ImGui::SetCurrentContext(mpSavedContext); } + +protected: + ImGuiContext* mpSavedContext; +}; + +template +class ScopedValue +{ +public: + ScopedValue(TType& ValueRef, TType Value) + : mValueRef(ValueRef) + , mValueRestore(ValueRef) + { + mValueRef = Value; + } + ~ScopedValue() + { + mValueRef = mValueRestore; + } +protected: + TType& mValueRef; + TType mValueRestore; + uint8_t mPadding[sizeof(void*)-(sizeof(TType)%8)]={}; + + // Prevents warning about implicitly delete functions + ScopedValue(const ScopedValue&) = delete; + ScopedValue(const ScopedValue&&) = delete; + void operator=(const ScopedValue&) = delete; +}; + +using ScopedBool = ScopedValue; + +//============================================================================= +// Class to safely exchange a pointer between two threads +//============================================================================= +template +class ExchangePtr +{ +public: + ExchangePtr():mpData(nullptr){} + ~ExchangePtr(); + inline TType* Release(); + inline void Assign(TType*& pNewData); + inline void Free(); + inline bool IsNull()const { return mpData.load() == nullptr; } +private: + std::atomic mpData; + +// Prevents warning about implicitly delete functions +private: + ExchangePtr(const ExchangePtr&) = delete; + ExchangePtr(const ExchangePtr&&) = delete; + void operator=(const ExchangePtr&) = delete; +}; + +//============================================================================= +// Make data serialization easier +//============================================================================= +template +struct OffsetPointer +{ + inline OffsetPointer(); + inline explicit OffsetPointer(TType* pPointer); + inline explicit OffsetPointer(uint64_t offset); + + inline bool IsPointer()const; + inline bool IsOffset()const; + + inline TType* ToPointer(); + inline uint64_t ToOffset(); + inline TType* operator->(); + inline const TType* operator->()const; + inline TType& operator[](size_t index); + inline const TType& operator[](size_t index)const; + + inline TType* Get(); + inline const TType* Get()const; + inline const ComDataType* GetComData()const; + inline uint64_t GetOff()const; + inline void SetPtr(TType* pPointer); + inline void SetComDataPtr(ComDataType* pPointer); + inline void SetOff(uint64_t offset); + +private: + union + { + uint64_t mOffset; + TType* mPointer; + }; +}; + +//============================================================================= +//============================================================================= +template +class Ringbuffer +{ +public: + Ringbuffer():mPosCur(0),mPosLast(0){} + void AddData(const TType* pData, size_t& count); + bool ReadData(TType* pData); +private: + TType mBuffer[TCount] = {0}; + std::atomic_uint64_t mPosCur; + std::atomic_uint64_t mPosLast; + +// Prevents warning about implicitly delete functions +private: + Ringbuffer(const Ringbuffer&) = delete; + Ringbuffer(const Ringbuffer&&) = delete; + void operator=(const Ringbuffer&) = delete; +}; + +template +constexpr std::size_t ArrayCount(T const (&)[N]) noexcept +{ + return N; +} + +//============================================================================= +//============================================================================= +template +inline void StringCopy(char (&output)[charCount], const char* pSrc, size_t srcCharCount=0xFFFFFFFE); + +template +int StringFormat(char(&output)[charCount], char const* const format, ...); + +//============================================================================= +// Get the (value / Denominator) rounded up to the next int value big enough +//============================================================================= +template +IntType DivUp(IntType Value, IntType Denominator); + +//============================================================================= +// Get the rounded up value +//============================================================================= +template +IntType RoundUp(IntType Value, IntType Round); + +inline uint64_t TextureCastFromID(ImTextureID textureID); +inline ImTextureID TextureCastFromPtr(void* pTexture); +inline ImTextureID TextureCastFromUInt(uint64_t textureID); + +}} //namespace NetImgui::Internal + +#include "NetImgui_Shared.inl" +#include "NetImgui_WarningReenable.h" + +#endif //NETIMGUI_ENABLED diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Shared.inl b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Shared.inl new file mode 100644 index 0000000..6907203 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_Shared.inl @@ -0,0 +1,319 @@ +#pragma once + +#include +#include + +namespace NetImgui { namespace Internal +{ + +template +TType* netImguiNew(Args... args) +{ + return new( ImGui::MemAlloc(sizeof(TType)) ) TType(args...); +} + +template +TType* netImguiSizedNew(size_t placementSize) +{ + return new( ImGui::MemAlloc(placementSize > sizeof(TType) ? placementSize : sizeof(TType)) ) TType(); +} + +template +void netImguiDelete(TType* pData) +{ + if( pData ) + { + pData->~TType(); + ImGui::MemFree(pData); + } +} + +template +void netImguiDeleteSafe(TType*& pData) +{ + netImguiDelete(pData); + pData = nullptr; +} + +//============================================================================= +// Acquire ownership of the resource +//============================================================================= +template +TType* ExchangePtr::Release() +{ + return mpData.exchange(nullptr); +} + +//----------------------------------------------------------------------------- +// Take ownership of the provided data. +// If there's a previous unclaimed pointer to some data, release it +//----------------------------------------------------------------------------- +template +void ExchangePtr::Assign(TType*& pNewData) +{ + netImguiDelete( mpData.exchange(pNewData) ); + pNewData = nullptr; +} + +template +void ExchangePtr::Free() +{ + TType* pNull(nullptr); + Assign(pNull); +} + +template +ExchangePtr::~ExchangePtr() +{ + Free(); +} + +//============================================================================= +// +//============================================================================= +template +OffsetPointer::OffsetPointer() +: mOffset(0) +{ + SetOff(0); +} + +template +OffsetPointer::OffsetPointer(TType* pPointer) +{ + SetPtr(pPointer); +} + +template +OffsetPointer::OffsetPointer(uint64_t offset) +{ + SetOff(offset); +} + +template +void OffsetPointer::SetPtr(TType* pPointer) +{ + mOffset = 0; // Remove 'offset flag' that can be left active on non 64bits pointer + mPointer = pPointer; +} + +template +void OffsetPointer::SetComDataPtr(ComDataType* pPointer) +{ + SetPtr(reinterpret_cast(pPointer)); +} + +template +void OffsetPointer::SetOff(uint64_t offset) +{ + mOffset = offset | 0x0000000000000001u; +} + +template +uint64_t OffsetPointer::GetOff()const +{ + return mOffset & ~0x0000000000000001u; +} + +template +bool OffsetPointer::IsOffset()const +{ + return (mOffset & 0x0000000000000001u) != 0; +} + +template +bool OffsetPointer::IsPointer()const +{ + return !IsOffset(); +} + +template +TType* OffsetPointer::ToPointer() +{ + assert(IsOffset()); + SetPtr( reinterpret_cast( reinterpret_cast(&mPointer) + GetOff() ) ); + return mPointer; +} + +template +uint64_t OffsetPointer::ToOffset() +{ + assert(IsPointer()); + SetOff( reinterpret_cast(mPointer) - reinterpret_cast(&mPointer) ); + return mOffset; +} + +template +TType* OffsetPointer::operator->() +{ + assert(IsPointer()); + return mPointer; +} + +template +const TType* OffsetPointer::operator->()const +{ + assert(IsPointer()); + return mPointer; +} + +template +TType* OffsetPointer::Get() +{ + assert(IsPointer()); + return mPointer; +} + +template +const TType* OffsetPointer::Get()const +{ + assert(IsPointer()); + return mPointer; +} + +template +const ComDataType* OffsetPointer::GetComData()const +{ + return reinterpret_cast(Get()); +} + +template +TType& OffsetPointer::operator[](size_t index) +{ + assert(IsPointer()); + return mPointer[index]; +} + +template +const TType& OffsetPointer::operator[](size_t index)const +{ + assert(IsPointer()); + return mPointer[index]; +} + +//============================================================================= +template +void Ringbuffer::AddData(const TType* pData, size_t& count) +//============================================================================= +{ + size_t i(0); + while (i < count && (mPosLast - mPosCur < TCount)) { + mBuffer[mPosLast % TCount] = pData[i]; + mPosLast++; + i++; + } + count = i; +} + +//============================================================================= +template +bool Ringbuffer::ReadData(TType* pData) +//============================================================================= +{ + if (mPosCur < mPosLast) + { + *pData = mBuffer[mPosCur % TCount]; + mPosCur++; + return true; + } + return false; +} + + +//============================================================================= +// The _s string functions are a mess. There's really no way to do this right +// in a cross-platform way. Best solution I've found is to set just use +// strncpy, infer the buffer length, and null terminate. Still need to suppress +// the warning on Windows. +// See https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/ +// and many other discussions online on the topic. +//============================================================================= +template +void StringCopy(char (&output)[charCount], const char* pSrc, size_t srcCharCount) +{ +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) + #pragma warning (push) + #pragma warning (disable: 4996) // warning C4996: 'strncpy': This function or variable may be unsafe. +#endif + + size_t charToCopyCount = charCount < srcCharCount + 1 ? charCount : srcCharCount + 1; + strncpy(output, pSrc, charToCopyCount - 1); + output[charCount - 1] = 0; + +#if defined(_MSC_VER) && defined(__clang__) + #pragma clang diagnostic pop +#elif defined(_MSC_VER) + #pragma warning (pop) +#endif +} + +template +int StringFormat(char(&output)[charCount], char const* const format, ...) +{ +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + + va_list args; + va_start(args, format); + int w = vsnprintf(output, charCount, format, args); + va_end(args); + output[charCount - 1] = 0; + return w; + +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} + +//============================================================================= +//============================================================================= +template +IntType DivUp(IntType Value, IntType Denominator) +{ + return (Value + Denominator - 1) / Denominator; +} + +template +IntType RoundUp(IntType Value, IntType Round) +{ + return DivUp(Value, Round) * Round; +} + +union TextureCastHelperUnion +{ + ImTextureID TextureID; + uint64_t TextureUint; + const void* TexturePtr; +}; + +uint64_t TextureCastFromID(ImTextureID textureID) +{ + TextureCastHelperUnion textureUnion; + textureUnion.TextureUint = 0; + textureUnion.TextureID = textureID; + return textureUnion.TextureUint; +} + +ImTextureID TextureCastFromPtr(void* pTexture) +{ + TextureCastHelperUnion textureUnion; + textureUnion.TextureUint = 0; + textureUnion.TexturePtr = pTexture; + return textureUnion.TextureID; +} + +#ifndef IS_NETIMGUISERVER +ImTextureID TextureCastFromUInt(uint64_t textureID) +{ + TextureCastHelperUnion textureUnion; + textureUnion.TextureUint = textureID; + return textureUnion.TextureID; +} +#endif + +}} //namespace NetImgui::Internal diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisable.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisable.h new file mode 100644 index 0000000..125f391 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisable.h @@ -0,0 +1,33 @@ +#pragma once +// +// Deactivate a few warnings to allow internal netImgui code to compile +// with 'Warning as error' and '-Wall' compile actions enabled +// + +//================================================================================================= +// Clang +//================================================================================================= +#if defined (__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" + #pragma clang diagnostic ignored "-Wmissing-prototypes" + +//================================================================================================= +// Visual Studio warnings +//================================================================================================= +#elif defined(_MSC_VER) + #pragma warning (disable: 5032) // detected #pragma warning(push) with no corresponding #pragma warning(pop) + + #pragma warning (push) + #pragma warning (disable: 4365) // conversion from 'long' to 'unsigned int', signed/unsigned mismatch for + #pragma warning (disable: 4464) // relative include path contains '..' + #pragma warning (disable: 4514) // unreferenced inline function has been removed + #pragma warning (disable: 4577) // 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify + #pragma warning (disable: 4710) // 'xxx': function not inlined + #pragma warning (disable: 4711) // function 'xxx' selected for automatic inline expansion + #pragma warning (disable: 4826) // Conversion from 'TType *' to 'uint64_t' is sign-extended. This may cause unexpected runtime behavior. + #pragma warning (disable: 5031) // #pragma warning(pop): likely mismatch, popping warning state pushed in different file + #pragma warning (disable: 5045) // Compiler will insert Spectre mitigation for memory load if / Qspectre switch specified + #pragma warning (disable: 5264) // 'xxx': 'const' variable is not used + +#endif diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisableImgui.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisableImgui.h new file mode 100644 index 0000000..ec16f63 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisableImgui.h @@ -0,0 +1,33 @@ +#pragma once +// +// Deactivate a few warnings to allow Imgui header includes, +// without generating warnings in '-Wall' compile actions enabled +// + +//================================================================================================= +// Clang +//================================================================================================= +#if defined (__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunknown-warning-option" + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" + #pragma clang diagnostic ignored "-Wnonportable-include-path" // Sharpmake convert include path to lowercase, avoid warning + #pragma clang diagnostic ignored "-Wreserved-identifier" // Enuma values using '__' or member starting with '_' in imgui.h + +//================================================================================================= +// Visual Studio warnings +//================================================================================================= +#elif defined(_MSC_VER) + #pragma warning (push) + #pragma warning (disable: 4514) // 'xxx': unreferenced inline function has been removed + #pragma warning (disable: 4365) // '=': conversion from 'ImGuiTabItemFlags' to 'ImGuiID', signed/unsigned mismatch + #pragma warning (disable: 4710) // 'xxx': function not inlined + #pragma warning (disable: 4820) // 'xxx': 'yyy' bytes padding added after data member 'zzz' + #pragma warning (disable: 5031) // #pragma warning(pop): likely mismatch, popping warning state pushed in different file + #pragma warning (disable: 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified +#if _MSC_VER >= 1920 + #pragma warning (disable: 5219) // implicit conversion from 'int' to 'float', possible loss of data +#endif + #pragma warning (disable: 26495) // Code Analysis warning : Variable 'ImGuiStorage::ImGuiStoragePair::::val_p' is uninitialized. Always initialize a member variable (type.6). +#endif + diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisableStd.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisableStd.h new file mode 100644 index 0000000..b3ded98 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningDisableStd.h @@ -0,0 +1,25 @@ +#pragma once +// +// Deactivate a few more warnings to allow standard header includes, +// without generating warnings in '-Wall' compile actions enabled +// + +#include "NetImgui_WarningDisable.h" + +//================================================================================================= +// Clang +//================================================================================================= +#if defined (__clang__) + + +//================================================================================================= +// Visual Studio warnings +//================================================================================================= +#elif defined(_MSC_VER) + #pragma warning (disable: 4061) // enumerator xxx in switch of enum yyy is not explicitly handled by a case label (d3d11.h) + #pragma warning (disable: 4548) // expression before comma has no effect; expected expression with side - effect (malloc.h VS2017) + #pragma warning (disable: 4668) // xxx is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' (winsock2.h) + #pragma warning (disable: 4574) // xxx is defined to be '0': did you mean to use yyy (winsock2.h VS2017) + #pragma warning (disable: 4820) // xxx : yyy bytes padding added after data member zzz + +#endif diff --git a/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningReenable.h b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningReenable.h new file mode 100644 index 0000000..232ebc7 --- /dev/null +++ b/Plugins/Cog/Source/ThirdParty/NetImgui/Private/NetImgui_WarningReenable.h @@ -0,0 +1,18 @@ + +#pragma once + +//================================================================================================= +// Clang +//================================================================================================= +#if defined(__clang__) + #pragma clang diagnostic pop + + +//================================================================================================= +// Visual Studio warnings +//================================================================================================= +#elif defined(_MSC_VER) + #pragma warning(pop) +#endif + + diff --git a/Plugins/CogAbility/Source/CogAbility/Private/CogAbilityWindow_Abilities.cpp b/Plugins/CogAbility/Source/CogAbility/Private/CogAbilityWindow_Abilities.cpp index 60c5b91..0639180 100644 --- a/Plugins/CogAbility/Source/CogAbility/Private/CogAbilityWindow_Abilities.cpp +++ b/Plugins/CogAbility/Source/CogAbility/Private/CogAbilityWindow_Abilities.cpp @@ -631,7 +631,7 @@ void FCogAbilityWindow_Abilities::RenderAbilityInfo(const UAbilitySystemComponen ImGui::TableNextColumn(); ImGui::TextColored(TextColor, "Ability Tags"); ImGui::TableNextColumn(); - FCogAbilityHelper::RenderTagContainer(Ability->AbilityTags); + FCogAbilityHelper::RenderTagContainer(Ability->GetAssetTags()); //------------------------ // RequiredTags diff --git a/Plugins/CogAll/Source/CogAll/Private/CogAll.cpp b/Plugins/CogAll/Source/CogAll/Private/CogAll.cpp index b44f392..a81a94f 100644 --- a/Plugins/CogAll/Source/CogAll/Private/CogAll.cpp +++ b/Plugins/CogAll/Source/CogAll/Private/CogAll.cpp @@ -19,6 +19,7 @@ #include "CogEngineWindow_LogCategories.h" #include "CogEngineWindow_Metrics.h" #include "CogEngineWindow_NetEmulation.h" +#include "CogEngineWindow_NetImGui.h" #include "CogEngineWindow_OutputLog.h" #include "CogEngineWindow_Plots.h" #include "CogEngineWindow_Scalability.h" @@ -65,11 +66,13 @@ void Cog::AddAllWindows(UCogWindowManager& CogWindowManager) CogWindowManager.AddWindow("Engine.Log Categories"); + CogWindowManager.AddWindow("Engine.Metrics"); + CogWindowManager.AddWindow("Engine.Net Emulation"); - CogWindowManager.AddWindow("Engine.Output Log"); + CogWindowManager.AddWindow("Engine.NetImGui"); - CogWindowManager.AddWindow("Engine.Metrics"); + CogWindowManager.AddWindow("Engine.Output Log"); CogWindowManager.AddWindow("Engine.Plots");