Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
#include "NetImguiServer_App.h"
#include "Fonts/Roboto_Medium.cpp"
#include "NetImguiServer_Config.h"
#include "NetImguiServer_Network.h"
#include "NetImguiServer_RemoteClient.h"
#include "NetImguiServer_UI.h"
namespace NetImguiServer {
namespace App {
constexpr uint32_t kClientCountMax =
32; //! @sammyfreg todo: support unlimited client count
ImVector<ServerTexture*>
gServerTextures; // List of ALL server created textures (used by server and
// clients)
ServerTexture* gServerTextureEmpty =
nullptr; // Empty texture used when no valid texture found
bool gLoadedConfigOnce = false;
void UpdateServerTextures();
bool Startup(const char* CmdLine) {
//---------------------------------------------------------------------------------------------
// Load Settings savefile and parse for auto connect commandline option
//---------------------------------------------------------------------------------------------
if (!gLoadedConfigOnce) {
NetImguiServer::Config::Client::LoadAll();
gLoadedConfigOnce = true;
}
AddTransientClientConfigFromString(CmdLine);
//---------------------------------------------------------------------------------------------
// Perform application initialization:
//---------------------------------------------------------------------------------------------
if (RemoteClient::Client::Startup(kClientCountMax) &&
NetImguiServer::Network::Startup() && NetImguiServer::UI::Startup()) {
NetImgui::Internal::CmdTexture cmdTexture;
uint32_t EmptyData[4 * 4] = {
0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF,
0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF,
0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF};
cmdTexture.mTextureClientID = 0;
cmdTexture.mFormat = ImTextureFormat::ImTextureFormat_RGBA32;
cmdTexture.mWidth = cmdTexture.mHeight = 4;
cmdTexture.mpTextureData.SetPtr((uint8_t*)EmptyData);
gServerTextureEmpty = CreateTexture(cmdTexture, sizeof(EmptyData));
LoadFonts();
ImGui::GetIO().IniFilename =
nullptr; // Disable server ImGui ini settings (not really needed, and
// avoid imgui.ini filename conflicts)
ImGui::GetIO().LogFilename = nullptr;
return HAL_Startup(CmdLine);
}
return false;
}
void Shutdown() {
// Mark all texture resources as wanting deletion
for (ServerTexture* texServer : gServerTextures) {
if (texServer && texServer->IsValid()) {
if (!texServer->mIsCustom) {
texServer->mTexData.SetStatus(ImTextureStatus_WantDestroy);
texServer->mTexData.UnusedFrames = 1;
} else {
#if TEXTURE_CUSTOM_SAMPLE
if (texServer && texServer->mIsCustom) {
0 // TODO
}
#endif
}
}
}
// Allow Dear ImGui to delete the textures
ImGui::NewFrame();
ImGui::Render();
HAL_RenderDrawData(ImGui::GetDrawData());
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
// Finish removing them from Dear ImGui user textures and delete objects
UpdateServerTextures();
// Update Dear ImGui texture list (so it doesn't have deleted items in it)
ImGui::NewFrame();
ImGui::Render();
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
// Remove deleted textures from clients
for (uint32_t i(0); i < RemoteClient::Client::GetCountMax(); ++i) {
RemoteClient::Client& client = RemoteClient::Client::Get(i);
client.mTextureTable.clear();
}
NetImguiServer::Network::Shutdown();
NetImguiServer::UI::Shutdown();
NetImguiServer::Config::Client::Clear();
RemoteClient::Client::Shutdown();
HAL_Shutdown();
}
//=================================================================================================
// INIT CLIENT CONFIG FROM STRING
// Take a commandline string, and create a ClientConfig from it.
// Simple format of (Hostname);(HostPort)
bool AddTransientClientConfigFromString(const char* string)
//=================================================================================================
{
NetImguiServer::Config::Client cmdlineClient;
const char* zEntryStart = string;
const char* zEntryCur = string;
int paramIndex = 0;
cmdlineClient.mConfigType =
NetImguiServer::Config::Client::eConfigType::Transient;
NetImgui::Internal::StringCopy(cmdlineClient.mClientName, "Commandline");
while (*zEntryCur != 0) {
zEntryCur++;
// Skip commandline preamble holding path to executable
if (*zEntryCur == ' ' && *(zEntryCur + 1) != 0) {
zEntryStart = zEntryCur + 1;
}
if ((*zEntryCur == ';' || *zEntryCur == 0)) {
if (paramIndex == 0)
NetImgui::Internal::StringCopy(cmdlineClient.mHostName, zEntryStart,
zEntryCur - zEntryStart);
else if (paramIndex == 1)
cmdlineClient.mHostPort = static_cast<uint32_t>(atoi(zEntryStart));
cmdlineClient.mConnectAuto =
paramIndex >=
1; // Mark valid for connexion as soon as we have a HostAddress
zEntryStart = zEntryCur + 1;
paramIndex++;
}
}
if (cmdlineClient.mConnectAuto) {
NetImguiServer::Config::Client::SetConfig(cmdlineClient);
return true;
}
return false;
}
//=================================================================================================
// DRAW CLIENT BACKGROUND
// Create a separate Dear ImGui drawing context, to generate a commandlist that
// will fill the RenderTarget with a specific background picture
void DrawClientBackground(RemoteClient::Client& client)
//=================================================================================================
{
NetImgui::Internal::CmdBackground* pBGUpdate =
client.mPendingBackgroundIn.Release();
if (pBGUpdate) {
client.mBGSettings = *pBGUpdate;
client.mBGNeedUpdate = true;
netImguiDeleteSafe(pBGUpdate);
}
if (client.mpBGContext == nullptr) {
client.mpBGContext = ImGui::CreateContext(ImGui::GetIO().Fonts);
client.mpBGContext->IO.DeltaTime = 1 / 30.f;
client.mpBGContext->IO.IniFilename =
nullptr; // Disable server ImGui ini settings (not really needed, and
// avoid imgui.ini filename conflicts)
client.mpBGContext->IO.LogFilename = nullptr;
client.mpBGContext->IO.BackendFlags |=
ImGuiBackendFlags_RendererHasTextures;
}
// Detect when the main font textures was updated,
// in which case we need to re-generate the BG DrawData
const ImVector<ImTextureData*>& MainTextures =
ImGui::GetPlatformIO().Textures;
NetImgui::Internal::ScopedImguiContext scopedCtx(client.mpBGContext);
const ImVector<ImTextureData*>& BGTextures = ImGui::GetPlatformIO().Textures;
for (ImTextureData* texData : BGTextures) {
client.mBGNeedUpdate |= MainTextures.contains(texData) == false;
}
// Update the BG DrawData
// Note: Maybe we should try rendering it directly in client windows every
// frame instead, to keep things simpler
if (client.mBGNeedUpdate) {
ImGui::GetIO().DisplaySize = ImVec2(client.mAreaSizeX, client.mAreaSizeY);
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(client.mAreaSizeX, client.mAreaSizeY));
ImGui::Begin("Background", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs |
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoSavedSettings);
// Look for the desired texture (and use default if not found)
auto texIt = client.mTextureTable.find(client.mBGSettings.mTextureId);
const ServerTexture* pTexture = texIt == client.mTextureTable.end()
? UI::GetBackgroundTexture()
: texIt->second;
UI::DrawCenteredBackground(pTexture,
ImVec4(client.mBGSettings.mTextureTint[0],
client.mBGSettings.mTextureTint[1],
client.mBGSettings.mTextureTint[2],
client.mBGSettings.mTextureTint[3]));
ImGui::End();
ImGui::Render();
client.mBGNeedUpdate = false;
}
}
//=================================================================================================
void UpdateServerTextures()
//=================================================================================================
{
for (int i(gServerTextures.size() - 1); i >= 0; --i) {
if (gServerTextures[i]) {
ServerTexture& ServerTex = *gServerTextures[i];
// Release un-needed pixel data once it has been processed by backend
if (ServerTex.mIsUpdatable == false &&
ServerTex.mTexData.Pixels != nullptr &&
ServerTex.mTexData.Status == ImTextureStatus_OK) {
ServerTex.mTexData.DestroyPixels();
}
// Backend deleted the texture, remove it from our list
else if (ServerTex.mTexData.Status == ImTextureStatus_Destroyed) {
ImGui::UnregisterUserTexture(&ServerTex.mTexData);
delete gServerTextures[i];
gServerTextures[i] = nullptr;
}
// Send deletion request to backend
else if (ServerTex.mTexData.WantDestroyNextFrame) {
const RemoteClient::Client* Client =
ServerTex.mOwnerClientIndex >= 0
? &RemoteClient::Client::Get(ServerTex.mOwnerClientIndex)
: nullptr;
if (!Client || !Client->mpImguiDrawData ||
Client->mpImguiDrawData->mFrameIndex > ServerTex.mLastFrameUsed) {
if (ServerTex.mTexData.UnusedFrames++ > 0) {
ServerTex.mTexData.Status = ImTextureStatus_WantDestroy;
}
}
}
}
// Remove release textures (null) from our list
if (gServerTextures[i] == nullptr) {
ImSwap(gServerTextures[i], gServerTextures[gServerTextures.Size - 1]);
gServerTextures.pop_back();
}
}
}
//=================================================================================================
// Default texture creation behavior, relying on Dear ImGui backend to do
// the heavy lifting of texture creation and management
bool CreateTexture_Default(ServerTexture& serverTexture,
const NetImgui::Internal::CmdTexture& cmdTexture,
uint32_t customDataSize)
//=================================================================================================
{
if (!serverTexture.mIsCustom && cmdTexture.mpTextureData.Get() != nullptr) {
serverTexture.mIsUpdatable = cmdTexture.mUpdatable;
serverTexture.mTexData.Create(ImTextureFormat::ImTextureFormat_RGBA32,
cmdTexture.mWidth, cmdTexture.mHeight);
if (cmdTexture.mFormat == ImTextureFormat::ImTextureFormat_RGBA32) {
memcpy(serverTexture.mTexData.Pixels, cmdTexture.mpTextureData.Get(),
customDataSize);
} else if (cmdTexture.mFormat == ImTextureFormat::ImTextureFormat_Alpha8) {
const uint8_t* pSrcCur = cmdTexture.mpTextureData.Get();
uint32_t* pDestCur =
reinterpret_cast<uint32_t*>(serverTexture.mTexData.GetPixels());
uint32_t* pDestEnd = &pDestCur[cmdTexture.mHeight * cmdTexture.mWidth];
while (pDestCur < pDestEnd) {
*pDestCur++ = 0x00FFFFFF | (uint64_t(*pSrcCur++) << 24);
}
} else {
IM_ASSERT_USER_ERROR(0, "Unsupported format");
}
serverTexture.mTexData.Status = ImTextureStatus_WantCreate;
serverTexture.mTexData.UseColors =
cmdTexture.mFormat == ImTextureFormat::ImTextureFormat_RGBA32;
ImGui::RegisterUserTexture(&serverTexture.mTexData);
return true;
}
return false;
}
//=================================================================================================
ServerTexture* CreateTexture(const NetImgui::Internal::CmdTexture& cmdTexture,
uint32_t textureDataSize)
//=================================================================================================
{
ServerTexture* serverTex(new ServerTexture);
if (serverTex) {
serverTex->mIsCustom =
cmdTexture.mFormat == NetImgui::eTexFormat::kTexFmtCustom;
serverTex->mClientTexID = cmdTexture.mTextureClientID;
serverTex->mIsUpdatable = false;
if (CreateTexture_Custom(*serverTex, cmdTexture, textureDataSize) ||
CreateTexture_Default(*serverTex, cmdTexture, textureDataSize)) {
gServerTextures.push_back(serverTex);
} else {
delete serverTex;
serverTex = NULL;
}
}
return serverTex;
}
//=================================================================================================
// Initialize all needed fonts by the NetImguiServer application
void LoadFonts()
//=================================================================================================
{
ImFontConfig fontConfig;
ImFontAtlas* pFontAtlas = ImGui::GetIO().Fonts;
// Add Fonts here...
// Using a different default font (provided with Dear ImGui)
NetImgui::Internal::StringCopy(fontConfig.Name, "Roboto Medium");
if (!pFontAtlas->AddFontFromMemoryCompressedTTF(Roboto_Medium_compressed_data,
Roboto_Medium_compressed_size,
0.f, &fontConfig)) {
pFontAtlas->AddFontDefault(&fontConfig);
}
}
//=================================================================================================
void UpdateWindowPlacement(int x, int y, int w, int h, bool isMaximized)
//=================================================================================================
{
NetImguiServer::Config::Server::sWindowPlacement[0] = x;
NetImguiServer::Config::Server::sWindowPlacement[1] = y;
NetImguiServer::Config::Server::sWindowPlacement[2] = w;
NetImguiServer::Config::Server::sWindowPlacement[3] = h;
NetImguiServer::Config::Server::sWindowMaximized = isMaximized;
NetImguiServer::Config::Client::SaveAll();
}
//=================================================================================================
WindowPlacement GetWindowPlacement()
//=================================================================================================
{
if (!gLoadedConfigOnce) {
NetImguiServer::Config::Client::LoadAll();
gLoadedConfigOnce = true;
}
WindowPlacement wp;
wp.x = NetImguiServer::Config::Server::sWindowPlacement[0];
wp.y = NetImguiServer::Config::Server::sWindowPlacement[1];
wp.w = ImMax(100, NetImguiServer::Config::Server::sWindowPlacement[2]);
wp.h = ImMax(100, NetImguiServer::Config::Server::sWindowPlacement[3]);
wp.isMaximized = NetImguiServer::Config::Server::sWindowMaximized;
return wp;
}
//=================================================================================================
// UPDATE REMOTE CONTENT
// Create a render target for each connected remote client once, and update it
// every frame with the last drawing commands received from it. This Render
// Target will then be used normally as the background image of each client
// window renderered by this Server
void UpdateClientDraw()
//=================================================================================================
{
for (uint32_t i(0); i < RemoteClient::Client::GetCountMax(); ++i) {
RemoteClient::Client& client = RemoteClient::Client::Get(i);
if (client.mbIsConnected) {
if (client.mbIsReleased) {
client.Uninitialize();
} else {
client.ProcessPendingTextureCmds();
if (client.mbIsVisible) {
// Update the RenderTarget destination of each client, of size was
// updated
if (client.mAreaSizeX > 0 && client.mAreaSizeY > 0 &&
(!client.mpHAL_AreaRT ||
client.mAreaRTSizeX != client.mAreaSizeX ||
client.mAreaRTSizeY != client.mAreaSizeY)) {
if (HAL_CreateRenderTarget(client.mAreaSizeX, client.mAreaSizeY,
client.mpHAL_AreaRT,
client.mHAL_AreaTexture)) {
client.mAreaRTSizeX = client.mAreaSizeX;
client.mAreaRTSizeY = client.mAreaSizeY;
client.mLastUpdateTime =
std::chrono::steady_clock::now() -
std::chrono::hours(1); // Will redraw the client
client.mBGNeedUpdate = true;
}
}
// Render the remote results
ImDrawData* pDrawData =
client.GetImguiDrawData(gServerTextureEmpty->mTexData.GetTexID());
if (pDrawData) {
DrawClientBackground(client);
HAL_RenderDrawData(client, pDrawData);
}
}
}
}
}
UpdateServerTextures();
}
} // namespace App
} // namespace NetImguiServer
e19a62fd1This file is pinned to the selected release. Download the original to open it in your design tool.