Home | History | Annotate | Download | only in win
      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