Skip to content

Commit 5005906

Browse files
committed
Fixed Windows preemptive scaling for HiDPI displays #4
1 parent 42407ff commit 5005906

File tree

2 files changed

+79
-44
lines changed

2 files changed

+79
-44
lines changed

README.md

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,42 +24,63 @@ msbuild /m /p:Configuration=Release .
2424
## Concept
2525
To enable dragging from some custom client area, our `WndProc` needs to return `HTCAPTION` when we know we're not over an imgui window. Therefore our `WndProc` needs to do those hittests with knowledge of those window rects:
2626
```cpp
27-
BorderlessWindow window; // Instantiate our borderless window
2827
// ...
2928
// (In Render Loop) Update imgui window rects for hit testing
3029
{
31-
// Get ScreenPos offset
32-
ImGuiViewport* vp = ImGui::GetMainViewport();
33-
HWND handle = (HWND)vp->PlatformHandle;
34-
RECT r;
35-
GetWindowRect(handle, &r);
36-
37-
// Only apply offset if Multi-viewports are not enabled
38-
ImVec2 origin = { (float)r.left, (float)r.top };
39-
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
30+
ImVec2 origin = { 0, 0 };
31+
if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) // Only apply offset if Multi-viewports are not enabled
4032
{
41-
origin = { 0, 0 };
33+
RECT r;
34+
GetWindowRect(hWnd, &r); // Get ScreenPos offset
35+
origin = { (float)r.left, (float)r.top };
4236
}
4337

44-
// Add imgui windows that aren't default rects/dockspaces/etc to client area whitelist, but explicitly include imgui demo
38+
// EXAMPLE:
39+
// Add imgui windows that aren't default rects/dockspaces/windows over viewports,
40+
// etc. to client area whitelist, but explicitly include imgui demo
4541
std::vector<RECT> WindowRects;
4642
for (ImGuiWindow* window : ImGui::GetCurrentContext()->Windows)
4743
{
48-
if ((!(std::string(window->Name).find("Default") != std::string::npos) &&
49-
(!(std::string(window->Name).find("Dock") != std::string::npos)) &&
50-
(!(std::string(window->Name).find("Menu") != std::string::npos))) ||
51-
(std::string(window->Name).find("Dear ImGui Demo") != std::string::npos))
52-
{
53-
ImVec2 pos = window->Pos;
54-
ImVec2 size = window->Size;
55-
RECT rect = { origin.x + pos.x,
56-
origin.y + pos.y,
57-
origin.x + (pos.x + size.x),
58-
origin.y + (pos.y + size.y) };
59-
WindowRects.push_back(rect);
44+
if(window->Active)
45+
{
46+
if ((!(std::string(window->Name).find("Default") != std::string::npos) &&
47+
(!(std::string(window->Name).find("Dock") != std::string::npos)) &&
48+
(!(std::string(window->Name).find("Menu") != std::string::npos)) &&
49+
(!(std::string(window->Name).find("WindowOverViewport") != std::string::npos))) ||
50+
(std::string(window->Name).find("Dear ImGui Demo") != std::string::npos))
51+
{
52+
ImVec2 pos = window->Pos;
53+
ImVec2 size = window->Size;
54+
RECT rect = { (LONG)(origin.x + pos.x),
55+
(LONG)(origin.y + pos.y),
56+
(LONG)(origin.x + (pos.x + size.x)),
57+
(LONG)(origin.y + (pos.y + size.y)) };
58+
59+
WindowRects.push_back(rect);
60+
}
6061
}
6162
}
62-
window.set_client_area(WindowRects);
63+
g_ClientCustomClientArea = std::move(WindowRects);
64+
}
65+
66+
// ... (In the WndProc) Update imgui window rects for hit testing
67+
case WM_NCHITTEST: {
68+
switch (result) {
69+
case left: return HTLEFT;
70+
case right: return HTRIGHT;
71+
case top: return HTTOP;
72+
case bottom: return HTBOTTOM;
73+
case top | left: return HTTOPLEFT;
74+
case top | right: return HTTOPRIGHT;
75+
case bottom | left: return HTBOTTOMLEFT;
76+
case bottom | right: return HTBOTTOMRIGHT;
77+
case client: {
78+
for (RECT rect : g_ClientCustomClientArea)
79+
if (PtInRect(&rect, cursor)) return HTCLIENT;
80+
return HTCAPTION;
81+
}
82+
default: return HTNOWHERE;
83+
}
6384
}
6485
```
6586

imgui-borderless-win32/main.cpp

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ extern "C"{
22
#define WIN32_LEAN_AND_MEAN
33
#include <windows.h>
44
#include <windowsx.h>
5+
#include <shellscalingapi.h>
56
#include <dwmapi.h>
67
#include <GL/gl.h>
78
#include "win32_window.h"
89
#include "swcadef.h" // Courtesy of https://gist.github.com/sylveon/9c199bb6684fe7dffcba1e3d383fb609
910
}
1011

12+
#pragma comment (lib, "shcore")
13+
#pragma comment (lib, "dwmapi")
1114
#pragma comment (lib, "opengl32")
1215
#pragma comment (lib, "glu32")
13-
#pragma comment (lib, "dwmapi")
1416

1517
#include "imgui.h"
1618
#include "imgui_internal.h"
@@ -52,7 +54,9 @@ static void Hook_Renderer_DestroyWindow(ImGuiViewport* viewport);
5254
static void Hook_Platform_RenderWindow(ImGuiViewport* viewport, void*);
5355
static void Hook_Renderer_SwapBuffers(ImGuiViewport* viewport, void*);
5456

55-
int APIENTRY wWinMain(
57+
int
58+
APIENTRY
59+
wWinMain(
5660
_In_ HINSTANCE hInstance,
5761
_In_opt_ HINSTANCE hPrevInstance,
5862
_In_ LPWSTR lpCmdLine,
@@ -63,29 +67,37 @@ int APIENTRY wWinMain(
6367
UNREFERENCED_PARAMETER(lpCmdLine);
6468
UNREFERENCED_PARAMETER(nCmdShow);
6569

66-
ImGui_ImplWin32_EnableDpiAwareness();
67-
win32_window_t w32Window = {0};
68-
win32_window_create(&w32Window, 1080, 720, 0, ImGuiBorderlessWin32::windowed);
69-
w32Window.msgHook = (win32_wndproc_hook_t)WndProcHook;
70+
win32_window_t win32_window = { 0 };
7071

71-
if (!CreateDeviceWGL(w32Window.hWnd, &g_MainWindow))
72+
HRESULT hr = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); // This can be set by a program's manifest or its corresponding registry settings
73+
if (E_INVALIDARG == hr)
7274
{
73-
CleanupDeviceWGL(w32Window.hWnd, &g_MainWindow);
74-
::DestroyWindow(w32Window.hWnd);
75-
::UnregisterClass(w32Window.tcClassName, GetModuleHandle(NULL));
75+
return 1;
76+
}
77+
78+
win32_window_create(&win32_window, 1080, 720, 0, ImGuiBorderlessWin32::windowed);
79+
win32_window.msgHook = (win32_wndproc_hook_t)WndProcHook;
80+
81+
if (!CreateDeviceWGL(win32_window.hWnd, &g_MainWindow))
82+
{
83+
CleanupDeviceWGL(win32_window.hWnd, &g_MainWindow);
84+
::DestroyWindow(win32_window.hWnd);
85+
::UnregisterClass(win32_window.tcClassName, GetModuleHandle(NULL));
7686
return 1;
7787
}
7888

7989
wglMakeCurrent(g_MainWindow.hDC, g_hRC);
8090

81-
::ShowWindow(w32Window.hWnd, SW_SHOWDEFAULT);
82-
::UpdateWindow(w32Window.hWnd);
91+
::ShowWindow(win32_window.hWnd, SW_SHOWDEFAULT);
92+
::UpdateWindow(win32_window.hWnd);
8393

8494
IMGUI_CHECKVERSION();
8595
ImGui::CreateContext();
8696
ImGuiIO& io = ImGui::GetIO();
8797
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
8898
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
99+
io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts;
100+
io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports;
89101

90102
// Setup Dear ImGui style
91103
ImGui::StyleColorsDark();
@@ -98,8 +110,9 @@ int APIENTRY wWinMain(
98110
}
99111

100112
// Setup Platform/Renderer backends
101-
ImGui_ImplWin32_InitForOpenGL((void*)w32Window.hWnd);
113+
ImGui_ImplWin32_InitForOpenGL((void*)win32_window.hWnd);
102114
ImGui_ImplOpenGL3_Init();
115+
ImGui_ImplWin32_EnableDpiAwareness();
103116

104117
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
105118
{
@@ -117,13 +130,14 @@ int APIENTRY wWinMain(
117130
for(;;)
118131
{
119132
MSG msg; //- Pump message loop; break on WM_QUIT
120-
if (!win32_window_pump_message_loop(&w32Window, &msg, FALSE)) break;
133+
if (!win32_window_pump_message_loop(&win32_window, &msg, FALSE)) break;
121134

122135
//- Set the client render function callback if not done so already (So we can also render in sizemoves)
123136
if (!g_ClientRenderFunction)
124137
{
125-
g_ClientRenderFunction = [io](HWND hWnd) {
138+
g_ClientRenderFunction = [](HWND hWnd) {
126139
static ImVec4 clear_color(.0f, .0f, .0f, .0f);
140+
ImGuiIO& io = ImGui::GetIO();
127141

128142
ImGui_ImplOpenGL3_NewFrame();
129143
ImGui_ImplWin32_NewFrame();
@@ -204,17 +218,17 @@ int APIENTRY wWinMain(
204218
}
205219

206220
//- Then just make that client render call like usual
207-
g_ClientRenderFunction(w32Window.hWnd);
221+
g_ClientRenderFunction(win32_window.hWnd);
208222
}
209223

210224
ImGui_ImplOpenGL3_Shutdown();
211225
ImGui_ImplWin32_Shutdown();
212226
ImGui::DestroyContext();
213227

214-
CleanupDeviceWGL(w32Window.hWnd, &g_MainWindow);
228+
CleanupDeviceWGL(win32_window.hWnd, &g_MainWindow);
215229
wglDeleteContext(g_hRC);
216-
::DestroyWindow(w32Window.hWnd);
217-
::UnregisterClass(w32Window.tcClassName, GetModuleHandle(NULL));
230+
::DestroyWindow(win32_window.hWnd);
231+
::UnregisterClass(win32_window.tcClassName, GetModuleHandle(NULL));
218232

219233
return 0;
220234
} // main

0 commit comments

Comments
 (0)