Skip to content

Commit

Permalink
Added remote clipboard support, fiex key input issue
Browse files Browse the repository at this point in the history
- Added copy/paste of text accross Server/Client
- Fixed a keys reception issue cause letter drop when quickly typed.
  • Loading branch information
sammyfreg committed Dec 24, 2023
1 parent 66e46e3 commit a5fde18
Show file tree
Hide file tree
Showing 16 changed files with 381 additions and 93 deletions.
58 changes: 38 additions & 20 deletions Code/Client/Private/NetImgui_Api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,9 +538,17 @@ static inline void AddKeyAnalogEvent(const Client::ClientInfo& client, const Cmd
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.mpInputPending;
CmdInput* pCmdInput = hasNewInput ? pCmdInputNew : client.mpCmdInputPending;
ImGuiIO& io = ImGui::GetIO();

if (pCmdInput)
Expand Down Expand Up @@ -596,6 +604,11 @@ bool ProcessInputData(Client::ClientInfo& client)
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_
Expand Down Expand Up @@ -629,6 +642,7 @@ bool ProcessInputData(Client::ClientInfo& client)
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)
Expand All @@ -647,14 +661,16 @@ bool ProcessInputData(Client::ClientInfo& client)
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_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)
Expand Down Expand Up @@ -687,29 +703,31 @@ bool ProcessInputData(Client::ClientInfo& client)
#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]);
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
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);

size_t keyCount(1);
uint16_t character;
io.InputQueueCharacters.resize(0);

client.mPendingKeyIn.ReadData(&character, keyCount);
while (keyCount > 0){
while (client.mPendingKeyIn.ReadData(&character)){
io.AddInputCharacter(character);
client.mPendingKeyIn.ReadData(&character, keyCount);
}

static_assert(sizeof(client.mPreviousInputState.mInputDownMask) == sizeof(pCmdInput->mInputDownMask), "Array size should match");
Expand All @@ -724,8 +742,8 @@ bool ProcessInputData(Client::ClientInfo& client)
}

if( hasNewInput ){
netImguiDeleteSafe(client.mpInputPending);
client.mpInputPending = pCmdInputNew;
netImguiDeleteSafe(client.mpCmdInputPending);
client.mpCmdInputPending = pCmdInputNew;
}
return hasNewInput;
}
Expand Down
122 changes: 96 additions & 26 deletions Code/Client/Private/NetImgui_Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ void SavedImguiContext::Save(ImGuiContext* copyFrom)
mBackendPlatformName = sourceIO.BackendPlatformName;
mBackendRendererName = sourceIO.BackendRendererName;
mDrawMouse = sourceIO.MouseDrawCursor;
mClipboardUserData = sourceIO.ClipboardUserData;
mGetClipboardTextFn = sourceIO.GetClipboardTextFn;
mSetClipboardTextFn = sourceIO.SetClipboardTextFn;
mClipboardUserData = sourceIO.ClipboardUserData;
mFontGlobalScale = sourceIO.FontGlobalScale;
mFontGeneratedSize = sourceIO.Fonts->Fonts.Size > 0 ? sourceIO.Fonts->Fonts[0]->FontSize : 13.f; // Save size to restore the font to original size
#if IMGUI_VERSION_NUM < 18700
Expand All @@ -42,13 +44,37 @@ void SavedImguiContext::Restore(ImGuiContext* copyTo)
destIO.BackendPlatformName = mBackendPlatformName;
destIO.BackendRendererName = mBackendRendererName;
destIO.MouseDrawCursor = mDrawMouse;
destIO.GetClipboardTextFn = mGetClipboardTextFn;
destIO.SetClipboardTextFn = mSetClipboardTextFn;
destIO.ClipboardUserData = mClipboardUserData;
destIO.FontGlobalScale = mFontGlobalScale;
#if IMGUI_VERSION_NUM < 18700
memcpy(destIO.KeyMap, mKeyMap, sizeof(destIO.KeyMap));
#endif
}

//=================================================================================================
// GET CLIPBOARD
// Content received from the Server
//=================================================================================================
static const char* GetClipboardTextFn_NetImguiImpl(void* user_data_ctx)
{
const ClientInfo* pClient = reinterpret_cast<const ClientInfo*>(user_data_ctx);
return pClient && pClient->mpCmdClipboard ? pClient->mpCmdClipboard->mContentUTF8.Get() : nullptr;
}

//=================================================================================================
// SET CLIPBOARD
//=================================================================================================
static void SetClipboardTextFn_NetImguiImpl(void* user_data_ctx, const char* text)
{
if(user_data_ctx){
ClientInfo* pClient = reinterpret_cast<ClientInfo*>(user_data_ctx);
CmdClipboard* pClipboardOut = CmdClipboard::Create(text);
pClient->mPendingClipboardOut.Assign(pClipboardOut);
}
}

//=================================================================================================
// COMMUNICATIONS INITIALIZE
// Initialize a new connection to a RemoteImgui server
Expand Down Expand Up @@ -94,6 +120,21 @@ void Communications_Incoming_Input(ClientInfo& client, uint8_t*& pCmdData)
}
}

//=================================================================================================
// INCOM: CLIPBOARD
// Receive server new clipboard content, updating internal cache
//=================================================================================================
void Communications_Incoming_Clipboard(ClientInfo& client, uint8_t*& pCmdData)
{
if( pCmdData )
{
auto pCmdClipboard = reinterpret_cast<CmdClipboard*>(pCmdData);
pCmdData = nullptr; // Take ownership of the data, prevent Free
pCmdClipboard->ToPointers();
client.mPendingClipboardIn.Assign(pCmdClipboard);
}
}

//=================================================================================================
// OUTCOM: TEXTURE
// Transmit all pending new/updated texture
Expand Down Expand Up @@ -142,39 +183,39 @@ bool Communications_Outgoing_Background(ClientInfo& client)
bool Communications_Outgoing_Frame(ClientInfo& client)
{
bool bSuccess(true);
CmdDrawFrame* pPendingDrawFrame = client.mPendingFrameOut.Release();
if( pPendingDrawFrame )
CmdDrawFrame* pPendingDraw = client.mPendingFrameOut.Release();
if( pPendingDraw )
{
pPendingDrawFrame->mFrameIndex = client.mFrameIndex++;
pPendingDraw->mFrameIndex = client.mFrameIndex++;
//---------------------------------------------------------------------
// Apply delta compression to DrawCommand, when requested
if( pPendingDrawFrame->mCompressed )
if( pPendingDraw->mCompressed )
{
// Create a new Compressed DrawFrame Command
if( client.mpDrawFramePrevious && !client.mServerCompressionSkip ){
client.mpDrawFramePrevious->ToPointers();
CmdDrawFrame* pDrawFrameCompressed = CompressCmdDrawFrame(client.mpDrawFramePrevious, pPendingDrawFrame);
netImguiDeleteSafe(client.mpDrawFramePrevious);
client.mpDrawFramePrevious = pPendingDrawFrame; // Keep original new command for next frame delta compression
pPendingDrawFrame = pDrawFrameCompressed; // Request compressed copy to be sent to server
if( client.mpCmdDrawLast && !client.mServerCompressionSkip ){
client.mpCmdDrawLast->ToPointers();
CmdDrawFrame* pDrawCompressed = CompressCmdDrawFrame(client.mpCmdDrawLast, pPendingDraw);
netImguiDeleteSafe(client.mpCmdDrawLast);
client.mpCmdDrawLast = pPendingDraw; // Keep original new command for next frame delta compression
pPendingDraw = pDrawCompressed; // Request compressed copy to be sent to server
}
// Save DrawCmd for next frame delta compression
else {
pPendingDrawFrame->mCompressed = false;
client.mpDrawFramePrevious = pPendingDrawFrame;
pPendingDraw->mCompressed = false;
client.mpCmdDrawLast = pPendingDraw;
}
}
client.mServerCompressionSkip = false;

//---------------------------------------------------------------------
// Send Command to server
pPendingDrawFrame->ToOffsets();
bSuccess = Network::DataSend(client.mpSocketComs, pPendingDrawFrame, pPendingDrawFrame->mHeader.mSize);
pPendingDraw->ToOffsets();
bSuccess = Network::DataSend(client.mpSocketComs, pPendingDraw, pPendingDraw->mHeader.mSize);

//---------------------------------------------------------------------
// Free created data once sent (when not used in next frame)
if( client.mpDrawFramePrevious != pPendingDrawFrame ){
netImguiDeleteSafe(pPendingDrawFrame);
if( client.mpCmdDrawLast != pPendingDraw ){
netImguiDeleteSafe(pPendingDraw);
}
}
return bSuccess;
Expand Down Expand Up @@ -205,6 +246,23 @@ bool Communications_Outgoing_Ping(ClientInfo& client)
return Network::DataSend(client.mpSocketComs, &cmdPing, cmdPing.mHeader.mSize);
}

//=================================================================================================
// OUTCOM: Clipboard
// Send client 'Copy' clipboard content to Server
//=================================================================================================
bool Communications_Outgoing_Clipboard(ClientInfo& client)
{
bool bResult(true);
CmdClipboard* pPendingClipboard = client.mPendingClipboardOut.Release();
if( pPendingClipboard ){
pPendingClipboard->ToOffsets();
bResult = Network::DataSend(client.mpSocketComs, pPendingClipboard, pPendingClipboard->mHeader.mSize);
netImguiDeleteSafe(pPendingClipboard);

}
return bResult;
}

//=================================================================================================
// INCOMING COMMUNICATIONS
//=================================================================================================
Expand All @@ -231,6 +289,7 @@ bool Communications_Incoming(ClientInfo& client)
case CmdHeader::eCommands::Ping: bPingReceived = true; break;
case CmdHeader::eCommands::Disconnect: bOk = false; break;
case CmdHeader::eCommands::Input: Communications_Incoming_Input(client, pCmdData); break;
case CmdHeader::eCommands::Clipboard: Communications_Incoming_Clipboard(client, pCmdData); break;
// Commands not received in main loop, by Client
case CmdHeader::eCommands::Invalid:
case CmdHeader::eCommands::Version:
Expand All @@ -248,18 +307,26 @@ bool Communications_Incoming(ClientInfo& client)
// OUTGOING COMMUNICATIONS
//=================================================================================================
bool Communications_Outgoing(ClientInfo& client)
{
{
bool bSuccess(true);
if( bSuccess )
if( bSuccess ){
bSuccess = Communications_Outgoing_Textures(client);
if( bSuccess )
}
if( bSuccess ){
bSuccess = Communications_Outgoing_Background(client);
if( bSuccess )
}
if( bSuccess ){
bSuccess = Communications_Outgoing_Clipboard(client);
}
if( bSuccess ){
bSuccess = Communications_Outgoing_Frame(client);
if( bSuccess )
}
if( bSuccess ){
bSuccess = Communications_Outgoing_Disconnect(client);
if( bSuccess )
}
if( bSuccess ){
bSuccess = Communications_Outgoing_Ping(client); // Always finish with a ping
}

return bSuccess;
}
Expand Down Expand Up @@ -372,8 +439,9 @@ ClientInfo::~ClientInfo()
netImguiDeleteSafe(mTexturesPending[i]);
}

netImguiDeleteSafe(mpInputPending);
netImguiDeleteSafe(mpDrawFramePrevious);
netImguiDeleteSafe(mpCmdInputPending);
netImguiDeleteSafe(mpCmdDrawLast);
netImguiDeleteSafe(mpCmdClipboard);
}

//=================================================================================================
Expand Down Expand Up @@ -415,7 +483,9 @@ void ClientInfo::ContextOverride()
{
ImGuiIO& newIO = ImGui::GetIO();
newIO.MouseDrawCursor = false;
newIO.ClipboardUserData = nullptr;
newIO.GetClipboardTextFn = GetClipboardTextFn_NetImguiImpl;
newIO.SetClipboardTextFn = SetClipboardTextFn_NetImguiImpl;
newIO.ClipboardUserData = this;
newIO.BackendPlatformName = "NetImgui";
newIO.BackendRendererName = "DirectX11";
if( mFontCreationFunction != nullptr )
Expand Down
37 changes: 21 additions & 16 deletions Code/Client/Private/NetImgui_Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,21 @@ struct SavedImguiContext
{
void Save(ImGuiContext* copyFrom);
void Restore(ImGuiContext* copyTo);
const char* mBackendPlatformName = nullptr;
const char* mBackendRendererName = nullptr;
void* mClipboardUserData = nullptr;
void* mImeWindowHandle = nullptr;
float mFontGlobalScale = 1.f;
float mFontGeneratedSize = 0.f;
ImGuiBackendFlags mBackendFlags = 0;
ImGuiConfigFlags mConfigFlags = 0;
bool mDrawMouse = false;
bool mSavedContext = false;
char mPadding1[2] = {};
int mKeyMap[ImGuiKey_COUNT] = {};
char mPadding2[8 - (sizeof(mKeyMap) % 8)]={};
const char* mBackendPlatformName = nullptr;
const char* mBackendRendererName = nullptr;
const char* (*mGetClipboardTextFn)(void*) = nullptr;
void (*mSetClipboardTextFn)(void*, const char*) = nullptr;
void* mClipboardUserData = nullptr;
void* mImeWindowHandle = nullptr;
float mFontGlobalScale = 1.f;
float mFontGeneratedSize = 0.f;
ImGuiBackendFlags mBackendFlags = 0;
ImGuiConfigFlags mConfigFlags = 0;
bool mDrawMouse = false;
bool mSavedContext = false;
char mPadding1[2] = {};
int mKeyMap[ImGuiKey_COUNT] = {};
char mPadding2[8 - (sizeof(mKeyMap) % 8)] ={};
};

//=============================================================================
Expand Down Expand Up @@ -79,10 +81,13 @@ struct ClientInfo
CmdTexture* mTexturesPending[16];
ExchangePtr<CmdDrawFrame> mPendingFrameOut;
ExchangePtr<CmdBackground> mPendingBackgroundOut;
ExchangePtr<CmdInput> mPendingInputIn;
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 (either the one active when connection request happened, or a clone)
CmdInput* mpInputPending = nullptr; // Last Input Command from server, waiting to be processed by client
CmdDrawFrame* mpDrawFramePrevious = nullptr; // Last sent Draw Command. Used by data compression, to generate delta between previous and current frame
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
Expand Down
Loading

0 comments on commit a5fde18

Please sign in to comment.