DLT_Splitter/Src/Main.cpp

438 lines
19 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// IMGUI相关库包含
#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"
// ImNodes相关
#include "imnodes.h"
extern "C"
{
// Mingw 相关库
#include "d3d11.h"
#include "tchar.h"
#include "wchar.h"
}
// 自定义头文件
#include "Config.hpp" // 配置文件
#include "Global_Variables.hpp" // 全局变量相关
#include "UI_Layout.hpp" // UI布局相关
#include "Command_Solve.hpp" // 命令处理相关
// 辅助函数声明
bool CreateDeviceD3D(HWND hWnd); // 创建 Direct 3D 设备
void CleanupDeviceD3D(); // 清理 Direct3D 设备
void CreateRenderTarget(); // 创建渲染目标
void CleanupRenderTarget(); // 清理渲染目标
// Windows窗体事件处理函数声明
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// 重定义窗体消息处理函数 >> imgui_impl_win32.cpp
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// 入口主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// 命令行前置处理
Pre_Command_Solve();
// 启用DPI响应并获取主显示器分辨率
ImGui_ImplWin32_EnableDpiAwareness();
float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY));
// 窗体类结构体
WNDCLASSEXW wc; // Windows 扩展窗体类
wc.cbSize = sizeof(wc); // 类结构大小
wc.style = CS_CLASSDC; // 绘制样式使用类设备上下文 Class Device Context
wc.lpfnWndProc = WndProc; // 窗体事件处理回调函数
wc.cbClsExtra = 0L; // 为窗口类额外分配的字节数,通常为 0
wc.cbWndExtra = 0L; // 为窗口实例额外分配的字节数,通常为 0
wc.hInstance = hInstance; // 应用程序实例的句柄。
wc.hIcon = LoadIconW(hInstance, IDI_APPLICATION); // 应用图标样式
wc.hCursor = LoadCursorW(hInstance, IDC_ARROW); // 应用光标样式
wc.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME; // 窗体背景颜色刷
wc.lpszMenuName = nullptr; // 窗体菜单名称
wc.lpszClassName = L"DLT_Splitter_Base_Window_Class"; // 窗体注册类名
wc.hIconSm = LoadIconW(hInstance, IDI_APPLICATION); // 应用小图标样式
// 注册窗体类
RegisterClassExW(&wc);
// 创建窗体
Main_Window_hWnd = CreateWindowW(wc.lpszClassName, // 创建的窗体类名
MAIN_FRAME_TITTLE, // 窗体标题
WS_OVERLAPPEDWINDOW, // 窗体风格为标准可重叠的顶层窗口
100, // 起始坐标x
100, // 起始坐标y
(int)(MAIN_FRAME_SIZE_WIDTH_MIN * main_scale), // 窗体长
(int)(MAIN_FRAME_SIZE_HEIGHT_MIN * main_scale), // 窗体宽
nullptr, // 父窗体句柄
nullptr, // 菜单句柄
wc.hInstance, // 创建实例句柄
nullptr); // 额外的事件参数指针
// 在窗体内创建 Direct 3D 设备
if (!CreateDeviceD3D(Main_Window_hWnd))
{
// 未创建完成时清理 Direct 3D 设备并清理窗体注册类
CleanupDeviceD3D();
UnregisterClassW(wc.lpszClassName, wc.hInstance);
// 返回错误代码
return -1;
}
// 启用文件拖拽输入
DragAcceptFiles(Main_Window_hWnd, TRUE);
// 显示窗体并强制更新一次窗体
ShowWindow(Main_Window_hWnd, SW_SHOWDEFAULT);
UpdateWindow(Main_Window_hWnd);
// 检查版本
IMGUI_CHECKVERSION();
// 建立 Dear ImGui 上下文
ImGui::CreateContext();
// 建立ImNodes 上下文
ImNodes::CreateContext();
// 获取ImGui IO 设备配置结构体
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
// io.ConfigViewportsNoAutoMerge = true;
// io.ConfigViewportsNoTaskBarIcon = true;
// io.ConfigDockingAlwaysTabBar = true;
// io.ConfigDockingTransparentPayload = true;
io.IniFilename = nullptr; // 不生成ini布局配置文件
// 主题为亮/暗
ImGui::StyleColorsLight();
// ImGui::StyleColorsDark();
// Setup scaling
// 获取风格渲染配置并整缩放比例
ImGuiStyle &style = ImGui::GetStyle();
style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose)
io.ConfigDpiScaleFonts = true; // [Experimental] Automatically overwrite style.FontScaleDpi in Begin() when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now.
io.ConfigDpiScaleViewports = true; // [Experimental] Scale Dear ImGui and Platform Windows when Monitor DPI changes.
// 调整视窗移动出主窗体范围外时的风格以匹配原生风格
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
// 初始化渲染器后端(Renderer backends)
ImGui_ImplWin32_Init(Main_Window_hWnd);
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
// 字体配置相关
{
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details. If you like the default font but want it to scale better, consider using the 'ProggyVector' from the same author!
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
// style.FontSizeBase = 20.0f;
// io.Fonts->AddFontDefault();
// io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
// io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
// io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
// io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
// ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf");
// IM_ASSERT(font != nullptr);
// 加载字体为微软雅黑
io.Fonts->AddFontFromFileTTF("c:/windows/Fonts/msyh.ttc", 15.0f * main_scale, NULL, io.Fonts->GetGlyphRangesChineseFull());
}
// 主事件循环
bool done = false;
while (!done)
{
// 非阻塞式获取所有窗体消息Pick消息后从消息队列删除并转译和派发消息
MSG msg;
while (PeekMessageW(&msg, nullptr, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg); // 转译
DispatchMessageW(&msg); // 派发
// 窗体关闭事件
if (msg.message == WM_QUIT)
done = true;
}
if (done)
break;
// 处理窗体最小化或屏幕被锁定
if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
{
// 休眠10ms并跳过渲染
continue;
}
g_SwapChainOccluded = false;
// 开始创建当前绘制帧(创建顺序:DirectX -> Win32 -> ImGui)
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// 此处进行UI布局
UI_Layout();
// 渲染部分
{
// 调用 ImGui 渲染器进行预渲染
ImGui::Render();
// 设置D3D渲染目标
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
// 创建一个带透明度通道的RGB色
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
const float clear_color_with_alpha[4] = {clear_color.x * clear_color.w,
clear_color.y * clear_color.w,
clear_color.z * clear_color.w,
clear_color.w};
// 使用创建的RGB色擦除D3D渲染画布
g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
// 渲染 ImGui 原始画面
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
// 启用脱离窗口渲染时更新主窗体外的部分
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
// 呈现当前帧到画布
// HRESULT hr = g_pSwapChain->Present(0, 0); // 非垂直同步
HRESULT hr = g_pSwapChain->Present(1, 0); // 垂直同步
// HRESULT hr = g_pSwapChain->Present(2, 0); // 等待双倍的垂直同步时间
// 更新交换链阻塞状态
g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
}
}
// 收尾清理工作
ImGui_ImplDX11_Shutdown(); // 关闭 ImGui DX11 组件
ImGui_ImplWin32_Shutdown(); // 关闭 ImGui Win32 组件
ImGui::DestroyContext(); // 销毁 ImGui 上下文
ImNodes::DestroyContext(); // 销毁 ImNodes 上下文
CleanupDeviceD3D(); // 清理 Direct 3D 设备
DestroyWindow(Main_Window_hWnd); // 销毁窗体
UnregisterClassW(wc.lpszClassName, wc.hInstance); // 取消窗体注册类
return 0;
}
// 创建 Direct 3D 设备
bool CreateDeviceD3D(HWND hWnd)
{
// Setup swap chain
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2;
sd.BufferDesc.Width = 0;
sd.BufferDesc.Height = 0;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
UINT createDeviceFlags = 0;
// createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
D3D_FEATURE_LEVEL featureLevel;
const D3D_FEATURE_LEVEL featureLevelArray[2] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_0,
};
HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available.
res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
if (res != S_OK)
return false;
// Disable DXGI's default Alt+Enter fullscreen behavior.
// - You are free to leave this enabled, but it will not work properly with multiple viewports.
// - This must be done for all windows associated to the device. Our DX11 backend does this automatically for secondary viewports that it creates.
IDXGIFactory *pSwapChainFactory;
if (SUCCEEDED(g_pSwapChain->GetParent(IID_PPV_ARGS(&pSwapChainFactory))))
pSwapChainFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER);
CreateRenderTarget();
return true;
}
// 清理 Direct3D 设备
void CleanupDeviceD3D()
{
CleanupRenderTarget();
if (g_pSwapChain)
{
g_pSwapChain->Release();
g_pSwapChain = nullptr;
}
if (g_pd3dDeviceContext)
{
g_pd3dDeviceContext->Release();
g_pd3dDeviceContext = nullptr;
}
if (g_pd3dDevice)
{
g_pd3dDevice->Release();
g_pd3dDevice = nullptr;
}
}
// 创建渲染目标
void CreateRenderTarget()
{
ID3D11Texture2D *pBackBuffer;
g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
pBackBuffer->Release();
}
// 清理渲染目标
void CleanupRenderTarget()
{
if (g_mainRenderTargetView)
{
g_mainRenderTargetView->Release();
g_mainRenderTargetView = nullptr;
}
}
// Windows窗体事件处理函数
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// 优先让ImGui处理消息
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true;
// 筛查并处理余下的消息事件
switch (msg)
{
case WM_DROPFILES: // 文件拖拽事件
{
HDROP hDrop = (HDROP)wParam; // 拖拽数据句柄
// 获取拖拽的文件总数
UINT fileCount = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
// 非法输入检测
bool with_illegal_input = false;
bool with_duplicated_input = false;
// 添加到文件列表
for (UINT count = 0; count < fileCount; count++)
{
wchar_t filePath[MAX_PATH] = {L'\0'};
DragQueryFileW(
hDrop, // 拖拽句柄
count, // 文件索引0 表示第一个文件)
filePath, // 存储路径的缓冲区
MAX_PATH // 缓冲区大小
);
char file_path_multi_byte[MAX_PATH] = {'\0'};
int size_needed = WideCharToMultiByte(CP_UTF8, 0, filePath, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_UTF8, 0, filePath, -1, file_path_multi_byte, size_needed, NULL, NULL);
// 重复文件剔除
bool duplicated = false;
for (Input_File_Node *p_file_node = input_dlt_file_list_head; p_file_node != nullptr; p_file_node = p_file_node->p_next)
if (!strcmp(p_file_node->file_name_str, file_path_multi_byte))
{
duplicated = true;
with_duplicated_input = true;
break;
}
if (duplicated)
continue;
// 文件类型后缀过滤
const char *p_extension_name = file_path_multi_byte + (strlen(file_path_multi_byte) - 4);
if (p_extension_name[0] != '.' || toupper(p_extension_name[1]) != 'D' || toupper(p_extension_name[2]) != 'L' || toupper(p_extension_name[3]) != 'T')
{
with_illegal_input = true;
continue;
}
// 开辟新文件节点
Input_File_Node *p_new_file_node = (Input_File_Node *)malloc(sizeof(Input_File_Node));
p_new_file_node->file_name_str = (char *)malloc(strlen(file_path_multi_byte) + 1);
memset(p_new_file_node->file_name_str, '\0', strlen(file_path_multi_byte) + 1);
memcpy(p_new_file_node->file_name_str, file_path_multi_byte, strlen(file_path_multi_byte) + 1);
p_new_file_node->p_next = nullptr;
// 插入文件列表
if (input_dlt_file_list_head == nullptr)
input_dlt_file_list_head = p_new_file_node;
else
{
Input_File_Node *p_target_node = input_dlt_file_list_head;
for (size_t count = 1; count < input_dlt_file_count; count++)
p_target_node = p_target_node->p_next;
p_target_node->p_next = p_new_file_node;
}
input_dlt_file_count++;
}
// 释放拖拽资源(必须调用,否则内存泄漏)
DragFinish(hDrop);
if (with_duplicated_input)
MessageBoxW(hWnd, L"存在重复输入", L"", MB_OK);
if (with_illegal_input)
MessageBoxW(hWnd, L"存在非法输入,仅支持 .dlt 文件", L"", MB_OK);
return 0;
}
case WM_SIZE: // 窗体大小变事件
if (wParam != SIZE_MINIMIZED) // 窗体未处于最小化时进行重绘
{
// 清理渲染器并重新创建对应窗体大小的渲染器目标 低字为宽,高字为长
CleanupRenderTarget();
g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
CreateRenderTarget();
}
return 0;
case WM_SYSCOMMAND: // 系统指令事件
if ((wParam & 0xfff0) == SC_KEYMENU) // 禁用ALT键菜单响应交给IMGUI实现 Disable ALT
return 0;
break;
case WM_DESTROY: // 窗体销毁事件
PostQuitMessage(0); // 派发应用退出(Quit)消息
return 0;
}
// 剩余的消息交给默认的消息处理函数
return DefWindowProcW(hWnd, msg, wParam, lParam);
}