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