1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/gfx/win/dpi.h" 6 7 #include <windows.h> 8 #include "base/command_line.h" 9 #include "base/win/scoped_hdc.h" 10 #include "base/win/windows_version.h" 11 #include "base/win/registry.h" 12 #include "ui/gfx/display.h" 13 #include "ui/gfx/switches.h" 14 #include "ui/gfx/point_conversions.h" 15 #include "ui/gfx/rect_conversions.h" 16 #include "ui/gfx/size_conversions.h" 17 18 namespace { 19 20 int kDefaultDPIX = 96; 21 int kDefaultDPIY = 96; 22 23 bool force_highdpi_for_testing = false; 24 25 BOOL IsProcessDPIAwareWrapper() { 26 typedef BOOL(WINAPI *IsProcessDPIAwarePtr)(VOID); 27 IsProcessDPIAwarePtr is_process_dpi_aware_func = 28 reinterpret_cast<IsProcessDPIAwarePtr>( 29 GetProcAddress(GetModuleHandleA("user32.dll"), "IsProcessDPIAware")); 30 if (is_process_dpi_aware_func) 31 return is_process_dpi_aware_func(); 32 return FALSE; 33 } 34 35 float g_device_scale_factor = 0.0f; 36 37 float GetUnforcedDeviceScaleFactor() { 38 // If the global device scale factor is initialized use it. This is to ensure 39 // we use the same scale factor across all callsites. We don't use the 40 // GetDeviceScaleFactor function here because it fires a DCHECK if the 41 // g_device_scale_factor global is 0. 42 if (g_device_scale_factor) 43 return g_device_scale_factor; 44 return static_cast<float>(gfx::GetDPI().width()) / 45 static_cast<float>(kDefaultDPIX); 46 } 47 48 // Duplicated from Win8.1 SDK ShellScalingApi.h 49 typedef enum PROCESS_DPI_AWARENESS { 50 PROCESS_DPI_UNAWARE = 0, 51 PROCESS_SYSTEM_DPI_AWARE = 1, 52 PROCESS_PER_MONITOR_DPI_AWARE = 2 53 } PROCESS_DPI_AWARENESS; 54 55 typedef enum MONITOR_DPI_TYPE { 56 MDT_EFFECTIVE_DPI = 0, 57 MDT_ANGULAR_DPI = 1, 58 MDT_RAW_DPI = 2, 59 MDT_DEFAULT = MDT_EFFECTIVE_DPI 60 } MONITOR_DPI_TYPE; 61 62 // Win8.1 supports monitor-specific DPI scaling. 63 bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) { 64 typedef BOOL(WINAPI *SetProcessDpiAwarenessPtr)(PROCESS_DPI_AWARENESS); 65 SetProcessDpiAwarenessPtr set_process_dpi_awareness_func = 66 reinterpret_cast<SetProcessDpiAwarenessPtr>( 67 GetProcAddress(GetModuleHandleA("user32.dll"), 68 "SetProcessDpiAwarenessInternal")); 69 if (set_process_dpi_awareness_func) { 70 HRESULT hr = set_process_dpi_awareness_func(value); 71 if (SUCCEEDED(hr)) { 72 VLOG(1) << "SetProcessDpiAwareness succeeded."; 73 return true; 74 } else if (hr == E_ACCESSDENIED) { 75 LOG(ERROR) << "Access denied error from SetProcessDpiAwareness. " 76 "Function called twice, or manifest was used."; 77 } 78 } 79 return false; 80 } 81 82 // This function works for Windows Vista through Win8. Win8.1 must use 83 // SetProcessDpiAwareness[Wrapper] 84 BOOL SetProcessDPIAwareWrapper() { 85 typedef BOOL(WINAPI *SetProcessDPIAwarePtr)(VOID); 86 SetProcessDPIAwarePtr set_process_dpi_aware_func = 87 reinterpret_cast<SetProcessDPIAwarePtr>( 88 GetProcAddress(GetModuleHandleA("user32.dll"), 89 "SetProcessDPIAware")); 90 return set_process_dpi_aware_func && 91 set_process_dpi_aware_func(); 92 } 93 94 DWORD ReadRegistryValue(HKEY root, 95 const wchar_t* base_key, 96 const wchar_t* value_name, 97 DWORD default_value) { 98 base::win::RegKey reg_key(HKEY_CURRENT_USER, 99 base_key, 100 KEY_QUERY_VALUE); 101 DWORD value; 102 if (reg_key.Valid() && 103 reg_key.ReadValueDW(value_name, &value) == ERROR_SUCCESS) { 104 return value; 105 } 106 return default_value; 107 } 108 109 } // namespace 110 111 namespace gfx { 112 113 void InitDeviceScaleFactor(float scale) { 114 DCHECK_NE(0.0f, scale); 115 g_device_scale_factor = scale; 116 } 117 118 Size GetDPI() { 119 static int dpi_x = 0; 120 static int dpi_y = 0; 121 static bool should_initialize = true; 122 123 if (should_initialize) { 124 should_initialize = false; 125 base::win::ScopedGetDC screen_dc(NULL); 126 // This value is safe to cache for the life time of the app since the 127 // user must logout to change the DPI setting. This value also applies 128 // to all screens. 129 dpi_x = GetDeviceCaps(screen_dc, LOGPIXELSX); 130 dpi_y = GetDeviceCaps(screen_dc, LOGPIXELSY); 131 } 132 return Size(dpi_x, dpi_y); 133 } 134 135 float GetDPIScale() { 136 if (IsHighDPIEnabled()) { 137 if (gfx::Display::HasForceDeviceScaleFactor()) 138 return gfx::Display::GetForcedDeviceScaleFactor(); 139 float dpi_scale = GetUnforcedDeviceScaleFactor(); 140 if (dpi_scale <= 1.25) { 141 // Force 125% and below to 100% scale. We do this to maintain previous 142 // (non-DPI-aware) behavior where only the font size was boosted. 143 dpi_scale = 1.0; 144 } 145 return dpi_scale; 146 } 147 return 1.0; 148 } 149 150 void ForceHighDPISupportForTesting(float scale) { 151 g_device_scale_factor = scale; 152 } 153 154 bool IsHighDPIEnabled() { 155 // Flag stored in HKEY_CURRENT_USER\SOFTWARE\\Google\\Chrome\\Profile, 156 // under the DWORD value high-dpi-support. 157 // Default is disabled. 158 static DWORD value = ReadRegistryValue( 159 HKEY_CURRENT_USER, gfx::win::kRegistryProfilePath, 160 gfx::win::kHighDPISupportW, TRUE); 161 return value != 0; 162 } 163 164 bool IsInHighDPIMode() { 165 return GetDPIScale() > 1.0; 166 } 167 168 void EnableHighDPISupport() { 169 if (IsHighDPIEnabled() && 170 !SetProcessDpiAwarenessWrapper(PROCESS_SYSTEM_DPI_AWARE)) { 171 SetProcessDPIAwareWrapper(); 172 } 173 } 174 175 namespace win { 176 177 GFX_EXPORT const wchar_t kRegistryProfilePath[] = 178 L"Software\\Google\\Chrome\\Profile"; 179 GFX_EXPORT const wchar_t kHighDPISupportW[] = L"high-dpi-support"; 180 181 float GetDeviceScaleFactor() { 182 DCHECK_NE(0.0f, g_device_scale_factor); 183 return g_device_scale_factor; 184 } 185 186 Point ScreenToDIPPoint(const Point& pixel_point) { 187 return ToFlooredPoint(ScalePoint(pixel_point, 188 1.0f / GetDeviceScaleFactor())); 189 } 190 191 Point DIPToScreenPoint(const Point& dip_point) { 192 return ToFlooredPoint(ScalePoint(dip_point, GetDeviceScaleFactor())); 193 } 194 195 Rect ScreenToDIPRect(const Rect& pixel_bounds) { 196 // It's important we scale the origin and size separately. If we instead 197 // calculated the size from the floored origin and ceiled right the size could 198 // vary depending upon where the two points land. That would cause problems 199 // for the places this code is used (in particular mapping from native window 200 // bounds to DIPs). 201 return Rect(ScreenToDIPPoint(pixel_bounds.origin()), 202 ScreenToDIPSize(pixel_bounds.size())); 203 } 204 205 Rect DIPToScreenRect(const Rect& dip_bounds) { 206 // See comment in ScreenToDIPRect for why we calculate size like this. 207 return Rect(DIPToScreenPoint(dip_bounds.origin()), 208 DIPToScreenSize(dip_bounds.size())); 209 } 210 211 Size ScreenToDIPSize(const Size& size_in_pixels) { 212 // Always ceil sizes. Otherwise we may be leaving off part of the bounds. 213 return ToCeiledSize( 214 ScaleSize(size_in_pixels, 1.0f / GetDeviceScaleFactor())); 215 } 216 217 Size DIPToScreenSize(const Size& dip_size) { 218 // Always ceil sizes. Otherwise we may be leaving off part of the bounds. 219 return ToCeiledSize(ScaleSize(dip_size, GetDeviceScaleFactor())); 220 } 221 222 int GetSystemMetricsInDIP(int metric) { 223 return static_cast<int>(GetSystemMetrics(metric) / 224 GetDeviceScaleFactor() + 0.5); 225 } 226 227 bool IsDeviceScaleFactorSet() { 228 return g_device_scale_factor != 0.0f; 229 } 230 231 } // namespace win 232 } // namespace gfx 233