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 ¤t_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