Chromium Code Reviews| Index: chromeos/display/output_configurator.cc |
| diff --git a/chromeos/display/output_configurator.cc b/chromeos/display/output_configurator.cc |
| index 78cb31869d4bca60403cbc4c4909b547e95d431a..2b1d9916d430fd06179e492eaf03b384a2268cb4 100644 |
| --- a/chromeos/display/output_configurator.cc |
| +++ b/chromeos/display/output_configurator.cc |
| @@ -101,12 +101,17 @@ bool IsProjecting( |
| OutputConfigurator::ModeInfo::ModeInfo() |
| : width(0), |
| height(0), |
| - interlaced(false) {} |
| + interlaced(false), |
| + refresh_rate(0.0) {} |
| -OutputConfigurator::ModeInfo::ModeInfo(int width, int height, bool interlaced) |
| +OutputConfigurator::ModeInfo::ModeInfo(int width, |
| + int height, |
| + bool interlaced, |
| + float refresh_rate) |
| : width(width), |
| height(height), |
| - interlaced(interlaced) {} |
| + interlaced(interlaced), |
| + refresh_rate(refresh_rate) {} |
| OutputConfigurator::CoordinateTransformation::CoordinateTransformation() |
| : x_scale(1.0), |
| @@ -172,7 +177,7 @@ const OutputConfigurator::ModeInfo* OutputConfigurator::GetModeInfo( |
| if (mode == None) |
| return NULL; |
| - std::map<RRMode, ModeInfo>::const_iterator it = output.mode_infos.find(mode); |
| + ModeInfoMap::const_iterator it = output.mode_infos.find(mode); |
| if (it == output.mode_infos.end()) { |
| LOG(WARNING) << "Unable to find info about mode " << mode |
| << " for output " << output.output; |
| @@ -181,9 +186,44 @@ const OutputConfigurator::ModeInfo* OutputConfigurator::GetModeInfo( |
| return &it->second; |
| } |
| +// static |
| +RRMode OutputConfigurator::FindOutputModeMatchingSize( |
| + const OutputSnapshot& output, |
| + int width, |
| + int height) { |
| + RRMode found = None; |
| + float best_rate = 0; |
| + bool non_interlaced_found = false; |
| + for (ModeInfoMap::const_iterator it = output.mode_infos.begin(); |
| + it != output.mode_infos.end(); ++it) { |
| + RRMode mode = it->first; |
| + const ModeInfo& info = it->second; |
| + |
| + if (info.width == width && info.height == height) { |
| + if (info.interlaced) { |
| + if (non_interlaced_found) |
| + continue; |
| + } else { |
| + // Reset the best rate if the non interlaced is |
| + // found the first time. |
| + if (!non_interlaced_found) |
| + best_rate = info.refresh_rate; |
| + non_interlaced_found = true; |
| + } |
| + if (info.refresh_rate < best_rate) |
| + continue; |
| + |
| + found = mode; |
| + best_rate = info.refresh_rate; |
| + } |
| + } |
| + return found; |
| +} |
| + |
| OutputConfigurator::OutputConfigurator() |
| : state_controller_(NULL), |
| mirroring_controller_(NULL), |
| + is_panel_fitting_enabled_(false), |
| configure_display_(base::chromeos::IsRunningOnChromeOS()), |
| xrandr_event_base_(0), |
| output_state_(STATE_INVALID), |
| @@ -203,12 +243,12 @@ void OutputConfigurator::SetInitialDisplayPower(DisplayPowerState power_state) { |
| } |
| void OutputConfigurator::Init(bool is_panel_fitting_enabled) { |
| + is_panel_fitting_enabled_ = is_panel_fitting_enabled; |
| if (!configure_display_) |
| return; |
| if (!delegate_) |
| delegate_.reset(new RealOutputConfiguratorDelegate()); |
| - delegate_->SetPanelFittingEnabled(is_panel_fitting_enabled); |
| } |
| void OutputConfigurator::Start(uint32 background_color_argb) { |
| @@ -218,8 +258,7 @@ void OutputConfigurator::Start(uint32 background_color_argb) { |
| delegate_->GrabServer(); |
| delegate_->InitXRandRExtension(&xrandr_event_base_); |
| - std::vector<OutputSnapshot> outputs = |
| - delegate_->GetOutputs(state_controller_); |
| + std::vector<OutputSnapshot> outputs = GetOutputs(); |
| if (outputs.size() > 1 && background_color_argb) |
| delegate_->SetBackgroundColor(background_color_argb); |
| const OutputState new_state = GetOutputState(outputs, power_state_); |
| @@ -249,8 +288,7 @@ bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state, |
| return true; |
| delegate_->GrabServer(); |
| - std::vector<OutputSnapshot> outputs = |
| - delegate_->GetOutputs(state_controller_); |
| + std::vector<OutputSnapshot> outputs = GetOutputs(); |
| const OutputState new_state = GetOutputState(outputs, power_state); |
| bool attempted_change = false; |
| @@ -291,8 +329,7 @@ bool OutputConfigurator::SetDisplayMode(OutputState new_state) { |
| } |
| delegate_->GrabServer(); |
| - std::vector<OutputSnapshot> outputs = |
| - delegate_->GetOutputs(state_controller_); |
| + std::vector<OutputSnapshot> outputs = GetOutputs(); |
| const bool success = EnterStateOrFallBackToSoftwareMirroring( |
| new_state, power_state_, outputs); |
| delegate_->UngrabServer(); |
| @@ -418,12 +455,138 @@ void OutputConfigurator::ScheduleConfigureOutputs() { |
| } |
| } |
| +std::vector<OutputConfigurator::OutputSnapshot> |
| +OutputConfigurator::GetOutputs() { |
| + std::vector<OutputSnapshot> outputs = delegate_->GetOutputs(); |
| + |
| + // Set |selected_mode| fields. |
| + for (size_t i = 0; i < outputs.size(); ++i) { |
| + OutputSnapshot* output = &outputs[i]; |
| + if (output->has_display_id) { |
| + int width = 0, height = 0; |
| + if (state_controller_ && |
| + state_controller_->GetResolutionForDisplayId( |
| + output->display_id, &width, &height)) { |
| + output->selected_mode = |
| + FindOutputModeMatchingSize(*output, width, height); |
| + } |
| + } |
| + // Fall back to native mode. |
| + if (output->selected_mode == None) |
| + output->selected_mode = output->native_mode; |
| + } |
| + |
| + // Set |mirror_mode| fields. |
| + if (outputs.size() == 2) { |
| + bool one_is_internal = outputs[0].is_internal; |
| + bool two_is_internal = outputs[1].is_internal; |
| + int internal_outputs = (one_is_internal ? 1 : 0) + |
| + (two_is_internal ? 1 : 0); |
| + DCHECK_LT(internal_outputs, 2); |
| + LOG_IF(WARNING, internal_outputs == 2) |
| + << "Two internal outputs detected."; |
| + |
| + bool can_mirror = false; |
| + for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { |
| + // Try preserving external output's aspect ratio on the first attempt. |
| + // If that fails, fall back to the highest matching resolution. |
| + bool preserve_aspect = attempt == 0; |
| + |
| + if (internal_outputs == 1) { |
| + if (one_is_internal) { |
| + can_mirror = FindOrCreateMirrorMode(&outputs[0], &outputs[1], |
| + is_panel_fitting_enabled_, preserve_aspect); |
| + } else { |
| + DCHECK(two_is_internal); |
| + can_mirror = FindOrCreateMirrorMode(&outputs[1], &outputs[0], |
| + is_panel_fitting_enabled_, preserve_aspect); |
| + } |
| + } else { // if (internal_outputs == 0) |
| + // No panel fitting for external outputs, so fall back to exact match. |
| + can_mirror = FindOrCreateMirrorMode(&outputs[0], &outputs[1], false, |
| + preserve_aspect); |
| + if (!can_mirror && preserve_aspect) { |
| + // FindOrCreateMirrorMode will try to preserve aspect ratio of |
| + // what it thinks is external display, so if it didn't succeed |
| + // with one, maybe it will succeed with the other. This way we |
| + // will have the correct aspect ratio on at least one of them. |
| + can_mirror = FindOrCreateMirrorMode(&outputs[1], &outputs[0], |
| + false, preserve_aspect); |
| + } |
| + } |
| + } |
| + } |
| + |
| + return outputs; |
| +} |
| + |
| +bool OutputConfigurator::FindOrCreateMirrorMode( |
| + OutputSnapshot* internal_output, |
| + OutputSnapshot* external_output, |
| + bool try_creating, |
| + bool preserve_aspect) { |
| + const ModeInfo* internal_native_info = |
| + GetModeInfo(*internal_output, internal_output->native_mode); |
| + const ModeInfo* external_native_info = |
| + GetModeInfo(*external_output, external_output->native_mode); |
| + if (!internal_native_info || !external_native_info) |
| + return false; |
| + |
| + // Check if some external output resolution can be mirrored on internal. |
| + // Prefer the modes in the order that X sorts them, assuming this is the order |
| + // in which they look better on the monitor. |
| + for (ModeInfoMap::const_iterator external_it = |
| + external_output->mode_infos.begin(); |
| + external_it != external_output->mode_infos.end(); ++external_it) { |
| + const ModeInfo& external_info = external_it->second; |
| + bool is_native_aspect_ratio = |
| + external_native_info->width * external_info.height == |
| + external_native_info->height * external_info.width; |
| + if (preserve_aspect && !is_native_aspect_ratio) |
| + continue; // Allow only aspect ratio preserving modes for mirroring. |
| + |
| + // Try finding an exact match. |
| + for (ModeInfoMap::const_iterator internal_it = |
| + internal_output->mode_infos.begin(); |
| + internal_it != internal_output->mode_infos.end(); ++internal_it) { |
| + const ModeInfo& internal_info = internal_it->second; |
| + if (internal_info.width == external_info.width && |
| + internal_info.height == external_info.height && |
| + internal_info.interlaced == external_info.interlaced) { |
| + internal_output->mirror_mode = internal_it->first; |
| + external_output->mirror_mode = external_it->first; |
| + return true; // Mirror mode found. |
| + } |
| + } |
| + |
| + // Try to create a matching internal output mode by panel fitting. |
| + if (try_creating) { |
| + // We can downscale by 1.125, and upscale indefinitely. Downscaling looks |
| + // ugly, so, can fit == can upscale. Also, internal panels don't support |
| + // fitting interlaced modes. |
| + bool can_fit = |
| + internal_native_info->width >= external_info.width && |
| + internal_native_info->height >= external_info.height && |
| + !external_info.interlaced; |
| + if (can_fit) { |
| + RRMode mode = external_it->first; |
| + delegate_->AddOutputMode(internal_output->output, mode); |
| + internal_output->mode_infos.insert(std::make_pair(mode, external_info)); |
|
Daniel Erat
2013/09/17 15:53:27
(Side note: Apart from this line, which is the act
|
| + internal_output->mirror_mode = mode; |
| + external_output->mirror_mode = mode; |
| + return true; // Mirror mode created. |
| + } |
| + } |
| + } |
| + |
| + return false; |
| +} |
| + |
| void OutputConfigurator::ConfigureOutputs() { |
| configure_timer_.reset(); |
| delegate_->GrabServer(); |
| - std::vector<OutputSnapshot> outputs = |
| - delegate_->GetOutputs(state_controller_); |
| + std::vector<OutputSnapshot> outputs = GetOutputs(); |
| const OutputState new_state = GetOutputState(outputs, power_state_); |
| const bool success = EnterStateOrFallBackToSoftwareMirroring( |
| new_state, power_state_, outputs); |