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:
Matija Kecman
2026-07-27 12:31:15 -07:00
committed by Copybara-Service
parent c7b6e0b8dd
commit ca5337161d
40 changed files with 15483 additions and 0 deletions
@@ -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
@@ -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
@@ -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
@@ -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