From a44cba1e241d0aa7d5c64595e9e7c95d0f06cced Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 6 Jul 2020 16:03:23 +1000 Subject: Start High-DPI support by scaling the parent window according to the DPI. --- src/dutil/dpiutil.cpp | 117 ++++++++++++++++++++++++++++++++++++++++ src/dutil/dutil.vcxproj | 2 + src/dutil/dutil.vcxproj.filters | 15 ++++-- src/dutil/inc/dpiutil.h | 55 +++++++++++++++++++ src/dutil/inc/dutilsources.h | 1 + src/dutil/inc/thmutil.h | 6 +++ src/dutil/precomp.h | 2 + src/dutil/thmutil.cpp | 90 ++++++++++++++++++++++++++++--- 8 files changed, 277 insertions(+), 11 deletions(-) create mode 100644 src/dutil/dpiutil.cpp create mode 100644 src/dutil/inc/dpiutil.h (limited to 'src') diff --git a/src/dutil/dpiutil.cpp b/src/dutil/dpiutil.cpp new file mode 100644 index 00000000..edab8f01 --- /dev/null +++ b/src/dutil/dpiutil.cpp @@ -0,0 +1,117 @@ +// 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. + +#include "precomp.h" + +// Exit macros +#define DpiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__) +#define DpiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) +#define DpiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) +#define DpiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__) +#define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__) +#define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__) + +static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL; +static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL; + +static HMODULE vhShcoreDll = NULL; +static HMODULE vhUser32Dll = NULL; +static BOOL vfDpiuInitialized = FALSE; + +DAPI_(void) DpiuInitialize() +{ + HRESULT hr = S_OK; + + hr = LoadSystemLibrary(L"Shcore.dll", &vhShcoreDll); + if (SUCCEEDED(hr)) + { + // Ignore failures. + vpfnGetDpiForMonitor = reinterpret_cast(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor")); + } + + hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll); + if (SUCCEEDED(hr)) + { + // Ignore failures. + vpfnGetDpiForWindow = reinterpret_cast(::GetProcAddress(vhUser32Dll, "GetDpiForWindow")); + } + + vfDpiuInitialized = TRUE; +} + +DAPI_(void) DpiuUninitialize() +{ + if (vhShcoreDll) + { + ::FreeLibrary(vhShcoreDll); + } + + if (vhUser32Dll) + { + ::FreeLibrary(vhUser32Dll); + } + + vhShcoreDll = NULL; + vhUser32Dll = NULL; + vpfnGetDpiForMonitor = NULL; + vpfnGetDpiForWindow = NULL; + vfDpiuInitialized = FALSE; +} + +DAPI_(void) DpiuGetWindowContext( + __in HWND hWnd, + __in DPIU_WINDOW_CONTEXT* pWindowContext + ) +{ + HRESULT hr = S_OK; + HMONITOR hMonitor = NULL; + UINT dpiX = 0; + UINT dpiY = 0; + HDC hdc = NULL; + + pWindowContext->nDpi = USER_DEFAULT_SCREEN_DPI; + + if (vpfnGetDpiForWindow) + { + pWindowContext->nDpi = vpfnGetDpiForWindow(hWnd); + ExitFunction(); + } + + if (vpfnGetDpiForMonitor) + { + hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (hMonitor) + { + hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + if (SUCCEEDED(hr)) + { + pWindowContext->nDpi = dpiX; + ExitFunction(); + } + } + } + + hdc = ::GetDC(hWnd); + if (hdc) + { + pWindowContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX); + } + +LExit: + if (hdc) + { + ::ReleaseDC(hWnd, hdc); + } +} + +DAPI_(int) DpiuScaleValue( + __in int nDefaultDpiValue, + __in UINT nTargetDpi + ) +{ + return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI); +} diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj index af8385d1..4bae04d6 100644 --- a/src/dutil/dutil.vcxproj +++ b/src/dutil/dutil.vcxproj @@ -73,6 +73,7 @@ + Create 4091;4458 @@ -139,6 +140,7 @@ + diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters index 4dd90fdd..01dd6661 100644 --- a/src/dutil/dutil.vcxproj.filters +++ b/src/dutil/dutil.vcxproj.filters @@ -60,6 +60,9 @@ Source Files + + Source Files + Source Files @@ -230,9 +233,15 @@ Header Files + + Header Files + Header Files + + Header Files + Header Files @@ -287,6 +296,9 @@ Header Files + + Header Files + Header Files @@ -344,9 +356,6 @@ Header Files - - Header Files - diff --git a/src/dutil/inc/dpiutil.h b/src/dutil/inc/dpiutil.h new file mode 100644 index 00000000..c6f73b02 --- /dev/null +++ b/src/dutil/inc/dpiutil.h @@ -0,0 +1,55 @@ +#pragma once +// 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. + + +#ifdef __cplusplus +extern "C" { +#endif + +// from WinUser.h +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif +#ifndef USER_DEFAULT_SCREEN_DPI +#define USER_DEFAULT_SCREEN_DPI 96 +#endif + +typedef struct _DPIU_WINDOW_CONTEXT +{ + UINT nDpi; +} DPIU_WINDOW_CONTEXT; + +typedef HRESULT (APIENTRY *PFN_GETDPIFORMONITOR)( + __in HMONITOR hmonitor, + __in MONITOR_DPI_TYPE dpiType, + __in UINT* dpiX, + __in UINT* dpiY + ); +typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)( + __in HWND hwnd + ); + +void DAPI DpiuInitialize(); +void DAPI DpiuUninitialize(); + +/******************************************************************** + DpiuGetWindowContext - get the DPI context of the given window. + +*******************************************************************/ +void DAPI DpiuGetWindowContext( + __in HWND hWnd, + __in DPIU_WINDOW_CONTEXT* pWindowContext + ); + +/******************************************************************** + DpiuScaleValue - scale the value to the target DPI. + +*******************************************************************/ +int DAPI DpiuScaleValue( + __in int nDefaultDpiValue, + __in UINT nTargetDpi + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/dutil/inc/dutilsources.h b/src/dutil/inc/dutilsources.h index c88ada37..bf3da16f 100644 --- a/src/dutil/inc/dutilsources.h +++ b/src/dutil/inc/dutilsources.h @@ -19,6 +19,7 @@ typedef enum DUTIL_SOURCE DUTIL_SOURCE_DICTUTIL, DUTIL_SOURCE_DIRUTIL, DUTIL_SOURCE_DLUTIL, + DUTIL_SOURCE_DPIUTIL, DUTIL_SOURCE_DUTIL, DUTIL_SOURCE_ESEUTIL, DUTIL_SOURCE_FILEUTIL, diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h index e661a6cb..52de755f 100644 --- a/src/dutil/inc/thmutil.h +++ b/src/dutil/inc/thmutil.h @@ -255,6 +255,10 @@ struct THEME DWORD dwFontId; HANDLE hIcon; LPWSTR sczCaption; + int nDefaultDpiHeight; + int nDefaultDpiMinimumHeight; + int nDefaultDpiWidth; + int nDefaultDpiMinimumWidth; int nHeight; int nMinimumHeight; int nWidth; @@ -283,6 +287,8 @@ struct THEME DWORD dwCurrentPageId; HWND hwndTooltip; + UINT nDpi; + // callback functions PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition; PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString; diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h index eebdd160..7fdc83ae 100644 --- a/src/dutil/precomp.h +++ b/src/dutil/precomp.h @@ -39,6 +39,7 @@ #include #include #include +#include #include "dutilsources.h" #include "dutil.h" @@ -53,6 +54,7 @@ #include "eseutil.h" #include "dirutil.h" #include "dlutil.h" +#include "dpiutil.h" #include "fileutil.h" #include "guidutil.h" #include "gdiputil.h" diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp index 2050b420..4cc149c9 100644 --- a/src/dutil/thmutil.cpp +++ b/src/dutil/thmutil.cpp @@ -287,6 +287,11 @@ static BOOL OnButtonClicked( __in HWND hWnd, __in const THEME_CONTROL* pControl ); +static BOOL OnDpiChanged( + __in THEME* pTheme, + __in WPARAM wParam, + __in LPARAM lParam + ); static void OnNcCreate( __in THEME* pTheme, __in HWND hWnd, @@ -353,6 +358,12 @@ static void ResizeControl( __in THEME_CONTROL* pControl, __in const RECT* prcParent ); +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y + ); static void GetControls( __in THEME* pTheme, __in_opt THEME_CONTROL* pParentControl, @@ -380,6 +391,8 @@ DAPI_(HRESULT) ThemeInitialize( HRESULT hr = S_OK; INITCOMMONCONTROLSEX icex = { }; + DpiuInitialize(); + hr = XmlInitialize(); ThmExitOnFailure(hr, "Failed to initialize XML."); @@ -430,6 +443,7 @@ DAPI_(void) ThemeUninitialize() } XmlUninitialize(); + DpiuUninitialize(); } @@ -797,10 +811,10 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc( } break; - case WM_WINDOWPOSCHANGED: + case WM_DPICHANGED: + if (OnDpiChanged(pTheme, wParam, lParam)) { - //WINDOWPOS* pos = reinterpret_cast(lParam); - //ThemeWindowPositionChanged(pTheme, pos); + return 0; } break; @@ -1652,6 +1666,7 @@ static HRESULT ParseTheme( ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme."); pTheme->wId = ++wThemeId; + pTheme->nDpi = USER_DEFAULT_SCREEN_DPI; // Parse the optional background resource image. hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage); @@ -1816,6 +1831,7 @@ static HRESULT ParseWindow( { HRESULT hr = S_OK; IXMLDOMNode* pixn = NULL; + DWORD dwValue = 0; BSTR bstr = NULL; LPWSTR sczIconFile = NULL; @@ -1833,7 +1849,7 @@ static HRESULT ParseWindow( } ThmExitOnFailure(hr, "Failed to get window AutoResize attribute."); - hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast(&pTheme->nWidth)); + hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); @@ -1841,7 +1857,9 @@ static HRESULT ParseWindow( } ThmExitOnFailure(hr, "Failed to get window Width attribute."); - hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast(&pTheme->nHeight)); + pTheme->nWidth = pTheme->nDefaultDpiWidth = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); @@ -1849,20 +1867,28 @@ static HRESULT ParseWindow( } ThmExitOnFailure(hr, "Failed to get window Height attribute."); - hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", reinterpret_cast(&pTheme->nMinimumWidth)); + pTheme->nHeight = pTheme->nDefaultDpiHeight = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue); if (S_FALSE == hr) { + dwValue = 0; hr = S_OK; } ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute."); - hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", reinterpret_cast(&pTheme->nMinimumHeight)); + pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = dwValue; + + hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", &dwValue); if (S_FALSE == hr) { + dwValue = 0; hr = S_OK; } ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute."); + pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = dwValue; + hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId); if (S_FALSE == hr) { @@ -4161,14 +4187,45 @@ static BOOL OnButtonClicked( LExit: return fHandled; } + +static BOOL OnDpiChanged( + __in THEME* pTheme, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + UINT nDpi = HIWORD(wParam); + RECT* pRect = reinterpret_cast(lParam); + BOOL fIgnored = pTheme->nDpi == nDpi; + + if (fIgnored) + { + ExitFunction(); + } + + ScaleTheme(pTheme, nDpi, pRect->left, pRect->top); + +LExit: + return !fIgnored; +} static void OnNcCreate( __in THEME* pTheme, __in HWND hWnd, - __in LPARAM /*lParam*/ + __in LPARAM lParam ) { + DPIU_WINDOW_CONTEXT windowContext = { }; + CREATESTRUCTW* pCreateStruct = reinterpret_cast(lParam); + pTheme->hwndParent = hWnd; + + DpiuGetWindowContext(pTheme->hwndParent, &windowContext); + + if (windowContext.nDpi != pTheme->nDpi) + { + ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y); + } } static HRESULT OnRichEditEnLink( @@ -5282,6 +5339,23 @@ static void ResizeControl( } } +static void ScaleTheme( + __in THEME* pTheme, + __in UINT nDpi, + __in int x, + __in int y + ) +{ + pTheme->nDpi = nDpi; + + pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi); + pTheme->nWidth = DpiuScaleValue(pTheme->nDefaultDpiWidth, pTheme->nDpi); + pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi); + pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi); + + ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWidth, pTheme->nHeight, SWP_NOACTIVATE | SWP_NOZORDER); +} + static void UnloadControls( __in DWORD cControls, __in THEME_CONTROL* rgControls -- cgit v1.2.3-55-g6feb