Export netimgui files in MuJoCo Copybara configuration for use with the upcoming Web Viewer
PiperOrigin-RevId: 954769526 Change-Id: I4cdee083082f7d55c2babd5e664ec0283a2aaf63
This commit is contained in:
committed by
Copybara-Service
parent
c7b6e0b8dd
commit
ca5337161d
@@ -0,0 +1,353 @@
|
||||
#pragma once
|
||||
|
||||
//=================================================================================================
|
||||
//! @Name : NetImgui
|
||||
//=================================================================================================
|
||||
//! @author : Sammy Fatnassi
|
||||
//! @date : 2026/01/04
|
||||
//! @version : v1.13.0
|
||||
//! @Details : For integration info :
|
||||
//! https://github.com/sammyfreg/netImgui/wiki
|
||||
//=================================================================================================
|
||||
#define NETIMGUI_VERSION "1.13.0" // Release of v 1.13
|
||||
#define NETIMGUI_VERSION_NUM 11300
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// 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
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Control support for native Dear ImGui texture backend support
|
||||
// At the moment, meant to always be active on recent Dear Imgui version (1.92+)
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
#ifndef NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
#ifdef IMGUI_HAS_TEXTURES
|
||||
#define NETIMGUI_IMGUI_TEXTURES_ENABLED 1
|
||||
#else
|
||||
#define NETIMGUI_IMGUI_TEXTURES_ENABLED 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if NETIMGUI_ENABLED
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//=================================================================================================
|
||||
// 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 {
|
||||
// Match Dear Imgui 1.92 'ImTextureFormat'
|
||||
kTexFmtRGBA8,
|
||||
kTexFmtA8,
|
||||
|
||||
// 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. NOTE: Not used by Dear
|
||||
//ImGui 1.92+, unneeded with font update support.
|
||||
//=================================================================================================
|
||||
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
|
||||
// Note: Can now rely on native Dear ImGui managed texture support to let the
|
||||
// system handle their
|
||||
// creation/update/destruction automatically, without needing to
|
||||
//call this function (since Dear ImGui 1.92+. See 'SampleTextures').
|
||||
//=================================================================================================
|
||||
NETIMGUI_API void SendDataTexture(ImTextureID textureId, void* pData,
|
||||
uint16_t width, uint16_t height,
|
||||
eTexFormat format, uint32_t dataSize = 0);
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
NETIMGUI_API void SendDataTexture(const ImTextureRef& textureRef, void* pData,
|
||||
uint16_t width, uint16_t height,
|
||||
eTexFormat format, uint32_t dataSize = 0);
|
||||
#endif
|
||||
|
||||
//=================================================================================================
|
||||
// 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);
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
NETIMGUI_API void SetBackground(const ImVec4& bgColor,
|
||||
const ImVec4& textureTint,
|
||||
const ImTextureRef& bgTextureRef);
|
||||
#endif
|
||||
|
||||
//=================================================================================================
|
||||
// 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);
|
||||
} // namespace NetImgui
|
||||
|
||||
//=================================================================================================
|
||||
// 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
|
||||
@@ -0,0 +1,71 @@
|
||||
#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 <imgui.h>
|
||||
|
||||
#ifdef NETIMGUI_INTERNAL_INCLUDE
|
||||
#include <imgui_internal.h> // Only needed when compiling NetImgui, not when using the NetImgui Api
|
||||
|
||||
#include "Private/NetImgui_WarningDisableImgui.h" // Disable some extra warning generated by imgui_internal in '-Wall'
|
||||
#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
|
||||
@@ -0,0 +1,948 @@
|
||||
// Google modifications:
|
||||
// - Adding missing headers.
|
||||
// - Added logging for network events.
|
||||
#include "NetImgui_Shared.h"
|
||||
#include "NetImgui_WarningDisable.h"
|
||||
|
||||
#if NETIMGUI_ENABLED
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#include "NetImgui_Client.h"
|
||||
#include "NetImgui_CmdPackets.h"
|
||||
#include "NetImgui_Network.h"
|
||||
#include "google/logging.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);
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
IM_UNUSED(FontCreateFunction);
|
||||
#else
|
||||
client.mFontCreationFunction = FontCreateFunction;
|
||||
#endif
|
||||
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);
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
IM_UNUSED(FontCreateFunction);
|
||||
#else
|
||||
client.mFontCreationFunction = FontCreateFunction;
|
||||
#endif
|
||||
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<float>(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(elapsedCheck)
|
||||
.count()) /
|
||||
1000.f;
|
||||
auto elapsedDrawMs =
|
||||
static_cast<float>(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(elapsedDraw)
|
||||
.count()) /
|
||||
1000.f;
|
||||
client.mLastOutgoingDrawCheckTime = std::chrono::steady_clock::now();
|
||||
|
||||
// Update input and see if remote netImgui expect a new frame
|
||||
client.mbValidDrawFrame = false;
|
||||
|
||||
// Take into account delay until next method call, for more precise fps
|
||||
bool shouldDraw =
|
||||
client.mDesiredFps > 0.f &&
|
||||
(elapsedDrawMs + elapsedCheckMs / 2.f) > (1000.f / client.mDesiredFps);
|
||||
|
||||
VLOG(2,
|
||||
"NewFrame: elapsedDrawMs=%.2f, elapsedCheckMs=%.2f, shouldDraw=%s, "
|
||||
"mDesiredFps=%.1f (target=%.1f)",
|
||||
elapsedDrawMs, elapsedCheckMs, shouldDraw ? "Y" : "N",
|
||||
client.mDesiredFps, 1000.f / client.mDesiredFps);
|
||||
|
||||
if (shouldDraw) {
|
||||
VLOG(2, "NewFrame: shouldDraw=Y, triggering draw!");
|
||||
|
||||
client.mLastOutgoingDrawTime = std::chrono::steady_clock::now();
|
||||
client.mbValidDrawFrame = true;
|
||||
client.mSavedDisplaySize = ImGui::GetIO().DisplaySize;
|
||||
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
client.mFontSavedScaling = ImGui::GetStyle().FontScaleDpi;
|
||||
ImGui::GetStyle().FontScaleDpi = client.mFontServerScale;
|
||||
#else
|
||||
// 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 != ConvertToClientTexID(pFonts->TexID))) {
|
||||
uint8_t* pPixelData(nullptr);
|
||||
int width(0), height(0);
|
||||
ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pPixelData, &width, &height);
|
||||
SendDataTexture(pFonts->TexID, pPixelData, static_cast<uint16_t>(width),
|
||||
static_cast<uint16_t>(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);
|
||||
#endif
|
||||
}
|
||||
|
||||
ProcessInputData(client);
|
||||
|
||||
// Update current active content with our time
|
||||
ImGui::GetIO().DeltaTime =
|
||||
std::max<float>(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;
|
||||
|
||||
// Reset Dear ImGui managed Textures status if not handled by backend, to not
|
||||
// re-process the same elements (active on 1.92+)
|
||||
client.TextureTrackingClear();
|
||||
|
||||
// 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 'EndFrame' ImGui hook, in which case
|
||||
// no need to call this again
|
||||
if (!client.mbInsideHook) {
|
||||
ImGui::Render();
|
||||
}
|
||||
|
||||
// Detect all DearImgui ImGui managed Textures waiting for updates before
|
||||
// backend process them (active on 1.92+)
|
||||
client.TextureTrackingUpdate();
|
||||
|
||||
// Prepare the Dear Imgui DrawData for later transmission to Server
|
||||
ImDrawData* imDrawData = ImGui::GetDrawData();
|
||||
client.ProcessDrawData(imDrawData, Cursor);
|
||||
|
||||
// Detect change to background settings by user, and forward them to server
|
||||
if (client.mBGSetting != client.mBGSettingSent) {
|
||||
CmdBackground* pCmdBackground = netImguiNew<CmdBackground>();
|
||||
*pCmdBackground = client.mBGSetting;
|
||||
client.mBGSettingSent = client.mBGSetting;
|
||||
client.mPendingBackgroundOut.Assign(pCmdBackground);
|
||||
}
|
||||
|
||||
if (client.mbIsRemoteDrawing) {
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
// Clear the ImGui Draw Data while leaving the texture updates intact
|
||||
// This allows the backend to process the texture updates/status normally
|
||||
// (usually done in Render backend implementation) while not displaying
|
||||
// anything.
|
||||
if (imDrawData) {
|
||||
imDrawData->CmdLists.resize(0);
|
||||
imDrawData->CmdListsCount = 0;
|
||||
imDrawData->TotalIdxCount = 0;
|
||||
imDrawData->TotalVtxCount = 0;
|
||||
}
|
||||
ImGui::GetStyle().FontScaleDpi = client.mFontSavedScaling;
|
||||
#endif
|
||||
// Restore display size, so we never lose original setting
|
||||
// that may get updated after initial connection
|
||||
ImGui::GetIO().DisplaySize = client.mSavedDisplaySize;
|
||||
}
|
||||
}
|
||||
|
||||
VLOG(2, "EndFrame: mbIsDrawing was %s, mbIsRemoteDrawing was %s",
|
||||
client.mbIsDrawing ? "Y" : "N", client.mbIsRemoteDrawing ? "Y" : "N");
|
||||
|
||||
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;
|
||||
ClientTextureID clientTexID = ConvertToClientTexID(textureId);
|
||||
|
||||
// Add/Update a texture
|
||||
if (pData != nullptr) {
|
||||
CmdTexture* pCmdTexture =
|
||||
client.TextureCmdAllocate(clientTexID, width, height, format, dataSize);
|
||||
if (pCmdTexture) {
|
||||
memcpy(pCmdTexture->mpTextureData.Get(), pData, dataSize);
|
||||
pCmdTexture->mpTextureData.ToOffset();
|
||||
client.TextureTrackingAdd(*pCmdTexture);
|
||||
|
||||
// Detects when user is sending the font texture
|
||||
#if !NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
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 = clientTexID;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// Texture to remove
|
||||
else {
|
||||
client.TextureTrackingRem(clientTexID);
|
||||
}
|
||||
}
|
||||
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
//=================================================================================================
|
||||
void SendDataTexture(const ImTextureRef& textureRef, void* pData,
|
||||
uint16_t width, uint16_t height, eTexFormat format,
|
||||
uint32_t dataSize)
|
||||
//=================================================================================================
|
||||
{
|
||||
if (!gpClientInfo) return;
|
||||
Client::ClientInfo& client = *gpClientInfo;
|
||||
ClientTextureID clientTexID = ConvertToClientTexID(textureRef);
|
||||
|
||||
// Add/Update a texture
|
||||
if (pData != nullptr) {
|
||||
CmdTexture* pCmdTexture =
|
||||
client.TextureCmdAllocate(clientTexID, width, height, format, dataSize);
|
||||
if (pCmdTexture) {
|
||||
memcpy(pCmdTexture->mpTextureData.Get(), pData, dataSize);
|
||||
pCmdTexture->mpTextureData.ToOffset();
|
||||
client.TextureTrackingAdd(*pCmdTexture);
|
||||
}
|
||||
}
|
||||
// Texture to remove
|
||||
else {
|
||||
client.TextureTrackingRem(clientTexID);
|
||||
}
|
||||
}
|
||||
#endif // NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
|
||||
//=================================================================================================
|
||||
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<ImTextureID*>(&texId64)[0] = bgTextureID;
|
||||
client.mBGSetting.mTextureId = texId64;
|
||||
}
|
||||
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
//=================================================================================================
|
||||
void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint,
|
||||
const ImTextureRef& bgTextureRef)
|
||||
//=================================================================================================
|
||||
{
|
||||
SetBackground(bgColor, textureTint, ConvertToClientTexID(bgTextureRef));
|
||||
}
|
||||
#endif
|
||||
|
||||
//=================================================================================================
|
||||
void SetCompressionMode(eCompressionMode eMode)
|
||||
//=================================================================================================
|
||||
{
|
||||
if (!gpClientInfo) return;
|
||||
|
||||
Client::ClientInfo& client = *gpClientInfo;
|
||||
client.mClientCompressionMode = static_cast<uint8_t>(eMode);
|
||||
}
|
||||
//=================================================================================================
|
||||
eCompressionMode GetCompressionMode()
|
||||
//=================================================================================================
|
||||
{
|
||||
if (!gpClientInfo) return eCompressionMode::kUseServerSetting;
|
||||
|
||||
Client::ClientInfo& client = *gpClientInfo;
|
||||
return static_cast<eCompressionMode>(client.mClientCompressionMode);
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
bool Startup(void)
|
||||
//=================================================================================================
|
||||
{
|
||||
if (!gpClientInfo) {
|
||||
gpClientInfo = netImguiNew<Client::ClientInfo>();
|
||||
}
|
||||
|
||||
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<uint32_t>(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<uint32_t>(netimguiKey) &&
|
||||
static_cast<uint32_t>(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<int>(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]);
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
client.mFontServerScale = pCmdInput->mFontDPIScaling;
|
||||
#else
|
||||
// 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) {
|
||||
io.FontGlobalScale = 1.f;
|
||||
if (abs(gpClientInfo->mFontServerScale - pCmdInput->mFontDPIScaling) >
|
||||
0.01f) {
|
||||
gpClientInfo->mFontCreationFunction(gpClientInfo->mFontSavedScaling,
|
||||
pCmdInput->mFontDPIScaling);
|
||||
client.mFontServerScale = 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;
|
||||
}
|
||||
#endif
|
||||
|
||||
#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); i < CmdInput::NetImguiMouseButton::ImGuiMouseButton_COUNT;
|
||||
++i) {
|
||||
uint64_t valMask = 0x0000000000000001ull << i;
|
||||
if ((pCmdInput->mMouseDownMask ^
|
||||
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<ImWchar>(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"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,236 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "NetImgui_CmdPackets.h"
|
||||
#include "NetImgui_Shared.h"
|
||||
|
||||
//=============================================================================
|
||||
// Forward Declares
|
||||
//=============================================================================
|
||||
namespace NetImgui {
|
||||
namespace Internal {
|
||||
namespace Network {
|
||||
struct SocketInfo;
|
||||
}
|
||||
} // namespace Internal
|
||||
} // namespace NetImgui
|
||||
|
||||
namespace NetImgui {
|
||||
namespace Internal {
|
||||
namespace Client {
|
||||
|
||||
//=============================================================================
|
||||
// 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;
|
||||
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
|
||||
#if !NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
float mFontGlobalScale = 1.f;
|
||||
float mFontGeneratedSize = 0.f;
|
||||
#endif
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Keep all Client infos needed for communication with server
|
||||
//=============================================================================
|
||||
struct ClientInfo {
|
||||
using BufferKeys = Ringbuffer<uint16_t, 1024>;
|
||||
using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
|
||||
|
||||
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;
|
||||
inline bool IsConnected() const;
|
||||
inline bool IsConnectPending() const;
|
||||
inline bool IsActive() const;
|
||||
|
||||
bool TextureTrackingAdd(CmdTexture& cmdTexture);
|
||||
bool TextureTrackingRem(ClientTextureID cmdTexture);
|
||||
void TextureTrackingClear();
|
||||
void TextureTrackingUpdate(
|
||||
bool bResendAll = false); // Process Backend ImGui textures
|
||||
CmdTexture* TextureCmdAllocate(ClientTextureID clientTexID, uint16_t width,
|
||||
uint16_t height, eTexFormat format,
|
||||
uint32_t& dataSizeInOut);
|
||||
|
||||
void TexturePendingServerAdd(
|
||||
CmdTexture& cmdTexture); // Add CmdTexture to list of command waiting for
|
||||
// send off to Server
|
||||
|
||||
void ProcessDrawData(const ImDrawData* pDearImguiData,
|
||||
ImGuiMouseCursor mouseCursor);
|
||||
|
||||
std::atomic<Network::SocketInfo*>
|
||||
mpSocketPending; // Hold socket info until communication is established
|
||||
std::atomic<Network::SocketInfo*>
|
||||
mpSocketComs; // Socket used for communications with server
|
||||
std::atomic<Network::SocketInfo*>
|
||||
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
|
||||
char mName[64] = {};
|
||||
uint64_t mFrameIndex =
|
||||
0; // Incremented every time we send a DrawFrame Command
|
||||
std::mutex mPendingTexturesLock; // Lock to prevent thread contention on the
|
||||
// list of texure cmd waiting to be sent to
|
||||
// the NetImgui Server
|
||||
CmdTexture* mPendingTextures =
|
||||
nullptr; // List of texture commands waiting to be send to Sever (single
|
||||
// linked list with oldest item at the head)
|
||||
ImVector<CmdTexture*>
|
||||
mTrackedTextures; // List of texture commands to create textures used by
|
||||
// this client (note for large texture count, should be
|
||||
// replace with a unordered_map for fast operations)
|
||||
ExchangePtr<CmdDrawFrame> mPendingFrameOut;
|
||||
ExchangePtr<CmdBackground> mPendingBackgroundOut;
|
||||
ExchangePtr<CmdInput> mPendingInputIn;
|
||||
ExchangePtr<CmdClipboard>
|
||||
mPendingClipboardIn; // Clipboard content received from Server and
|
||||
// waiting to be taken by client
|
||||
ExchangePtr<CmdClipboard>
|
||||
mPendingClipboardOut; // Clipboard content copied on Client and waiting
|
||||
// to be sent to Server
|
||||
ImGuiContext* mpContext =
|
||||
nullptr; // Context that the remote drawing should use (the one active
|
||||
// when connection request happened)
|
||||
PendingCom mPendingRcv; // Data being currently received from Server
|
||||
PendingCom mPendingSend; // Data being currently sent to Server
|
||||
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)
|
||||
SavedImguiContext mSavedContextValues; // Oiginal ImGui context values that
|
||||
// will be restored on disconnect
|
||||
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 mbTrackedTexturesPending =
|
||||
false; // True if there are some pending tracked textures waiting to be
|
||||
// removed
|
||||
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 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
|
||||
float mFontSavedScaling = 0.f; // Original Font scaling before our override
|
||||
// between NewFrame / EndFrame
|
||||
float mFontServerScale =
|
||||
1.f; // Desired Font DPI Scaling by the NetImgui Server
|
||||
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;
|
||||
int mClientTextureIDNext =
|
||||
0; // Next available ID to assign to new Dear ImGui managed textures
|
||||
int mDearImguiTextureCount =
|
||||
0; // Keep track of number of Dear ImGui managed texture added (to detect
|
||||
// when DearImgui released some)
|
||||
#if !NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
const void* mpFontTextureData =
|
||||
nullptr; // Last font texture data send to server (used to detect if font
|
||||
// was changed)
|
||||
uint64_t mFontTextureID =
|
||||
0; // Used to detect textureID change [Before ImGui 1.92, old Font Atlas]
|
||||
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. //NOTE: Unused by Dear imGui 1.92+
|
||||
bool mbFontUploaded = false; // Auto detect if font was sent to server
|
||||
#endif
|
||||
|
||||
// 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 Client
|
||||
} // namespace Internal
|
||||
} // namespace NetImgui
|
||||
|
||||
#include "NetImgui_Client.inl"
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
#include "NetImgui_Network.h"
|
||||
|
||||
namespace NetImgui { namespace Internal { namespace Client {
|
||||
|
||||
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
|
||||
@@ -0,0 +1,426 @@
|
||||
#pragma once
|
||||
|
||||
#include "NetImgui_CmdPackets_DrawFrame.h"
|
||||
#include "NetImgui_Shared.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 mSent =
|
||||
false; // True when command finished being sent to client or server
|
||||
uint8_t mPadding[2] = {};
|
||||
};
|
||||
|
||||
// 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
|
||||
ManagedTextures = 18, // Adding support for Dear Imgui Managed Textures
|
||||
// (introduced in 1.92))
|
||||
// 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<uint8_t>(sizeof(ImWchar));
|
||||
uint8_t mFlags = 0;
|
||||
uint8_t 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,
|
||||
ImGuiKey_Oem102, // Non-US backslash.
|
||||
|
||||
// // XBOX | SWITCH | PLAYSTA. | ->
|
||||
// ACTION
|
||||
ImGuiKey_GamepadStart, // Menu | + | Options |
|
||||
ImGuiKey_GamepadBack, // View | - | Share |
|
||||
ImGuiKey_GamepadFaceLeft, // X | Y | Square | Tap: Toggle
|
||||
// Menu. Hold: Windowing mode (Focus/Move/Resize
|
||||
// windows)
|
||||
ImGuiKey_GamepadFaceRight, // B | A | Circle | Cancel /
|
||||
// Close / Exit
|
||||
ImGuiKey_GamepadFaceUp, // Y | X | Triangle | Text Input /
|
||||
// On-screen Keyboard
|
||||
ImGuiKey_GamepadFaceDown, // A | B | Cross | 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 | L | L1 | Tweak Slower /
|
||||
// Focus Previous (in Windowing mode)
|
||||
ImGuiKey_GamepadR1, // R Bumper | R | R1 | Tweak Faster /
|
||||
// Focus Next (in Windowing mode)
|
||||
ImGuiKey_GamepadL2, // L Trigger | ZL | L2 | [Analog]
|
||||
ImGuiKey_GamepadR2, // R Trigger | ZR | R2 | [Analog]
|
||||
ImGuiKey_GamepadL3, // L Stick | L3 | L3 |
|
||||
ImGuiKey_GamepadR3, // R Stick | R3 | R3 |
|
||||
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 {
|
||||
enum class eType : uint8_t { Create, Update, Destroy };
|
||||
CmdTexture() : CmdHeader(CmdHeader::eCommands::Texture, sizeof(CmdTexture)) {}
|
||||
ClientTextureID mTextureClientID = 0;
|
||||
eType mStatus = eType::Create;
|
||||
uint8_t mFormat = eTexFormat::kTexFmt_Invalid; // eTexFormat
|
||||
uint8_t mUpdatable = false; // Set to true on Create, for updatable textures
|
||||
uint8_t mIsDearImGuiManaged =
|
||||
false; // True if this is not an user created/managed texture
|
||||
uint16_t mWidth =
|
||||
0; // Either the texture width on create, or the update area width
|
||||
uint16_t mHeight =
|
||||
0; // Either the texture height on create, or the update area height
|
||||
uint16_t mOffsetX = 0; // Used by partial update
|
||||
uint16_t mOffsetY = 0; // Used by partial update
|
||||
uint8_t PADDING[4] = {};
|
||||
alignas(8) CmdTexture* mpNext =
|
||||
nullptr; // Used for single linked list of pending textures (alignas
|
||||
// needed to keep class size the same between win32/x64)
|
||||
OffsetPointer<uint8_t> mpTextureData;
|
||||
};
|
||||
|
||||
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<ImguiDrawGroup> 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<char> 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 Internal
|
||||
} // namespace NetImgui
|
||||
|
||||
#include "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<sizeof(CmdBackground)/8; i++){
|
||||
sameValue &= reinterpret_cast<const uint64_t*>(this)[i] == reinterpret_cast<const uint64_t*>(&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<size_t>(clipboardByteSize, ComDataSize);
|
||||
auto pNewClipboard = NetImgui::Internal::netImguiSizedNew<CmdClipboard>(totalDataCount*ComDataSize);
|
||||
pNewClipboard->mSize = static_cast<uint32_t>(totalDataCount*ComDataSize);
|
||||
pNewClipboard->mByteSize = clipboardByteSize;
|
||||
pNewClipboard->mContentUTF8.SetPtr(reinterpret_cast<char*>(&pNewClipboard[1]));
|
||||
memcpy(pNewClipboard->mContentUTF8.Get(), clipboard, clipboardByteSize);
|
||||
return pNewClipboard;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}} // namespace NetImgui::Internal
|
||||
+477
@@ -0,0 +1,477 @@
|
||||
#include "NetImgui_Shared.h"
|
||||
|
||||
#if NETIMGUI_ENABLED
|
||||
#include "NetImgui_CmdPackets.h"
|
||||
#include "NetImgui_WarningDisable.h"
|
||||
|
||||
namespace NetImgui {
|
||||
namespace Internal {
|
||||
|
||||
template <typename TType>
|
||||
inline void SetAndIncreaseDataPointer(OffsetPointer<TType>& dataPointer,
|
||||
uint32_t dataSize,
|
||||
ComDataType*& pDataOutput) {
|
||||
dataPointer.SetComDataPtr(pDataOutput);
|
||||
const size_t dataCount = DivUp<size_t>(dataSize, ComDataSize);
|
||||
pDataOutput[dataCount - 1] = 0;
|
||||
pDataOutput += dataCount;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Safely convert a pointer to a int value, even if int storage size > pointer
|
||||
//=============================================================================
|
||||
template <typename TInt, typename TPointer>
|
||||
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<uint32_t>(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<int>(drawGroupOut.mIndiceCount); ++i)
|
||||
reinterpret_cast<uint16_t*>(drawGroupOut.mpIndices.Get())[i] =
|
||||
static_cast<uint16_t>(cmdList.IdxBuffer[i]);
|
||||
}
|
||||
// From 16bits to 32bits
|
||||
else {
|
||||
for (int i(0); i < static_cast<int>(drawGroupOut.mIndiceCount); ++i)
|
||||
reinterpret_cast<uint32_t*>(drawGroupOut.mpIndices.Get())[i] =
|
||||
static_cast<uint32_t>(cmdList.IdxBuffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
//
|
||||
//=================================================================================================
|
||||
inline void ImGui_ExtractVertices(const ImDrawList& cmdList,
|
||||
ImguiDrawGroup& drawGroupOut,
|
||||
ComDataType*& pDataOutput) {
|
||||
drawGroupOut.mVerticeCount = static_cast<uint32_t>(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 < static_cast<int>(drawGroupOut.mVerticeCount); ++i) {
|
||||
const auto& Vtx = cmdList.VtxBuffer[i];
|
||||
pVertices[i].mColor = Vtx.col;
|
||||
pVertices[i].mUV[0] = static_cast<uint16_t>(
|
||||
(Vtx.uv.x - static_cast<float>(ImguiVert::kUvRange_Min) +
|
||||
0.5f / 65535.f) *
|
||||
0xFFFF / (ImguiVert::kUvRange_Max - ImguiVert::kUvRange_Min));
|
||||
pVertices[i].mUV[1] = static_cast<uint16_t>(
|
||||
(Vtx.uv.y - static_cast<float>(ImguiVert::kUvRange_Min) +
|
||||
0.5f / 65535.f) *
|
||||
0xFFFF / (ImguiVert::kUvRange_Max - ImguiVert::kUvRange_Min));
|
||||
pVertices[i].mPos[0] = static_cast<uint16_t>(
|
||||
(Vtx.pos.x - drawGroupOut.mReferenceCoord[0] -
|
||||
static_cast<float>(ImguiVert::kPosRange_Min)) *
|
||||
0xFFFF / (ImguiVert::kPosRange_Max - ImguiVert::kPosRange_Min));
|
||||
pVertices[i].mPos[1] = static_cast<uint16_t>(
|
||||
(Vtx.pos.y - drawGroupOut.mReferenceCoord[1] -
|
||||
static_cast<float>(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<int>(cmdList.CmdBuffer.size());
|
||||
uint32_t drawCount = 0;
|
||||
ImguiDraw* pOutDraws = reinterpret_cast<ImguiDraw*>(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
|
||||
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
ClientTextureID texClientID = ConvertToClientTexID(pCmd->TexRef);
|
||||
#else
|
||||
ClientTextureID texClientID = ConvertToClientTexID(pCmd->TextureId);
|
||||
#endif
|
||||
pOutDraws[drawCount].mClientTexId = texClientID;
|
||||
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 2 data stream and output a stream with only the data difference from
|
||||
// each other
|
||||
//=================================================================================================
|
||||
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<size_t>(DivUp(dataSizePrev, sizeof(uint64_t)));
|
||||
const size_t elemCountNew =
|
||||
static_cast<size_t>(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<uint32_t*>(
|
||||
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<uint32_t>(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<uint32_t>(pCommandMemoryInOut -
|
||||
reinterpret_cast<ComDataType*>(pBlockInfo)) -
|
||||
1;
|
||||
}
|
||||
}
|
||||
|
||||
// New frame has more element than previous frame, add the remaining entries
|
||||
if (elemCount < elemCountNew) {
|
||||
uint32_t* pBlockInfo = reinterpret_cast<uint32_t*>(
|
||||
pCommandMemoryInOut++); // Add a new block info to output
|
||||
while (n < elemCountNew) {
|
||||
*pCommandMemoryInOut = pDataNew[n++];
|
||||
++pCommandMemoryInOut;
|
||||
}
|
||||
pBlockInfo[0] = 0;
|
||||
pBlockInfo[1] =
|
||||
static_cast<uint32_t>(pCommandMemoryInOut -
|
||||
reinterpret_cast<uint64_t*>(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<const uint32_t*>(
|
||||
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<size_t>(pDrawFrameNew->mSize, ComDataSize) +
|
||||
6 * static_cast<size_t>(pDrawFrameNew->mDrawGroupCount);
|
||||
CmdDrawFrame* pDrawFramePacked =
|
||||
netImguiSizedNew<CmdDrawFrame>(neededDataCount * ComDataSize);
|
||||
*pDrawFramePacked = *pDrawFrameNew;
|
||||
pDrawFramePacked->mCompressed = true;
|
||||
|
||||
ComDataType* pDataOutput =
|
||||
reinterpret_cast<ComDataType*>(&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);
|
||||
j < groupCountPrev &&
|
||||
drawGroup.mDrawGroupIdxPrev == ImguiDrawGroup::kInvalidDrawGroup;
|
||||
++j) {
|
||||
drawGroup.mDrawGroupIdxPrev =
|
||||
(drawGroup.mGroupID == pDrawFramePrev->mpDrawGroups[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<const uint64_t*>(drawGroupPrev.mpVertices.Get());
|
||||
pIndicePrev =
|
||||
reinterpret_cast<const uint64_t*>(drawGroupPrev.mpIndices.Get());
|
||||
pDrawsPrev =
|
||||
reinterpret_cast<const uint64_t*>(drawGroupPrev.mpDraws.Get());
|
||||
verticeSizePrev = drawGroupPrev.mVerticeCount * sizeof(ImguiVert);
|
||||
indiceSizePrev = drawGroupPrev.mIndiceCount *
|
||||
static_cast<size_t>(drawGroupPrev.mBytePerIndex);
|
||||
drawSizePrev = drawGroupPrev.mDrawCount * sizeof(ImguiDraw);
|
||||
}
|
||||
|
||||
drawGroup.mpIndices.SetComDataPtr(pDataOutput);
|
||||
CompressData(pIndicePrev, indiceSizePrev,
|
||||
drawGroupNew.mpIndices.GetComData(),
|
||||
drawGroupNew.mIndiceCount *
|
||||
static_cast<size_t>(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<uint32_t>(
|
||||
(pDataOutput - reinterpret_cast<ComDataType*>(pDrawFramePacked))) *
|
||||
static_cast<uint32_t>(sizeof(uint64_t));
|
||||
return pDrawFramePacked;
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
//
|
||||
//=================================================================================================
|
||||
CmdDrawFrame* DecompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev,
|
||||
const CmdDrawFrame* pDrawFramePacked) {
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// Allocate memory for the new uncompressed compressed command
|
||||
//-----------------------------------------------------------------------------------------
|
||||
CmdDrawFrame* pDrawFrameNew =
|
||||
netImguiSizedNew<CmdDrawFrame>(pDrawFramePacked->mUncompressedSize);
|
||||
*pDrawFrameNew = *pDrawFramePacked;
|
||||
pDrawFrameNew->mCompressed = false;
|
||||
ComDataType* pDataOutput = reinterpret_cast<ComDataType*>(&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<const ComDataType*>(drawGroupPrev.mpVertices.Get());
|
||||
pIndicePrev =
|
||||
reinterpret_cast<const ComDataType*>(drawGroupPrev.mpIndices.Get());
|
||||
pDrawsPrev =
|
||||
reinterpret_cast<const ComDataType*>(drawGroupPrev.mpDraws.Get());
|
||||
verticeSizePrev = drawGroupPrev.mVerticeCount * sizeof(ImguiVert);
|
||||
indiceSizePrev = drawGroupPrev.mIndiceCount *
|
||||
static_cast<size_t>(drawGroupPrev.mBytePerIndex);
|
||||
drawSizePrev = drawGroupPrev.mDrawCount * sizeof(ImguiDraw);
|
||||
}
|
||||
|
||||
drawGroup.mpIndices.SetComDataPtr(pDataOutput);
|
||||
DecompressData(pIndicePrev, indiceSizePrev,
|
||||
drawGroupPack.mpIndices.GetComData(),
|
||||
drawGroupPack.mIndiceCount *
|
||||
static_cast<size_t>(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<size_t>(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<size_t>(pCmdList->VtxBuffer.size()) * sizeof(ImguiVert),
|
||||
ComDataSize);
|
||||
neededDataCount += DivUp(
|
||||
static_cast<size_t>(pCmdList->IdxBuffer.size()) * (is16Bit ? 2 : 4),
|
||||
ComDataSize);
|
||||
neededDataCount += DivUp(
|
||||
static_cast<size_t>(pCmdList->CmdBuffer.size()) * sizeof(ImguiDraw),
|
||||
ComDataSize);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// Allocate Data and initialize general frame information
|
||||
//-----------------------------------------------------------------------------------------
|
||||
CmdDrawFrame* pDrawFrame =
|
||||
netImguiSizedNew<CmdDrawFrame>(neededDataCount * ComDataSize);
|
||||
ComDataType* pDataOutput = reinterpret_cast<ComDataType*>(&pDrawFrame[1]);
|
||||
pDrawFrame->mMouseCursor = static_cast<uint32_t>(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<uint32_t>(pDearImguiData->CmdListsCount);
|
||||
SetAndIncreaseDataPointer(pDrawFrame->mpDrawGroups,
|
||||
static_cast<uint32_t>(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<int>(n)];
|
||||
drawGroup = ImguiDrawGroup();
|
||||
drawGroup.mGroupID = PointerCast<uint64_t>(
|
||||
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<uint32_t>(pDataOutput -
|
||||
reinterpret_cast<const ComDataType*>(pDrawFrame)) *
|
||||
ComDataSize;
|
||||
pDrawFrame->mUncompressedSize =
|
||||
pDrawFrame->mSize; // No compression with this item, so same value
|
||||
return pDrawFrame;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace NetImgui
|
||||
|
||||
#include "NetImgui_WarningReenable.h"
|
||||
#endif // #if NETIMGUI_ENABLED
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
#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 {
|
||||
ClientTextureID
|
||||
mClientTexId; // TextureID used by client to identify the texture
|
||||
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<uint8_t> mpIndices;
|
||||
OffsetPointer<ImguiVert> mpVertices;
|
||||
OffsetPointer<ImguiDraw> 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 Internal
|
||||
} // namespace NetImgui
|
||||
@@ -0,0 +1,44 @@
|
||||
// Google modifications:
|
||||
// - Added #include <cstdint> for uint32_t (not implicitly available in Google)
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
namespace NetImgui {
|
||||
namespace Internal {
|
||||
struct PendingCom;
|
||||
}
|
||||
} // namespace NetImgui
|
||||
|
||||
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 Network
|
||||
} // namespace Internal
|
||||
} // namespace NetImgui
|
||||
@@ -0,0 +1,391 @@
|
||||
// Google modifications:
|
||||
// - Replaced select() with poll() to avoid FD_SETSIZE (1024) limit and
|
||||
// a SIGSEGV caused by Linux select() writing to a const timeval placed
|
||||
// in read-only memory by the compiler.
|
||||
// - Added logging for network events.
|
||||
#include "NetImgui_Shared.h"
|
||||
#include "google/logging.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable : 4221)
|
||||
#endif
|
||||
|
||||
#if NETIMGUI_ENABLED && NETIMGUI_POSIX_SOCKETS_ENABLED
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/tcp.h> // Required for TCP_NODELAY
|
||||
#include <poll.h> // Preferred over select() (no FD_SETSIZE limit)
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string> // Required for std::string / std::to_string with GCC
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "NetImgui_CmdPackets.h"
|
||||
|
||||
// NOTE: Removed static_assert(0) as requested changes are implemented below
|
||||
|
||||
namespace NetImgui {
|
||||
namespace Internal {
|
||||
namespace Network {
|
||||
|
||||
//=================================================================================================
|
||||
// Wrapper around native socket object and init some socket options
|
||||
//=================================================================================================
|
||||
struct SocketInfo {
|
||||
SocketInfo(int socket) : mSocket(socket) {
|
||||
if (mSocket != -1) {
|
||||
// Set Non-Blocking
|
||||
int flags = fcntl(mSocket, F_GETFL, 0);
|
||||
fcntl(mSocket, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
// Set TCP No Delay
|
||||
int flag = 1;
|
||||
setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int));
|
||||
|
||||
// Optional: Set Send Buffer Size (Mirroring Win32's attempt)
|
||||
// int kComsSendBuffer = 2 * mSendSizeMax;
|
||||
// setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, (char*)&kComsSendBuffer,
|
||||
// sizeof(kComsSendBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
int mSocket = -1;
|
||||
int mSendSizeMax = 1024 * 1024; // Limit tx data to avoid socket error on
|
||||
// large amount (texture) [cite: 258]
|
||||
};
|
||||
|
||||
bool Startup() {
|
||||
// No specific startup needed for POSIX sockets like WSAStartup in Winsock
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
// No specific cleanup needed for POSIX sockets like WSACleanup in Winsock
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Try establishing a connection to a remote client at given address
|
||||
// (Non-Blocking)
|
||||
//=================================================================================================
|
||||
SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort) {
|
||||
int ClientSocket = -1;
|
||||
addrinfo hints, *pResults = nullptr, *pResultCur = nullptr;
|
||||
SocketInfo* pSocketInfo = nullptr;
|
||||
char zPortName[32];
|
||||
sprintf(zPortName, "%u", ServerPort);
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (getaddrinfo(ServerHost, zPortName, &hints, &pResults) != 0) {
|
||||
return nullptr; // Failed to resolve host
|
||||
}
|
||||
|
||||
for (pResultCur = pResults; pResultCur != nullptr && pSocketInfo == nullptr;
|
||||
pResultCur = pResultCur->ai_next) {
|
||||
ClientSocket = socket(pResultCur->ai_family, pResultCur->ai_socktype,
|
||||
pResultCur->ai_protocol);
|
||||
if (ClientSocket == -1) {
|
||||
continue; // Failed to create socket for this address
|
||||
}
|
||||
|
||||
// Set non-blocking *before* connect for non-blocking connect
|
||||
int flags = fcntl(ClientSocket, F_GETFL, 0);
|
||||
fcntl(ClientSocket, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
int Result =
|
||||
connect(ClientSocket, pResultCur->ai_addr, pResultCur->ai_addrlen);
|
||||
bool Connected = (Result == 0);
|
||||
|
||||
if (Result == -1 && errno == EINPROGRESS) {
|
||||
// Connection attempt is in progress, use poll to wait
|
||||
struct pollfd pfd = {ClientSocket, POLLOUT, 0};
|
||||
Result = poll(&pfd, 1, 1000); // 1 second timeout
|
||||
|
||||
if (Result > 0) {
|
||||
// Select indicated socket is writable, check for connection errors
|
||||
int optVal;
|
||||
socklen_t optLen = sizeof(optVal);
|
||||
if (getsockopt(ClientSocket, SOL_SOCKET, SO_ERROR, &optVal, &optLen) ==
|
||||
0 &&
|
||||
optVal == 0) {
|
||||
Connected = true;
|
||||
} else {
|
||||
// Connection failed
|
||||
Connected = false;
|
||||
}
|
||||
} else {
|
||||
// Select timed out or error
|
||||
Connected = false;
|
||||
}
|
||||
} else if (Result == -1) {
|
||||
// Immediate connection error
|
||||
Connected = false;
|
||||
}
|
||||
|
||||
if (Connected) {
|
||||
pSocketInfo = netImguiNew<SocketInfo>(ClientSocket);
|
||||
// Socket is already non-blocking from the SocketInfo constructor
|
||||
} else if (ClientSocket != -1) {
|
||||
close(ClientSocket); // Close socket if connection failed
|
||||
ClientSocket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(pResults);
|
||||
|
||||
if (!pSocketInfo && ClientSocket != -1) {
|
||||
close(ClientSocket); // Clean up socket if loop finished without success
|
||||
}
|
||||
|
||||
return pSocketInfo;
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Start waiting for connection request on this socket
|
||||
//=================================================================================================
|
||||
SocketInfo* ListenStart(uint32_t ListenPort) {
|
||||
addrinfo hints, *addrInfo;
|
||||
int ListenSocket = -1;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_INET; // Typically listen on IPv4 for simplicity, or
|
||||
// AF_UNSPEC for both
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE; // Use my IP
|
||||
|
||||
std::string portStr = std::to_string(ListenPort);
|
||||
if (getaddrinfo(nullptr, portStr.c_str(), &hints, &addrInfo) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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));
|
||||
#ifdef SO_REUSEPORT // SO_REUSEPORT might not be available on all POSIX systems
|
||||
setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (bind(ListenSocket, addrInfo->ai_addr, addrInfo->ai_addrlen) != -1 &&
|
||||
listen(ListenSocket, SOMAXCONN) != -1) // Use SOMAXCONN for backlog
|
||||
{
|
||||
// Keep listening socket blocking for accept() simplicity,
|
||||
// the *accepted* socket will be non-blocking via SocketInfo ctor
|
||||
int flags = fcntl(ListenSocket, F_GETFL, 0);
|
||||
fcntl(ListenSocket, F_SETFL, flags & (~O_NONBLOCK)); // Ensure blocking
|
||||
|
||||
freeaddrinfo(addrInfo);
|
||||
// Note: We create SocketInfo wrapper here just for consistency,
|
||||
// but it doesn't set options on the *listening* socket.
|
||||
return netImguiNew<SocketInfo>(ListenSocket);
|
||||
}
|
||||
close(ListenSocket);
|
||||
}
|
||||
|
||||
freeaddrinfo(addrInfo);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Accept a new connection (blocking call on ListenSocket)
|
||||
//=================================================================================================
|
||||
SocketInfo* ListenConnect(SocketInfo* pListenSocket) {
|
||||
if (pListenSocket && pListenSocket->mSocket != -1) {
|
||||
sockaddr_storage ClientAddress;
|
||||
socklen_t Size = sizeof(ClientAddress);
|
||||
// ListenSocket should be blocking (set in ListenStart)
|
||||
int ServerSocket =
|
||||
accept(pListenSocket->mSocket, (sockaddr*)&ClientAddress, &Size);
|
||||
if (ServerSocket != -1) {
|
||||
VLOG(1, "TCP connection accepted (fd=%d)", ServerSocket);
|
||||
// Create SocketInfo wrapper, which sets the new socket to non-blocking
|
||||
return netImguiNew<SocketInfo>(ServerSocket);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Close a connection and free allocated object
|
||||
//=================================================================================================
|
||||
void Disconnect(SocketInfo* pClientSocket) {
|
||||
if (pClientSocket && pClientSocket->mSocket != -1) {
|
||||
// Set SO_LINGER option to force close and discard pending data
|
||||
// to ensure the socket is closed immediately and exits the CLOSE_WAIT state
|
||||
// more reliably
|
||||
struct linger sl;
|
||||
sl.l_onoff = 1; // Enable linger
|
||||
sl.l_linger = 0; // Set timeout to 0 seconds (force RST)
|
||||
setsockopt(pClientSocket->mSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));
|
||||
|
||||
shutdown(pClientSocket->mSocket, SHUT_RDWR);
|
||||
close(pClientSocket->mSocket);
|
||||
pClientSocket->mSocket = -1; // Mark as closed
|
||||
}
|
||||
netImguiDelete(pClientSocket);
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Return true if data has been received, or there's a connection error
|
||||
//=================================================================================================
|
||||
bool DataReceivePending(SocketInfo* pClientSocket) {
|
||||
if (!pClientSocket || pClientSocket->mSocket == -1) {
|
||||
return true; // Error condition
|
||||
}
|
||||
|
||||
// Use poll() instead of select() to avoid FD_SETSIZE limitation.
|
||||
struct pollfd pfd = {pClientSocket->mSocket, POLLIN, 0};
|
||||
int result = poll(&pfd, 1, 0); // 0ms timeout = non-blocking check
|
||||
|
||||
// poll() syscall error (e.g. EINTR) — treat as "no data" and retry next
|
||||
// frame rather than speculatively calling recv() on unknown socket state.
|
||||
if (result < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No events on the socket.
|
||||
if (result == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// result > 0: the socket has events.
|
||||
if (pfd.revents & POLLERR) {
|
||||
// Report as data-pending so the caller's recv() surfaces the error
|
||||
// through its own error-handling path. Info, not Error: this is routine
|
||||
// when the peer tears the connection down (e.g. viewer shutdown).
|
||||
VLOG(1, "DataReceivePending: POLLERR revents=%d", pfd.revents);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pfd.revents & POLLHUP) {
|
||||
// Peer hung up. The persistent TCP proxy keeps connections alive across
|
||||
// browser refreshes, so a transient POLLHUP does not mean the session is
|
||||
// over — ignore it.
|
||||
VLOG(1, "DataReceivePending: POLLHUP revents=%d", pfd.revents);
|
||||
}
|
||||
|
||||
// Only signal data-pending when the kernel has bytes ready to read.
|
||||
return (pfd.revents & POLLIN) != 0;
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Receive as much as possible into PendingCom buffer (Non-Blocking)
|
||||
//=================================================================================================
|
||||
void DataReceive(SocketInfo* pClientSocket,
|
||||
NetImgui::Internal::PendingCom& PendingComRcv) {
|
||||
// Invalid command or socket state
|
||||
if (!pClientSocket || pClientSocket->mSocket == -1 ||
|
||||
!PendingComRcv.pCommand) {
|
||||
PendingComRcv.bError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t BytesToRead =
|
||||
PendingComRcv.pCommand->mSize - PendingComRcv.SizeCurrent;
|
||||
if (BytesToRead == 0) {
|
||||
return; // Already fully received
|
||||
}
|
||||
|
||||
// Receive data from remote connection (non-blocking)
|
||||
ssize_t resultRcv =
|
||||
recv(pClientSocket->mSocket,
|
||||
&reinterpret_cast<uint8_t*>(
|
||||
PendingComRcv.pCommand)[PendingComRcv.SizeCurrent],
|
||||
BytesToRead,
|
||||
0); // No flags, non-blocking behavior comes from socket setting
|
||||
|
||||
if (resultRcv > 0) {
|
||||
// Successfully received some data
|
||||
PendingComRcv.SizeCurrent += static_cast<size_t>(resultRcv);
|
||||
PendingComRcv.bError = false; // Reset error flag on successful read
|
||||
} else if (resultRcv == 0) {
|
||||
// Connection closed gracefully by peer
|
||||
PendingComRcv.bError = true;
|
||||
VLOG(1, "DataReceive: Connection closed gracefully");
|
||||
} else { // resultRcv < 0
|
||||
// Error occurred
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
// Not an error, just no data available right now on non-blocking socket
|
||||
PendingComRcv.bError = false;
|
||||
} else {
|
||||
// Actual socket error
|
||||
PendingComRcv.bError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Send as much as possible from PendingCom buffer (Non-Blocking)
|
||||
//=================================================================================================
|
||||
void DataSend(SocketInfo* pClientSocket,
|
||||
NetImgui::Internal::PendingCom& PendingComSend) {
|
||||
// Invalid command or socket state
|
||||
if (!pClientSocket || pClientSocket->mSocket == -1 ||
|
||||
!PendingComSend.pCommand) {
|
||||
PendingComSend.bError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t BytesRemaining =
|
||||
PendingComSend.pCommand->mSize - PendingComSend.SizeCurrent;
|
||||
if (BytesRemaining == 0) {
|
||||
return; // Already fully sent
|
||||
}
|
||||
|
||||
// Limit send size per call [cite: 281]
|
||||
size_t BytesToSend =
|
||||
BytesRemaining > static_cast<size_t>(pClientSocket->mSendSizeMax)
|
||||
? static_cast<size_t>(pClientSocket->mSendSizeMax)
|
||||
: BytesRemaining;
|
||||
|
||||
// Send data to remote connection (non-blocking)
|
||||
ssize_t resultSent =
|
||||
send(pClientSocket->mSocket,
|
||||
&reinterpret_cast<const uint8_t*>(
|
||||
PendingComSend.pCommand)[PendingComSend.SizeCurrent],
|
||||
BytesToSend,
|
||||
MSG_NOSIGNAL); // Use MSG_NOSIGNAL to prevent SIGPIPE on Linux if
|
||||
// connection is broken
|
||||
|
||||
if (resultSent > 0) {
|
||||
// Successfully sent some data
|
||||
PendingComSend.SizeCurrent += static_cast<size_t>(resultSent);
|
||||
PendingComSend.bError = false; // Reset error flag on successful send
|
||||
} else if (resultSent == 0) {
|
||||
// This shouldn't typically happen with TCP unless BytesToSend was 0
|
||||
PendingComSend.bError = false; // Treat as non-error for now
|
||||
} else { // resultSent < 0
|
||||
// Error occurred
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
// Not an error, socket buffer is full, try again later
|
||||
PendingComSend.bError = false;
|
||||
} else {
|
||||
// Actual socket error (e.g., EPIPE if connection broken and MSG_NOSIGNAL
|
||||
// not used/supported)
|
||||
PendingComSend.bError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Network
|
||||
} // namespace Internal
|
||||
} // namespace NetImgui
|
||||
#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
|
||||
@@ -0,0 +1,258 @@
|
||||
#include "NetImgui_Shared.h"
|
||||
|
||||
#if NETIMGUI_ENABLED && NETIMGUI_WINSOCKET_ENABLED
|
||||
#include <WS2tcpip.h>
|
||||
#include <WinSock2.h>
|
||||
|
||||
#include "NetImgui_WarningDisableStd.h"
|
||||
|
||||
#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<long>(FIONBIO), &kNonBlocking);
|
||||
|
||||
constexpr DWORD kComsNoDelay = 1;
|
||||
setsockopt(mSocket, SOL_SOCKET, TCP_NODELAY,
|
||||
reinterpret_cast<const char*>(&kComsNoDelay),
|
||||
sizeof(kComsNoDelay));
|
||||
|
||||
const int kComsSendBuffer = 2 * mSendSizeMax;
|
||||
setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF,
|
||||
reinterpret_cast<const char*>(&kComsSendBuffer),
|
||||
sizeof(kComsSendBuffer));
|
||||
|
||||
// constexpr int kComsRcvBuffer = 1014*1024;
|
||||
// setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const
|
||||
// char*>(&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<long>(FIONBIO), &kNonBlocking);
|
||||
while (pResultCur && !pSocketInfo) {
|
||||
int Result = connect(ClientSocket, pResultCur->ai_addr,
|
||||
static_cast<int>(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<SocketInfo>(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<USHORT>(ListenPort));
|
||||
|
||||
#if NETIMGUI_FORCE_TCP_LISTEN_BINDING
|
||||
constexpr BOOL ReUseAdrValue(true);
|
||||
setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char*>(&ReUseAdrValue),
|
||||
sizeof(ReUseAdrValue));
|
||||
#endif
|
||||
if (bind(ListenSocket, reinterpret_cast<sockaddr*>(&server),
|
||||
sizeof(server)) != SOCKET_ERROR &&
|
||||
listen(ListenSocket, 0) != SOCKET_ERROR) {
|
||||
u_long kIsNonBlocking = false;
|
||||
ioctlsocket(ListenSocket, static_cast<long>(FIONBIO), &kIsNonBlocking);
|
||||
return netImguiNew<SocketInfo>(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<SocketInfo>(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<char*>(
|
||||
PendingComRcv.pCommand)[PendingComRcv.SizeCurrent],
|
||||
static_cast<int>(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<size_t>(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<int>(PendingComSend.pCommand->mSize -
|
||||
PendingComSend.SizeCurrent);
|
||||
sizeToSend = sizeToSend > pClientSocket->mSendSizeMax
|
||||
? pClientSocket->mSendSizeMax
|
||||
: sizeToSend;
|
||||
int resultSent =
|
||||
send(pClientSocket->mSocket,
|
||||
&reinterpret_cast<char*>(
|
||||
PendingComSend.pCommand)[PendingComSend.SizeCurrent],
|
||||
sizeToSend, 0);
|
||||
|
||||
if (resultSent != SOCKET_ERROR) {
|
||||
PendingComSend.SizeCurrent += static_cast<size_t>(resultSent);
|
||||
}
|
||||
// Connection error, abort transmission
|
||||
else if (WSAGetLastError() != WSAEWOULDBLOCK) {
|
||||
PendingComSend.bError = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Network
|
||||
} // namespace Internal
|
||||
} // namespace NetImgui
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,205 @@
|
||||
#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 <atomic>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "NetImgui_WarningDisableStd.h"
|
||||
#include "NetImgui_WarningReenable.h"
|
||||
//=================================================================================================
|
||||
|
||||
//=================================================================================================
|
||||
#include "NetImgui_WarningDisable.h"
|
||||
namespace NetImgui {
|
||||
namespace Internal {
|
||||
|
||||
using ComDataType = uint64_t;
|
||||
constexpr size_t ComDataSize = sizeof(ComDataType);
|
||||
using ClientTextureID = uint64_t;
|
||||
|
||||
//=============================================================================
|
||||
// All allocations made by netImgui goes through here.
|
||||
// Relies in ImGui allocator
|
||||
//=============================================================================
|
||||
template <typename TType, typename... Args>
|
||||
TType* netImguiNew(Args... args);
|
||||
template <typename TType>
|
||||
TType* netImguiSizedNew(size_t placementSize);
|
||||
template <typename TType>
|
||||
void netImguiDelete(TType* pData);
|
||||
template <typename TType>
|
||||
void netImguiDeleteSafe(TType*& pData);
|
||||
|
||||
class ScopedImguiContext {
|
||||
public:
|
||||
ScopedImguiContext(ImGuiContext* pNewContext)
|
||||
: mpSavedContext(ImGui::GetCurrentContext()) {
|
||||
ImGui::SetCurrentContext(pNewContext);
|
||||
}
|
||||
~ScopedImguiContext() { ImGui::SetCurrentContext(mpSavedContext); }
|
||||
|
||||
protected:
|
||||
ImGuiContext* mpSavedContext;
|
||||
};
|
||||
|
||||
template <typename TType>
|
||||
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<bool>;
|
||||
|
||||
//=============================================================================
|
||||
// Class to safely exchange a pointer between two threads
|
||||
//=============================================================================
|
||||
template <typename TType>
|
||||
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<TType*> 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 <typename TType>
|
||||
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 <typename TType, size_t TCount>
|
||||
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 <typename T, std::size_t N>
|
||||
constexpr std::size_t ArrayCount(T const (&)[N]) noexcept {
|
||||
return N;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
template <size_t charCount>
|
||||
inline void StringCopy(char (&output)[charCount], const char* pSrc,
|
||||
size_t srcCharCount = 0xFFFFFFFE);
|
||||
|
||||
template <size_t charCount>
|
||||
int StringFormat(char (&output)[charCount], char const* const format, ...);
|
||||
|
||||
//=============================================================================
|
||||
// Get the (value / Denominator) rounded up to the next int value big enough
|
||||
//=============================================================================
|
||||
template <typename IntType>
|
||||
IntType DivUp(IntType Value, IntType Denominator);
|
||||
|
||||
//=============================================================================
|
||||
// Get the rounded up value
|
||||
//=============================================================================
|
||||
template <typename IntType>
|
||||
IntType RoundUp(IntType Value, IntType Round);
|
||||
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
inline NetImgui::eTexFormat ConvertTextureFormat(ImTextureFormat ImFormat);
|
||||
inline ClientTextureID ConvertToClientTexID(const ImTextureRef& textureRef);
|
||||
#endif
|
||||
inline ClientTextureID ConvertToClientTexID(ImTextureID textureID);
|
||||
inline ImTextureID ConvertFromClientTexID(ClientTextureID textureID);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace NetImgui
|
||||
|
||||
#include "NetImgui_Shared.inl"
|
||||
#include "NetImgui_WarningReenable.h"
|
||||
|
||||
#endif // NETIMGUI_ENABLED
|
||||
@@ -0,0 +1,341 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace NetImgui { namespace Internal
|
||||
{
|
||||
|
||||
template <typename TType, typename... Args>
|
||||
TType* netImguiNew(Args... args)
|
||||
{
|
||||
return new( ImGui::MemAlloc(sizeof(TType)) ) TType(args...);
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
TType* netImguiSizedNew(size_t placementSize)
|
||||
{
|
||||
return new( ImGui::MemAlloc(placementSize > sizeof(TType) ? placementSize : sizeof(TType)) ) TType();
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
void netImguiDelete(TType* pData)
|
||||
{
|
||||
if( pData )
|
||||
{
|
||||
pData->~TType();
|
||||
ImGui::MemFree(pData);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
void netImguiDeleteSafe(TType*& pData)
|
||||
{
|
||||
netImguiDelete(pData);
|
||||
pData = nullptr;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Acquire ownership of the resource
|
||||
//=============================================================================
|
||||
template <typename TType>
|
||||
TType* ExchangePtr<TType>::Release()
|
||||
{
|
||||
return mpData.exchange(nullptr);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Take ownership of the provided data.
|
||||
// If there's a previous unclaimed pointer to some data, release it
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename TType>
|
||||
void ExchangePtr<TType>::Assign(TType*& pNewData)
|
||||
{
|
||||
netImguiDelete( mpData.exchange(pNewData) );
|
||||
pNewData = nullptr;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
void ExchangePtr<TType>::Free()
|
||||
{
|
||||
TType* pNull(nullptr);
|
||||
Assign(pNull);
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
ExchangePtr<TType>::~ExchangePtr()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//=============================================================================
|
||||
template <typename TType>
|
||||
OffsetPointer<TType>::OffsetPointer()
|
||||
: mOffset(0)
|
||||
{
|
||||
SetOff(0);
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
OffsetPointer<TType>::OffsetPointer(TType* pPointer)
|
||||
{
|
||||
SetPtr(pPointer);
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
OffsetPointer<TType>::OffsetPointer(uint64_t offset)
|
||||
{
|
||||
SetOff(offset);
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
void OffsetPointer<TType>::SetPtr(TType* pPointer)
|
||||
{
|
||||
mOffset = 0; // Remove 'offset flag' that can be left active on non 64bits pointer
|
||||
mPointer = pPointer;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
void OffsetPointer<TType>::SetComDataPtr(ComDataType* pPointer)
|
||||
{
|
||||
SetPtr(reinterpret_cast<TType*>(pPointer));
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
void OffsetPointer<TType>::SetOff(uint64_t offset)
|
||||
{
|
||||
mOffset = offset | 0x0000000000000001u;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
uint64_t OffsetPointer<TType>::GetOff()const
|
||||
{
|
||||
return mOffset & ~0x0000000000000001u;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
bool OffsetPointer<TType>::IsOffset()const
|
||||
{
|
||||
return (mOffset & 0x0000000000000001u) != 0;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
bool OffsetPointer<TType>::IsPointer()const
|
||||
{
|
||||
return !IsOffset();
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
TType* OffsetPointer<TType>::ToPointer()
|
||||
{
|
||||
assert(IsOffset());
|
||||
SetPtr( reinterpret_cast<TType*>( reinterpret_cast<uint64_t>(&mPointer) + GetOff() ) );
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
uint64_t OffsetPointer<TType>::ToOffset()
|
||||
{
|
||||
assert(IsPointer());
|
||||
SetOff( reinterpret_cast<uint64_t>(mPointer) - reinterpret_cast<uint64_t>(&mPointer) );
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
TType* OffsetPointer<TType>::operator->()
|
||||
{
|
||||
assert(IsPointer());
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
const TType* OffsetPointer<TType>::operator->()const
|
||||
{
|
||||
assert(IsPointer());
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
TType* OffsetPointer<TType>::Get()
|
||||
{
|
||||
assert(IsPointer());
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
const TType* OffsetPointer<TType>::Get()const
|
||||
{
|
||||
assert(IsPointer());
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
const ComDataType* OffsetPointer<TType>::GetComData()const
|
||||
{
|
||||
return reinterpret_cast<const ComDataType*>(Get());
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
TType& OffsetPointer<TType>::operator[](size_t index)
|
||||
{
|
||||
assert(IsPointer());
|
||||
return mPointer[index];
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
const TType& OffsetPointer<TType>::operator[](size_t index)const
|
||||
{
|
||||
assert(IsPointer());
|
||||
return mPointer[index];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
template <typename TType, size_t TCount>
|
||||
void Ringbuffer<TType,TCount>::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 <typename TType, size_t TCount>
|
||||
bool Ringbuffer<TType,TCount>::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 <size_t charCount>
|
||||
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 <size_t charCount>
|
||||
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 <typename IntType>
|
||||
IntType DivUp(IntType Value, IntType Denominator)
|
||||
{
|
||||
return (Value + Denominator - 1) / Denominator;
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
IntType RoundUp(IntType Value, IntType Round)
|
||||
{
|
||||
return DivUp(Value, Round) * Round;
|
||||
}
|
||||
|
||||
union TextureCastHelperUnion
|
||||
{
|
||||
const void* TexData;
|
||||
ImTextureID TexID;
|
||||
ClientTextureID TexClientID;
|
||||
};
|
||||
|
||||
#if NETIMGUI_IMGUI_TEXTURES_ENABLED
|
||||
NetImgui::eTexFormat ConvertTextureFormat(ImTextureFormat ImFormat)
|
||||
{
|
||||
switch(ImFormat)
|
||||
{
|
||||
case ImTextureFormat_RGBA32: return eTexFormat::kTexFmtRGBA8;
|
||||
case ImTextureFormat_Alpha8: return eTexFormat::kTexFmtA8;
|
||||
}
|
||||
return eTexFormat::kTexFmtRGBA8;
|
||||
}
|
||||
|
||||
ClientTextureID ConvertToClientTexID(const ImTextureRef& textureRef)
|
||||
{
|
||||
static_assert(sizeof(uint64_t) >= sizeof(ImTextureID), "ImTextureID is bigger than 64bits, CmdTexture::mTextureId needs to be updated to support it");
|
||||
TextureCastHelperUnion textureUnion;
|
||||
textureUnion.TexClientID = 0;
|
||||
if( textureRef._TexData ){
|
||||
//Note: Cannot rely on textureRef.GetTexID() because it uses a pointer to
|
||||
// a texture object that might not have been created by the backend yet.
|
||||
// Instead, use a stable value that remain valid for texture lifetime,
|
||||
// to id it with the NetImgui Server
|
||||
textureUnion.TexClientID = static_cast<ClientTextureID>(textureRef._TexData->UniqueID);
|
||||
}
|
||||
else{
|
||||
textureUnion.TexID = textureRef._TexID;
|
||||
}
|
||||
return textureUnion.TexClientID;
|
||||
}
|
||||
#endif
|
||||
|
||||
ClientTextureID ConvertToClientTexID(ImTextureID textureID)
|
||||
{
|
||||
static_assert(sizeof(uint64_t) >= sizeof(ImTextureID), "ImTextureID is bigger than 64bits, CmdTexture::mTextureId needs to be updated to support it");
|
||||
TextureCastHelperUnion textureUnion;
|
||||
textureUnion.TexClientID = 0;
|
||||
textureUnion.TexID = textureID;
|
||||
return textureUnion.TexClientID;
|
||||
|
||||
}
|
||||
|
||||
ImTextureID ConvertFromClientTexID(ClientTextureID clientTexID)
|
||||
{
|
||||
TextureCastHelperUnion textureUnion;
|
||||
textureUnion.TexClientID = clientTexID;
|
||||
return textureUnion.TexID;
|
||||
}
|
||||
|
||||
}} //namespace NetImgui::Internal
|
||||
@@ -0,0 +1,52 @@
|
||||
#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 "-Wunknown-warning-option"
|
||||
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // For ImTextureID_Invalid
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||
#pragma clang diagnostic ignored "-Wswitch-default"
|
||||
#pragma clang diagnostic ignored \
|
||||
"-Wnontrivial-memcall" // For ImGui::IO memcpy warning
|
||||
|
||||
//=================================================================================================
|
||||
// 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 <atomic>
|
||||
#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
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#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" // Enum 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::<unnamed-tag>::val_p' is
|
||||
// uninitialized. Always initialize a member variable (type.6).
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#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
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
//=================================================================================================
|
||||
// Clang
|
||||
//=================================================================================================
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
//=================================================================================================
|
||||
// Visual Studio warnings
|
||||
//=================================================================================================
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
Reference in New Issue
Block a user