From ceec3cd3fd1c2331e45b18dd64d985a07328b4cc Mon Sep 17 00:00:00 2001 From: Omar Date: Mon, 17 Feb 2020 11:11:08 +0100 Subject: [PATCH] Backends: Win32: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helpers functions. (backported from the docking branch) --- docs/CHANGELOG.txt | 4 ++ examples/imgui_impl_win32.cpp | 100 ++++++++++++++++++++++++++++++++++ examples/imgui_impl_win32.h | 10 ++++ 3 files changed, 114 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4216ab50..f3d7cbfe 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,6 +42,10 @@ Other Changes: glyph. (#2149, #515) - Font: Fixed non-ASCII space occasionally creating unnecessary empty polygons. - Demo: Added a black and white gradient to Demo>Examples>Custom Rendering. +- Backends: Win32: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), + ImGui_ImplWin32_GetDpiScaleForMonitor() helpers functions (backported from the docking branch). + Those functions makes it easier for example apps to support hi-dpi features without setting up + a manifest. - Backends: SDL: Added ImGui_ImplSDL2_InitForMetal() for API consistency (even though the function currently does nothing). - Examples: Added SDL2+Metal example application. (#3017) [@coding-jackalope] diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 2740fe0e..449922f5 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -28,6 +28,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions. // 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT. // 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. // 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter(). @@ -337,3 +338,102 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; } + +//-------------------------------------------------------------------------------------------------------- +// DPI-related helpers (optional) +//-------------------------------------------------------------------------------------------------------- +// - Use to enable DPI awareness without having to create an application manifest. +// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. +// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. +// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, +// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. +//--------------------------------------------------------------------------------------------------------- +// This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable. +// ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically. +// If you are trying to implement your own back-end for your own engine, you may ignore that noise. +//--------------------------------------------------------------------------------------------------------- + +// Implement some of the functions and types normally declared in recent Windows SDK. +#if !defined(_versionhelpers_H_INCLUDED_) && !defined(_INC_VERSIONHELPERS) +static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, { 0 }, sp }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; + ULONGLONG cond = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = ::VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = ::VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + return ::VerifyVersionInfoW(&osvi, mask, cond); +} +#define IsWindows8Point1OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WINBLUE +#endif + +#ifndef DPI_ENUMS_DECLARED +typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; +typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; +#endif +#ifndef _DPI_AWARENESS_CONTEXTS_ +DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3 +#endif +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4 +#endif +typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+ +typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+ +typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update) + +// Helper function to enable DPI awareness without setting up a manifest +void ImGui_ImplWin32_EnableDpiAwareness() +{ + // if (IsWindows10OrGreater()) // This needs a manifest to succeed. Instead we try to grab the function pointer! + { + static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process + if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) + { + SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + return; + } + } + if (IsWindows8Point1OrGreater()) + { + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness")) + { + SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE); + return; + } + } + SetProcessDPIAware(); +} + +#ifdef _MSC_VER +#pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps() +#endif + +float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) +{ + UINT xdpi = 96, ydpi = 96; + if (IsWindows8Point1OrGreater()) + { + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor")) + GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + } + else + { + const HDC dc = ::GetDC(NULL); + xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); + ydpi = ::GetDeviceCaps(dc, LOGPIXELSY); + ::ReleaseDC(NULL, dc); + } + IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! + return xdpi / 96.0f; +} + +float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) +{ + HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST); + return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); +} + +//--------------------------------------------------------------------------------------------------------- diff --git a/examples/imgui_impl_win32.h b/examples/imgui_impl_win32.h index e4b9ec3a..daadf317 100644 --- a/examples/imgui_impl_win32.h +++ b/examples/imgui_impl_win32.h @@ -23,3 +23,13 @@ IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); #if 0 extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); #endif + +// DPI-related helpers (optional) +// - Use to enable DPI awareness without having to create an application manifest. +// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. +// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. +// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, +// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. +IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); +IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd +IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor