f25f05a94342de085e130ac3da5429019c067406
[platform/framework/web/crosswalk.git] / src / ui / display / chromeos / display_configurator.cc
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/display_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/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"
18
19 namespace ui {
20
21 namespace {
22
23 typedef std::vector<const DisplayMode*> DisplayModeList;
24
25 // The delay to perform configuration after RRNotify. See the comment for
26 // |configure_timer_|.
27 const int kConfigureDelayMs = 500;
28
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;
34
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) + ")";
48   }
49 }
50
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";
64   }
65   NOTREACHED() << "Unknown state " << state;
66   return "INVALID";
67 }
68
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());
79
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++;
92   }
93   return num_on_displays;
94 }
95
96 }  // namespace
97
98
99 const int DisplayConfigurator::kSetDisplayPowerNoFlags = 0;
100 const int DisplayConfigurator::kSetDisplayPowerForceProbe = 1 << 0;
101 const int
102 DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay = 1 << 1;
103
104 DisplayConfigurator::DisplayState::DisplayState()
105     : display(NULL),
106       touch_device_id(0),
107       selected_mode(NULL),
108       mirror_mode(NULL) {}
109
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;
117   }
118 }
119
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;
129
130     if (mode->size() != size)
131       continue;
132
133     if (!best_mode) {
134       best_mode = mode;
135       continue;
136     }
137
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;
147       }
148     }
149     if (mode->refresh_rate() < best_mode->refresh_rate())
150       continue;
151
152     best_mode = mode;
153   }
154
155   return best_mode;
156 }
157
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) {}
166
167 DisplayConfigurator::~DisplayConfigurator() {
168   if (native_display_delegate_)
169     native_display_delegate_->RemoveObserver(this);
170 }
171
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_);
177
178   native_display_delegate_ = display_delegate.Pass();
179   touchscreen_delegate_ = touchscreen_delegate.Pass();
180   configure_display_ = true;
181 }
182
183 void DisplayConfigurator::SetInitialDisplayPower(
184     chromeos::DisplayPowerState power_state) {
185   DCHECK_EQ(display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
186   power_state_ = power_state;
187 }
188
189 void DisplayConfigurator::Init(bool is_panel_fitting_enabled) {
190   is_panel_fitting_enabled_ = is_panel_fitting_enabled;
191   if (!configure_display_)
192     return;
193
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);
199   }
200
201   if (!touchscreen_delegate_)
202     touchscreen_delegate_ = CreatePlatformTouchscreenDelegate();
203 }
204
205 void DisplayConfigurator::ForceInitialConfigure(
206     uint32_t background_color_argb) {
207   if (!configure_display_)
208     return;
209
210   native_display_delegate_->GrabServer();
211   native_display_delegate_->Initialize();
212
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_);
219
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);
225 }
226
227 bool DisplayConfigurator::IsMirroring() const {
228   return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
229       (mirroring_controller_ &&
230        mirroring_controller_->SoftwareMirroringEnabled());
231 }
232
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;
238
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;
252     }
253
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;
268       }
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;
277     }
278   }
279
280   return true;
281 }
282
283 DisplayConfigurator::ContentProtectionClientId
284 DisplayConfigurator::RegisterContentProtectionClient() {
285   if (!configure_display_)
286     return kInvalidClientId;
287
288   return next_display_protection_client_id_++;
289 }
290
291 void DisplayConfigurator::UnregisterContentProtectionClient(
292     ContentProtectionClientId client_id) {
293   client_protection_requests_.erase(client_id);
294
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;
304     }
305   }
306
307   ApplyProtections(protections);
308 }
309
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;
317
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;
327
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;
344       }
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;
353     }
354   }
355
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;
365   }
366   return true;
367 }
368
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;
375
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;
387     }
388   }
389   protections[display_id] |= desired_method_mask;
390
391   if (!ApplyProtections(protections))
392     return false;
393
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);
400     }
401   } else {
402     client_protection_requests_[client_id][display_id] = desired_method_mask;
403   }
404
405   return true;
406 }
407
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);
417       }
418     }
419   }
420
421   return std::vector<ui::ColorCalibrationProfile>();
422 }
423
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);
432     }
433   }
434
435   return false;
436 }
437
438 void DisplayConfigurator::PrepareForExit() {
439   configure_display_ = false;
440 }
441
442 bool DisplayConfigurator::SetDisplayPower(
443     chromeos::DisplayPowerState power_state,
444     int flags) {
445   if (!configure_display_)
446     return false;
447
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;
454
455   native_display_delegate_->GrabServer();
456   UpdateCachedDisplays();
457
458   const MultipleDisplayState new_state = ChooseDisplayState(power_state);
459   bool attempted_change = false;
460   bool success = false;
461
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;
470
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();
475   }
476
477   native_display_delegate_->UngrabServer();
478   if (attempted_change)
479     NotifyObservers(success, new_state);
480   return true;
481 }
482
483 bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
484   if (!configure_display_)
485     return false;
486
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;
497   }
498
499   native_display_delegate_->GrabServer();
500   UpdateCachedDisplays();
501   const bool success =
502       EnterStateOrFallBackToSoftwareMirroring(new_state, power_state_);
503   native_display_delegate_->UngrabServer();
504
505   NotifyObservers(success, new_state);
506   return success;
507 }
508
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);
525   }
526 }
527
528 void DisplayConfigurator::AddObserver(Observer* observer) {
529   observers_.AddObserver(observer);
530 }
531
532 void DisplayConfigurator::RemoveObserver(Observer* observer) {
533   observers_.RemoveObserver(observer);
534 }
535
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);
545
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();
550   }
551 }
552
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));
563 }
564
565 void DisplayConfigurator::UpdateCachedDisplays() {
566   std::vector<DisplaySnapshot*> snapshots =
567       native_display_delegate_->GetDisplays();
568
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);
574   }
575
576   touchscreen_delegate_->AssociateTouchscreens(&cached_displays_);
577
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);
588       }
589     }
590     // Fall back to native mode.
591     if (!display_state->selected_mode)
592       display_state->selected_mode = display_state->display->native_mode();
593   }
594
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.";
606
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;
612
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);
625         }
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);
639         }
640       }
641     }
642   }
643 }
644
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;
655
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.
669
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.
682       }
683     }
684
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.
702       }
703     }
704   }
705
706   return false;
707 }
708
709 void DisplayConfigurator::ConfigureDisplays() {
710   if (!configure_display_)
711     return;
712
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();
719
720   NotifyObservers(success, new_state);
721 }
722
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));
732   }
733 }
734
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;
747     }
748     mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring);
749   }
750   return success;
751 }
752
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);
760
761   // Framebuffer dimensions.
762   gfx::Size size;
763
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());
768
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;
779       }
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;
788       }
789
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;
793
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;
800           }
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();
808           }
809           size = mode_info->size();
810         }
811       }
812       break;
813     }
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;
821       }
822
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;
828       }
829       size = mode_info->size();
830
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;
834       }
835       break;
836     }
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;
844       }
845
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;
850
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;
859         }
860
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());
864       }
865       break;
866     }
867   }
868
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;
876
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]);
882
883           configure_succeeded = true;
884           break;
885         }
886
887         const DisplayMode* mode_info = new_mode[i];
888         if (!mode_info)
889           break;
890
891         // Find the mode with the next-best resolution and see if that can
892         // be set.
893         int best_mode_pixels = 0;
894
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;
905           }
906         }
907
908         if (best_mode_pixels == 0)
909           break;
910       }
911
912       if (!configure_succeeded)
913         all_succeeded = false;
914
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;
922     }
923   }
924
925   if (all_succeeded) {
926     display_state_ = display_state;
927     power_state_ = power_state;
928     framebuffer_size_ = size;
929   }
930   return all_succeeded;
931 }
932
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());
957         }
958         return state_controller_->GetStateForDisplayIds(display_ids);
959       }
960     }
961     default:
962       NOTREACHED();
963   }
964   return MULTIPLE_DISPLAY_STATE_INVALID;
965 }
966
967 }  // namespace ui