@joebigelow / wix-1 / commits / a44cba1e

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