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 return gfx::Display::HasForceDeviceScaleFactor() ?
138 gfx::Display::GetForcedDeviceScaleFactor() :
139 GetUnforcedDeviceScaleFactor();
140 }
141 return 1.0;
142 }
143
ForceHighDPISupportForTesting(float scale)144 void ForceHighDPISupportForTesting(float scale) {
145 g_device_scale_factor = scale;
146 }
147
IsHighDPIEnabled()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
IsInHighDPIMode()158 bool IsInHighDPIMode() {
159 return GetDPIScale() > 1.0;
160 }
161
EnableHighDPISupport()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
GetDeviceScaleFactor()175 float GetDeviceScaleFactor() {
176 DCHECK_NE(0.0f, g_device_scale_factor);
177 return g_device_scale_factor;
178 }
179
ScreenToDIPPoint(const Point & pixel_point)180 Point ScreenToDIPPoint(const Point& pixel_point) {
181 return ToFlooredPoint(ScalePoint(pixel_point,
182 1.0f / GetDeviceScaleFactor()));
183 }
184
DIPToScreenPoint(const Point & dip_point)185 Point DIPToScreenPoint(const Point& dip_point) {
186 return ToFlooredPoint(ScalePoint(dip_point, GetDeviceScaleFactor()));
187 }
188
ScreenToDIPRect(const Rect & pixel_bounds)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
DIPToScreenRect(const Rect & dip_bounds)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
ScreenToDIPSize(const Size & size_in_pixels)209 Size ScreenToDIPSize(const Size& size_in_pixels) {
210 return ToFlooredSize(
211 ScaleSize(size_in_pixels, 1.0f / GetDeviceScaleFactor()));
212 }
213
DIPToScreenSize(const Size & dip_size)214 Size DIPToScreenSize(const Size& dip_size) {
215 return ToFlooredSize(ScaleSize(dip_size, GetDeviceScaleFactor()));
216 }
217
GetSystemMetricsInDIP(int metric)218 int GetSystemMetricsInDIP(int metric) {
219 return static_cast<int>(GetSystemMetrics(metric) /
220 GetDeviceScaleFactor() + 0.5);
221 }
222
IsDeviceScaleFactorSet()223 bool IsDeviceScaleFactorSet() {
224 return g_device_scale_factor != 0.0f;
225 }
226
227 } // namespace win
228 } // namespace gfx
229