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