mirror of
https://github.com/Ed94/Cog.git
synced 2026-06-22 12:04:59 -07:00
796 lines
33 KiB
C++
796 lines
33 KiB
C++
#include "NetImgui_Shared.h"
|
|
#include "NetImgui_WarningDisable.h"
|
|
|
|
#if NETIMGUI_ENABLED
|
|
#include <algorithm>
|
|
#include <thread>
|
|
#include "NetImgui_Client.h"
|
|
#include "NetImgui_Network.h"
|
|
#include "NetImgui_CmdPackets.h"
|
|
|
|
using namespace NetImgui::Internal;
|
|
|
|
namespace NetImgui {
|
|
|
|
static Client::ClientInfo* gpClientInfo = nullptr;
|
|
|
|
bool ProcessInputData(Client::ClientInfo& client);
|
|
|
|
//=================================================================================================
|
|
void DefaultStartCommunicationThread(void ComFunctPtr(void*), void* pClient)
|
|
//=================================================================================================
|
|
{
|
|
// Visual Studio 2017 generate this warning on std::thread, avoid the warning preventing build
|
|
#if defined(_MSC_VER) && (_MSC_VER < 1920)
|
|
#pragma warning (push)
|
|
#pragma warning (disable: 4625) // 'std::_LaunchPad<_Target>' : copy constructor was implicitly defined as deleted
|
|
#pragma warning (disable: 4626) // 'std::_LaunchPad<_Target>' : assignment operator was implicitly defined as deleted
|
|
#endif
|
|
|
|
std::thread(ComFunctPtr, pClient).detach();
|
|
|
|
#if defined(_MSC_VER) && (_MSC_VER < 1920)
|
|
#pragma warning (pop)
|
|
#endif
|
|
}
|
|
|
|
|
|
//=================================================================================================
|
|
bool ConnectToApp(const char* clientName, const char* ServerHost, uint32_t serverPort, ThreadFunctPtr threadFunction, FontCreateFuncPtr FontCreateFunction)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return false;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
Disconnect();
|
|
|
|
while (client.IsActive())
|
|
std::this_thread::yield();
|
|
|
|
client.ContextRestore(); // Restore context setting override, after a disconnect
|
|
client.ContextRemoveHooks(); // Remove hooks callback only when completely disconnected
|
|
|
|
StringCopy(client.mName, (clientName == nullptr || clientName[0] == 0 ? "Unnamed" : clientName));
|
|
client.mpSocketPending = Network::Connect(ServerHost, serverPort);
|
|
client.mFontCreationFunction = FontCreateFunction;
|
|
if (client.mpSocketPending.load() != nullptr)
|
|
{
|
|
client.ContextInitialize();
|
|
threadFunction = threadFunction == nullptr ? DefaultStartCommunicationThread : threadFunction;
|
|
threadFunction(Client::CommunicationsConnect, &client);
|
|
}
|
|
|
|
return client.IsActive();
|
|
}
|
|
|
|
//=================================================================================================
|
|
bool ConnectFromApp(const char* clientName, uint32_t serverPort, ThreadFunctPtr threadFunction, FontCreateFuncPtr FontCreateFunction)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return false;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
Disconnect();
|
|
|
|
while (client.IsActive())
|
|
std::this_thread::yield();
|
|
|
|
client.ContextRestore(); // Restore context setting override, after a disconnect
|
|
client.ContextRemoveHooks(); // Remove hooks callback only when completly disconnected
|
|
|
|
StringCopy(client.mName, (clientName == nullptr || clientName[0] == 0 ? "Unnamed" : clientName));
|
|
client.mpSocketPending = Network::ListenStart(serverPort);
|
|
client.mFontCreationFunction = FontCreateFunction;
|
|
client.mThreadFunction = (threadFunction == nullptr) ? DefaultStartCommunicationThread : threadFunction;
|
|
if (client.mpSocketPending.load() != nullptr)
|
|
{
|
|
client.ContextInitialize();
|
|
client.mSocketListenPort = serverPort;
|
|
client.mThreadFunction(Client::CommunicationsHost, &client);
|
|
}
|
|
|
|
return client.IsActive();
|
|
}
|
|
|
|
//=================================================================================================
|
|
void Disconnect(void)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return;
|
|
|
|
// Attempt fake connection on local socket waiting for a Server connection,
|
|
// so the blocking operation can terminate and release the communication thread
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
client.mbDisconnectPending = true;
|
|
client.mbDisconnectListen = true;
|
|
if( client.mpSocketListen.load() != nullptr && client.mSocketListenPort != 0 )
|
|
{
|
|
Network::SocketInfo* pFakeSocket = Network::Connect("127.0.0.1", client.mSocketListenPort);
|
|
client.mSocketListenPort = 0;
|
|
client.mbDisconnectPending = true;
|
|
|
|
if(pFakeSocket){
|
|
Network::Disconnect(pFakeSocket);
|
|
}
|
|
}
|
|
|
|
// Wait for connection attempt to complete and fail
|
|
while( client.mbComInitActive || client.mbClientThreadActive );
|
|
|
|
// If fake connection to exit Listening failed, force disconnect socket directly
|
|
// even though it might potentially cause a race condition
|
|
Network::SocketInfo* pListenSocket = client.mpSocketListen.exchange(nullptr);
|
|
if( pListenSocket ){
|
|
Network::Disconnect(pListenSocket);
|
|
}
|
|
|
|
Network::SocketInfo* pPendingSocket = client.mpSocketPending.exchange(nullptr);
|
|
if( pPendingSocket ){
|
|
Network::Disconnect(pPendingSocket);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
bool IsConnected(void)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return false;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
|
|
// If disconnected in middle of a remote frame drawing,
|
|
// want to behave like it is still connected to finish frame properly
|
|
return client.IsConnected() || IsDrawingRemote();
|
|
}
|
|
|
|
//=================================================================================================
|
|
bool IsConnectionPending(void)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return false;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
return client.IsConnectPending();
|
|
}
|
|
|
|
//=================================================================================================
|
|
bool IsDrawing(void)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return false;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
return client.mbIsDrawing;
|
|
}
|
|
|
|
//=================================================================================================
|
|
bool IsDrawingRemote(void)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return false;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
return IsDrawing() && client.mbIsRemoteDrawing;
|
|
}
|
|
|
|
//=================================================================================================
|
|
bool NewFrame(bool bSupportFrameSkip)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo || gpClientInfo->mbIsDrawing) return false;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
ScopedBool scopedInside(client.mbInsideNewEnd, true);
|
|
|
|
// ImGui Newframe handled by remote connection settings
|
|
if( NetImgui::IsConnected() )
|
|
{
|
|
ImGui::SetCurrentContext(client.mpContext);
|
|
|
|
// Save current context settings and override settings to fit our netImgui usage
|
|
if (!client.IsContextOverriden() )
|
|
{
|
|
client.ContextOverride();
|
|
}
|
|
|
|
auto elapsedCheck = std::chrono::steady_clock::now() - client.mLastOutgoingDrawCheckTime;
|
|
auto elapsedDraw = std::chrono::steady_clock::now() - client.mLastOutgoingDrawTime;
|
|
auto elapsedCheckMs = static_cast<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.mSavedDisplaySize = ImGui::GetIO().DisplaySize;
|
|
client.mbValidDrawFrame = false;
|
|
|
|
// Take into account delay until next method call, for more precise fps
|
|
if( client.mDesiredFps > 0.f && (elapsedDrawMs + elapsedCheckMs/2.f) > (1000.f/client.mDesiredFps) )
|
|
{
|
|
client.mLastOutgoingDrawTime = std::chrono::steady_clock::now();
|
|
client.mbValidDrawFrame = true;
|
|
}
|
|
|
|
ProcessInputData(client);
|
|
|
|
// We are about to start drawing for remote context, check for font data update
|
|
const ImFontAtlas* pFonts = ImGui::GetIO().Fonts;
|
|
if( pFonts->TexPixelsAlpha8 &&
|
|
(pFonts->TexPixelsAlpha8 != client.mpFontTextureData || client.mFontTextureID != pFonts->TexID ))
|
|
{
|
|
uint8_t* pPixelData(nullptr); int width(0), height(0);
|
|
ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pPixelData, &width, &height);
|
|
SendDataTexture(pFonts->TexID, pPixelData, static_cast<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);
|
|
|
|
// 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;
|
|
|
|
// This function can be called from a 'NewFrame' ImGui hook, we should not start a new frame again
|
|
if (!client.mbInsideHook)
|
|
{
|
|
ImGui::NewFrame();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//=================================================================================================
|
|
void EndFrame(void)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
ScopedBool scopedInside(client.mbInsideNewEnd, true);
|
|
|
|
if ( client.mbIsDrawing )
|
|
{
|
|
// Must be fetched before 'Render'
|
|
ImGuiMouseCursor Cursor = ImGui::GetMouseCursor();
|
|
|
|
// This function can be called from a 'NewFrame' ImGui hook, in which case no need to call this again
|
|
if( !client.mbInsideHook ){
|
|
ImGui::Render();
|
|
}
|
|
|
|
// Prepare the Dear Imgui DrawData for later tranmission to Server
|
|
client.ProcessDrawData(ImGui::GetDrawData(), Cursor);
|
|
|
|
// Detect change to background settings by user, and forward them to server
|
|
if( client.mBGSetting != client.mBGSettingSent )
|
|
{
|
|
CmdBackground* pCmdBackground = netImguiNew<CmdBackground>();
|
|
*pCmdBackground = client.mBGSetting;
|
|
client.mBGSettingSent = client.mBGSetting;
|
|
client.mPendingBackgroundOut.Assign(pCmdBackground);
|
|
}
|
|
|
|
// Restore display size, so we never lose original setting that may get updated after initial connection
|
|
if( client.mbIsRemoteDrawing ) {
|
|
ImGui::GetIO().DisplaySize = client.mSavedDisplaySize;
|
|
}
|
|
}
|
|
|
|
client.mbIsRemoteDrawing = false;
|
|
client.mbIsDrawing = false;
|
|
client.mbValidDrawFrame = false;
|
|
}
|
|
|
|
//=================================================================================================
|
|
ImGuiContext* GetContext()
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return nullptr;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
return client.mpContext;
|
|
}
|
|
|
|
//=================================================================================================
|
|
void SendDataTexture(ImTextureID textureId, void* pData, uint16_t width, uint16_t height, eTexFormat format, uint32_t dataSize)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
CmdTexture* pCmdTexture = nullptr;
|
|
|
|
// Makes sure even 32bits ImTextureID value are received properly as 64bits
|
|
uint64_t texId64(0);
|
|
static_assert(sizeof(uint64_t) >= sizeof(textureId), "ImTextureID is bigger than 64bits, CmdTexture::mTextureId needs to be updated to support it");
|
|
reinterpret_cast<ImTextureID*>(&texId64)[0] = textureId;
|
|
|
|
// Add/Update a texture
|
|
if( pData != nullptr )
|
|
{
|
|
if( format != eTexFormat::kTexFmtCustom ){
|
|
dataSize = GetTexture_BytePerImage(format, width, height);
|
|
}
|
|
uint32_t SizeNeeded = dataSize + sizeof(CmdTexture);
|
|
pCmdTexture = netImguiSizedNew<CmdTexture>(SizeNeeded);
|
|
|
|
pCmdTexture->mpTextureData.SetPtr(reinterpret_cast<uint8_t*>(&pCmdTexture[1]));
|
|
memcpy(pCmdTexture->mpTextureData.Get(), pData, dataSize);
|
|
|
|
pCmdTexture->mSize = SizeNeeded;
|
|
pCmdTexture->mWidth = width;
|
|
pCmdTexture->mHeight = height;
|
|
pCmdTexture->mTextureId = texId64;
|
|
pCmdTexture->mFormat = static_cast<uint8_t>(format);
|
|
pCmdTexture->mpTextureData.ToOffset();
|
|
|
|
// Detects when user is sending the font texture
|
|
ScopedImguiContext scopedCtx(client.mpContext ? client.mpContext : ImGui::GetCurrentContext());
|
|
if( ImGui::GetIO().Fonts && ImGui::GetIO().Fonts->TexID == textureId )
|
|
{
|
|
client.mbFontUploaded |= true;
|
|
client.mpFontTextureData = ImGui::GetIO().Fonts->TexPixelsAlpha8;
|
|
client.mFontTextureID = textureId;
|
|
}
|
|
}
|
|
// Texture to remove
|
|
else
|
|
{
|
|
pCmdTexture = netImguiNew<CmdTexture>();
|
|
pCmdTexture->mTextureId = texId64;
|
|
pCmdTexture->mWidth = 0;
|
|
pCmdTexture->mHeight = 0;
|
|
pCmdTexture->mFormat = eTexFormat::kTexFmt_Invalid;
|
|
pCmdTexture->mpTextureData.SetOff(0);
|
|
}
|
|
|
|
// In unlikely event of too many textures, wait for them to be processed
|
|
// (if connected) or Process them now (if not)
|
|
while( (client.mTexturesPendingCreated - client.mTexturesPendingSent) >= static_cast<uint32_t>(ArrayCount(client.mTexturesPending)) )
|
|
{
|
|
if( IsConnected() )
|
|
std::this_thread::yield();
|
|
else
|
|
client.ProcessTexturePending();
|
|
}
|
|
uint32_t idx = client.mTexturesPendingCreated.fetch_add(1) % static_cast<uint32_t>(ArrayCount(client.mTexturesPending));
|
|
client.mTexturesPending[idx] = pCmdTexture;
|
|
|
|
// If not connected to server yet, update all pending textures
|
|
if( !IsConnected() )
|
|
client.ProcessTexturePending();
|
|
}
|
|
|
|
//=================================================================================================
|
|
void SetBackground(const ImVec4& bgColor)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
client.mBGSetting = NetImgui::Internal::CmdBackground();
|
|
client.mBGSetting.mClearColor[0] = bgColor.x;
|
|
client.mBGSetting.mClearColor[1] = bgColor.y;
|
|
client.mBGSetting.mClearColor[2] = bgColor.z;
|
|
client.mBGSetting.mClearColor[3] = bgColor.w;
|
|
}
|
|
|
|
//=================================================================================================
|
|
void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint )
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
client.mBGSetting.mClearColor[0] = bgColor.x;
|
|
client.mBGSetting.mClearColor[1] = bgColor.y;
|
|
client.mBGSetting.mClearColor[2] = bgColor.z;
|
|
client.mBGSetting.mClearColor[3] = bgColor.w;
|
|
client.mBGSetting.mTextureTint[0] = textureTint.x;
|
|
client.mBGSetting.mTextureTint[1] = textureTint.y;
|
|
client.mBGSetting.mTextureTint[2] = textureTint.z;
|
|
client.mBGSetting.mTextureTint[3] = textureTint.w;
|
|
client.mBGSetting.mTextureId = NetImgui::Internal::CmdBackground::kDefaultTexture;
|
|
}
|
|
|
|
//=================================================================================================
|
|
void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint, ImTextureID bgTextureID)
|
|
//=================================================================================================
|
|
{
|
|
if (!gpClientInfo) return;
|
|
|
|
Client::ClientInfo& client = *gpClientInfo;
|
|
client.mBGSetting.mClearColor[0] = bgColor.x;
|
|
client.mBGSetting.mClearColor[1] = bgColor.y;
|
|
client.mBGSetting.mClearColor[2] = bgColor.z;
|
|
client.mBGSetting.mClearColor[3] = bgColor.w;
|
|
client.mBGSetting.mTextureTint[0] = textureTint.x;
|
|
client.mBGSetting.mTextureTint[1] = textureTint.y;
|
|
client.mBGSetting.mTextureTint[2] = textureTint.z;
|
|
client.mBGSetting.mTextureTint[3] = textureTint.w;
|
|
|
|
uint64_t texId64(0);
|
|
reinterpret_cast<ImTextureID*>(&texId64)[0] = bgTextureID;
|
|
client.mBGSetting.mTextureId = texId64;
|
|
}
|
|
|
|
//=================================================================================================
|
|
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]);
|
|
|
|
// User assigned a function callback handling FontScaling,
|
|
// use it to request a Font update on DPI scaling change on the server
|
|
if (gpClientInfo->mFontCreationFunction != nullptr)
|
|
{
|
|
if(abs(gpClientInfo->mFontCreationScaling - pCmdInput->mFontDPIScaling) > 0.01f)
|
|
{
|
|
gpClientInfo->mFontCreationFunction(gpClientInfo->mFontCreationScaling, pCmdInput->mFontDPIScaling);
|
|
gpClientInfo->mFontCreationScaling = pCmdInput->mFontDPIScaling;
|
|
}
|
|
}
|
|
// Client doesn't support regenerating the font at new DPI
|
|
// Use FontGlobalScale to affect rendering size, resulting in blurrier result
|
|
else
|
|
{
|
|
io.FontGlobalScale = pCmdInput->mFontDPIScaling;
|
|
}
|
|
|
|
#if IMGUI_VERSION_NUM < 18700
|
|
io.MousePos = ImVec2(pCmdInput->mMousePos[0], pCmdInput->mMousePos[1]);
|
|
io.MouseWheel = wheelY;
|
|
io.MouseWheelH = wheelX;
|
|
for (uint32_t i(0); i < CmdInput::NetImguiMouseButton::ImGuiMouseButton_COUNT; ++i) {
|
|
io.MouseDown[i] = (pCmdInput->mMouseDownMask & (0x0000000000000001ull << i)) != 0;
|
|
}
|
|
|
|
#define AddInputDown(KEYNAME) AddKeyEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey_::KEYNAME);
|
|
AddInputDown(ImGuiKey_Tab)
|
|
AddInputDown(ImGuiKey_LeftArrow)
|
|
AddInputDown(ImGuiKey_RightArrow)
|
|
AddInputDown(ImGuiKey_UpArrow)
|
|
AddInputDown(ImGuiKey_DownArrow)
|
|
AddInputDown(ImGuiKey_PageUp)
|
|
AddInputDown(ImGuiKey_PageDown)
|
|
AddInputDown(ImGuiKey_Home)
|
|
AddInputDown(ImGuiKey_End)
|
|
AddInputDown(ImGuiKey_Insert)
|
|
AddInputDown(ImGuiKey_Delete)
|
|
AddInputDown(ImGuiKey_Backspace)
|
|
AddInputDown(ImGuiKey_Space)
|
|
AddInputDown(ImGuiKey_Enter)
|
|
AddInputDown(ImGuiKey_Escape)
|
|
AddInputDown(ImGuiKey_A) // for text edit CTRL+A: select all
|
|
AddInputDown(ImGuiKey_C) // for text edit CTRL+C: copy
|
|
AddInputDown(ImGuiKey_V) // for text edit CTRL+V: paste
|
|
AddInputDown(ImGuiKey_X) // for text edit CTRL+X: cut
|
|
AddInputDown(ImGuiKey_Y) // for text edit CTRL+Y: redo
|
|
AddInputDown(ImGuiKey_Z) // for text edit CTRL+Z: undo
|
|
|
|
io.KeyShift = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModShift);
|
|
io.KeyCtrl = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModCtrl);
|
|
io.KeyAlt = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModAlt);
|
|
io.KeySuper = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModSuper);
|
|
#else
|
|
#if IMGUI_VERSION_NUM < 18837
|
|
#define ImGuiKey ImGuiKey_
|
|
#endif
|
|
// At the moment All Dear Imgui version share the same ImGuiKey_ enum (with a 512 value offset),
|
|
// but could change in the future, so convert from our own enum version, to Dear ImGui.
|
|
#define AddInputDown(KEYNAME) AddKeyEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey::KEYNAME);
|
|
#define AddAnalogInputDown(KEYNAME) AddKeyAnalogEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey::KEYNAME);
|
|
AddInputDown(ImGuiKey_Tab)
|
|
AddInputDown(ImGuiKey_LeftArrow)
|
|
AddInputDown(ImGuiKey_RightArrow)
|
|
AddInputDown(ImGuiKey_UpArrow)
|
|
AddInputDown(ImGuiKey_DownArrow)
|
|
AddInputDown(ImGuiKey_PageUp)
|
|
AddInputDown(ImGuiKey_PageDown)
|
|
AddInputDown(ImGuiKey_Home)
|
|
AddInputDown(ImGuiKey_End)
|
|
AddInputDown(ImGuiKey_Insert)
|
|
AddInputDown(ImGuiKey_Delete)
|
|
AddInputDown(ImGuiKey_Backspace)
|
|
AddInputDown(ImGuiKey_Space)
|
|
AddInputDown(ImGuiKey_Enter)
|
|
AddInputDown(ImGuiKey_Escape)
|
|
|
|
AddInputDown(ImGuiKey_LeftCtrl) AddInputDown(ImGuiKey_LeftShift) AddInputDown(ImGuiKey_LeftAlt) AddInputDown(ImGuiKey_LeftSuper)
|
|
AddInputDown(ImGuiKey_RightCtrl) AddInputDown(ImGuiKey_RightShift) AddInputDown(ImGuiKey_RightAlt) AddInputDown(ImGuiKey_RightSuper)
|
|
AddInputDown(ImGuiKey_Menu)
|
|
AddInputDown(ImGuiKey_0) AddInputDown(ImGuiKey_1) AddInputDown(ImGuiKey_2) AddInputDown(ImGuiKey_3) AddInputDown(ImGuiKey_4) AddInputDown(ImGuiKey_5) AddInputDown(ImGuiKey_6) AddInputDown(ImGuiKey_7) AddInputDown(ImGuiKey_8) AddInputDown(ImGuiKey_9)
|
|
AddInputDown(ImGuiKey_A) AddInputDown(ImGuiKey_B) AddInputDown(ImGuiKey_C) AddInputDown(ImGuiKey_D) AddInputDown(ImGuiKey_E) AddInputDown(ImGuiKey_F) AddInputDown(ImGuiKey_G) AddInputDown(ImGuiKey_H) AddInputDown(ImGuiKey_I) AddInputDown(ImGuiKey_J)
|
|
AddInputDown(ImGuiKey_K) AddInputDown(ImGuiKey_L) AddInputDown(ImGuiKey_M) AddInputDown(ImGuiKey_N) AddInputDown(ImGuiKey_O) AddInputDown(ImGuiKey_P) AddInputDown(ImGuiKey_Q) AddInputDown(ImGuiKey_R) AddInputDown(ImGuiKey_S) AddInputDown(ImGuiKey_T)
|
|
AddInputDown(ImGuiKey_U) AddInputDown(ImGuiKey_V) AddInputDown(ImGuiKey_W) AddInputDown(ImGuiKey_X) AddInputDown(ImGuiKey_Y) AddInputDown(ImGuiKey_Z)
|
|
AddInputDown(ImGuiKey_F1) AddInputDown(ImGuiKey_F2) AddInputDown(ImGuiKey_F3) AddInputDown(ImGuiKey_F4) AddInputDown(ImGuiKey_F5) AddInputDown(ImGuiKey_F6)
|
|
AddInputDown(ImGuiKey_F7) AddInputDown(ImGuiKey_F8) AddInputDown(ImGuiKey_F9) AddInputDown(ImGuiKey_F10) AddInputDown(ImGuiKey_F11) AddInputDown(ImGuiKey_F12)
|
|
|
|
AddInputDown(ImGuiKey_Apostrophe)
|
|
AddInputDown(ImGuiKey_Comma)
|
|
AddInputDown(ImGuiKey_Minus)
|
|
AddInputDown(ImGuiKey_Period)
|
|
AddInputDown(ImGuiKey_Slash)
|
|
AddInputDown(ImGuiKey_Semicolon)
|
|
AddInputDown(ImGuiKey_Equal)
|
|
AddInputDown(ImGuiKey_LeftBracket)
|
|
AddInputDown(ImGuiKey_Backslash)
|
|
AddInputDown(ImGuiKey_RightBracket)
|
|
AddInputDown(ImGuiKey_GraveAccent)
|
|
AddInputDown(ImGuiKey_CapsLock)
|
|
AddInputDown(ImGuiKey_ScrollLock)
|
|
AddInputDown(ImGuiKey_NumLock)
|
|
AddInputDown(ImGuiKey_PrintScreen)
|
|
AddInputDown(ImGuiKey_Pause)
|
|
AddInputDown(ImGuiKey_Keypad0) AddInputDown(ImGuiKey_Keypad1) AddInputDown(ImGuiKey_Keypad2) AddInputDown(ImGuiKey_Keypad3) AddInputDown(ImGuiKey_Keypad4)
|
|
AddInputDown(ImGuiKey_Keypad5) AddInputDown(ImGuiKey_Keypad6) AddInputDown(ImGuiKey_Keypad7) AddInputDown(ImGuiKey_Keypad8) AddInputDown(ImGuiKey_Keypad9)
|
|
AddInputDown(ImGuiKey_KeypadDecimal) AddInputDown(ImGuiKey_KeypadDivide) AddInputDown(ImGuiKey_KeypadMultiply)
|
|
AddInputDown(ImGuiKey_KeypadSubtract) AddInputDown(ImGuiKey_KeypadAdd) AddInputDown(ImGuiKey_KeypadEnter)
|
|
AddInputDown(ImGuiKey_KeypadEqual)
|
|
|
|
#if IMGUI_VERSION_NUM >= 19000
|
|
AddInputDown(ImGuiKey_F13) AddInputDown(ImGuiKey_F14) AddInputDown(ImGuiKey_F15) AddInputDown(ImGuiKey_F16) AddInputDown(ImGuiKey_F17) AddInputDown(ImGuiKey_F18)
|
|
AddInputDown(ImGuiKey_F19) AddInputDown(ImGuiKey_F20) AddInputDown(ImGuiKey_F21) AddInputDown(ImGuiKey_F22) AddInputDown(ImGuiKey_F23) AddInputDown(ImGuiKey_F24)
|
|
AddInputDown(ImGuiKey_AppBack)
|
|
AddInputDown(ImGuiKey_AppForward)
|
|
#endif
|
|
// Gamepad
|
|
AddInputDown(ImGuiKey_GamepadStart)
|
|
AddInputDown(ImGuiKey_GamepadBack)
|
|
AddInputDown(ImGuiKey_GamepadFaceUp)
|
|
AddInputDown(ImGuiKey_GamepadFaceDown)
|
|
AddInputDown(ImGuiKey_GamepadFaceLeft)
|
|
AddInputDown(ImGuiKey_GamepadFaceRight)
|
|
AddInputDown(ImGuiKey_GamepadDpadUp)
|
|
AddInputDown(ImGuiKey_GamepadDpadDown)
|
|
AddInputDown(ImGuiKey_GamepadDpadLeft)
|
|
AddInputDown(ImGuiKey_GamepadDpadRight)
|
|
AddInputDown(ImGuiKey_GamepadL1)
|
|
AddInputDown(ImGuiKey_GamepadR1)
|
|
AddInputDown(ImGuiKey_GamepadL2)
|
|
AddInputDown(ImGuiKey_GamepadR2)
|
|
AddInputDown(ImGuiKey_GamepadL3)
|
|
AddInputDown(ImGuiKey_GamepadR3)
|
|
AddAnalogInputDown(ImGuiKey_GamepadLStickUp)
|
|
AddAnalogInputDown(ImGuiKey_GamepadLStickDown)
|
|
AddAnalogInputDown(ImGuiKey_GamepadLStickLeft)
|
|
AddAnalogInputDown(ImGuiKey_GamepadLStickRight)
|
|
AddAnalogInputDown(ImGuiKey_GamepadRStickUp)
|
|
AddAnalogInputDown(ImGuiKey_GamepadRStickDown)
|
|
AddAnalogInputDown(ImGuiKey_GamepadRStickLeft)
|
|
AddAnalogInputDown(ImGuiKey_GamepadRStickRight)
|
|
|
|
#undef AddInputDown
|
|
#undef AddAnalogInputDown
|
|
#if IMGUI_VERSION_NUM < 18837
|
|
#undef ImGuiKey
|
|
#endif
|
|
|
|
#if IMGUI_VERSION_NUM < 18837
|
|
#define ImGuiMod_Ctrl ImGuiKey_ModCtrl
|
|
#define ImGuiMod_Shift ImGuiKey_ModShift
|
|
#define ImGuiMod_Alt ImGuiKey_ModAlt
|
|
#define ImGuiMod_Super ImGuiKey_ModSuper
|
|
#endif
|
|
io.AddKeyEvent(ImGuiMod_Ctrl, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModCtrl));
|
|
io.AddKeyEvent(ImGuiMod_Shift, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModShift));
|
|
io.AddKeyEvent(ImGuiMod_Alt, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModAlt));
|
|
io.AddKeyEvent(ImGuiMod_Super, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModSuper));
|
|
|
|
// Mouse
|
|
io.AddMouseWheelEvent(wheelX, wheelY);
|
|
io.AddMousePosEvent(pCmdInput->mMousePos[0], pCmdInput->mMousePos[1]);
|
|
for(int i(0); 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"
|