| Index: ui/display/chromeos/display_configurator.cc
|
| diff --git a/ui/display/chromeos/display_configurator.cc b/ui/display/chromeos/display_configurator.cc
|
| index 01f47fabb038179be68ec56719d92d215caafb95..f912a60a8586d41635e24b33c526ce28e2b3e7a3 100644
|
| --- a/ui/display/chromeos/display_configurator.cc
|
| +++ b/ui/display/chromeos/display_configurator.cc
|
| @@ -10,7 +10,6 @@
|
| #include "base/sys_info.h"
|
| #include "base/time/time.h"
|
| #include "ui/display/chromeos/display_util.h"
|
| -#include "ui/display/chromeos/update_display_configuration_task.h"
|
| #include "ui/display/display_switches.h"
|
| #include "ui/display/types/display_mode.h"
|
| #include "ui/display/types/display_snapshot.h"
|
| @@ -54,347 +53,6 @@
|
| return false;
|
| }
|
| }
|
| -
|
| -////////////////////////////////////////////////////////////////////////////////
|
| -// DisplayConfigurator::DisplayLayoutManagerImpl implementation
|
| -
|
| -class DisplayConfigurator::DisplayLayoutManagerImpl
|
| - : public DisplayLayoutManager {
|
| - public:
|
| - DisplayLayoutManagerImpl(DisplayConfigurator* configurator);
|
| - ~DisplayLayoutManagerImpl() override;
|
| -
|
| - // DisplayConfigurator::DisplayLayoutManager:
|
| - SoftwareMirroringController* GetSoftwareMirroringController() const override;
|
| - StateController* GetStateController() const override;
|
| - MultipleDisplayState GetDisplayState() const override;
|
| - chromeos::DisplayPowerState GetPowerState() const override;
|
| - std::vector<DisplayState> ParseDisplays(
|
| - const std::vector<DisplaySnapshot*>& displays) const override;
|
| - bool GetDisplayLayout(const std::vector<DisplayState>& displays,
|
| - MultipleDisplayState new_display_state,
|
| - chromeos::DisplayPowerState new_power_state,
|
| - std::vector<DisplayConfigureRequest>* requests,
|
| - gfx::Size* framebuffer_size) const override;
|
| -
|
| - private:
|
| - // Helper method for ParseDisplays() that initializes the passed-in
|
| - // displays' |mirror_mode| fields by looking for a mode in |internal_display|
|
| - // and |external_display| having the same resolution. Returns false if a
|
| - // shared mode wasn't found or created.
|
| - //
|
| - // |try_panel_fitting| allows creating a panel-fitting mode for
|
| - // |internal_display| instead of only searching for a matching mode (note that
|
| - // it may lead to a crash if |internal_display| is not capable of panel
|
| - // fitting).
|
| - //
|
| - // |preserve_aspect| limits the search/creation only to the modes having the
|
| - // native aspect ratio of |external_display|.
|
| - bool FindMirrorMode(DisplayState* internal_display,
|
| - DisplayState* external_display,
|
| - bool try_panel_fitting,
|
| - bool preserve_aspect) const;
|
| -
|
| - DisplayConfigurator* configurator_; // Not owned.
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(DisplayLayoutManagerImpl);
|
| -};
|
| -
|
| -DisplayConfigurator::DisplayLayoutManagerImpl::DisplayLayoutManagerImpl(
|
| - DisplayConfigurator* configurator)
|
| - : configurator_(configurator) {
|
| -}
|
| -
|
| -DisplayConfigurator::DisplayLayoutManagerImpl::~DisplayLayoutManagerImpl() {
|
| -}
|
| -
|
| -DisplayConfigurator::SoftwareMirroringController*
|
| -DisplayConfigurator::DisplayLayoutManagerImpl::GetSoftwareMirroringController()
|
| - const {
|
| - return configurator_->mirroring_controller_;
|
| -}
|
| -
|
| -DisplayConfigurator::StateController*
|
| -DisplayConfigurator::DisplayLayoutManagerImpl::GetStateController() const {
|
| - return configurator_->state_controller_;
|
| -}
|
| -
|
| -MultipleDisplayState
|
| -DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayState() const {
|
| - return configurator_->current_display_state_;
|
| -}
|
| -
|
| -chromeos::DisplayPowerState
|
| -DisplayConfigurator::DisplayLayoutManagerImpl::GetPowerState() const {
|
| - return configurator_->current_power_state_;
|
| -}
|
| -
|
| -std::vector<DisplayConfigurator::DisplayState>
|
| -DisplayConfigurator::DisplayLayoutManagerImpl::ParseDisplays(
|
| - const std::vector<DisplaySnapshot*>& snapshots) const {
|
| - std::vector<DisplayState> cached_displays;
|
| - for (auto snapshot : snapshots) {
|
| - DisplayState display_state;
|
| - display_state.display = snapshot;
|
| - cached_displays.push_back(display_state);
|
| - }
|
| -
|
| - // Set |selected_mode| fields.
|
| - for (size_t i = 0; i < cached_displays.size(); ++i) {
|
| - DisplayState* display_state = &cached_displays[i];
|
| - gfx::Size size;
|
| - if (GetStateController() &&
|
| - GetStateController()->GetResolutionForDisplayId(
|
| - display_state->display->display_id(), &size)) {
|
| - display_state->selected_mode =
|
| - FindDisplayModeMatchingSize(*display_state->display, size);
|
| - }
|
| -
|
| - // Fall back to native mode.
|
| - if (!display_state->selected_mode)
|
| - display_state->selected_mode = display_state->display->native_mode();
|
| - }
|
| -
|
| - // Set |mirror_mode| fields.
|
| - if (cached_displays.size() == 2) {
|
| - bool one_is_internal =
|
| - cached_displays[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
|
| - bool two_is_internal =
|
| - cached_displays[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
|
| - int internal_displays =
|
| - (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0);
|
| - DCHECK_LT(internal_displays, 2);
|
| - LOG_IF(WARNING, internal_displays >= 2)
|
| - << "At least two internal displays detected.";
|
| -
|
| - bool can_mirror = false;
|
| - for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
|
| - // Try preserving external display's aspect ratio on the first attempt.
|
| - // If that fails, fall back to the highest matching resolution.
|
| - bool preserve_aspect = attempt == 0;
|
| -
|
| - if (internal_displays == 1) {
|
| - can_mirror = FindMirrorMode(&cached_displays[one_is_internal ? 0 : 1],
|
| - &cached_displays[one_is_internal ? 1 : 0],
|
| - configurator_->is_panel_fitting_enabled_,
|
| - preserve_aspect);
|
| - } else { // if (internal_displays == 0)
|
| - // No panel fitting for external displays, so fall back to exact match.
|
| - can_mirror = FindMirrorMode(&cached_displays[0], &cached_displays[1],
|
| - false, preserve_aspect);
|
| - if (!can_mirror && preserve_aspect) {
|
| - // FindMirrorMode() 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 = FindMirrorMode(&cached_displays[1], &cached_displays[0],
|
| - false, preserve_aspect);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - return cached_displays;
|
| -}
|
| -
|
| -bool DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayLayout(
|
| - const std::vector<DisplayState>& displays,
|
| - MultipleDisplayState new_display_state,
|
| - chromeos::DisplayPowerState new_power_state,
|
| - std::vector<DisplayConfigureRequest>* requests,
|
| - gfx::Size* framebuffer_size) const {
|
| - std::vector<bool> display_power;
|
| - int num_on_displays =
|
| - GetDisplayPower(displays, new_power_state, &display_power);
|
| - VLOG(1) << "EnterState: display="
|
| - << MultipleDisplayStateToString(new_display_state)
|
| - << " power=" << DisplayPowerStateToString(new_power_state);
|
| -
|
| - // Framebuffer dimensions.
|
| - gfx::Size size;
|
| -
|
| - for (size_t i = 0; i < displays.size(); ++i) {
|
| - requests->push_back(DisplayConfigureRequest(
|
| - displays[i].display, displays[i].display->current_mode(),
|
| - gfx::Point()));
|
| - }
|
| -
|
| - switch (new_display_state) {
|
| - case MULTIPLE_DISPLAY_STATE_INVALID:
|
| - NOTREACHED() << "Ignoring request to enter invalid state with "
|
| - << displays.size() << " connected display(s)";
|
| - return false;
|
| - case MULTIPLE_DISPLAY_STATE_HEADLESS:
|
| - if (displays.size() != 0) {
|
| - LOG(WARNING) << "Ignoring request to enter headless mode with "
|
| - << displays.size() << " connected display(s)";
|
| - return false;
|
| - }
|
| - break;
|
| - case MULTIPLE_DISPLAY_STATE_SINGLE: {
|
| - // If there are multiple displays connected, only one should be turned on.
|
| - if (displays.size() != 1 && num_on_displays != 1) {
|
| - LOG(WARNING) << "Ignoring request to enter single mode with "
|
| - << displays.size() << " connected displays and "
|
| - << num_on_displays << " turned on";
|
| - return false;
|
| - }
|
| -
|
| - for (size_t i = 0; i < displays.size(); ++i) {
|
| - const DisplayConfigurator::DisplayState* state = &displays[i];
|
| - (*requests)[i].mode = display_power[i] ? state->selected_mode : NULL;
|
| -
|
| - if (display_power[i] || displays.size() == 1) {
|
| - const DisplayMode* mode_info = state->selected_mode;
|
| - if (!mode_info) {
|
| - LOG(WARNING) << "No selected mode when configuring display: "
|
| - << state->display->ToString();
|
| - return false;
|
| - }
|
| - if (mode_info->size() == gfx::Size(1024, 768)) {
|
| - VLOG(1) << "Potentially misdetecting display(1024x768):"
|
| - << " displays size=" << displays.size()
|
| - << ", num_on_displays=" << num_on_displays
|
| - << ", current size:" << size.width() << "x" << size.height()
|
| - << ", i=" << i << ", display=" << state->display->ToString()
|
| - << ", display_mode=" << mode_info->ToString();
|
| - }
|
| - size = mode_info->size();
|
| - }
|
| - }
|
| - break;
|
| - }
|
| - case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: {
|
| - if (displays.size() != 2 ||
|
| - (num_on_displays != 0 && num_on_displays != 2)) {
|
| - LOG(WARNING) << "Ignoring request to enter mirrored mode with "
|
| - << displays.size() << " connected display(s) and "
|
| - << num_on_displays << " turned on";
|
| - return false;
|
| - }
|
| -
|
| - const DisplayMode* mode_info = displays[0].mirror_mode;
|
| - if (!mode_info) {
|
| - LOG(WARNING) << "No mirror mode when configuring display: "
|
| - << displays[0].display->ToString();
|
| - return false;
|
| - }
|
| - size = mode_info->size();
|
| -
|
| - for (size_t i = 0; i < displays.size(); ++i) {
|
| - const DisplayConfigurator::DisplayState* state = &displays[i];
|
| - (*requests)[i].mode = display_power[i] ? state->mirror_mode : NULL;
|
| - }
|
| - break;
|
| - }
|
| - case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED:
|
| - case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED: {
|
| - if ((new_display_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED &&
|
| - displays.size() != 2) ||
|
| - (new_display_state == MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED &&
|
| - displays.size() <= 2) ||
|
| - (num_on_displays != 0 &&
|
| - num_on_displays != static_cast<int>(displays.size()))) {
|
| - LOG(WARNING) << "Ignoring request to enter extended mode with "
|
| - << displays.size() << " connected display(s) and "
|
| - << num_on_displays << " turned on";
|
| - return false;
|
| - }
|
| -
|
| - for (size_t i = 0; i < displays.size(); ++i) {
|
| - const DisplayConfigurator::DisplayState* state = &displays[i];
|
| - (*requests)[i].origin.set_y(size.height() ? size.height() + kVerticalGap
|
| - : 0);
|
| - (*requests)[i].mode = display_power[i] ? state->selected_mode : NULL;
|
| -
|
| - // Retain the full screen size even if all displays are off so the
|
| - // same desktop configuration can be restored when the displays are
|
| - // turned back on.
|
| - const DisplayMode* mode_info = displays[i].selected_mode;
|
| - if (!mode_info) {
|
| - LOG(WARNING) << "No selected mode when configuring display: "
|
| - << state->display->ToString();
|
| - return false;
|
| - }
|
| -
|
| - size.set_width(std::max<int>(size.width(), mode_info->size().width()));
|
| - size.set_height(size.height() + (size.height() ? kVerticalGap : 0) +
|
| - mode_info->size().height());
|
| - }
|
| - break;
|
| - }
|
| - }
|
| -
|
| - *framebuffer_size = size;
|
| - return true;
|
| -}
|
| -
|
| -bool DisplayConfigurator::DisplayLayoutManagerImpl::FindMirrorMode(
|
| - DisplayState* internal_display,
|
| - DisplayState* external_display,
|
| - bool try_panel_fitting,
|
| - bool preserve_aspect) const {
|
| - const DisplayMode* internal_native_info =
|
| - internal_display->display->native_mode();
|
| - const DisplayMode* external_native_info =
|
| - external_display->display->native_mode();
|
| - if (!internal_native_info || !external_native_info)
|
| - return false;
|
| -
|
| - // Check if some external display resolution can be mirrored on internal.
|
| - // Prefer the modes in the order they're present in DisplaySnapshot, assuming
|
| - // this is the order in which they look better on the monitor.
|
| - for (DisplayModeList::const_iterator external_it =
|
| - external_display->display->modes().begin();
|
| - external_it != external_display->display->modes().end(); ++external_it) {
|
| - const DisplayMode& external_info = **external_it;
|
| - bool is_native_aspect_ratio =
|
| - external_native_info->size().width() * external_info.size().height() ==
|
| - external_native_info->size().height() * external_info.size().width();
|
| - if (preserve_aspect && !is_native_aspect_ratio)
|
| - continue; // Allow only aspect ratio preserving modes for mirroring.
|
| -
|
| - // Try finding an exact match.
|
| - for (DisplayModeList::const_iterator internal_it =
|
| - internal_display->display->modes().begin();
|
| - internal_it != internal_display->display->modes().end();
|
| - ++internal_it) {
|
| - const DisplayMode& internal_info = **internal_it;
|
| - if (internal_info.size().width() == external_info.size().width() &&
|
| - internal_info.size().height() == external_info.size().height() &&
|
| - internal_info.is_interlaced() == external_info.is_interlaced()) {
|
| - internal_display->mirror_mode = *internal_it;
|
| - external_display->mirror_mode = *external_it;
|
| - return true; // Mirror mode found.
|
| - }
|
| - }
|
| -
|
| - // Try to create a matching internal display mode by panel fitting.
|
| - if (try_panel_fitting) {
|
| - // 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->size().width() >=
|
| - external_info.size().width() &&
|
| - internal_native_info->size().height() >=
|
| - external_info.size().height() &&
|
| - !external_info.is_interlaced();
|
| - if (can_fit) {
|
| - configurator_->native_display_delegate_->AddMode(
|
| - *internal_display->display, *external_it);
|
| - internal_display->display->add_mode(*external_it);
|
| - internal_display->mirror_mode = *external_it;
|
| - external_display->mirror_mode = *external_it;
|
| - return true; // Mirror mode created.
|
| - }
|
| - }
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -////////////////////////////////////////////////////////////////////////////////
|
| -// DisplayConfigurator implementation
|
|
|
| // static
|
| const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize(
|
| @@ -444,17 +102,11 @@
|
| mirroring_controller_(NULL),
|
| is_panel_fitting_enabled_(false),
|
| configure_display_(base::SysInfo::IsRunningOnChromeOS()),
|
| - current_display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
|
| + display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
|
| + requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
|
| current_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
|
| - requested_display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
|
| - requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
|
| - requested_power_state_change_(false),
|
| - requested_power_flags_(kSetDisplayPowerNoFlags),
|
| - force_configure_(false),
|
| next_display_protection_client_id_(1),
|
| - display_externally_controlled_(false),
|
| - layout_manager_(new DisplayLayoutManagerImpl(this)),
|
| - weak_ptr_factory_(this) {
|
| + display_externally_controlled_(false) {
|
| }
|
|
|
| DisplayConfigurator::~DisplayConfigurator() {
|
| @@ -472,7 +124,7 @@
|
|
|
| void DisplayConfigurator::SetInitialDisplayPower(
|
| chromeos::DisplayPowerState power_state) {
|
| - DCHECK_EQ(current_display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
|
| + DCHECK_EQ(display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
|
| requested_power_state_ = current_power_state_ = power_state;
|
| }
|
|
|
| @@ -521,25 +173,28 @@
|
| if (!configure_display_ || display_externally_controlled_)
|
| return;
|
|
|
| + native_display_delegate_->GrabServer();
|
| native_display_delegate_->Initialize();
|
|
|
| - // ForceInitialConfigure should be the first configuration so there shouldn't
|
| - // be anything scheduled.
|
| - DCHECK(!configuration_task_);
|
| -
|
| - configuration_task_.reset(new UpdateDisplayConfigurationTask(
|
| - native_display_delegate_.get(), layout_manager_.get(),
|
| - requested_display_state_, requested_power_state_,
|
| - kSetDisplayPowerForceProbe, background_color_argb, true,
|
| - base::Bind(&DisplayConfigurator::OnConfigured,
|
| - weak_ptr_factory_.GetWeakPtr())));
|
| - configuration_task_->Run();
|
| + UpdateCachedDisplays();
|
| + if (cached_displays_.size() > 1 && background_color_argb)
|
| + native_display_delegate_->SetBackgroundColor(background_color_argb);
|
| + const MultipleDisplayState new_state = ChooseDisplayState(
|
| + requested_power_state_);
|
| + const bool success = EnterStateOrFallBackToSoftwareMirroring(
|
| + new_state, requested_power_state_);
|
| +
|
| + // Force the DPMS on chrome startup as the driver doesn't always detect
|
| + // that all displays are on when signing out.
|
| + native_display_delegate_->ForceDPMSOn();
|
| + native_display_delegate_->UngrabServer();
|
| + NotifyObservers(success, new_state);
|
| }
|
|
|
| bool DisplayConfigurator::IsMirroring() const {
|
| - return current_display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
|
| - (mirroring_controller_ &&
|
| - mirroring_controller_->SoftwareMirroringEnabled());
|
| + return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
|
| + (mirroring_controller_ &&
|
| + mirroring_controller_->SoftwareMirroringEnabled());
|
| }
|
|
|
| bool DisplayConfigurator::ApplyProtections(const ContentProtections& requests) {
|
| @@ -761,34 +416,55 @@
|
| configure_display_ = false;
|
| }
|
|
|
| -void DisplayConfigurator::SetDisplayPower(
|
| +bool DisplayConfigurator::SetDisplayPower(
|
| chromeos::DisplayPowerState power_state,
|
| int flags) {
|
| if (!configure_display_ || display_externally_controlled_)
|
| - return;
|
| + return false;
|
|
|
| VLOG(1) << "SetDisplayPower: power_state="
|
| << DisplayPowerStateToString(power_state) << " flags=" << flags
|
| << ", configure timer="
|
| << (configure_timer_.IsRunning() ? "Running" : "Stopped");
|
| - if (power_state == requested_power_state_ &&
|
| + if (power_state == current_power_state_ &&
|
| !(flags & kSetDisplayPowerForceProbe))
|
| - return;
|
| -
|
| - requested_power_state_ = power_state;
|
| - requested_power_state_change_ = true;
|
| - requested_power_flags_ = flags;
|
| -
|
| - RunPendingConfiguration();
|
| -}
|
| -
|
| -void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
|
| + return true;
|
| +
|
| + native_display_delegate_->GrabServer();
|
| + UpdateCachedDisplays();
|
| +
|
| + const MultipleDisplayState new_state = ChooseDisplayState(power_state);
|
| + bool attempted_change = false;
|
| + bool success = false;
|
| +
|
| + bool only_if_single_internal_display =
|
| + flags & kSetDisplayPowerOnlyIfSingleInternalDisplay;
|
| + bool single_internal_display =
|
| + cached_displays_.size() == 1 &&
|
| + cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
|
| + if (single_internal_display || !only_if_single_internal_display) {
|
| + success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state);
|
| + attempted_change = true;
|
| +
|
| + // Force the DPMS on since the driver doesn't always detect that it
|
| + // should turn on. This is needed when coming back from idle suspend.
|
| + if (success && power_state != chromeos::DISPLAY_POWER_ALL_OFF)
|
| + native_display_delegate_->ForceDPMSOn();
|
| + }
|
| +
|
| + native_display_delegate_->UngrabServer();
|
| + if (attempted_change)
|
| + NotifyObservers(success, new_state);
|
| + return success;
|
| +}
|
| +
|
| +bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
|
| if (!configure_display_ || display_externally_controlled_)
|
| - return;
|
| + return false;
|
|
|
| VLOG(1) << "SetDisplayMode: state="
|
| << MultipleDisplayStateToString(new_state);
|
| - if (current_display_state_ == new_state) {
|
| + if (display_state_ == new_state) {
|
| // Cancel software mirroring if the state is moving from
|
| // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
|
| // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
|
| @@ -796,12 +472,17 @@
|
| new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED)
|
| mirroring_controller_->SetSoftwareMirroring(false);
|
| NotifyObservers(true, new_state);
|
| - return;
|
| - }
|
| -
|
| - requested_display_state_ = new_state;
|
| -
|
| - RunPendingConfiguration();
|
| + return true;
|
| + }
|
| +
|
| + native_display_delegate_->GrabServer();
|
| + UpdateCachedDisplays();
|
| + const bool success = EnterStateOrFallBackToSoftwareMirroring(
|
| + new_state, requested_power_state_);
|
| + native_display_delegate_->UngrabServer();
|
| +
|
| + NotifyObservers(success, new_state);
|
| + return success;
|
| }
|
|
|
| void DisplayConfigurator::OnConfigurationChanged() {
|
| @@ -857,90 +538,160 @@
|
| base::Unretained(this)));
|
| }
|
|
|
| +void DisplayConfigurator::UpdateCachedDisplays() {
|
| + std::vector<DisplaySnapshot*> snapshots =
|
| + native_display_delegate_->GetDisplays();
|
| +
|
| + cached_displays_.clear();
|
| + for (size_t i = 0; i < snapshots.size(); ++i) {
|
| + DisplayState display_state;
|
| + display_state.display = snapshots[i];
|
| + cached_displays_.push_back(display_state);
|
| + }
|
| +
|
| + // Set |selected_mode| fields.
|
| + for (size_t i = 0; i < cached_displays_.size(); ++i) {
|
| + DisplayState* display_state = &cached_displays_[i];
|
| + gfx::Size size;
|
| + if (state_controller_ &&
|
| + state_controller_->GetResolutionForDisplayId(
|
| + display_state->display->display_id(), &size)) {
|
| + display_state->selected_mode =
|
| + FindDisplayModeMatchingSize(*display_state->display, size);
|
| + }
|
| +
|
| + // Fall back to native mode.
|
| + if (!display_state->selected_mode)
|
| + display_state->selected_mode = display_state->display->native_mode();
|
| + }
|
| +
|
| + // Set |mirror_mode| fields.
|
| + if (cached_displays_.size() == 2) {
|
| + bool one_is_internal =
|
| + cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
|
| + bool two_is_internal =
|
| + cached_displays_[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
|
| + int internal_displays =
|
| + (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0);
|
| + DCHECK_LT(internal_displays, 2);
|
| + LOG_IF(WARNING, internal_displays == 2)
|
| + << "Two internal displays detected.";
|
| +
|
| + bool can_mirror = false;
|
| + for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
|
| + // Try preserving external display's aspect ratio on the first attempt.
|
| + // If that fails, fall back to the highest matching resolution.
|
| + bool preserve_aspect = attempt == 0;
|
| +
|
| + if (internal_displays == 1) {
|
| + if (one_is_internal) {
|
| + can_mirror = FindMirrorMode(&cached_displays_[0],
|
| + &cached_displays_[1],
|
| + is_panel_fitting_enabled_,
|
| + preserve_aspect);
|
| + } else {
|
| + DCHECK(two_is_internal);
|
| + can_mirror = FindMirrorMode(&cached_displays_[1],
|
| + &cached_displays_[0],
|
| + is_panel_fitting_enabled_,
|
| + preserve_aspect);
|
| + }
|
| + } else { // if (internal_displays == 0)
|
| + // No panel fitting for external displays, so fall back to exact match.
|
| + can_mirror = FindMirrorMode(
|
| + &cached_displays_[0], &cached_displays_[1], false, preserve_aspect);
|
| + if (!can_mirror && preserve_aspect) {
|
| + // FindMirrorMode() 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 = FindMirrorMode(&cached_displays_[1],
|
| + &cached_displays_[0],
|
| + false,
|
| + preserve_aspect);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool DisplayConfigurator::FindMirrorMode(DisplayState* internal_display,
|
| + DisplayState* external_display,
|
| + bool try_panel_fitting,
|
| + bool preserve_aspect) {
|
| + const DisplayMode* internal_native_info =
|
| + internal_display->display->native_mode();
|
| + const DisplayMode* external_native_info =
|
| + external_display->display->native_mode();
|
| + if (!internal_native_info || !external_native_info)
|
| + return false;
|
| +
|
| + // Check if some external display resolution can be mirrored on internal.
|
| + // Prefer the modes in the order they're present in DisplaySnapshot, assuming
|
| + // this is the order in which they look better on the monitor.
|
| + for (DisplayModeList::const_iterator external_it =
|
| + external_display->display->modes().begin();
|
| + external_it != external_display->display->modes().end();
|
| + ++external_it) {
|
| + const DisplayMode& external_info = **external_it;
|
| + bool is_native_aspect_ratio =
|
| + external_native_info->size().width() * external_info.size().height() ==
|
| + external_native_info->size().height() * external_info.size().width();
|
| + if (preserve_aspect && !is_native_aspect_ratio)
|
| + continue; // Allow only aspect ratio preserving modes for mirroring.
|
| +
|
| + // Try finding an exact match.
|
| + for (DisplayModeList::const_iterator internal_it =
|
| + internal_display->display->modes().begin();
|
| + internal_it != internal_display->display->modes().end();
|
| + ++internal_it) {
|
| + const DisplayMode& internal_info = **internal_it;
|
| + if (internal_info.size().width() == external_info.size().width() &&
|
| + internal_info.size().height() == external_info.size().height() &&
|
| + internal_info.is_interlaced() == external_info.is_interlaced()) {
|
| + internal_display->mirror_mode = *internal_it;
|
| + external_display->mirror_mode = *external_it;
|
| + return true; // Mirror mode found.
|
| + }
|
| + }
|
| +
|
| + // Try to create a matching internal display mode by panel fitting.
|
| + if (try_panel_fitting) {
|
| + // 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->size().width() >=
|
| + external_info.size().width() &&
|
| + internal_native_info->size().height() >=
|
| + external_info.size().height() &&
|
| + !external_info.is_interlaced();
|
| + if (can_fit) {
|
| + native_display_delegate_->AddMode(*internal_display->display,
|
| + *external_it);
|
| + internal_display->display->add_mode(*external_it);
|
| + internal_display->mirror_mode = *external_it;
|
| + external_display->mirror_mode = *external_it;
|
| + return true; // Mirror mode created.
|
| + }
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| void DisplayConfigurator::ConfigureDisplays() {
|
| if (!configure_display_ || display_externally_controlled_)
|
| return;
|
|
|
| - force_configure_ = true;
|
| - RunPendingConfiguration();
|
| -}
|
| -
|
| -void DisplayConfigurator::RunPendingConfiguration() {
|
| - // Configuration task is currently running. Do not start a second
|
| - // configuration.
|
| - if (configuration_task_)
|
| - return;
|
| -
|
| - if (!ShouldRunConfigurationTask()) {
|
| - LOG(ERROR) << "Called RunPendingConfiguration without any changes"
|
| - " requested";
|
| - return;
|
| - }
|
| -
|
| - configuration_task_.reset(new UpdateDisplayConfigurationTask(
|
| - native_display_delegate_.get(), layout_manager_.get(),
|
| - requested_display_state_, requested_power_state_, requested_power_flags_,
|
| - 0, force_configure_, base::Bind(&DisplayConfigurator::OnConfigured,
|
| - weak_ptr_factory_.GetWeakPtr())));
|
| -
|
| - // Reset the flags before running the task; otherwise it may end up scheduling
|
| - // another configuration.
|
| - force_configure_ = false;
|
| - requested_power_flags_ = kSetDisplayPowerNoFlags;
|
| - requested_power_state_change_ = false;
|
| - requested_display_state_ = MULTIPLE_DISPLAY_STATE_INVALID;
|
| -
|
| - configuration_task_->Run();
|
| -}
|
| -
|
| -void DisplayConfigurator::OnConfigured(
|
| - bool success,
|
| - const std::vector<DisplayState>& displays,
|
| - const gfx::Size& framebuffer_size,
|
| - MultipleDisplayState new_display_state,
|
| - chromeos::DisplayPowerState new_power_state) {
|
| - VLOG(1) << "OnConfigured: success=" << success << " new_display_state="
|
| - << MultipleDisplayStateToString(new_display_state)
|
| - << " new_power_state=" << DisplayPowerStateToString(new_power_state);
|
| -
|
| - cached_displays_ = displays;
|
| - if (success) {
|
| - current_display_state_ = new_display_state;
|
| - current_power_state_ = new_power_state;
|
| - framebuffer_size_ = framebuffer_size;
|
| - // If the requested power state hasn't changed then make sure that value
|
| - // gets updated as well since the last requested value may have been
|
| - // dependent on certain conditions (ie: if only the internal monitor was
|
| - // present).
|
| - if (!requested_power_state_change_)
|
| - requested_power_state_ = new_power_state;
|
| - }
|
| -
|
| - configuration_task_.reset();
|
| - NotifyObservers(success, new_display_state);
|
| -
|
| - if (success && !configure_timer_.IsRunning() &&
|
| - ShouldRunConfigurationTask()) {
|
| - configure_timer_.Start(FROM_HERE,
|
| - base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
|
| - this, &DisplayConfigurator::RunPendingConfiguration);
|
| - }
|
| -}
|
| -
|
| -bool DisplayConfigurator::ShouldRunConfigurationTask() const {
|
| - if (force_configure_)
|
| - return true;
|
| -
|
| - // Schedule if there is a request to change the display state.
|
| - if (requested_display_state_ != current_display_state_ &&
|
| - requested_display_state_ != MULTIPLE_DISPLAY_STATE_INVALID)
|
| - return true;
|
| -
|
| - // Schedule if there is a request to change the power state.
|
| - if (requested_power_state_change_)
|
| - return true;
|
| -
|
| - return false;
|
| + native_display_delegate_->GrabServer();
|
| + UpdateCachedDisplays();
|
| + const MultipleDisplayState new_state = ChooseDisplayState(
|
| + requested_power_state_);
|
| + const bool success = EnterStateOrFallBackToSoftwareMirroring(
|
| + new_state, requested_power_state_);
|
| + native_display_delegate_->UngrabServer();
|
| +
|
| + NotifyObservers(success, new_state);
|
| }
|
|
|
| void DisplayConfigurator::RestoreRequestedPowerStateAfterResume() {
|
| @@ -961,4 +712,243 @@
|
| }
|
| }
|
|
|
| +bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring(
|
| + MultipleDisplayState display_state,
|
| + chromeos::DisplayPowerState power_state) {
|
| + bool success = EnterState(display_state, power_state);
|
| + if (mirroring_controller_) {
|
| + bool enable_software_mirroring = false;
|
| + if (!success && display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) {
|
| + if (display_state_ != MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED ||
|
| + current_power_state_ != power_state)
|
| + EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, power_state);
|
| + enable_software_mirroring = success =
|
| + display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
|
| + }
|
| + mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring);
|
| + }
|
| + return success;
|
| +}
|
| +
|
| +bool DisplayConfigurator::EnterState(MultipleDisplayState display_state,
|
| + chromeos::DisplayPowerState power_state) {
|
| + std::vector<bool> display_power;
|
| + int num_on_displays =
|
| + GetDisplayPower(cached_displays_, power_state, &display_power);
|
| + VLOG(1) << "EnterState: display="
|
| + << MultipleDisplayStateToString(display_state)
|
| + << " power=" << DisplayPowerStateToString(power_state);
|
| +
|
| + // Save the requested state so we'll try to use it next time even if we fail.
|
| + requested_power_state_ = power_state;
|
| +
|
| + // Framebuffer dimensions.
|
| + gfx::Size size;
|
| +
|
| + std::vector<gfx::Point> new_origins(cached_displays_.size(), gfx::Point());
|
| + std::vector<const DisplayMode*> new_mode;
|
| + for (size_t i = 0; i < cached_displays_.size(); ++i)
|
| + new_mode.push_back(cached_displays_[i].display->current_mode());
|
| +
|
| + switch (display_state) {
|
| + case MULTIPLE_DISPLAY_STATE_INVALID:
|
| + NOTREACHED() << "Ignoring request to enter invalid state with "
|
| + << cached_displays_.size() << " connected display(s)";
|
| + return false;
|
| + case MULTIPLE_DISPLAY_STATE_HEADLESS:
|
| + if (cached_displays_.size() != 0) {
|
| + LOG(WARNING) << "Ignoring request to enter headless mode with "
|
| + << cached_displays_.size() << " connected display(s)";
|
| + return false;
|
| + }
|
| + break;
|
| + case MULTIPLE_DISPLAY_STATE_SINGLE: {
|
| + // If there are multiple displays connected, only one should be turned on.
|
| + if (cached_displays_.size() != 1 && num_on_displays != 1) {
|
| + LOG(WARNING) << "Ignoring request to enter single mode with "
|
| + << cached_displays_.size() << " connected displays and "
|
| + << num_on_displays << " turned on";
|
| + return false;
|
| + }
|
| +
|
| + for (size_t i = 0; i < cached_displays_.size(); ++i) {
|
| + DisplayState* state = &cached_displays_[i];
|
| + new_mode[i] = display_power[i] ? state->selected_mode : NULL;
|
| +
|
| + if (display_power[i] || cached_displays_.size() == 1) {
|
| + const DisplayMode* mode_info = state->selected_mode;
|
| + if (!mode_info) {
|
| + LOG(WARNING) << "No selected mode when configuring display: "
|
| + << state->display->ToString();
|
| + return false;
|
| + }
|
| + if (mode_info->size() == gfx::Size(1024, 768)) {
|
| + VLOG(1) << "Potentially misdetecting display(1024x768):"
|
| + << " displays size=" << cached_displays_.size()
|
| + << ", num_on_displays=" << num_on_displays
|
| + << ", current size:" << size.width() << "x" << size.height()
|
| + << ", i=" << i << ", display=" << state->display->ToString()
|
| + << ", display_mode=" << mode_info->ToString();
|
| + }
|
| + size = mode_info->size();
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: {
|
| + if (cached_displays_.size() != 2 ||
|
| + (num_on_displays != 0 && num_on_displays != 2)) {
|
| + LOG(WARNING) << "Ignoring request to enter mirrored mode with "
|
| + << cached_displays_.size() << " connected display(s) and "
|
| + << num_on_displays << " turned on";
|
| + return false;
|
| + }
|
| +
|
| + const DisplayMode* mode_info = cached_displays_[0].mirror_mode;
|
| + if (!mode_info) {
|
| + LOG(WARNING) << "No mirror mode when configuring display: "
|
| + << cached_displays_[0].display->ToString();
|
| + return false;
|
| + }
|
| + size = mode_info->size();
|
| +
|
| + for (size_t i = 0; i < cached_displays_.size(); ++i) {
|
| + DisplayState* state = &cached_displays_[i];
|
| + new_mode[i] = display_power[i] ? state->mirror_mode : NULL;
|
| + }
|
| + break;
|
| + }
|
| + case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED:
|
| + case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED: {
|
| + if ((display_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED &&
|
| + cached_displays_.size() != 2) ||
|
| + (display_state == MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED &&
|
| + cached_displays_.size() <= 2) ||
|
| + (num_on_displays != 0 &&
|
| + num_on_displays != static_cast<int>(cached_displays_.size()))) {
|
| + LOG(WARNING) << "Ignoring request to enter extended mode with "
|
| + << cached_displays_.size() << " connected display(s) and "
|
| + << num_on_displays << " turned on";
|
| + return false;
|
| + }
|
| +
|
| + for (size_t i = 0; i < cached_displays_.size(); ++i) {
|
| + DisplayState* state = &cached_displays_[i];
|
| + new_origins[i].set_y(size.height() ? size.height() + kVerticalGap : 0);
|
| + new_mode[i] = display_power[i] ? state->selected_mode : NULL;
|
| +
|
| + // Retain the full screen size even if all displays are off so the
|
| + // same desktop configuration can be restored when the displays are
|
| + // turned back on.
|
| + const DisplayMode* mode_info = cached_displays_[i].selected_mode;
|
| + if (!mode_info) {
|
| + LOG(WARNING) << "No selected mode when configuring display: "
|
| + << state->display->ToString();
|
| + return false;
|
| + }
|
| +
|
| + size.set_width(std::max<int>(size.width(), mode_info->size().width()));
|
| + size.set_height(size.height() + (size.height() ? kVerticalGap : 0) +
|
| + mode_info->size().height());
|
| + }
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // Finally, apply the desired changes.
|
| + bool all_succeeded = true;
|
| + if (!cached_displays_.empty()) {
|
| + native_display_delegate_->CreateFrameBuffer(size);
|
| + for (size_t i = 0; i < cached_displays_.size(); ++i) {
|
| + const DisplayState& state = cached_displays_[i];
|
| + bool configure_succeeded = false;
|
| +
|
| + while (true) {
|
| + if (native_display_delegate_->Configure(
|
| + *state.display, new_mode[i], new_origins[i])) {
|
| + state.display->set_current_mode(new_mode[i]);
|
| + state.display->set_origin(new_origins[i]);
|
| +
|
| + configure_succeeded = true;
|
| + break;
|
| + }
|
| +
|
| + const DisplayMode* mode_info = new_mode[i];
|
| + if (!mode_info)
|
| + break;
|
| +
|
| + // Find the mode with the next-best resolution and see if that can
|
| + // be set.
|
| + int best_mode_pixels = 0;
|
| +
|
| + int current_mode_pixels = mode_info->size().GetArea();
|
| + for (DisplayModeList::const_iterator it =
|
| + state.display->modes().begin();
|
| + it != state.display->modes().end();
|
| + it++) {
|
| + int pixel_count = (*it)->size().GetArea();
|
| + if ((pixel_count < current_mode_pixels) &&
|
| + (pixel_count > best_mode_pixels)) {
|
| + new_mode[i] = *it;
|
| + best_mode_pixels = pixel_count;
|
| + }
|
| + }
|
| +
|
| + if (best_mode_pixels == 0)
|
| + break;
|
| + }
|
| +
|
| + if (!configure_succeeded)
|
| + all_succeeded = false;
|
| +
|
| + // If we are trying to set mirror mode and one of the modesets fails,
|
| + // then the two monitors will be mis-matched. In this case, return
|
| + // false to let the observers be aware.
|
| + if (display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR &&
|
| + display_power[i] &&
|
| + state.display->current_mode() != state.mirror_mode)
|
| + all_succeeded = false;
|
| + }
|
| + }
|
| +
|
| + if (all_succeeded) {
|
| + display_state_ = display_state;
|
| + current_power_state_ = power_state;
|
| + framebuffer_size_ = size;
|
| + }
|
| + return all_succeeded;
|
| +}
|
| +
|
| +MultipleDisplayState DisplayConfigurator::ChooseDisplayState(
|
| + chromeos::DisplayPowerState power_state) const {
|
| + int num_on_displays = GetDisplayPower(cached_displays_, power_state, NULL);
|
| + switch (cached_displays_.size()) {
|
| + case 0:
|
| + return MULTIPLE_DISPLAY_STATE_HEADLESS;
|
| + case 1:
|
| + return MULTIPLE_DISPLAY_STATE_SINGLE;
|
| + default: {
|
| + if (num_on_displays == 1) {
|
| + // If only one display is currently turned on, return the "single"
|
| + // state so that its native mode will be used.
|
| + return MULTIPLE_DISPLAY_STATE_SINGLE;
|
| + } if (num_on_displays >= 3) {
|
| + return MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED;
|
| + } else if (cached_displays_.size() == 2) {
|
| + if (!state_controller_)
|
| + return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
|
| + // With either both displays on or both displays off, use one of the
|
| + // dual modes.
|
| + std::vector<int64_t> display_ids;
|
| + for (size_t i = 0; i < cached_displays_.size(); ++i)
|
| + display_ids.push_back(cached_displays_[i].display->display_id());
|
| +
|
| + return state_controller_->GetStateForDisplayIds(display_ids);
|
| + }
|
| + NOTREACHED();
|
| + }
|
| + }
|
| + return MULTIPLE_DISPLAY_STATE_INVALID;
|
| +}
|
| +
|
| } // namespace ui
|
|
|