Home | History | Annotate | Download | only in display
      1 // Copyright (c) 2013 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 <stdio.h>
      6 #include <string>
      7 #include <vector>
      8 
      9 #include "ash/display/display_info.h"
     10 #include "base/logging.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "ui/gfx/display.h"
     15 #include "ui/gfx/size_conversions.h"
     16 #include "ui/gfx/size_f.h"
     17 
     18 #if defined(OS_WIN)
     19 #include "ui/aura/window_tree_host.h"
     20 #include "ui/gfx/win/dpi.h"
     21 #endif
     22 
     23 namespace ash {
     24 namespace {
     25 
     26 bool allow_upgrade_to_high_dpi = false;
     27 
     28 }
     29 
     30 DisplayMode::DisplayMode()
     31     : refresh_rate(0.0f), interlaced(false), native(false) {}
     32 
     33 DisplayMode::DisplayMode(const gfx::Size& size,
     34                          float refresh_rate,
     35                          bool interlaced,
     36                          bool native)
     37     : size(size),
     38       refresh_rate(refresh_rate),
     39       interlaced(interlaced),
     40       native(native) {}
     41 
     42 // satic
     43 DisplayInfo DisplayInfo::CreateFromSpec(const std::string& spec) {
     44   return CreateFromSpecWithID(spec, gfx::Display::kInvalidDisplayID);
     45 }
     46 
     47 // static
     48 void DisplayInfo::SetAllowUpgradeToHighDPI(bool enable) {
     49   allow_upgrade_to_high_dpi = enable;
     50 }
     51 
     52 // static
     53 DisplayInfo DisplayInfo::CreateFromSpecWithID(const std::string& spec,
     54                                               int64 id) {
     55   // Default bounds for a display.
     56   const int kDefaultHostWindowX = 200;
     57   const int kDefaultHostWindowY = 200;
     58   const int kDefaultHostWindowWidth = 1366;
     59   const int kDefaultHostWindowHeight = 768;
     60 
     61   // Use larger than max int to catch overflow early.
     62   static int64 synthesized_display_id = 2200000000LL;
     63 
     64 #if defined(OS_WIN)
     65   gfx::Rect bounds_in_native(aura::WindowTreeHost::GetNativeScreenSize());
     66 #else
     67   gfx::Rect bounds_in_native(kDefaultHostWindowX, kDefaultHostWindowY,
     68                              kDefaultHostWindowWidth, kDefaultHostWindowHeight);
     69 #endif
     70   std::string main_spec = spec;
     71 
     72   float ui_scale = 1.0f;
     73   std::vector<std::string> parts;
     74   if (Tokenize(main_spec, "@", &parts) == 2) {
     75     double scale_in_double = 0;
     76     if (base::StringToDouble(parts[1], &scale_in_double))
     77       ui_scale = scale_in_double;
     78     main_spec = parts[0];
     79   }
     80 
     81   size_t count = Tokenize(main_spec, "/", &parts);
     82   gfx::Display::Rotation rotation(gfx::Display::ROTATE_0);
     83   bool has_overscan = false;
     84   if (count) {
     85     main_spec = parts[0];
     86     if (count >= 2) {
     87       std::string options = parts[1];
     88       for (size_t i = 0; i < options.size(); ++i) {
     89         char c = options[i];
     90         switch (c) {
     91           case 'o':
     92             has_overscan = true;
     93             break;
     94           case 'r':  // rotate 90 degrees to 'right'.
     95             rotation = gfx::Display::ROTATE_90;
     96             break;
     97           case 'u':  // 180 degrees, 'u'pside-down.
     98             rotation = gfx::Display::ROTATE_180;
     99             break;
    100           case 'l':  // rotate 90 degrees to 'left'.
    101             rotation = gfx::Display::ROTATE_270;
    102             break;
    103         }
    104       }
    105     }
    106   }
    107 
    108   int x = 0, y = 0, width, height;
    109   float device_scale_factor = 1.0f;
    110   if (sscanf(main_spec.c_str(), "%dx%d*%f",
    111              &width, &height, &device_scale_factor) >= 2 ||
    112       sscanf(main_spec.c_str(), "%d+%d-%dx%d*%f", &x, &y, &width, &height,
    113              &device_scale_factor) >= 4) {
    114     bounds_in_native.SetRect(x, y, width, height);
    115   } else {
    116 #if defined(OS_WIN)
    117     if (gfx::IsHighDPIEnabled()) {
    118       device_scale_factor = gfx::GetDPIScale();
    119     }
    120 #endif
    121   }
    122 
    123   std::vector<DisplayMode> display_modes;
    124   if (Tokenize(main_spec, "#", &parts) == 2) {
    125     size_t native_mode = 0;
    126     int largest_area = -1;
    127     float highest_refresh_rate = -1.0f;
    128     main_spec = parts[0];
    129     std::string resolution_list = parts[1];
    130     count = Tokenize(resolution_list, "|", &parts);
    131     for (size_t i = 0; i < count; ++i) {
    132       std::string resolution = parts[i];
    133       int width, height;
    134       float refresh_rate = 0.0f;
    135       if (sscanf(resolution.c_str(),
    136                  "%dx%d%%%f",
    137                  &width,
    138                  &height,
    139                  &refresh_rate) >= 2) {
    140         if (width * height >= largest_area &&
    141             refresh_rate > highest_refresh_rate) {
    142           // Use mode with largest area and highest refresh rate as native.
    143           largest_area = width * height;
    144           highest_refresh_rate = refresh_rate;
    145           native_mode = i;
    146         }
    147         display_modes.push_back(
    148             DisplayMode(gfx::Size(width, height), refresh_rate, false, false));
    149       }
    150     }
    151     display_modes[native_mode].native = true;
    152   }
    153 
    154   if (id == gfx::Display::kInvalidDisplayID)
    155     id = synthesized_display_id++;
    156   DisplayInfo display_info(
    157       id, base::StringPrintf("Display-%d", static_cast<int>(id)), has_overscan);
    158   display_info.set_device_scale_factor(device_scale_factor);
    159   display_info.set_rotation(rotation);
    160   display_info.set_configured_ui_scale(ui_scale);
    161   display_info.SetBounds(bounds_in_native);
    162   display_info.set_display_modes(display_modes);
    163 
    164   // To test the overscan, it creates the default 5% overscan.
    165   if (has_overscan) {
    166     int width = bounds_in_native.width() / device_scale_factor / 40;
    167     int height = bounds_in_native.height() / device_scale_factor / 40;
    168     display_info.SetOverscanInsets(gfx::Insets(height, width, height, width));
    169     display_info.UpdateDisplaySize();
    170   }
    171 
    172   DVLOG(1) << "DisplayInfoFromSpec info=" << display_info.ToString()
    173            << ", spec=" << spec;
    174   return display_info;
    175 }
    176 
    177 DisplayInfo::DisplayInfo()
    178     : id_(gfx::Display::kInvalidDisplayID),
    179       has_overscan_(false),
    180       rotation_(gfx::Display::ROTATE_0),
    181       touch_support_(gfx::Display::TOUCH_SUPPORT_UNKNOWN),
    182       touch_device_id_(0),
    183       device_scale_factor_(1.0f),
    184       overscan_insets_in_dip_(0, 0, 0, 0),
    185       configured_ui_scale_(1.0f),
    186       native_(false),
    187       color_profile_(ui::COLOR_PROFILE_STANDARD) {
    188 }
    189 
    190 DisplayInfo::DisplayInfo(int64 id,
    191                          const std::string& name,
    192                          bool has_overscan)
    193     : id_(id),
    194       name_(name),
    195       has_overscan_(has_overscan),
    196       rotation_(gfx::Display::ROTATE_0),
    197       touch_support_(gfx::Display::TOUCH_SUPPORT_UNKNOWN),
    198       touch_device_id_(0),
    199       device_scale_factor_(1.0f),
    200       overscan_insets_in_dip_(0, 0, 0, 0),
    201       configured_ui_scale_(1.0f),
    202       native_(false),
    203       color_profile_(ui::COLOR_PROFILE_STANDARD) {
    204 }
    205 
    206 DisplayInfo::~DisplayInfo() {
    207 }
    208 
    209 void DisplayInfo::Copy(const DisplayInfo& native_info) {
    210   DCHECK(id_ == native_info.id_);
    211   name_ = native_info.name_;
    212   has_overscan_ = native_info.has_overscan_;
    213 
    214   DCHECK(!native_info.bounds_in_native_.IsEmpty());
    215   bounds_in_native_ = native_info.bounds_in_native_;
    216   size_in_pixel_ = native_info.size_in_pixel_;
    217   device_scale_factor_ = native_info.device_scale_factor_;
    218   display_modes_ = native_info.display_modes_;
    219   touch_support_ = native_info.touch_support_;
    220   touch_device_id_ = native_info.touch_device_id_;
    221 
    222   // Copy overscan_insets_in_dip_ if it's not empty. This is for test
    223   // cases which use "/o" annotation which sets the overscan inset
    224   // to native, and that overscan has to be propagated. This does not
    225   // happen on the real environment.
    226   if (!native_info.overscan_insets_in_dip_.empty())
    227     overscan_insets_in_dip_ = native_info.overscan_insets_in_dip_;
    228 
    229   // Rotation_ and ui_scale_ color_profile_ are given by preference,
    230   // or unit tests. Don't copy if this native_info came from
    231   // DisplayChangeObserver.
    232   if (!native_info.native()) {
    233     rotation_ = native_info.rotation_;
    234     configured_ui_scale_ = native_info.configured_ui_scale_;
    235     color_profile_ = native_info.color_profile();
    236   }
    237 
    238   available_color_profiles_ = native_info.available_color_profiles();
    239 
    240   // Don't copy insets as it may be given by preference.  |rotation_|
    241   // is treated as a native so that it can be specified in
    242   // |CreateFromSpec|.
    243 }
    244 
    245 void DisplayInfo::SetBounds(const gfx::Rect& new_bounds_in_native) {
    246   bounds_in_native_ = new_bounds_in_native;
    247   size_in_pixel_ = new_bounds_in_native.size();
    248   UpdateDisplaySize();
    249 }
    250 
    251 float DisplayInfo::GetEffectiveDeviceScaleFactor() const {
    252   if (allow_upgrade_to_high_dpi && configured_ui_scale_ < 1.0f &&
    253       device_scale_factor_ == 1.0f) {
    254     return 2.0f;
    255   } else if (device_scale_factor_ == 2.0f && configured_ui_scale_ == 2.0f) {
    256     return 1.0f;
    257   }
    258   return device_scale_factor_;
    259 }
    260 
    261 float DisplayInfo::GetEffectiveUIScale() const {
    262   if (allow_upgrade_to_high_dpi && configured_ui_scale_ < 1.0f &&
    263       device_scale_factor_ == 1.0f) {
    264     return configured_ui_scale_ * 2.0f;
    265   } else if (device_scale_factor_ == 2.0f && configured_ui_scale_ == 2.0f) {
    266     return 1.0f;
    267   }
    268   return configured_ui_scale_;
    269 }
    270 
    271 void DisplayInfo::UpdateDisplaySize() {
    272   size_in_pixel_ = bounds_in_native_.size();
    273   if (!overscan_insets_in_dip_.empty()) {
    274     gfx::Insets insets_in_pixel =
    275         overscan_insets_in_dip_.Scale(device_scale_factor_);
    276     size_in_pixel_.Enlarge(-insets_in_pixel.width(), -insets_in_pixel.height());
    277   } else {
    278     overscan_insets_in_dip_.Set(0, 0, 0, 0);
    279   }
    280 
    281   if (rotation_ == gfx::Display::ROTATE_90 ||
    282       rotation_ == gfx::Display::ROTATE_270)
    283     size_in_pixel_.SetSize(size_in_pixel_.height(), size_in_pixel_.width());
    284   gfx::SizeF size_f(size_in_pixel_);
    285   size_f.Scale(GetEffectiveUIScale());
    286   size_in_pixel_ = gfx::ToFlooredSize(size_f);
    287 }
    288 
    289 void DisplayInfo::SetOverscanInsets(const gfx::Insets& insets_in_dip) {
    290   overscan_insets_in_dip_ = insets_in_dip;
    291 }
    292 
    293 gfx::Insets DisplayInfo::GetOverscanInsetsInPixel() const {
    294   return overscan_insets_in_dip_.Scale(device_scale_factor_);
    295 }
    296 
    297 std::string DisplayInfo::ToString() const {
    298   int rotation_degree = static_cast<int>(rotation_) * 90;
    299   return base::StringPrintf(
    300       "DisplayInfo[%lld] native bounds=%s, size=%s, scale=%f, "
    301       "overscan=%s, rotation=%d, ui-scale=%f, touchscreen=%s, "
    302       "touch-device-id=%d",
    303       static_cast<long long int>(id_),
    304       bounds_in_native_.ToString().c_str(),
    305       size_in_pixel_.ToString().c_str(),
    306       device_scale_factor_,
    307       overscan_insets_in_dip_.ToString().c_str(),
    308       rotation_degree,
    309       configured_ui_scale_,
    310       touch_support_ == gfx::Display::TOUCH_SUPPORT_AVAILABLE
    311           ? "yes"
    312           : touch_support_ == gfx::Display::TOUCH_SUPPORT_UNAVAILABLE
    313                 ? "no"
    314                 : "unknown",
    315       touch_device_id_);
    316 }
    317 
    318 std::string DisplayInfo::ToFullString() const {
    319   std::string display_modes_str;
    320   std::vector<DisplayMode>::const_iterator iter = display_modes_.begin();
    321   for (; iter != display_modes_.end(); ++iter) {
    322     if (!display_modes_str.empty())
    323       display_modes_str += ",";
    324     base::StringAppendF(&display_modes_str,
    325                         "(%dx%d@%f%c%s)",
    326                         iter->size.width(),
    327                         iter->size.height(),
    328                         iter->refresh_rate,
    329                         iter->interlaced ? 'I' : 'P',
    330                         iter->native ? "(N)" : "");
    331   }
    332   return ToString() + ", display_modes==" + display_modes_str;
    333 }
    334 
    335 void DisplayInfo::SetColorProfile(ui::ColorCalibrationProfile profile) {
    336   if (IsColorProfileAvailable(profile))
    337     color_profile_ = profile;
    338 }
    339 
    340 bool DisplayInfo::IsColorProfileAvailable(
    341     ui::ColorCalibrationProfile profile) const {
    342   return std::find(available_color_profiles_.begin(),
    343                    available_color_profiles_.end(),
    344                    profile) != available_color_profiles_.end();
    345 }
    346 
    347 }  // namespace ash
    348