diff --git a/CMakeLists.txt b/CMakeLists.txt index 803c30de..87b0f04e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,12 @@ option(HELLOIMGUI_HAS_NULL "Null rendering backend" OFF) # for testing and remo option(HELLOIMGUI_NULL_BACKEND "Use Null rendering/platform backend" OFF) # # do not remove this line (used by the script that generates the documentation) +#------------------------------------------------------------------------------ +# Options / Remoting +#------------------------------------------------------------------------------ +# Using https://github.com/sammyfreg/netImgui, you can use HelloImGui with remote rendering +# (this is useful for example to render ImGui on a remote device, or to render ImGui in a web browser) +option(HELLOIMGUI_WITH_NETIMGUI "Use netImgui for remote rendering" OFF) #------------------------------------------------------------------------------ # Options / Freetype diff --git a/CMakePresets.json b/CMakePresets.json index f96aafca..fdaaa20c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -29,14 +29,16 @@ "HELLOIMGUI_HAS_VULKAN": "ON", "HELLOIMGUI_HAS_DIRECTX11": "ON", "HELLOIMGUI_HAS_DIRECTX12": "ON", - "HELLOIMGUI_HAS_NULL": "ON" + "HELLOIMGUI_HAS_NULL": "ON", + "HELLOIMGUI_WITH_NETIMGUI": "ON" } }, { - "name": "build_null_backend", - "description": "Build with null backend.", + "name": "build_netimgui", + "description": "Build with netImgui.", "cacheVariables": { - "HELLOIMGUI_NULL_BACKEND": "ON" + "HELLOIMGUI_NULL_BACKEND": "ON", + "HELLOIMGUI_WITH_NETIMGUI": "ON" } }, { diff --git a/external/.gitignore b/external/.gitignore index fea25349..972ae40c 100644 --- a/external/.gitignore +++ b/external/.gitignore @@ -1,3 +1,4 @@ SDL SDL2-*/ qtimgui/ +netImgui/ diff --git a/hello_imgui_cmake/hello_imgui_build_lib.cmake b/hello_imgui_cmake/hello_imgui_build_lib.cmake index f53da250..ebe06eb3 100644 --- a/hello_imgui_cmake/hello_imgui_build_lib.cmake +++ b/hello_imgui_cmake/hello_imgui_build_lib.cmake @@ -1031,6 +1031,37 @@ function(_him_fetch_glfw_if_needed) endfunction() +################################################################################################### +# Remoting with https://github.com/sammyfreg/netImgui: API = him_with_netimgui +################################################################################################### +function(him_with_netimgui) + target_compile_definitions(${HELLOIMGUI_TARGET} PUBLIC HELLOIMGUI_WITH_NETIMGUI) + +# message(STATUS "HelloImGui: downloading and building netImgui") +# include(FetchContent) +# # Set(FETCHCONTENT_QUIET FALSE) +# FetchContent_Declare(net_imgui +# GIT_REPOSITORY https://github.com/pthom/netImgui.git +# GIT_TAG cmake_multiplatform +# GIT_PROGRESS TRUE +# ) +# FetchContent_MakeAvailable(net_imgui) + + + # Add netImgui to the project + set(netimgui_dir ${HELLOIMGUI_BASEPATH}/external/netImgui) + set(NETIMGUI_BUILD_IMGUI OFF CACHE BOOL "" FORCE) + set(NETIMGUI_BUILD_CLIENT ON CACHE BOOL "" FORCE) + set(NETIMGUI_BUILD_SERVER ON CACHE BOOL "" FORCE) + set(NETIMGUI_BUILD_SAMPLES OFF CACHE BOOL "" FORCE) + add_subdirectory(${netimgui_dir} netimgui) + + target_link_libraries(${HELLOIMGUI_TARGET} PUBLIC net_imgui_client) + him_add_installable_dependency(net_imgui_client) +endfunction() + + + ################################################################################################### # Miscellaneous: API = him_add_misc_options ################################################################################################### @@ -1083,8 +1114,9 @@ function(him_log_configuration) =========================================================================== Hello ImGui build options: =========================================================================== - Platform Backend(s): ${active_platform_backends} - Rendering Backend(s): ${active_rendering_backends} + Platform Backend(s): ${active_platform_backends} + Rendering Backend(s): ${active_rendering_backends} + Use netImGui: ${HELLOIMGUI_WITH_NETIMGUI} --------------------------------------------------------------------------- Options: HELLOIMGUI_USE_FREETYPE: ${HELLOIMGUI_USE_FREETYPE} (${HELLOIMGUI_FREETYPE_SELECTED_INFO}) @@ -1185,6 +1217,10 @@ function(him_main_add_hello_imgui_library) target_compile_definitions(${HELLOIMGUI_TARGET} PUBLIC HELLOIMGUI_HAS_NULL) endif() + if (HELLOIMGUI_WITH_NETIMGUI) + him_with_netimgui() + endif() + him_add_apple_options() him_add_linux_options() him_add_windows_options() diff --git a/src/hello_imgui/impl/hello_imgui.cpp b/src/hello_imgui/impl/hello_imgui.cpp index aec1a945..b43194af 100644 --- a/src/hello_imgui/impl/hello_imgui.cpp +++ b/src/hello_imgui/impl/hello_imgui.cpp @@ -197,6 +197,8 @@ std::string PlatformBackendTypeToString(PlatformBackendType platformBackendType) return "Glfw"; else if (platformBackendType == PlatformBackendType::Sdl) return "Sdl"; + else if (platformBackendType == PlatformBackendType::Null) + return "Null"; else return "Unknown platform backend"; } @@ -213,6 +215,8 @@ std::string RendererBackendTypeToString(RendererBackendType rendererBackendType) return "DirectX11"; else if (rendererBackendType == RendererBackendType::DirectX12) return "DirectX12"; + else if (rendererBackendType == RendererBackendType::Null) + return "Null"; else return "Unknown renderer backend"; } @@ -220,6 +224,8 @@ std::string RendererBackendTypeToString(RendererBackendType rendererBackendType) std::string GetBackendDescription() { const auto& params = GetRunnerParams(); + if (params->remoteParams.enableRemoting) + return "Remote"; std::string platformBackend = PlatformBackendTypeToString(params->platformBackendType); std::string rendererBackend = RendererBackendTypeToString(params->rendererBackendType); return platformBackend + " - " + rendererBackend; diff --git a/src/hello_imgui/internal/backend_impls/abstract_runner.cpp b/src/hello_imgui/internal/backend_impls/abstract_runner.cpp index 0c545d3a..25afff45 100644 --- a/src/hello_imgui/internal/backend_impls/abstract_runner.cpp +++ b/src/hello_imgui/internal/backend_impls/abstract_runner.cpp @@ -21,11 +21,16 @@ #define SCOPED_RELEASE_GIL_ON_MAIN_THREAD #endif +#ifdef HELLOIMGUI_WITH_NETIMGUI +#include "NetImgui_Api.h" +#endif + #include #include #include #include #include +#include #if __APPLE__ #include @@ -676,8 +681,96 @@ void AbstractRunner::Setup() style.Colors[ImGuiCol_TitleBgCollapsed].w = 1.f; } params.callbacks.SetupImGuiStyle(); + +#ifdef HELLOIMGUI_WITH_NETIMGUI + if (params.remoteParams.enableRemoting) + { + if (!NetImGui_Connect()) + { + IM_ASSERT(false && "NetImGui_Connect() failed! Please launch the server/display app before!"); + } + } +#endif +} + + +void AbstractRunner::NetImGui_LogConnectionStatusOnce() +{ + if (! params.remoteParams.enableRemoting) + return; +#ifdef HELLOIMGUI_WITH_NETIMGUI + // Log connection status and return + if (NetImgui::IsConnectionPending()) + { + static bool wasMessageShown = false; + if (!wasMessageShown) + { + wasMessageShown = true; + printf("Waiting for NetImgui connection...\n"); + } + } + else if (NetImgui::IsConnected()) + { + static bool wasMessageShown = false; + if (!wasMessageShown) + { + wasMessageShown = true; + printf("Connected to NetImgui server\n"); + } + } +#endif } + +bool AbstractRunner::NetImGui_Connect() +{ +#ifdef HELLOIMGUI_WITH_NETIMGUI + // 0. Startup NetImgui + NetImgui::Startup(); + // 1. Connect to NetImgui server + std::string clientName = params.remoteParams.clientName; + if (clientName.empty()) + clientName = params.appWindowParams.windowTitle; + if (clientName.empty()) + clientName = "HelloImGui"; + bool clientActiveImmediately = NetImgui::ConnectToApp(clientName.c_str(), params.remoteParams.serverHost.c_str(), params.remoteParams.serverPort); + + // 2. Wait a bit for the connection to be established (this is optional, just to display the messages after) + if (!clientActiveImmediately) + { + printf("Sleeping 0.1s to wait for NetImgui connection...\n"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // 3. Log connection status (this is optional also, since this is called also inside CreateFramesAndRender) + NetImGui_LogConnectionStatusOnce(); + if (!NetImgui::IsConnected() && !NetImgui::IsConnectionPending()) + { + printf("Connection to NetImgui server failed!\n"); + return false; + } + + // 4. Send Font texture to NetImgui server + 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); + NetImgui::SendDataTexture(pFonts->TexID, pPixelData, static_cast(width), static_cast(height), NetImgui::eTexFormat::kTexFmtA8); + } + if( pFonts->TexPixelsRGBA32) // && (pFonts->TexPixelsAlpha8 != client.mpFontTextureData || client.mFontTextureID != pFonts->TexID )) + { + uint8_t* pPixelData(nullptr); int width(0), height(0); + ImGui::GetIO().Fonts->GetTexDataAsRGBA32(&pPixelData, &width, &height); + NetImgui::SendDataTexture(pFonts->TexID, pPixelData, static_cast(width), static_cast(height), NetImgui::eTexFormat::kTexFmtRGBA8); + } + return true; +#else + return false; +#endif +} + + void AbstractRunner::SetLayoutResetIfNeeded() { if (params.imGuiWindowParams.defaultImGuiWindowType == DefaultImGuiWindowType::ProvideFullScreenDockSpace) @@ -771,6 +864,11 @@ void AbstractRunner::CreateFramesAndRender() // (it is here only to make the code more readable, and to enable to collapse blocks of code) constexpr bool foldable_region = true; +#ifdef HELLOIMGUI_WITH_NETIMGUI + if (params.remoteParams.enableRemoting) + NetImGui_LogConnectionStatusOnce(); +#endif + if (foldable_region) // Update frame rate stats { _UpdateFrameRateStats(); @@ -1137,6 +1235,13 @@ void AbstractRunner::TearDown(bool gotException) if (params.useImGuiTestEngine) TestEngineCallbacks::TearDown_ImGuiContextDestroyed(); #endif + +#ifdef HELLOIMGUI_WITH_NETIMGUI + if (params.remoteParams.enableRemoting) + { + NetImgui::Shutdown(); + } +#endif } diff --git a/src/hello_imgui/internal/backend_impls/abstract_runner.h b/src/hello_imgui/internal/backend_impls/abstract_runner.h index cf6bef1b..d4881b3b 100644 --- a/src/hello_imgui/internal/backend_impls/abstract_runner.h +++ b/src/hello_imgui/internal/backend_impls/abstract_runner.h @@ -98,6 +98,9 @@ class AbstractRunner void LayoutSettings_Load(); void LayoutSettings_Save(); + bool NetImGui_Connect(); + void NetImGui_LogConnectionStatusOnce(); + public: BackendApi::WindowPointer mWindow = nullptr; protected: diff --git a/src/hello_imgui/runner_params.h b/src/hello_imgui/runner_params.h index a7ea9c99..72184a08 100644 --- a/src/hello_imgui/runner_params.h +++ b/src/hello_imgui/runner_params.h @@ -133,6 +133,36 @@ struct FpsIdling // -------------------------------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- + +// @@md#NetImGuiParams + +// NetImGuiParams is a struct that contains the settings for displaying the application on a remote device. +// using https://github.com/sammyfreg/netImgui +// (This will only work if the application is compiled with the option -DHELLOIMGUI_WITH_NETIMGUI=ON) +struct NetImGuiParams +{ + bool enableRemoting = true; + + // Name of the client (if empty, will use params.appWindowParams.windowTitle) + // (The client is the app that contains the application logic) + std::string clientName = ""; + + // The server host (if empty, will use "localhost") + // The server is the app that simply displays the application on a remote device + std::string serverHost = "localhost"; + // The server port (default is 8888) + uint32_t serverPort = 8888; + + // The client port (default is 8889) + // This is unused since it is the client (i.e. the app logic) that connects to the server, + // using NetImgui::ConnectToApp + uint32_t clientPort = 8889; +}; + +// @@md + +// -------------------------------------------------------------------------------------------------------------------- // @@md#RunnerParams @@ -195,6 +225,8 @@ struct RunnerParams // Only useful when multiple rendering backend are compiled and available. RendererBackendType rendererBackendType = RendererBackendType::FirstAvailable; + // --------------- RemoteParams ------------------- + NetImGuiParams remoteParams; // --------------- Settings -------------------