Files
CoopAllTheThings/host/src/imgui_layer.cpp
BlackMark 30eccf749d Apply clang-format across the whole tree
Run clang-format (the repo's .clang-format: LLVM base, 120 cols, tabs,
Allman functions) over every source file so the tree is formatter-clean.
Whitespace only -- no behavior change; full x64 + x86 suites pass.

Also set SortIncludes: false in .clang-format. Windows include order is
load-bearing (windows.h must precede tlhelp32.h / mmreg.h / xinput.h /
dinput.h; winsock2.h must precede windows.h), and the default
alphabetical sort reorders tlhelp32.h ahead of windows.h -- a build
break. Leaving order alone keeps the manual, correct grouping.
2026-07-12 11:52:53 +02:00

105 lines
3.1 KiB
C++

#include "imgui_layer.hpp"
#include <string>
#include <imgui.h>
#include <imgui_impl_dx11.h>
#include <imgui_impl_win32.h>
#include "coop/dpi.hpp"
#include "coop/tool_paths.hpp"
#include "ui/app_chrome.hpp"
#include "util/utf8.hpp"
namespace coop {
ImGuiLayer::~ImGuiLayer()
{
if (initialized_) {
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
initialized_ = false;
}
}
bool ImGuiLayer::init(HWND hwnd, ID3D11Device* device, ID3D11DeviceContext* context)
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
// Persist the panel layout next to the executable so window positions/sizes survive
// restarts. CWD is unreliable when Steam launches us under the donor appid, so anchor
// to the exe directory. If a layout already exists, tell the chrome not to force the
// computed default on startup (which would clobber the restored positions).
const std::wstring ini_w = exe_directory() + L"coop_layout.ini";
const bool had_layout = GetFileAttributesW(ini_w.c_str()) != INVALID_FILE_ATTRIBUTES;
// ImGui treats IniFilename as UTF-8 (it converts to wide for _wfopen), so narrow to UTF-8, not
// the system ANSI code page.
ini_path_ = narrow(ini_w);
io.IniFilename = ini_path_.c_str();
set_layout_persisted(had_layout);
if (!ImGui_ImplWin32_Init(hwnd)) {
return false;
}
if (!ImGui_ImplDX11_Init(device, context)) {
return false;
}
initialized_ = true;
// Scale the overlay (font + style) to the monitor the window opened on. The process is
// per-monitor-DPI-aware (set in wWinMain), so GetDpiForWindow returns that monitor's real DPI.
apply_dpi(GetDpiForWindow(hwnd));
return true;
}
void ImGuiLayer::apply_dpi(unsigned dpi)
{
const float scale = dpi_scale_from(dpi);
ImGuiIO& io = ImGui::GetIO();
// Rebuild the font atlas at the DPI-scaled pixel size so ImGui's built-in default font is
// rasterized crisp at the target size, rather than bitmap-stretched the way io.FontGlobalScale
// would leave it. scaled_font_px() scales the one named base-size constant.
io.Fonts->Clear();
ImFontConfig cfg;
cfg.SizePixels = scaled_font_px(dpi);
io.Fonts->AddFontDefault(&cfg);
// Reset to the base dark theme, then scale spacing/padding/border sizes to match. Resetting
// first keeps repeated DPI changes from compounding (ScaleAllSizes multiplies the style in place).
ImGui::StyleColorsDark();
ImGui::GetStyle().ScaleAllSizes(scale);
// Drop the DX11 backend's cached font texture so it rebuilds from the new atlas next frame. Before
// the first frame nothing is built yet, so this is a harmless no-op during init().
if (initialized_) {
ImGui_ImplDX11_InvalidateDeviceObjects();
}
dpi_scale_ = scale;
}
void ImGuiLayer::set_dpi(unsigned dpi)
{
if (!initialized_ || dpi_scale_from(dpi) == dpi_scale_) {
return; // not up yet, or the scale didn't actually change -- skip a needless atlas rebuild
}
apply_dpi(dpi);
}
void ImGuiLayer::begin_frame()
{
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
}
void ImGuiLayer::end_frame()
{
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
} // namespace coop