diff --git a/shell/platform/windows/win32_dpi_helper.cc b/shell/platform/windows/win32_dpi_helper.cc index c31044ce2c767..40a79e72c31d9 100644 --- a/shell/platform/windows/win32_dpi_helper.cc +++ b/shell/platform/windows/win32_dpi_helper.cc @@ -4,6 +4,8 @@ namespace flutter { namespace { +constexpr UINT kDefaultDpi = 96; + template bool AssignProcAddress(HMODULE comBaseModule, const char* name, T*& outProc) { outProc = reinterpret_cast(GetProcAddress(comBaseModule, name)); @@ -13,60 +15,57 @@ bool AssignProcAddress(HMODULE comBaseModule, const char* name, T*& outProc) { } // namespace Win32DpiHelper::Win32DpiHelper() { - // TODO ensure that this helper works correctly on downlevel builds. user32_module_ = LoadLibraryA("User32.dll"); if (user32_module_ == nullptr) { return; } - if (!AssignProcAddress(user32_module_, "EnableNonClientDpiScaling", - enable_non_client_dpi_scaling_)) { + if (AssignProcAddress(user32_module_, "GetDpiForWindow", + get_dpi_for_window_)) { + dpi_for_window_supported_ = true; return; } - if (!AssignProcAddress(user32_module_, "GetDpiForWindow", - get_dpi_for_window_)) { + shlib_module_ = LoadLibraryA("Shcore.dll"); + if (shlib_module_ == nullptr) { return; } - if (!AssignProcAddress(user32_module_, "SetProcessDpiAwarenessContext", - set_process_dpi_awareness_context_)) { - return; + if (AssignProcAddress(shlib_module_, "GetDpiForMonitor", + get_dpi_for_monitor_) && + AssignProcAddress(user32_module_, "MonitorFromWindow", + monitor_from_window_)) { + dpi_for_monitor_supported_ = true; } - - permonitorv2_supported_ = true; } Win32DpiHelper::~Win32DpiHelper() { if (user32_module_ != nullptr) { FreeLibrary(user32_module_); } -} - -bool Win32DpiHelper::IsPerMonitorV2Available() { - return permonitorv2_supported_; -} - -BOOL Win32DpiHelper::EnableNonClientDpiScaling(HWND hwnd) { - if (!permonitorv2_supported_) { - return false; + if (shlib_module_ != nullptr) { + FreeLibrary(shlib_module_); } - return enable_non_client_dpi_scaling_(hwnd); } -UINT Win32DpiHelper::GetDpiForWindow(HWND hwnd) { - if (!permonitorv2_supported_) { - return false; +UINT Win32DpiHelper::GetDpi(HWND hwnd) { + // GetDpiForWindow returns the DPI for any awareness mode. If not available, + // fallback to a per monitor, system, or default DPI. + if (dpi_for_window_supported_) { + return get_dpi_for_window_(hwnd); } - return get_dpi_for_window_(hwnd); -} -BOOL Win32DpiHelper::SetProcessDpiAwarenessContext( - DPI_AWARENESS_CONTEXT context) { - if (!permonitorv2_supported_) { - return false; + if (dpi_for_monitor_supported_) { + HMONITOR monitor = monitor_from_window_(hwnd, MONITOR_DEFAULTTONEAREST); + UINT dpi_x = 0, dpi_y = 0; + HRESULT result = + get_dpi_for_monitor_(monitor, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y); + return SUCCEEDED(result) ? dpi_x : kDefaultDpi; } - return set_process_dpi_awareness_context_(context); -} + HDC hdc = GetDC(hwnd); + UINT dpi = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(hwnd, hdc); + return dpi; +} } // namespace flutter diff --git a/shell/platform/windows/win32_dpi_helper.h b/shell/platform/windows/win32_dpi_helper.h index ddf51184bf956..6ded84836713f 100644 --- a/shell/platform/windows/win32_dpi_helper.h +++ b/shell/platform/windows/win32_dpi_helper.h @@ -10,38 +10,34 @@ namespace flutter { -// A helper class for abstracting various Windows DPI related functions across -// Windows OS versions. +/// A helper class for abstracting various Windows DPI related functions across +/// Windows OS versions. class Win32DpiHelper { public: Win32DpiHelper(); ~Win32DpiHelper(); - // Check if Windows Per Monitor V2 DPI scaling functionality is available on - // current system. - bool IsPerMonitorV2Available(); - - // Wrapper for OS functionality to turn on automatic window non-client scaling - BOOL EnableNonClientDpiScaling(HWND); - - // Wrapper for OS functionality to return the DPI for |HWND| - UINT GetDpiForWindow(HWND); - - // Sets the current process to a specified DPI awareness context. - BOOL SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT); + /// Returns the current DPI. Supports all DPI awareness modes, and is backward + /// compatible down to Windows Vista. + UINT GetDpi(HWND); private: - using EnableNonClientDpiScaling_ = BOOL __stdcall(HWND); using GetDpiForWindow_ = UINT __stdcall(HWND); - using SetProcessDpiAwarenessContext_ = BOOL __stdcall(DPI_AWARENESS_CONTEXT); + using GetDpiForMonitor_ = HRESULT __stdcall(HMONITOR hmonitor, + MONITOR_DPI_TYPE dpiType, + UINT* dpiX, + UINT* dpiY); + using MonitorFromWindow_ = HMONITOR __stdcall(HWND hwnd, DWORD dwFlags); - EnableNonClientDpiScaling_* enable_non_client_dpi_scaling_ = nullptr; GetDpiForWindow_* get_dpi_for_window_ = nullptr; - SetProcessDpiAwarenessContext_* set_process_dpi_awareness_context_ = nullptr; + GetDpiForMonitor_* get_dpi_for_monitor_ = nullptr; + MonitorFromWindow_* monitor_from_window_ = nullptr; HMODULE user32_module_ = nullptr; - bool permonitorv2_supported_ = false; + HMODULE shlib_module_ = nullptr; + bool dpi_for_window_supported_ = false; + bool dpi_for_monitor_supported_ = false; }; } // namespace flutter diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index 43690efec2753..e499bfbd129f8 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -6,16 +6,8 @@ namespace flutter { -Win32Window::Win32Window() { - // Assume Windows 10 1703 or greater for DPI handling. When running on a - // older release of Windows where this context doesn't exist, DPI calls will - // fail and Flutter rendering will be impacted until this is fixed. - // To handle downlevel correctly, dpi_helper must use the most recent DPI - // context available should be used: Windows 1703: Per-Monitor V2, 8.1: - // Per-Monitor V1, Windows 7: System See - // https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows - // for more information. -} +Win32Window::Win32Window() {} + Win32Window::~Win32Window() { Destroy(); } @@ -26,7 +18,7 @@ void Win32Window::InitializeChild(const char* title, Destroy(); std::wstring converted_title = NarrowToWide(title); - WNDCLASS window_class = ResgisterWindowClass(converted_title); + WNDCLASS window_class = RegisterWindowClass(converted_title); auto* result = CreateWindowEx( 0, window_class.lpszClassName, converted_title.c_str(), @@ -54,7 +46,7 @@ std::wstring Win32Window::NarrowToWide(const char* source) { return wideTitle; } -WNDCLASS Win32Window::ResgisterWindowClass(std::wstring& title) { +WNDCLASS Win32Window::RegisterWindowClass(std::wstring& title) { window_class_name_ = title; WNDCLASS window_class{}; @@ -82,14 +74,7 @@ LRESULT CALLBACK Win32Window::WndProc(HWND const window, reinterpret_cast(cs->lpCreateParams)); auto that = static_cast(cs->lpCreateParams); - - // Since the application is running in Per-monitor V2 mode, turn on - // automatic titlebar scaling - BOOL result = that->dpi_helper_->EnableNonClientDpiScaling(window); - if (result != TRUE) { - OutputDebugString(L"Failed to enable non-client area autoscaling"); - } - that->current_dpi_ = that->dpi_helper_->GetDpiForWindow(window); + that->current_dpi_ = that->dpi_helper_->GetDpi(window); that->window_handle_ = window; } else if (Win32Window* that = GetThisFromHandle(window)) { return that->MessageHandler(window, message, wparam, lparam); @@ -282,7 +267,7 @@ Win32Window::HandleDpiChange(HWND hwnd, // The DPI is only passed for DPI change messages on top level windows, // hence call function to get DPI if needed. if (uDpi == 0) { - uDpi = dpi_helper_->GetDpiForWindow(hwnd); + uDpi = dpi_helper_->GetDpi(hwnd); } current_dpi_ = uDpi; window->OnDpiScale(uDpi); diff --git a/shell/platform/windows/win32_window.h b/shell/platform/windows/win32_window.h index e670a52519308..63a00c355dea6 100644 --- a/shell/platform/windows/win32_window.h +++ b/shell/platform/windows/win32_window.h @@ -57,7 +57,7 @@ class Win32Window { // Registers a window class with default style attributes, cursor and // icon. - WNDCLASS ResgisterWindowClass(std::wstring& title); + WNDCLASS RegisterWindowClass(std::wstring& title); // OS callback called by message pump. Handles the WM_NCCREATE message which // is passed when the non-client area is being created and enables automatic