// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "win32_dpi_utils.h" namespace flutter { namespace { constexpr UINT kDefaultDpi = 96; // This is the MDT_EFFECTIVE_DPI value from MONITOR_DPI_TYPE, an enum declared // in ShellScalingApi.h. Replicating here to avoid importing the library // directly. constexpr UINT kEffectiveDpiMonitorType = 0; template /// Retrieves a function |name| from a given |comBaseModule| into |outProc|. /// Returns a bool indicating whether the function was found. bool AssignProcAddress(HMODULE comBaseModule, const char* name, T*& outProc) { outProc = reinterpret_cast(GetProcAddress(comBaseModule, name)); return *outProc != nullptr; } /// A helper class for abstracting various Windows DPI related functions across /// Windows OS versions. class Win32DpiHelper { public: Win32DpiHelper(); ~Win32DpiHelper(); /// Returns the DPI for |hwnd|. Supports all DPI awareness modes, and is /// backward compatible down to Windows Vista. If |hwnd| is nullptr, returns /// the DPI for the primary monitor. If Per-Monitor DPI awareness is not /// available, returns the system's DPI. UINT GetDpiForWindow(HWND); /// Returns the DPI of a given monitor. Defaults to 96 if the API is not /// available. UINT GetDpiForMonitor(HMONITOR); private: using GetDpiForWindow_ = UINT __stdcall(HWND); using GetDpiForMonitor_ = HRESULT __stdcall(HMONITOR hmonitor, UINT dpiType, UINT* dpiX, UINT* dpiY); using EnableNonClientDpiScaling_ = BOOL __stdcall(HWND hwnd); GetDpiForWindow_* get_dpi_for_window_ = nullptr; GetDpiForMonitor_* get_dpi_for_monitor_ = nullptr; EnableNonClientDpiScaling_* enable_non_client_dpi_scaling_ = nullptr; HMODULE user32_module_ = nullptr; HMODULE shlib_module_ = nullptr; bool dpi_for_window_supported_ = false; bool dpi_for_monitor_supported_ = false; }; Win32DpiHelper::Win32DpiHelper() { if ((user32_module_ = LoadLibraryA("User32.dll")) != nullptr) { dpi_for_window_supported_ = (AssignProcAddress( user32_module_, "GetDpiForWindow", get_dpi_for_window_)); } if ((shlib_module_ = LoadLibraryA("Shcore.dll")) != nullptr) { dpi_for_monitor_supported_ = AssignProcAddress( shlib_module_, "GetDpiForMonitor", get_dpi_for_monitor_); } } Win32DpiHelper::~Win32DpiHelper() { if (user32_module_ != nullptr) { FreeLibrary(user32_module_); } if (shlib_module_ != nullptr) { FreeLibrary(shlib_module_); } } UINT Win32DpiHelper::GetDpiForWindow(HWND hwnd) { // GetDpiForWindow returns the DPI for any awareness mode. If not available, // or no |hwnd| is provided, fallback to a per monitor, system, or default // DPI. if (dpi_for_window_supported_ && hwnd != nullptr) { return get_dpi_for_window_(hwnd); } if (dpi_for_monitor_supported_) { HMONITOR monitor = nullptr; if (hwnd != nullptr) { monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); } return GetDpiForMonitor(monitor); } HDC hdc = GetDC(hwnd); UINT dpi = GetDeviceCaps(hdc, LOGPIXELSX); ReleaseDC(hwnd, hdc); return dpi; } UINT Win32DpiHelper::GetDpiForMonitor(HMONITOR monitor) { if (dpi_for_monitor_supported_) { if (monitor == nullptr) { const POINT target_point = {0, 0}; monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTOPRIMARY); } UINT dpi_x = 0, dpi_y = 0; HRESULT result = get_dpi_for_monitor_(monitor, kEffectiveDpiMonitorType, &dpi_x, &dpi_y); if (SUCCEEDED(result)) { return dpi_x; } } return kDefaultDpi; } // namespace Win32DpiHelper* GetHelper() { static Win32DpiHelper* dpi_helper = new Win32DpiHelper(); return dpi_helper; } } // namespace UINT GetDpiForHWND(HWND hwnd) { return GetHelper()->GetDpiForWindow(hwnd); } UINT GetDpiForMonitor(HMONITOR monitor) { return GetHelper()->GetDpiForMonitor(monitor); } } // namespace flutter