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.
5 #include "ui/gfx/win/dpi.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"
20 int kDefaultDPIX = 96;
21 int kDefaultDPIY = 96;
23 BOOL IsProcessDPIAwareWrapper() {
24 typedef BOOL(WINAPI *IsProcessDPIAwarePtr)(VOID);
25 IsProcessDPIAwarePtr is_process_dpi_aware_func =
26 reinterpret_cast<IsProcessDPIAwarePtr>(
27 GetProcAddress(GetModuleHandleA("user32.dll"), "IsProcessDPIAware"));
28 if (is_process_dpi_aware_func)
29 return is_process_dpi_aware_func();
33 float g_device_scale_factor = 0.0f;
35 float GetUnforcedDeviceScaleFactor() {
36 return static_cast<float>(gfx::GetDPI().width()) /
37 static_cast<float>(kDefaultDPIX);
40 float GetModernUIScaleWrapper() {
42 typedef float(WINAPI *GetModernUIScalePtr)(VOID);
43 HMODULE lib = LoadLibraryA("metro_driver.dll");
45 GetModernUIScalePtr func =
46 reinterpret_cast<GetModernUIScalePtr>(
47 GetProcAddress(lib, "GetModernUIScale"));
55 // Duplicated from Win8.1 SDK ShellScalingApi.h
56 typedef enum PROCESS_DPI_AWARENESS {
57 PROCESS_DPI_UNAWARE = 0,
58 PROCESS_SYSTEM_DPI_AWARE = 1,
59 PROCESS_PER_MONITOR_DPI_AWARE = 2
60 } PROCESS_DPI_AWARENESS;
62 typedef enum MONITOR_DPI_TYPE {
63 MDT_EFFECTIVE_DPI = 0,
66 MDT_DEFAULT = MDT_EFFECTIVE_DPI
69 // Win8.1 supports monitor-specific DPI scaling.
70 bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) {
71 typedef BOOL(WINAPI *SetProcessDpiAwarenessPtr)(PROCESS_DPI_AWARENESS);
72 SetProcessDpiAwarenessPtr set_process_dpi_awareness_func =
73 reinterpret_cast<SetProcessDpiAwarenessPtr>(
74 GetProcAddress(GetModuleHandleA("user32.dll"),
75 "SetProcessDpiAwarenessInternal"));
76 if (set_process_dpi_awareness_func) {
77 HRESULT hr = set_process_dpi_awareness_func(value);
79 VLOG(1) << "SetProcessDpiAwareness succeeded.";
81 } else if (hr == E_ACCESSDENIED) {
82 LOG(ERROR) << "Access denied error from SetProcessDpiAwareness. "
83 "Function called twice, or manifest was used.";
89 // This function works for Windows Vista through Win8. Win8.1 must use
90 // SetProcessDpiAwareness[Wrapper]
91 BOOL SetProcessDPIAwareWrapper() {
92 typedef BOOL(WINAPI *SetProcessDPIAwarePtr)(VOID);
93 SetProcessDPIAwarePtr set_process_dpi_aware_func =
94 reinterpret_cast<SetProcessDPIAwarePtr>(
95 GetProcAddress(GetModuleHandleA("user32.dll"),
96 "SetProcessDPIAware"));
97 return set_process_dpi_aware_func &&
98 set_process_dpi_aware_func();
105 float GetModernUIScale() {
106 return GetModernUIScaleWrapper();
109 void InitDeviceScaleFactor(float scale) {
110 DCHECK_NE(0.0f, scale);
111 g_device_scale_factor = scale;
115 static int dpi_x = 0;
116 static int dpi_y = 0;
117 static bool should_initialize = true;
119 if (should_initialize) {
120 should_initialize = false;
121 base::win::ScopedGetDC screen_dc(NULL);
122 // This value is safe to cache for the life time of the app since the
123 // user must logout to change the DPI setting. This value also applies
125 dpi_x = GetDeviceCaps(screen_dc, LOGPIXELSX);
126 dpi_y = GetDeviceCaps(screen_dc, LOGPIXELSY);
128 return Size(dpi_x, dpi_y);
131 float GetDPIScale() {
132 if (IsHighDPIEnabled()) {
133 return gfx::Display::HasForceDeviceScaleFactor() ?
134 gfx::Display::GetForcedDeviceScaleFactor() :
135 GetUnforcedDeviceScaleFactor();
140 bool IsHighDPIEnabled() {
141 // Default is disabled.
142 if (CommandLine::ForCurrentProcess()->HasSwitch(
143 switches::kHighDPISupport)) {
144 return CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
145 switches::kHighDPISupport).compare("1") == 0;
150 bool IsInHighDPIMode() {
151 return GetDPIScale() > 1.0;
154 void EnableHighDPISupport() {
155 if (IsHighDPIEnabled() &&
156 (base::win::GetVersion() < base::win::VERSION_WIN8_1)) {
157 if (!SetProcessDpiAwarenessWrapper(PROCESS_SYSTEM_DPI_AWARE)) {
158 SetProcessDPIAwareWrapper();
165 float GetDeviceScaleFactor() {
166 DCHECK_NE(0.0f, g_device_scale_factor);
167 return g_device_scale_factor;
170 Point ScreenToDIPPoint(const Point& pixel_point) {
171 static float scaling_factor =
172 GetDeviceScaleFactor() > GetUnforcedDeviceScaleFactor() ?
173 1.0f / GetDeviceScaleFactor() :
175 return ToFlooredPoint(ScalePoint(pixel_point,
179 Point DIPToScreenPoint(const Point& dip_point) {
180 return ToFlooredPoint(ScalePoint(dip_point, GetDeviceScaleFactor()));
183 Rect ScreenToDIPRect(const Rect& pixel_bounds) {
184 // TODO(kevers): Switch to non-deprecated method for float to int conversions.
185 return ToFlooredRectDeprecated(
186 ScaleRect(pixel_bounds, 1.0f / GetDeviceScaleFactor()));
189 Rect DIPToScreenRect(const Rect& dip_bounds) {
190 // TODO(kevers): Switch to non-deprecated method for float to int conversions.
191 return ToFlooredRectDeprecated(
192 ScaleRect(dip_bounds, GetDeviceScaleFactor()));
195 Size ScreenToDIPSize(const Size& size_in_pixels) {
196 return ToFlooredSize(
197 ScaleSize(size_in_pixels, 1.0f / GetDeviceScaleFactor()));
200 Size DIPToScreenSize(const Size& dip_size) {
201 return ToFlooredSize(ScaleSize(dip_size, GetDeviceScaleFactor()));
204 int GetSystemMetricsInDIP(int metric) {
205 return static_cast<int>(GetSystemMetrics(metric) /
206 GetDeviceScaleFactor() + 0.5);
209 double GetUndocumentedDPIScale() {
210 // TODO(girard): Remove this code when chrome is DPIAware.
211 static double scale = -1.0;
214 if (!IsProcessDPIAwareWrapper()) {
215 base::win::RegKey key(HKEY_CURRENT_USER,
216 L"Control Panel\\Desktop\\WindowMetrics",
220 if (key.ReadValueDW(L"AppliedDPI", &value) == ERROR_SUCCESS) {
221 scale = static_cast<double>(value) / kDefaultDPIX;
229 double GetUndocumentedDPITouchScale() {
230 static double scale =
231 (base::win::GetVersion() < base::win::VERSION_WIN8_1) ?
232 GetUndocumentedDPIScale() : 1.0;