1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
#include "stdafx.h"
#include "Win32Dpi.h"
namespace gui {
// Default DPI (i.e. 100%, no scaling).
const Nat defaultDpi = 96;
}
#ifdef GUI_WIN32
namespace gui {
// From the MSDN documentation.
#ifndef DPI_AWARENESS_CONTEXT_UNAWARE
#define DPI_AWARENESS_CONTEXT_UNAWARE ((DWORD)-1)
#endif
#ifndef DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DWORD)-2)
#endif
#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DWORD)-3)
#endif
#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DWORD)-4)
#endif
#ifndef DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED
#define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DWORD)-5)
#endif
enum PROCESS_DPI_AWARENESS {
PROCESS_DPI_UNAWARE,
PROCESS_SYSTEM_DPI_AWARE,
PROCESS_PER_MONITOR_DPI_AWARE
};
// DPI awareness mode.
enum DpiMode {
// Not DPI aware.
dpiNone,
// System DPI aware (i.e., checks at startup, but not afterwards).
dpiSystem,
// Per-monitor DPI aware (i.e., dynamically responds to changes).
dpiMonitor,
};
// Current DPI mode.
static DpiMode dpiMode;
// Do we want to call "EnableNonClientScaling" (using the non-v2 mode of per monitor in Windows 8.1)?
static bool callNonClientScaling;
// Misc. Win32 functions we load. These might be null.
typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND);
EnableNonClientDpiScaling enableNonClientDpiScaling;
typedef UINT (WINAPI *GetDpiForSystem)();
GetDpiForSystem getDpiForSystem;
typedef UINT (WINAPI *GetDpiForWindow)(HWND);
GetDpiForWindow getDpiForWindow;
typedef int (WINAPI *GetSystemMetricsForDpi)(int, UINT);
GetSystemMetricsForDpi getSystemMetricsForDpi;
typedef BOOL (WINAPI *SystemParametersInfoDpi)(UINT, UINT, LPVOID, UINT, UINT);
SystemParametersInfoDpi systemParametersInfoDpi;
typedef BOOL (WINAPI *AdjustWindowRectExForDpi)(LPRECT, DWORD, BOOL, DWORD, UINT);
AdjustWindowRectExForDpi adjustWindowRectExForDpi;
void setDpiAware() {
HMODULE user32 = GetModuleHandle(L"user32.dll");
dpiMode = dpiNone;
callNonClientScaling = false;
enableNonClientDpiScaling = (EnableNonClientDpiScaling)GetProcAddress(user32, "EnableNonClientDpiScaling");
getDpiForSystem = (GetDpiForSystem)GetProcAddress(user32, "GetDpiForSystem");
getDpiForWindow = (GetDpiForWindow)GetProcAddress(user32, "GetDpiForWindow");
getSystemMetricsForDpi = (GetSystemMetricsForDpi)GetProcAddress(user32, "GetSystemMetricsForDpi");
systemParametersInfoDpi = (SystemParametersInfoDpi)GetProcAddress(user32, "SystemParametersInfoForDpi");
adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)GetProcAddress(user32, "AdjustWindowRectExForDpi");
// Windows 10 and onwards (per monitor v2)
typedef BOOL (WINAPI *SetDpiContext)(DWORD);
SetDpiContext dpiContext = (SetDpiContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext");
if (dpiContext && dpiContext(DWORD(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))) {
dpiMode = dpiMonitor;
return;
}
// Windows 8.1 and onwards. If we're not on Windows 10 (1607), this will leave menus and
// window border unscaled (which the Microsoft samples do not deal with, so we won't either).
{
// Note: This is a known DLL, so this does not pose any danger of loading the wrong DLL.
HMODULE shcore = LoadLibrary(L"SHCORE.DLL");
typedef HRESULT (WINAPI *SetDpiProcess)(PROCESS_DPI_AWARENESS);
SetDpiProcess dpiProcess = (SetDpiProcess)GetProcAddress(shcore, "SetProcessDpiAwareness");
if (dpiProcess && dpiProcess(PROCESS_PER_MONITOR_DPI_AWARE) == S_OK) {
dpiMode = dpiMonitor;
callNonClientScaling = true;
}
FreeLibrary(shcore);
if (dpiMode != dpiNone)
return;
}
typedef void (WINAPI *SetDpiAware)();
SetDpiAware dpiAware = (SetDpiAware)GetProcAddress(user32, "SetProcessDPIAware");
if (dpiAware) {
dpiAware();
dpiMode = dpiSystem;
}
}
void enableNcScaling(HWND hWnd) {
if (!callNonClientScaling)
return;
if (enableNonClientDpiScaling)
enableNonClientDpiScaling(hWnd);
}
Nat windowDpi(HWND hWnd) {
if (dpiMode == dpiMonitor && getDpiForWindow) {
return getDpiForWindow(hWnd);
} else if (dpiMode == dpiSystem && getDpiForSystem) {
return getDpiForSystem();
} else {
return defaultDpi;
}
}
int dpiSystemMetrics(int index, Nat dpi) {
if (getSystemMetricsForDpi)
return getSystemMetricsForDpi(index, dpi);
else
return GetSystemMetrics(index);
}
BOOL dpiSystemParametersInfo(UINT action, UINT param, void *data, UINT winIni, UINT dpi) {
if (systemParametersInfoDpi)
return systemParametersInfoDpi(action, param, data, winIni, dpi);
else
return SystemParametersInfo(action, param, data, winIni);
}
BOOL dpiAdjustWindowRectEx(RECT *rect, DWORD style, bool menu, DWORD exStyle, Nat dpi) {
if (adjustWindowRectExForDpi)
return adjustWindowRectExForDpi(rect, style, menu, exStyle, dpi);
else
return AdjustWindowRectEx(rect, style, menu, exStyle);
}
Float dpiScale(Nat dpi) {
return Float(dpi) / Float(defaultDpi);
}
Float dpiScaleInv(Nat dpi) {
return Float(defaultDpi) / Float(dpi);
}
Point dpiToPx(Nat dpi, Point pt) {
return pt * dpiScale(dpi);
}
Size dpiToPx(Nat dpi, Size size) {
return size * dpiScale(dpi);
}
Rect dpiToPx(Nat dpi, Rect rect) {
Float scale = dpiScale(dpi);
rect.p0 *= scale;
rect.p1 *= scale;
return rect;
}
Point dpiFromPx(Nat dpi, Point pt) {
return pt * dpiScaleInv(dpi);
}
Size dpiFromPx(Nat dpi, Size size) {
return size * dpiScaleInv(dpi);
}
Rect dpiFromPx(Nat dpi, Rect rect) {
Float scale = dpiScaleInv(dpi);
rect.p0 *= scale;
rect.p1 *= scale;
return rect;
}
}
#endif
|