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