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