• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsProcessDPIAwareWrapper()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 
GetUnforcedDeviceScaleFactor()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.
SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value)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]
SetProcessDPIAwareWrapper()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 
ReadRegistryValue(HKEY root,const wchar_t * base_key,const wchar_t * value_name,DWORD default_value)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 
InitDeviceScaleFactor(float scale)113 void InitDeviceScaleFactor(float scale) {
114   DCHECK_NE(0.0f, scale);
115   g_device_scale_factor = scale;
116 }
117 
GetDPI()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 
GetDPIScale()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 
ForceHighDPISupportForTesting(float scale)150 void ForceHighDPISupportForTesting(float scale) {
151   g_device_scale_factor = scale;
152 }
153 
IsHighDPIEnabled()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 
IsInHighDPIMode()164 bool IsInHighDPIMode() {
165   return GetDPIScale() > 1.0;
166 }
167 
EnableHighDPISupport()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 
GetDeviceScaleFactor()181 float GetDeviceScaleFactor() {
182   DCHECK_NE(0.0f, g_device_scale_factor);
183   return g_device_scale_factor;
184 }
185 
ScreenToDIPPoint(const Point & pixel_point)186 Point ScreenToDIPPoint(const Point& pixel_point) {
187   return ToFlooredPoint(ScalePoint(pixel_point,
188                                    1.0f / GetDeviceScaleFactor()));
189 }
190 
DIPToScreenPoint(const Point & dip_point)191 Point DIPToScreenPoint(const Point& dip_point) {
192   return ToFlooredPoint(ScalePoint(dip_point, GetDeviceScaleFactor()));
193 }
194 
ScreenToDIPRect(const Rect & pixel_bounds)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 
DIPToScreenRect(const Rect & dip_bounds)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 
ScreenToDIPSize(const Size & size_in_pixels)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 
DIPToScreenSize(const Size & dip_size)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 
GetSystemMetricsInDIP(int metric)222 int GetSystemMetricsInDIP(int metric) {
223   return static_cast<int>(GetSystemMetrics(metric) /
224       GetDeviceScaleFactor() + 0.5);
225 }
226 
IsDeviceScaleFactorSet()227 bool IsDeviceScaleFactorSet() {
228   return g_device_scale_factor != 0.0f;
229 }
230 
231 }  // namespace win
232 }  // namespace gfx
233