// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/display/mirror_window_controller.h" #include "ash/ash_switches.h" #include "ash/display/display_manager.h" #include "ash/display/window_tree_host_manager.h" #include "ash/screen_util.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "ash/test/cursor_manager_test_api.h" #include "ash/test/display_manager_test_api.h" #include "ash/test/mirror_window_test_api.h" #include "base/command_line.h" #include "base/strings/stringprintf.h" #include "ui/aura/env.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/test/test_windows.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/base/hit_test.h" #include "ui/events/test/event_generator.h" namespace ash { namespace { DisplayInfo CreateDisplayInfo(int64_t id, const gfx::Rect& bounds) { DisplayInfo info(id, base::StringPrintf("x-%d", static_cast(id)), false); info.SetBounds(bounds); return info; } class MirrorOnBootTest : public test::AshTestBase { public: MirrorOnBootTest() {} ~MirrorOnBootTest() override {} void SetUp() override { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kAshHostWindowBounds, "1+1-300x300,1+301-300x300"); base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kAshEnableSoftwareMirroring); test::AshTestBase::SetUp(); } void TearDown() override { test::AshTestBase::TearDown(); } private: DISALLOW_COPY_AND_ASSIGN(MirrorOnBootTest); }; } typedef test::AshTestBase MirrorWindowControllerTest; #if defined(OS_WIN) // Software mirroring does not work on win. #define MAYBE_MirrorCursorBasic DISABLED_MirrorCursorBasic #define MAYBE_MirrorCursorLocations DISABLED_MirrorCursorLocations #define MAYBE_MirrorCursorMoveOnEnter DISABLED_MirrorCursorMoveOnEnter #define MAYBE_MirrorCursorRotate DISABLED_MirrorCursorRotate #define MAYBE_DockMode DISABLED_DockMode #define MAYBE_MirrorOnBoot DISABLED_MirrorOnBoot #else #define MAYBE_MirrorCursorBasic MirrorCursorBasic #define MAYBE_MirrorCursorLocations MirrorCursorLocations #define MAYBE_MirrorCursorMoveOnEnter MirrorCursorMoveOnEnter #define MAYBE_MirrorCursorRotate MirrorCursorRotate #define MAYBE_DockMode DockMode #define MAYBE_MirrorOnBoot MirrorOnBoot #endif TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursorBasic) { test::MirrorWindowTestApi test_api; aura::test::TestWindowDelegate test_window_delegate; test_window_delegate.set_window_component(HTTOP); DisplayManager* display_manager = Shell::GetInstance()->display_manager(); display_manager->SetMultiDisplayMode(DisplayManager::MIRRORING); UpdateDisplay("400x400,400x400"); aura::Window* root = Shell::GetInstance()->GetPrimaryRootWindow(); scoped_ptr window(aura::test::CreateTestWindowWithDelegate( &test_window_delegate, 0, gfx::Rect(50, 50, 100, 100), root)); window->Show(); window->SetName("foo"); EXPECT_TRUE(test_api.GetCursorWindow()); EXPECT_EQ("50,50 100x100", window->bounds().ToString()); ui::test::EventGenerator generator(root); generator.MoveMouseTo(10, 10); // Test if cursor movement is propertly reflected in mirror window. EXPECT_EQ("4,4", test_api.GetCursorHotPoint().ToString()); EXPECT_EQ("10,10", test_api.GetCursorHotPointLocationInRootWindow().ToString()); EXPECT_EQ(ui::kCursorNull, test_api.GetCurrentCursorType()); EXPECT_TRUE(test_api.GetCursorWindow()->IsVisible()); // Test if cursor type change is propertly reflected in mirror window. generator.MoveMouseTo(100, 100); EXPECT_EQ("100,100", test_api.GetCursorHotPointLocationInRootWindow().ToString()); EXPECT_EQ(ui::kCursorNorthResize, test_api.GetCurrentCursorType()); // Test if visibility change is propertly reflected in mirror window. // A key event hides cursor. generator.PressKey(ui::VKEY_A, 0); generator.ReleaseKey(ui::VKEY_A, 0); EXPECT_FALSE(test_api.GetCursorWindow()->IsVisible()); // Mouse event makes it visible again. generator.MoveMouseTo(300, 300); EXPECT_EQ("300,300", test_api.GetCursorHotPointLocationInRootWindow().ToString()); EXPECT_EQ(ui::kCursorNull, test_api.GetCurrentCursorType()); EXPECT_TRUE(test_api.GetCursorWindow()->IsVisible()); } TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursorRotate) { test::MirrorWindowTestApi test_api; aura::test::TestWindowDelegate test_window_delegate; test_window_delegate.set_window_component(HTTOP); DisplayManager* display_manager = Shell::GetInstance()->display_manager(); display_manager->SetMultiDisplayMode(DisplayManager::MIRRORING); UpdateDisplay("400x400,400x400"); aura::Window* root = Shell::GetInstance()->GetPrimaryRootWindow(); scoped_ptr window(aura::test::CreateTestWindowWithDelegate( &test_window_delegate, 0, gfx::Rect(50, 50, 100, 100), root)); window->Show(); window->SetName("foo"); EXPECT_TRUE(test_api.GetCursorWindow()); EXPECT_EQ("50,50 100x100", window->bounds().ToString()); ui::test::EventGenerator generator(root); generator.MoveMouseToInHost(100, 100); // Test if cursor movement is propertly reflected in mirror window. EXPECT_EQ("11,12", test_api.GetCursorHotPoint().ToString()); EXPECT_EQ("100,100", test_api.GetCursorHotPointLocationInRootWindow().ToString()); EXPECT_EQ(ui::kCursorNorthResize, test_api.GetCurrentCursorType()); UpdateDisplay("400x400/r,400x400"); // 90 degrees. generator.MoveMouseToInHost(300, 100); EXPECT_EQ(ui::kCursorNorthResize, test_api.GetCurrentCursorType()); // The size of cursor image is 25x25, so the rotated hot point must // be (25-12, 11). EXPECT_EQ("13,11", test_api.GetCursorHotPoint().ToString()); EXPECT_EQ("300,100", test_api.GetCursorHotPointLocationInRootWindow().ToString()); UpdateDisplay("400x400/u,400x400"); // 180 degrees. generator.MoveMouseToInHost(300, 300); EXPECT_EQ(ui::kCursorNorthResize, test_api.GetCurrentCursorType()); // Rotated hot point must be (25-11, 25-12). EXPECT_EQ("14,13", test_api.GetCursorHotPoint().ToString()); EXPECT_EQ("300,300", test_api.GetCursorHotPointLocationInRootWindow().ToString()); UpdateDisplay("400x400/l,400x400"); // 270 degrees. generator.MoveMouseToInHost(100, 300); EXPECT_EQ(ui::kCursorNorthResize, test_api.GetCurrentCursorType()); // Rotated hot point must be (12, 25-11). EXPECT_EQ("12,14", test_api.GetCursorHotPoint().ToString()); EXPECT_EQ("100,300", test_api.GetCursorHotPointLocationInRootWindow().ToString()); } // Make sure that the mirror cursor's location is same as // the source display's host location in the mirror root window's // coordinates. TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursorLocations) { test::MirrorWindowTestApi test_api; DisplayManager* display_manager = Shell::GetInstance()->display_manager(); display_manager->SetMultiDisplayMode(DisplayManager::MIRRORING); // Test with device scale factor. UpdateDisplay("400x600*2,400x600"); aura::Window* root = Shell::GetInstance()->GetPrimaryRootWindow(); ui::test::EventGenerator generator(root); generator.MoveMouseToInHost(10, 20); EXPECT_EQ("7,7", test_api.GetCursorHotPoint().ToString()); EXPECT_EQ("10,20", test_api.GetCursorHotPointLocationInRootWindow().ToString()); // Test with ui scale UpdateDisplay("400x600*0.5,400x600"); generator.MoveMouseToInHost(20, 30); EXPECT_EQ("4,4", test_api.GetCursorHotPoint().ToString()); EXPECT_EQ("20,30", test_api.GetCursorHotPointLocationInRootWindow().ToString()); // Test with rotation UpdateDisplay("400x600/r,400x600"); generator.MoveMouseToInHost(30, 40); EXPECT_EQ("21,4", test_api.GetCursorHotPoint().ToString()); EXPECT_EQ("30,40", test_api.GetCursorHotPointLocationInRootWindow().ToString()); } // Test the behavior of the cursor when entering software mirror mode swaps the // cursor's display. TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursorMoveOnEnter) { aura::Env* env = aura::Env::GetInstance(); Shell* shell = Shell::GetInstance(); WindowTreeHostManager* window_tree_host_manager = shell->window_tree_host_manager(); UpdateDisplay("400x400*2/r,400x400"); int64_t primary_display_id = window_tree_host_manager->GetPrimaryDisplayId(); int64_t secondary_display_id = ScreenUtil::GetSecondaryDisplay().id(); test::ScopedSetInternalDisplayId set_internal(primary_display_id); // Chrome uses the internal display as the source display for software mirror // mode. Move the cursor to the external display. aura::Window* secondary_root_window = window_tree_host_manager->GetRootWindowForDisplayId(secondary_display_id); secondary_root_window->MoveCursorTo(gfx::Point(100, 200)); EXPECT_EQ("300,200", env->last_mouse_location().ToString()); test::CursorManagerTestApi cursor_test_api(shell->cursor_manager()); EXPECT_EQ(1.0f, cursor_test_api.GetCurrentCursor().device_scale_factor()); EXPECT_EQ(gfx::Display::ROTATE_0, cursor_test_api.GetCurrentCursorRotation()); shell->display_manager()->SetMultiDisplayMode(DisplayManager::MIRRORING); UpdateDisplay("400x400*2/r,400x400"); // Entering mirror mode should have centered the cursor on the primary display // because the cursor's previous position is out of bounds. // Check real cursor's position and properties. EXPECT_EQ("100,100", env->last_mouse_location().ToString()); EXPECT_EQ(2.0f, cursor_test_api.GetCurrentCursor().device_scale_factor()); EXPECT_EQ(gfx::Display::ROTATE_90, cursor_test_api.GetCurrentCursorRotation()); // Check mirrored cursor's location. test::MirrorWindowTestApi test_api; gfx::Point hot_point = test_api.GetCursorHotPoint(); // Rotated hot point must be (25-7, 7). EXPECT_EQ("18,7", test_api.GetCursorHotPoint().ToString()); // New coordinates are not (200,200) because (200,200) is not the center of // the display. EXPECT_EQ("199,200", test_api.GetCursorHotPointLocationInRootWindow().ToString()); } // Make sure that the compositor based mirroring can switch // from/to dock mode. TEST_F(MirrorWindowControllerTest, MAYBE_DockMode) { DisplayManager* display_manager = Shell::GetInstance()->display_manager(); const int64_t internal_id = 1; const int64_t external_id = 2; const DisplayInfo internal_display_info = CreateDisplayInfo(internal_id, gfx::Rect(0, 0, 500, 500)); const DisplayInfo external_display_info = CreateDisplayInfo(external_id, gfx::Rect(1, 1, 100, 100)); std::vector display_info_list; display_manager->SetMultiDisplayMode(DisplayManager::MIRRORING); // 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 = test::DisplayManagerTestApi().SetFirstDisplayAsInternalDisplay(); EXPECT_EQ(internal_id, internal_display_id); EXPECT_EQ(1U, display_manager->GetNumDisplays()); EXPECT_TRUE(display_manager->IsInMirrorMode()); EXPECT_EQ(external_id, display_manager->mirroring_display_id()); // dock mode. display_info_list.clear(); display_info_list.push_back(external_display_info); display_manager->SetMultiDisplayMode(DisplayManager::MIRRORING); display_manager->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(1U, display_manager->GetNumDisplays()); EXPECT_FALSE(display_manager->IsInMirrorMode()); // back to software mirroring. display_info_list.clear(); display_info_list.push_back(internal_display_info); display_info_list.push_back(external_display_info); display_manager->SetMultiDisplayMode(DisplayManager::MIRRORING); display_manager->OnNativeDisplaysChanged(display_info_list); EXPECT_EQ(1U, display_manager->GetNumDisplays()); EXPECT_TRUE(display_manager->IsInMirrorMode()); EXPECT_EQ(external_id, display_manager->mirroring_display_id()); } TEST_F(MirrorOnBootTest, MAYBE_MirrorOnBoot) { DisplayManager* display_manager = Shell::GetInstance()->display_manager(); EXPECT_TRUE(display_manager->IsInMirrorMode()); RunAllPendingInMessageLoop(); test::MirrorWindowTestApi test_api; EXPECT_TRUE(test_api.GetHost()); } } // namespace ash