Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / display / chromeos / display_configurator.cc
blobf25f05a94342de085e130ac3da5429019c067406
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/chromeos/display_mode.h"
16 #include "ui/display/types/chromeos/display_snapshot.h"
17 #include "ui/display/types/chromeos/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 touch_device_id(0),
107 selected_mode(NULL),
108 mirror_mode(NULL) {}
110 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() {
111 if (configurator_->configure_timer_.IsRunning()) {
112 configurator_->configure_timer_.user_task().Run();
113 configurator_->configure_timer_.Stop();
114 return true;
115 } else {
116 return false;
120 // static
121 const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize(
122 const DisplaySnapshot& display,
123 const gfx::Size& size) {
124 const DisplayMode* best_mode = NULL;
125 for (DisplayModeList::const_iterator it = display.modes().begin();
126 it != display.modes().end();
127 ++it) {
128 const DisplayMode* mode = *it;
130 if (mode->size() != size)
131 continue;
133 if (!best_mode) {
134 best_mode = mode;
135 continue;
138 if (mode->is_interlaced()) {
139 if (!best_mode->is_interlaced())
140 continue;
141 } else {
142 // Reset the best rate if the non interlaced is
143 // found the first time.
144 if (best_mode->is_interlaced()) {
145 best_mode = mode;
146 continue;
149 if (mode->refresh_rate() < best_mode->refresh_rate())
150 continue;
152 best_mode = mode;
155 return best_mode;
158 DisplayConfigurator::DisplayConfigurator()
159 : state_controller_(NULL),
160 mirroring_controller_(NULL),
161 is_panel_fitting_enabled_(false),
162 configure_display_(base::SysInfo::IsRunningOnChromeOS()),
163 display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
164 power_state_(chromeos::DISPLAY_POWER_ALL_ON),
165 next_display_protection_client_id_(1) {}
167 DisplayConfigurator::~DisplayConfigurator() {
168 if (native_display_delegate_)
169 native_display_delegate_->RemoveObserver(this);
172 void DisplayConfigurator::SetDelegatesForTesting(
173 scoped_ptr<NativeDisplayDelegate> display_delegate,
174 scoped_ptr<TouchscreenDelegate> touchscreen_delegate) {
175 DCHECK(!native_display_delegate_);
176 DCHECK(!touchscreen_delegate_);
178 native_display_delegate_ = display_delegate.Pass();
179 touchscreen_delegate_ = touchscreen_delegate.Pass();
180 configure_display_ = true;
183 void DisplayConfigurator::SetInitialDisplayPower(
184 chromeos::DisplayPowerState power_state) {
185 DCHECK_EQ(display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
186 power_state_ = power_state;
189 void DisplayConfigurator::Init(bool is_panel_fitting_enabled) {
190 is_panel_fitting_enabled_ = is_panel_fitting_enabled;
191 if (!configure_display_)
192 return;
194 // If the delegates are already initialized don't update them (For example,
195 // tests set their own delegates).
196 if (!native_display_delegate_) {
197 native_display_delegate_ = CreatePlatformNativeDisplayDelegate();
198 native_display_delegate_->AddObserver(this);
201 if (!touchscreen_delegate_)
202 touchscreen_delegate_ = CreatePlatformTouchscreenDelegate();
205 void DisplayConfigurator::ForceInitialConfigure(
206 uint32_t background_color_argb) {
207 if (!configure_display_)
208 return;
210 native_display_delegate_->GrabServer();
211 native_display_delegate_->Initialize();
213 UpdateCachedDisplays();
214 if (cached_displays_.size() > 1 && background_color_argb)
215 native_display_delegate_->SetBackgroundColor(background_color_argb);
216 const MultipleDisplayState new_state = ChooseDisplayState(power_state_);
217 const bool success =
218 EnterStateOrFallBackToSoftwareMirroring(new_state, 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 new_desired_state =
262 (all_desired & CONTENT_PROTECTION_METHOD_HDCP) ?
263 HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED;
264 if (!native_display_delegate_->SetHDCPState(*it->display,
265 new_desired_state))
266 return false;
267 break;
269 case DISPLAY_CONNECTION_TYPE_INTERNAL:
270 case DISPLAY_CONNECTION_TYPE_VGA:
271 case DISPLAY_CONNECTION_TYPE_NETWORK:
272 // No protections for these types. Do nothing.
273 break;
274 case DISPLAY_CONNECTION_TYPE_NONE:
275 NOTREACHED();
276 break;
280 return true;
283 DisplayConfigurator::ContentProtectionClientId
284 DisplayConfigurator::RegisterContentProtectionClient() {
285 if (!configure_display_)
286 return kInvalidClientId;
288 return next_display_protection_client_id_++;
291 void DisplayConfigurator::UnregisterContentProtectionClient(
292 ContentProtectionClientId client_id) {
293 client_protection_requests_.erase(client_id);
295 ContentProtections protections;
296 for (ProtectionRequests::const_iterator it =
297 client_protection_requests_.begin();
298 it != client_protection_requests_.end();
299 ++it) {
300 for (ContentProtections::const_iterator it2 = it->second.begin();
301 it2 != it->second.end();
302 ++it2) {
303 protections[it2->first] |= it2->second;
307 ApplyProtections(protections);
310 bool DisplayConfigurator::QueryContentProtectionStatus(
311 ContentProtectionClientId client_id,
312 int64_t display_id,
313 uint32_t* link_mask,
314 uint32_t* protection_mask) {
315 if (!configure_display_)
316 return false;
318 uint32_t enabled = 0;
319 uint32_t unfulfilled = 0;
320 *link_mask = 0;
321 for (DisplayStateList::const_iterator it = cached_displays_.begin();
322 it != cached_displays_.end();
323 ++it) {
324 // Query display if it is in mirror mode or client on the same display.
325 if (!IsMirroring() && it->display->display_id() != display_id)
326 continue;
328 *link_mask |= it->display->type();
329 switch (it->display->type()) {
330 case DISPLAY_CONNECTION_TYPE_UNKNOWN:
331 return false;
332 // DisplayPort, DVI, and HDMI all support HDCP.
333 case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
334 case DISPLAY_CONNECTION_TYPE_DVI:
335 case DISPLAY_CONNECTION_TYPE_HDMI: {
336 HDCPState state;
337 if (!native_display_delegate_->GetHDCPState(*it->display, &state))
338 return false;
339 if (state == HDCP_STATE_ENABLED)
340 enabled |= CONTENT_PROTECTION_METHOD_HDCP;
341 else
342 unfulfilled |= CONTENT_PROTECTION_METHOD_HDCP;
343 break;
345 case DISPLAY_CONNECTION_TYPE_INTERNAL:
346 case DISPLAY_CONNECTION_TYPE_VGA:
347 case DISPLAY_CONNECTION_TYPE_NETWORK:
348 // No protections for these types. Do nothing.
349 break;
350 case DISPLAY_CONNECTION_TYPE_NONE:
351 NOTREACHED();
352 break;
356 // Don't reveal protections requested by other clients.
357 ProtectionRequests::iterator it = client_protection_requests_.find(client_id);
358 if (it != client_protection_requests_.end()) {
359 uint32_t requested_mask = 0;
360 if (it->second.find(display_id) != it->second.end())
361 requested_mask = it->second[display_id];
362 *protection_mask = enabled & ~unfulfilled & requested_mask;
363 } else {
364 *protection_mask = 0;
366 return true;
369 bool DisplayConfigurator::EnableContentProtection(
370 ContentProtectionClientId client_id,
371 int64_t display_id,
372 uint32_t desired_method_mask) {
373 if (!configure_display_)
374 return false;
376 ContentProtections protections;
377 for (ProtectionRequests::const_iterator it =
378 client_protection_requests_.begin();
379 it != client_protection_requests_.end();
380 ++it) {
381 for (ContentProtections::const_iterator it2 = it->second.begin();
382 it2 != it->second.end();
383 ++it2) {
384 if (it->first == client_id && it2->first == display_id)
385 continue;
386 protections[it2->first] |= it2->second;
389 protections[display_id] |= desired_method_mask;
391 if (!ApplyProtections(protections))
392 return false;
394 if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) {
395 if (client_protection_requests_.find(client_id) !=
396 client_protection_requests_.end()) {
397 client_protection_requests_[client_id].erase(display_id);
398 if (client_protection_requests_[client_id].size() == 0)
399 client_protection_requests_.erase(client_id);
401 } else {
402 client_protection_requests_[client_id][display_id] = desired_method_mask;
405 return true;
408 std::vector<ui::ColorCalibrationProfile>
409 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) {
410 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
411 switches::kDisableDisplayColorCalibration)) {
412 for (size_t i = 0; i < cached_displays_.size(); ++i) {
413 if (cached_displays_[i].display &&
414 cached_displays_[i].display->display_id() == display_id) {
415 return native_display_delegate_->GetAvailableColorCalibrationProfiles(
416 *cached_displays_[i].display);
421 return std::vector<ui::ColorCalibrationProfile>();
424 bool DisplayConfigurator::SetColorCalibrationProfile(
425 int64_t display_id,
426 ui::ColorCalibrationProfile new_profile) {
427 for (size_t i = 0; i < cached_displays_.size(); ++i) {
428 if (cached_displays_[i].display &&
429 cached_displays_[i].display->display_id() == display_id) {
430 return native_display_delegate_->SetColorCalibrationProfile(
431 *cached_displays_[i].display, new_profile);
435 return false;
438 void DisplayConfigurator::PrepareForExit() {
439 configure_display_ = false;
442 bool DisplayConfigurator::SetDisplayPower(
443 chromeos::DisplayPowerState power_state,
444 int flags) {
445 if (!configure_display_)
446 return false;
448 VLOG(1) << "SetDisplayPower: power_state="
449 << DisplayPowerStateToString(power_state) << " flags=" << flags
450 << ", configure timer="
451 << (configure_timer_.IsRunning() ? "Running" : "Stopped");
452 if (power_state == power_state_ && !(flags & kSetDisplayPowerForceProbe))
453 return true;
455 native_display_delegate_->GrabServer();
456 UpdateCachedDisplays();
458 const MultipleDisplayState new_state = ChooseDisplayState(power_state);
459 bool attempted_change = false;
460 bool success = false;
462 bool only_if_single_internal_display =
463 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay;
464 bool single_internal_display =
465 cached_displays_.size() == 1 &&
466 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
467 if (single_internal_display || !only_if_single_internal_display) {
468 success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state);
469 attempted_change = true;
471 // Force the DPMS on since the driver doesn't always detect that it
472 // should turn on. This is needed when coming back from idle suspend.
473 if (success && power_state != chromeos::DISPLAY_POWER_ALL_OFF)
474 native_display_delegate_->ForceDPMSOn();
477 native_display_delegate_->UngrabServer();
478 if (attempted_change)
479 NotifyObservers(success, new_state);
480 return true;
483 bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
484 if (!configure_display_)
485 return false;
487 VLOG(1) << "SetDisplayMode: state=" << DisplayStateToString(new_state);
488 if (display_state_ == new_state) {
489 // Cancel software mirroring if the state is moving from
490 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
491 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
492 if (mirroring_controller_ &&
493 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED)
494 mirroring_controller_->SetSoftwareMirroring(false);
495 NotifyObservers(true, new_state);
496 return true;
499 native_display_delegate_->GrabServer();
500 UpdateCachedDisplays();
501 const bool success =
502 EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_);
503 native_display_delegate_->UngrabServer();
505 NotifyObservers(success, new_state);
506 return success;
509 void DisplayConfigurator::OnConfigurationChanged() {
510 // Configure displays with |kConfigureDelayMs| delay,
511 // so that time-consuming ConfigureDisplays() won't be called multiple times.
512 if (configure_timer_.IsRunning()) {
513 // Note: when the timer is running it is possible that a different task
514 // (SetDisplayPower()) is scheduled. In these cases, prefer the already
515 // scheduled task to ConfigureDisplays() since ConfigureDisplays() performs
516 // only basic configuration while SetDisplayPower() will perform additional
517 // operations.
518 configure_timer_.Reset();
519 } else {
520 configure_timer_.Start(
521 FROM_HERE,
522 base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
523 this,
524 &DisplayConfigurator::ConfigureDisplays);
528 void DisplayConfigurator::AddObserver(Observer* observer) {
529 observers_.AddObserver(observer);
532 void DisplayConfigurator::RemoveObserver(Observer* observer) {
533 observers_.RemoveObserver(observer);
536 void DisplayConfigurator::SuspendDisplays() {
537 // If the display is off due to user inactivity and there's only a single
538 // internal display connected, switch to the all-on state before
539 // suspending. This shouldn't be very noticeable to the user since the
540 // backlight is off at this point, and doing this lets us resume directly
541 // into the "on" state, which greatly reduces resume times.
542 if (power_state_ == chromeos::DISPLAY_POWER_ALL_OFF) {
543 SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
544 kSetDisplayPowerOnlyIfSingleInternalDisplay);
546 // We need to make sure that the monitor configuration we just did actually
547 // completes before we return, because otherwise the X message could be
548 // racing with the HandleSuspendReadiness message.
549 native_display_delegate_->SyncWithServer();
553 void DisplayConfigurator::ResumeDisplays() {
554 // Force probing to ensure that we pick up any changes that were made
555 // while the system was suspended.
556 configure_timer_.Start(
557 FROM_HERE,
558 base::TimeDelta::FromMilliseconds(kResumeDelayMs),
559 base::Bind(base::IgnoreResult(&DisplayConfigurator::SetDisplayPower),
560 base::Unretained(this),
561 power_state_,
562 kSetDisplayPowerForceProbe));
565 void DisplayConfigurator::UpdateCachedDisplays() {
566 std::vector<DisplaySnapshot*> snapshots =
567 native_display_delegate_->GetDisplays();
569 cached_displays_.clear();
570 for (size_t i = 0; i < snapshots.size(); ++i) {
571 DisplayState display_state;
572 display_state.display = snapshots[i];
573 cached_displays_.push_back(display_state);
576 touchscreen_delegate_->AssociateTouchscreens(&cached_displays_);
578 // Set |selected_mode| fields.
579 for (size_t i = 0; i < cached_displays_.size(); ++i) {
580 DisplayState* display_state = &cached_displays_[i];
581 if (display_state->display->has_proper_display_id()) {
582 gfx::Size size;
583 if (state_controller_ &&
584 state_controller_->GetResolutionForDisplayId(
585 display_state->display->display_id(), &size)) {
586 display_state->selected_mode =
587 FindDisplayModeMatchingSize(*display_state->display, size);
590 // Fall back to native mode.
591 if (!display_state->selected_mode)
592 display_state->selected_mode = display_state->display->native_mode();
595 // Set |mirror_mode| fields.
596 if (cached_displays_.size() == 2) {
597 bool one_is_internal =
598 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
599 bool two_is_internal =
600 cached_displays_[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
601 int internal_displays =
602 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0);
603 DCHECK_LT(internal_displays, 2);
604 LOG_IF(WARNING, internal_displays == 2)
605 << "Two internal displays detected.";
607 bool can_mirror = false;
608 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
609 // Try preserving external display's aspect ratio on the first attempt.
610 // If that fails, fall back to the highest matching resolution.
611 bool preserve_aspect = attempt == 0;
613 if (internal_displays == 1) {
614 if (one_is_internal) {
615 can_mirror = FindMirrorMode(&cached_displays_[0],
616 &cached_displays_[1],
617 is_panel_fitting_enabled_,
618 preserve_aspect);
619 } else {
620 DCHECK(two_is_internal);
621 can_mirror = FindMirrorMode(&cached_displays_[1],
622 &cached_displays_[0],
623 is_panel_fitting_enabled_,
624 preserve_aspect);
626 } else { // if (internal_displays == 0)
627 // No panel fitting for external displays, so fall back to exact match.
628 can_mirror = FindMirrorMode(
629 &cached_displays_[0], &cached_displays_[1], false, preserve_aspect);
630 if (!can_mirror && preserve_aspect) {
631 // FindMirrorMode() will try to preserve aspect ratio of what it
632 // thinks is external display, so if it didn't succeed with one, maybe
633 // it will succeed with the other. This way we will have the correct
634 // aspect ratio on at least one of them.
635 can_mirror = FindMirrorMode(&cached_displays_[1],
636 &cached_displays_[0],
637 false,
638 preserve_aspect);
645 bool DisplayConfigurator::FindMirrorMode(DisplayState* internal_display,
646 DisplayState* external_display,
647 bool try_panel_fitting,
648 bool preserve_aspect) {
649 const DisplayMode* internal_native_info =
650 internal_display->display->native_mode();
651 const DisplayMode* external_native_info =
652 external_display->display->native_mode();
653 if (!internal_native_info || !external_native_info)
654 return false;
656 // Check if some external display resolution can be mirrored on internal.
657 // Prefer the modes in the order they're present in DisplaySnapshot, assuming
658 // this is the order in which they look better on the monitor.
659 for (DisplayModeList::const_iterator external_it =
660 external_display->display->modes().begin();
661 external_it != external_display->display->modes().end();
662 ++external_it) {
663 const DisplayMode& external_info = **external_it;
664 bool is_native_aspect_ratio =
665 external_native_info->size().width() * external_info.size().height() ==
666 external_native_info->size().height() * external_info.size().width();
667 if (preserve_aspect && !is_native_aspect_ratio)
668 continue; // Allow only aspect ratio preserving modes for mirroring.
670 // Try finding an exact match.
671 for (DisplayModeList::const_iterator internal_it =
672 internal_display->display->modes().begin();
673 internal_it != internal_display->display->modes().end();
674 ++internal_it) {
675 const DisplayMode& internal_info = **internal_it;
676 if (internal_info.size().width() == external_info.size().width() &&
677 internal_info.size().height() == external_info.size().height() &&
678 internal_info.is_interlaced() == external_info.is_interlaced()) {
679 internal_display->mirror_mode = *internal_it;
680 external_display->mirror_mode = *external_it;
681 return true; // Mirror mode found.
685 // Try to create a matching internal display mode by panel fitting.
686 if (try_panel_fitting) {
687 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
688 // ugly, so, can fit == can upscale. Also, internal panels don't support
689 // fitting interlaced modes.
690 bool can_fit = internal_native_info->size().width() >=
691 external_info.size().width() &&
692 internal_native_info->size().height() >=
693 external_info.size().height() &&
694 !external_info.is_interlaced();
695 if (can_fit) {
696 native_display_delegate_->AddMode(*internal_display->display,
697 *external_it);
698 internal_display->display->add_mode(*external_it);
699 internal_display->mirror_mode = *external_it;
700 external_display->mirror_mode = *external_it;
701 return true; // Mirror mode created.
706 return false;
709 void DisplayConfigurator::ConfigureDisplays() {
710 if (!configure_display_)
711 return;
713 native_display_delegate_->GrabServer();
714 UpdateCachedDisplays();
715 const MultipleDisplayState new_state = ChooseDisplayState(power_state_);
716 const bool success =
717 EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_);
718 native_display_delegate_->UngrabServer();
720 NotifyObservers(success, new_state);
723 void DisplayConfigurator::NotifyObservers(
724 bool success,
725 MultipleDisplayState attempted_state) {
726 if (success) {
727 FOR_EACH_OBSERVER(
728 Observer, observers_, OnDisplayModeChanged(cached_displays_));
729 } else {
730 FOR_EACH_OBSERVER(
731 Observer, observers_, OnDisplayModeChangeFailed(attempted_state));
735 bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring(
736 MultipleDisplayState display_state,
737 chromeos::DisplayPowerState power_state) {
738 bool success = EnterState(display_state, power_state);
739 if (mirroring_controller_) {
740 bool enable_software_mirroring = false;
741 if (!success && display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) {
742 if (display_state_ != MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED ||
743 power_state_ != power_state)
744 EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, power_state);
745 enable_software_mirroring = success =
746 display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
748 mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring);
750 return success;
753 bool DisplayConfigurator::EnterState(MultipleDisplayState display_state,
754 chromeos::DisplayPowerState power_state) {
755 std::vector<bool> display_power;
756 int num_on_displays =
757 GetDisplayPower(cached_displays_, power_state, &display_power);
758 VLOG(1) << "EnterState: display=" << DisplayStateToString(display_state)
759 << " power=" << DisplayPowerStateToString(power_state);
761 // Framebuffer dimensions.
762 gfx::Size size;
764 std::vector<gfx::Point> new_origins(cached_displays_.size(), gfx::Point());
765 std::vector<const DisplayMode*> new_mode;
766 for (size_t i = 0; i < cached_displays_.size(); ++i)
767 new_mode.push_back(cached_displays_[i].display->current_mode());
769 switch (display_state) {
770 case MULTIPLE_DISPLAY_STATE_INVALID:
771 NOTREACHED() << "Ignoring request to enter invalid state with "
772 << cached_displays_.size() << " connected display(s)";
773 return false;
774 case MULTIPLE_DISPLAY_STATE_HEADLESS:
775 if (cached_displays_.size() != 0) {
776 LOG(WARNING) << "Ignoring request to enter headless mode with "
777 << cached_displays_.size() << " connected display(s)";
778 return false;
780 break;
781 case MULTIPLE_DISPLAY_STATE_SINGLE: {
782 // If there are multiple displays connected, only one should be turned on.
783 if (cached_displays_.size() != 1 && num_on_displays != 1) {
784 LOG(WARNING) << "Ignoring request to enter single mode with "
785 << cached_displays_.size() << " connected displays and "
786 << num_on_displays << " turned on";
787 return false;
790 for (size_t i = 0; i < cached_displays_.size(); ++i) {
791 DisplayState* state = &cached_displays_[i];
792 new_mode[i] = display_power[i] ? state->selected_mode : NULL;
794 if (display_power[i] || cached_displays_.size() == 1) {
795 const DisplayMode* mode_info = state->selected_mode;
796 if (!mode_info) {
797 LOG(WARNING) << "No selected mode when configuring display: "
798 << state->display->ToString();
799 return false;
801 if (mode_info->size() == gfx::Size(1024, 768)) {
802 VLOG(1) << "Potentially misdetecting display(1024x768):"
803 << " displays size=" << cached_displays_.size()
804 << ", num_on_displays=" << num_on_displays
805 << ", current size:" << size.width() << "x" << size.height()
806 << ", i=" << i << ", display=" << state->display->ToString()
807 << ", display_mode=" << mode_info->ToString();
809 size = mode_info->size();
812 break;
814 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: {
815 if (cached_displays_.size() != 2 ||
816 (num_on_displays != 0 && num_on_displays != 2)) {
817 LOG(WARNING) << "Ignoring request to enter mirrored mode with "
818 << cached_displays_.size() << " connected display(s) and "
819 << num_on_displays << " turned on";
820 return false;
823 const DisplayMode* mode_info = cached_displays_[0].mirror_mode;
824 if (!mode_info) {
825 LOG(WARNING) << "No mirror mode when configuring display: "
826 << cached_displays_[0].display->ToString();
827 return false;
829 size = mode_info->size();
831 for (size_t i = 0; i < cached_displays_.size(); ++i) {
832 DisplayState* state = &cached_displays_[i];
833 new_mode[i] = display_power[i] ? state->mirror_mode : NULL;
835 break;
837 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: {
838 if (cached_displays_.size() != 2 ||
839 (num_on_displays != 0 && num_on_displays != 2)) {
840 LOG(WARNING) << "Ignoring request to enter extended mode with "
841 << cached_displays_.size() << " connected display(s) and "
842 << num_on_displays << " turned on";
843 return false;
846 for (size_t i = 0; i < cached_displays_.size(); ++i) {
847 DisplayState* state = &cached_displays_[i];
848 new_origins[i].set_y(size.height() ? size.height() + kVerticalGap : 0);
849 new_mode[i] = display_power[i] ? state->selected_mode : NULL;
851 // Retain the full screen size even if all displays are off so the
852 // same desktop configuration can be restored when the displays are
853 // turned back on.
854 const DisplayMode* mode_info = cached_displays_[i].selected_mode;
855 if (!mode_info) {
856 LOG(WARNING) << "No selected mode when configuring display: "
857 << state->display->ToString();
858 return false;
861 size.set_width(std::max<int>(size.width(), mode_info->size().width()));
862 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) +
863 mode_info->size().height());
865 break;
869 // Finally, apply the desired changes.
870 bool all_succeeded = true;
871 if (!cached_displays_.empty()) {
872 native_display_delegate_->CreateFrameBuffer(size);
873 for (size_t i = 0; i < cached_displays_.size(); ++i) {
874 const DisplayState& state = cached_displays_[i];
875 bool configure_succeeded = false;
877 while (true) {
878 if (native_display_delegate_->Configure(
879 *state.display, new_mode[i], new_origins[i])) {
880 state.display->set_current_mode(new_mode[i]);
881 state.display->set_origin(new_origins[i]);
883 configure_succeeded = true;
884 break;
887 const DisplayMode* mode_info = new_mode[i];
888 if (!mode_info)
889 break;
891 // Find the mode with the next-best resolution and see if that can
892 // be set.
893 int best_mode_pixels = 0;
895 int current_mode_pixels = mode_info->size().GetArea();
896 for (DisplayModeList::const_iterator it =
897 state.display->modes().begin();
898 it != state.display->modes().end();
899 it++) {
900 int pixel_count = (*it)->size().GetArea();
901 if ((pixel_count < current_mode_pixels) &&
902 (pixel_count > best_mode_pixels)) {
903 new_mode[i] = *it;
904 best_mode_pixels = pixel_count;
908 if (best_mode_pixels == 0)
909 break;
912 if (!configure_succeeded)
913 all_succeeded = false;
915 // If we are trying to set mirror mode and one of the modesets fails,
916 // then the two monitors will be mis-matched. In this case, return
917 // false to let the observers be aware.
918 if (display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR &&
919 display_power[i] &&
920 state.display->current_mode() != state.mirror_mode)
921 all_succeeded = false;
925 if (all_succeeded) {
926 display_state_ = display_state;
927 power_state_ = power_state;
928 framebuffer_size_ = size;
930 return all_succeeded;
933 MultipleDisplayState DisplayConfigurator::ChooseDisplayState(
934 chromeos::DisplayPowerState power_state) const {
935 int num_on_displays = GetDisplayPower(cached_displays_, power_state, NULL);
936 switch (cached_displays_.size()) {
937 case 0:
938 return MULTIPLE_DISPLAY_STATE_HEADLESS;
939 case 1:
940 return MULTIPLE_DISPLAY_STATE_SINGLE;
941 case 2: {
942 if (num_on_displays == 1) {
943 // If only one display is currently turned on, return the "single"
944 // state so that its native mode will be used.
945 return MULTIPLE_DISPLAY_STATE_SINGLE;
946 } else {
947 if (!state_controller_)
948 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
949 // With either both displays on or both displays off, use one of the
950 // dual modes.
951 std::vector<int64_t> display_ids;
952 for (size_t i = 0; i < cached_displays_.size(); ++i) {
953 // If display id isn't available, switch to extended mode.
954 if (!cached_displays_[i].display->has_proper_display_id())
955 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
956 display_ids.push_back(cached_displays_[i].display->display_id());
958 return state_controller_->GetStateForDisplayIds(display_ids);
961 default:
962 NOTREACHED();
964 return MULTIPLE_DISPLAY_STATE_INVALID;
967 } // namespace ui