Skip to content

Commit

Permalink
Examples: DPI: Portable DPI related helpers in the _Win32 examples. U…
Browse files Browse the repository at this point in the history
…sing one in examples's main.cpp, the GetDpiScale functions are not wired anywhere for now. (#1542, #1676)
  • Loading branch information
ocornut committed Mar 12, 2018
1 parent 10030ff commit a2fbcc9
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 21 deletions.
2 changes: 2 additions & 0 deletions examples/directx10_example/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)

int main(int, char**)
{
ImGui_ImplWin32_EnableDpiAwareness();

// Create application window
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL };
RegisterClassEx(&wc);
Expand Down
20 changes: 1 addition & 19 deletions examples/directx11_example/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,9 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
return DefWindowProc(hWnd, msg, wParam, lParam);
}

// FIXME-DPI: For now we just want to call SetProcessDpiAwareness(PROCESS_PER_MONITOR_AWARE) without requiring SDK 8.1 or 10
void SetupDpiAwareness()
{
typedef enum PROCESS_DPI_AWARENESS
{
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS;
if (HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"))
{
typedef HRESULT(WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"))
SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE);
::FreeLibrary(shcore_dll);
}
}

int main(int, char**)
{
SetupDpiAwareness();
ImGui_ImplWin32_EnableDpiAwareness();

// Create application window
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL };
Expand Down
78 changes: 78 additions & 0 deletions examples/imgui_impl_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,84 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa
return 0;
}

// --------------------------------------------------------------------------------------------------------
// DPI handling
// Those in theory should be simple calls but Windows has multiple ways to handle DPI, and most of them
// require recent Windows versions at runtime or recent Windows SDK at compile-time. Neither we want to depend on.
// 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 here) and other applications aiming to be portable.
//---------------------------------------------------------------------------------------------------------
// FIXME-DPI: For now we just call SetProcessDpiAwareness(PROCESS_PER_MONITOR_AWARE) without requiring SDK 8.1 or 10.
// We may allow/aim calling the most-recent-available version, e.g. Windows 10 Creators Update has SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
// At this point ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it ourselves.
//---------------------------------------------------------------------------------------------------------

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
#define IsWindows10OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WIN10

#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
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

void ImGui_ImplWin32_EnableDpiAwareness()
{
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);
}
else
{
SetProcessDPIAware();
}
}

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);
}

float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2)
{
RECT viewport_rect = { (UINT)x1, (UINT)y1, (UINT)x2, (UINT)y2 };
HMONITOR monitor = ::MonitorFromRect(&viewport_rect, MONITOR_DEFAULTTONEAREST);
return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
}

// --------------------------------------------------------------------------------------------------------
// Platform Windows
// --------------------------------------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions examples/imgui_impl_win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ IMGUI_API bool ImGui_ImplWin32_Init(void* hwnd);
IMGUI_API void ImGui_ImplWin32_Shutdown();
IMGUI_API void ImGui_ImplWin32_NewFrame();

// DPI-related helpers (which run and compile without requiring 8.1 or 10, neither Windows version, neither associated SDK)
IMGUI_API void ImGui_ImplWin32_EnableDpiAwareness();
IMGUI_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
IMGUI_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
IMGUI_API float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2);

// Handler for Win32 messages, update mouse/keyboard data.
// You may or not need this for your implementation, but it can serve as reference for handling inputs.
// Intentionally commented out to avoid dragging dependencies on <windows.h> types. You can copy the extern declaration in your code.
Expand Down
4 changes: 2 additions & 2 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13804,13 +13804,13 @@ static void ScaleWindow(ImGuiWindow* window, float scale)
// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
void ImGui::ScaleWindowsInViewport(ImGuiViewport* viewport, float scale)
{
ImGuiContext& g = *GImGui;
if (viewport->Window)
{
ScaleWindow(viewport->Window, scale);
}
else
{
ImGuiContext& g = *GImGui;
for (int i = 0; i != g.Windows.Size; i++)
if (g.Windows[i]->Viewport == viewport)
ScaleWindow(g.Windows[i], scale);
Expand Down Expand Up @@ -13992,7 +13992,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
{
ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y);
ImGui::BulletText("Flags: 0x%04X", viewport->Flags);
ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f)", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y);
ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y, viewport->PlatformDpiScale * 100.0f);
for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[0].Size; draw_list_i++)
Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[0][draw_list_i], "DrawList");
ImGui::TreePop();
Expand Down

0 comments on commit a2fbcc9

Please sign in to comment.