Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
b47e70849This file is pinned to the selected release. Download the original to open it in your design tool.
// 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"