// Copyright 2016 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/test/fake_display_delegate.h"
#include <string>
#include <utility>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/hash/hash.h"
#include "base/logging.h"
#include "base/observer_list.h"
#include "base/strings/string_split.h"
#include "base/time/time.h"
#include "ui/display/display.h"
#include "ui/display/display_switches.h"
#include "ui/display/manager/test/fake_display_snapshot.h"
#include "ui/display/types/display_configuration_params.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/types/native_display_observer.h"
#include "ui/display/util/display_util.h"
namespace display {
namespace {
// The EDID specification marks the top bit of the manufacturer id as reserved.
constexpr uint16_t kReservedManufacturerID = 1 << 15;
// A random product name hash.
constexpr uint32_t kProductCodeHash = 3692486807;
// Delay for Configure().
constexpr base::TimeDelta kConfigureDisplayDelay = base::Milliseconds(200);
bool AreModesEqual(const display::DisplayMode& lhs,
const display::DisplayMode& rhs) {
return lhs.size() == rhs.size() &&
lhs.is_interlaced() == rhs.is_interlaced() &&
lhs.refresh_rate() == rhs.refresh_rate();
}
} // namespace
FakeDisplayDelegate::FakeDisplayDelegate() = default;
FakeDisplayDelegate::~FakeDisplayDelegate() = default;
int64_t FakeDisplayDelegate::AddDisplay(const gfx::Size& display_size) {
DCHECK(!display_size.IsEmpty());
if (next_display_id_ == 0xFF) {
LOG(ERROR) << "Exceeded display id limit";
return kInvalidDisplayId;
}
int64_t id = GenerateDisplayID(kReservedManufacturerID, kProductCodeHash,
++next_display_id_);
FakeDisplaySnapshot::Builder builder;
builder.SetId(id).SetNativeMode(display_size);
return AddDisplay(builder.Build()) ? id : kInvalidDisplayId;
}
bool FakeDisplayDelegate::AddDisplay(std::unique_ptr<DisplaySnapshot> display) {
DCHECK(display);
int64_t display_id = display->display_id();
// Check there is no existing display with the same id.
for (auto& existing_display : displays_) {
if (existing_display->display_id() == display_id) {
LOG(ERROR) << "Display with id " << display_id << " already exists";
return false;
}
}
DVLOG(1) << "Added display " << display->ToString();
displays_.push_back(std::move(display));
OnConfigurationChanged();
return true;
}
bool FakeDisplayDelegate::RemoveDisplay(int64_t display_id) {
// Find display snapshot with matching id and remove it.
for (auto iter = displays_.begin(); iter != displays_.end(); ++iter) {
if ((*iter)->display_id() == display_id) {
DVLOG(1) << "Removed display " << (*iter)->ToString();
displays_.erase(iter);
OnConfigurationChanged();
return true;
}
}
return false;
}
void FakeDisplayDelegate::Initialize() {
DCHECK(!initialized_);
// The default display will be an internal display with a native resolution
// of 1366x768 if --screen-config not specified on the command line.
std::string command_str = "1366x768/i";
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kScreenConfig))
command_str = command_line->GetSwitchValueASCII(switches::kScreenConfig);
CreateDisplaysFromSpecString(command_str);
initialized_ = true;
}
void FakeDisplayDelegate::TakeDisplayControl(DisplayControlCallback callback) {
std::move(callback).Run(false);
}
void FakeDisplayDelegate::RelinquishDisplayControl(
DisplayControlCallback callback) {
std::move(callback).Run(false);
}
void FakeDisplayDelegate::GetDisplays(GetDisplaysCallback callback) {
std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>> displays;
for (auto& display : displays_)
displays.push_back(display.get());
std::move(callback).Run(displays);
}
void FakeDisplayDelegate::Configure(
const std::vector<display::DisplayConfigurationParams>& config_requests,
ConfigureCallback callback,
display::ModesetFlags modeset_flags) {
bool config_success = true;
for (const auto& config : config_requests) {
bool request_success = false;
if (config.mode) {
// Find display snapshot of display ID.
auto snapshot =
find_if(displays_.begin(), displays_.end(),
[&config](std::unique_ptr<DisplaySnapshot>& snapshot) {
return snapshot->display_id() == config.id;
});
if (snapshot != displays_.end()) {
// Check that config mode is appropriate for the display snapshot.
for (const auto& existing_mode : snapshot->get()->modes()) {
if (AreModesEqual(*existing_mode.get(), *config.mode)) {
request_success = true;
break;
}
}
}
} else {
// This is a request to turn off the display.
request_success = true;
}
config_success &= request_success;
}
configure_callbacks_.push(
base::BindOnce(std::move(callback), config_requests, config_success));
// Start the timer if it's not already running. If there are multiple queued
// configuration requests then ConfigureDone() will handle starting the
// next request.
if (!configure_timer_.IsRunning()) {
configure_timer_.Start(FROM_HERE, kConfigureDisplayDelay, this,
&FakeDisplayDelegate::ConfigureDone);
}
}
void FakeDisplayDelegate::SetHdcpKeyProp(int64_t display_id,
const std::string& key,
SetHdcpKeyPropCallback callback) {
std::move(callback).Run(false);
}
void FakeDisplayDelegate::GetHDCPState(const DisplaySnapshot& output,
GetHDCPStateCallback callback) {
std::move(callback).Run(false, HDCP_STATE_UNDESIRED,
CONTENT_PROTECTION_METHOD_NONE);
}
void FakeDisplayDelegate::SetHDCPState(
const DisplaySnapshot& output,
HDCPState state,
ContentProtectionMethod protection_method,
SetHDCPStateCallback callback) {
std::move(callback).Run(false);
}
void FakeDisplayDelegate::SetColorTemperatureAdjustment(
int64_t display_id,
const ColorTemperatureAdjustment& cta) {}
void FakeDisplayDelegate::SetColorCalibration(
int64_t display_id,
const ColorCalibration& calibration) {}
void FakeDisplayDelegate::SetGammaAdjustment(int64_t display_id,
const GammaAdjustment& gamma) {}
void FakeDisplayDelegate::SetPrivacyScreen(int64_t display_id,
bool enabled,
SetPrivacyScreenCallback callback) {
std::move(callback).Run(false);
}
void FakeDisplayDelegate::GetSeamlessRefreshRates(
int64_t display_id,
GetSeamlessRefreshRatesCallback callback) const {
std::move(callback).Run(std::nullopt);
}
void FakeDisplayDelegate::AddObserver(NativeDisplayObserver* observer) {
observers_.AddObserver(observer);
}
void FakeDisplayDelegate::RemoveObserver(NativeDisplayObserver* observer) {
observers_.RemoveObserver(observer);
}
FakeDisplayController* FakeDisplayDelegate::GetFakeDisplayController() {
return this;
}
void FakeDisplayDelegate::CreateDisplaysFromSpecString(const std::string& str) {
// Start without any displays.
if (str == "none")
return;
// Split on commas and parse each display string.
for (const std::string& part : base::SplitString(
str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
int64_t id = GenerateDisplayID(kReservedManufacturerID, kProductCodeHash,
next_display_id_);
std::unique_ptr<DisplaySnapshot> snapshot =
FakeDisplaySnapshot::CreateFromSpec(id, part);
if (snapshot) {
AddDisplay(std::move(snapshot));
next_display_id_++;
} else {
LOG(FATAL) << "Bad --" << switches::kScreenConfig << " flag provided.";
}
}
}
void FakeDisplayDelegate::OnConfigurationChanged() {
if (!initialized_)
return;
for (NativeDisplayObserver& observer : observers_)
observer.OnConfigurationChanged();
}
void FakeDisplayDelegate::ConfigureDone() {
DCHECK(!configure_callbacks_.empty());
std::move(configure_callbacks_.front()).Run();
configure_callbacks_.pop();
// If there are more configuration requests waiting then restart the timer.
if (!configure_callbacks_.empty()) {
configure_timer_.Start(FROM_HERE, kConfigureDisplayDelay, this,
&FakeDisplayDelegate::ConfigureDone);
}
}
} // namespace display