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