// Copyright 2012 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/display_manager.h"
#include "ash/accelerators/accelerator_commands.h"
#include "ash/accelerometer/accelerometer_reader.h"
#include "ash/accelerometer/accelerometer_types.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/constants/ash_switches.h"
#include "ash/display/cursor_window_controller.h"
#include "ash/display/display_configuration_controller.h"
#include "ash/display/display_util.h"
#include "ash/display/mirror_window_controller.h"
#include "ash/display/mirror_window_test_api.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/display/window_tree_host_manager.h"
#include "ash/root_window_controller.h"
#include "ash/rounded_display/rounded_display_provider.h"
#include "ash/rounded_display/rounded_display_provider_test_api.h"
#include "ash/screen_util.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/format_macros.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/math_constants.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chromeos/ui/base/app_types.h"
#include "chromeos/ui/base/window_properties.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/display/display.h"
#include "ui/display/display_features.h"
#include "ui/display/display_layout.h"
#include "ui/display/display_layout_builder.h"
#include "ui/display/display_observer.h"
#include "ui/display/display_switches.h"
#include "ui/display/manager/display_change_observer.h"
#include "ui/display/manager/display_layout_store.h"
#include "ui/display/manager/display_manager_observer.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/manager/test/fake_display_snapshot.h"
#include "ui/display/manager/test/touch_device_manager_test_api.h"
#include "ui/display/manager/util/display_manager_test_util.h"
#include "ui/display/manager/util/display_manager_util.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/util/display_util.h"
#include "ui/events/devices/touchscreen_device.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/font_render_params.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/overlay_transform.h"
#include "ui/wm/core/compound_event_filter.h"
namespace ash {
using std::string;
using std::vector;
using base::StringPrintf;
namespace {
std::string ToDisplayName(int64_t id) {
return base::StringPrintf("Display-%d", static_cast<int>(id));
}
// Asserts that metrics propagated by DisplayManager and DisplayManagerObserver
// are consistent.
class DisplayManagerObserverValidator : public display::DisplayObserver,
public display::DisplayManagerObserver {
public:
DisplayManagerObserverValidator() {
display_observer_.emplace(this);
display_manager_observation_.Observe(Shell::Get()->display_manager());
}
// display::DisplayObserver:
void OnDisplayAdded(const display::Display& new_display) override {
if (!base::Contains(added_displays_, new_display)) {
added_displays_.push_back(new_display);
}
}
void OnDisplaysRemoved(const display::Displays& removed_displays) override {
for (const auto& display : removed_displays) {
if (!base::Contains(added_displays_, display)) {
removed_displays_.push_back(display);
}
}
}
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) override {
if (!base::Contains(changed_displays_, display)) {
changed_displays_.push_back(display);
}
if (!changed_metrics_.try_emplace(display.id(), changed_metrics).second) {
changed_metrics_[display.id()] |= changed_metrics;
}
}
// display::DisplayManager::Observer:
void OnWillProcessDisplayChanges() override {
// There should not be multiple OnWillProcessDisplayChanges() calls before
// the subsequent call to OnDidProcessDisplayChanges().
EXPECT_FALSE(processing_display_changes_);
processing_display_changes_ = true;
}
void OnDidProcessDisplayChanges(
const DisplayConfigurationChange& configuration_change) override {
EXPECT_TRUE(processing_display_changes_);
EXPECT_TRUE(base::ranges::is_permutation(
added_displays_, configuration_change.added_displays));
EXPECT_TRUE(base::ranges::is_permutation(
removed_displays_, configuration_change.removed_displays));
EXPECT_EQ(changed_metrics_.size(),
configuration_change.display_metrics_changes.size());
for (const auto& change : configuration_change.display_metrics_changes) {
EXPECT_TRUE(base::Contains(changed_metrics_, change.display->id()));
EXPECT_EQ(changed_metrics_[change.display->id()], change.changed_metrics);
}
processing_display_changes_ = false;
added_displays_.clear();
removed_displays_.clear();
changed_displays_.clear();
changed_metrics_.clear();
}
private:
bool processing_display_changes_ = false;
vector<display::Display> added_displays_;
vector<display::Display> removed_displays_;
vector<display::Display> changed_displays_;
base::flat_map<int64_t, uint32_t> changed_metrics_;
std::optional<display::ScopedDisplayObserver> display_observer_;
base::ScopedObservation<display::DisplayManager,
display::DisplayManagerObserver>
display_manager_observation_{this};
};
} // namespace
class DisplayManagerTest : public AshTestBase,
public display::DisplayObserver,
public aura::WindowObserver,
public display::DisplayManagerObserver {
public:
DisplayManagerTest() = default;
DisplayManagerTest(const DisplayManagerTest&) = delete;
DisplayManagerTest& operator=(const DisplayManagerTest&) = delete;
~DisplayManagerTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
display_observer_.emplace(this);
display_manager_observation_.Observe(Shell::Get()->display_manager());
Shell::GetPrimaryRootWindow()->AddObserver(this);
display_manager_observer_validator_.emplace();
}
void TearDown() override {
display_manager_observer_validator_.reset();
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
display_manager_observation_.Reset();
display_observer_.reset();
AshTestBase::TearDown();
}
const vector<display::Display>& changed() const { return changed_; }
const vector<display::Display>& added() const { return added_; }
int32_t changed_metrics() const {
int32_t changed_metrics = 0;
for (const auto& display_metrics : changed_metrics_) {
changed_metrics |= display_metrics.second;
}
return changed_metrics;
}
int32_t changed_metrics(int64_t display_id) const {
return changed_metrics_.at(display_id);
}
string GetCountSummary() const {
return StringPrintf("c%" PRIuS " a%" PRIuS " r%" PRIuS " w%" PRIuS
" d%" PRIuS,
changed_.size(), added_.size(), removed_count_,
will_process_count_, did_process_count_);
}
void reset() {
changed_.clear();
added_.clear();
removed_count_ = will_process_count_ = did_process_count_ = 0U;
changed_metrics_.clear();
root_window_destroyed_ = false;
}
bool root_window_destroyed() const { return root_window_destroyed_; }
display::ManagedDisplayInfo CreateDisplayInfo(int64_t id,
const gfx::Rect& bounds) {
display::ManagedDisplayInfo info = display::CreateDisplayInfo(id, bounds);
// Each display should have at least one native mode.
display::ManagedDisplayMode mode(bounds.size(), /*refresh_rate=*/60.f,
/*is_interlaced=*/true,
/*native=*/true);
info.SetManagedDisplayModes({mode});
return info;
}
const display::ManagedDisplayInfo& GetDisplayInfo(
const display::Display& display) {
return display_manager()->GetDisplayInfo(display.id());
}
const display::ManagedDisplayInfo& GetDisplayInfoAt(int index) {
return GetDisplayInfo(display_manager()->GetDisplayAt(index));
}
const display::Display& GetDisplayForId(int64_t id) {
return display_manager()->GetDisplayForId(id);
}
const display::ManagedDisplayInfo& GetDisplayInfoForId(int64_t id) {
return GetDisplayInfo(display_manager()->GetDisplayForId(id));
}
// display::DisplayObserver:
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) override {
changed_.push_back(display);
if (!changed_metrics_.try_emplace(display.id(), changed_metrics).second)
changed_metrics_[display.id()] |= changed_metrics;
}
void OnDisplayAdded(const display::Display& new_display) override {
added_.push_back(new_display);
}
void OnDisplaysRemoved(const display::Displays& removed_displays) override {
removed_count_ += removed_displays.size();
}
// display::DisplayManager::Observer:
void OnWillProcessDisplayChanges() override { ++will_process_count_; }
void OnDidProcessDisplayChanges(
const DisplayConfigurationChange& configuration_change) override {
++did_process_count_;
}
// aura::WindowObserver overrides:
void OnWindowDestroying(aura::Window* window) override {
if (check_root_window_on_destruction_)
ASSERT_EQ(Shell::GetPrimaryRootWindow(), window);
root_window_destroyed_ = true;
}
// Returns true if there exists any overlapping mirroring displays.
bool OverlappingMirroringDisplaysExist() {
const auto& mirroring_displays =
display_manager()->software_mirroring_display_list();
for (size_t i = 0; i < mirroring_displays.size() - 1; ++i) {
for (size_t j = i + 1; j < mirroring_displays.size(); ++j) {
const gfx::Rect& bounds_1 = mirroring_displays[i].bounds();
const gfx::Rect& bounds_2 = mirroring_displays[j].bounds();
if (bounds_1.Intersects(bounds_2))
return true;
}
}
return false;
}
void SetSoftwareMirrorMode(bool active) {
display_manager()->SetMirrorMode(
active ? display::MirrorMode::kNormal : display::MirrorMode::kOff,
std::nullopt);
base::RunLoop().RunUntilIdle();
}
void disable_check_root_window_on_destruction() {
check_root_window_on_destruction_ = false;
}
base::test::ScopedFeatureList& scoped_feature_list() {
return scoped_features_;
}
private:
vector<display::Display> changed_;
vector<display::Display> added_;
size_t removed_count_ = 0u;
size_t will_process_count_ = 0u;
size_t did_process_count_ = 0u;
bool root_window_destroyed_ = false;
base::flat_map<int64_t, uint32_t> changed_metrics_;
bool check_root_window_on_destruction_ = true;
std::optional<DisplayManagerObserverValidator>
display_manager_observer_validator_;
std::optional<display::ScopedDisplayObserver> display_observer_;
base::ScopedObservation<display::DisplayManager,
display::DisplayManagerObserver>
display_manager_observation_{this};
// Currently `display::features::kRoundedDisplay` feature is used during the
// `ash::Shell` shutdown as we call `AshTestBase::TearDown()`, therefore
// `scoped_features_` needs to outlive the call.
base::test::ScopedFeatureList scoped_features_;
};
TEST_F(DisplayManagerTest,
RoundedDisplayProviderIsOnlyCreatedForEachRoundedDisplay) {
scoped_feature_list().InitAndEnableFeature(
display::features::kRoundedDisplay);
WindowTreeHostManager* window_tree_host_manager =
Shell::Get()->window_tree_host_manager();
// Have 4 displays out of which 2 displays have rounded panels. Value after
// '~' specifies radii of the display's panel.
UpdateDisplay("500x400,400x300~15,400x300~16,500x400");
ASSERT_EQ(4U, display_manager()->GetNumDisplays());
for (auto& display : display_manager()->active_display_list()) {
const display::ManagedDisplayInfo& display_info =
display_manager()->GetDisplayInfo(display.id());
const RoundedDisplayProvider* rounded_display_provider =
window_tree_host_manager->GetRoundedDisplayProvider(display.id());
EXPECT_EQ(!!rounded_display_provider,
!display_info.panel_corners_radii().IsEmpty());
}
}
TEST_F(DisplayManagerTest, RoundedDisplayProviderIsRemovedForRemovedDisplay) {
scoped_feature_list().InitAndEnableFeature(
display::features::kRoundedDisplay);
WindowTreeHostManager* window_tree_host_manager =
Shell::Get()->window_tree_host_manager();
// Have 4 displays out of which 2 displays have rounded panels. Value after
// '~' specifies radii of the display's panel.
UpdateDisplay("500x400,400x300~15,400x300~16,500x400");
ASSERT_EQ(4U, display_manager()->GetNumDisplays());
auto to_be_removed_display_id = display_manager()->GetDisplayAt(2).id();
const RoundedDisplayProvider* rounded_display_provider =
window_tree_host_manager->GetRoundedDisplayProvider(
to_be_removed_display_id);
EXPECT_TRUE(rounded_display_provider);
// Remove one display that had rounded corners.
UpdateDisplay("500x400,400x300~15");
rounded_display_provider =
window_tree_host_manager->GetRoundedDisplayProvider(
to_be_removed_display_id);
EXPECT_FALSE(rounded_display_provider);
}
TEST_F(DisplayManagerTest, UpdateDisplayTest) {
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
// Update primary and add secondary.
UpdateDisplay("100+0-500x400,0+501-400x300");
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ("c1 a1 r0 w1 d1", GetCountSummary());
// Metrics change immediately when new displays set shelf work area insets.
// After that, DisplayManager::OnNativeDisplaysChanged trigger changes of the
// primary display's metrics. So the observed order of changes is [1, 0].
EXPECT_EQ(display_manager()->GetDisplayAt(0).id(), changed()[0].id());
EXPECT_EQ(display_manager()->GetDisplayAt(1).id(), added()[0].id());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), changed()[0].bounds());
// Secondary display is on right.
EXPECT_EQ(gfx::Rect(500, 0, 400, 300), added()[0].bounds());
EXPECT_EQ(gfx::Rect(0, 501, 400, 300),
GetDisplayInfo(added()[0]).bounds_in_native());
reset();
// Delete secondary.
UpdateDisplay("100+0-500x400");
EXPECT_EQ("c0 a0 r1 w1 d1", GetCountSummary());
reset();
// Change primary.
UpdateDisplay("1+1-1000x600");
EXPECT_EQ("c1 a0 r0 w1 d1", GetCountSummary());
EXPECT_EQ(display_manager()->GetDisplayAt(0).id(), changed()[0].id());
EXPECT_EQ(gfx::Rect(0, 0, 1000, 600), changed()[0].bounds());
reset();
// Add secondary.
UpdateDisplay("1+1-1000x600,1002+0-600x400");
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ("c0 a1 r0 w1 d1", GetCountSummary());
EXPECT_EQ(display_manager()->GetDisplayAt(1).id(), added()[0].id());
// Secondary display is on right.
EXPECT_EQ(gfx::Rect(1000, 0, 600, 400), added()[0].bounds());
EXPECT_EQ(gfx::Rect(1002, 0, 600, 400),
GetDisplayInfo(added()[0]).bounds_in_native());
reset();
// Secondary removed, primary changed.
UpdateDisplay("1+1-800x300");
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ("c1 a0 r1 w1 d1", GetCountSummary());
EXPECT_EQ(display_manager()->GetDisplayAt(0).id(), changed()[0].id());
EXPECT_EQ(gfx::Rect(0, 0, 800, 300), changed()[0].bounds());
reset();
// # of display can go to zero when screen is off.
const vector<display::ManagedDisplayInfo> empty;
display_manager()->OnNativeDisplaysChanged(empty);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
// Going to 0 displays doesn't actually change the active display list but the
// detected bit for the previously connected displays is propagated as false.
EXPECT_EQ("c1 a0 r0 w1 d1", GetCountSummary());
EXPECT_FALSE(root_window_destroyed());
// Display configuration stays the same
EXPECT_EQ(gfx::Rect(0, 0, 800, 300),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_FALSE(display_manager()->GetDisplayAt(0).detected());
EXPECT_EQ(changed_metrics(),
display::DisplayObserver::DISPLAY_METRIC_DETECTED);
reset();
// Connect to display again.
UpdateDisplay("1+1-800x300");
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ("c1 a0 r0 w1 d1", GetCountSummary());
EXPECT_FALSE(root_window_destroyed());
EXPECT_EQ(gfx::Rect(800, 300), changed()[0].bounds());
EXPECT_EQ(gfx::Rect(1, 1, 800, 300),
GetDisplayInfo(changed()[0]).bounds_in_native());
EXPECT_TRUE(display_manager()->GetDisplayAt(0).detected());
EXPECT_EQ(changed_metrics(),
display::DisplayObserver::DISPLAY_METRIC_DETECTED);
// Resumed with different resolution.
display_manager()->OnNativeDisplaysChanged(empty);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
reset();
UpdateDisplay("100+100-500x400");
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ("c1 a0 r0 w1 d1", GetCountSummary());
EXPECT_FALSE(root_window_destroyed());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), changed()[0].bounds());
EXPECT_EQ(gfx::Rect(100, 100, 500, 400),
GetDisplayInfo(changed()[0]).bounds_in_native());
EXPECT_TRUE(display_manager()->GetDisplayAt(0).detected());
EXPECT_EQ(changed_metrics(),
(display::DisplayObserver::DISPLAY_METRIC_DETECTED |
display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA));
reset();
// Go back to zero and wake up with multiple displays.
display_manager()->OnNativeDisplaysChanged(empty);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_FALSE(root_window_destroyed());
reset();
// Add secondary.
UpdateDisplay("0+0-1000x600,1000+1000-600x400");
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 1000, 600),
display_manager()->GetDisplayAt(0).bounds());
// Secondary display is on right.
EXPECT_EQ(gfx::Rect(1000, 0, 600, 400),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(1000, 1000, 600, 400),
GetDisplayInfoAt(1).bounds_in_native());
reset();
// Changing primary will update secondary as well.
UpdateDisplay("0+0-800x600,1000+1000-600x400");
EXPECT_EQ("c2 a0 r0 w1 d1", GetCountSummary());
reset();
EXPECT_EQ(gfx::Rect(0, 0, 800, 600),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(800, 0, 600, 400),
display_manager()->GetDisplayAt(1).bounds());
}
// Test recommended zoom factor will be applied to external display when
// connected for the first time.
TEST_F(DisplayManagerTest, UpdateDisplayWithUnseenExternalDisplayTest) {
// Set up internal display and external display.
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
const int external_id_1 = 10;
const display::ManagedDisplayInfo internal_display_info =
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 1920, 1200));
display::ManagedDisplayInfo external_display_info_1 =
CreateDisplayInfo(external_id_1, gfx::Rect(1, 1, 3840, 2160));
const float external_display_dpi_1 = 192.f;
external_display_info_1.set_device_dpi(external_display_dpi_1);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info_1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
// The recommended zoom factor should be applied since this external display
// is connected for the first time.
// The available zoom factors for 3840X2160 are
// {1.f, 1.10f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f, 2.40f}. The expected
// zoom factor = external_display_dpi_1 /
// kRecommendedDefaultExternalDisplayDpi = 192 / 96 = 2, which is available.
const float expect_zoom_factor_1 = 2.f;
EXPECT_EQ(expect_zoom_factor_1,
GetDisplayInfoForId(external_id_1).zoom_factor());
// Update the external display again.
external_display_info_1.set_device_dpi(300.f);
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info_1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// The recommended zoom factor should not be applied since this external
// display is connected before.
EXPECT_EQ(1.f, GetDisplayInfoForId(external_id_1).zoom_factor());
// Test with a new external display with different display dpi.
const int external_id_2 = 20;
display::ManagedDisplayInfo external_display_info_2 =
CreateDisplayInfo(external_id_2, gfx::Rect(1, 1, 3840, 2160));
const float external_display_dpi_2 = 140.f;
external_display_info_2.set_device_dpi(external_display_dpi_2);
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info_2);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// The available zoom factors for 3840X2160 are
// {1.f, 1.10f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f, 2.40f}. The expected
// zoom factor = external_display_dpi_2 /
// kRecommendedDefaultExternalDisplayDpi = 140 / 96 = 1.46, the closest
// available zoom factor is 1.4.
const float expect_zoom_factor_2 = 1.4f;
EXPECT_EQ(expect_zoom_factor_2,
GetDisplayInfoForId(external_id_2).zoom_factor());
// Test with a new external display with a large display dpi.
const int external_id_3 = 30;
display::ManagedDisplayInfo external_display_info_3 =
CreateDisplayInfo(external_id_3, gfx::Rect(1, 1, 3840, 2160));
const float external_display_dpi_3 = 300.f;
external_display_info_3.set_device_dpi(external_display_dpi_3);
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info_3);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// The available zoom factors for 3840X2160 are
// {1.f, 1.10f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f, 2.40f}. The expected
// zoom factor = external_display_dpi_3 /
// kRecommendedDefaultExternalDisplayDpi = 300 / 96 = 3.125, the closest
// available zoom factor is 2.4.
const float expect_zoom_factor_3 = 2.4f;
EXPECT_EQ(expect_zoom_factor_3,
GetDisplayInfoForId(external_id_3).zoom_factor());
// Test with a new external display with a small display dpi.
const int external_id_4 = 40;
display::ManagedDisplayInfo external_display_info_4 =
CreateDisplayInfo(external_id_4, gfx::Rect(1, 1, 3840, 2160));
const float external_display_dpi_4 = 50.f;
external_display_info_4.set_device_dpi(external_display_dpi_4);
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info_4);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// The available zoom factors for 3840X2160 are
// {1.f, 1.10f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f, 2.40f}. The expected
// zoom factor = external_display_dpi_4 /
// kRecommendedDefaultExternalDisplayDpi = 50 / 96 = 0.52, the closest
// available zoom factor is 1.
const float expect_zoom_factor_4 = 1.f;
EXPECT_EQ(expect_zoom_factor_4,
GetDisplayInfoForId(external_id_4).zoom_factor());
}
// Test in emulation mode (use_fullscreen_host_window=false)
TEST_F(DisplayManagerTest, EmulatorTest) {
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
display_manager()->AddRemoveDisplay();
// Add secondary.
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ("c0 a1 r0 w1 d1", GetCountSummary());
reset();
display_manager()->AddRemoveDisplay();
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ("c0 a0 r1 w1 d1", GetCountSummary());
reset();
display_manager()->AddRemoveDisplay();
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ("c0 a1 r0 w1 d1", GetCountSummary());
}
// Tests support for 3 displays.
TEST_F(DisplayManagerTest, UpdateThreeDisplaysWithDefaultLayout) {
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
// Test with three displays. Native origin will not affect ash
// display layout.
UpdateDisplay("0+0-640x480,1000+0-320x200,2000+0-400x300");
EXPECT_EQ(3U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 640, 480),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(640, 0, 320, 200),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(960, 0, 400, 300),
display_manager()->GetDisplayAt(2).bounds());
EXPECT_EQ("c1 a2 r0 w1 d1", GetCountSummary());
// Metrics change immediately when new displays set shelf work area insets.
// After that, DisplayManager::OnNativeDisplaysChanged trigger changes of the
// primary display's metrics. So the observed order of changes is [1, 2, 0].
EXPECT_EQ(display_manager()->GetDisplayAt(0).id(), changed()[0].id());
EXPECT_EQ(display_manager()->GetDisplayAt(1).id(), added()[0].id());
EXPECT_EQ(display_manager()->GetDisplayAt(2).id(), added()[1].id());
EXPECT_EQ(gfx::Rect(0, 0, 640, 480), changed()[0].bounds());
// Secondary and tertiary displays are on right.
EXPECT_EQ(gfx::Rect(640, 0, 320, 200), added()[0].bounds());
EXPECT_EQ(gfx::Rect(1000, 0, 320, 200),
GetDisplayInfo(added()[0]).bounds_in_native());
EXPECT_EQ(gfx::Rect(960, 0, 400, 300), added()[1].bounds());
EXPECT_EQ(gfx::Rect(2000, 0, 400, 300),
GetDisplayInfo(added()[1]).bounds_in_native());
// Verify calling ReconfigureDisplays doesn't change anything.
display_manager()->ReconfigureDisplays();
EXPECT_EQ(3U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 640, 480),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(640, 0, 320, 200),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(960, 0, 400, 300),
display_manager()->GetDisplayAt(2).bounds());
display::DisplayPlacement default_placement(display::DisplayPlacement::BOTTOM,
10);
display_manager()->layout_store()->SetDefaultDisplayPlacement(
default_placement);
// Test with new displays.
UpdateDisplay("640x480");
UpdateDisplay("640x480,320x200,400x300");
EXPECT_EQ(gfx::Rect(0, 0, 640, 480),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(10, 480, 320, 200),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(20, 680, 400, 300),
display_manager()->GetDisplayAt(2).bounds());
}
TEST_F(DisplayManagerTest, LayoutMoreThanThreeDisplaysTest) {
int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 3);
{
// Layout: [2]
// [1][P]
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::LEFT, 10);
builder.AddDisplayPlacement(list[2], list[1],
display::DisplayPlacement::TOP, 10);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("640x480,320x200,400x300");
EXPECT_EQ(3U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 640, 480),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(-320, 10, 320, 200),
display_manager()->GetDisplayAt(1).bounds());
// The above layout causes an overlap between [P] and [2], making [2]'s
// bounds be "-310,-290 400x300" if the overlap is not fixed. The overlap
// must be detected and fixed and [2] is shifted up to remove the overlap.
EXPECT_EQ(gfx::Rect(-310, -300, 400, 300),
display_manager()->GetDisplayAt(2).bounds());
}
{
// Layout: [1]
// [P][2]
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::TOP, 10);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::RIGHT, 10);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("640x480,320x200,400x300");
EXPECT_EQ(3U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 640, 480),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(10, -200, 320, 200),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(640, 10, 400, 300),
display_manager()->GetDisplayAt(2).bounds());
}
{
// Layout: [P]
// [2]
// [1]
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], list[2],
display::DisplayPlacement::BOTTOM, 10);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::BOTTOM, 10);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("640x480,320x200,400x300");
EXPECT_EQ(3U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 640, 480),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(20, 780, 320, 200),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(10, 480, 400, 300),
display_manager()->GetDisplayAt(2).bounds());
}
{
list = display::test::CreateDisplayIdListN(primary_id, 5);
// Layout: [P][2]
// [3][4]
// [1]
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::RIGHT, 10);
builder.AddDisplayPlacement(list[1], list[3],
display::DisplayPlacement::BOTTOM, 10);
builder.AddDisplayPlacement(list[3], list[4],
display::DisplayPlacement::LEFT, 10);
builder.AddDisplayPlacement(list[4], primary_id,
display::DisplayPlacement::BOTTOM, 10);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("640x480,320x200,400x300,300x200,200x100");
EXPECT_EQ(5U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 640, 480),
display_manager()->GetDisplayAt(0).bounds());
// 2nd is right of the primary.
EXPECT_EQ(gfx::Rect(640, 10, 400, 300),
display_manager()->GetDisplayAt(2).bounds());
// 4th is bottom of the primary.
EXPECT_EQ(gfx::Rect(10, 480, 200, 100),
display_manager()->GetDisplayAt(4).bounds());
// 3rd is the left of 4th.
EXPECT_EQ(gfx::Rect(-290, 480, 300, 200),
display_manager()->GetDisplayAt(3).bounds());
// 1st is the bottom of 3rd.
EXPECT_EQ(gfx::Rect(-280, 680, 320, 200),
display_manager()->GetDisplayAt(1).bounds());
}
}
// Makes sure that layouts with overlapped displays are detected and fixed when
// applied.
TEST_F(DisplayManagerTest, NoOverlappedDisplays) {
int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
{
// Layout with multiple overlaps and special cases:
//
// +-----+
// +----+-+6 |
// | 5 | | |
// +----+----+ | |
// | 7 | | | |
// +----+----+-+---+---+-+---------+
// | | P | | 2 |
// +------+ | | +----------+
// | | | | 3 |
// | | | | |
// +--+----+-+-+-+-----+--+ |
// | 1 | | 4 | | |
// | | | +--+-------+
// | | | |
// +--------+ +--------+
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 8);
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::BOTTOM, 50);
builder.AddDisplayPlacement(list[2], list[1],
display::DisplayPlacement::TOP, 300);
builder.AddDisplayPlacement(list[3], list[2],
display::DisplayPlacement::RIGHT, 30);
builder.AddDisplayPlacement(list[4], list[2],
display::DisplayPlacement::BOTTOM, 400);
builder.AddDisplayPlacement(list[5], primary_id,
display::DisplayPlacement::LEFT, -300);
builder.AddDisplayPlacement(list[6], primary_id,
display::DisplayPlacement::TOP, -250);
builder.AddDisplayPlacement(list[7], list[6],
display::DisplayPlacement::LEFT, 250);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay(
"480x400,480x400,480x400,480x400,480x400,480x400,480x400,530x150");
// The resulting layout after overlaps had been removed:
//
//
// +---------+
// | 7 +-----+
// +-+-------+ 6 |
// | 5 | |
// | | |
// | | |
// | |-+---+----+---------+
// | | | P | 2 |
// +-------+ | | +----------+
// | | | 3 |
// | | | |
// +--+-----+-+-------+ |
// | 1 | | |
// | | +----+---+------+
// | | | 4 |
// +-------+ | |
// | |
// +--------+
EXPECT_EQ(8U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 480, 400),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(50, 400, 480, 400),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(480, 0, 480, 400),
display_manager()->GetDisplayAt(2).bounds());
EXPECT_EQ(gfx::Rect(960, 30, 480, 400),
display_manager()->GetDisplayAt(3).bounds());
EXPECT_EQ(gfx::Rect(730, 430, 480, 400),
display_manager()->GetDisplayAt(4).bounds());
EXPECT_EQ(gfx::Rect(-730, -300, 480, 400),
display_manager()->GetDisplayAt(5).bounds());
EXPECT_EQ(gfx::Rect(-250, -400, 480, 400),
display_manager()->GetDisplayAt(6).bounds());
EXPECT_EQ(gfx::Rect(-780, -450, 530, 150),
display_manager()->GetDisplayAt(7).bounds());
// Expect that the displays have been reparented correctly, such that a
// child is always touching its parent.
display::DisplayLayoutBuilder expected_layout_builder(primary_id);
expected_layout_builder.AddDisplayPlacement(
list[1], primary_id, display::DisplayPlacement::BOTTOM, 50);
expected_layout_builder.AddDisplayPlacement(
list[2], list[1], display::DisplayPlacement::TOP, 430);
expected_layout_builder.AddDisplayPlacement(
list[3], list[2], display::DisplayPlacement::RIGHT, 30);
// [4] became a child of [3] instead of [2] as they no longer touch.
expected_layout_builder.AddDisplayPlacement(
list[4], list[3], display::DisplayPlacement::BOTTOM, -230);
// [5] became a child of [6] instead of [P] as they no longer touch.
expected_layout_builder.AddDisplayPlacement(
list[5], list[6], display::DisplayPlacement::LEFT, 100);
expected_layout_builder.AddDisplayPlacement(
list[6], primary_id, display::DisplayPlacement::TOP, -250);
expected_layout_builder.AddDisplayPlacement(
list[7], list[6], display::DisplayPlacement::LEFT, -50);
const display::DisplayLayout& layout =
display_manager()->GetCurrentResolvedDisplayLayout();
EXPECT_TRUE(
layout.HasSamePlacementList(*(expected_layout_builder.Build())));
}
{
// The following is a special case where a child display is closer to the
// origin than its parent. Test that we can handle it successfully without
// introducing a circular dependency.
//
// +---------+
// | P | +---------+
// | | | 3 |
// | | | |
// | | | |
// +------+--+----+ |
// | 1 | | 2 +---------+
// | | | |
// | | | |
// | | | |
// +------+--+----+
//
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 4);
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::BOTTOM, 0);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::BOTTOM, 464);
builder.AddDisplayPlacement(list[3], list[2],
display::DisplayPlacement::RIGHT, -700);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("696x800,696x800,300x800,696x800");
// The expected layout should be:
//
// +---------+
// | P | +---------+
// | | | 3 |
// | | | |
// | | | |
// +---------+-------+ |
// | 1 | 2 +---------+
// | | |
// | | |
// | | |
// +---------+-------+
//
//
EXPECT_EQ(4U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 696, 800),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(0, 800, 696, 800),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(696, 800, 300, 800),
display_manager()->GetDisplayAt(2).bounds());
EXPECT_EQ(gfx::Rect(996, 100, 696, 800),
display_manager()->GetDisplayAt(3).bounds());
// This case if not handled correctly might lead to a cyclic dependency.
// Make sure this doesn't happen.
display::DisplayLayoutBuilder expected_layout_builder(primary_id);
expected_layout_builder.AddDisplayPlacement(
list[1], primary_id, display::DisplayPlacement::BOTTOM, 0);
expected_layout_builder.AddDisplayPlacement(
list[2], primary_id, display::DisplayPlacement::BOTTOM, 696);
expected_layout_builder.AddDisplayPlacement(
list[3], list[2], display::DisplayPlacement::RIGHT, -700);
const display::DisplayLayout& layout =
display_manager()->GetCurrentResolvedDisplayLayout();
EXPECT_TRUE(
layout.HasSamePlacementList(*(expected_layout_builder.Build())));
}
{
// The following is a layout with an overlap to the left of the primary
// display.
//
// +---------+---------+
// | 1 | P |
// | | |
// +---------+ |
// | | |
// +---------+---------+
// | 2 |
// | |
// +---------+
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 3);
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::LEFT, 0);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::LEFT, 250);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("696x500,696x500,696x500");
// The expected layout should be:
//
// +---------+---------+
// | 1 | P |
// | | |
// | | |
// | | |
// +---------+---------+
// | 2 |
// | |
// | |
// | |
// +---------+
EXPECT_EQ(3U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 696, 500),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(-696, 0, 696, 500),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(-696, 500, 696, 500),
display_manager()->GetDisplayAt(2).bounds());
}
{
// The following is a layout with an overlap occurring above the primary
// display.
//
// +------+--+------+
// | 2 | | 1 |
// | | | |
// | | | |
// | | | |
// +------+--+------+
// | P |
// | |
// | |
// | |
// +---------+
//
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 3);
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::TOP, 0);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::TOP, -348);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("696x500,696x500,696x500");
// The expected layout should be:
//
// +---------+---------+
// | 2 | 1 |
// | | |
// | | |
// | | |
// +---------+---------+
// | P |
// | |
// | |
// | |
// +---------+
//
EXPECT_EQ(3U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 696, 500),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(0, -500, 696, 500),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(-696, -500, 696, 500),
display_manager()->GetDisplayAt(2).bounds());
}
}
TEST_F(DisplayManagerTest, NoOverlappedDisplaysNotFitBetweenTwo) {
// +------+--+----+--+------+
// | 1 | | 2 | | 3 |
// | | | | | |
// | | | | | |
// | | | | | |
// +-+----+--+----+--+---+--+
// | P |
// | |
// | |
// | |
// +-------------------+
//
int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 4);
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::TOP, -110);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::TOP, 300);
builder.AddDisplayPlacement(list[3], primary_id,
display::DisplayPlacement::TOP, 600);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("1200x500,600x500,600x500,600x500");
// The expected layout should be:
//
// +---------+---------+---------+
// | 1 | 2 | 3 |
// | | | |
// | | | |
// | | | |
// +-+-------+---------+-+-------+
// | P |
// | |
// | |
// | |
// +-------------------+
//
EXPECT_EQ(4U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 1200, 500),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(-110, -500, 600, 500),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(490, -500, 600, 500),
display_manager()->GetDisplayAt(2).bounds());
EXPECT_EQ(gfx::Rect(1090, -500, 600, 500),
display_manager()->GetDisplayAt(3).bounds());
}
TEST_F(DisplayManagerTest, NoOverlappedDisplaysAfterResolutionChange) {
// Starting with a good layout with no overlaps, test that if the resolution
// of one of the displays is changed, it won't result in any overlaps.
//
// +-------------------+
// | 4 |
// | |
// | |
// | |
// +----+----+---------+----+----+
// | 1 | 2 | 3 |
// | | | |
// | | | |
// | | | |
// +----+----+---------+----+----+
// | p |
// | |
// | |
// | |
// +-------------------+
//
int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 5);
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::TOP, -250);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::TOP, 250);
builder.AddDisplayPlacement(list[3], primary_id,
display::DisplayPlacement::TOP, 750);
builder.AddDisplayPlacement(list[4], list[1], display::DisplayPlacement::TOP,
250);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("1000x500,600x500,600x500,600x500,1000x500");
// There should be no overlap at all.
EXPECT_EQ(5U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 1000, 500),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(-250, -500, 600, 500),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(350, -500, 600, 500),
display_manager()->GetDisplayAt(2).bounds());
EXPECT_EQ(gfx::Rect(950, -500, 600, 500),
display_manager()->GetDisplayAt(3).bounds());
EXPECT_EQ(gfx::Rect(0, -1000, 1000, 500),
display_manager()->GetDisplayAt(4).bounds());
// Change the resolution of display (2) and expect the following layout.
//
// +-------------------+
// | 4 |
// | |
// | |
// | |
// +----+-------------++
// | 2 |
// +---------+ +---------+
// | 1 | | 3 |
// | | | |
// | | | |
// | | | |
// +----+----+-------------++--------+
// | p |
// | |
// | |
// | |
// +-------------------+
//
UpdateDisplay("1000x500,600x500,600x700,600x500,1000x500");
EXPECT_EQ(5U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 1000, 500),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(-250, -500, 600, 500),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(350, -700, 600, 700),
display_manager()->GetDisplayAt(2).bounds());
EXPECT_EQ(gfx::Rect(950, -500, 600, 500),
display_manager()->GetDisplayAt(3).bounds());
EXPECT_EQ(gfx::Rect(0, -1200, 1000, 500),
display_manager()->GetDisplayAt(4).bounds());
}
TEST_F(DisplayManagerTest, NoOverlappedDisplaysWithDetachedDisplays) {
// Detached displays that intersect other non-detached displays.
//
// +---------+---------+---------+
// | 1 | 2 | 3 |
// | | | |
// | | | |
// | | | |
// +----+----+-----+---+----+----+
// | 4, 5 | P |
// | detached | |
// | | |
// +----------+ |
// +-------------------+
//
int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 6);
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::TOP, -250);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::TOP, 250);
builder.AddDisplayPlacement(list[3], primary_id,
display::DisplayPlacement::TOP, 750);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("1000x500,600x500,600x500,600x500,500x400,500x400");
// Detached displays will be de-intersected and reparented appropriately.
//
// +---------+---------+---------+
// | 1 | 2 | 3 |
// | | | |
// | | | |
// | | | |
// +----+----+---------+----+----+
// | P |
// | |
// | |
// | |
// +----------+--------+
// | 4 |
// | |
// | |
// +----------+
// | 5 |
// | |
// | |
// +----------+
//
EXPECT_EQ(6U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 1000, 500),
display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(gfx::Rect(-250, -500, 600, 500),
display_manager()->GetDisplayAt(1).bounds());
EXPECT_EQ(gfx::Rect(350, -500, 600, 500),
display_manager()->GetDisplayAt(2).bounds());
EXPECT_EQ(gfx::Rect(950, -500, 600, 500),
display_manager()->GetDisplayAt(3).bounds());
EXPECT_EQ(gfx::Rect(0, 500, 500, 400),
display_manager()->GetDisplayAt(4).bounds());
EXPECT_EQ(gfx::Rect(0, 900, 500, 400),
display_manager()->GetDisplayAt(5).bounds());
// This case if not handled correctly might lead to a cyclic dependency.
// Make sure this doesn't happen.
display::DisplayLayoutBuilder expected_layout_builder(primary_id);
expected_layout_builder.AddDisplayPlacement(
list[1], primary_id, display::DisplayPlacement::TOP, -250);
expected_layout_builder.AddDisplayPlacement(
list[2], primary_id, display::DisplayPlacement::TOP, 350);
expected_layout_builder.AddDisplayPlacement(
list[3], primary_id, display::DisplayPlacement::TOP, 950);
expected_layout_builder.AddDisplayPlacement(
list[4], primary_id, display::DisplayPlacement::BOTTOM, 0);
expected_layout_builder.AddDisplayPlacement(
list[5], list[4], display::DisplayPlacement::BOTTOM, 0);
const display::DisplayLayout& layout =
display_manager()->GetCurrentResolvedDisplayLayout();
EXPECT_TRUE(layout.HasSamePlacementList(*(expected_layout_builder.Build())));
}
TEST_F(DisplayManagerTest, OverscanInsetsTest) {
UpdateDisplay("0+0-500x400,0+501-400x300");
reset();
ASSERT_EQ(2u, display_manager()->GetNumDisplays());
const display::ManagedDisplayInfo display_info1 = GetDisplayInfoAt(0);
const display::ManagedDisplayInfo display_info2 = GetDisplayInfoAt(1);
display_manager()->SetOverscanInsets(display_info2.id(),
gfx::Insets::TLBR(13, 12, 11, 10));
std::vector<display::Display> changed_displays = changed();
ASSERT_EQ(1u, changed_displays.size());
EXPECT_EQ(display_info2.id(), changed_displays[0].id());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), GetDisplayInfoAt(0).bounds_in_native());
display::ManagedDisplayInfo updated_display_info2 = GetDisplayInfoAt(1);
EXPECT_EQ(gfx::Rect(0, 501, 400, 300),
updated_display_info2.bounds_in_native());
EXPECT_EQ(gfx::Size(378, 276), updated_display_info2.size_in_pixel());
EXPECT_EQ(gfx::Insets::TLBR(13, 12, 11, 10),
updated_display_info2.overscan_insets_in_dip());
display::test::DisplayManagerTestApi display_manager_test(display_manager());
EXPECT_EQ(gfx::Rect(500, 0, 378, 276),
display_manager_test.GetSecondaryDisplay().bounds());
// Make sure that SetOverscanInsets() is idempotent.
display_manager()->SetOverscanInsets(display_info1.id(), gfx::Insets());
display_manager()->SetOverscanInsets(display_info2.id(),
gfx::Insets::TLBR(13, 12, 11, 10));
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), GetDisplayInfoAt(0).bounds_in_native());
updated_display_info2 = GetDisplayInfoAt(1);
EXPECT_EQ(gfx::Rect(0, 501, 400, 300),
updated_display_info2.bounds_in_native());
EXPECT_EQ(gfx::Size(378, 276), updated_display_info2.size_in_pixel());
EXPECT_EQ(gfx::Insets::TLBR(13, 12, 11, 10),
updated_display_info2.overscan_insets_in_dip());
display_manager()->SetOverscanInsets(display_info2.id(),
gfx::Insets::TLBR(10, 11, 12, 13));
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), GetDisplayInfoAt(0).bounds_in_native());
EXPECT_EQ(gfx::Size(376, 278), GetDisplayInfoAt(1).size_in_pixel());
EXPECT_EQ(gfx::Insets::TLBR(10, 11, 12, 13),
GetDisplayInfoAt(1).overscan_insets_in_dip());
// Recreate a new 2nd display. It won't apply the overscan inset because the
// new display has a different ID.
UpdateDisplay("0+0-500x400");
UpdateDisplay("0+0-500x400,0+501-400x300");
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), GetDisplayInfoAt(0).bounds_in_native());
EXPECT_EQ(gfx::Rect(0, 501, 400, 300),
GetDisplayInfoAt(1).bounds_in_native());
// Recreate the displays with the same ID. It should apply the overscan
// inset.
UpdateDisplay("0+0-500x400");
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(display_info1);
display_info_list.push_back(display_info2);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), GetDisplayInfoAt(0).bounds_in_native());
updated_display_info2 = GetDisplayInfoAt(1);
EXPECT_EQ(gfx::Size(376, 278), updated_display_info2.size_in_pixel());
EXPECT_EQ(gfx::Insets::TLBR(10, 11, 12, 13),
updated_display_info2.overscan_insets_in_dip());
// HiDPI but overscan display. The specified insets size should be doubled.
UpdateDisplay("0+0-500x400,0+501-400x300*2");
display_manager()->SetOverscanInsets(display_manager()->GetDisplayAt(1).id(),
gfx::Insets::TLBR(4, 5, 6, 7));
EXPECT_EQ(gfx::Rect(0, 0, 500, 400), GetDisplayInfoAt(0).bounds_in_native());
updated_display_info2 = GetDisplayInfoAt(1);
EXPECT_EQ(gfx::Rect(0, 501, 400, 300),
updated_display_info2.bounds_in_native());
EXPECT_EQ(gfx::Size(376, 280), updated_display_info2.size_in_pixel());
EXPECT_EQ(gfx::Insets::TLBR(4, 5, 6, 7),
updated_display_info2.overscan_insets_in_dip());
EXPECT_EQ(gfx::Insets::TLBR(8, 10, 12, 14),
updated_display_info2.GetOverscanInsetsInPixel());
// Make sure switching primary display applies the overscan offset only once.
Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(
display_manager_test.GetSecondaryDisplay().id());
EXPECT_EQ(gfx::Rect(-500, 0, 500, 400),
display_manager_test.GetSecondaryDisplay().bounds());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayInfo(display_manager_test.GetSecondaryDisplay())
.bounds_in_native());
EXPECT_EQ(gfx::Rect(0, 501, 400, 300),
GetDisplayInfo(display::Screen::GetScreen()->GetPrimaryDisplay())
.bounds_in_native());
EXPECT_EQ(gfx::Rect(0, 0, 188, 140),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
// Make sure just moving the overscan area should property notify observers.
UpdateDisplay("0+0-500x400");
int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display_manager()->SetOverscanInsets(primary_id,
gfx::Insets::TLBR(0, 0, 20, 20));
EXPECT_EQ(gfx::Rect(0, 0, 480, 380),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
reset();
display_manager()->SetOverscanInsets(primary_id, gfx::Insets(10));
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
EXPECT_EQ(gfx::Rect(0, 0, 480, 380),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
reset();
display_manager()->SetOverscanInsets(primary_id, gfx::Insets());
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
}
TEST_F(DisplayManagerTest, ZeroOverscanInsets) {
// Make sure the display change events is emitted for overscan inset changes.
UpdateDisplay("0+0-500x400,0+501-400x300");
ASSERT_EQ(2u, display_manager()->GetNumDisplays());
int64_t display2_id = display_manager()->GetDisplayAt(1).id();
reset();
display_manager()->SetOverscanInsets(display2_id, gfx::Insets());
EXPECT_EQ(0u, changed().size());
reset();
display_manager()->SetOverscanInsets(display2_id,
gfx::Insets::TLBR(1, 0, 0, 0));
ASSERT_EQ(1u, changed().size());
EXPECT_EQ(display2_id, changed()[0].id());
reset();
display_manager()->SetOverscanInsets(display2_id, gfx::Insets());
ASSERT_EQ(1u, changed().size());
EXPECT_EQ(display2_id, changed()[0].id());
}
TEST_F(DisplayManagerTest, TouchCalibrationTest) {
UpdateDisplay("0+0-500x400,0+501-1024x600");
reset();
display::TouchDeviceManager* touch_device_manager =
display_manager()->touch_device_manager();
display::test::TouchDeviceManagerTestApi tdm_test_api(touch_device_manager);
const ui::TouchscreenDevice touchdevice(
11, ui::InputDeviceType::INPUT_DEVICE_USB,
std::string("test touch device"), gfx::Size(123, 456), 1);
ASSERT_EQ(2u, display_manager()->GetNumDisplays());
const display::ManagedDisplayInfo display_info1 = GetDisplayInfoAt(0);
const display::ManagedDisplayInfo display_info2 = GetDisplayInfoAt(1);
EXPECT_FALSE(tdm_test_api.AreAssociated(display_info2, touchdevice));
const display::TouchCalibrationData::CalibrationPointPairQuad
point_pair_quad = {
{std::make_pair(gfx::Point(50, 50), gfx::Point(43, 51)),
std::make_pair(gfx::Point(950, 50), gfx::Point(975, 45)),
std::make_pair(gfx::Point(50, 550), gfx::Point(48, 534)),
std::make_pair(gfx::Point(950, 550), gfx::Point(967, 574))}};
const gfx::Size bounds_at_calibration(display_info2.size_in_pixel());
const display::TouchCalibrationData touch_data(point_pair_quad,
bounds_at_calibration);
// Set the touch calibration data for the secondary display.
display_manager()->SetTouchCalibrationData(
display_info2.id(), point_pair_quad, bounds_at_calibration, touchdevice,
/*apply_spatial_calibration=*/true);
EXPECT_TRUE(tdm_test_api.AreAssociated(display_info2, touchdevice));
EXPECT_EQ(touch_data, touch_device_manager->GetCalibrationData(
touchdevice, display_info2.id()));
// Clearing touch calibration data from the secondary display.
touch_device_manager->ClearTouchCalibrationData(touchdevice,
GetDisplayInfoAt(1).id());
EXPECT_TRUE(touch_device_manager
->GetCalibrationData(touchdevice, GetDisplayInfoAt(1).id())
.IsEmpty());
// Make sure that SetTouchCalibrationData() is idempotent.
display::TouchCalibrationData::CalibrationPointPairQuad point_pair_quad_2 =
point_pair_quad;
point_pair_quad_2[1] =
std::make_pair(gfx::Point(950, 50), gfx::Point(975, 53));
display::TouchCalibrationData touch_data_2(point_pair_quad_2,
bounds_at_calibration);
display_manager()->SetTouchCalibrationData(
display_info2.id(), point_pair_quad_2, bounds_at_calibration, touchdevice,
/*apply_spatial_calibration=*/true);
EXPECT_EQ(touch_data_2, touch_device_manager->GetCalibrationData(
touchdevice, GetDisplayInfoAt(1).id()));
// Recreate a new 2nd display. It won't apply the touch calibration data
// because the new display has a different ID.
UpdateDisplay("0+0-500x400");
UpdateDisplay("0+0-500x400,0+501-400x300");
tdm_test_api.ResetTouchDeviceManager();
// Recreate the displays with the same ID. It should apply the touch
// calibration associated data.
UpdateDisplay("0+0-500x400");
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(display_info1);
display_info_list.push_back(display_info2);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// Make sure multiple touch devices works.
display_manager()->SetTouchCalibrationData(
display_info2.id(), point_pair_quad, bounds_at_calibration, touchdevice,
/*apply_spatial_calibration=*/true);
EXPECT_EQ(touch_data, touch_device_manager->GetCalibrationData(
touchdevice, GetDisplayInfoAt(1).id()));
const ui::TouchscreenDevice touchdevice_2(
12, ui::InputDeviceType::INPUT_DEVICE_USB,
std::string("test touch device 2"), gfx::Size(234, 567), 1);
display_manager()->SetTouchCalibrationData(
display_info2.id(), point_pair_quad_2, bounds_at_calibration,
touchdevice_2, /*apply_spatial_calibration=*/true);
EXPECT_EQ(touch_data_2, touch_device_manager->GetCalibrationData(
touchdevice_2, GetDisplayInfoAt(1).id()));
EXPECT_EQ(touch_data, touch_device_manager->GetCalibrationData(
touchdevice, GetDisplayInfoAt(1).id()));
}
TEST_F(DisplayManagerTest, UpdateDisplayZoomTest) {
// Initialize a display pair.
UpdateDisplay("1920x1080#1280x720|640x480%60, 600x400*2#600x400");
reset();
// The second display has a device scale factor of 2 set.
constexpr float display_2_dsf = 2.0f;
ASSERT_EQ(2u, display_manager()->GetNumDisplays());
const display::ManagedDisplayInfo& info_1 = GetDisplayInfoAt(0);
// The display should have 2 display modes based on the initialization spec.
ASSERT_EQ(2u, info_1.display_modes().size());
const display::ManagedDisplayInfo::ManagedDisplayModeList& modes =
info_1.display_modes();
// Set the display mode.
display::test::SetDisplayResolution(display_manager(), info_1.id(),
modes[0].size());
display_manager()->UpdateDisplays();
// Since no zoom factor or device scale factor has been set on the display,
// the total/effective device scale factor on the display is 1.
EXPECT_EQ(
display_manager()->GetDisplayForId(info_1.id()).device_scale_factor(),
1.f);
float zoom_factor_1 = 2.0f;
display_manager()->UpdateZoomFactor(info_1.id(), zoom_factor_1);
EXPECT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factor_1);
// With the zoom factor set for the display. The effective zoom factor
// returned should have the display zoom taken into consideration.
EXPECT_EQ(
display_manager()->GetDisplayForId(info_1.id()).device_scale_factor(),
zoom_factor_1);
// Update the zoom factor for a different display mode.
float zoom_factor_2 = 1.5f;
display_manager()->UpdateZoomFactor(info_1.id(), zoom_factor_2);
EXPECT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factor_2);
// Change the display mode of the device.
display::test::SetDisplayResolution(display_manager(), info_1.id(),
modes[1].size());
display_manager()->UpdateDisplays();
// Since the display mode was changed, the zoom factor for the display will
// be retrieved from cache. If not available in cache, default to 1.
EXPECT_EQ(
display_manager()->GetDisplayForId(info_1.id()).device_scale_factor(),
1.f);
// When setting the display mode back to the old one, the final effective
// device scale factor should be using the correct zoom factor.
display::test::SetDisplayResolution(display_manager(), info_1.id(),
modes[0].size());
display_manager()->UpdateDisplays();
// Set the zoom factor back to |zoom_factor_2| for first display.
display_manager()->UpdateZoomFactor(info_1.id(), zoom_factor_2);
EXPECT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factor_2);
EXPECT_EQ(
display_manager()->GetDisplayForId(info_1.id()).device_scale_factor(),
zoom_factor_2);
// Update the zoom factor for the second display.
float zoom_factor_3 = 1.25f;
const display::ManagedDisplayInfo& info_2 = GetDisplayInfoAt(1);
display_manager()->UpdateZoomFactor(info_2.id(), zoom_factor_3);
EXPECT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
zoom_factor_3);
EXPECT_EQ(
display_manager()->GetDisplayForId(info_2.id()).device_scale_factor(),
zoom_factor_3 * display_2_dsf);
// Modifying zoom factor for a display should not effect zoom factors of
// other displays.
EXPECT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factor_2);
// Update the zoom factor for display to see if it gets reflected.
display_manager()->UpdateZoomFactor(info_1.id(), zoom_factor_3);
EXPECT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factor_3);
display::test::SetDisplayResolution(display_manager(), info_1.id(),
modes[0].size());
display_manager()->UpdateDisplays();
EXPECT_EQ(
display_manager()->GetDisplayForId(info_1.id()).device_scale_factor(),
zoom_factor_3);
EXPECT_EQ(
display_manager()->GetDisplayForId(info_2.id()).device_scale_factor(),
zoom_factor_3 * display_2_dsf);
}
TEST_F(DisplayManagerTest, ZoomDisplay) {
// Initialize a display pair.
UpdateDisplay("1920x1080#1920x1080|1280x720%60, 2560x1440*2#2560x1440");
reset();
ASSERT_EQ(2u, display_manager()->GetNumDisplays());
const display::ManagedDisplayInfo& info_1 = GetDisplayInfoAt(0);
const display::ManagedDisplayInfo::ManagedDisplayModeList& modes_1 =
info_1.display_modes();
const display::ManagedDisplayInfo& info_2 = GetDisplayInfoAt(1);
const display::ManagedDisplayInfo::ManagedDisplayModeList& modes_2 =
info_2.display_modes();
// Set the display mode for each display.
display::test::SetDisplayResolution(display_manager(), info_1.id(),
modes_1[0].size());
display::test::SetDisplayResolution(display_manager(), info_2.id(),
modes_2[0].size());
display_manager()->UpdateDisplays();
// Enumerate the zoom factors for display.
const std::vector<float> zoom_factors_1 =
display::GetDisplayZoomFactors(modes_1[0]);
// Set the zoom factor to one of the enumerated zoom factors for the said
// display.
const std::size_t zoom_factor_idx_1 = 0;
display_manager()->UpdateZoomFactor(info_1.id(),
zoom_factors_1[zoom_factor_idx_1]);
// Make sure the change was successful.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factors_1[zoom_factor_idx_1]);
// Zoom out the display. This should have no effect, since the display is
// already at the minimum zoom level.
display_manager()->ZoomDisplay(info_1.id(), true /* up */);
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factors_1[zoom_factor_idx_1]);
// Ensure that this call did not modify the zoom value for the other display.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
1.f);
// Zoom in the display.
display_manager()->ZoomDisplay(info_1.id(), false /* up */);
// The zoom factor for the display should be set to the next zoom factor in
// list.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factors_1[zoom_factor_idx_1 + 1]);
// Zoom out the display.
display_manager()->ZoomDisplay(info_1.id(), true /* up */);
// The zoom level should decrease from the previous level.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factors_1[zoom_factor_idx_1]);
// Enumerate the zoom factors for display.
const std::vector<float> zoom_factors_2 =
display::GetDisplayZoomFactors(modes_2[0]);
// Set the zoom factor to one of the enumerated zoom factors for the said
// display.
const std::size_t zoom_factor_idx_2 = zoom_factors_2.size() - 1;
display_manager()->UpdateZoomFactor(info_2.id(),
zoom_factors_2[zoom_factor_idx_2]);
// Make sure the change was successful.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
zoom_factors_2[zoom_factor_idx_2]);
// Zoom in the display. This should have no effect since we are already at
// maximum zoom.
display_manager()->ZoomDisplay(info_2.id(), false /* up */);
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
zoom_factors_2[zoom_factor_idx_2]);
// Zoom out the display
display_manager()->ZoomDisplay(info_2.id(), true /* up */);
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
zoom_factors_2[zoom_factor_idx_2 - 1]);
// Ensure that this call did not modify the zoom value for the other display.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
zoom_factors_1[zoom_factor_idx_1]);
// Reset the zoom value for displays.
display_manager()->ResetDisplayZoom(info_1.id());
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
1.f);
// Resetting the zoom level of one display should not effect the other display
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
zoom_factors_2[zoom_factor_idx_2 - 1]);
// Now reset the zoom value for other display.
display_manager()->ResetDisplayZoom(info_2.id());
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
1.f);
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
1.f);
}
TEST_F(DisplayManagerTest, ZoomFactorMapTest) {
// Initialize a display pair.
UpdateDisplay("2560x1440#1920x1080|1280x720");
reset();
ASSERT_EQ(1u, display_manager()->GetNumDisplays());
const display::ManagedDisplayInfo& info = GetDisplayInfoAt(0);
const display::ManagedDisplayInfo::ManagedDisplayModeList& modes =
info.display_modes();
// Set the display mode.
display::test::SetDisplayResolution(display_manager(), info.id(),
modes[0].size());
display_manager()->UpdateDisplays();
// Set the zoom factor.
float zoom_factor_1 = 1.2f;
display_manager()->UpdateZoomFactor(info.id(), zoom_factor_1);
// Make sure the change was successful.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info.id()).zoom_factor(),
zoom_factor_1);
// Make sure zoom factor is stored in cache.
display::DisplaySizeToZoomFactorMap zoom_factor_map =
display_manager()->GetDisplayInfo(info.id()).zoom_factor_map();
const auto iter1 = zoom_factor_map.find(modes[0].size().ToString());
EXPECT_NE(iter1, zoom_factor_map.end());
EXPECT_EQ(iter1->second, zoom_factor_1);
// Set to a different display mode.
display::test::SetDisplayResolution(display_manager(), info.id(),
modes[1].size());
display_manager()->UpdateDisplays();
// If not available in cache, default zoom factor is 1.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info.id()).zoom_factor(),
1.f);
// Set the zoom factor.
float zoom_factor_2 = 1.5f;
display_manager()->UpdateZoomFactor(info.id(), zoom_factor_2);
// Make sure the change was successful.
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info.id()).zoom_factor(),
zoom_factor_2);
// Make sure zoom factor is stored in cache.
zoom_factor_map =
display_manager()->GetDisplayInfo(info.id()).zoom_factor_map();
const auto iter2 = zoom_factor_map.find(modes[1].size().ToString());
EXPECT_NE(iter2, zoom_factor_map.end());
EXPECT_EQ(iter2->second, zoom_factor_2);
// Set back to previous display mode.
display::test::SetDisplayResolution(display_manager(), info.id(),
modes[0].size());
display_manager()->UpdateDisplays();
// Make sure zoom factor is retrieved from cache.
zoom_factor_map =
display_manager()->GetDisplayInfo(info.id()).zoom_factor_map();
const auto iter3 = zoom_factor_map.find(modes[0].size().ToString());
EXPECT_NE(iter3, zoom_factor_map.end());
EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info.id()).zoom_factor(),
iter3->second);
}
TEST_F(DisplayManagerTest, TestDeviceScaleOnlyChange) {
UpdateDisplay("1000x600");
aura::WindowTreeHost* host = Shell::GetPrimaryRootWindow()->GetHost();
EXPECT_EQ(1, host->compositor()->device_scale_factor());
EXPECT_EQ(gfx::Size(1000, 600),
Shell::GetPrimaryRootWindow()->bounds().size());
EXPECT_EQ("c1 a0 r0 w1 d1", GetCountSummary());
UpdateDisplay("1000x600*2");
EXPECT_EQ(2, host->compositor()->device_scale_factor());
EXPECT_EQ("c2 a0 r0 w2 d2", GetCountSummary());
EXPECT_EQ(gfx::Size(500, 300),
Shell::GetPrimaryRootWindow()->bounds().size());
}
TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
// Disable restoring mirror mode to prevent interference from previous
// display configuration.
display_manager()->set_disable_restoring_mirror_mode_for_test(true);
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
const int external_id = 10;
const int mirror_id = 11;
const int64_t invalid_id = display::kInvalidDisplayId;
const display::ManagedDisplayInfo internal_display_info =
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 500, 400));
const display::ManagedDisplayInfo external_display_info =
CreateDisplayInfo(external_id, gfx::Rect(1, 1, 200, 100));
const display::ManagedDisplayInfo mirroring_display_info =
CreateDisplayInfo(mirror_id, gfx::Rect(0, 0, 500, 400));
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
gfx::Rect default_bounds = display_manager()->GetDisplayAt(0).bounds();
std::vector<display::ManagedDisplayInfo> display_info_list;
// Primary disconnected.
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(default_bounds, display_manager()->GetDisplayAt(0).bounds());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// External connected while primary was disconnected.
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(invalid_id, GetDisplayForId(internal_display_id).id());
EXPECT_EQ(gfx::Rect(1, 1, 200, 100),
GetDisplayInfoForId(external_id).bounds_in_native());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(external_id,
display::Screen::GetScreen()->GetPrimaryDisplay().id());
EXPECT_EQ(internal_display_id, display::Display::InternalDisplayId());
// Primary connected, with different bounds.
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(internal_display_id,
display::Screen::GetScreen()->GetPrimaryDisplay().id());
// This combination is new, so internal display becomes primary.
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
EXPECT_EQ(gfx::Rect(1, 1, 200, 100),
GetDisplayInfoForId(10).bounds_in_native());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(ToDisplayName(internal_display_id),
display_manager()->GetDisplayNameForId(internal_display_id));
// Emulate suspend.
display_info_list.clear();
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
EXPECT_EQ(gfx::Rect(1, 1, 200, 100),
GetDisplayInfoForId(10).bounds_in_native());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(ToDisplayName(internal_display_id),
display_manager()->GetDisplayNameForId(internal_display_id));
// External display has disconnected then resumed.
display_info_list.push_back(internal_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// External display was changed during suspend.
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// suspend...
display_info_list.clear();
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// and resume with different external display.
display_info_list.push_back(internal_display_info);
display_info_list.push_back(CreateDisplayInfo(12, gfx::Rect(1, 1, 200, 100)));
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// mirrored...
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_info_list.push_back(mirroring_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
EXPECT_EQ(11U, display_manager()->GetMirroringDestinationDisplayIdList()[0]);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
// Test display name.
EXPECT_EQ(ToDisplayName(internal_display_id),
display_manager()->GetDisplayNameForId(internal_display_id));
EXPECT_EQ("Display-10", display_manager()->GetDisplayNameForId(10));
EXPECT_EQ("Display-11", display_manager()->GetDisplayNameForId(11));
EXPECT_EQ("Display-12", display_manager()->GetDisplayNameForId(12));
// Default name for the id that doesn't exist.
EXPECT_EQ("Display 100", display_manager()->GetDisplayNameForId(100));
// and exit mirroring.
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
EXPECT_EQ(gfx::Rect(500, 0, 200, 100), GetDisplayForId(10).bounds());
// Turn off internal
display_info_list.clear();
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(invalid_id, GetDisplayForId(internal_display_id).id());
EXPECT_EQ(gfx::Rect(1, 1, 200, 100),
GetDisplayInfoForId(external_id).bounds_in_native());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// Switched to another display
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayInfoForId(internal_display_id).bounds_in_native());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
display_manager()->set_disable_restoring_mirror_mode_for_test(false);
}
// Make sure crash does not happen if add and remove happens at the same time.
// See: crbug.com/414394
TEST_F(DisplayManagerTest, DisplayAddRemoveAtTheSameTime) {
UpdateDisplay("100+0-500x400,0+501-400x300");
display::test::DisplayManagerTestApi display_manager_test(display_manager());
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id = display_manager_test.GetSecondaryDisplay().id();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
// An id which is different from primary and secondary.
const int64_t third_id = display::SynthesizeDisplayIdFromSeed(secondary_id);
display::ManagedDisplayInfo third_info =
CreateDisplayInfo(third_id, gfx::Rect(0, 0, 600, 500));
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(third_info);
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// Secondary secondary_id becomes the primary as it has smaller output index.
EXPECT_EQ(secondary_id, WindowTreeHostManager::GetPrimaryDisplayId());
EXPECT_EQ(third_id, display_manager_test.GetSecondaryDisplay().id());
EXPECT_EQ(gfx::Size(600, 500), GetDisplayForId(third_id).size());
}
TEST_F(DisplayManagerTest, TestNativeDisplaysChangedNoInternal) {
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
// Don't change the display info if all displays are disconnected.
std::vector<display::ManagedDisplayInfo> display_info_list;
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
// Connect another display which will become primary.
const display::ManagedDisplayInfo external_display_info =
CreateDisplayInfo(10, gfx::Rect(1, 1, 200, 100));
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(1, 1, 200, 100),
GetDisplayInfoForId(10).bounds_in_native());
EXPECT_EQ(
gfx::Size(200, 100),
Shell::GetPrimaryRootWindow()->GetHost()->GetBoundsInPixels().size());
}
// TODO(crbug.com/40902297): Fix the test flakiness on MSan.
#if defined(MEMORY_SANITIZER)
#define MAYBE_NativeDisplaysChangedAfterPrimaryChange \
DISABLED_NativeDisplaysChangedAfterPrimaryChange
#else
#define MAYBE_NativeDisplaysChangedAfterPrimaryChange \
NativeDisplaysChangedAfterPrimaryChange
#endif
TEST_F(DisplayManagerTest, MAYBE_NativeDisplaysChangedAfterPrimaryChange) {
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
const display::ManagedDisplayInfo native_display_info =
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 500, 400));
const display::ManagedDisplayInfo secondary_display_info =
CreateDisplayInfo(10, gfx::Rect(1, 1, 200, 100));
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(native_display_info);
display_info_list.push_back(secondary_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
EXPECT_EQ(gfx::Rect(500, 0, 200, 100), GetDisplayForId(10).bounds());
Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(
secondary_display_info.id());
EXPECT_EQ(gfx::Rect(-500, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
EXPECT_EQ(gfx::Rect(0, 0, 200, 100), GetDisplayForId(10).bounds());
// OnNativeDisplaysChanged may change the display bounds. Here makes sure
// nothing changed if the exactly same displays are specified.
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(-500, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
EXPECT_EQ(gfx::Rect(0, 0, 200, 100), GetDisplayForId(10).bounds());
}
TEST_F(DisplayManagerTest, ActiveModeWhenNativeResolutionNotSupported) {
int display_id = 1000;
display::ManagedDisplayInfo native_display_info =
CreateDisplayInfo(display_id, gfx::Rect(0, 0, 800, 300));
native_display_info.set_is_interlaced(false);
native_display_info.set_native(false);
native_display_info.set_refresh_rate(59.0f);
display::ManagedDisplayInfo::ManagedDisplayModeList display_modes;
display_modes.emplace_back(gfx::Size(1000, 500), 58.0f,
/*is_interlaced=*/false, /*native=*/true);
display_modes.emplace_back(gfx::Size(800, 300), 59.0f,
/*is_interlaced=*/false, /*native=*/false);
display_modes.emplace_back(gfx::Size(400, 500), 60.0f,
/*is_interlaced=*/false, /*native=*/false);
native_display_info.SetManagedDisplayModes(display_modes);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(native_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
display::ManagedDisplayMode expected_mode(gfx::Size(800, 300), 59.0f,
/*is_interlaced=*/false,
/*native=*/false);
// Make sure there is no selected mode.
display::ManagedDisplayMode mode;
EXPECT_FALSE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
// Check display info for the active mode to handle the case when native mode
// is not supported.
display::ManagedDisplayMode active_mode;
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(display_id, &active_mode));
EXPECT_TRUE(expected_mode.IsEquivalent(active_mode));
}
TEST_F(DisplayManagerTest, DontRememberBestResolution) {
int display_id = 1000;
display::ManagedDisplayInfo native_display_info =
CreateDisplayInfo(display_id, gfx::Rect(0, 0, 1000, 500));
display::ManagedDisplayInfo::ManagedDisplayModeList display_modes;
display_modes.emplace_back(gfx::Size(1000, 500), 58.0f,
/*is_interlaced=*/false, /*native=*/true);
display_modes.emplace_back(gfx::Size(800, 300), 59.0f,
/*is_interlaced=*/false, /*native=*/false);
display_modes.emplace_back(gfx::Size(400, 500), 60.0f,
/*is_interlaced=*/false, /*native=*/false);
native_display_info.SetManagedDisplayModes(display_modes);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(native_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
display::ManagedDisplayMode expected_mode(gfx::Size(1000, 500), 58.0f,
/*is_interlaced=*/false,
/*native=*/true);
display::ManagedDisplayMode mode;
EXPECT_FALSE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
display::ManagedDisplayMode active_mode;
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(display_id, &active_mode));
EXPECT_TRUE(expected_mode.IsEquivalent(active_mode));
// Unsupported resolution.
display::test::SetDisplayResolution(display_manager(), display_id,
gfx::Size(800, 4000));
EXPECT_FALSE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(display_id, &active_mode));
EXPECT_TRUE(expected_mode.IsEquivalent(active_mode));
// Supported resolution.
display::test::SetDisplayResolution(display_manager(), display_id,
gfx::Size(800, 300));
EXPECT_TRUE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
EXPECT_EQ(gfx::Size(800, 300), mode.size());
EXPECT_EQ(59.0f, mode.refresh_rate());
EXPECT_FALSE(mode.native());
expected_mode = display::ManagedDisplayMode(
gfx::Size(800, 300), 59.0f, /*is_interlaced=*/false, /*native=*/false);
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(display_id, &active_mode));
EXPECT_TRUE(expected_mode.IsEquivalent(active_mode));
// Best resolution.
display::test::SetDisplayResolution(display_manager(), display_id,
gfx::Size(1000, 500));
EXPECT_TRUE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
EXPECT_EQ(gfx::Size(1000, 500), mode.size());
EXPECT_EQ(58.0f, mode.refresh_rate());
EXPECT_TRUE(mode.native());
expected_mode = display::ManagedDisplayMode(
gfx::Size(1000, 500), 58.0f, /*is_interlaced=*/false, /*native=*/true);
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(display_id, &active_mode));
EXPECT_TRUE(expected_mode.IsEquivalent(active_mode));
}
TEST_F(DisplayManagerTest, ResolutionFallback) {
int display_id = 1000;
display::ManagedDisplayInfo native_display_info =
CreateDisplayInfo(display_id, gfx::Rect(0, 0, 1000, 500));
display::ManagedDisplayInfo::ManagedDisplayModeList display_modes;
display_modes.emplace_back(gfx::Size(1000, 500), 60.0f,
/*is_interlaced=*/false, /*native=*/true);
display_modes.emplace_back(gfx::Size(800, 300), 59.0f,
/*is_interlaced=*/false, /*native=*/false);
display_modes.emplace_back(gfx::Size(400, 500), 60.0f,
/*is_interlaced=*/false, /*native=*/false);
native_display_info.SetManagedDisplayModes(display_modes);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(native_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
{
display::test::SetDisplayResolution(display_manager(), display_id,
gfx::Size(800, 300));
display::ManagedDisplayInfo new_native_display_info =
CreateDisplayInfo(display_id, gfx::Rect(0, 0, 400, 500));
new_native_display_info.SetManagedDisplayModes(display_modes);
std::vector<display::ManagedDisplayInfo> new_display_info_list;
new_display_info_list.push_back(new_native_display_info);
display_manager()->OnNativeDisplaysChanged(new_display_info_list);
display::ManagedDisplayMode mode;
EXPECT_TRUE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
EXPECT_EQ(gfx::Size(400, 500), mode.size());
EXPECT_EQ(60.0f, mode.refresh_rate());
EXPECT_FALSE(mode.native());
}
{
// Best resolution should find itself on the resolutions list.
display::test::SetDisplayResolution(display_manager(), display_id,
gfx::Size(800, 300));
display::ManagedDisplayInfo new_native_display_info =
CreateDisplayInfo(display_id, gfx::Rect(0, 0, 1000, 500));
new_native_display_info.set_native(true);
new_native_display_info.SetManagedDisplayModes(display_modes);
std::vector<display::ManagedDisplayInfo> new_display_info_list;
new_display_info_list.push_back(new_native_display_info);
display_manager()->OnNativeDisplaysChanged(new_display_info_list);
display::ManagedDisplayMode mode;
EXPECT_TRUE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
EXPECT_EQ(gfx::Size(1000, 500), mode.size());
EXPECT_EQ(60.0f, mode.refresh_rate());
EXPECT_TRUE(mode.native());
}
}
TEST_F(DisplayManagerTest, DisplayRemovedOnlyOnceWhenEnteringDockedMode) {
// Create two displays, one internal, and one external, such that the full ID
// of the internal display is *greater* than the full ID of the external
// display, but the port-index part (least significant 8-bit) of the ID of the
// internal display is *less* than the port-index part of the external
// display.
constexpr int64_t kInternalDisplayId = 0x4D10DBEBF24802LL;
constexpr int64_t kExternalDisplayId = 0x4CABEF61B95735LL;
const auto internal_info = display::ManagedDisplayInfo::CreateFromSpecWithID(
"0+0-400x300", kInternalDisplayId);
const auto external_info = display::ManagedDisplayInfo::CreateFromSpecWithID(
"401+0-600x500", kExternalDisplayId);
vector<display::ManagedDisplayInfo> display_info_list{internal_info,
external_info};
display_manager()->OnNativeDisplaysChanged(display_info_list);
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
// Switching to docked mode in this configuration should result in only a
// single display removal, and no new display additions.
// https://crbug.com/921275.
reset();
display_info_list.clear();
display_info_list.emplace_back(external_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// There should only be 1 display change, 0 adds, and 1 removal.
EXPECT_EQ("c1 a0 r1 w1 d1", GetCountSummary());
const int expected_changed_metrics =
display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA |
display::DisplayObserver::DISPLAY_METRIC_PRIMARY;
EXPECT_EQ(expected_changed_metrics, changed_metrics());
// Exit docked mode by re-adding the internal display again.
reset();
display_info_list.clear();
display_info_list.emplace_back(internal_info);
display_info_list.emplace_back(external_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// Expect that we get a "primary" change notification.
EXPECT_EQ("c4 a1 r0 w1 d1", GetCountSummary());
EXPECT_EQ(expected_changed_metrics, changed_metrics());
}
TEST_F(DisplayManagerTest, Rotate) {
UpdateDisplay("100x200/r,300x400/l");
EXPECT_EQ(gfx::Rect(1, 1, 100, 200), GetDisplayInfoAt(0).bounds_in_native());
EXPECT_EQ(gfx::Size(200, 100), GetDisplayInfoAt(0).size_in_pixel());
EXPECT_EQ(gfx::Rect(1, 201, 300, 400),
GetDisplayInfoAt(1).bounds_in_native());
EXPECT_EQ(gfx::Size(400, 300), GetDisplayInfoAt(1).size_in_pixel());
reset();
UpdateDisplay("100x200/b,300x400");
EXPECT_EQ("c2 a0 r0 w1 d1", GetCountSummary());
reset();
EXPECT_EQ(gfx::Rect(1, 1, 100, 200), GetDisplayInfoAt(0).bounds_in_native());
EXPECT_EQ(gfx::Size(100, 200), GetDisplayInfoAt(0).size_in_pixel());
EXPECT_EQ(gfx::Rect(1, 201, 300, 400),
GetDisplayInfoAt(1).bounds_in_native());
EXPECT_EQ(gfx::Size(300, 400), GetDisplayInfoAt(1).size_in_pixel());
// Just Rotating display will change the bounds on both display.
UpdateDisplay("100x200/l,300x400");
EXPECT_EQ("c2 a0 r0 w1 d1", GetCountSummary());
reset();
// Updating to the same configuration should report no changes. A will/did
// change is still sent.
UpdateDisplay("100x200/l,300x400");
EXPECT_EQ("c0 a0 r0 w1 d1", GetCountSummary());
reset();
// Rotating 180 degrees should report one change.
UpdateDisplay("100x200/r,300x400");
EXPECT_EQ("c1 a0 r0 w1 d1", GetCountSummary());
reset();
UpdateDisplay("300x200");
EXPECT_EQ("c1 a0 r1 w1 d1", GetCountSummary());
reset();
// Rotating 180 degrees should report one change.
UpdateDisplay("300x200/u");
EXPECT_EQ("c1 a0 r0 w1 d1", GetCountSummary());
reset();
UpdateDisplay("300x200/l");
EXPECT_EQ("c1 a0 r0 w1 d1", GetCountSummary());
// Having the internal display deactivated should restore user rotation. Newly
// set rotations should be applied.
UpdateDisplay("300x200, 300x200");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
display_manager()->SetDisplayRotation(internal_display_id,
display::Display::ROTATE_90,
display::Display::RotationSource::USER);
display_manager()->SetDisplayRotation(
internal_display_id, display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
const display::ManagedDisplayInfo info =
GetDisplayInfoForId(internal_display_id);
EXPECT_EQ(display::Display::ROTATE_0, info.GetActiveRotation());
// Deactivate internal display to simulate Docked Mode.
vector<display::ManagedDisplayInfo> secondary_only;
secondary_only.push_back(GetDisplayInfoAt(1));
display_manager()->OnNativeDisplaysChanged(secondary_only);
const display::ManagedDisplayInfo& post_removal_info =
display::test::DisplayManagerTestApi(display_manager())
.GetInternalManagedDisplayInfo(internal_display_id);
EXPECT_NE(info.GetActiveRotation(), post_removal_info.GetActiveRotation());
EXPECT_EQ(display::Display::ROTATE_90, post_removal_info.GetActiveRotation());
display_manager()->SetDisplayRotation(
internal_display_id, display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
const display::ManagedDisplayInfo& post_rotation_info =
display::test::DisplayManagerTestApi(display_manager())
.GetInternalManagedDisplayInfo(internal_display_id);
EXPECT_NE(info.GetActiveRotation(), post_rotation_info.GetActiveRotation());
EXPECT_EQ(display::Display::ROTATE_180,
post_rotation_info.GetActiveRotation());
}
namespace {
class CloseDisplayHandler : public ui::EventHandler {
public:
CloseDisplayHandler(AshTestBase* test_base, aura::Window* root)
: test_base_(test_base), root_(root) {}
CloseDisplayHandler(const CloseDisplayHandler&) = delete;
CloseDisplayHandler& operator=(const CloseDisplayHandler&) = delete;
~CloseDisplayHandler() override = default;
// ui::EventHandler:
void OnKeyEvent(ui::KeyEvent* event) override {
test_base_->UpdateDisplay("300x200");
root_->RemovePreTargetHandler(this);
}
private:
raw_ptr<AshTestBase> test_base_;
raw_ptr<aura::Window> root_;
};
} // namespace
// Make sure that we can emulate disconnecting an external display using
// Key/MouseEvent while it is in the unified desktop mode. This doesn't happen
// in real device as the disconnect is triggered by ozone, not by UI events, but
// this is still useful in the testing environment.
TEST_F(DisplayManagerTest, CloseDisplayByEvent) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
display_manager()->SetUnifiedDesktopEnabled(true);
UpdateDisplay("300x200, 600x400");
EXPECT_EQ(2u, display_manager()->software_mirroring_display_list().size());
auto* desktop_root = Shell::GetPrimaryRootWindow();
CloseDisplayHandler handler(this, desktop_root);
desktop_root->AddPreTargetHandler(&handler);
auto* mirror_window_controller =
Shell::Get()->window_tree_host_manager()->mirror_window_controller();
auto* host_root = mirror_window_controller->GetAllRootWindows()[1].get();
ui::test::EventGenerator generator(host_root);
generator.PressAndReleaseKey(ui::VKEY_A);
EXPECT_FALSE(display_manager()->IsInUnifiedMode());
EXPECT_EQ(0u, display_manager()->software_mirroring_display_list().size());
// the root window the handler was added has already been destroyed.
EXPECT_NE(desktop_root, Shell::GetPrimaryRootWindow());
}
TEST_F(DisplayManagerTest, ResolutionChangeInUnifiedMode) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
display_manager()->SetUnifiedDesktopEnabled(true);
UpdateDisplay("300x200, 600x400");
int64_t unified_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display::ManagedDisplayInfo info =
display_manager()->GetDisplayInfo(unified_id);
ASSERT_EQ(2u, info.display_modes().size());
EXPECT_EQ(gfx::Size(600, 200), info.display_modes()[0].size());
EXPECT_TRUE(info.display_modes()[0].native());
EXPECT_EQ(gfx::Size(1200, 400), info.display_modes()[1].size());
EXPECT_FALSE(info.display_modes()[1].native());
EXPECT_EQ(gfx::Size(600, 200),
display::Screen::GetScreen()->GetPrimaryDisplay().size());
display::ManagedDisplayMode active_mode;
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(unified_id, &active_mode));
EXPECT_EQ(gfx::Size(600, 200), active_mode.size());
EXPECT_TRUE(display::test::SetDisplayResolution(display_manager(), unified_id,
gfx::Size(1200, 400)));
EXPECT_EQ(gfx::Size(1200, 400),
display::Screen::GetScreen()->GetPrimaryDisplay().size());
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(unified_id, &active_mode));
EXPECT_EQ(gfx::Size(1200, 400), active_mode.size());
// resolution change will not persist in unified desktop mode.
UpdateDisplay("600x400, 300x200");
EXPECT_EQ(gfx::Size(1200, 400),
display::Screen::GetScreen()->GetPrimaryDisplay().size());
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(unified_id, &active_mode));
EXPECT_TRUE(active_mode.native());
EXPECT_EQ(gfx::Size(1200, 400), active_mode.size());
}
TEST_F(DisplayManagerTest, RotateExternalDisplayWithNonNativeMode) {
const int64_t internal_display_id = 5;
const int64_t external_id = 11;
const display::ManagedDisplayInfo internal_display_info =
display::ManagedDisplayInfo::CreateFromSpecWithID(
"1920x1080#1280x720|640x480%60", internal_display_id);
// Create an external display with a different origin to avoid triggering HW
// mirroring.
display::ManagedDisplayInfo external_display_info =
display::ManagedDisplayInfo::CreateFromSpecWithID(
"1+1-1280x720#1280x720|640x480%60", external_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
EXPECT_EQ(2U, display_manager()->num_connected_displays());
EXPECT_EQ(internal_display_id,
display::Screen::GetScreen()->GetPrimaryDisplay().id());
display::ManagedDisplayMode active_mode;
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(external_id, &active_mode));
EXPECT_TRUE(active_mode.native());
const auto& modes = external_display_info.display_modes();
EXPECT_TRUE(display::test::SetDisplayResolution(
display_manager(), external_id, modes[0].size()));
display_manager()->UpdateDisplays();
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(external_id, &active_mode));
EXPECT_FALSE(active_mode.native());
// Rotate the display.
display_manager()->SetDisplayRotation(
external_id, display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
// Refresh |external_display_info| since we have rotated the display.
external_display_info = display_manager()->GetDisplayInfo(external_id);
// Disconnect the external display.
display_info_list.clear();
display_info_list.push_back(internal_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->num_connected_displays());
// Reconnect the external display.
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->num_connected_displays());
// Verify the display maintains the rotation.
auto external_info = display_manager()->GetDisplayInfo(external_id);
EXPECT_EQ(display::Display::ROTATE_90, external_info.GetActiveRotation());
}
TEST_F(DisplayManagerTest, UpdateMouseCursorAfterRotateZoom) {
// Make sure just rotating will not change native location.
UpdateDisplay("300x200,200x150");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
aura::Env* env = aura::Env::GetInstance();
ui::test::EventGenerator generator1(root_windows[0]);
ui::test::EventGenerator generator2(root_windows[1]);
// Test on 1st display.
generator1.MoveMouseToInHost(150, 50);
EXPECT_EQ(gfx::Point(150, 50), env->last_mouse_location());
UpdateDisplay("300x200/r,200x150");
EXPECT_EQ(gfx::Point(50, 150), env->last_mouse_location());
// Test on 2nd display.
generator2.MoveMouseToInHost(50, 100);
EXPECT_EQ(gfx::Point(250, 100), env->last_mouse_location());
UpdateDisplay("300x200/r,200x150/l");
EXPECT_EQ(gfx::Point(250, 50), env->last_mouse_location());
// The native location is now outside, so move to the center
// of closest display.
UpdateDisplay("300x200/r,100x50/l");
EXPECT_EQ(gfx::Point(225, 50), env->last_mouse_location());
// Make sure just zooming will not change native location.
UpdateDisplay("600x400*2,400x300");
// Test on 1st display.
generator1.MoveMouseToInHost(200, 300);
EXPECT_EQ(gfx::Point(100, 150), env->last_mouse_location());
UpdateDisplay("600x400*[email protected],400x300");
EXPECT_EQ(gfx::Point(66, 100), env->last_mouse_location());
// Test on 2nd display.
UpdateDisplay("600x400,400x300*2");
generator2.MoveMouseToInHost(200, 250);
EXPECT_EQ(gfx::Point(700, 125), env->last_mouse_location());
UpdateDisplay("600x400,400x300*[email protected]");
EXPECT_EQ(gfx::Point(666, 84), env->last_mouse_location());
// The native location is now outside, so move to the
// center of closest display.
UpdateDisplay("600x400,400x200*[email protected]");
EXPECT_EQ(gfx::Point(665, 66), env->last_mouse_location());
}
class TestDisplayObserver : public display::DisplayObserver {
public:
TestDisplayObserver() = default;
TestDisplayObserver(const TestDisplayObserver&) = delete;
TestDisplayObserver& operator=(const TestDisplayObserver&) = delete;
~TestDisplayObserver() override = default;
// display::DisplayObserver overrides:
void OnDisplayMetricsChanged(const display::Display&, uint32_t) override {}
void OnDisplayAdded(const display::Display& new_display) override {
// Mirror window should already be delete before restoring
// the external display.
EXPECT_TRUE(test_api.GetHosts().empty());
changed_ = true;
}
void OnDisplaysRemoved(const display::Displays& removed_displays) override {
// Mirror window should not be created until the external display
// is removed.
EXPECT_TRUE(test_api.GetHosts().empty());
changed_ = true;
}
bool changed_and_reset() {
bool changed = changed_;
changed_ = false;
return changed;
}
private:
MirrorWindowTestApi test_api;
bool changed_ = false;
};
TEST_F(DisplayManagerTest, SoftwareMirroring) {
UpdateDisplay("300x400,400x500");
MirrorWindowTestApi test_api;
EXPECT_TRUE(test_api.GetHosts().empty());
TestDisplayObserver display_observer;
display::Screen::GetScreen()->AddObserver(&display_observer);
display_manager()->SetMultiDisplayMode(display::DisplayManager::MIRRORING);
display_manager()->UpdateDisplays();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(display_observer.changed_and_reset());
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 300, 400),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
std::vector<aura::WindowTreeHost*> hosts = test_api.GetHosts();
ASSERT_EQ(1U, hosts.size());
EXPECT_EQ(gfx::Size(400, 500), hosts[0]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(300, 400), hosts[0]->window()->bounds().size());
EXPECT_TRUE(display_manager()->IsInMirrorMode());
SetSoftwareMirrorMode(false);
EXPECT_TRUE(display_observer.changed_and_reset());
EXPECT_TRUE(test_api.GetHosts().empty());
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// Make sure the mirror window has the pixel size of the
// source display.
SetSoftwareMirrorMode(true);
EXPECT_TRUE(display_observer.changed_and_reset());
UpdateDisplay("[email protected],400x500");
EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ(gfx::Size(300, 400),
test_api.GetHosts()[0]->window()->bounds().size());
UpdateDisplay("310x410*2,400x500");
EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ(gfx::Size(310, 410),
test_api.GetHosts()[0]->window()->bounds().size());
UpdateDisplay("320x420/r,400x500");
EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ(gfx::Size(420, 320),
test_api.GetHosts()[0]->window()->bounds().size());
UpdateDisplay("330x440/r,400x500");
EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ(gfx::Size(440, 330),
test_api.GetHosts()[0]->window()->bounds().size());
// Overscan insets are ignored.
UpdateDisplay("400x600/o,600x800/o");
EXPECT_FALSE(display_observer.changed_and_reset());
EXPECT_EQ(gfx::Size(400, 600),
test_api.GetHosts()[0]->window()->bounds().size());
display::Screen::GetScreen()->RemoveObserver(&display_observer);
}
TEST_F(DisplayManagerTest, RotateInSoftwareMirroring) {
UpdateDisplay("600x400,500x300");
SetSoftwareMirrorMode(true);
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display_manager()->SetDisplayRotation(
primary_id, display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
SetSoftwareMirrorMode(false);
}
TEST_F(DisplayManagerTest, InvertLayout) {
EXPECT_EQ("left, 0",
display::DisplayPlacement(display::DisplayPlacement::RIGHT, 0)
.Swap()
.ToString());
EXPECT_EQ("left, -100",
display::DisplayPlacement(display::DisplayPlacement::RIGHT, 100)
.Swap()
.ToString());
EXPECT_EQ("left, 50",
display::DisplayPlacement(display::DisplayPlacement::RIGHT, -50)
.Swap()
.ToString());
EXPECT_EQ("right, 0",
display::DisplayPlacement(display::DisplayPlacement::LEFT, 0)
.Swap()
.ToString());
EXPECT_EQ("right, -90",
display::DisplayPlacement(display::DisplayPlacement::LEFT, 90)
.Swap()
.ToString());
EXPECT_EQ("right, 60",
display::DisplayPlacement(display::DisplayPlacement::LEFT, -60)
.Swap()
.ToString());
EXPECT_EQ("bottom, 0",
display::DisplayPlacement(display::DisplayPlacement::TOP, 0)
.Swap()
.ToString());
EXPECT_EQ("bottom, -80",
display::DisplayPlacement(display::DisplayPlacement::TOP, 80)
.Swap()
.ToString());
EXPECT_EQ("bottom, 70",
display::DisplayPlacement(display::DisplayPlacement::TOP, -70)
.Swap()
.ToString());
EXPECT_EQ("top, 0",
display::DisplayPlacement(display::DisplayPlacement::BOTTOM, 0)
.Swap()
.ToString());
EXPECT_EQ("top, -70",
display::DisplayPlacement(display::DisplayPlacement::BOTTOM, 70)
.Swap()
.ToString());
EXPECT_EQ("top, 80",
display::DisplayPlacement(display::DisplayPlacement::BOTTOM, -80)
.Swap()
.ToString());
}
TEST_F(DisplayManagerTest, NotifyPrimaryChangeSwapped) {
UpdateDisplay("500x400,500x400");
int64_t old_primary_id = GetPrimaryDisplay().id();
int64_t new_primary_id = GetSecondaryDisplay().id();
SwapPrimaryDisplay();
// Old primary display.
EXPECT_TRUE(changed_metrics(old_primary_id) &
display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
EXPECT_TRUE(changed_metrics(old_primary_id) &
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
EXPECT_FALSE(changed_metrics(old_primary_id) &
display::DisplayObserver::DISPLAY_METRIC_PRIMARY);
// New primary display.
EXPECT_TRUE(changed_metrics(new_primary_id) &
display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
EXPECT_TRUE(changed_metrics(new_primary_id) &
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
EXPECT_TRUE(changed_metrics(new_primary_id) &
display::DisplayObserver::DISPLAY_METRIC_PRIMARY);
}
TEST_F(DisplayManagerTest, NotifyPrimaryChangeDock) {
UpdateDisplay("500x400,500x400");
SwapPrimaryDisplay();
reset();
UpdateDisplay("500x400");
EXPECT_FALSE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
EXPECT_FALSE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_PRIMARY);
UpdateDisplay("500x400,500x400");
SwapPrimaryDisplay();
UpdateDisplay("500x400");
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_PRIMARY);
}
TEST_F(DisplayManagerTest, NotifyPrimaryChangeUndock) {
// Assume the default display is an external display, and
// emulates undocking by switching to another display.
display::ManagedDisplayInfo another_display_info =
CreateDisplayInfo(1, gfx::Rect(0, 0, 1280, 800));
std::vector<display::ManagedDisplayInfo> info_list;
info_list.push_back(another_display_info);
reset();
display_manager()->OnNativeDisplaysChanged(info_list);
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_PRIMARY);
}
TEST_F(DisplayManagerTest, UpdateDisplayWithHostOrigin) {
UpdateDisplay("100x200,300x400");
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
aura::Window::Windows root_windows = Shell::Get()->GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
aura::WindowTreeHost* host0 = root_windows[0]->GetHost();
aura::WindowTreeHost* host1 = root_windows[1]->GetHost();
EXPECT_EQ(gfx::Point(1, 1), host0->GetBoundsInPixels().origin());
EXPECT_EQ(gfx::Size(100, 200), host0->GetBoundsInPixels().size());
// UpdateDisplay set the origin if it's not set.
EXPECT_NE(gfx::Point(1, 1), host1->GetBoundsInPixels().origin());
EXPECT_EQ(gfx::Size(300, 400), host1->GetBoundsInPixels().size());
UpdateDisplay("100x200,200+300-300x400");
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
EXPECT_EQ(gfx::Point(0, 0), host0->GetBoundsInPixels().origin());
EXPECT_EQ(gfx::Size(100, 200), host0->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Point(200, 300), host1->GetBoundsInPixels().origin());
EXPECT_EQ(gfx::Size(300, 400), host1->GetBoundsInPixels().size());
UpdateDisplay("400+500-200x300,300x400");
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
EXPECT_EQ(gfx::Point(400, 500), host0->GetBoundsInPixels().origin());
EXPECT_EQ(gfx::Size(200, 300), host0->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Point(0, 0), host1->GetBoundsInPixels().origin());
EXPECT_EQ(gfx::Size(300, 400), host1->GetBoundsInPixels().size());
UpdateDisplay("100+200-100x200,300+500-200x300");
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
EXPECT_EQ(gfx::Point(100, 200), host0->GetBoundsInPixels().origin());
EXPECT_EQ(gfx::Size(100, 200), host0->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Point(300, 500), host1->GetBoundsInPixels().origin());
EXPECT_EQ(gfx::Size(200, 300), host1->GetBoundsInPixels().size());
}
TEST_F(DisplayManagerTest, UnifiedDesktopBasic) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("400x500,300x200");
// Enable after extended mode.
display_manager()->SetUnifiedDesktopEnabled(true);
// Defaults to the unified desktop.
display::Screen* screen = display::Screen::GetScreen();
// The 2nd display is scaled so that it has the same height as 1st display.
// 300 * 500 / 200 + 400 = 1150.
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
SetSoftwareMirrorMode(true);
EXPECT_EQ(gfx::Size(400, 500), screen->GetPrimaryDisplay().size());
SetSoftwareMirrorMode(false);
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
// Switch to single desktop.
UpdateDisplay("500x300");
EXPECT_EQ(gfx::Size(500, 300), screen->GetPrimaryDisplay().size());
// Switch to unified desktop.
UpdateDisplay("500x300,400x500");
// 400 * 300 / 500 + 500 ~= 739.
EXPECT_EQ(gfx::Size(739, 300), screen->GetPrimaryDisplay().size());
// The default should fit to the internal display.
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(CreateDisplayInfo(10, gfx::Rect(0, 0, 500, 300)));
display_info_list.push_back(
CreateDisplayInfo(11, gfx::Rect(500, 0, 400, 500)));
{
display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
11);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// 500 * 500 / 300 + 400 ~= 1233.
EXPECT_EQ(gfx::Size(1233, 500), screen->GetPrimaryDisplay().size());
}
// Switch to 3 displays.
UpdateDisplay("500x300,400x500,500x300");
EXPECT_EQ(gfx::Size(1239, 300), screen->GetPrimaryDisplay().size());
// Switch back to extended desktop.
display_manager()->SetUnifiedDesktopEnabled(false);
EXPECT_EQ(gfx::Size(500, 300), screen->GetPrimaryDisplay().size());
display::test::DisplayManagerTestApi display_manager_test(display_manager());
EXPECT_EQ(gfx::Size(400, 500),
display_manager_test.GetSecondaryDisplay().size());
EXPECT_EQ(gfx::Size(500, 300),
display_manager()
->GetDisplayForId(display::SynthesizeDisplayIdFromSeed(
display_manager_test.GetSecondaryDisplay().id()))
.size());
}
TEST_F(DisplayManagerTest, UnifiedDesktopWithHardwareMirroring) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
// Enter to hardware mirroring.
display::ManagedDisplayInfo d1 =
CreateDisplayInfo(1, gfx::Rect(0, 0, 500, 400));
display::ManagedDisplayInfo d2 =
CreateDisplayInfo(2, gfx::Rect(0, 0, 500, 400));
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(d1);
display_info_list.push_back(d2);
display_manager()->OnNativeDisplaysChanged(display_info_list);
ASSERT_TRUE(display_manager()->IsInHardwareMirrorMode());
display_manager()->SetUnifiedDesktopEnabled(true);
EXPECT_TRUE(display_manager()->IsInHardwareMirrorMode());
// The display manager automatically switches to software mirroring if
// hardware mirroring is no longer available, because previous mirror mode
// enforces current display mode to be mirror mode.
display::DisplayIdList list = display::test::CreateDisplayIdList2(1, 2);
display::DisplayLayoutBuilder builder(
display_manager()->layout_store()->GetRegisteredDisplayLayout(list));
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
d2.SetBounds(gfx::Rect(0, 500, 500, 400));
display_info_list.clear();
display_info_list.push_back(d1);
display_info_list.push_back(d2);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_FALSE(display_manager()->IsInUnifiedMode());
// Exit software mirroring and enter unified desktop mode after mirror mode is
// turned off.
SetSoftwareMirrorMode(false);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_TRUE(display_manager()->IsInUnifiedMode());
}
TEST_F(DisplayManagerTest, UnifiedDesktopEnabledWithExtended) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("400x500,300x200");
display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
display::DisplayLayoutBuilder builder(
display_manager()->layout_store()->GetRegisteredDisplayLayout(list));
builder.SetDefaultUnified(false);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
display_manager()->SetUnifiedDesktopEnabled(true);
EXPECT_FALSE(display_manager()->IsInUnifiedMode());
}
TEST_F(DisplayManagerTest, UnifiedDesktopWith2xDSF) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
display_manager()->SetUnifiedDesktopEnabled(true);
display::Screen* screen = display::Screen::GetScreen();
// 2nd display is 2x.
UpdateDisplay("400x500,1000x800*2");
display::ManagedDisplayInfo info =
display_manager()->GetDisplayInfo(screen->GetPrimaryDisplay().id());
ASSERT_EQ(2u, info.display_modes().size());
EXPECT_EQ(gfx::Size(1640, 800), info.display_modes()[0].size());
EXPECT_EQ(2.0f, info.display_modes()[0].device_scale_factor());
EXPECT_EQ(gfx::Size(1025, 500), info.display_modes()[1].size());
EXPECT_EQ(1.0f, info.display_modes()[1].device_scale_factor());
// For 1x, 400 + 500 / 800 * 100 = 1025.
EXPECT_EQ(gfx::Size(1025, 500), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(1025, 500),
Shell::GetPrimaryRootWindow()->bounds().size());
accelerators::ZoomDisplay(false);
// (800 / 500 * 400 + 500) /2 = 820
EXPECT_EQ(gfx::Size(820, 400), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(820, 400),
Shell::GetPrimaryRootWindow()->bounds().size());
// 1st display is 2x.
UpdateDisplay("1200x800*2,1100x1000");
info = display_manager()->GetDisplayInfo(screen->GetPrimaryDisplay().id());
ASSERT_EQ(2u, info.display_modes().size());
EXPECT_EQ(gfx::Size(2080, 800), info.display_modes()[0].size());
EXPECT_EQ(2.0f, info.display_modes()[0].device_scale_factor());
EXPECT_EQ(gfx::Size(2600, 1000), info.display_modes()[1].size());
EXPECT_EQ(1.0f, info.display_modes()[1].device_scale_factor());
// For 2x, (800 / 1000 * 1100 + 1200) / 2 = 1040
EXPECT_EQ(gfx::Size(1040, 400), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(1040, 400),
Shell::GetPrimaryRootWindow()->bounds().size());
accelerators::ZoomDisplay(true);
// 1000 / 800 * 1200 + 1100 = 2600
EXPECT_EQ(gfx::Size(2600, 1000), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(2600, 1000),
Shell::GetPrimaryRootWindow()->bounds().size());
// Both displays are 2x.
// 1st display is 2x.
UpdateDisplay("1200x800*2,1100x1000*2");
info = display_manager()->GetDisplayInfo(screen->GetPrimaryDisplay().id());
ASSERT_EQ(2u, info.display_modes().size());
EXPECT_EQ(gfx::Size(2080, 800), info.display_modes()[0].size());
EXPECT_EQ(2.0f, info.display_modes()[0].device_scale_factor());
EXPECT_EQ(gfx::Size(2600, 1000), info.display_modes()[1].size());
EXPECT_EQ(2.0f, info.display_modes()[1].device_scale_factor());
EXPECT_EQ(gfx::Size(1040, 400), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(1040, 400),
Shell::GetPrimaryRootWindow()->bounds().size());
accelerators::ZoomDisplay(true);
EXPECT_EQ(gfx::Size(1300, 500), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(1300, 500),
Shell::GetPrimaryRootWindow()->bounds().size());
// Both displays have the same physical height, with the first display
// being 2x.
UpdateDisplay("1000x800*2,300x800");
info = display_manager()->GetDisplayInfo(screen->GetPrimaryDisplay().id());
ASSERT_EQ(2u, info.display_modes().size());
EXPECT_EQ(gfx::Size(1300, 800), info.display_modes()[0].size());
EXPECT_EQ(2.0f, info.display_modes()[0].device_scale_factor());
EXPECT_EQ(gfx::Size(1300, 800), info.display_modes()[1].size());
EXPECT_EQ(1.0f, info.display_modes()[1].device_scale_factor());
EXPECT_EQ(gfx::Size(650, 400), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(650, 400),
Shell::GetPrimaryRootWindow()->bounds().size());
accelerators::ZoomDisplay(true);
EXPECT_EQ(gfx::Size(1300, 800), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(1300, 800),
Shell::GetPrimaryRootWindow()->bounds().size());
// Both displays have the same physical height, with the second display
// being 2x.
UpdateDisplay("1000x800,300x800*2");
ASSERT_EQ(2u, info.display_modes().size());
EXPECT_EQ(gfx::Size(1300, 800), info.display_modes()[0].size());
EXPECT_EQ(2.0f, info.display_modes()[0].device_scale_factor());
EXPECT_EQ(gfx::Size(1300, 800), info.display_modes()[1].size());
EXPECT_EQ(1.0f, info.display_modes()[1].device_scale_factor());
EXPECT_EQ(gfx::Size(1300, 800), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(1300, 800),
Shell::GetPrimaryRootWindow()->bounds().size());
accelerators::ZoomDisplay(false);
EXPECT_EQ(gfx::Size(650, 400), screen->GetPrimaryDisplay().size());
EXPECT_EQ(gfx::Size(650, 400),
Shell::GetPrimaryRootWindow()->bounds().size());
}
// Updating displays again in unified desktop mode should not crash.
// crbug.com/491094.
TEST_F(DisplayManagerTest, ConfigureUnifiedTwice) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("300x200,400x500");
// Mirror windows are created in a posted task.
base::RunLoop().RunUntilIdle();
UpdateDisplay("300x250,400x550");
base::RunLoop().RunUntilIdle();
}
TEST_F(DisplayManagerTest, NoRotateUnifiedDesktop) {
display_manager()->SetUnifiedDesktopEnabled(true);
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("400x500,300x200");
display::Screen* screen = display::Screen::GetScreen();
const display::Display& display = screen->GetPrimaryDisplay();
EXPECT_EQ(gfx::Size(1150, 500), display.size());
display_manager()->SetDisplayRotation(
display.id(), display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
EXPECT_EQ(display::Display::ROTATE_0,
screen->GetPrimaryDisplay().panel_rotation());
display_manager()->SetDisplayRotation(
display.id(), display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
EXPECT_EQ(display::Display::ROTATE_0,
screen->GetPrimaryDisplay().panel_rotation());
UpdateDisplay("400x500");
EXPECT_EQ(gfx::Size(400, 500), screen->GetPrimaryDisplay().size());
}
// Validate that setting an invalid matrix will fall back to the default
// horizontal unified desktop layout.
TEST_F(DisplayManagerTest, UnifiedDesktopInvalidMatrices) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("400x500,300x200");
display_manager()->SetUnifiedDesktopEnabled(true);
display::Screen* screen = display::Screen::GetScreen();
display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
ASSERT_EQ(2u, list.size());
{
// Create an empty matrix.
display::UnifiedDesktopLayoutMatrix matrix;
display_manager()->SetUnifiedDesktopMatrix(matrix);
// The result is still a valid default horizontal layout.
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
// 2 x 1 empty matrix.
matrix.resize(2u);
display_manager()->SetUnifiedDesktopMatrix(matrix);
// The result is still a valid default horizontal layout.
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
}
{
// 2 x 1 vertical matrix with invalid IDs.
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(2u);
matrix[0].emplace_back(list[0]);
matrix[1].emplace_back(-100);
display_manager()->SetUnifiedDesktopMatrix(matrix);
// The result is still a valid default horizontal layout.
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
}
{
// Matrix with a missing ID.
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(2u);
matrix[0].emplace_back(list[0]);
display_manager()->SetUnifiedDesktopMatrix(matrix);
// The result is still a valid default horizontal layout.
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
}
// Switch to 3 displays.
UpdateDisplay("500x300,400x500,500x300");
list = display_manager()->GetConnectedDisplayIdList();
ASSERT_EQ(3u, list.size());
{
// Create a matrix with unequal rows
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(3u);
matrix[0].emplace_back(list[0]);
matrix[1].emplace_back(list[1]);
matrix[1].emplace_back(list[2]); // Typo; meant to say matrix[2].
display_manager()->SetUnifiedDesktopMatrix(matrix);
// The result is still a valid default horizontal layout.
EXPECT_EQ(gfx::Size(1239, 300), screen->GetPrimaryDisplay().size());
}
{
// Create a matrix with repeated IDs.
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(3u);
matrix[0].emplace_back(list[0]);
matrix[1].emplace_back(list[1]);
matrix[2].emplace_back(list[1]); // Typo; meant to say list[2].
display_manager()->SetUnifiedDesktopMatrix(matrix);
// The result is still a valid default horizontal layout.
EXPECT_EQ(gfx::Size(1239, 300), screen->GetPrimaryDisplay().size());
}
}
TEST_F(DisplayManagerTest, UnifiedDesktopVerticalLayout2x1) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("400x500,300x200");
display_manager()->SetUnifiedDesktopEnabled(true);
display::Screen* screen = display::Screen::GetScreen();
// This is still a horizontal layout.
EXPECT_EQ(gfx::Size(1150, 500), screen->GetPrimaryDisplay().size());
display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
ASSERT_EQ(2u, list.size());
{
// Create a 2 x 1 vertical layout matrix and set it.
// [400 x 500]
// [300 x 200]
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(2u);
matrix[0].emplace_back(list[0]);
matrix[1].emplace_back(list[1]);
display_manager()->SetUnifiedDesktopMatrix(matrix);
// 500 + 400 * 200 / 300 ~= 766.
EXPECT_EQ(gfx::Size(400, 766), screen->GetPrimaryDisplay().size());
// Default shelf alignment is bottom. Display in bottom-left cell is
// considered the primary mirroring display.
EXPECT_EQ(list[1], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Validate display rows and max heights.
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[0]));
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[1]));
EXPECT_EQ(500, display_manager()->GetUnifiedDesktopRowMaxHeight(0));
EXPECT_EQ(400 * 200 / 300,
display_manager()->GetUnifiedDesktopRowMaxHeight(1));
EXPECT_FALSE(OverlappingMirroringDisplaysExist());
}
{
// Change the order of the displays such that the [300 x 200] is on top,
// which should make its bounds used for the default mode.
// [300 x 200]
// [400 x 500]
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(2u);
matrix[0].emplace_back(list[1]);
matrix[1].emplace_back(list[0]);
display_manager()->SetUnifiedDesktopMatrix(matrix);
// 200 + 300 * 500 / 400 ~= 574 (Note that we actually scale the max unified
// bounds).
EXPECT_EQ(gfx::Size(300, 574), screen->GetPrimaryDisplay().size());
// Display in bottom-left cell is considered primary.
EXPECT_EQ(list[0], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Validate display rows and max heights.
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[0]));
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[1]));
EXPECT_EQ(199, display_manager()->GetUnifiedDesktopRowMaxHeight(0));
// 300 * 500 / 400.
EXPECT_EQ(375, display_manager()->GetUnifiedDesktopRowMaxHeight(1));
EXPECT_FALSE(OverlappingMirroringDisplaysExist());
}
{
// Revert to the first matrix, but mark the [300 x 200] display as internal.
// [400 x 500]
// [300 x 200] : Internal
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(2u);
matrix[0].emplace_back(list[0]);
matrix[1].emplace_back(list[1]);
display_manager()->SetUnifiedDesktopMatrix(matrix);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.emplace_back(
CreateDisplayInfo(list[0], gfx::Rect(0, 0, 400, 500)));
display_info_list.emplace_back(
CreateDisplayInfo(list[1], gfx::Rect(400, 0, 300, 200)));
display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
list[1]);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// Run loop to create mirroring displays.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(gfx::Size(300, 574), screen->GetPrimaryDisplay().size());
// Display in bottom-left cell is considered primary.
EXPECT_EQ(list[1], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Validate display rows and max heights.
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[0]));
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[1]));
// 300 * 500 / 400.
EXPECT_EQ(375, display_manager()->GetUnifiedDesktopRowMaxHeight(0));
EXPECT_EQ(199, display_manager()->GetUnifiedDesktopRowMaxHeight(1));
EXPECT_FALSE(OverlappingMirroringDisplaysExist());
}
}
TEST_F(DisplayManagerTest, UnifiedDesktopVerticalLayout3x1) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("500x300,400x500,500x300");
display_manager()->SetUnifiedDesktopEnabled(true);
display::Screen* screen = display::Screen::GetScreen();
display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
ASSERT_EQ(3u, list.size());
{
// Create a 3 x 1 vertical layout matrix and set it.
// [500 x 300]
// [400 x 500]
// [500 x 300]
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(3u);
matrix[0].emplace_back(list[0]);
matrix[1].emplace_back(list[1]);
matrix[2].emplace_back(list[2]);
display_manager()->SetUnifiedDesktopMatrix(matrix);
EXPECT_EQ(gfx::Size(500, 1225), screen->GetPrimaryDisplay().size());
// Display in bottom-left cell is considered primary.
EXPECT_EQ(list[2], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Validate display rows and max heights.
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[0]));
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[1]));
EXPECT_EQ(2, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[2]));
EXPECT_EQ(300, display_manager()->GetUnifiedDesktopRowMaxHeight(0));
// 500 * 500 / 400 = 625.
EXPECT_EQ(625, display_manager()->GetUnifiedDesktopRowMaxHeight(1));
EXPECT_EQ(300, display_manager()->GetUnifiedDesktopRowMaxHeight(2));
EXPECT_FALSE(OverlappingMirroringDisplaysExist());
}
{
// We can change the order however we want.
// [400 x 500]
// [500 x 300]
// [500 x 300]
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(3u);
matrix[0].emplace_back(list[1]);
matrix[1].emplace_back(list[0]);
matrix[2].emplace_back(list[2]);
display_manager()->SetUnifiedDesktopMatrix(matrix);
EXPECT_EQ(gfx::Size(400, 980), screen->GetPrimaryDisplay().size());
// Display in bottom-left cell is considered primary.
EXPECT_EQ(list[2], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Validate display rows and max heights.
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[0]));
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[1]));
EXPECT_EQ(2, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[2]));
EXPECT_EQ(500, display_manager()->GetUnifiedDesktopRowMaxHeight(0));
// 400 * 300 / 500 = 240.
EXPECT_EQ(240, display_manager()->GetUnifiedDesktopRowMaxHeight(1));
EXPECT_EQ(240, display_manager()->GetUnifiedDesktopRowMaxHeight(2));
EXPECT_FALSE(OverlappingMirroringDisplaysExist());
}
}
TEST_F(DisplayManagerTest, UnifiedDesktopGridLayout2x2) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("500x300,400x500,300x600,200x300");
display_manager()->SetUnifiedDesktopEnabled(true);
display::Screen* screen = display::Screen::GetScreen();
display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
ASSERT_EQ(4u, list.size());
// Create a 2 x 2 vertical layout matrix and set it.
// [500 x 300] [400 x 500]
// [300 x 600] [200 x 300]
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(2u);
matrix[0].emplace_back(list[0]);
matrix[0].emplace_back(list[1]);
matrix[1].emplace_back(list[2]);
matrix[1].emplace_back(list[3]);
display_manager()->SetUnifiedDesktopMatrix(matrix);
EXPECT_EQ(gfx::Size(739, 933), screen->GetPrimaryDisplay().size());
// Default shelf alignment is bottom.
Shelf* shelf = Shell::GetPrimaryRootWindowController()->shelf();
EXPECT_EQ(shelf->alignment(), ShelfAlignment::kBottom);
// Display in bottom-left cell is considered the primary mirroring display.
EXPECT_EQ(list[2], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Validate display rows and max heights.
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[0]));
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[1]));
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[2]));
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[3]));
EXPECT_EQ(300, display_manager()->GetUnifiedDesktopRowMaxHeight(0));
EXPECT_EQ(633, display_manager()->GetUnifiedDesktopRowMaxHeight(1));
EXPECT_FALSE(OverlappingMirroringDisplaysExist());
// Change the shelf alignment to left, and expect that the primary mirroring
// display in the top-left display in the matrix.
shelf->SetAlignment(ShelfAlignment::kLeft);
EXPECT_EQ(list[0], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Change the shelf alignment to right, and expect that the primary mirroring
// display in the top-right display in the matrix.
shelf->SetAlignment(ShelfAlignment::kRight);
EXPECT_EQ(list[1], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
}
TEST_F(DisplayManagerTest, UnifiedDesktopGridLayout3x2) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("500x300,400x500,300x600,200x300,700x200,350x480");
display_manager()->SetUnifiedDesktopEnabled(true);
display::Screen* screen = display::Screen::GetScreen();
display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
ASSERT_EQ(6u, list.size());
// Create a 3 x 2 vertical layout matrix and set it.
// [500 x 300] [400 x 500]
// [300 x 600] [200 x 300]
// [700 x 200] [350 x 480]
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(3u);
matrix[0].emplace_back(list[0]);
matrix[0].emplace_back(list[1]);
matrix[1].emplace_back(list[2]);
matrix[1].emplace_back(list[3]);
matrix[2].emplace_back(list[4]);
matrix[2].emplace_back(list[5]);
display_manager()->SetUnifiedDesktopMatrix(matrix);
EXPECT_EQ(gfx::Size(739, 1108), screen->GetPrimaryDisplay().size());
// Default shelf alignment is bottom.
Shelf* shelf = Shell::GetPrimaryRootWindowController()->shelf();
EXPECT_EQ(shelf->alignment(), ShelfAlignment::kBottom);
// Display in bottom-left cell is considered the primary mirroring display.
EXPECT_EQ(list[4], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Validate display rows and max heights.
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[0]));
EXPECT_EQ(0, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[1]));
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[2]));
EXPECT_EQ(1, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[3]));
EXPECT_EQ(2, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[4]));
EXPECT_EQ(2, display_manager()->GetMirroringDisplayRowIndexInUnifiedMatrix(
list[5]));
EXPECT_EQ(300, display_manager()->GetUnifiedDesktopRowMaxHeight(0));
EXPECT_EQ(633, display_manager()->GetUnifiedDesktopRowMaxHeight(1));
EXPECT_EQ(175, display_manager()->GetUnifiedDesktopRowMaxHeight(2));
EXPECT_FALSE(OverlappingMirroringDisplaysExist());
// Change the shelf alignment to left, and expect that the primary mirroring
// display in the top-left display in the matrix.
shelf->SetAlignment(ShelfAlignment::kLeft);
EXPECT_EQ(list[0], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
// Change the shelf alignment to right, and expect that the primary mirroring
// display in the top-right display in the matrix.
shelf->SetAlignment(ShelfAlignment::kRight);
EXPECT_EQ(list[1], Shell::Get()
->display_configuration_controller()
->GetPrimaryMirroringDisplayForUnifiedDesktop()
.id());
}
TEST_F(DisplayManagerTest, UnifiedDesktopTabletMode) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("400x300,800x700");
base::RunLoop().RunUntilIdle();
// Set the first display as internal display so that the tablet mode can be
// enabled.
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
display_manager()->SetUnifiedDesktopEnabled(true);
EXPECT_TRUE(display_manager()->IsInUnifiedMode());
// Turn on tablet mode, expect that we switch to mirror mode without any
// crashes.
ash::TabletModeControllerTestApi().EnterTabletMode();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
// The Home Launcher should be created and shown, not dismissed as a result of
// the destruction of the Unified host when we switched to mirror mode
// asynchronously.
auto* app_list_controller = Shell::Get()->app_list_controller();
EXPECT_TRUE(display::Screen::GetScreen()->InTabletMode());
EXPECT_TRUE(
app_list_controller->IsVisible(display_manager()->first_display_id()));
// Exiting tablet mode should exit mirror mode and return back to Unified
// mode.
ash::TabletModeControllerTestApi().LeaveTabletMode();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_TRUE(display_manager()->IsInUnifiedMode());
// Home Launcher should be dismissed.
EXPECT_FALSE(display::Screen::GetScreen()->InTabletMode());
EXPECT_FALSE(
app_list_controller->IsVisible(display_manager()->first_display_id()));
}
TEST_F(DisplayManagerTest, UnifiedDesktopPrimarySizeWithRotatedDisplays) {
MirrorWindowTestApi test_api;
// RootWidow for primary changes during unified desktop transition.
disable_check_root_window_on_destruction();
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
display_manager()->SetUnifiedDesktopEnabled(true);
UpdateDisplay("1000x700/r");
EXPECT_EQ(gfx::Size(700, 1000),
display::Screen::GetScreen()->GetPrimaryDisplay().size());
UpdateDisplay("1000x700/r,1000x700/r");
EXPECT_EQ(gfx::Size(1400, 1000),
display::Screen::GetScreen()->GetPrimaryDisplay().size());
std::vector<aura::WindowTreeHost*> host_list = test_api.GetHosts();
std::vector<display::Display> display_list =
display_manager()->software_mirroring_display_list();
EXPECT_EQ(gfx::Size(700, 1000), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_90,
host_list[0]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_90, display_list[0].panel_rotation());
EXPECT_EQ(gfx::Size(700, 1000), host_list[1]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_90,
host_list[1]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_90, display_list[1].panel_rotation());
// Use custom display offset to ensure rotation is properly updated.
UpdateDisplay("1000x700/r,1200+100-1000x700/l");
EXPECT_EQ(gfx::Size(1400, 1000),
display::Screen::GetScreen()->GetPrimaryDisplay().size());
host_list = test_api.GetHosts();
display_list = display_manager()->software_mirroring_display_list();
EXPECT_EQ(gfx::Size(700, 1000), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_90,
host_list[0]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_90, display_list[0].panel_rotation());
EXPECT_EQ(gfx::Size(700, 1000), host_list[1]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_270,
host_list[1]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_270, display_list[1].panel_rotation());
UpdateDisplay("1000x700/r,1000x700");
// width = 1000 / 700 * 1000 + 700 ~= 2128
EXPECT_EQ(gfx::Size(2128, 1000),
display::Screen::GetScreen()->GetPrimaryDisplay().size());
host_list = test_api.GetHosts();
display_list = display_manager()->software_mirroring_display_list();
EXPECT_EQ(gfx::Size(700, 1000), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_90,
host_list[0]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_90, display_list[0].panel_rotation());
EXPECT_EQ(gfx::Size(1000, 700), host_list[1]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_NONE,
host_list[1]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_0, display_list[1].panel_rotation());
// Three displays
UpdateDisplay("1000x700/l,1000x700/r,1000x700/l");
EXPECT_EQ(gfx::Size(2100, 1000),
display::Screen::GetScreen()->GetPrimaryDisplay().size());
host_list = test_api.GetHosts();
display_list = display_manager()->software_mirroring_display_list();
EXPECT_EQ(gfx::Size(700, 1000), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_270,
host_list[0]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_270, display_list[0].panel_rotation());
EXPECT_EQ(gfx::Size(700, 1000), host_list[1]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_90,
host_list[1]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_90, display_list[1].panel_rotation());
EXPECT_EQ(gfx::Size(700, 1000), host_list[2]->window()->bounds().size());
EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_CLOCKWISE_270,
host_list[2]->compositor()->display_transform_hint());
EXPECT_EQ(display::Display::ROTATE_270, display_list[2].panel_rotation());
}
TEST_F(DisplayManagerTest, DisplayPrefsAndForcedMirrorMode) {
UpdateDisplay("400x300,800x700");
base::RunLoop().RunUntilIdle();
// Set the first display as internal display so that the tablet mode can be
// enabled.
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
// Initially we can save display prefs ...
EXPECT_TRUE(Shell::Get()->ShouldSaveDisplaySettings());
// ... and there are no external displays that are candidates for mirror
// restore.
EXPECT_TRUE(display_manager()->external_display_mirror_info().empty());
// Turn on tablet mode, and expect that it can persist certain
// display prefs while forced mirror mode is active.
ash::TabletModeControllerTestApi().EnterTabletMode();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_TRUE(
display_manager()->layout_store()->forced_mirror_mode_for_tablet());
EXPECT_TRUE(Shell::Get()->ShouldSaveDisplaySettings());
// Forced mirror mode does not add external displays as candidates for mirror
// restore.
EXPECT_TRUE(display_manager()->external_display_mirror_info().empty());
// Exit tablet mode and expect everything is back to normal.
ash::TabletModeControllerTestApi().LeaveTabletMode();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_FALSE(
display_manager()->layout_store()->forced_mirror_mode_for_tablet());
EXPECT_TRUE(Shell::Get()->ShouldSaveDisplaySettings());
EXPECT_TRUE(display_manager()->external_display_mirror_info().empty());
}
TEST_F(DisplayManagerTest, ForcedMirrorModeExited) {
UpdateDisplay("400x300,800x700");
base::RunLoop().RunUntilIdle();
// Set the first display as internal display so that the tablet mode can be
// enabled.
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
// Initially we can save display prefs ...
EXPECT_TRUE(Shell::Get()->ShouldSaveDisplaySettings());
// ... and there are no external displays that are candidates for mirror
// restore.
EXPECT_TRUE(display_manager()->external_display_mirror_info().empty());
// Turn on tablet mode, and expect that it can persist certain
// display prefs while forced mirror mode is active.
ash::TabletModeControllerTestApi().EnterTabletMode();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_TRUE(
display_manager()->layout_store()->forced_mirror_mode_for_tablet());
EXPECT_TRUE(Shell::Get()->ShouldSaveDisplaySettings());
// Forced mirror mode does not add external displays as candidates for mirror
// restore.
EXPECT_TRUE(display_manager()->external_display_mirror_info().empty());
// Exit mirror mode, and expect that `forced_mirror_mode_for_tablet` is now
// false.
SetSoftwareMirrorMode(false);
EXPECT_FALSE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_FALSE(
display_manager()->layout_store()->forced_mirror_mode_for_tablet());
// Randomly change the external monitor's resolution/refresh rate, and
// expect that the setting is retained.
const display::ManagedDisplayInfo& info_1 = GetDisplayInfoAt(0);
const display::ManagedDisplayInfo::ManagedDisplayModeList& modes =
info_1.display_modes();
display::test::SetDisplayResolution(display_manager(), info_1.id(),
modes[0].size());
display_manager()->UpdateDisplays();
EXPECT_EQ(
display_manager()->GetDisplayForId(info_1.id()).device_scale_factor(),
1.f);
EXPECT_FALSE(display_manager()->IsInSoftwareMirrorMode());
}
TEST_F(DisplayManagerTest, DisplayPrefsAndKioskMode) {
// Login in as kiosk app.
UserSession session;
session.session_id = 1u;
session.user_info.type = user_manager::UserType::kKioskApp;
session.user_info.account_id = AccountId::FromUserEmail("[email protected]");
session.user_info.display_name = "User 1";
session.user_info.display_email = "[email protected]";
Shell::Get()->session_controller()->UpdateUserSession(std::move(session));
EXPECT_EQ(LoginStatus::KIOSK_APP,
Shell::Get()->session_controller()->login_status());
UpdateDisplay("400x300,800x700");
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(Shell::Get()->ShouldSaveDisplaySettings());
}
TEST_F(DisplayManagerTest, DockMode) {
const int64_t internal_id = 1;
const int64_t external_id = 2;
const display::ManagedDisplayInfo internal_display_info =
CreateDisplayInfo(internal_id, gfx::Rect(0, 0, 500, 400));
const display::ManagedDisplayInfo external_display_info =
CreateDisplayInfo(external_id, gfx::Rect(1, 1, 200, 100));
std::vector<display::ManagedDisplayInfo> display_info_list;
// software mirroring.
display_info_list.push_back(internal_display_info);
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
EXPECT_EQ(internal_id, internal_display_id);
display_info_list.clear();
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1U, display_manager()->active_display_list().size());
EXPECT_TRUE(display_manager()->IsActiveDisplayId(external_id));
EXPECT_FALSE(display_manager()->IsActiveDisplayId(internal_id));
}
// Make sure that bad layout information is ignored and does not crash.
TEST_F(DisplayManagerTest, DontRegisterBadConfig) {
display::DisplayIdList list = display::test::CreateDisplayIdList2(1, 2);
display::DisplayLayoutBuilder builder(1);
builder.AddDisplayPlacement(2, 1, display::DisplayPlacement::LEFT, 0);
builder.AddDisplayPlacement(3, 1, display::DisplayPlacement::BOTTOM, 0);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
}
class ScreenShutdownTest : public AshTestBase {
public:
ScreenShutdownTest() = default;
ScreenShutdownTest(const ScreenShutdownTest&) = delete;
ScreenShutdownTest& operator=(const ScreenShutdownTest&) = delete;
~ScreenShutdownTest() override = default;
void TearDown() override {
display::Screen* orig_screen = display::Screen::GetScreen();
AshTestBase::TearDown();
display::Screen* screen = display::Screen::GetScreen();
EXPECT_NE(orig_screen, screen);
EXPECT_EQ(2, screen->GetNumDisplays());
EXPECT_EQ(gfx::Size(500, 300), screen->GetPrimaryDisplay().size());
std::vector<display::Display> all = screen->GetAllDisplays();
EXPECT_EQ(gfx::Size(500, 300), all[0].size());
EXPECT_EQ(gfx::Size(800, 400), all[1].size());
}
};
TEST_F(ScreenShutdownTest, ScreenAfterShutdown) {
UpdateDisplay("500x300,800x400");
}
namespace {
// A helper class that sets the display configuration and starts ash.
// This is to make sure the font configuration happens during ash
// initialization process.
class FontTestHelper : public AshTestBase {
public:
enum DisplayType { INTERNAL, EXTERNAL };
FontTestHelper(float scale, DisplayType display_type) {
gfx::ClearFontRenderParamsCacheForTest();
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (display_type == INTERNAL)
command_line->AppendSwitch(::switches::kUseFirstDisplayAsInternal);
command_line->AppendSwitchASCII(::switches::kHostWindowBounds,
StringPrintf("1000x800*%f", scale));
SetUp();
}
FontTestHelper(const FontTestHelper&) = delete;
FontTestHelper& operator=(const FontTestHelper&) = delete;
~FontTestHelper() override { TearDown(); }
// AshTestBase:
void TestBody() override { NOTREACHED(); }
};
bool IsTextSubpixelPositioningEnabled() {
gfx::FontRenderParams params =
gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr);
return params.subpixel_positioning;
}
gfx::FontRenderParams::Hinting GetFontHintingParams() {
gfx::FontRenderParams params =
gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr);
return params.hinting;
}
} // namespace
using DisplayManagerFontTest = testing::Test;
TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf100Internal) {
FontTestHelper helper(1.0f, FontTestHelper::INTERNAL);
ASSERT_DOUBLE_EQ(
1.0f,
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
EXPECT_FALSE(IsTextSubpixelPositioningEnabled());
EXPECT_NE(gfx::FontRenderParams::HINTING_NONE, GetFontHintingParams());
}
TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf200Internal) {
FontTestHelper helper(2.0f, FontTestHelper::INTERNAL);
ASSERT_DOUBLE_EQ(
2.0f,
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
EXPECT_FALSE(IsTextSubpixelPositioningEnabled());
EXPECT_NE(gfx::FontRenderParams::HINTING_NONE, GetFontHintingParams());
helper.display_manager()->UpdateZoomFactor(
display::Screen::GetScreen()->GetPrimaryDisplay().id(), 0.5f);
ASSERT_DOUBLE_EQ(
1.0f,
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
EXPECT_FALSE(IsTextSubpixelPositioningEnabled());
EXPECT_NE(gfx::FontRenderParams::HINTING_NONE, GetFontHintingParams());
}
TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf100External) {
FontTestHelper helper(1.0f, FontTestHelper::EXTERNAL);
ASSERT_DOUBLE_EQ(
1.0f,
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
EXPECT_FALSE(IsTextSubpixelPositioningEnabled());
EXPECT_NE(gfx::FontRenderParams::HINTING_NONE, GetFontHintingParams());
}
TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf125External) {
FontTestHelper helper(1.25f, FontTestHelper::EXTERNAL);
ASSERT_DOUBLE_EQ(
1.25f,
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
EXPECT_TRUE(IsTextSubpixelPositioningEnabled());
EXPECT_EQ(gfx::FontRenderParams::HINTING_NONE, GetFontHintingParams());
}
TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf200External) {
FontTestHelper helper(2.0f, FontTestHelper::EXTERNAL);
ASSERT_DOUBLE_EQ(
2.0f,
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
EXPECT_FALSE(IsTextSubpixelPositioningEnabled());
EXPECT_NE(gfx::FontRenderParams::HINTING_NONE, GetFontHintingParams());
}
TEST_F(DisplayManagerFontTest,
TextSubpixelPositioningWithDsf125InternalWithScaling) {
FontTestHelper helper(1.25f, FontTestHelper::INTERNAL);
ASSERT_DOUBLE_EQ(
1.25f,
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
EXPECT_TRUE(IsTextSubpixelPositioningEnabled());
EXPECT_EQ(gfx::FontRenderParams::HINTING_NONE, GetFontHintingParams());
helper.display_manager()->UpdateZoomFactor(
display::Screen::GetScreen()->GetPrimaryDisplay().id(), 0.8f);
ASSERT_DOUBLE_EQ(
1.f,
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor());
EXPECT_FALSE(IsTextSubpixelPositioningEnabled());
EXPECT_NE(gfx::FontRenderParams::HINTING_NONE, GetFontHintingParams());
}
TEST_F(DisplayManagerTest, CheckInitializationOfRotationProperty) {
int64_t id = display_manager()->GetDisplayAt(0).id();
display_manager()->RegisterDisplayProperty(
id, display::Display::ROTATE_90, /*overscan_insets=*/nullptr,
/*resolution_in_pixels=*/gfx::Size(),
/*device_scale_factor=*/1.0f, /*display_zoom_factor=*/1.0f,
/*display_zoom_factor_map=*/{}, /*refresh_rate=*/60.f,
/*is_interlaced=*/false,
/*variable_refresh_rate_state=*/
display::VariableRefreshRateState::kVrrNotCapable,
/*vsync_rate_min=*/std::nullopt);
const display::ManagedDisplayInfo& info =
display_manager()->GetDisplayInfo(id);
EXPECT_EQ(display::Display::ROTATE_90,
info.GetRotation(display::Display::RotationSource::USER));
EXPECT_EQ(display::Display::ROTATE_90,
info.GetRotation(display::Display::RotationSource::ACTIVE));
}
TEST_F(DisplayManagerTest, RejectInvalidLayoutData) {
display::DisplayLayoutStore* layout_store = display_manager()->layout_store();
int64_t id1 = 10001;
int64_t id2 = 10002;
ASSERT_TRUE(display::CompareDisplayIds(id1, id2));
display::DisplayLayoutBuilder good_builder(id1);
good_builder.SetSecondaryPlacement(id2, display::DisplayPlacement::LEFT, 0);
std::unique_ptr<display::DisplayLayout> good(good_builder.Build());
display::DisplayIdList good_list =
display::test::CreateDisplayIdList2(id1, id2);
layout_store->RegisterLayoutForDisplayIdList(good_list, good->Copy());
display::DisplayLayoutBuilder bad(id1);
bad.SetSecondaryPlacement(id2, display::DisplayPlacement::BOTTOM, 0);
display::DisplayIdList bad_list(2);
bad_list[0] = id2;
bad_list[1] = id1;
layout_store->RegisterLayoutForDisplayIdList(bad_list, bad.Build());
EXPECT_EQ(good->ToString(),
layout_store->GetRegisteredDisplayLayout(good_list).ToString());
}
TEST_F(DisplayManagerTest, GuessDisplayIdFieldsInDisplayLayout) {
int64_t id1 = 10001;
int64_t id2 = 10002;
std::unique_ptr<display::DisplayLayout> old_layout(
new display::DisplayLayout);
old_layout->placement_list.emplace_back(display::DisplayPlacement::BOTTOM, 0);
old_layout->primary_id = id1;
display::DisplayLayoutStore* layout_store = display_manager()->layout_store();
display::DisplayIdList list = display::test::CreateDisplayIdList2(id1, id2);
layout_store->RegisterLayoutForDisplayIdList(list, std::move(old_layout));
const display::DisplayLayout& stored =
layout_store->GetRegisteredDisplayLayout(list);
EXPECT_EQ(id1, stored.placement_list[0].parent_display_id);
EXPECT_EQ(id2, stored.placement_list[0].display_id);
}
TEST_F(DisplayManagerTest, AccelerometerSupport) {
display::test::DisplayManagerTestApi display_manager_test(display_manager());
display_manager_test.SetFirstDisplayAsInternalDisplay();
display::Screen* screen = display::Screen::GetScreen();
EXPECT_EQ(display::Display::AccelerometerSupport::UNAVAILABLE,
screen->GetPrimaryDisplay().accelerometer_support());
display_manager()->set_internal_display_has_accelerometer(true);
display_manager()->UpdateDisplays();
EXPECT_EQ(display::Display::AccelerometerSupport::AVAILABLE,
screen->GetPrimaryDisplay().accelerometer_support());
UpdateDisplay("1000x9000,800x700");
EXPECT_EQ(display::Display::AccelerometerSupport::AVAILABLE,
screen->GetPrimaryDisplay().accelerometer_support());
EXPECT_EQ(display::Display::AccelerometerSupport::UNAVAILABLE,
display_manager_test.GetSecondaryDisplay().accelerometer_support());
// Secondary is now primary and should not have accelerometer support.
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(
CreateDisplayInfo(display_manager_test.GetSecondaryDisplay().id(),
gfx::Rect(1, 1, 200, 100)));
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(display::Display::AccelerometerSupport::UNAVAILABLE,
screen->GetPrimaryDisplay().accelerometer_support());
// Re-enable internal display.
display_info_list.clear();
display_info_list.push_back(CreateDisplayInfo(
display::Display::InternalDisplayId(), gfx::Rect(1, 1, 200, 100)));
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(display::Display::AccelerometerSupport::AVAILABLE,
screen->GetPrimaryDisplay().accelerometer_support());
}
namespace {
std::unique_ptr<display::DisplayMode> MakeDisplayMode() {
return std::make_unique<display::DisplayMode>(gfx::Size(1366, 768), false,
60);
}
} // namespace
TEST_F(DisplayManagerTest, DisconnectedInternalDisplayShouldUpdateDisplayInfo) {
constexpr int64_t external_id = 123;
const int64_t internal_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
display::Screen* screen = display::Screen::GetScreen();
DCHECK(screen);
Shell* shell = Shell::Get();
display::DisplayChangeObserver observer(shell->display_manager());
display::DisplayConfigurator::DisplayStateList outputs;
std::unique_ptr<display::DisplaySnapshot> internal_snapshot =
display::FakeDisplaySnapshot::Builder()
.SetId(internal_id)
.SetType(display::DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetDPI(210) // 1.6f
.SetNativeMode(MakeDisplayMode())
.Build();
EXPECT_FALSE(internal_snapshot->current_mode());
outputs.push_back(internal_snapshot.get());
std::unique_ptr<display::DisplaySnapshot> external_snapshot =
display::FakeDisplaySnapshot::Builder()
.SetId(external_id)
.SetNativeMode(MakeDisplayMode())
.AddMode(MakeDisplayMode())
.SetOrigin({0, 1000})
.Build();
// "Connected display" has the current mode.
external_snapshot->set_current_mode(external_snapshot->native_mode());
outputs.push_back(external_snapshot.get());
// Update the display manager through DisplayChangeObserver.
observer.GetStateForDisplayIds(outputs);
observer.OnDisplayConfigurationChanged(outputs);
EXPECT_EQ(1u, display_manager()->GetNumDisplays());
EXPECT_TRUE(display_manager()->IsActiveDisplayId(external_id));
EXPECT_FALSE(display_manager()->IsActiveDisplayId(internal_id));
const display::ManagedDisplayInfo& display_info =
display_manager()->GetDisplayInfo(internal_id);
EXPECT_EQ(1.6f, display_info.device_scale_factor());
ASSERT_EQ(1u, display_info.display_modes().size());
EXPECT_EQ(1.6f, display_info.display_modes()[0].device_scale_factor());
}
TEST_F(DisplayManagerTest, UpdateInternalDisplayNativeBounds) {
constexpr int64_t external_id = 123;
const int64_t internal_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
display::Screen* screen = display::Screen::GetScreen();
DCHECK(screen);
display::DisplayChangeObserver observer(display_manager());
display::DisplayConfigurator::DisplayStateList outputs;
std::unique_ptr<display::DisplaySnapshot> internal_snapshot =
display::FakeDisplaySnapshot::Builder()
.SetId(internal_id)
.SetType(display::DISPLAY_CONNECTION_TYPE_INTERNAL)
.SetDPI(210) // 1.6f
.SetNativeMode(MakeDisplayMode())
.Build();
internal_snapshot->set_current_mode(internal_snapshot->native_mode());
outputs.push_back(internal_snapshot.get());
observer.GetStateForDisplayIds(outputs);
observer.OnDisplayConfigurationChanged(outputs);
EXPECT_EQ(1u, display_manager()->GetNumDisplays());
internal_snapshot->set_origin({0, 1000});
std::unique_ptr<display::DisplaySnapshot> external_snapshot =
display::FakeDisplaySnapshot::Builder()
.SetId(external_id)
.SetNativeMode(MakeDisplayMode())
.AddMode(MakeDisplayMode())
.Build();
// "Connected display" has the current mode.
external_snapshot->set_current_mode(external_snapshot->native_mode());
outputs.push_back(external_snapshot.get());
reset();
observer.GetStateForDisplayIds(outputs);
observer.OnDisplayConfigurationChanged(outputs);
EXPECT_EQ(2u, display_manager()->GetNumDisplays());
EXPECT_TRUE(changed_metrics() &
display::DisplayObserver::DISPLAY_METRIC_BOUNDS);
}
TEST_F(DisplayManagerTest, InternalDisplayWithMultipleModesAndOneNative) {
int display_id = 1000;
display::ManagedDisplayInfo native_display_info =
CreateDisplayInfo(display_id, gfx::Rect(0, 0, 800, 300));
display::ManagedDisplayInfo::ManagedDisplayModeList display_modes;
display_modes.emplace_back(gfx::Size(1000, 500), 58.0f, false, false);
display_modes.emplace_back(gfx::Size(800, 300), 59.0f, false, true);
display_modes.emplace_back(gfx::Size(400, 500), 60.0f, false, false);
native_display_info.SetManagedDisplayModes(display_modes);
display::SetInternalDisplayIds({display_id});
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(native_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
display::ManagedDisplayMode expected_mode(gfx::Size(800, 300), 59.0f, false,
true);
display::ManagedDisplayMode active_mode;
EXPECT_TRUE(
display_manager()->GetActiveModeForDisplayId(display_id, &active_mode));
EXPECT_TRUE(expected_mode.IsEquivalent(active_mode));
}
// It's difficult to test with full stack due to crbug.com/771178.
// Improve the coverage once it is fixed.
TEST_F(DisplayManagerTest, ForcedMirrorMode) {
// Disable restoring mirror mode to prevent interference from previous
// display configuration.
display_manager()->set_disable_restoring_mirror_mode_for_test(true);
constexpr int64_t id1 = 1;
constexpr int64_t id2 = 2;
display::Screen* screen = display::Screen::GetScreen();
DCHECK(screen);
display::DisplayChangeObserver observer(display_manager());
display::DisplayConfigurator::DisplayStateList outputs;
std::unique_ptr<display::DisplaySnapshot> snapshot1 =
display::FakeDisplaySnapshot::Builder()
.SetId(id1)
.SetNativeMode(MakeDisplayMode())
.Build();
std::unique_ptr<display::DisplaySnapshot> snapshot2 =
display::FakeDisplaySnapshot::Builder()
.SetId(id2)
.SetNativeMode(MakeDisplayMode())
.SetOrigin({0, 1000})
.Build();
snapshot1->set_current_mode(snapshot1->native_mode());
snapshot2->set_current_mode(snapshot2->native_mode());
outputs.push_back(snapshot1.get());
outputs.push_back(snapshot2.get());
EXPECT_EQ(display::MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED,
observer.GetStateForDisplayIds(outputs));
display_manager()->layout_store()->set_forced_mirror_mode_for_tablet(true);
observer.OnDisplayConfigurationChanged(outputs);
const display::DisplayIdList current_list =
display_manager()->GetConnectedDisplayIdList();
display_manager()->layout_store()->UpdateDefaultUnified(current_list,
false /* unified */);
EXPECT_EQ(display::MULTIPLE_DISPLAY_STATE_MULTI_MIRROR,
observer.GetStateForDisplayIds(outputs));
display_manager()->layout_store()->set_forced_mirror_mode_for_tablet(false);
EXPECT_EQ(display::MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED,
observer.GetStateForDisplayIds(outputs));
display_manager()->set_disable_restoring_mirror_mode_for_test(false);
}
namespace {
class DisplayManagerOrientationTest : public DisplayManagerTest {
public:
DisplayManagerOrientationTest() = default;
DisplayManagerOrientationTest(const DisplayManagerOrientationTest&) = delete;
DisplayManagerOrientationTest& operator=(
const DisplayManagerOrientationTest&) = delete;
~DisplayManagerOrientationTest() override = default;
void SetUp() override {
DisplayManagerTest::SetUp();
portrait_primary.Set(ACCELEROMETER_SOURCE_SCREEN, -base::kMeanGravityFloat,
0.f, 0.f);
portrait_secondary.Set(ACCELEROMETER_SOURCE_SCREEN, base::kMeanGravityFloat,
0.f, 0.f);
landscape_primary.Set(ACCELEROMETER_SOURCE_SCREEN, 0,
-base::kMeanGravityFloat, 0.f);
}
protected:
AccelerometerUpdate portrait_primary;
AccelerometerUpdate portrait_secondary;
AccelerometerUpdate landscape_primary;
};
class TestObserver : public ScreenOrientationController::Observer {
public:
TestObserver() = default;
~TestObserver() override = default;
void OnUserRotationLockChanged() override { count_++; }
int countAndReset() {
int tmp = count_;
count_ = 0;
return tmp;
}
private:
int count_ = 0;
};
} // namespace
TEST_F(DisplayManagerOrientationTest, SaveRestoreUserRotationLock) {
Shell* shell = Shell::Get();
display::DisplayManager* display_manager = shell->display_manager();
display::test::DisplayManagerTestApi(display_manager)
.SetFirstDisplayAsInternalDisplay();
ScreenOrientationController* orientation_controller =
shell->screen_orientation_controller();
ScreenOrientationControllerTestApi test_api(orientation_controller);
TestObserver test_observer;
orientation_controller->AddObserver(&test_observer);
// Set up windows with portrait, landscape, and any.
aura::Window* window_a = CreateTestWindowInShellWithId(0);
{
window_a->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
orientation_controller->LockOrientationForWindow(
window_a, chromeos::OrientationType::kAny);
}
aura::Window* window_p = CreateTestWindowInShellWithId(0);
{
window_p->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
orientation_controller->LockOrientationForWindow(
window_p, chromeos::OrientationType::kPortrait);
}
aura::Window* window_l = CreateTestWindowInShellWithId(0);
{
window_l->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
orientation_controller->LockOrientationForWindow(
window_l, chromeos::OrientationType::kLandscape);
}
DisplayConfigurationController* configuration_controller =
shell->display_configuration_controller();
display::Screen* screen = display::Screen::GetScreen();
// Rotate to portrait in clamshell.
configuration_controller->SetDisplayRotation(
screen->GetPrimaryDisplay().id(), display::Display::ROTATE_270,
display::Display::RotationSource::USER);
EXPECT_EQ(display::Display::ROTATE_270,
screen->GetPrimaryDisplay().rotation());
EXPECT_FALSE(display_manager->registered_internal_display_rotation_lock());
EXPECT_EQ(0, test_observer.countAndReset());
// Just enabling will not save the lock.
ash::TabletModeControllerTestApi().EnterTabletMode();
EXPECT_EQ(1, test_observer.countAndReset());
EXPECT_EQ(display::Display::ROTATE_0, screen->GetPrimaryDisplay().rotation());
EXPECT_FALSE(display_manager->registered_internal_display_rotation_lock());
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
test_api.GetCurrentOrientation());
// Enable lock at 0.
orientation_controller->ToggleUserRotationLock();
EXPECT_EQ(1, test_observer.countAndReset());
EXPECT_TRUE(display_manager->registered_internal_display_rotation_lock());
EXPECT_EQ(display::Display::ROTATE_0,
display_manager->registered_internal_display_rotation());
// Application can overwrite the locked orientation.
wm::ActivateWindow(window_p);
EXPECT_EQ(display::Display::ROTATE_270,
screen->GetPrimaryDisplay().rotation());
EXPECT_EQ(display::Display::ROTATE_0,
display_manager->registered_internal_display_rotation());
EXPECT_EQ(0, test_observer.countAndReset());
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
test_api.GetCurrentOrientation());
// Any will rotate to the locked rotation.
wm::ActivateWindow(window_a);
EXPECT_EQ(display::Display::ROTATE_0, screen->GetPrimaryDisplay().rotation());
EXPECT_TRUE(display_manager->registered_internal_display_rotation_lock());
EXPECT_EQ(display::Display::ROTATE_0,
display_manager->registered_internal_display_rotation());
EXPECT_EQ(0, test_observer.countAndReset());
wm::ActivateWindow(window_l);
EXPECT_EQ(display::Display::ROTATE_0, screen->GetPrimaryDisplay().rotation());
EXPECT_TRUE(display_manager->registered_internal_display_rotation_lock());
EXPECT_EQ(display::Display::ROTATE_0,
display_manager->registered_internal_display_rotation());
EXPECT_EQ(0, test_observer.countAndReset());
// Exit tablet mode reset to clamshell's rotation, which is 90.
ash::TabletModeControllerTestApi().LeaveTabletMode();
EXPECT_EQ(1, test_observer.countAndReset());
EXPECT_EQ(display::Display::ROTATE_270,
screen->GetPrimaryDisplay().rotation());
// Activate Any.
wm::ActivateWindow(window_a);
ash::TabletModeControllerTestApi().EnterTabletMode();
EXPECT_EQ(1, test_observer.countAndReset());
// Entering with active ANY will lock again to landscape.
EXPECT_EQ(display::Display::ROTATE_0, screen->GetPrimaryDisplay().rotation());
wm::ActivateWindow(window_p);
EXPECT_EQ(display::Display::ROTATE_270,
screen->GetPrimaryDisplay().rotation());
EXPECT_EQ(0, test_observer.countAndReset());
orientation_controller->ToggleUserRotationLock();
orientation_controller->ToggleUserRotationLock();
EXPECT_EQ(2, test_observer.countAndReset());
EXPECT_TRUE(display_manager->registered_internal_display_rotation_lock());
EXPECT_EQ(display::Display::ROTATE_270,
display_manager->registered_internal_display_rotation());
wm::ActivateWindow(window_l);
EXPECT_EQ(display::Display::ROTATE_0, screen->GetPrimaryDisplay().rotation());
EXPECT_EQ(display::Display::ROTATE_270,
display_manager->registered_internal_display_rotation());
// ANY will rotate to locked rotation.
wm::ActivateWindow(window_a);
EXPECT_EQ(display::Display::ROTATE_270,
screen->GetPrimaryDisplay().rotation());
orientation_controller->RemoveObserver(&test_observer);
}
TEST_F(DisplayManagerOrientationTest, UserRotationLockReverse) {
Shell* shell = Shell::Get();
display::DisplayManager* display_manager = shell->display_manager();
display::test::DisplayManagerTestApi test_api(display_manager);
test_api.SetFirstDisplayAsInternalDisplay();
ScreenOrientationController* orientation_controller =
shell->screen_orientation_controller();
// Set up windows with portrait, landscape, and any.
aura::Window* window = CreateTestWindowInShellWithId(0);
window->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
display::Screen* screen = display::Screen::GetScreen();
// Just enabling will not save the lock.
ash::TabletModeControllerTestApi().EnterTabletMode();
orientation_controller->LockOrientationForWindow(
window, chromeos::OrientationType::kPortrait);
EXPECT_EQ(display::Display::ROTATE_270,
screen->GetPrimaryDisplay().rotation());
orientation_controller->OnAccelerometerUpdated(portrait_secondary);
EXPECT_EQ(display::Display::ROTATE_90,
screen->GetPrimaryDisplay().rotation());
orientation_controller->OnAccelerometerUpdated(portrait_primary);
EXPECT_EQ(display::Display::ROTATE_270,
screen->GetPrimaryDisplay().rotation());
// Enable lock at 270.
orientation_controller->ToggleUserRotationLock();
EXPECT_TRUE(display_manager->registered_internal_display_rotation_lock());
EXPECT_EQ(display::Display::ROTATE_270,
display_manager->registered_internal_display_rotation());
orientation_controller->OnAccelerometerUpdated(portrait_secondary);
EXPECT_EQ(display::Display::ROTATE_270,
screen->GetPrimaryDisplay().rotation());
}
TEST_F(DisplayManagerOrientationTest, LockToSpecificOrientation) {
Shell* shell = Shell::Get();
display::DisplayManager* display_manager = shell->display_manager();
display::test::DisplayManagerTestApi(display_manager)
.SetFirstDisplayAsInternalDisplay();
ScreenOrientationController* orientation_controller =
shell->screen_orientation_controller();
ScreenOrientationControllerTestApi test_api(orientation_controller);
aura::Window* window_a = CreateTestWindowInShellWithId(0);
{
window_a->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
orientation_controller->LockOrientationForWindow(
window_a, chromeos::OrientationType::kAny);
}
wm::ActivateWindow(window_a);
ash::TabletModeControllerTestApi().EnterTabletMode();
orientation_controller->OnAccelerometerUpdated(portrait_primary);
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
test_api.GetCurrentOrientation());
orientation_controller->OnAccelerometerUpdated(portrait_secondary);
aura::Window* window_lsc = CreateTestWindowInShellWithId(1);
window_lsc->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
aura::Window* window_psc = CreateTestWindowInShellWithId(1);
window_psc->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
orientation_controller->LockOrientationForWindow(
window_psc, chromeos::OrientationType::kPortraitSecondary);
orientation_controller->LockOrientationForWindow(
window_psc, chromeos::OrientationType::kCurrent);
wm::ActivateWindow(window_psc);
orientation_controller->LockOrientationForWindow(
window_lsc, chromeos::OrientationType::kLandscapeSecondary);
orientation_controller->LockOrientationForWindow(
window_lsc, chromeos::OrientationType::kCurrent);
EXPECT_EQ(chromeos::OrientationType::kPortraitSecondary,
test_api.GetCurrentOrientation());
// The orientation should stay portrait secondary.
orientation_controller->OnAccelerometerUpdated(portrait_primary);
EXPECT_EQ(chromeos::OrientationType::kPortraitSecondary,
test_api.GetCurrentOrientation());
wm::ActivateWindow(window_lsc);
EXPECT_EQ(chromeos::OrientationType::kLandscapeSecondary,
test_api.GetCurrentOrientation());
// The orientation should stay landscape secondary.
orientation_controller->OnAccelerometerUpdated(landscape_primary);
EXPECT_EQ(chromeos::OrientationType::kLandscapeSecondary,
test_api.GetCurrentOrientation());
wm::ActivateWindow(window_a);
orientation_controller->OnAccelerometerUpdated(portrait_primary);
// Switching to |window_a| enables rotation.
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
test_api.GetCurrentOrientation());
// The orientation has already been locked to secondary once, so
// it should switch back to the portrait secondary.
wm::ActivateWindow(window_psc);
EXPECT_EQ(chromeos::OrientationType::kPortraitSecondary,
test_api.GetCurrentOrientation());
}
// crbug.com/734107
TEST_F(DisplayManagerOrientationTest, DisplayChangeShouldNotSaveUserRotation) {
Shell* shell = Shell::Get();
display::DisplayManager* display_manager = shell->display_manager();
display::test::DisplayManagerTestApi test_api(display_manager);
test_api.SetFirstDisplayAsInternalDisplay();
display::Screen* screen = display::Screen::GetScreen();
ash::TabletModeControllerTestApi().EnterTabletMode();
// Emulate that Animator is calling this async when animation is completed.
display_manager->SetDisplayRotation(
screen->GetPrimaryDisplay().id(), display::Display::ROTATE_90,
display::Display::RotationSource::ACCELEROMETER);
EXPECT_EQ(display::Display::ROTATE_90,
screen->GetPrimaryDisplay().rotation());
ash::TabletModeControllerTestApi().LeaveTabletMode();
EXPECT_EQ(display::Display::ROTATE_0, screen->GetPrimaryDisplay().rotation());
}
TEST_F(DisplayManagerTest, HardwareMirrorMode) {
// Create three displays with the same origin in frame buffer.
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
constexpr int64_t first_mirror_id = 11;
constexpr int64_t second_mirror_id = 12;
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 500, 400)));
display_info_list.push_back(
CreateDisplayInfo(first_mirror_id, gfx::Rect(0, 0, 500, 400)));
display_info_list.push_back(
CreateDisplayInfo(second_mirror_id, gfx::Rect(0, 0, 500, 400)));
// mirrored across 3 displays...
display_manager()->OnNativeDisplaysChanged(display_info_list);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(3U, display_manager()->num_connected_displays());
EXPECT_EQ(internal_display_id, display_manager()->mirroring_source_id());
EXPECT_EQ(gfx::Rect(0, 0, 500, 400),
GetDisplayForId(internal_display_id).bounds());
const display::DisplayIdList id_list =
display_manager()->GetMirroringDestinationDisplayIdList();
ASSERT_EQ(2U, id_list.size());
EXPECT_EQ(11U, id_list[0]);
EXPECT_EQ(12U, id_list[1]);
EXPECT_FALSE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_TRUE(display_manager()->IsInHardwareMirrorMode());
}
TEST_F(DisplayManagerTest, SoftwareMirrorModeBasics) {
UpdateDisplay("300x400,400x500,500x600");
// There's not mirror window by default.
MirrorWindowTestApi test_api;
EXPECT_TRUE(test_api.GetHosts().empty());
TestDisplayObserver display_observer;
display::Screen::GetScreen()->AddObserver(&display_observer);
// Turn on mirror mode.
SetSoftwareMirrorMode(true);
EXPECT_TRUE(display_observer.changed_and_reset());
EXPECT_EQ(1U, display_manager()->GetNumDisplays());
EXPECT_EQ(gfx::Rect(0, 0, 300, 400),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
std::vector<aura::WindowTreeHost*> host_list = test_api.GetHosts();
ASSERT_EQ(2U, host_list.size());
EXPECT_EQ(gfx::Size(400, 500), host_list[0]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(300, 400), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::Size(500, 600), host_list[1]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(300, 400), host_list[1]->window()->bounds().size());
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_FALSE(display_manager()->IsInHardwareMirrorMode());
// Turn off mirror mode.
SetSoftwareMirrorMode(false);
EXPECT_TRUE(display_observer.changed_and_reset());
EXPECT_EQ(3U, display_manager()->GetNumDisplays());
host_list = test_api.GetHosts();
EXPECT_TRUE(host_list.empty());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// Make sure the mirror window has the pixel size of the
// source display.
SetSoftwareMirrorMode(true);
EXPECT_TRUE(display_observer.changed_and_reset());
UpdateDisplay("[email protected],400x500,500x600");
EXPECT_FALSE(display_observer.changed_and_reset());
host_list = test_api.GetHosts();
EXPECT_EQ(gfx::Size(300, 400), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::Size(300, 400), host_list[1]->window()->bounds().size());
UpdateDisplay("310x410*2,400x500,500x600");
EXPECT_FALSE(display_observer.changed_and_reset());
host_list = test_api.GetHosts();
EXPECT_EQ(gfx::Size(310, 410), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::Size(310, 410), host_list[1]->window()->bounds().size());
UpdateDisplay("320x420/r,400x500,500x600");
EXPECT_FALSE(display_observer.changed_and_reset());
host_list = test_api.GetHosts();
EXPECT_EQ(gfx::Size(420, 320), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::Size(420, 320), host_list[1]->window()->bounds().size());
UpdateDisplay("330x440/r,400x500,500x600");
EXPECT_FALSE(display_observer.changed_and_reset());
host_list = test_api.GetHosts();
EXPECT_EQ(gfx::Size(440, 330), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::Size(440, 330), host_list[1]->window()->bounds().size());
// Overscan insets are ignored.
UpdateDisplay("400x600/o,600x800/o,500x600/o");
EXPECT_FALSE(display_observer.changed_and_reset());
host_list = test_api.GetHosts();
EXPECT_EQ(gfx::Size(400, 600), host_list[0]->window()->bounds().size());
EXPECT_EQ(gfx::Size(400, 600), host_list[1]->window()->bounds().size());
display::Screen::GetScreen()->RemoveObserver(&display_observer);
}
TEST_F(DisplayManagerTest, SwitchToAndFromSoftwareMirrorMode) {
// Don't check root window destruction in unified mode.
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
UpdateDisplay("300x400,400x500,500x600");
// Switch from extended to mirroring.
SetSoftwareMirrorMode(true);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_FALSE(display_manager()->IsInUnifiedMode());
// Switch from mirroring to extended.
SetSoftwareMirrorMode(false);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_FALSE(display_manager()->IsInUnifiedMode());
// Switch from mirroring to unified, but it fails.
SetSoftwareMirrorMode(true);
display_manager()->SetUnifiedDesktopEnabled(true);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_FALSE(display_manager()->IsInUnifiedMode());
// Turn off mirroring, it switches to unified.
SetSoftwareMirrorMode(false);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_TRUE(display_manager()->IsInUnifiedMode());
// Switch from unified to mirroring.
SetSoftwareMirrorMode(true);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_FALSE(display_manager()->IsInUnifiedMode());
}
TEST_F(DisplayManagerTest, SourceAndDestinationInSoftwareMirrorMode) {
constexpr int64_t first_display_id = 10;
constexpr int64_t second_display_id = 11;
constexpr int64_t third_display_id = 12;
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.emplace_back(
CreateDisplayInfo(first_display_id, gfx::Rect(0, 0, 200, 100)));
display_info_list.emplace_back(
CreateDisplayInfo(second_display_id, gfx::Rect(1, 1, 500, 400)));
display_info_list.emplace_back(
CreateDisplayInfo(third_display_id, gfx::Rect(2, 2, 500, 400)));
// Connect all displays.
display_manager()->OnNativeDisplaysChanged(display_info_list);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(display::kInvalidDisplayId,
display_manager()->mirroring_source_id());
EXPECT_TRUE(
display_manager()->GetMirroringDestinationDisplayIdList().empty());
// Activate software mirror mode.
SetSoftwareMirrorMode(true);
EXPECT_EQ(first_display_id, display_manager()->mirroring_source_id());
display::DisplayIdList id_list =
display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(2U, id_list.size());
EXPECT_EQ(second_display_id, id_list[0]);
EXPECT_EQ(third_display_id, id_list[1]);
// Set the second display as internal display.
SetSoftwareMirrorMode(false);
display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
second_display_id);
display_manager()->OnNativeDisplaysChanged(display_info_list);
SetSoftwareMirrorMode(true);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(second_display_id, display_manager()->mirroring_source_id());
id_list = display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(2U, id_list.size());
EXPECT_EQ(first_display_id, id_list[0]);
EXPECT_EQ(third_display_id, id_list[1]);
}
TEST_F(DisplayManagerTest, CompositingCursorInMultiSoftwareMirroring) {
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
constexpr int64_t first_mirror_id = 11;
constexpr int64_t second_mirror_id = 12;
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 200, 100)));
display_info_list.push_back(
CreateDisplayInfo(first_mirror_id, gfx::Rect(1, 1, 500, 400)));
display_info_list.push_back(
CreateDisplayInfo(second_mirror_id, gfx::Rect(2, 2, 500, 400)));
// Connect all displays, cursor compositing is disabled by default.
display_manager()->OnNativeDisplaysChanged(display_info_list);
base::RunLoop().RunUntilIdle();
CursorWindowController* cursor_window_controller =
Shell::Get()->window_tree_host_manager()->cursor_window_controller();
EXPECT_FALSE(cursor_window_controller->is_cursor_compositing_enabled());
MirrorWindowTestApi test_api;
EXPECT_EQ(nullptr, test_api.GetCursorHostWindow());
// Turn on mirror mode, cursor compositing is enabled and cursor window is
// composited in internal display's root window.
SetSoftwareMirrorMode(true);
EXPECT_TRUE(cursor_window_controller->is_cursor_compositing_enabled());
EXPECT_TRUE(Shell::GetRootWindowForDisplayId(internal_display_id)
->Contains(test_api.GetCursorHostWindow()));
// Turn off mirror mode, cursor compositing is disabled and cursor window does
// not exist.
SetSoftwareMirrorMode(false);
EXPECT_FALSE(cursor_window_controller->is_cursor_compositing_enabled());
EXPECT_EQ(nullptr, test_api.GetCursorHostWindow());
}
TEST_F(DisplayManagerTest, MirrorModeRestore) {
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
constexpr int64_t first_display_id = 210000001;
constexpr int64_t second_display_id = 220000002;
const int64_t first_display_masked_id =
display::GetDisplayIdWithoutOutputIndex(first_display_id);
const int64_t second_display_masked_id =
display::GetDisplayIdWithoutOutputIndex(second_display_id);
display::ManagedDisplayInfo first_mirror_info =
CreateDisplayInfo(first_display_id, gfx::Rect(1, 1, 500, 400));
display::ManagedDisplayInfo second_mirror_info =
CreateDisplayInfo(second_display_id, gfx::Rect(2, 2, 500, 400));
std::vector<display::ManagedDisplayInfo> display_info_list;
// There's no external display now.
display_info_list.push_back(
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 200, 100)));
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_TRUE(display_manager()->external_display_mirror_info().empty());
// Connect the first external display.
display_info_list.push_back(first_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_TRUE(display_manager()->external_display_mirror_info().empty());
// Turn on mirror mode.
SetSoftwareMirrorMode(true);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(1U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
// Remove the first external display.
display_info_list.erase(display_info_list.end() - 1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(1U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
// Reconnect the first external display.
display_info_list.push_back(first_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(1U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
// Remove the first external display.
display_info_list.erase(display_info_list.end() - 1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(1U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
// Connect the second external display.
display_info_list.push_back(second_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(1U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
// Remove the second external display.
display_info_list.erase(display_info_list.end() - 1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(1U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
// Add the first and then add the second external display (not mirrored
// before).
display_info_list.push_back(first_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
display_info_list.push_back(second_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(2U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
second_display_masked_id));
// Remove the second display.
display_info_list.erase(display_info_list.end() - 1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(2U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
second_display_masked_id));
// Remove the first display and then add the second display.
display_info_list.erase(display_info_list.end() - 1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
display_info_list.push_back(second_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(2U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
second_display_masked_id));
// Turn off mirror mode.
SetSoftwareMirrorMode(false);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(1U, display_manager()->external_display_mirror_info().size());
EXPECT_TRUE(display_manager()->external_display_mirror_info().count(
first_display_masked_id));
// Add the first display (mirrored before).
display_info_list.push_back(first_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_TRUE(display_manager()->external_display_mirror_info().empty());
}
TEST_F(DisplayManagerTest, MixedMirrorModeBasics) {
UpdateDisplay("300x400,400x500,500x600");
display::DisplayIdList id_list =
display_manager()->GetConnectedDisplayIdList();
// Turn on mixed mirror mode. (Mirror from the first display to the second
// display)
display::DisplayIdList dst_ids;
dst_ids.emplace_back(id_list[1]);
std::optional<display::MixedMirrorModeParams> mixed_params(
std::in_place, id_list[0], dst_ids);
display_manager()->SetMirrorMode(display::MirrorMode::kMixed, mixed_params);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(id_list[0], display_manager()->mirroring_source_id());
display::DisplayIdList destination_ids =
display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(1U, destination_ids.size());
EXPECT_EQ(id_list[1], destination_ids[0]);
EXPECT_TRUE(display_manager()->mixed_mirror_mode_params());
EXPECT_EQ(gfx::Point(300, 0),
display_manager()->GetDisplayForId(id_list[2]).bounds().origin());
// Turn off mirror mode.
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_FALSE(display_manager()->mixed_mirror_mode_params());
EXPECT_EQ(gfx::Point(300, 0),
display_manager()->GetDisplayForId(id_list[1]).bounds().origin());
EXPECT_EQ(gfx::Point(700, 0),
display_manager()->GetDisplayForId(id_list[2]).bounds().origin());
}
TEST_F(DisplayManagerTest, MixedMirrorModeToMirrorMode) {
UpdateDisplay("300x400,400x500,500x600");
display::DisplayIdList id_list =
display_manager()->GetConnectedDisplayIdList();
// Turn on mixed mirror mode. (Mirror from the first display to the second
// display)
display::DisplayIdList dst_ids;
dst_ids.emplace_back(id_list[1]);
std::optional<display::MixedMirrorModeParams> mixed_params(
std::in_place, id_list[0], dst_ids);
display_manager()->SetMirrorMode(display::MirrorMode::kMixed, mixed_params);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(id_list[0], display_manager()->mirroring_source_id());
display::DisplayIdList destination_ids =
display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(1U, destination_ids.size());
EXPECT_EQ(id_list[1], destination_ids[0]);
EXPECT_TRUE(display_manager()->mixed_mirror_mode_params());
// Overwrite mixed mirror mode with default mirror mode (Mirror all
// displays).
display_manager()->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(id_list[0], display_manager()->mirroring_source_id());
destination_ids = display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(2U, destination_ids.size());
EXPECT_EQ(id_list[1], destination_ids[0]);
EXPECT_EQ(id_list[2], destination_ids[1]);
EXPECT_FALSE(display_manager()->mixed_mirror_mode_params());
}
TEST_F(DisplayManagerTest, MirrorModeToMixedMirrorMode) {
UpdateDisplay("300x400,400x500,500x600");
display::DisplayIdList id_list =
display_manager()->GetConnectedDisplayIdList();
// Turn on mirror mode.
display_manager()->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(id_list[0], display_manager()->mirroring_source_id());
display::DisplayIdList destination_ids =
display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(2U, destination_ids.size());
EXPECT_EQ(id_list[1], destination_ids[0]);
EXPECT_EQ(id_list[2], destination_ids[1]);
EXPECT_FALSE(display_manager()->mixed_mirror_mode_params());
// Overwrite default mirror mode with mixed mirror mode. (Mirror from the
// first display to the second display)
display::DisplayIdList dst_ids;
dst_ids.emplace_back(id_list[1]);
std::optional<display::MixedMirrorModeParams> mixed_params(
std::in_place, id_list[0], dst_ids);
display_manager()->SetMirrorMode(display::MirrorMode::kMixed, mixed_params);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(id_list[0], display_manager()->mirroring_source_id());
destination_ids = display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(1U, destination_ids.size());
EXPECT_EQ(id_list[1], destination_ids[0]);
EXPECT_TRUE(display_manager()->mixed_mirror_mode_params());
}
TEST_F(DisplayManagerTest, MixedMirrorModeRestore) {
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
constexpr int64_t first_display_id = 210000001;
constexpr int64_t second_display_id = 220000002;
display::ManagedDisplayInfo first_mirror_info =
CreateDisplayInfo(first_display_id, gfx::Rect(1, 1, 500, 400));
display::ManagedDisplayInfo second_mirror_info =
CreateDisplayInfo(second_display_id, gfx::Rect(2, 2, 500, 400));
std::vector<display::ManagedDisplayInfo> display_info_list;
// Connect the first and second displays.
display_info_list.push_back(
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 200, 100)));
display_info_list.push_back(first_mirror_info);
display_info_list.push_back(second_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// Turn on mixed mirror mode. (Mirror from the internal display to the
// first display)
display::DisplayIdList dst_ids;
dst_ids.emplace_back(first_display_id);
std::optional<display::MixedMirrorModeParams> mixed_params(
std::in_place, internal_display_id, dst_ids);
display_manager()->SetMirrorMode(display::MirrorMode::kMixed, mixed_params);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(internal_display_id, display_manager()->mirroring_source_id());
display::DisplayIdList destination_ids =
display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(1U, destination_ids.size());
EXPECT_EQ(first_display_id, destination_ids[0]);
// Remove the second display. Mirroring is not changed.
display_info_list.erase(display_info_list.end() - 1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(internal_display_id, display_manager()->mirroring_source_id());
destination_ids = display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(1U, destination_ids.size());
EXPECT_EQ(first_display_id, destination_ids[0]);
// Add the second display. Mirroring is not changed.
display_info_list.push_back(second_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(internal_display_id, display_manager()->mirroring_source_id());
destination_ids = display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(1U, destination_ids.size());
EXPECT_EQ(first_display_id, destination_ids[0]);
// Remove the first display. Mirroring ends.
display_info_list.erase(display_info_list.end() - 2);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
// Add the first display. Mirroring is restored.
display_info_list.push_back(first_mirror_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(internal_display_id, display_manager()->mirroring_source_id());
destination_ids = display_manager()->GetMirroringDestinationDisplayIdList();
EXPECT_EQ(1U, destination_ids.size());
EXPECT_EQ(first_display_id, destination_ids[0]);
}
TEST_F(DisplayManagerTest, MirrorModeRestoreAfterResume) {
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
constexpr int64_t external_display_id = 210000001;
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.emplace_back(
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 200, 100)));
display_info_list.emplace_back(
CreateDisplayInfo(external_display_id, gfx::Rect(1, 1, 500, 400)));
// Turn on mirror mode.
display_manager()->OnNativeDisplaysChanged(display_info_list);
display_manager()->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
// Suspend.
display_manager()->SetMultiDisplayMode(display::DisplayManager::MIRRORING);
display_manager()->OnNativeDisplaysChanged(
std::vector<display::ManagedDisplayInfo>());
EXPECT_TRUE(display_manager()->IsInMirrorMode());
// Resume.
display_manager()->SetMultiDisplayMode(display::DisplayManager::MIRRORING);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
}
TEST_F(DisplayManagerTest, SoftwareMirrorRotationForTablet) {
enum Scenario {
// Auto mirror mode set when entering tablet mode.
kForcedMirror,
// Manual mirror mode with device is in physical tablet mode.
kPhysicalTablet,
};
// Set the first display as internal display so that the tablet mode can be
// enabled.
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
auto tablet_mode_test_api = std::make_unique<TabletModeControllerTestApi>();
for (auto sc : {kForcedMirror, kPhysicalTablet}) {
SCOPED_TRACE(testing::Message() << "Scenario: " << sc);
UpdateDisplay("400x300,800x700");
switch (sc) {
case kForcedMirror: {
// Simulate turning on mirror mode triggered by tablet mode on.
tablet_mode_test_api->EnterTabletMode();
base::RunLoop().RunUntilIdle();
break;
}
case kPhysicalTablet: {
// Simulate physical tablet mode with clamshell ui.
tablet_mode_test_api->EnterTabletMode();
tablet_mode_test_api->AttachExternalMouse();
base::RunLoop().RunUntilIdle();
// Manual mirror mode.
SetSoftwareMirrorMode(true);
ASSERT_TRUE(Shell::Get()
->tablet_mode_controller()
->is_in_tablet_physical_state());
ASSERT_FALSE(display::Screen::GetScreen()->InTabletMode());
break;
}
}
ASSERT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
MirrorWindowTestApi test_api;
std::vector<aura::WindowTreeHost*> host_list = test_api.GetHosts();
ASSERT_EQ(1U, host_list.size());
EXPECT_EQ(gfx::Size(800, 700), host_list[0]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(400, 300), host_list[0]->window()->bounds().size());
// Test the target display's bounds after the transforms are applied.
gfx::RectF transformed_rect1 =
Shell::Get()->GetPrimaryRootWindow()->transform().MapRect(gfx::RectF(
display::Screen::GetScreen()->GetPrimaryDisplay().bounds()));
transformed_rect1 =
host_list[0]->window()->transform().MapRect(transformed_rect1);
EXPECT_EQ(gfx::RectF(0.0f, 50.0f, 800.0f, 600.0f), transformed_rect1);
// Rotate the source display by 90 degrees.
UpdateDisplay("400x300/r,800x700");
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(gfx::Rect(0, 0, 300, 400),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
host_list = test_api.GetHosts();
ASSERT_EQ(1U, host_list.size());
EXPECT_EQ(gfx::Size(800, 700), host_list[0]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(300, 400), host_list[0]->window()->bounds().size());
// Test the target display's bounds after the transforms are applied.
gfx::RectF transformed_rect2 =
Shell::Get()->GetPrimaryRootWindow()->transform().MapRect(gfx::RectF(
display::Screen::GetScreen()->GetPrimaryDisplay().bounds()));
transformed_rect2 =
host_list[0]->window()->transform().MapRect(transformed_rect2);
// Use gfx::ToEnclosingRect because `transformed_rect2` has rounding errors:
// 137.000000,0.000000 524.999939x699.999939
EXPECT_EQ(gfx::Rect(137.0f, 0.0f, 525.0f, 700.0f),
gfx::ToEnclosingRect(transformed_rect2));
// Change the bounds of the source display and rotate the source display by
// 90 degrees.
UpdateDisplay("300x400/r,800x700");
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
host_list = test_api.GetHosts();
ASSERT_EQ(1U, host_list.size());
EXPECT_EQ(gfx::Size(800, 700), host_list[0]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(400, 300), host_list[0]->window()->bounds().size());
// Test the target display's bounds after the transforms are applied.
gfx::RectF transformed_rect3 =
Shell::Get()->GetPrimaryRootWindow()->transform().MapRect(gfx::RectF(
display::Screen::GetScreen()->GetPrimaryDisplay().bounds()));
transformed_rect3 =
host_list[0]->window()->transform().MapRect(transformed_rect3);
EXPECT_EQ(gfx::RectF(0.0f, 50.0f, 800.0f, 600.0f), transformed_rect3);
}
}
TEST_F(DisplayManagerTest, SoftwareMirrorRotationForNonTablet) {
MirrorWindowTestApi test_api;
UpdateDisplay("400x300,800x700");
// Simulate turning on mirror mode not triggered by tablet mode.
SetSoftwareMirrorMode(true);
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
std::vector<aura::WindowTreeHost*> host_list = test_api.GetHosts();
ASSERT_EQ(1U, host_list.size());
EXPECT_EQ(gfx::Size(800, 700), host_list[0]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(400, 300), host_list[0]->window()->bounds().size());
// Test the target display's bounds after the transforms are applied.
gfx::RectF transformed_rect1 =
Shell::Get()->GetPrimaryRootWindow()->transform().MapRect(gfx::RectF(
display::Screen::GetScreen()->GetPrimaryDisplay().bounds()));
transformed_rect1 =
host_list[0]->window()->transform().MapRect(transformed_rect1);
EXPECT_EQ(gfx::RectF(0.0f, 50.0f, 800.0f, 600.0f), transformed_rect1);
// Rotate the source display by 90 degrees.
UpdateDisplay("400x300/r,800x700");
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(gfx::Rect(0, 0, 300, 400),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
host_list = test_api.GetHosts();
ASSERT_EQ(1U, host_list.size());
EXPECT_EQ(gfx::Size(800, 700), host_list[0]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(300, 400), host_list[0]->window()->bounds().size());
// Test the target display's bounds after the transforms are applied.
gfx::RectF transformed_rect2 =
Shell::Get()->GetPrimaryRootWindow()->transform().MapRect(gfx::RectF(
display::Screen::GetScreen()->GetPrimaryDisplay().bounds()));
transformed_rect2 =
host_list[0]->window()->transform().MapRect(transformed_rect2);
EXPECT_EQ(gfx::RectF(50.0f, 0.0f, 600.0f, 800.0f), transformed_rect2);
// Change the bounds of the source display and rotate the source display by 90
// degrees.
UpdateDisplay("300x400/r,800x700");
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
host_list = test_api.GetHosts();
ASSERT_EQ(1U, host_list.size());
EXPECT_EQ(gfx::Size(800, 700), host_list[0]->GetBoundsInPixels().size());
EXPECT_EQ(gfx::Size(400, 300), host_list[0]->window()->bounds().size());
// Test the target display's bounds after the transforms are applied.
gfx::RectF transformed_rect3 =
Shell::Get()->GetPrimaryRootWindow()->transform().MapRect(gfx::RectF(
display::Screen::GetScreen()->GetPrimaryDisplay().bounds()));
transformed_rect3 =
host_list[0]->window()->transform().MapRect(transformed_rect3);
// Use gfx::ToEnclosingRect because `transformed_rect3` has rounding errors.
EXPECT_EQ(gfx::Rect(0.0f, 137.0f, 700.0f, 525.0f),
gfx::ToEnclosingRect(transformed_rect3));
}
TEST_F(DisplayManagerTest, DPSizeTest) {
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
UpdateDisplay(base::StringPrintf("3840x2160*%s", display::kDsfStr_2_666));
{
gfx::Size expected(1440, 810);
EXPECT_EQ(expected,
display::Screen::GetScreen()->GetPrimaryDisplay().size());
EXPECT_EQ(expected, Shell::GetPrimaryRootWindow()->bounds().size());
}
UpdateDisplay(base::StringPrintf("1920x1200*%s", display::kDsfStr_1_777));
{
gfx::Size expected(1080, 675);
EXPECT_EQ(expected,
display::Screen::GetScreen()->GetPrimaryDisplay().size());
EXPECT_EQ(expected, Shell::GetPrimaryRootWindow()->bounds().size());
}
UpdateDisplay(base::StringPrintf("3000x2000*%s", display::kDsfStr_2_252));
{
gfx::Size expected(1332, 888);
EXPECT_EQ(expected,
display::Screen::GetScreen()->GetPrimaryDisplay().size());
EXPECT_EQ(expected, Shell::GetPrimaryRootWindow()->bounds().size());
}
UpdateDisplay(base::StringPrintf("2160x1440*%s", display::kDsfStr_1_8));
{
gfx::Size expected(1200, 800);
EXPECT_EQ(expected,
display::Screen::GetScreen()->GetPrimaryDisplay().size());
EXPECT_EQ(expected, Shell::GetPrimaryRootWindow()->bounds().size());
}
}
TEST_F(DisplayManagerTest, PanelOrientation) {
int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
display_id);
// The panel is portrait but its orientation is landscape.
display::ManagedDisplayInfo native_display_info =
CreateDisplayInfo(display_id, gfx::Rect(0, 0, 1920, 1080));
native_display_info.set_panel_orientation(
display::PanelOrientation::kRightUp);
const display::ManagedDisplayMode base_mode(gfx::Size(1920, 1080), 60.0f,
false, false);
display::ManagedDisplayInfo::ManagedDisplayModeList mode_list =
CreateInternalManagedDisplayModeList(base_mode);
native_display_info.SetManagedDisplayModes(mode_list);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(native_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// Check display is landscape at ROTATE_0.
EXPECT_EQ(gfx::Size(1080, 1920),
display::Screen::GetScreen()->GetPrimaryDisplay().GetSizeInPixel());
EXPECT_EQ(display::Display::ROTATE_0,
display::Screen::GetScreen()->GetPrimaryDisplay().rotation());
// Check the orientation controller reports correct orientation.
auto* screen_orientation_controller =
Shell::Get()->screen_orientation_controller();
EXPECT_EQ(chromeos::OrientationType::kLandscape,
screen_orientation_controller->natural_orientation());
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
screen_orientation_controller->GetCurrentOrientation());
// Test if changing rotation works as if it's landscape panel.
DisplayConfigurationController::DisableAnimatorForTest();
ScreenOrientationControllerTestApi(screen_orientation_controller)
.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::USER);
EXPECT_EQ(gfx::Size(1920, 1080),
display::Screen::GetScreen()->GetPrimaryDisplay().GetSizeInPixel());
EXPECT_EQ(display::Display::ROTATE_270,
display::Screen::GetScreen()->GetPrimaryDisplay().rotation());
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
screen_orientation_controller->GetCurrentOrientation());
}
TEST_F(DisplayManagerTest, UpdateRootWindowForNewWindows) {
Shell::GetPrimaryRootWindow()->RemoveObserver(this);
// Sets display configuration with three displays, sets the "root window for
// new windows" to the one at index before, removes the one at index 2, and
// checks that the "root window for new windows" is the one at index after.
const auto test_removing_secondary = [this](size_t before, size_t after) {
UpdateDisplay("800x600,800x600,800x600");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
Shell::SetRootWindowForNewWindows(root_windows[before]);
UpdateDisplay("800x600,800x600");
EXPECT_EQ(root_windows[after], Shell::GetRootWindowForNewWindows());
};
test_removing_secondary(0u, 0u);
test_removing_secondary(1u, 1u);
test_removing_secondary(2u, 0u);
// Each iteration sets display configuration with three displays, sets the
// "root window for new windows" to the one at index before, enters unified
// desktop mode, and checks that the "root window for new windows" is the
// primary one.
for (size_t before = 0u; before < 3u; ++before) {
UpdateDisplay("800x600,800x600,800x600");
Shell::SetRootWindowForNewWindows(Shell::GetAllRootWindows()[before]);
display_manager()->SetUnifiedDesktopEnabled(true);
EXPECT_EQ(Shell::GetPrimaryRootWindow(),
Shell::GetRootWindowForNewWindows());
display_manager()->SetUnifiedDesktopEnabled(false);
}
}
// Test that exiting mirror mode in tablet mode with a fullscreen window
// does not cause a crash (crbug.com/1021662).
TEST_F(DisplayManagerTest, ExitMirrorModeInTabletMode) {
// Simulate a tablet with and external display connected.
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
UpdateDisplay("800x600,800x600");
ash::TabletModeControllerTestApi().EnterTabletMode();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
// Create a window to force in-app shelf.
std::unique_ptr<aura::Window> window = CreateTestWindow();
// Exit mirror mode.
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(display_manager()->IsInSoftwareMirrorMode());
}
TEST_F(DisplayManagerTest, DifferentDisplayConnectedToSameOutput) {
// Emulate connect a display to the same output. This can happen when
// a display is swapped while the device is in sleep mode.
auto display_manager_test =
display::test::DisplayManagerTestApi(display_manager());
display_manager_test.SetFirstDisplayAsInternalDisplay();
const auto internal_display_info = display_manager()->GetDisplayInfo(
*display::GetInternalDisplayIds().begin());
constexpr int64_t kExternalId_1 = 210000010;
constexpr int64_t kExternalId_2 = 220000010;
const auto external_info_1 =
display::ManagedDisplayInfo::CreateFromSpecWithID("401+0-400x300",
kExternalId_1);
const auto external_info_2 =
display::ManagedDisplayInfo::CreateFromSpecWithID("401+0-500x400",
kExternalId_2);
const auto second_external_info =
display::ManagedDisplayInfo::CreateFromSpecWithID("0+601-800x600",
230000011);
display_manager()->OnNativeDisplaysChanged(
vector<display::ManagedDisplayInfo>{
internal_display_info, external_info_1, second_external_info});
auto* screen = display::Screen::GetScreen();
EXPECT_EQ(3u, screen->GetAllDisplays().size());
EXPECT_EQ(kExternalId_1, screen->GetAllDisplays()[1].id());
reset();
display_manager()->OnNativeDisplaysChanged(
vector<display::ManagedDisplayInfo>{
internal_display_info, external_info_2, second_external_info});
// There should be 1 display change, 1 removal, and 1 add.
EXPECT_EQ("c1 a1 r1 w1 d1", GetCountSummary());
EXPECT_EQ(3u, screen->GetAllDisplays().size());
EXPECT_EQ(kExternalId_2, screen->GetAllDisplays()[1].id());
}
// Regression test for crbug.com/327282654. This asserts that DisplayManager
// changes that occur during change notification propagation are sequenced
// correctly to all observers.
TEST_F(DisplayManagerTest, DisplayManagerObserverNestedChangesOrdering) {
// Assert that observers receive display configuration notifications in order.
class ChangeOrderingObserver : public display::DisplayManagerObserver {
public:
explicit ChangeOrderingObserver(
base::OnceClosure on_processed_cb = base::NullCallback())
: on_processed_cb_(std::move(on_processed_cb)) {
auto* display_manager = Shell::Get()->display_manager();
EXPECT_EQ(1u, display_manager->GetNumDisplays());
tracked_display_ids_.insert(display_manager->GetDisplayAt(0).id());
}
// display::DisplayManagerObserver:
void OnWillProcessDisplayChanges() override {
EXPECT_EQ(0u, will_process_count_);
will_process_count_++;
}
void OnDidProcessDisplayChanges(
const DisplayConfigurationChange& configuration_change) override {
EXPECT_EQ(1u, will_process_count_);
will_process_count_ = 0;
// Update the set of tracked display ids communicated through did
// process changes.
base::ranges::transform(
configuration_change.added_displays,
std::inserter(tracked_display_ids_, tracked_display_ids_.begin()),
[](const display::Display& display) { return display.id(); });
// If correctly ordered observers should be notified of added displays
// before any changes to the metrics for these displays.
base::ranges::for_each(configuration_change.display_metrics_changes,
[this](const auto& change) {
EXPECT_TRUE(base::Contains(
tracked_display_ids_, change.display->id()));
});
if (on_processed_cb_) {
std::move(on_processed_cb_).Run();
}
}
private:
size_t will_process_count_ = 0;
base::OnceClosure on_processed_cb_;
std::set<int64_t> tracked_display_ids_;
};
// Add a second display and configure the first observer to update the insets
// of the second display when a did process event for the display addition
// is received. The events should be received by the observers in the expected
// order.
ChangeOrderingObserver observer_1(base::BindOnce([]() {
auto* display_manager = Shell::Get()->display_manager();
EXPECT_EQ(2u, display_manager->GetNumDisplays());
const int64_t second_display_id = display_manager->GetDisplayAt(1).id();
display_manager->SetOverscanInsets(second_display_id,
gfx::Insets::TLBR(13, 12, 11, 10));
}));
ChangeOrderingObserver observer_2;
display_manager()->AddDisplayManagerObserver(&observer_1);
display_manager()->AddDisplayManagerObserver(&observer_2);
UpdateDisplay("800x600,800x600");
}
} // namespace ash