Start High-DPI support by scaling the parent window according to the DPI.
Sean Hall committed
Jul 6, 2020 at 16:03 UTC
a44cba1e241d0aa7d5c64595e9e7c95d0f06cced
8 files changed
+277
-11
src/dutil/dpiutil.cpp
new
+117
@@ -0,0 +1,117 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include "precomp.h"
4
+
5
+// Exit macros
6
+#define DpiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
7
+#define DpiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
8
+#define DpiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
9
+#define DpiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
10
+#define DpiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
11
+#define DpiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
12
+#define DpiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__)
13
+#define DpiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__)
14
+#define DpiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__)
15
+#define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__)
16
+#define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__)
17
+
18
+static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL;
19
+static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL;
20
+
21
+static HMODULE vhShcoreDll = NULL;
22
+static HMODULE vhUser32Dll = NULL;
23
+static BOOL vfDpiuInitialized = FALSE;
24
+
25
+DAPI_(void) DpiuInitialize()
26
+{
27
+ HRESULT hr = S_OK;
28
+
29
+ hr = LoadSystemLibrary(L"Shcore.dll", &vhShcoreDll);
30
+ if (SUCCEEDED(hr))
31
+ {
32
+ // Ignore failures.
33
+ vpfnGetDpiForMonitor = reinterpret_cast<PFN_GETDPIFORMONITOR>(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor"));
34
+ }
35
+
36
+ hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll);
37
+ if (SUCCEEDED(hr))
38
+ {
39
+ // Ignore failures.
40
+ vpfnGetDpiForWindow = reinterpret_cast<PFN_GETDPIFORWINDOW>(::GetProcAddress(vhUser32Dll, "GetDpiForWindow"));
41
+ }
42
+
43
+ vfDpiuInitialized = TRUE;
44
+}
45
+
46
+DAPI_(void) DpiuUninitialize()
47
+{
48
+ if (vhShcoreDll)
49
+ {
50
+ ::FreeLibrary(vhShcoreDll);
51
+ }
52
+
53
+ if (vhUser32Dll)
54
+ {
55
+ ::FreeLibrary(vhUser32Dll);
56
+ }
57
+
58
+ vhShcoreDll = NULL;
59
+ vhUser32Dll = NULL;
60
+ vpfnGetDpiForMonitor = NULL;
61
+ vpfnGetDpiForWindow = NULL;
62
+ vfDpiuInitialized = FALSE;
63
+}
64
+
65
+DAPI_(void) DpiuGetWindowContext(
66
+ __in HWND hWnd,
67
+ __in DPIU_WINDOW_CONTEXT* pWindowContext
68
+ )
69
+{
70
+ HRESULT hr = S_OK;
71
+ HMONITOR hMonitor = NULL;
72
+ UINT dpiX = 0;
73
+ UINT dpiY = 0;
74
+ HDC hdc = NULL;
75
+
76
+ pWindowContext->nDpi = USER_DEFAULT_SCREEN_DPI;
77
+
78
+ if (vpfnGetDpiForWindow)
79
+ {
80
+ pWindowContext->nDpi = vpfnGetDpiForWindow(hWnd);
81
+ ExitFunction();
82
+ }
83
+
84
+ if (vpfnGetDpiForMonitor)
85
+ {
86
+ hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
87
+ if (hMonitor)
88
+ {
89
+ hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
90
+ if (SUCCEEDED(hr))
91
+ {
92
+ pWindowContext->nDpi = dpiX;
93
+ ExitFunction();
94
+ }
95
+ }
96
+ }
97
+
98
+ hdc = ::GetDC(hWnd);
99
+ if (hdc)
100
+ {
101
+ pWindowContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX);
102
+ }
103
+
104
+LExit:
105
+ if (hdc)
106
+ {
107
+ ::ReleaseDC(hWnd, hdc);
108
+ }
109
+}
110
+
111
+DAPI_(int) DpiuScaleValue(
112
+ __in int nDefaultDpiValue,
113
+ __in UINT nTargetDpi
114
+ )
115
+{
116
+ return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI);
117
+}
src/dutil/dutil.vcxproj
+2
@@ -73,6 +73,7 @@
73
<ClCompile Include="dictutil.cpp" />
74
<ClCompile Include="dirutil.cpp" />
75
<ClCompile Include="dlutil.cpp" />
76
+ <ClCompile Include="dpiutil.cpp" />
77
<ClCompile Include="dutil.cpp">
78
<PrecompiledHeader>Create</PrecompiledHeader>
79
<DisableSpecificWarnings>4091;4458</DisableSpecificWarnings>
@@ -139,6 +140,7 @@
140
<ClInclude Include="inc\dictutil.h" />
141
<ClInclude Include="inc\dirutil.h" />
142
<ClInclude Include="inc\dlutil.h" />
143
+ <ClInclude Include="inc\dpiutil.h" />
144
<ClInclude Include="inc\dutil.h" />
145
<ClInclude Include="inc\dutilsources.h" />
146
<ClInclude Include="inc\eseutil.h" />
src/dutil/dutil.vcxproj.filters
+12
-3
@@ -60,6 +60,9 @@
60
<ClCompile Include="dlutil.cpp">
61
<Filter>Source Files</Filter>
62
</ClCompile>
63
+ <ClCompile Include="dpiutil.cpp">
64
+ <Filter>Source Files</Filter>
65
+ </ClCompile>
66
<ClCompile Include="dutil.cpp">
67
<Filter>Source Files</Filter>
68
</ClCompile>
@@ -230,9 +233,15 @@
233
<ClInclude Include="inc\dlutil.h">
234
<Filter>Header Files</Filter>
235
</ClInclude>
236
+ <ClInclude Include="inc\dpiutil.h">
237
+ <Filter>Header Files</Filter>
238
+ </ClInclude>
239
<ClInclude Include="inc\dutil.h">
240
<Filter>Header Files</Filter>
241
</ClInclude>
242
+ <ClInclude Include="inc\dutilsources.h">
243
+ <Filter>Header Files</Filter>
244
+ </ClInclude>
245
<ClInclude Include="inc\eseutil.h">
246
<Filter>Header Files</Filter>
247
</ClInclude>
@@ -287,6 +296,9 @@
296
<ClInclude Include="inc\reswutil.h">
297
<Filter>Header Files</Filter>
298
</ClInclude>
299
+ <ClInclude Include="inc\rmutil.h">
300
+ <Filter>Header Files</Filter>
301
+ </ClInclude>
302
<ClInclude Include="inc\rexutil.h">
303
<Filter>Header Files</Filter>
304
</ClInclude>
@@ -344,9 +356,6 @@
356
<ClInclude Include="inc\deputil.h">
357
<Filter>Header Files</Filter>
358
</ClInclude>
347
- <ClInclude Include="inc\dutilsources.h">
348
- <Filter>Header Files</Filter>
349
- </ClInclude>
359
</ItemGroup>
360
<ItemGroup>
361
<None Include="xsd\thmutil.xsd">
src/dutil/inc/dpiutil.h
new
+55
@@ -0,0 +1,55 @@
1
+#pragma once
2
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
+
4
+
5
+#ifdef __cplusplus
6
+extern "C" {
7
+#endif
8
+
9
+// from WinUser.h
10
+#ifndef WM_DPICHANGED
11
+#define WM_DPICHANGED 0x02E0
12
+#endif
13
+#ifndef USER_DEFAULT_SCREEN_DPI
14
+#define USER_DEFAULT_SCREEN_DPI 96
15
+#endif
16
+
17
+typedef struct _DPIU_WINDOW_CONTEXT
18
+{
19
+ UINT nDpi;
20
+} DPIU_WINDOW_CONTEXT;
21
+
22
+typedef HRESULT (APIENTRY *PFN_GETDPIFORMONITOR)(
23
+ __in HMONITOR hmonitor,
24
+ __in MONITOR_DPI_TYPE dpiType,
25
+ __in UINT* dpiX,
26
+ __in UINT* dpiY
27
+ );
28
+typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)(
29
+ __in HWND hwnd
30
+ );
31
+
32
+void DAPI DpiuInitialize();
33
+void DAPI DpiuUninitialize();
34
+
35
+/********************************************************************
36
+ DpiuGetWindowContext - get the DPI context of the given window.
37
+
38
+*******************************************************************/
39
+void DAPI DpiuGetWindowContext(
40
+ __in HWND hWnd,
41
+ __in DPIU_WINDOW_CONTEXT* pWindowContext
42
+ );
43
+
44
+/********************************************************************
45
+ DpiuScaleValue - scale the value to the target DPI.
46
+
47
+*******************************************************************/
48
+int DAPI DpiuScaleValue(
49
+ __in int nDefaultDpiValue,
50
+ __in UINT nTargetDpi
51
+ );
52
+
53
+#ifdef __cplusplus
54
+}
55
+#endif
src/dutil/inc/dutilsources.h
+1
@@ -19,6 +19,7 @@ typedef enum DUTIL_SOURCE
19
DUTIL_SOURCE_DICTUTIL,
20
DUTIL_SOURCE_DIRUTIL,
21
DUTIL_SOURCE_DLUTIL,
22
+ DUTIL_SOURCE_DPIUTIL,
23
DUTIL_SOURCE_DUTIL,
24
DUTIL_SOURCE_ESEUTIL,
25
DUTIL_SOURCE_FILEUTIL,
src/dutil/inc/thmutil.h
+6
@@ -255,6 +255,10 @@ struct THEME
255
DWORD dwFontId;
256
HANDLE hIcon;
257
LPWSTR sczCaption;
258
+ int nDefaultDpiHeight;
259
+ int nDefaultDpiMinimumHeight;
260
+ int nDefaultDpiWidth;
261
+ int nDefaultDpiMinimumWidth;
262
int nHeight;
263
int nMinimumHeight;
264
int nWidth;
@@ -283,6 +287,8 @@ struct THEME
287
DWORD dwCurrentPageId;
288
HWND hwndTooltip;
289
290
+ UINT nDpi;
291
+
292
// callback functions
293
PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition;
294
PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString;
src/dutil/precomp.h
+2
@@ -39,6 +39,7 @@
39
#include <wuapi.h>
40
#include <commctrl.h>
41
#include <dbt.h>
42
+#include <ShellScalingApi.h>
43
44
#include "dutilsources.h"
45
#include "dutil.h"
@@ -53,6 +54,7 @@
54
#include "eseutil.h"
55
#include "dirutil.h"
56
#include "dlutil.h"
57
+#include "dpiutil.h"
58
#include "fileutil.h"
59
#include "guidutil.h"
60
#include "gdiputil.h"
src/dutil/thmutil.cpp
+82
-8
@@ -287,6 +287,11 @@ static BOOL OnButtonClicked(
287
__in HWND hWnd,
288
__in const THEME_CONTROL* pControl
289
);
290
+static BOOL OnDpiChanged(
291
+ __in THEME* pTheme,
292
+ __in WPARAM wParam,
293
+ __in LPARAM lParam
294
+ );
295
static void OnNcCreate(
296
__in THEME* pTheme,
297
__in HWND hWnd,
@@ -353,6 +358,12 @@ static void ResizeControl(
358
__in THEME_CONTROL* pControl,
359
__in const RECT* prcParent
360
);
361
+static void ScaleTheme(
362
+ __in THEME* pTheme,
363
+ __in UINT nDpi,
364
+ __in int x,
365
+ __in int y
366
+ );
367
static void GetControls(
368
__in THEME* pTheme,
369
__in_opt THEME_CONTROL* pParentControl,
@@ -380,6 +391,8 @@ DAPI_(HRESULT) ThemeInitialize(
391
HRESULT hr = S_OK;
392
INITCOMMONCONTROLSEX icex = { };
393
394
+ DpiuInitialize();
395
+
396
hr = XmlInitialize();
397
ThmExitOnFailure(hr, "Failed to initialize XML.");
398
@@ -430,6 +443,7 @@ DAPI_(void) ThemeUninitialize()
443
}
444
445
XmlUninitialize();
446
+ DpiuUninitialize();
447
}
448
449
@@ -797,10 +811,10 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc(
811
}
812
break;
813
800
- case WM_WINDOWPOSCHANGED:
814
+ case WM_DPICHANGED:
815
+ if (OnDpiChanged(pTheme, wParam, lParam))
816
{
802
- //WINDOWPOS* pos = reinterpret_cast<LPWINDOWPOS>(lParam);
803
- //ThemeWindowPositionChanged(pTheme, pos);
817
+ return 0;
818
}
819
break;
820
@@ -1652,6 +1666,7 @@ static HRESULT ParseTheme(
1666
ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme.");
1667
1668
pTheme->wId = ++wThemeId;
1669
+ pTheme->nDpi = USER_DEFAULT_SCREEN_DPI;
1670
1671
// Parse the optional background resource image.
1672
hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage);
@@ -1816,6 +1831,7 @@ static HRESULT ParseWindow(
1831
{
1832
HRESULT hr = S_OK;
1833
IXMLDOMNode* pixn = NULL;
1834
+ DWORD dwValue = 0;
1835
BSTR bstr = NULL;
1836
LPWSTR sczIconFile = NULL;
1837
@@ -1833,7 +1849,7 @@ static HRESULT ParseWindow(
1849
}
1850
ThmExitOnFailure(hr, "Failed to get window AutoResize attribute.");
1851
1836
- hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast<DWORD*>(&pTheme->nWidth));
1852
+ hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue);
1853
if (S_FALSE == hr)
1854
{
1855
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
@@ -1841,7 +1857,9 @@ static HRESULT ParseWindow(
1857
}
1858
ThmExitOnFailure(hr, "Failed to get window Width attribute.");
1859
1844
- hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast<DWORD*>(&pTheme->nHeight));
1860
+ pTheme->nWidth = pTheme->nDefaultDpiWidth = dwValue;
1861
+
1862
+ hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue);
1863
if (S_FALSE == hr)
1864
{
1865
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
@@ -1849,20 +1867,28 @@ static HRESULT ParseWindow(
1867
}
1868
ThmExitOnFailure(hr, "Failed to get window Height attribute.");
1869
1852
- hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", reinterpret_cast<DWORD*>(&pTheme->nMinimumWidth));
1870
+ pTheme->nHeight = pTheme->nDefaultDpiHeight = dwValue;
1871
+
1872
+ hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue);
1873
if (S_FALSE == hr)
1874
{
1875
+ dwValue = 0;
1876
hr = S_OK;
1877
}
1878
ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute.");
1879
1859
- hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", reinterpret_cast<DWORD*>(&pTheme->nMinimumHeight));
1880
+ pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = dwValue;
1881
+
1882
+ hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", &dwValue);
1883
if (S_FALSE == hr)
1884
{
1885
+ dwValue = 0;
1886
hr = S_OK;
1887
}
1888
ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute.");
1889
1890
+ pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = dwValue;
1891
+
1892
hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId);
1893
if (S_FALSE == hr)
1894
{
@@ -4161,14 +4187,45 @@ static BOOL OnButtonClicked(
4187
LExit:
4188
return fHandled;
4189
}
4190
+
4191
+static BOOL OnDpiChanged(
4192
+ __in THEME* pTheme,
4193
+ __in WPARAM wParam,
4194
+ __in LPARAM lParam
4195
+ )
4196
+{
4197
+ UINT nDpi = HIWORD(wParam);
4198
+ RECT* pRect = reinterpret_cast<RECT*>(lParam);
4199
+ BOOL fIgnored = pTheme->nDpi == nDpi;
4200
+
4201
+ if (fIgnored)
4202
+ {
4203
+ ExitFunction();
4204
+ }
4205
+
4206
+ ScaleTheme(pTheme, nDpi, pRect->left, pRect->top);
4207
+
4208
+LExit:
4209
+ return !fIgnored;
4210
+}
4211
4212
static void OnNcCreate(
4213
__in THEME* pTheme,
4214
__in HWND hWnd,
4168
- __in LPARAM /*lParam*/
4215
+ __in LPARAM lParam
4216
)
4217
{
4218
+ DPIU_WINDOW_CONTEXT windowContext = { };
4219
+ CREATESTRUCTW* pCreateStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
4220
+
4221
pTheme->hwndParent = hWnd;
4222
+
4223
+ DpiuGetWindowContext(pTheme->hwndParent, &windowContext);
4224
+
4225
+ if (windowContext.nDpi != pTheme->nDpi)
4226
+ {
4227
+ ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y);
4228
+ }
4229
}
4230
4231
static HRESULT OnRichEditEnLink(
@@ -5282,6 +5339,23 @@ static void ResizeControl(
5339
}
5340
}
5341
5342
+static void ScaleTheme(
5343
+ __in THEME* pTheme,
5344
+ __in UINT nDpi,
5345
+ __in int x,
5346
+ __in int y
5347
+ )
5348
+{
5349
+ pTheme->nDpi = nDpi;
5350
+
5351
+ pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi);
5352
+ pTheme->nWidth = DpiuScaleValue(pTheme->nDefaultDpiWidth, pTheme->nDpi);
5353
+ pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi);
5354
+ pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi);
5355
+
5356
+ ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWidth, pTheme->nHeight, SWP_NOACTIVATE | SWP_NOZORDER);
5357
+}
5358
+
5359
static void UnloadControls(
5360
__in DWORD cControls,
5361
__in THEME_CONTROL* rgControls