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