diff --git a/UE4SS/include/GUI/GLFW3_OpenGL3.hpp b/UE4SS/include/GUI/GLFW3_OpenGL3.hpp index 84aa28ae4..e63d1c222 100644 --- a/UE4SS/include/GUI/GLFW3_OpenGL3.hpp +++ b/UE4SS/include/GUI/GLFW3_OpenGL3.hpp @@ -25,6 +25,7 @@ namespace RC::GUI auto handle_window_resize(int64_t param_1, int64_t param_2) -> void override; auto on_os_backend_set() -> void override; auto get_window_size() -> WindowSize override; + auto get_window_position() -> WindowPosition override; auto exit_requested() -> bool override; }; } // namespace RC::GUI diff --git a/UE4SS/include/GUI/GUI.hpp b/UE4SS/include/GUI/GUI.hpp index a404c6b0f..76026b6ec 100644 --- a/UE4SS/include/GUI/GUI.hpp +++ b/UE4SS/include/GUI/GUI.hpp @@ -10,6 +10,8 @@ #include #include +struct ImGuiSettingsHandler; + namespace RC::GUI { class GUITab; // dunno why forward declaration is necessary @@ -27,8 +29,14 @@ namespace RC::GUI struct WindowSize { - long x; - long y; + int32_t x; + int32_t y; + }; + + struct WindowPosition + { + int32_t x; + int32_t y; }; class GfxBackendBase @@ -60,6 +68,10 @@ namespace RC::GUI { return {}; }; + virtual auto get_window_position() -> WindowPosition + { + return {}; + }; virtual inline auto exit_requested() -> bool { return false; @@ -89,12 +101,13 @@ namespace RC::GUI public: virtual auto init() -> void = 0; virtual auto imgui_backend_newframe() -> void = 0; - virtual auto create_window() -> void = 0; + virtual auto create_window(int loc_x = 100, int loc_y = 100, int size_x = 1280, int size_y = 800) -> void = 0; virtual auto exec_message_loop(bool* exit_requested) -> void = 0; virtual auto shutdown() -> void = 0; virtual auto cleanup() -> void = 0; virtual auto get_window_handle() -> void* = 0; virtual auto get_window_size() -> WindowSize = 0; + virtual auto get_window_position() -> WindowPosition = 0; virtual auto on_gfx_backend_set() -> void = 0; }; @@ -114,7 +127,7 @@ namespace RC::GUI inline auto imgui_backend_newframe() -> void override { } - inline auto create_window() -> void override + inline auto create_window(int loc_x, int loc_y, int size_x, int size_y) -> void override { } inline auto exec_message_loop([[maybe_unused]] bool* exit_requested) -> void override @@ -134,6 +147,10 @@ namespace RC::GUI { return {}; } + inline auto get_window_position() -> WindowPosition override + { + return {}; + } inline auto on_gfx_backend_set() -> void override { } @@ -181,6 +198,14 @@ namespace RC::GUI public: using EndOfFrameCallback = std::function; + struct WindowSettings + { + int pos_x = 100; + int pos_y = 100; + int size_x = 1280; + int size_y = 800; + }; + private: std::unique_ptr m_gfx_backend{}; std::unique_ptr m_os_backend{}; @@ -191,6 +216,8 @@ namespace RC::GUI bool m_exit_requested{}; std::vector> m_tabs; std::mutex m_tabs_mutex; + std::string m_imgui_ini_file{}; + WindowSettings m_backend_window_settings; public: bool m_event_thread_busy{}; @@ -225,6 +252,14 @@ namespace RC::GUI auto on_update() -> void; auto main_loop_internal() -> void; + private: + // TODO: Move ImGui data saves to their own object + std::chrono::time_point m_imgui_last_save = std::chrono::steady_clock::now(); + static auto imgui_ue4ss_data_should_save() -> bool; + static auto imgui_ue4ss_data_read_open(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -> void*; + static auto imgui_ue4ss_data_read_line(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) -> void; + static auto imgui_ue4ss_data_write_all(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -> void; + public: static auto execute_at_end_of_frame(EndOfFrameCallback callback) -> void; }; diff --git a/UE4SS/include/GUI/Windows.hpp b/UE4SS/include/GUI/Windows.hpp index 3471b7a89..a2de6e470 100644 --- a/UE4SS/include/GUI/Windows.hpp +++ b/UE4SS/include/GUI/Windows.hpp @@ -12,12 +12,13 @@ namespace RC::GUI public: auto init() -> void override; auto imgui_backend_newframe() -> void override; - auto create_window() -> void override; + auto create_window(int loc_x, int loc_y, int size_x, int size_y) -> void override; auto exec_message_loop(bool* exit_requested) -> void override; auto shutdown() -> void override; auto cleanup() -> void override; auto get_window_handle() -> void* override; auto get_window_size() -> WindowSize override; + auto get_window_position() -> WindowPosition override; auto on_gfx_backend_set() -> void override; }; } // namespace RC::GUI diff --git a/UE4SS/src/GUI/GLFW3_OpenGL3.cpp b/UE4SS/src/GUI/GLFW3_OpenGL3.cpp index 85ceb85e0..24727ba9a 100644 --- a/UE4SS/src/GUI/GLFW3_OpenGL3.cpp +++ b/UE4SS/src/GUI/GLFW3_OpenGL3.cpp @@ -113,6 +113,14 @@ namespace RC::GUI return {w + left + right, h + top + bottom}; } + auto Backend_GLFW3_OpenGL3::get_window_position() -> WindowPosition + { + int left, top, right, bottom; + glfwGetWindowFrameSize(m_window, &left, &top, &right, &bottom); + + return {left, top}; + } + auto Backend_GLFW3_OpenGL3::exit_requested() -> bool { return glfwWindowShouldClose(m_window); diff --git a/UE4SS/src/GUI/GUI.cpp b/UE4SS/src/GUI/GUI.cpp index 3134e4fc5..e770705c0 100644 --- a/UE4SS/src/GUI/GUI.cpp +++ b/UE4SS/src/GUI/GUI.cpp @@ -21,6 +21,7 @@ #include "FaSolid900.hpp" #include #include +#include namespace RC::GUI { @@ -52,7 +53,6 @@ namespace RC::GUI auto DebuggingGUI::on_update() -> void { static bool show_window = true; - static bool is_console_open = true; if (!is_valid()) { @@ -61,6 +61,11 @@ namespace RC::GUI if (show_window) { + if (imgui_ue4ss_data_should_save()) + { + ImGui::SaveIniSettingsToDisk(m_imgui_ini_file.c_str()); + } + ImGui::SetNextWindowPos({0, 0}); auto current_window_size = m_os_backend->is_valid() ? m_os_backend->get_window_size() : m_gfx_backend->get_window_size(); ImGui::SetNextWindowSize({static_cast(current_window_size.x), static_cast(current_window_size.y)}); @@ -306,7 +311,7 @@ namespace RC::GUI while (!m_exit_requested && !m_gfx_backend->exit_requested()) { m_os_backend->exec_message_loop(&m_exit_requested); - + if (m_exit_requested) { break; @@ -365,6 +370,81 @@ namespace RC::GUI s_end_of_frame_callbacks.emplace_back(callback); } + auto DebuggingGUI::imgui_ue4ss_data_should_save() -> bool + { + auto& debugging_gui = UE4SSProgram::get_program().get_debugging_ui(); + + auto now = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(now - debugging_gui.m_imgui_last_save).count(); + if (duration <= 5) + { + return false; + } + + auto& settings = debugging_gui.m_backend_window_settings; + + auto const current_window_size = debugging_gui.m_os_backend->is_valid() ? debugging_gui.m_os_backend->get_window_size() : debugging_gui.m_gfx_backend->get_window_size(); + if (current_window_size.x != settings.size_x || current_window_size.y != settings.size_y) + { + settings.size_x = current_window_size.x; + settings.size_y = current_window_size.y; + debugging_gui.m_imgui_last_save = now; + return true; + } + + auto const current_window_position = debugging_gui.m_os_backend->is_valid() ? debugging_gui.m_os_backend->get_window_position() : debugging_gui.m_gfx_backend->get_window_position(); + if (current_window_position.x != settings.pos_x || current_window_position.y != settings.pos_y) + { + settings.pos_x = current_window_position.x; + settings.pos_y = current_window_position.y; + debugging_gui.m_imgui_last_save = now; + return true; + } + + return false; + } + + auto DebuggingGUI::imgui_ue4ss_data_read_open(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -> void* + { + // UE4SS ImGui Settings + return (void*)name; + } + + auto DebuggingGUI::imgui_ue4ss_data_read_line(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) -> void + { + auto& debugging_gui = UE4SSProgram::get_program().get_debugging_ui(); + + // Read settings for backend window size/position + WindowSettings& settings = debugging_gui.m_backend_window_settings; + if (std::string((const char*)entry) == "Backend_Window") + { + int x, y; + if (sscanf_s(line, "Pos=%i,%i", &x, &y) == 2) { settings.pos_x = x; settings.pos_y = y; } + else if (sscanf_s(line, "Size=%i,%i", &x, &y) == 2) { settings.size_x = x; settings.size_y = y; } + } + + } + + auto DebuggingGUI::imgui_ue4ss_data_write_all(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -> void + { + /*ImGuiContext& g = *ctx;*/ + auto& debugging_gui = UE4SSProgram::get_program().get_debugging_ui(); + + // Write settings for backend window size/position + auto current_window_size = debugging_gui.m_os_backend->is_valid() ? debugging_gui.m_os_backend->get_window_size() : debugging_gui.m_gfx_backend->get_window_size(); + auto current_window_position = debugging_gui.m_os_backend->is_valid() ? debugging_gui.m_os_backend->get_window_position() : debugging_gui.m_gfx_backend->get_window_position(); + + // Write to text buffer + buf->reserve(buf->size() + 15 * 6); // ballpark reserve + const char* backend_window_settings_name = "Backend_Window"; + buf->appendf("[%s][%s]\n", handler->TypeName, backend_window_settings_name); + buf->appendf("Pos=%d,%d\n", static_cast(current_window_position.x), static_cast(current_window_position.y)); + buf->appendf("Size=%d,%d\n", static_cast(current_window_size.x), static_cast(current_window_size.y)); + buf->append("\n"); + + // Add any additional ImGui UE4SS settings here + } + auto DebuggingGUI::setup(std::stop_token&& stop_token) -> void { if (!is_valid()) @@ -374,14 +454,28 @@ namespace RC::GUI m_thread_stop_token = stop_token; m_live_view.initialize(); - - m_os_backend->create_window(); - + IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); - (void)io; - io.IniFilename = nullptr; + m_imgui_ini_file = to_string(StringType{UE4SSProgram::get_program().get_working_directory()} + STR("\\imgui.ini")); + io.IniFilename = m_imgui_ini_file.c_str(); + + // Add .ini handle for UserData type + ImGuiSettingsHandler ini_handler; + ini_handler.TypeName = "UE4SSData"; + ini_handler.TypeHash = ImHashStr("UE4SSData"); + ini_handler.ReadOpenFn = imgui_ue4ss_data_read_open; + ini_handler.ReadLineFn = imgui_ue4ss_data_read_line; + ini_handler.WriteAllFn = imgui_ue4ss_data_write_all; + ImGui::AddSettingsHandler(&ini_handler); + + ImGui::LoadIniSettingsFromDisk(m_imgui_ini_file.c_str()); + + auto& debugging_gui = UE4SSProgram::get_program().get_debugging_ui(); + WindowSettings& settings = debugging_gui.m_backend_window_settings; + + m_os_backend->create_window(settings.pos_x, settings.pos_y, settings.size_x, settings.size_y); gui_setup_style(); io.Fonts->Clear(); diff --git a/UE4SS/src/GUI/Windows.cpp b/UE4SS/src/GUI/Windows.cpp index 5a1eb07b4..486654ef4 100644 --- a/UE4SS/src/GUI/Windows.cpp +++ b/UE4SS/src/GUI/Windows.cpp @@ -34,7 +34,7 @@ namespace RC::GUI ImGui_ImplWin32_NewFrame(); } - auto Backend_Windows::create_window() -> void + auto Backend_Windows::create_window(int loc_x, int loc_y, int size_x, int size_y) -> void { StringType title_bar_text{STR("UE4SS Debugging Tools")}; if (dynamic_cast(m_gfx_backend)) @@ -58,7 +58,7 @@ namespace RC::GUI s_wc.lpszClassName = title_bar_text.c_str(); s_wc.hIconSm = NULL; ::RegisterClassEx(&s_wc); - s_hwnd = ::CreateWindow(s_wc.lpszClassName, title_bar_text.c_str(), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, s_wc.hInstance, NULL); + s_hwnd = ::CreateWindow(s_wc.lpszClassName, title_bar_text.c_str(), WS_OVERLAPPEDWINDOW, loc_x, loc_y, size_x, size_y, NULL, NULL, s_wc.hInstance, NULL); if (!m_gfx_backend->create_device()) { @@ -105,7 +105,14 @@ namespace RC::GUI { RECT current_window_rect{}; GetWindowRect(s_hwnd, ¤t_window_rect); - return {current_window_rect.right - current_window_rect.left, current_window_rect.bottom - current_window_rect.top}; + return {static_cast(current_window_rect.right - current_window_rect.left), static_cast(current_window_rect.bottom - current_window_rect.top)}; + } + + auto Backend_Windows::get_window_position() -> WindowPosition + { + RECT current_window_rect{}; + GetWindowRect(s_hwnd, ¤t_window_rect); + return {static_cast(current_window_rect.left), static_cast(current_window_rect.top)}; } auto Backend_Windows::on_gfx_backend_set() -> void