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 return gfx::Display::HasForceDeviceScaleFactor() ? 138 gfx::Display::GetForcedDeviceScaleFactor() : 139 GetUnforcedDeviceScaleFactor(); 140 } 141 return 1.0; 142 } 143 144 void ForceHighDPISupportForTesting(float scale) { 145 g_device_scale_factor = scale; 146 } 147 148 bool IsHighDPIEnabled() { 149 // Flag stored in HKEY_CURRENT_USER\SOFTWARE\\Google\\Chrome\\Profile, 150 // under the DWORD value high-dpi-support. 151 // Default is disabled. 152 static DWORD value = ReadRegistryValue( 153 HKEY_CURRENT_USER, gfx::win::kRegistryProfilePath, 154 gfx::win::kHighDPISupportW, TRUE); 155 return value != 0; 156 } 157 158 bool IsInHighDPIMode() { 159 return GetDPIScale() > 1.0; 160 } 161 162 void EnableHighDPISupport() { 163 if (IsHighDPIEnabled() && 164 !SetProcessDpiAwarenessWrapper(PROCESS_SYSTEM_DPI_AWARE)) { 165 SetProcessDPIAwareWrapper(); 166 } 167 } 168 169 namespace win { 170 171 GFX_EXPORT const wchar_t kRegistryProfilePath[] = 172 L"Software\\Google\\Chrome\\Profile"; 173 GFX_EXPORT const wchar_t kHighDPISupportW[] = L"high-dpi-support"; 174 175 float GetDeviceScaleFactor() { 176 DCHECK_NE(0.0f, g_device_scale_factor); 177 return g_device_scale_factor; 178 } 179 180 Point ScreenToDIPPoint(const Point& pixel_point) { 181 return ToFlooredPoint(ScalePoint(pixel_point, 182 1.0f / GetDeviceScaleFactor())); 183 } 184 185 Point DIPToScreenPoint(const Point& dip_point) { 186 return ToFlooredPoint(ScalePoint(dip_point, GetDeviceScaleFactor())); 187 } 188 189 Rect ScreenToDIPRect(const Rect& pixel_bounds) { 190 // TODO(kevers): Switch to non-deprecated method for float to int conversions. 191 return ToFlooredRectDeprecated( 192 ScaleRect(pixel_bounds, 1.0f / GetDeviceScaleFactor())); 193 } 194 195 Rect DIPToScreenRect(const Rect& dip_bounds) { 196 // We scale the origin by the scale factor and round up via ceil. This 197 // ensures that we get the original logical origin back when we scale down. 198 // We round the size down after scaling. It may be better to round this up 199 // on the same lines as the origin. 200 // TODO(ananta) 201 // Investigate if rounding size up on the same lines as origin is workable. 202 return gfx::Rect( 203 gfx::ToCeiledPoint(gfx::ScalePoint( 204 dip_bounds.origin(), GetDeviceScaleFactor())), 205 gfx::ToFlooredSize(gfx::ScaleSize( 206 dip_bounds.size(), GetDeviceScaleFactor()))); 207 } 208 209 Size ScreenToDIPSize(const Size& size_in_pixels) { 210 return ToFlooredSize( 211 ScaleSize(size_in_pixels, 1.0f / GetDeviceScaleFactor())); 212 } 213 214 Size DIPToScreenSize(const Size& dip_size) { 215 return ToFlooredSize(ScaleSize(dip_size, GetDeviceScaleFactor())); 216 } 217 218 int GetSystemMetricsInDIP(int metric) { 219 return static_cast<int>(GetSystemMetrics(metric) / 220 GetDeviceScaleFactor() + 0.5); 221 } 222 223 bool IsDeviceScaleFactorSet() { 224 return g_device_scale_factor != 0.0f; 225 } 226 227 } // namespace win 228 } // namespace gfx 229