• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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|.
DisplayPowerStateToString(chromeos::DisplayPowerState 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|.
DisplayStateToString(MultipleDisplayState 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|.
GetDisplayPower(const std::vector<DisplayConfigurator::DisplayState> & display_states,chromeos::DisplayPowerState state,std::vector<bool> * display_power)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 
DisplayState()92 DisplayConfigurator::DisplayState::DisplayState()
93     : display(NULL),
94       touch_device_id(0),
95       selected_mode(NULL),
96       mirror_mode(NULL) {}
97 
TriggerConfigureTimeout()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
FindDisplayModeMatchingSize(const DisplaySnapshot & display,const gfx::Size & size)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 
DisplayConfigurator()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 
~DisplayConfigurator()156 DisplayConfigurator::~DisplayConfigurator() {
157   if (native_display_delegate_)
158     native_display_delegate_->RemoveObserver(this);
159 }
160 
SetDelegatesForTesting(scoped_ptr<NativeDisplayDelegate> display_delegate,scoped_ptr<TouchscreenDelegate> touchscreen_delegate)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 
SetInitialDisplayPower(chromeos::DisplayPowerState power_state)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 
Init(bool is_panel_fitting_enabled)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 
InitializeDelegates(scoped_ptr<NativeDisplayDelegate> display_delegate,scoped_ptr<TouchscreenDelegate> touchscreen_delegate)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 
ForceInitialConfigure(uint32_t background_color_argb)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 
IsMirroring() const218 bool DisplayConfigurator::IsMirroring() const {
219   return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
220       (mirroring_controller_ &&
221        mirroring_controller_->SoftwareMirroringEnabled());
222 }
223 
ApplyProtections(const ContentProtections & requests)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
RegisterContentProtectionClient()275 DisplayConfigurator::RegisterContentProtectionClient() {
276   if (!configure_display_)
277     return kInvalidClientId;
278 
279   return next_display_protection_client_id_++;
280 }
281 
UnregisterContentProtectionClient(ContentProtectionClientId client_id)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 
QueryContentProtectionStatus(ContentProtectionClientId client_id,int64_t display_id,uint32_t * link_mask,uint32_t * protection_mask)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 
EnableContentProtection(ContentProtectionClientId client_id,int64_t display_id,uint32_t desired_method_mask)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>
GetAvailableColorCalibrationProfiles(int64_t display_id)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 
SetColorCalibrationProfile(int64_t display_id,ui::ColorCalibrationProfile new_profile)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 
PrepareForExit()429 void DisplayConfigurator::PrepareForExit() {
430   configure_display_ = false;
431 }
432 
SetDisplayPower(chromeos::DisplayPowerState power_state,int flags)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 
SetDisplayMode(MultipleDisplayState new_state)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 
OnConfigurationChanged()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 
AddObserver(Observer * observer)516 void DisplayConfigurator::AddObserver(Observer* observer) {
517   observers_.AddObserver(observer);
518 }
519 
RemoveObserver(Observer * observer)520 void DisplayConfigurator::RemoveObserver(Observer* observer) {
521   observers_.RemoveObserver(observer);
522 }
523 
SuspendDisplays()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 
ResumeDisplays()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 
UpdateCachedDisplays()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 
FindMirrorMode(DisplayState * internal_display,DisplayState * external_display,bool try_panel_fitting,bool preserve_aspect)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 
ConfigureDisplays()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 
NotifyObservers(bool success,MultipleDisplayState attempted_state)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 
EnterStateOrFallBackToSoftwareMirroring(MultipleDisplayState display_state,chromeos::DisplayPowerState power_state)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 
EnterState(MultipleDisplayState display_state,chromeos::DisplayPowerState power_state)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 
ChooseDisplayState(chromeos::DisplayPowerState power_state) const915 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 
GetMirroredDisplayAreaRatio(const DisplayState & display_state)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