h2-mod/src/client/component/gui.cpp

278 lines
6.4 KiB
C++
Raw Normal View History

2021-12-19 11:42:37 -05:00
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "scheduler.hpp"
2021-12-19 19:53:01 -05:00
#include "gui.hpp"
2021-12-19 11:42:37 -05:00
#include <utils/string.hpp>
#include <utils/hook.hpp>
2021-12-19 19:53:01 -05:00
#include <utils/concurrency.hpp>
2021-12-19 11:42:37 -05:00
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
namespace gui
{
2021-12-19 19:53:01 -05:00
std::unordered_map<std::string, bool> enabled_menus;
2021-12-19 11:42:37 -05:00
namespace
{
2021-12-19 19:53:01 -05:00
utils::concurrency::container<std::vector<std::function<void()>>> on_frame_callbacks;
2021-12-20 12:58:23 -05:00
utils::concurrency::container<std::vector<notification_t>> notifications;
2021-12-19 19:53:01 -05:00
2021-12-19 11:42:37 -05:00
ID3D11Device* device;
ID3D11DeviceContext* device_context;
bool initialized = false;
bool toggled = false;
void initialize_gui_context()
{
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui_ImplWin32_Init(*game::hWnd);
ImGui_ImplDX11_Init(device, device_context);
initialized = true;
}
void new_gui_frame()
{
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
*game::keyCatchers |= 0x10;
}
void end_gui_frame()
{
ImGui::EndFrame();
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
2021-12-19 19:53:01 -05:00
void toggle_menu(const std::string& name)
{
enabled_menus[name] = !enabled_menus[name];
}
2021-12-20 11:32:06 -05:00
void show_notifications()
{
2021-12-22 16:25:41 -05:00
static const auto window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_NoMove;
2021-12-20 11:32:06 -05:00
2021-12-20 12:58:23 -05:00
notifications.access([](std::vector<notification_t>& notifications_)
2021-12-20 11:32:06 -05:00
{
2021-12-20 12:58:23 -05:00
auto index = 0;
for (auto i = notifications_.begin(); i != notifications_.end(); ++i)
2021-12-20 11:32:06 -05:00
{
2021-12-20 12:58:23 -05:00
const auto now = std::chrono::high_resolution_clock::now();
if (now - i->creation_time >= i->duration)
{
notifications_.erase(i--);
continue;
}
2021-12-20 11:32:06 -05:00
2021-12-22 16:25:41 -05:00
const auto title = utils::string::truncate(i->title, 34, "...");
const auto text = utils::string::truncate(i->text, 34, "...");
2021-12-20 11:32:06 -05:00
2021-12-20 12:58:23 -05:00
ImGui::SetNextWindowSizeConstraints(ImVec2(250, 50), ImVec2(250, 50));
ImGui::SetNextWindowBgAlpha(0.6f);
ImGui::Begin(utils::string::va("Notification #%i", index), nullptr, window_flags);
2021-12-20 11:32:06 -05:00
2021-12-20 12:58:23 -05:00
ImGui::SetWindowPos(ImVec2(10, 30.f + static_cast<float>(index) * 60.f));
ImGui::SetWindowSize(ImVec2(250, 0));
ImGui::Text(title.data());
ImGui::Text(text.data());
2021-12-20 11:32:06 -05:00
2021-12-20 12:58:23 -05:00
ImGui::End();
2021-12-20 11:32:06 -05:00
2021-12-20 12:58:23 -05:00
index++;
}
});
2021-12-20 11:32:06 -05:00
}
2021-12-22 16:25:41 -05:00
void menu_checkbox(const std::string& name, const std::string& menu)
{
ImGui::Checkbox(name.data(), &enabled_menus[menu]);
}
2021-12-19 11:42:37 -05:00
void gui_draw()
{
2021-12-20 11:32:06 -05:00
show_notifications();
2021-12-19 19:53:01 -05:00
on_frame_callbacks.access([](std::vector<std::function<void()>>& callbacks)
{
for (const auto& callback : callbacks)
{
callback();
}
});
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("Windows"))
{
2021-12-22 16:25:41 -05:00
menu_checkbox("Asset list", "asset_list");
menu_checkbox("Entity list", "entity_list");
menu_checkbox("Console", "console");
2021-12-19 19:53:01 -05:00
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
2021-12-19 11:42:37 -05:00
}
2021-12-19 19:53:01 -05:00
void gui_on_frame()
2021-12-19 11:42:37 -05:00
{
if (!initialized)
{
initialize_gui_context();
}
if (toggled)
{
new_gui_frame();
gui_draw();
end_gui_frame();
}
}
HRESULT d3d11_create_device_stub(IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software,
UINT Flags, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion,
ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel, ID3D11DeviceContext** ppImmediateContext)
{
const auto result = D3D11CreateDevice(pAdapter, DriverType, Software, Flags, pFeatureLevels,
FeatureLevels, SDKVersion, ppDevice, pFeatureLevel, ppImmediateContext);
if (ppDevice != nullptr && ppImmediateContext != nullptr)
{
device = *ppDevice;
device_context = *ppImmediateContext;
}
return result;
}
2021-12-19 19:53:01 -05:00
void dxgi_swap_chain_present_stub(utils::hook::assembler& a)
{
2021-12-19 19:53:01 -05:00
a.pushad64();
a.call_aligned(gui_on_frame);
a.popad64();
a.mov(r8d, esi);
a.mov(edx, r15d);
a.mov(rcx, rdi);
a.call_aligned(rbx);
a.mov(ecx, eax);
a.jmp(0x7A14D1_b);
}
2021-12-19 11:42:37 -05:00
utils::hook::detour wnd_proc_hook;
LRESULT wnd_proc_stub(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
2021-12-22 16:25:41 -05:00
if (wParam != VK_ESCAPE && toggled && ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
2021-12-19 11:42:37 -05:00
{
return TRUE;
}
return wnd_proc_hook.invoke<LRESULT>(hWnd, msg, wParam, lParam);
}
}
bool gui_key_event(const int local_client_num, const int key, const int down)
{
if (key == game::K_F10 && down)
{
toggled = !toggled;
return false;
}
2021-12-19 19:53:01 -05:00
if (key == game::K_ESCAPE && down && toggled)
{
toggled = false;
return false;
}
2021-12-19 11:42:37 -05:00
return !toggled;
}
bool gui_char_event(const int local_client_num, const int key)
{
return !toggled;
}
bool gui_mouse_event(const int local_client_num, int x, int y)
{
return !toggled;
}
2021-12-19 19:53:01 -05:00
void on_frame(const std::function<void()>& callback)
{
on_frame_callbacks.access([callback](std::vector<std::function<void()>>& callbacks)
{
callbacks.push_back(callback);
});
}
bool is_menu_open(const std::string& name)
{
return enabled_menus[name];
}
2021-12-20 11:32:06 -05:00
void notification(const std::string& title, const std::string& text, const std::chrono::milliseconds duration)
{
notification_t notification{};
notification.title = title;
notification.text = text;
notification.duration = duration;
notification.creation_time = std::chrono::high_resolution_clock::now();
2021-12-20 12:58:23 -05:00
notifications.access([notification](std::vector<notification_t>& notifications_)
{
notifications_.insert(notifications_.begin(), notification);
});
2021-12-20 11:32:06 -05:00
}
2021-12-21 09:21:50 -05:00
void copy_to_clipboard(const std::string& text)
{
utils::string::set_clipboard_data(text);
gui::notification("Text copied to clipboard", utils::string::va("\"%s\"", text.data()));
}
2021-12-19 11:42:37 -05:00
class component final : public component_interface
{
public:
void* load_import(const std::string& library, const std::string& function) override
{
if (function == "D3D11CreateDevice")
{
return d3d11_create_device_stub;
}
return nullptr;
}
void post_unpack() override
{
2021-12-19 19:53:01 -05:00
utils::hook::jump(0x7A14C4_b, utils::hook::assemble(dxgi_swap_chain_present_stub), true);
2021-12-19 11:42:37 -05:00
wnd_proc_hook.create(0x650F10_b, wnd_proc_stub);
}
void pre_destroy() override
{
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
};
}
REGISTER_COMPONENT(gui::component)