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