| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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/display/chromeos/output_configurator.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/command_line.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/strings/string_number_conversions.h" | |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #include "base/sys_info.h" | |
| 13 #include "base/time/time.h" | |
| 14 #include "ui/display/chromeos/display_mode.h" | |
| 15 #include "ui/display/chromeos/display_snapshot.h" | |
| 16 #include "ui/display/chromeos/native_display_delegate.h" | |
| 17 #include "ui/display/display_switches.h" | |
| 18 | |
| 19 #if defined(USE_OZONE) | |
| 20 #include "ui/display/chromeos/ozone/native_display_delegate_ozone.h" | |
| 21 #include "ui/display/chromeos/ozone/touchscreen_delegate_ozone.h" | |
| 22 #elif defined(USE_X11) | |
| 23 #include "ui/display/chromeos/x11/native_display_delegate_x11.h" | |
| 24 #include "ui/display/chromeos/x11/touchscreen_delegate_x11.h" | |
| 25 #endif | |
| 26 | |
| 27 namespace ui { | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 typedef std::vector<const DisplayMode*> DisplayModeList; | |
| 32 | |
| 33 // The delay to perform configuration after RRNotify. See the comment | |
| 34 // in |Dispatch()|. | |
| 35 const int kConfigureDelayMs = 500; | |
| 36 | |
| 37 // Returns a string describing |state|. | |
| 38 std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) { | |
| 39 switch (state) { | |
| 40 case chromeos::DISPLAY_POWER_ALL_ON: | |
| 41 return "ALL_ON"; | |
| 42 case chromeos::DISPLAY_POWER_ALL_OFF: | |
| 43 return "ALL_OFF"; | |
| 44 case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON: | |
| 45 return "INTERNAL_OFF_EXTERNAL_ON"; | |
| 46 case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF: | |
| 47 return "INTERNAL_ON_EXTERNAL_OFF"; | |
| 48 default: | |
| 49 return "unknown (" + base::IntToString(state) + ")"; | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 // Returns a string describing |state|. | |
| 54 std::string OutputStateToString(OutputState state) { | |
| 55 switch (state) { | |
| 56 case OUTPUT_STATE_INVALID: | |
| 57 return "INVALID"; | |
| 58 case OUTPUT_STATE_HEADLESS: | |
| 59 return "HEADLESS"; | |
| 60 case OUTPUT_STATE_SINGLE: | |
| 61 return "SINGLE"; | |
| 62 case OUTPUT_STATE_DUAL_MIRROR: | |
| 63 return "DUAL_MIRROR"; | |
| 64 case OUTPUT_STATE_DUAL_EXTENDED: | |
| 65 return "DUAL_EXTENDED"; | |
| 66 } | |
| 67 NOTREACHED() << "Unknown state " << state; | |
| 68 return "INVALID"; | |
| 69 } | |
| 70 | |
| 71 // Returns the number of outputs in |outputs| that should be turned on, per | |
| 72 // |state|. If |output_power| is non-NULL, it is updated to contain the | |
| 73 // on/off state of each corresponding entry in |outputs|. | |
| 74 int GetOutputPower(const std::vector<OutputConfigurator::DisplayState>& outputs, | |
| 75 chromeos::DisplayPowerState state, | |
| 76 std::vector<bool>* output_power) { | |
| 77 int num_on_outputs = 0; | |
| 78 if (output_power) | |
| 79 output_power->resize(outputs.size()); | |
| 80 | |
| 81 for (size_t i = 0; i < outputs.size(); ++i) { | |
| 82 bool internal = outputs[i].display->type() == OUTPUT_TYPE_INTERNAL; | |
| 83 bool on = | |
| 84 state == chromeos::DISPLAY_POWER_ALL_ON || | |
| 85 (state == chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON && | |
| 86 !internal) || | |
| 87 (state == chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && internal); | |
| 88 if (output_power) | |
| 89 (*output_power)[i] = on; | |
| 90 if (on) | |
| 91 num_on_outputs++; | |
| 92 } | |
| 93 return num_on_outputs; | |
| 94 } | |
| 95 | |
| 96 } // namespace | |
| 97 | |
| 98 OutputConfigurator::CoordinateTransformation::CoordinateTransformation() | |
| 99 : x_scale(1.0), | |
| 100 x_offset(0.0), | |
| 101 y_scale(1.0), | |
| 102 y_offset(0.0) {} | |
| 103 | |
| 104 OutputConfigurator::DisplayState::DisplayState() | |
| 105 : display(NULL), | |
| 106 touch_device_id(0), | |
| 107 selected_mode(NULL), | |
| 108 mirror_mode(NULL) {} | |
| 109 | |
| 110 bool OutputConfigurator::TestApi::TriggerConfigureTimeout() { | |
| 111 if (configurator_->configure_timer_.get() && | |
| 112 configurator_->configure_timer_->IsRunning()) { | |
| 113 configurator_->configure_timer_.reset(); | |
| 114 configurator_->ConfigureOutputs(); | |
| 115 return true; | |
| 116 } else { | |
| 117 return false; | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 // static | |
| 122 const DisplayMode* OutputConfigurator::FindDisplayModeMatchingSize( | |
| 123 const DisplaySnapshot& output, | |
| 124 const gfx::Size& size) { | |
| 125 const DisplayMode* best_mode = NULL; | |
| 126 for (DisplayModeList::const_iterator it = output.modes().begin(); | |
| 127 it != output.modes().end(); | |
| 128 ++it) { | |
| 129 const DisplayMode* mode = *it; | |
| 130 | |
| 131 if (mode->size() != size) | |
| 132 continue; | |
| 133 | |
| 134 if (!best_mode) { | |
| 135 best_mode = mode; | |
| 136 continue; | |
| 137 } | |
| 138 | |
| 139 if (mode->is_interlaced()) { | |
| 140 if (!best_mode->is_interlaced()) | |
| 141 continue; | |
| 142 } else { | |
| 143 // Reset the best rate if the non interlaced is | |
| 144 // found the first time. | |
| 145 if (best_mode->is_interlaced()) { | |
| 146 best_mode = mode; | |
| 147 continue; | |
| 148 } | |
| 149 } | |
| 150 if (mode->refresh_rate() < best_mode->refresh_rate()) | |
| 151 continue; | |
| 152 | |
| 153 best_mode = mode; | |
| 154 } | |
| 155 | |
| 156 return best_mode; | |
| 157 } | |
| 158 | |
| 159 OutputConfigurator::OutputConfigurator() | |
| 160 : state_controller_(NULL), | |
| 161 mirroring_controller_(NULL), | |
| 162 is_panel_fitting_enabled_(false), | |
| 163 configure_display_(base::SysInfo::IsRunningOnChromeOS()), | |
| 164 output_state_(OUTPUT_STATE_INVALID), | |
| 165 power_state_(chromeos::DISPLAY_POWER_ALL_ON), | |
| 166 next_output_protection_client_id_(1) {} | |
| 167 | |
| 168 OutputConfigurator::~OutputConfigurator() { | |
| 169 if (native_display_delegate_) | |
| 170 native_display_delegate_->RemoveObserver(this); | |
| 171 } | |
| 172 | |
| 173 void OutputConfigurator::SetNativeDisplayDelegateForTesting( | |
| 174 scoped_ptr<NativeDisplayDelegate> delegate) { | |
| 175 DCHECK(!native_display_delegate_); | |
| 176 | |
| 177 native_display_delegate_ = delegate.Pass(); | |
| 178 native_display_delegate_->AddObserver(this); | |
| 179 configure_display_ = true; | |
| 180 } | |
| 181 | |
| 182 void OutputConfigurator::SetTouchscreenDelegateForTesting( | |
| 183 scoped_ptr<TouchscreenDelegate> delegate) { | |
| 184 DCHECK(!touchscreen_delegate_); | |
| 185 | |
| 186 touchscreen_delegate_ = delegate.Pass(); | |
| 187 } | |
| 188 | |
| 189 void OutputConfigurator::SetInitialDisplayPower( | |
| 190 chromeos::DisplayPowerState power_state) { | |
| 191 DCHECK_EQ(output_state_, OUTPUT_STATE_INVALID); | |
| 192 power_state_ = power_state; | |
| 193 } | |
| 194 | |
| 195 void OutputConfigurator::Init(bool is_panel_fitting_enabled) { | |
| 196 is_panel_fitting_enabled_ = is_panel_fitting_enabled; | |
| 197 if (!configure_display_) | |
| 198 return; | |
| 199 | |
| 200 if (!native_display_delegate_) { | |
| 201 #if defined(USE_OZONE) | |
| 202 native_display_delegate_.reset(new NativeDisplayDelegateOzone()); | |
| 203 #elif defined(USE_X11) | |
| 204 native_display_delegate_.reset(new NativeDisplayDelegateX11()); | |
| 205 #else | |
| 206 NOTREACHED(); | |
| 207 #endif | |
| 208 native_display_delegate_->AddObserver(this); | |
| 209 } | |
| 210 | |
| 211 if (!touchscreen_delegate_) { | |
| 212 #if defined(USE_OZONE) | |
| 213 touchscreen_delegate_.reset(new TouchscreenDelegateOzone()); | |
| 214 #elif defined(USE_X11) | |
| 215 touchscreen_delegate_.reset(new TouchscreenDelegateX11()); | |
| 216 #else | |
| 217 NOTREACHED(); | |
| 218 #endif | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 void OutputConfigurator::ForceInitialConfigure(uint32_t background_color_argb) { | |
| 223 if (!configure_display_) | |
| 224 return; | |
| 225 | |
| 226 native_display_delegate_->GrabServer(); | |
| 227 native_display_delegate_->Initialize(); | |
| 228 | |
| 229 UpdateCachedOutputs(); | |
| 230 if (cached_outputs_.size() > 1 && background_color_argb) | |
| 231 native_display_delegate_->SetBackgroundColor(background_color_argb); | |
| 232 const OutputState new_state = ChooseOutputState(power_state_); | |
| 233 const bool success = | |
| 234 EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_); | |
| 235 | |
| 236 // Force the DPMS on chrome startup as the driver doesn't always detect | |
| 237 // that all displays are on when signing out. | |
| 238 native_display_delegate_->ForceDPMSOn(); | |
| 239 native_display_delegate_->UngrabServer(); | |
| 240 NotifyObservers(success, new_state); | |
| 241 } | |
| 242 | |
| 243 bool OutputConfigurator::ApplyProtections(const DisplayProtections& requests) { | |
| 244 for (DisplayStateList::const_iterator it = cached_outputs_.begin(); | |
| 245 it != cached_outputs_.end(); | |
| 246 ++it) { | |
| 247 uint32_t all_desired = 0; | |
| 248 DisplayProtections::const_iterator request_it = | |
| 249 requests.find(it->display->display_id()); | |
| 250 if (request_it != requests.end()) | |
| 251 all_desired = request_it->second; | |
| 252 switch (it->display->type()) { | |
| 253 case OUTPUT_TYPE_UNKNOWN: | |
| 254 return false; | |
| 255 // DisplayPort, DVI, and HDMI all support HDCP. | |
| 256 case OUTPUT_TYPE_DISPLAYPORT: | |
| 257 case OUTPUT_TYPE_DVI: | |
| 258 case OUTPUT_TYPE_HDMI: { | |
| 259 HDCPState new_desired_state = | |
| 260 (all_desired & OUTPUT_PROTECTION_METHOD_HDCP) ? | |
| 261 HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED; | |
| 262 if (!native_display_delegate_->SetHDCPState(*it->display, | |
| 263 new_desired_state)) | |
| 264 return false; | |
| 265 break; | |
| 266 } | |
| 267 case OUTPUT_TYPE_INTERNAL: | |
| 268 case OUTPUT_TYPE_VGA: | |
| 269 case OUTPUT_TYPE_NETWORK: | |
| 270 // No protections for these types. Do nothing. | |
| 271 break; | |
| 272 case OUTPUT_TYPE_NONE: | |
| 273 NOTREACHED(); | |
| 274 break; | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 return true; | |
| 279 } | |
| 280 | |
| 281 OutputConfigurator::OutputProtectionClientId | |
| 282 OutputConfigurator::RegisterOutputProtectionClient() { | |
| 283 if (!configure_display_) | |
| 284 return kInvalidClientId; | |
| 285 | |
| 286 return next_output_protection_client_id_++; | |
| 287 } | |
| 288 | |
| 289 void OutputConfigurator::UnregisterOutputProtectionClient( | |
| 290 OutputProtectionClientId client_id) { | |
| 291 client_protection_requests_.erase(client_id); | |
| 292 | |
| 293 DisplayProtections protections; | |
| 294 for (ProtectionRequests::const_iterator it = | |
| 295 client_protection_requests_.begin(); | |
| 296 it != client_protection_requests_.end(); | |
| 297 ++it) { | |
| 298 for (DisplayProtections::const_iterator it2 = it->second.begin(); | |
| 299 it2 != it->second.end(); | |
| 300 ++it2) { | |
| 301 protections[it2->first] |= it2->second; | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 ApplyProtections(protections); | |
| 306 } | |
| 307 | |
| 308 bool OutputConfigurator::QueryOutputProtectionStatus( | |
| 309 OutputProtectionClientId client_id, | |
| 310 int64_t display_id, | |
| 311 uint32_t* link_mask, | |
| 312 uint32_t* protection_mask) { | |
| 313 if (!configure_display_) | |
| 314 return false; | |
| 315 | |
| 316 uint32_t enabled = 0; | |
| 317 uint32_t unfulfilled = 0; | |
| 318 *link_mask = 0; | |
| 319 for (DisplayStateList::const_iterator it = cached_outputs_.begin(); | |
| 320 it != cached_outputs_.end(); | |
| 321 ++it) { | |
| 322 if (it->display->display_id() != display_id) | |
| 323 continue; | |
| 324 *link_mask |= it->display->type(); | |
| 325 switch (it->display->type()) { | |
| 326 case OUTPUT_TYPE_UNKNOWN: | |
| 327 return false; | |
| 328 // DisplayPort, DVI, and HDMI all support HDCP. | |
| 329 case OUTPUT_TYPE_DISPLAYPORT: | |
| 330 case OUTPUT_TYPE_DVI: | |
| 331 case OUTPUT_TYPE_HDMI: { | |
| 332 HDCPState state; | |
| 333 if (!native_display_delegate_->GetHDCPState(*it->display, &state)) | |
| 334 return false; | |
| 335 if (state == HDCP_STATE_ENABLED) | |
| 336 enabled |= OUTPUT_PROTECTION_METHOD_HDCP; | |
| 337 else | |
| 338 unfulfilled |= OUTPUT_PROTECTION_METHOD_HDCP; | |
| 339 break; | |
| 340 } | |
| 341 case OUTPUT_TYPE_INTERNAL: | |
| 342 case OUTPUT_TYPE_VGA: | |
| 343 case OUTPUT_TYPE_NETWORK: | |
| 344 // No protections for these types. Do nothing. | |
| 345 break; | |
| 346 case OUTPUT_TYPE_NONE: | |
| 347 NOTREACHED(); | |
| 348 break; | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 // Don't reveal protections requested by other clients. | |
| 353 ProtectionRequests::iterator it = client_protection_requests_.find(client_id); | |
| 354 if (it != client_protection_requests_.end()) { | |
| 355 uint32_t requested_mask = 0; | |
| 356 if (it->second.find(display_id) != it->second.end()) | |
| 357 requested_mask = it->second[display_id]; | |
| 358 *protection_mask = enabled & ~unfulfilled & requested_mask; | |
| 359 } else { | |
| 360 *protection_mask = 0; | |
| 361 } | |
| 362 return true; | |
| 363 } | |
| 364 | |
| 365 bool OutputConfigurator::EnableOutputProtection( | |
| 366 OutputProtectionClientId client_id, | |
| 367 int64_t display_id, | |
| 368 uint32_t desired_method_mask) { | |
| 369 if (!configure_display_) | |
| 370 return false; | |
| 371 | |
| 372 DisplayProtections protections; | |
| 373 for (ProtectionRequests::const_iterator it = | |
| 374 client_protection_requests_.begin(); | |
| 375 it != client_protection_requests_.end(); | |
| 376 ++it) { | |
| 377 for (DisplayProtections::const_iterator it2 = it->second.begin(); | |
| 378 it2 != it->second.end(); | |
| 379 ++it2) { | |
| 380 if (it->first == client_id && it2->first == display_id) | |
| 381 continue; | |
| 382 protections[it2->first] |= it2->second; | |
| 383 } | |
| 384 } | |
| 385 protections[display_id] |= desired_method_mask; | |
| 386 | |
| 387 if (!ApplyProtections(protections)) | |
| 388 return false; | |
| 389 | |
| 390 if (desired_method_mask == OUTPUT_PROTECTION_METHOD_NONE) { | |
| 391 if (client_protection_requests_.find(client_id) != | |
| 392 client_protection_requests_.end()) { | |
| 393 client_protection_requests_[client_id].erase(display_id); | |
| 394 if (client_protection_requests_[client_id].size() == 0) | |
| 395 client_protection_requests_.erase(client_id); | |
| 396 } | |
| 397 } else { | |
| 398 client_protection_requests_[client_id][display_id] = desired_method_mask; | |
| 399 } | |
| 400 | |
| 401 return true; | |
| 402 } | |
| 403 | |
| 404 std::vector<ui::ColorCalibrationProfile> | |
| 405 OutputConfigurator::GetAvailableColorCalibrationProfiles( | |
| 406 int64_t display_id) { | |
| 407 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 408 switches::kDisableDisplayColorCalibration)) { | |
| 409 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 410 if (cached_outputs_[i].display && | |
| 411 cached_outputs_[i].display->display_id() == display_id) { | |
| 412 return native_display_delegate_->GetAvailableColorCalibrationProfiles( | |
| 413 *cached_outputs_[i].display); | |
| 414 } | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 return std::vector<ui::ColorCalibrationProfile>(); | |
| 419 } | |
| 420 | |
| 421 bool OutputConfigurator::SetColorCalibrationProfile( | |
| 422 int64_t display_id, | |
| 423 ui::ColorCalibrationProfile new_profile) { | |
| 424 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 425 if (cached_outputs_[i].display && | |
| 426 cached_outputs_[i].display->display_id() == display_id) { | |
| 427 return native_display_delegate_->SetColorCalibrationProfile( | |
| 428 *cached_outputs_[i].display, new_profile); | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 return false; | |
| 433 } | |
| 434 | |
| 435 void OutputConfigurator::PrepareForExit() { | |
| 436 configure_display_ = false; | |
| 437 } | |
| 438 | |
| 439 bool OutputConfigurator::SetDisplayPower( | |
| 440 chromeos::DisplayPowerState power_state, | |
| 441 int flags) { | |
| 442 if (!configure_display_) | |
| 443 return false; | |
| 444 | |
| 445 VLOG(1) << "SetDisplayPower: power_state=" | |
| 446 << DisplayPowerStateToString(power_state) << " flags=" << flags | |
| 447 << ", configure timer=" | |
| 448 << ((configure_timer_.get() && configure_timer_->IsRunning()) ? | |
| 449 "Running" : "Stopped"); | |
| 450 if (power_state == power_state_ && !(flags & kSetDisplayPowerForceProbe)) | |
| 451 return true; | |
| 452 | |
| 453 native_display_delegate_->GrabServer(); | |
| 454 UpdateCachedOutputs(); | |
| 455 | |
| 456 const OutputState new_state = ChooseOutputState(power_state); | |
| 457 bool attempted_change = false; | |
| 458 bool success = false; | |
| 459 | |
| 460 bool only_if_single_internal_display = | |
| 461 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay; | |
| 462 bool single_internal_display = | |
| 463 cached_outputs_.size() == 1 && | |
| 464 cached_outputs_[0].display->type() == OUTPUT_TYPE_INTERNAL; | |
| 465 if (single_internal_display || !only_if_single_internal_display) { | |
| 466 success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state); | |
| 467 attempted_change = true; | |
| 468 | |
| 469 // Force the DPMS on since the driver doesn't always detect that it | |
| 470 // should turn on. This is needed when coming back from idle suspend. | |
| 471 if (success && power_state != chromeos::DISPLAY_POWER_ALL_OFF) | |
| 472 native_display_delegate_->ForceDPMSOn(); | |
| 473 } | |
| 474 | |
| 475 native_display_delegate_->UngrabServer(); | |
| 476 if (attempted_change) | |
| 477 NotifyObservers(success, new_state); | |
| 478 return true; | |
| 479 } | |
| 480 | |
| 481 bool OutputConfigurator::SetDisplayMode(OutputState new_state) { | |
| 482 if (!configure_display_) | |
| 483 return false; | |
| 484 | |
| 485 VLOG(1) << "SetDisplayMode: state=" << OutputStateToString(new_state); | |
| 486 if (output_state_ == new_state) { | |
| 487 // Cancel software mirroring if the state is moving from | |
| 488 // OUTPUT_STATE_DUAL_EXTENDED to OUTPUT_STATE_DUAL_EXTENDED. | |
| 489 if (mirroring_controller_ && new_state == OUTPUT_STATE_DUAL_EXTENDED) | |
| 490 mirroring_controller_->SetSoftwareMirroring(false); | |
| 491 NotifyObservers(true, new_state); | |
| 492 return true; | |
| 493 } | |
| 494 | |
| 495 native_display_delegate_->GrabServer(); | |
| 496 UpdateCachedOutputs(); | |
| 497 const bool success = | |
| 498 EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_); | |
| 499 native_display_delegate_->UngrabServer(); | |
| 500 | |
| 501 NotifyObservers(success, new_state); | |
| 502 return success; | |
| 503 } | |
| 504 | |
| 505 void OutputConfigurator::OnConfigurationChanged() { | |
| 506 // Configure outputs with |kConfigureDelayMs| delay, | |
| 507 // so that time-consuming ConfigureOutputs() won't be called multiple times. | |
| 508 if (configure_timer_.get()) { | |
| 509 configure_timer_->Reset(); | |
| 510 } else { | |
| 511 configure_timer_.reset(new base::OneShotTimer<OutputConfigurator>()); | |
| 512 configure_timer_->Start( | |
| 513 FROM_HERE, | |
| 514 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), | |
| 515 this, | |
| 516 &OutputConfigurator::ConfigureOutputs); | |
| 517 } | |
| 518 } | |
| 519 | |
| 520 void OutputConfigurator::AddObserver(Observer* observer) { | |
| 521 observers_.AddObserver(observer); | |
| 522 } | |
| 523 | |
| 524 void OutputConfigurator::RemoveObserver(Observer* observer) { | |
| 525 observers_.RemoveObserver(observer); | |
| 526 } | |
| 527 | |
| 528 void OutputConfigurator::SuspendDisplays() { | |
| 529 // If the display is off due to user inactivity and there's only a single | |
| 530 // internal display connected, switch to the all-on state before | |
| 531 // suspending. This shouldn't be very noticeable to the user since the | |
| 532 // backlight is off at this point, and doing this lets us resume directly | |
| 533 // into the "on" state, which greatly reduces resume times. | |
| 534 if (power_state_ == chromeos::DISPLAY_POWER_ALL_OFF) { | |
| 535 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | |
| 536 kSetDisplayPowerOnlyIfSingleInternalDisplay); | |
| 537 | |
| 538 // We need to make sure that the monitor configuration we just did actually | |
| 539 // completes before we return, because otherwise the X message could be | |
| 540 // racing with the HandleSuspendReadiness message. | |
| 541 native_display_delegate_->SyncWithServer(); | |
| 542 } | |
| 543 } | |
| 544 | |
| 545 void OutputConfigurator::ResumeDisplays() { | |
| 546 // Force probing to ensure that we pick up any changes that were made | |
| 547 // while the system was suspended. | |
| 548 SetDisplayPower(power_state_, kSetDisplayPowerForceProbe); | |
| 549 } | |
| 550 | |
| 551 void OutputConfigurator::UpdateCachedOutputs() { | |
| 552 std::vector<DisplaySnapshot*> snapshots = | |
| 553 native_display_delegate_->GetOutputs(); | |
| 554 | |
| 555 cached_outputs_.clear(); | |
| 556 for (size_t i = 0; i < snapshots.size(); ++i) { | |
| 557 DisplayState display_state; | |
| 558 display_state.display = snapshots[i]; | |
| 559 cached_outputs_.push_back(display_state); | |
| 560 } | |
| 561 | |
| 562 touchscreen_delegate_->AssociateTouchscreens(&cached_outputs_); | |
| 563 | |
| 564 // Set |selected_mode| fields. | |
| 565 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 566 DisplayState* output = &cached_outputs_[i]; | |
| 567 if (output->display->has_proper_display_id()) { | |
| 568 gfx::Size size; | |
| 569 if (state_controller_ && state_controller_->GetResolutionForDisplayId( | |
| 570 output->display->display_id(), &size)) { | |
| 571 output->selected_mode = | |
| 572 FindDisplayModeMatchingSize(*output->display, size); | |
| 573 } | |
| 574 } | |
| 575 // Fall back to native mode. | |
| 576 if (!output->selected_mode) | |
| 577 output->selected_mode = output->display->native_mode(); | |
| 578 } | |
| 579 | |
| 580 // Set |mirror_mode| fields. | |
| 581 if (cached_outputs_.size() == 2) { | |
| 582 bool one_is_internal = | |
| 583 cached_outputs_[0].display->type() == OUTPUT_TYPE_INTERNAL; | |
| 584 bool two_is_internal = | |
| 585 cached_outputs_[1].display->type() == OUTPUT_TYPE_INTERNAL; | |
| 586 int internal_outputs = | |
| 587 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0); | |
| 588 DCHECK_LT(internal_outputs, 2); | |
| 589 LOG_IF(WARNING, internal_outputs == 2) << "Two internal outputs detected."; | |
| 590 | |
| 591 bool can_mirror = false; | |
| 592 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { | |
| 593 // Try preserving external output's aspect ratio on the first attempt. | |
| 594 // If that fails, fall back to the highest matching resolution. | |
| 595 bool preserve_aspect = attempt == 0; | |
| 596 | |
| 597 if (internal_outputs == 1) { | |
| 598 if (one_is_internal) { | |
| 599 can_mirror = FindMirrorMode(&cached_outputs_[0], | |
| 600 &cached_outputs_[1], | |
| 601 is_panel_fitting_enabled_, | |
| 602 preserve_aspect); | |
| 603 } else { | |
| 604 DCHECK(two_is_internal); | |
| 605 can_mirror = FindMirrorMode(&cached_outputs_[1], | |
| 606 &cached_outputs_[0], | |
| 607 is_panel_fitting_enabled_, | |
| 608 preserve_aspect); | |
| 609 } | |
| 610 } else { // if (internal_outputs == 0) | |
| 611 // No panel fitting for external outputs, so fall back to exact match. | |
| 612 can_mirror = FindMirrorMode( | |
| 613 &cached_outputs_[0], &cached_outputs_[1], false, preserve_aspect); | |
| 614 if (!can_mirror && preserve_aspect) { | |
| 615 // FindMirrorMode() will try to preserve aspect ratio of what it | |
| 616 // thinks is external display, so if it didn't succeed with one, maybe | |
| 617 // it will succeed with the other. This way we will have the correct | |
| 618 // aspect ratio on at least one of them. | |
| 619 can_mirror = FindMirrorMode( | |
| 620 &cached_outputs_[1], &cached_outputs_[0], false, preserve_aspect); | |
| 621 } | |
| 622 } | |
| 623 } | |
| 624 } | |
| 625 } | |
| 626 | |
| 627 bool OutputConfigurator::FindMirrorMode(DisplayState* internal_output, | |
| 628 DisplayState* external_output, | |
| 629 bool try_panel_fitting, | |
| 630 bool preserve_aspect) { | |
| 631 const DisplayMode* internal_native_info = | |
| 632 internal_output->display->native_mode(); | |
| 633 const DisplayMode* external_native_info = | |
| 634 external_output->display->native_mode(); | |
| 635 if (!internal_native_info || !external_native_info) | |
| 636 return false; | |
| 637 | |
| 638 // Check if some external output resolution can be mirrored on internal. | |
| 639 // Prefer the modes in the order that X sorts them, assuming this is the order | |
| 640 // in which they look better on the monitor. | |
| 641 for (DisplayModeList::const_iterator external_it = | |
| 642 external_output->display->modes().begin(); | |
| 643 external_it != external_output->display->modes().end(); | |
| 644 ++external_it) { | |
| 645 const DisplayMode& external_info = **external_it; | |
| 646 bool is_native_aspect_ratio = | |
| 647 external_native_info->size().width() * external_info.size().height() == | |
| 648 external_native_info->size().height() * external_info.size().width(); | |
| 649 if (preserve_aspect && !is_native_aspect_ratio) | |
| 650 continue; // Allow only aspect ratio preserving modes for mirroring. | |
| 651 | |
| 652 // Try finding an exact match. | |
| 653 for (DisplayModeList::const_iterator internal_it = | |
| 654 internal_output->display->modes().begin(); | |
| 655 internal_it != internal_output->display->modes().end(); | |
| 656 ++internal_it) { | |
| 657 const DisplayMode& internal_info = **internal_it; | |
| 658 if (internal_info.size().width() == external_info.size().width() && | |
| 659 internal_info.size().height() == external_info.size().height() && | |
| 660 internal_info.is_interlaced() == external_info.is_interlaced()) { | |
| 661 internal_output->mirror_mode = *internal_it; | |
| 662 external_output->mirror_mode = *external_it; | |
| 663 return true; // Mirror mode found. | |
| 664 } | |
| 665 } | |
| 666 | |
| 667 // Try to create a matching internal output mode by panel fitting. | |
| 668 if (try_panel_fitting) { | |
| 669 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks | |
| 670 // ugly, so, can fit == can upscale. Also, internal panels don't support | |
| 671 // fitting interlaced modes. | |
| 672 bool can_fit = internal_native_info->size().width() >= | |
| 673 external_info.size().width() && | |
| 674 internal_native_info->size().height() >= | |
| 675 external_info.size().height() && | |
| 676 !external_info.is_interlaced(); | |
| 677 if (can_fit) { | |
| 678 native_display_delegate_->AddMode(*internal_output->display, | |
| 679 *external_it); | |
| 680 internal_output->display->add_mode(*external_it); | |
| 681 internal_output->mirror_mode = *external_it; | |
| 682 external_output->mirror_mode = *external_it; | |
| 683 return true; // Mirror mode created. | |
| 684 } | |
| 685 } | |
| 686 } | |
| 687 | |
| 688 return false; | |
| 689 } | |
| 690 | |
| 691 void OutputConfigurator::ConfigureOutputs() { | |
| 692 configure_timer_.reset(); | |
| 693 | |
| 694 if (!configure_display_) | |
| 695 return; | |
| 696 | |
| 697 native_display_delegate_->GrabServer(); | |
| 698 UpdateCachedOutputs(); | |
| 699 const OutputState new_state = ChooseOutputState(power_state_); | |
| 700 const bool success = | |
| 701 EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_); | |
| 702 native_display_delegate_->UngrabServer(); | |
| 703 | |
| 704 NotifyObservers(success, new_state); | |
| 705 } | |
| 706 | |
| 707 void OutputConfigurator::NotifyObservers(bool success, | |
| 708 OutputState attempted_state) { | |
| 709 if (success) { | |
| 710 FOR_EACH_OBSERVER( | |
| 711 Observer, observers_, OnDisplayModeChanged(cached_outputs_)); | |
| 712 } else { | |
| 713 FOR_EACH_OBSERVER( | |
| 714 Observer, observers_, OnDisplayModeChangeFailed(attempted_state)); | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 bool OutputConfigurator::EnterStateOrFallBackToSoftwareMirroring( | |
| 719 OutputState output_state, | |
| 720 chromeos::DisplayPowerState power_state) { | |
| 721 bool success = EnterState(output_state, power_state); | |
| 722 if (mirroring_controller_) { | |
| 723 bool enable_software_mirroring = false; | |
| 724 if (!success && output_state == OUTPUT_STATE_DUAL_MIRROR) { | |
| 725 if (output_state_ != OUTPUT_STATE_DUAL_EXTENDED || | |
| 726 power_state_ != power_state) | |
| 727 EnterState(OUTPUT_STATE_DUAL_EXTENDED, power_state); | |
| 728 enable_software_mirroring = success = | |
| 729 output_state_ == OUTPUT_STATE_DUAL_EXTENDED; | |
| 730 } | |
| 731 mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring); | |
| 732 } | |
| 733 return success; | |
| 734 } | |
| 735 | |
| 736 bool OutputConfigurator::EnterState(OutputState output_state, | |
| 737 chromeos::DisplayPowerState power_state) { | |
| 738 std::vector<bool> output_power; | |
| 739 int num_on_outputs = | |
| 740 GetOutputPower(cached_outputs_, power_state, &output_power); | |
| 741 VLOG(1) << "EnterState: output=" << OutputStateToString(output_state) | |
| 742 << " power=" << DisplayPowerStateToString(power_state); | |
| 743 | |
| 744 // Framebuffer dimensions. | |
| 745 gfx::Size size; | |
| 746 | |
| 747 std::vector<gfx::Point> new_origins(cached_outputs_.size(), gfx::Point()); | |
| 748 std::vector<const DisplayMode*> new_mode; | |
| 749 for (size_t i = 0; i < cached_outputs_.size(); ++i) | |
| 750 new_mode.push_back(cached_outputs_[i].display->current_mode()); | |
| 751 | |
| 752 switch (output_state) { | |
| 753 case OUTPUT_STATE_INVALID: | |
| 754 NOTREACHED() << "Ignoring request to enter invalid state with " | |
| 755 << cached_outputs_.size() << " connected output(s)"; | |
| 756 return false; | |
| 757 case OUTPUT_STATE_HEADLESS: | |
| 758 if (cached_outputs_.size() != 0) { | |
| 759 LOG(WARNING) << "Ignoring request to enter headless mode with " | |
| 760 << cached_outputs_.size() << " connected output(s)"; | |
| 761 return false; | |
| 762 } | |
| 763 break; | |
| 764 case OUTPUT_STATE_SINGLE: { | |
| 765 // If there are multiple outputs connected, only one should be turned on. | |
| 766 if (cached_outputs_.size() != 1 && num_on_outputs != 1) { | |
| 767 LOG(WARNING) << "Ignoring request to enter single mode with " | |
| 768 << cached_outputs_.size() << " connected outputs and " | |
| 769 << num_on_outputs << " turned on"; | |
| 770 return false; | |
| 771 } | |
| 772 | |
| 773 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 774 DisplayState* output = &cached_outputs_[i]; | |
| 775 new_mode[i] = output_power[i] ? output->selected_mode : NULL; | |
| 776 | |
| 777 if (output_power[i] || cached_outputs_.size() == 1) { | |
| 778 const DisplayMode* mode_info = output->selected_mode; | |
| 779 if (!mode_info) | |
| 780 return false; | |
| 781 if (mode_info->size() == gfx::Size(1024, 768)) { | |
| 782 VLOG(1) << "Potentially misdetecting display(1024x768):" | |
| 783 << " outputs size=" << cached_outputs_.size() | |
| 784 << ", num_on_outputs=" << num_on_outputs | |
| 785 << ", current size:" << size.width() << "x" << size.height() | |
| 786 << ", i=" << i << ", output=" << output->display->ToString() | |
| 787 << ", display_mode=" << mode_info->ToString(); | |
| 788 } | |
| 789 size = mode_info->size(); | |
| 790 } | |
| 791 } | |
| 792 break; | |
| 793 } | |
| 794 case OUTPUT_STATE_DUAL_MIRROR: { | |
| 795 if (cached_outputs_.size() != 2 || | |
| 796 (num_on_outputs != 0 && num_on_outputs != 2)) { | |
| 797 LOG(WARNING) << "Ignoring request to enter mirrored mode with " | |
| 798 << cached_outputs_.size() << " connected output(s) and " | |
| 799 << num_on_outputs << " turned on"; | |
| 800 return false; | |
| 801 } | |
| 802 | |
| 803 const DisplayMode* mode_info = cached_outputs_[0].mirror_mode; | |
| 804 if (!mode_info) | |
| 805 return false; | |
| 806 size = mode_info->size(); | |
| 807 | |
| 808 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 809 DisplayState* output = &cached_outputs_[i]; | |
| 810 new_mode[i] = output_power[i] ? output->mirror_mode : NULL; | |
| 811 if (output->touch_device_id) { | |
| 812 // CTM needs to be calculated if aspect preserving scaling is used. | |
| 813 // Otherwise, assume it is full screen, and use identity CTM. | |
| 814 if (output->mirror_mode != output->display->native_mode() && | |
| 815 output->display->is_aspect_preserving_scaling()) { | |
| 816 output->transform = GetMirrorModeCTM(*output); | |
| 817 mirrored_display_area_ratio_map_[output->touch_device_id] = | |
| 818 GetMirroredDisplayAreaRatio(*output); | |
| 819 } | |
| 820 } | |
| 821 } | |
| 822 break; | |
| 823 } | |
| 824 case OUTPUT_STATE_DUAL_EXTENDED: { | |
| 825 if (cached_outputs_.size() != 2 || | |
| 826 (num_on_outputs != 0 && num_on_outputs != 2)) { | |
| 827 LOG(WARNING) << "Ignoring request to enter extended mode with " | |
| 828 << cached_outputs_.size() << " connected output(s) and " | |
| 829 << num_on_outputs << " turned on"; | |
| 830 return false; | |
| 831 } | |
| 832 | |
| 833 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 834 DisplayState* output = &cached_outputs_[i]; | |
| 835 new_origins[i].set_y(size.height() ? size.height() + kVerticalGap : 0); | |
| 836 new_mode[i] = output_power[i] ? output->selected_mode : NULL; | |
| 837 | |
| 838 // Retain the full screen size even if all outputs are off so the | |
| 839 // same desktop configuration can be restored when the outputs are | |
| 840 // turned back on. | |
| 841 const DisplayMode* mode_info = cached_outputs_[i].selected_mode; | |
| 842 if (!mode_info) | |
| 843 return false; | |
| 844 | |
| 845 size.set_width(std::max<int>(size.width(), mode_info->size().width())); | |
| 846 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) + | |
| 847 mode_info->size().height()); | |
| 848 } | |
| 849 | |
| 850 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 851 DisplayState* output = &cached_outputs_[i]; | |
| 852 if (output->touch_device_id) | |
| 853 output->transform = GetExtendedModeCTM(*output, new_origins[i], size); | |
| 854 } | |
| 855 break; | |
| 856 } | |
| 857 } | |
| 858 | |
| 859 // Finally, apply the desired changes. | |
| 860 bool all_succeeded = true; | |
| 861 if (!cached_outputs_.empty()) { | |
| 862 native_display_delegate_->CreateFrameBuffer(size); | |
| 863 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 864 const DisplayState& output = cached_outputs_[i]; | |
| 865 bool configure_succeeded = false; | |
| 866 | |
| 867 while (true) { | |
| 868 if (native_display_delegate_->Configure( | |
| 869 *output.display, new_mode[i], new_origins[i])) { | |
| 870 output.display->set_current_mode(new_mode[i]); | |
| 871 output.display->set_origin(new_origins[i]); | |
| 872 | |
| 873 configure_succeeded = true; | |
| 874 break; | |
| 875 } | |
| 876 | |
| 877 const DisplayMode* mode_info = new_mode[i]; | |
| 878 if (!mode_info) | |
| 879 break; | |
| 880 | |
| 881 // Find the mode with the next-best resolution and see if that can | |
| 882 // be set. | |
| 883 int best_mode_pixels = 0; | |
| 884 | |
| 885 int current_mode_pixels = mode_info->size().GetArea(); | |
| 886 for (DisplayModeList::const_iterator it = | |
| 887 output.display->modes().begin(); | |
| 888 it != output.display->modes().end(); | |
| 889 it++) { | |
| 890 int pixel_count = (*it)->size().GetArea(); | |
| 891 if ((pixel_count < current_mode_pixels) && | |
| 892 (pixel_count > best_mode_pixels)) { | |
| 893 new_mode[i] = *it; | |
| 894 best_mode_pixels = pixel_count; | |
| 895 } | |
| 896 } | |
| 897 | |
| 898 if (best_mode_pixels == 0) | |
| 899 break; | |
| 900 } | |
| 901 | |
| 902 if (configure_succeeded) { | |
| 903 if (output.touch_device_id) | |
| 904 touchscreen_delegate_->ConfigureCTM(output.touch_device_id, | |
| 905 output.transform); | |
| 906 } else { | |
| 907 all_succeeded = false; | |
| 908 } | |
| 909 | |
| 910 // If we are trying to set mirror mode and one of the modesets fails, | |
| 911 // then the two monitors will be mis-matched. In this case, return | |
| 912 // false to let the observers be aware. | |
| 913 if (output_state == OUTPUT_STATE_DUAL_MIRROR && output_power[i] && | |
| 914 output.display->current_mode() != output.mirror_mode) | |
| 915 all_succeeded = false; | |
| 916 } | |
| 917 } | |
| 918 | |
| 919 if (all_succeeded) { | |
| 920 output_state_ = output_state; | |
| 921 power_state_ = power_state; | |
| 922 } | |
| 923 return all_succeeded; | |
| 924 } | |
| 925 | |
| 926 OutputState OutputConfigurator::ChooseOutputState( | |
| 927 chromeos::DisplayPowerState power_state) const { | |
| 928 int num_on_outputs = GetOutputPower(cached_outputs_, power_state, NULL); | |
| 929 switch (cached_outputs_.size()) { | |
| 930 case 0: | |
| 931 return OUTPUT_STATE_HEADLESS; | |
| 932 case 1: | |
| 933 return OUTPUT_STATE_SINGLE; | |
| 934 case 2: { | |
| 935 if (num_on_outputs == 1) { | |
| 936 // If only one output is currently turned on, return the "single" | |
| 937 // state so that its native mode will be used. | |
| 938 return OUTPUT_STATE_SINGLE; | |
| 939 } else { | |
| 940 if (!state_controller_) | |
| 941 return OUTPUT_STATE_DUAL_EXTENDED; | |
| 942 // With either both outputs on or both outputs off, use one of the | |
| 943 // dual modes. | |
| 944 std::vector<int64_t> display_ids; | |
| 945 for (size_t i = 0; i < cached_outputs_.size(); ++i) { | |
| 946 // If display id isn't available, switch to extended mode. | |
| 947 if (!cached_outputs_[i].display->has_proper_display_id()) | |
| 948 return OUTPUT_STATE_DUAL_EXTENDED; | |
| 949 display_ids.push_back(cached_outputs_[i].display->display_id()); | |
| 950 } | |
| 951 return state_controller_->GetStateForDisplayIds(display_ids); | |
| 952 } | |
| 953 } | |
| 954 default: | |
| 955 NOTREACHED(); | |
| 956 } | |
| 957 return OUTPUT_STATE_INVALID; | |
| 958 } | |
| 959 | |
| 960 OutputConfigurator::CoordinateTransformation | |
| 961 OutputConfigurator::GetMirrorModeCTM(const DisplayState& output) { | |
| 962 CoordinateTransformation ctm; // Default to identity | |
| 963 const DisplayMode* native_mode_info = output.display->native_mode(); | |
| 964 const DisplayMode* mirror_mode_info = output.mirror_mode; | |
| 965 | |
| 966 if (!native_mode_info || !mirror_mode_info || | |
| 967 native_mode_info->size().height() == 0 || | |
| 968 mirror_mode_info->size().height() == 0 || | |
| 969 native_mode_info->size().width() == 0 || | |
| 970 mirror_mode_info->size().width() == 0) | |
| 971 return ctm; | |
| 972 | |
| 973 float native_mode_ar = static_cast<float>(native_mode_info->size().width()) / | |
| 974 static_cast<float>(native_mode_info->size().height()); | |
| 975 float mirror_mode_ar = static_cast<float>(mirror_mode_info->size().width()) / | |
| 976 static_cast<float>(mirror_mode_info->size().height()); | |
| 977 | |
| 978 if (mirror_mode_ar > native_mode_ar) { // Letterboxing | |
| 979 ctm.x_scale = 1.0; | |
| 980 ctm.x_offset = 0.0; | |
| 981 ctm.y_scale = mirror_mode_ar / native_mode_ar; | |
| 982 ctm.y_offset = (native_mode_ar / mirror_mode_ar - 1.0) * 0.5; | |
| 983 return ctm; | |
| 984 } | |
| 985 if (native_mode_ar > mirror_mode_ar) { // Pillarboxing | |
| 986 ctm.y_scale = 1.0; | |
| 987 ctm.y_offset = 0.0; | |
| 988 ctm.x_scale = native_mode_ar / mirror_mode_ar; | |
| 989 ctm.x_offset = (mirror_mode_ar / native_mode_ar - 1.0) * 0.5; | |
| 990 return ctm; | |
| 991 } | |
| 992 | |
| 993 return ctm; // Same aspect ratio - return identity | |
| 994 } | |
| 995 | |
| 996 OutputConfigurator::CoordinateTransformation | |
| 997 OutputConfigurator::GetExtendedModeCTM(const DisplayState& output, | |
| 998 const gfx::Point& new_origin, | |
| 999 const gfx::Size& framebuffer_size) { | |
| 1000 CoordinateTransformation ctm; // Default to identity | |
| 1001 const DisplayMode* mode_info = output.selected_mode; | |
| 1002 DCHECK(mode_info); | |
| 1003 if (!mode_info) | |
| 1004 return ctm; | |
| 1005 // An example of how to calculate the CTM. | |
| 1006 // Suppose we have 2 monitors, the first one has size 1366 x 768. | |
| 1007 // The second one has size 2560 x 1600 | |
| 1008 // The total size of framebuffer is 2560 x 2428 | |
| 1009 // where 2428 = 768 + 60 (hidden gap) + 1600 | |
| 1010 // and the sceond monitor is translated to Point (0, 828) in the | |
| 1011 // framebuffer. | |
| 1012 // X will first map input event location to [0, 2560) x [0, 2428), | |
| 1013 // then apply CTM on it. | |
| 1014 // So to compute CTM, for monitor1, we have | |
| 1015 // x_scale = (1366 - 1) / (2560 - 1) | |
| 1016 // x_offset = 0 / (2560 - 1) | |
| 1017 // y_scale = (768 - 1) / (2428 - 1) | |
| 1018 // y_offset = 0 / (2428 -1) | |
| 1019 // For Monitor 2, we have | |
| 1020 // x_scale = (2560 - 1) / (2560 - 1) | |
| 1021 // x_offset = 0 / (2560 - 1) | |
| 1022 // y_scale = (1600 - 1) / (2428 - 1) | |
| 1023 // y_offset = 828 / (2428 -1) | |
| 1024 // See the unittest OutputConfiguratorTest.CTMForMultiScreens. | |
| 1025 ctm.x_scale = static_cast<float>(mode_info->size().width() - 1) / | |
| 1026 (framebuffer_size.width() - 1); | |
| 1027 ctm.x_offset = | |
| 1028 static_cast<float>(new_origin.x()) / (framebuffer_size.width() - 1); | |
| 1029 ctm.y_scale = static_cast<float>(mode_info->size().height() - 1) / | |
| 1030 (framebuffer_size.height() - 1); | |
| 1031 ctm.y_offset = | |
| 1032 static_cast<float>(new_origin.y()) / (framebuffer_size.height() - 1); | |
| 1033 return ctm; | |
| 1034 } | |
| 1035 | |
| 1036 float OutputConfigurator::GetMirroredDisplayAreaRatio( | |
| 1037 const DisplayState& output) { | |
| 1038 float area_ratio = 1.0f; | |
| 1039 const DisplayMode* native_mode_info = output.display->native_mode(); | |
| 1040 const DisplayMode* mirror_mode_info = output.mirror_mode; | |
| 1041 | |
| 1042 if (!native_mode_info || !mirror_mode_info || | |
| 1043 native_mode_info->size().height() == 0 || | |
| 1044 mirror_mode_info->size().height() == 0 || | |
| 1045 native_mode_info->size().width() == 0 || | |
| 1046 mirror_mode_info->size().width() == 0) | |
| 1047 return area_ratio; | |
| 1048 | |
| 1049 float width_ratio = static_cast<float>(mirror_mode_info->size().width()) / | |
| 1050 static_cast<float>(native_mode_info->size().width()); | |
| 1051 float height_ratio = static_cast<float>(mirror_mode_info->size().height()) / | |
| 1052 static_cast<float>(native_mode_info->size().height()); | |
| 1053 | |
| 1054 area_ratio = width_ratio * height_ratio; | |
| 1055 return area_ratio; | |
| 1056 } | |
| 1057 | |
| 1058 } // namespace ui | |
| OLD | NEW |