// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/display/manager/update_display_configuration_task.h"
#include <memory>
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "display_configurator.h"
#include "ui/display/manager/configure_displays_task.h"
#include "ui/display/manager/display_layout_manager.h"
#include "ui/display/manager/util/display_manager_util.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/types/native_display_delegate.h"
namespace display {
namespace {
// Move all internal panel displays to the front of the display list. Otherwise,
// the list remains in order.
void MoveInternalDisplaysToTheFront(
std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>& displays) {
DisplayConfigurator::DisplayStateList sorted_displays;
// First pass for internal panels.
for (DisplaySnapshot* display : displays) {
if (display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL)
sorted_displays.push_back(display);
}
// Second pass for the rest.
for (DisplaySnapshot* display : displays) {
if (display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL)
continue;
sorted_displays.push_back(display);
}
displays.swap(sorted_displays);
}
bool ResolveOverrides(
const DisplayConfigurator::RefreshRateOverrideMap& refresh_rate_overrides,
std::vector<DisplayConfigureRequest>& requests) {
for (auto& request : requests) {
if (!request.mode) {
continue;
}
auto override_it =
refresh_rate_overrides.find(request.display->display_id());
if (override_it == refresh_rate_overrides.end()) {
continue;
}
request.mode = std::make_unique<const DisplayMode>(
request.mode->size(), request.mode->is_interlaced(),
override_it->second, /*vsync_rate_min=*/std::nullopt);
}
return true;
}
} // namespace
UpdateDisplayConfigurationTask::UpdateDisplayConfigurationTask(
NativeDisplayDelegate* delegate,
DisplayLayoutManager* layout_manager,
MultipleDisplayState new_display_state,
chromeos::DisplayPowerState new_power_state,
int power_flags,
const base::flat_set<int64_t>& new_vrr_state,
const DisplayConfigurator::RefreshRateOverrideMap& refresh_rate_overrides,
bool force_configure,
ConfigurationType configuration_type,
ResponseCallback callback)
: delegate_(delegate),
layout_manager_(layout_manager),
new_display_state_(new_display_state),
new_power_state_(new_power_state),
power_flags_(power_flags),
new_vrr_state_(new_vrr_state),
refresh_rate_overrides_(refresh_rate_overrides),
force_configure_(force_configure),
configuration_type_(configuration_type),
callback_(std::move(callback)),
requesting_displays_(false) {
delegate_->AddObserver(this);
}
UpdateDisplayConfigurationTask::~UpdateDisplayConfigurationTask() {
delegate_->RemoveObserver(this);
}
void UpdateDisplayConfigurationTask::Run() {
requesting_displays_ = true;
delegate_->GetDisplays(
base::BindOnce(&UpdateDisplayConfigurationTask::OnDisplaysUpdated,
weak_ptr_factory_.GetWeakPtr()));
}
void UpdateDisplayConfigurationTask::OnConfigurationChanged() {}
void UpdateDisplayConfigurationTask::OnDisplaySnapshotsInvalidated() {
cached_displays_.clear();
if (!requesting_displays_ && weak_ptr_factory_.HasWeakPtrs()) {
// This task has already been run and getting the displays request is not in
// flight. We need to re-run it to get updated displays snapshots.
weak_ptr_factory_.InvalidateWeakPtrs();
Run();
}
}
void UpdateDisplayConfigurationTask::OnDisplaysUpdated(
const std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>& displays) {
cached_displays_ = displays;
MoveInternalDisplaysToTheFront(cached_displays_);
requesting_displays_ = false;
// If the user hasn't requested a display state, update it using the requested
// power state.
if (new_display_state_ == MULTIPLE_DISPLAY_STATE_INVALID)
new_display_state_ = ChooseDisplayState();
VLOG(1) << "OnDisplaysUpdated: new_display_state="
<< MultipleDisplayStateToString(new_display_state_)
<< " new_power_state=" << DisplayPowerStateToString(new_power_state_)
<< " flags=" << power_flags_
<< " new_vrr_state=" << VrrStateToString(new_vrr_state_)
<< " refresh_rate_overrides="
<< RefreshRateOverrideToString(refresh_rate_overrides_)
<< " force_configure=" << force_configure_
<< " display_count=" << cached_displays_.size();
if (ShouldConfigure()) {
EnterState(base::BindOnce(&UpdateDisplayConfigurationTask::OnStateEntered,
weak_ptr_factory_.GetWeakPtr()));
} else {
// If we don't have to configure then we're sticking with the old
// configuration. Update it such that it reflects in the reported value.
new_power_state_ = layout_manager_->GetPowerState();
FinishConfiguration(true);
}
}
void UpdateDisplayConfigurationTask::EnterState(
ConfigureDisplaysTask::ResponseCallback callback) {
VLOG(2) << "EnterState";
std::vector<DisplayConfigureRequest> requests;
if (!layout_manager_->GetDisplayLayout(cached_displays_, new_display_state_,
new_power_state_, new_vrr_state_,
&requests)) {
std::move(callback).Run(ConfigureDisplaysTask::ERROR);
return;
}
if (!ResolveOverrides(refresh_rate_overrides_, requests)) {
std::move(callback).Run(ConfigureDisplaysTask::ERROR);
return;
}
if (!requests.empty()) {
configure_task_ = std::make_unique<ConfigureDisplaysTask>(
delegate_, requests, std::move(callback), configuration_type_);
configure_task_->Run();
} else {
VLOG(2) << "No displays";
std::move(callback).Run(ConfigureDisplaysTask::SUCCESS);
}
}
void UpdateDisplayConfigurationTask::OnStateEntered(
ConfigureDisplaysTask::Status status) {
bool success = status != ConfigureDisplaysTask::ERROR;
if (new_display_state_ == MULTIPLE_DISPLAY_STATE_MULTI_MIRROR &&
status == ConfigureDisplaysTask::PARTIAL_SUCCESS)
success = false;
if (layout_manager_->GetSoftwareMirroringController()) {
bool enable_software_mirroring = false;
if (!success && new_display_state_ == MULTIPLE_DISPLAY_STATE_MULTI_MIRROR) {
if (layout_manager_->GetDisplayState() !=
MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED ||
layout_manager_->GetPowerState() != new_power_state_ ||
force_configure_) {
new_display_state_ = MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED;
EnterState(base::BindOnce(
&UpdateDisplayConfigurationTask::OnEnableSoftwareMirroring,
weak_ptr_factory_.GetWeakPtr()));
return;
}
success = layout_manager_->GetDisplayState() ==
MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED;
enable_software_mirroring = success;
if (success)
new_display_state_ = MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED;
}
layout_manager_->GetSoftwareMirroringController()->SetSoftwareMirroring(
enable_software_mirroring);
}
FinishConfiguration(success);
}
void UpdateDisplayConfigurationTask::OnEnableSoftwareMirroring(
ConfigureDisplaysTask::Status status) {
bool success = status != ConfigureDisplaysTask::ERROR;
layout_manager_->GetSoftwareMirroringController()->SetSoftwareMirroring(
success);
FinishConfiguration(success);
}
void UpdateDisplayConfigurationTask::FinishConfiguration(bool success) {
base::UmaHistogramBoolean(
"DisplayManager.UpdateDisplayConfigurationTask.Success", success);
std::move(callback_).Run(success, cached_displays_,
cached_unassociated_displays_, new_display_state_,
new_power_state_);
}
bool UpdateDisplayConfigurationTask::ShouldForceDpms() const {
return new_power_state_ != chromeos::DISPLAY_POWER_ALL_OFF &&
(layout_manager_->GetPowerState() != new_power_state_ ||
(power_flags_ & DisplayConfigurator::kSetDisplayPowerForceProbe));
}
bool UpdateDisplayConfigurationTask::ShouldConfigure() const {
if (force_configure_)
return true;
if (cached_displays_.size() == 1 &&
cached_displays_[0]->type() == DISPLAY_CONNECTION_TYPE_INTERNAL)
return true;
if (!(power_flags_ &
DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay))
return true;
if (new_display_state_ != layout_manager_->GetDisplayState())
return true;
// Compare refresh rate overrides with current states.
if (ShouldConfigureRefreshRate()) {
return true;
}
if (ShouldConfigureVrr()) {
return true;
}
return false;
}
MultipleDisplayState UpdateDisplayConfigurationTask::ChooseDisplayState()
const {
int num_displays = cached_displays_.size();
int num_on_displays =
GetDisplayPower(cached_displays_, new_power_state_, nullptr);
if (num_displays == 0)
return MULTIPLE_DISPLAY_STATE_HEADLESS;
if (num_displays == 1 || 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;
}
// Try to use the saved configuration; otherwise, default to extended.
DisplayConfigurator::StateController* state_controller =
layout_manager_->GetStateController();
if (!state_controller)
return MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED;
return state_controller->GetStateForDisplayIds(cached_displays_);
}
bool UpdateDisplayConfigurationTask::ShouldConfigureVrr() const {
for (const DisplaySnapshot* display : cached_displays_) {
if (!display->IsVrrCapable()) {
continue;
}
if (new_vrr_state_.contains(display->display_id()) !=
display->IsVrrEnabled()) {
return true;
}
}
return false;
}
bool UpdateDisplayConfigurationTask::ShouldConfigureRefreshRate() const {
for (const DisplaySnapshot* display : cached_displays_) {
// TODO b/334104991: Refresh rate override is only enabled for internal
// displays.
if (display->type() != DISPLAY_CONNECTION_TYPE_INTERNAL) {
continue;
}
// No mode means display isn't turned on. Refresh rate override should
// not affect whether a display is enabled.
if (!display->current_mode() || !display->native_mode()) {
continue;
}
// Target refresh rate is the native mode's refresh rate, unless an override
// is specified.
float target_refresh_rate = display->native_mode()->refresh_rate();
auto it = refresh_rate_overrides_.find(display->display_id());
if (it != refresh_rate_overrides_.end()) {
target_refresh_rate = it->second;
}
// If the target refresh rate doesn't match the current refresh rate, then
// a configuration is needed.
if (display->current_mode()->refresh_rate() != target_refresh_rate) {
return true;
}
}
// Checked all displays, and none of them require a refresh rate override.
return false;
}
} // namespace display