MacViews: Get c/b/ui/views/tabs to build on Mac
[chromium-blink-merge.git] / ui / display / chromeos / display_configurator.cc
blob8d0a24106f6566b4eb493444d67e37ea3246b19d
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.
5 #include "ui/display/chromeos/display_configurator.h"
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/display_switches.h"
15 #include "ui/display/types/display_mode.h"
16 #include "ui/display/types/display_snapshot.h"
17 #include "ui/display/types/native_display_delegate.h"
19 namespace ui {
21 namespace {
23 typedef std::vector<const DisplayMode*> DisplayModeList;
25 // The delay to perform configuration after RRNotify. See the comment for
26 // |configure_timer_|.
27 const int kConfigureDelayMs = 500;
29 // The delay spent before reading the display configuration after coming out of
30 // suspend. While coming out of suspend the display state may be updating. This
31 // is used to wait until the hardware had a chance to update the display state
32 // such that we read an up to date state.
33 const int kResumeDelayMs = 500;
35 // Returns a string describing |state|.
36 std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) {
37 switch (state) {
38 case chromeos::DISPLAY_POWER_ALL_ON:
39 return "ALL_ON";
40 case chromeos::DISPLAY_POWER_ALL_OFF:
41 return "ALL_OFF";
42 case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON:
43 return "INTERNAL_OFF_EXTERNAL_ON";
44 case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF:
45 return "INTERNAL_ON_EXTERNAL_OFF";
46 default:
47 return "unknown (" + base::IntToString(state) + ")";
51 // Returns a string describing |state|.
52 std::string DisplayStateToString(MultipleDisplayState state) {
53 switch (state) {
54 case MULTIPLE_DISPLAY_STATE_INVALID:
55 return "INVALID";
56 case MULTIPLE_DISPLAY_STATE_HEADLESS:
57 return "HEADLESS";
58 case MULTIPLE_DISPLAY_STATE_SINGLE:
59 return "SINGLE";
60 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR:
61 return "DUAL_MIRROR";
62 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED:
63 return "DUAL_EXTENDED";
65 NOTREACHED() << "Unknown state " << state;
66 return "INVALID";
69 // Returns the number of displays in |displays| that should be turned on, per
70 // |state|. If |display_power| is non-NULL, it is updated to contain the
71 // on/off state of each corresponding entry in |displays|.
72 int GetDisplayPower(
73 const std::vector<DisplayConfigurator::DisplayState>& display_states,
74 chromeos::DisplayPowerState state,
75 std::vector<bool>* display_power) {
76 int num_on_displays = 0;
77 if (display_power)
78 display_power->resize(display_states.size());
80 for (size_t i = 0; i < display_states.size(); ++i) {
81 bool internal =
82 display_states[i].display->type() == DISPLAY_CONNECTION_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 (display_power)
89 (*display_power)[i] = on;
90 if (on)
91 num_on_displays++;
93 return num_on_displays;
96 } // namespace
99 const int DisplayConfigurator::kSetDisplayPowerNoFlags = 0;
100 const int DisplayConfigurator::kSetDisplayPowerForceProbe = 1 << 0;
101 const int
102 DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay = 1 << 1;
104 DisplayConfigurator::DisplayState::DisplayState()
105 : display(NULL),
106 selected_mode(NULL),
107 mirror_mode(NULL) {}
109 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() {
110 if (configurator_->configure_timer_.IsRunning()) {
111 configurator_->configure_timer_.user_task().Run();
112 configurator_->configure_timer_.Stop();
113 return true;
114 } else {
115 return false;
119 // static
120 const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize(
121 const DisplaySnapshot& display,
122 const gfx::Size& size) {
123 const DisplayMode* best_mode = NULL;
124 for (DisplayModeList::const_iterator it = display.modes().begin();
125 it != display.modes().end();
126 ++it) {
127 const DisplayMode* mode = *it;
129 if (mode->size() != size)
130 continue;
132 if (mode == display.native_mode()) {
133 best_mode = mode;
134 break;
137 if (!best_mode) {
138 best_mode = mode;
139 continue;
142 if (mode->is_interlaced()) {
143 if (!best_mode->is_interlaced())
144 continue;
145 } else {
146 // Reset the best rate if the non interlaced is
147 // found the first time.
148 if (best_mode->is_interlaced()) {
149 best_mode = mode;
150 continue;
153 if (mode->refresh_rate() < best_mode->refresh_rate())
154 continue;
156 best_mode = mode;
159 return best_mode;
162 DisplayConfigurator::DisplayConfigurator()
163 : state_controller_(NULL),
164 mirroring_controller_(NULL),
165 is_panel_fitting_enabled_(false),
166 configure_display_(base::SysInfo::IsRunningOnChromeOS()),
167 display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
168 requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
169 current_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
170 next_display_protection_client_id_(1) {}
172 DisplayConfigurator::~DisplayConfigurator() {
173 if (native_display_delegate_)
174 native_display_delegate_->RemoveObserver(this);
177 void DisplayConfigurator::SetDelegateForTesting(
178 scoped_ptr<NativeDisplayDelegate> display_delegate) {
179 DCHECK(!native_display_delegate_);
181 native_display_delegate_ = display_delegate.Pass();
182 configure_display_ = true;
185 void DisplayConfigurator::SetInitialDisplayPower(
186 chromeos::DisplayPowerState power_state) {
187 DCHECK_EQ(display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
188 requested_power_state_ = current_power_state_ = power_state;
191 void DisplayConfigurator::Init(bool is_panel_fitting_enabled) {
192 is_panel_fitting_enabled_ = is_panel_fitting_enabled;
193 if (!configure_display_)
194 return;
196 // If the delegate is already initialized don't update it (For example, tests
197 // set their own delegates).
198 if (!native_display_delegate_) {
199 native_display_delegate_ = CreatePlatformNativeDisplayDelegate();
200 native_display_delegate_->AddObserver(this);
204 void DisplayConfigurator::ForceInitialConfigure(
205 uint32_t background_color_argb) {
206 if (!configure_display_)
207 return;
209 native_display_delegate_->GrabServer();
210 native_display_delegate_->Initialize();
212 UpdateCachedDisplays();
213 if (cached_displays_.size() > 1 && background_color_argb)
214 native_display_delegate_->SetBackgroundColor(background_color_argb);
215 const MultipleDisplayState new_state = ChooseDisplayState(
216 requested_power_state_);
217 const bool success = EnterStateOrFallBackToSoftwareMirroring(
218 new_state, requested_power_state_);
220 // Force the DPMS on chrome startup as the driver doesn't always detect
221 // that all displays are on when signing out.
222 native_display_delegate_->ForceDPMSOn();
223 native_display_delegate_->UngrabServer();
224 NotifyObservers(success, new_state);
227 bool DisplayConfigurator::IsMirroring() const {
228 return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
229 (mirroring_controller_ &&
230 mirroring_controller_->SoftwareMirroringEnabled());
233 bool DisplayConfigurator::ApplyProtections(const ContentProtections& requests) {
234 for (DisplayStateList::const_iterator it = cached_displays_.begin();
235 it != cached_displays_.end();
236 ++it) {
237 uint32_t all_desired = 0;
239 // In mirror mode, protection request of all displays need to be fulfilled.
240 // In non-mirror mode, only request of client's display needs to be
241 // fulfilled.
242 ContentProtections::const_iterator request_it;
243 if (IsMirroring()) {
244 for (request_it = requests.begin();
245 request_it != requests.end();
246 ++request_it)
247 all_desired |= request_it->second;
248 } else {
249 request_it = requests.find(it->display->display_id());
250 if (request_it != requests.end())
251 all_desired = request_it->second;
254 switch (it->display->type()) {
255 case DISPLAY_CONNECTION_TYPE_UNKNOWN:
256 return false;
257 // DisplayPort, DVI, and HDMI all support HDCP.
258 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
259 case DISPLAY_CONNECTION_TYPE_DVI:
260 case DISPLAY_CONNECTION_TYPE_HDMI: {
261 HDCPState current_state;
262 // Need to poll the driver for updates since other applications may
263 // have updated the state.
264 if (!native_display_delegate_->GetHDCPState(*it->display,
265 &current_state))
266 return false;
267 bool current_desired = (current_state != HDCP_STATE_UNDESIRED);
268 bool new_desired = (all_desired & CONTENT_PROTECTION_METHOD_HDCP);
269 // Don't enable again if HDCP is already active. Some buggy drivers
270 // may disable and enable if setting "desired" in active state.
271 if (current_desired != new_desired) {
272 HDCPState new_state =
273 new_desired ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED;
274 if (!native_display_delegate_->SetHDCPState(*it->display, new_state))
275 return false;
277 break;
279 case DISPLAY_CONNECTION_TYPE_INTERNAL:
280 case DISPLAY_CONNECTION_TYPE_VGA:
281 case DISPLAY_CONNECTION_TYPE_NETWORK:
282 // No protections for these types. Do nothing.
283 break;
284 case DISPLAY_CONNECTION_TYPE_NONE:
285 NOTREACHED();
286 break;
290 return true;
293 DisplayConfigurator::ContentProtectionClientId
294 DisplayConfigurator::RegisterContentProtectionClient() {
295 if (!configure_display_)
296 return kInvalidClientId;
298 return next_display_protection_client_id_++;
301 void DisplayConfigurator::UnregisterContentProtectionClient(
302 ContentProtectionClientId client_id) {
303 client_protection_requests_.erase(client_id);
305 ContentProtections protections;
306 for (ProtectionRequests::const_iterator it =
307 client_protection_requests_.begin();
308 it != client_protection_requests_.end();
309 ++it) {
310 for (ContentProtections::const_iterator it2 = it->second.begin();
311 it2 != it->second.end();
312 ++it2) {
313 protections[it2->first] |= it2->second;
317 ApplyProtections(protections);
320 bool DisplayConfigurator::QueryContentProtectionStatus(
321 ContentProtectionClientId client_id,
322 int64_t display_id,
323 uint32_t* link_mask,
324 uint32_t* protection_mask) {
325 if (!configure_display_)
326 return false;
328 uint32_t enabled = 0;
329 uint32_t unfulfilled = 0;
330 *link_mask = 0;
331 for (DisplayStateList::const_iterator it = cached_displays_.begin();
332 it != cached_displays_.end();
333 ++it) {
334 // Query display if it is in mirror mode or client on the same display.
335 if (!IsMirroring() && it->display->display_id() != display_id)
336 continue;
338 *link_mask |= it->display->type();
339 switch (it->display->type()) {
340 case DISPLAY_CONNECTION_TYPE_UNKNOWN:
341 return false;
342 // DisplayPort, DVI, and HDMI all support HDCP.
343 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
344 case DISPLAY_CONNECTION_TYPE_DVI:
345 case DISPLAY_CONNECTION_TYPE_HDMI: {
346 HDCPState state;
347 if (!native_display_delegate_->GetHDCPState(*it->display, &state))
348 return false;
349 if (state == HDCP_STATE_ENABLED)
350 enabled |= CONTENT_PROTECTION_METHOD_HDCP;
351 else
352 unfulfilled |= CONTENT_PROTECTION_METHOD_HDCP;
353 break;
355 case DISPLAY_CONNECTION_TYPE_INTERNAL:
356 case DISPLAY_CONNECTION_TYPE_VGA:
357 case DISPLAY_CONNECTION_TYPE_NETWORK:
358 // No protections for these types. Do nothing.
359 break;
360 case DISPLAY_CONNECTION_TYPE_NONE:
361 NOTREACHED();
362 break;
366 // Don't reveal protections requested by other clients.
367 ProtectionRequests::iterator it = client_protection_requests_.find(client_id);
368 if (it != client_protection_requests_.end()) {
369 uint32_t requested_mask = 0;
370 if (it->second.find(display_id) != it->second.end())
371 requested_mask = it->second[display_id];
372 *protection_mask = enabled & ~unfulfilled & requested_mask;
373 } else {
374 *protection_mask = 0;
376 return true;
379 bool DisplayConfigurator::EnableContentProtection(
380 ContentProtectionClientId client_id,
381 int64_t display_id,
382 uint32_t desired_method_mask) {
383 if (!configure_display_)
384 return false;
386 ContentProtections protections;
387 for (ProtectionRequests::const_iterator it =
388 client_protection_requests_.begin();
389 it != client_protection_requests_.end();
390 ++it) {
391 for (ContentProtections::const_iterator it2 = it->second.begin();
392 it2 != it->second.end();
393 ++it2) {
394 if (it->first == client_id && it2->first == display_id)
395 continue;
396 protections[it2->first] |= it2->second;
399 protections[display_id] |= desired_method_mask;
401 if (!ApplyProtections(protections))
402 return false;
404 if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) {
405 if (client_protection_requests_.find(client_id) !=
406 client_protection_requests_.end()) {
407 client_protection_requests_[client_id].erase(display_id);
408 if (client_protection_requests_[client_id].size() == 0)
409 client_protection_requests_.erase(client_id);
411 } else {
412 client_protection_requests_[client_id][display_id] = desired_method_mask;
415 return true;
418 std::vector<ui::ColorCalibrationProfile>
419 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) {
420 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
421 switches::kDisableDisplayColorCalibration)) {
422 for (size_t i = 0; i < cached_displays_.size(); ++i) {
423 if (cached_displays_[i].display &&
424 cached_displays_[i].display->display_id() == display_id) {
425 return native_display_delegate_->GetAvailableColorCalibrationProfiles(
426 *cached_displays_[i].display);
431 return std::vector<ui::ColorCalibrationProfile>();
434 bool DisplayConfigurator::SetColorCalibrationProfile(
435 int64_t display_id,
436 ui::ColorCalibrationProfile new_profile) {
437 for (size_t i = 0; i < cached_displays_.size(); ++i) {
438 if (cached_displays_[i].display &&
439 cached_displays_[i].display->display_id() == display_id) {
440 return native_display_delegate_->SetColorCalibrationProfile(
441 *cached_displays_[i].display, new_profile);
445 return false;
448 void DisplayConfigurator::PrepareForExit() {
449 configure_display_ = false;
452 bool DisplayConfigurator::SetDisplayPower(
453 chromeos::DisplayPowerState power_state,
454 int flags) {
455 if (!configure_display_)
456 return false;
458 VLOG(1) << "SetDisplayPower: power_state="
459 << DisplayPowerStateToString(power_state) << " flags=" << flags
460 << ", configure timer="
461 << (configure_timer_.IsRunning() ? "Running" : "Stopped");
462 if (power_state == current_power_state_ &&
463 !(flags & kSetDisplayPowerForceProbe))
464 return true;
466 native_display_delegate_->GrabServer();
467 UpdateCachedDisplays();
469 const MultipleDisplayState new_state = ChooseDisplayState(power_state);
470 bool attempted_change = false;
471 bool success = false;
473 bool only_if_single_internal_display =
474 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay;
475 bool single_internal_display =
476 cached_displays_.size() == 1 &&
477 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
478 if (single_internal_display || !only_if_single_internal_display) {
479 success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state);
480 attempted_change = true;
482 // Force the DPMS on since the driver doesn't always detect that it
483 // should turn on. This is needed when coming back from idle suspend.
484 if (success && power_state != chromeos::DISPLAY_POWER_ALL_OFF)
485 native_display_delegate_->ForceDPMSOn();
488 native_display_delegate_->UngrabServer();
489 if (attempted_change)
490 NotifyObservers(success, new_state);
491 return success;
494 bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
495 if (!configure_display_)
496 return false;
498 VLOG(1) << "SetDisplayMode: state=" << DisplayStateToString(new_state);
499 if (display_state_ == new_state) {
500 // Cancel software mirroring if the state is moving from
501 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
502 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
503 if (mirroring_controller_ &&
504 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED)
505 mirroring_controller_->SetSoftwareMirroring(false);
506 NotifyObservers(true, new_state);
507 return true;
510 native_display_delegate_->GrabServer();
511 UpdateCachedDisplays();
512 const bool success = EnterStateOrFallBackToSoftwareMirroring(
513 new_state, requested_power_state_);
514 native_display_delegate_->UngrabServer();
516 NotifyObservers(success, new_state);
517 return success;
520 void DisplayConfigurator::OnConfigurationChanged() {
521 // Configure displays with |kConfigureDelayMs| delay,
522 // so that time-consuming ConfigureDisplays() won't be called multiple times.
523 if (configure_timer_.IsRunning()) {
524 // Note: when the timer is running it is possible that a different task
525 // (RestoreRequestedPowerStateAfterResume()) is scheduled. In these cases,
526 // prefer the already scheduled task to ConfigureDisplays() since
527 // ConfigureDisplays() performs only basic configuration while
528 // RestoreRequestedPowerStateAfterResume() will perform additional
529 // operations.
530 configure_timer_.Reset();
531 } else {
532 configure_timer_.Start(
533 FROM_HERE,
534 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
535 this,
536 &DisplayConfigurator::ConfigureDisplays);
540 void DisplayConfigurator::AddObserver(Observer* observer) {
541 observers_.AddObserver(observer);
544 void DisplayConfigurator::RemoveObserver(Observer* observer) {
545 observers_.RemoveObserver(observer);
548 void DisplayConfigurator::SuspendDisplays() {
549 // If the display is off due to user inactivity and there's only a single
550 // internal display connected, switch to the all-on state before
551 // suspending. This shouldn't be very noticeable to the user since the
552 // backlight is off at this point, and doing this lets us resume directly
553 // into the "on" state, which greatly reduces resume times.
554 if (requested_power_state_ == chromeos::DISPLAY_POWER_ALL_OFF) {
555 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
556 kSetDisplayPowerOnlyIfSingleInternalDisplay);
558 // We need to make sure that the monitor configuration we just did actually
559 // completes before we return, because otherwise the X message could be
560 // racing with the HandleSuspendReadiness message.
561 native_display_delegate_->SyncWithServer();
565 void DisplayConfigurator::ResumeDisplays() {
566 configure_timer_.Start(
567 FROM_HERE,
568 base::TimeDelta::FromMilliseconds(kResumeDelayMs),
569 base::Bind(&DisplayConfigurator::RestoreRequestedPowerStateAfterResume,
570 base::Unretained(this)));
573 void DisplayConfigurator::UpdateCachedDisplays() {
574 std::vector<DisplaySnapshot*> snapshots =
575 native_display_delegate_->GetDisplays();
577 cached_displays_.clear();
578 for (size_t i = 0; i < snapshots.size(); ++i) {
579 DisplayState display_state;
580 display_state.display = snapshots[i];
581 cached_displays_.push_back(display_state);
584 // Set |selected_mode| fields.
585 for (size_t i = 0; i < cached_displays_.size(); ++i) {
586 DisplayState* display_state = &cached_displays_[i];
587 gfx::Size size;
588 if (state_controller_ &&
589 state_controller_->GetResolutionForDisplayId(
590 display_state->display->display_id(), &size)) {
591 display_state->selected_mode =
592 FindDisplayModeMatchingSize(*display_state->display, size);
595 // Fall back to native mode.
596 if (!display_state->selected_mode)
597 display_state->selected_mode = display_state->display->native_mode();
600 // Set |mirror_mode| fields.
601 if (cached_displays_.size() == 2) {
602 bool one_is_internal =
603 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
604 bool two_is_internal =
605 cached_displays_[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
606 int internal_displays =
607 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0);
608 DCHECK_LT(internal_displays, 2);
609 LOG_IF(WARNING, internal_displays == 2)
610 << "Two internal displays detected.";
612 bool can_mirror = false;
613 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
614 // Try preserving external display's aspect ratio on the first attempt.
615 // If that fails, fall back to the highest matching resolution.
616 bool preserve_aspect = attempt == 0;
618 if (internal_displays == 1) {
619 if (one_is_internal) {
620 can_mirror = FindMirrorMode(&cached_displays_[0],
621 &cached_displays_[1],
622 is_panel_fitting_enabled_,
623 preserve_aspect);
624 } else {
625 DCHECK(two_is_internal);
626 can_mirror = FindMirrorMode(&cached_displays_[1],
627 &cached_displays_[0],
628 is_panel_fitting_enabled_,
629 preserve_aspect);
631 } else { // if (internal_displays == 0)
632 // No panel fitting for external displays, so fall back to exact match.
633 can_mirror = FindMirrorMode(
634 &cached_displays_[0], &cached_displays_[1], false, preserve_aspect);
635 if (!can_mirror && preserve_aspect) {
636 // FindMirrorMode() will try to preserve aspect ratio of what it
637 // thinks is external display, so if it didn't succeed with one, maybe
638 // it will succeed with the other. This way we will have the correct
639 // aspect ratio on at least one of them.
640 can_mirror = FindMirrorMode(&cached_displays_[1],
641 &cached_displays_[0],
642 false,
643 preserve_aspect);
650 bool DisplayConfigurator::FindMirrorMode(DisplayState* internal_display,
651 DisplayState* external_display,
652 bool try_panel_fitting,
653 bool preserve_aspect) {
654 const DisplayMode* internal_native_info =
655 internal_display->display->native_mode();
656 const DisplayMode* external_native_info =
657 external_display->display->native_mode();
658 if (!internal_native_info || !external_native_info)
659 return false;
661 // Check if some external display resolution can be mirrored on internal.
662 // Prefer the modes in the order they're present in DisplaySnapshot, assuming
663 // this is the order in which they look better on the monitor.
664 for (DisplayModeList::const_iterator external_it =
665 external_display->display->modes().begin();
666 external_it != external_display->display->modes().end();
667 ++external_it) {
668 const DisplayMode& external_info = **external_it;
669 bool is_native_aspect_ratio =
670 external_native_info->size().width() * external_info.size().height() ==
671 external_native_info->size().height() * external_info.size().width();
672 if (preserve_aspect && !is_native_aspect_ratio)
673 continue; // Allow only aspect ratio preserving modes for mirroring.
675 // Try finding an exact match.
676 for (DisplayModeList::const_iterator internal_it =
677 internal_display->display->modes().begin();
678 internal_it != internal_display->display->modes().end();
679 ++internal_it) {
680 const DisplayMode& internal_info = **internal_it;
681 if (internal_info.size().width() == external_info.size().width() &&
682 internal_info.size().height() == external_info.size().height() &&
683 internal_info.is_interlaced() == external_info.is_interlaced()) {
684 internal_display->mirror_mode = *internal_it;
685 external_display->mirror_mode = *external_it;
686 return true; // Mirror mode found.
690 // Try to create a matching internal display mode by panel fitting.
691 if (try_panel_fitting) {
692 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
693 // ugly, so, can fit == can upscale. Also, internal panels don't support
694 // fitting interlaced modes.
695 bool can_fit = internal_native_info->size().width() >=
696 external_info.size().width() &&
697 internal_native_info->size().height() >=
698 external_info.size().height() &&
699 !external_info.is_interlaced();
700 if (can_fit) {
701 native_display_delegate_->AddMode(*internal_display->display,
702 *external_it);
703 internal_display->display->add_mode(*external_it);
704 internal_display->mirror_mode = *external_it;
705 external_display->mirror_mode = *external_it;
706 return true; // Mirror mode created.
711 return false;
714 void DisplayConfigurator::ConfigureDisplays() {
715 if (!configure_display_)
716 return;
718 native_display_delegate_->GrabServer();
719 UpdateCachedDisplays();
720 const MultipleDisplayState new_state = ChooseDisplayState(
721 requested_power_state_);
722 const bool success = EnterStateOrFallBackToSoftwareMirroring(
723 new_state, requested_power_state_);
724 native_display_delegate_->UngrabServer();
726 NotifyObservers(success, new_state);
729 void DisplayConfigurator::RestoreRequestedPowerStateAfterResume() {
730 // Force probing to ensure that we pick up any changes that were made while
731 // the system was suspended.
732 SetDisplayPower(requested_power_state_, kSetDisplayPowerForceProbe);
735 void DisplayConfigurator::NotifyObservers(
736 bool success,
737 MultipleDisplayState attempted_state) {
738 if (success) {
739 FOR_EACH_OBSERVER(
740 Observer, observers_, OnDisplayModeChanged(cached_displays_));
741 } else {
742 FOR_EACH_OBSERVER(
743 Observer, observers_, OnDisplayModeChangeFailed(attempted_state));
747 bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring(
748 MultipleDisplayState display_state,
749 chromeos::DisplayPowerState power_state) {
750 bool success = EnterState(display_state, power_state);
751 if (mirroring_controller_) {
752 bool enable_software_mirroring = false;
753 if (!success && display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) {
754 if (display_state_ != MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED ||
755 current_power_state_ != power_state)
756 EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, power_state);
757 enable_software_mirroring = success =
758 display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
760 mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring);
762 return success;
765 bool DisplayConfigurator::EnterState(MultipleDisplayState display_state,
766 chromeos::DisplayPowerState power_state) {
767 std::vector<bool> display_power;
768 int num_on_displays =
769 GetDisplayPower(cached_displays_, power_state, &display_power);
770 VLOG(1) << "EnterState: display=" << DisplayStateToString(display_state)
771 << " power=" << DisplayPowerStateToString(power_state);
773 // Save the requested state so we'll try to use it next time even if we fail.
774 requested_power_state_ = power_state;
776 // Framebuffer dimensions.
777 gfx::Size size;
779 std::vector<gfx::Point> new_origins(cached_displays_.size(), gfx::Point());
780 std::vector<const DisplayMode*> new_mode;
781 for (size_t i = 0; i < cached_displays_.size(); ++i)
782 new_mode.push_back(cached_displays_[i].display->current_mode());
784 switch (display_state) {
785 case MULTIPLE_DISPLAY_STATE_INVALID:
786 NOTREACHED() << "Ignoring request to enter invalid state with "
787 << cached_displays_.size() << " connected display(s)";
788 return false;
789 case MULTIPLE_DISPLAY_STATE_HEADLESS:
790 if (cached_displays_.size() != 0) {
791 LOG(WARNING) << "Ignoring request to enter headless mode with "
792 << cached_displays_.size() << " connected display(s)";
793 return false;
795 break;
796 case MULTIPLE_DISPLAY_STATE_SINGLE: {
797 // If there are multiple displays connected, only one should be turned on.
798 if (cached_displays_.size() != 1 && num_on_displays != 1) {
799 LOG(WARNING) << "Ignoring request to enter single mode with "
800 << cached_displays_.size() << " connected displays and "
801 << num_on_displays << " turned on";
802 return false;
805 for (size_t i = 0; i < cached_displays_.size(); ++i) {
806 DisplayState* state = &cached_displays_[i];
807 new_mode[i] = display_power[i] ? state->selected_mode : NULL;
809 if (display_power[i] || cached_displays_.size() == 1) {
810 const DisplayMode* mode_info = state->selected_mode;
811 if (!mode_info) {
812 LOG(WARNING) << "No selected mode when configuring display: "
813 << state->display->ToString();
814 return false;
816 if (mode_info->size() == gfx::Size(1024, 768)) {
817 VLOG(1) << "Potentially misdetecting display(1024x768):"
818 << " displays size=" << cached_displays_.size()
819 << ", num_on_displays=" << num_on_displays
820 << ", current size:" << size.width() << "x" << size.height()
821 << ", i=" << i << ", display=" << state->display->ToString()
822 << ", display_mode=" << mode_info->ToString();
824 size = mode_info->size();
827 break;
829 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: {
830 if (cached_displays_.size() != 2 ||
831 (num_on_displays != 0 && num_on_displays != 2)) {
832 LOG(WARNING) << "Ignoring request to enter mirrored mode with "
833 << cached_displays_.size() << " connected display(s) and "
834 << num_on_displays << " turned on";
835 return false;
838 const DisplayMode* mode_info = cached_displays_[0].mirror_mode;
839 if (!mode_info) {
840 LOG(WARNING) << "No mirror mode when configuring display: "
841 << cached_displays_[0].display->ToString();
842 return false;
844 size = mode_info->size();
846 for (size_t i = 0; i < cached_displays_.size(); ++i) {
847 DisplayState* state = &cached_displays_[i];
848 new_mode[i] = display_power[i] ? state->mirror_mode : NULL;
850 break;
852 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: {
853 if (cached_displays_.size() != 2 ||
854 (num_on_displays != 0 && num_on_displays != 2)) {
855 LOG(WARNING) << "Ignoring request to enter extended mode with "
856 << cached_displays_.size() << " connected display(s) and "
857 << num_on_displays << " turned on";
858 return false;
861 for (size_t i = 0; i < cached_displays_.size(); ++i) {
862 DisplayState* state = &cached_displays_[i];
863 new_origins[i].set_y(size.height() ? size.height() + kVerticalGap : 0);
864 new_mode[i] = display_power[i] ? state->selected_mode : NULL;
866 // Retain the full screen size even if all displays are off so the
867 // same desktop configuration can be restored when the displays are
868 // turned back on.
869 const DisplayMode* mode_info = cached_displays_[i].selected_mode;
870 if (!mode_info) {
871 LOG(WARNING) << "No selected mode when configuring display: "
872 << state->display->ToString();
873 return false;
876 size.set_width(std::max<int>(size.width(), mode_info->size().width()));
877 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) +
878 mode_info->size().height());
880 break;
884 // Finally, apply the desired changes.
885 bool all_succeeded = true;
886 if (!cached_displays_.empty()) {
887 native_display_delegate_->CreateFrameBuffer(size);
888 for (size_t i = 0; i < cached_displays_.size(); ++i) {
889 const DisplayState& state = cached_displays_[i];
890 bool configure_succeeded = false;
892 while (true) {
893 if (native_display_delegate_->Configure(
894 *state.display, new_mode[i], new_origins[i])) {
895 state.display->set_current_mode(new_mode[i]);
896 state.display->set_origin(new_origins[i]);
898 configure_succeeded = true;
899 break;
902 const DisplayMode* mode_info = new_mode[i];
903 if (!mode_info)
904 break;
906 // Find the mode with the next-best resolution and see if that can
907 // be set.
908 int best_mode_pixels = 0;
910 int current_mode_pixels = mode_info->size().GetArea();
911 for (DisplayModeList::const_iterator it =
912 state.display->modes().begin();
913 it != state.display->modes().end();
914 it++) {
915 int pixel_count = (*it)->size().GetArea();
916 if ((pixel_count < current_mode_pixels) &&
917 (pixel_count > best_mode_pixels)) {
918 new_mode[i] = *it;
919 best_mode_pixels = pixel_count;
923 if (best_mode_pixels == 0)
924 break;
927 if (!configure_succeeded)
928 all_succeeded = false;
930 // If we are trying to set mirror mode and one of the modesets fails,
931 // then the two monitors will be mis-matched. In this case, return
932 // false to let the observers be aware.
933 if (display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR &&
934 display_power[i] &&
935 state.display->current_mode() != state.mirror_mode)
936 all_succeeded = false;
940 if (all_succeeded) {
941 display_state_ = display_state;
942 current_power_state_ = power_state;
943 framebuffer_size_ = size;
945 return all_succeeded;
948 MultipleDisplayState DisplayConfigurator::ChooseDisplayState(
949 chromeos::DisplayPowerState power_state) const {
950 int num_on_displays = GetDisplayPower(cached_displays_, power_state, NULL);
951 switch (cached_displays_.size()) {
952 case 0:
953 return MULTIPLE_DISPLAY_STATE_HEADLESS;
954 case 1:
955 return MULTIPLE_DISPLAY_STATE_SINGLE;
956 case 2: {
957 if (num_on_displays == 1) {
958 // If only one display is currently turned on, return the "single"
959 // state so that its native mode will be used.
960 return MULTIPLE_DISPLAY_STATE_SINGLE;
961 } else {
962 if (!state_controller_)
963 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
964 // With either both displays on or both displays off, use one of the
965 // dual modes.
966 std::vector<int64_t> display_ids;
967 for (size_t i = 0; i < cached_displays_.size(); ++i)
968 display_ids.push_back(cached_displays_[i].display->display_id());
970 return state_controller_->GetStateForDisplayIds(display_ids);
973 default:
974 NOTREACHED();
976 return MULTIPLE_DISPLAY_STATE_INVALID;
979 } // namespace ui