|  | // Copyright 2015 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/cursor_window_controller.h" | 
|  |  | 
|  | #include "ash/display/display_util.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/display_manager_test_api.h" | 
|  | #include "ui/aura/window.h" | 
|  | #include "ui/aura/window_tree_host.h" | 
|  | #include "ui/base/cursor/cursor.h" | 
|  | #include "ui/display/display.h" | 
|  | #include "ui/display/screen.h" | 
|  | #include "ui/events/test/event_generator.h" | 
|  | #include "ui/wm/core/coordinate_conversion.h" | 
|  |  | 
|  | namespace ash { | 
|  |  | 
|  | class CursorWindowControllerTest : public test::AshTestBase { | 
|  | public: | 
|  | CursorWindowControllerTest() {} | 
|  | ~CursorWindowControllerTest() override {} | 
|  |  | 
|  | // test::AshTestBase: | 
|  | void SetUp() override { | 
|  | AshTestBase::SetUp(); | 
|  | SetCursorCompositionEnabled(true); | 
|  | } | 
|  |  | 
|  | int GetCursorType() const { return cursor_window_controller_->cursor_type_; } | 
|  |  | 
|  | const gfx::Point& GetCursorHotPoint() const { | 
|  | return cursor_window_controller_->hot_point_; | 
|  | } | 
|  |  | 
|  | aura::Window* GetCursorWindow() const { | 
|  | return cursor_window_controller_->cursor_window_.get(); | 
|  | } | 
|  |  | 
|  | const gfx::ImageSkia& GetCursorImage() const { | 
|  | return cursor_window_controller_->GetCursorImageForTest(); | 
|  | } | 
|  |  | 
|  | int64_t GetCursorDisplayId() const { | 
|  | return cursor_window_controller_->display_.id(); | 
|  | } | 
|  |  | 
|  | void SetCursorCompositionEnabled(bool enabled) { | 
|  | cursor_window_controller_ = Shell::GetInstance() | 
|  | ->window_tree_host_manager() | 
|  | ->cursor_window_controller(); | 
|  | cursor_window_controller_->SetCursorCompositingEnabled(enabled); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Not owned. | 
|  | CursorWindowController* cursor_window_controller_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(CursorWindowControllerTest); | 
|  | }; | 
|  |  | 
|  | // Test that the composited cursor moves to another display when the real cursor | 
|  | // moves to another display. | 
|  | TEST_F(CursorWindowControllerTest, MoveToDifferentDisplay) { | 
|  | if (!SupportsMultipleDisplays()) | 
|  | return; | 
|  |  | 
|  | UpdateDisplay("200x200,200x200*2/r"); | 
|  |  | 
|  | WindowTreeHostManager* window_tree_host_manager = | 
|  | Shell::GetInstance()->window_tree_host_manager(); | 
|  | int64_t primary_display_id = window_tree_host_manager->GetPrimaryDisplayId(); | 
|  | int64_t secondary_display_id = ScreenUtil::GetSecondaryDisplay().id(); | 
|  | aura::Window* primary_root = | 
|  | window_tree_host_manager->GetRootWindowForDisplayId(primary_display_id); | 
|  | aura::Window* secondary_root = | 
|  | window_tree_host_manager->GetRootWindowForDisplayId(secondary_display_id); | 
|  |  | 
|  | ui::test::EventGenerator primary_generator(primary_root); | 
|  | primary_generator.MoveMouseToInHost(20, 50); | 
|  |  | 
|  | EXPECT_TRUE(primary_root->Contains(GetCursorWindow())); | 
|  | EXPECT_EQ(primary_display_id, GetCursorDisplayId()); | 
|  | EXPECT_EQ(ui::kCursorNull, GetCursorType()); | 
|  | gfx::Point hot_point = GetCursorHotPoint(); | 
|  | EXPECT_EQ("4,4", hot_point.ToString()); | 
|  | gfx::Rect cursor_bounds = GetCursorWindow()->GetBoundsInScreen(); | 
|  | EXPECT_EQ(20, cursor_bounds.x() + hot_point.x()); | 
|  | EXPECT_EQ(50, cursor_bounds.y() + hot_point.y()); | 
|  |  | 
|  | // The cursor can only be moved between displays via | 
|  | // WindowTreeHost::MoveCursorTo(). EventGenerator uses a hack to move the | 
|  | // cursor between displays. | 
|  | // Screen location: 220, 50 | 
|  | // Root location: 20, 50 | 
|  | secondary_root->MoveCursorTo(gfx::Point(20, 50)); | 
|  |  | 
|  | // Chrome relies on WindowTreeHost::MoveCursorTo() dispatching a mouse move | 
|  | // asynchronously. This is implemented in a platform specific way. Generate a | 
|  | // fake mouse move instead of waiting. | 
|  | gfx::Point new_cursor_position_in_host(20, 50); | 
|  | secondary_root->GetHost()->ConvertPointToHost(&new_cursor_position_in_host); | 
|  | ui::test::EventGenerator secondary_generator(secondary_root); | 
|  | secondary_generator.MoveMouseToInHost(new_cursor_position_in_host); | 
|  |  | 
|  | EXPECT_TRUE(secondary_root->Contains(GetCursorWindow())); | 
|  | EXPECT_EQ(secondary_display_id, GetCursorDisplayId()); | 
|  | EXPECT_EQ(ui::kCursorNull, GetCursorType()); | 
|  | hot_point = GetCursorHotPoint(); | 
|  | EXPECT_EQ("3,3", hot_point.ToString()); | 
|  | cursor_bounds = GetCursorWindow()->GetBoundsInScreen(); | 
|  | EXPECT_EQ(220, cursor_bounds.x() + hot_point.x()); | 
|  | EXPECT_EQ(50, cursor_bounds.y() + hot_point.y()); | 
|  | } | 
|  |  | 
|  | // Windows doesn't support compositor based cursor. | 
|  | #if !defined(OS_WIN) | 
|  | // Make sure that composition cursor inherits the visibility state. | 
|  | TEST_F(CursorWindowControllerTest, VisibilityTest) { | 
|  | ASSERT_TRUE(GetCursorWindow()); | 
|  | EXPECT_TRUE(GetCursorWindow()->IsVisible()); | 
|  | aura::client::CursorClient* client = Shell::GetInstance()->cursor_manager(); | 
|  | client->HideCursor(); | 
|  | ASSERT_TRUE(GetCursorWindow()); | 
|  | EXPECT_FALSE(GetCursorWindow()->IsVisible()); | 
|  |  | 
|  | // Normal cursor should be in the correct state. | 
|  | SetCursorCompositionEnabled(false); | 
|  | ASSERT_FALSE(GetCursorWindow()); | 
|  | ASSERT_FALSE(client->IsCursorVisible()); | 
|  |  | 
|  | // Cursor was hidden. | 
|  | SetCursorCompositionEnabled(true); | 
|  | ASSERT_TRUE(GetCursorWindow()); | 
|  | EXPECT_FALSE(GetCursorWindow()->IsVisible()); | 
|  |  | 
|  | // Goback to normal cursor and show the cursor. | 
|  | SetCursorCompositionEnabled(false); | 
|  | ASSERT_FALSE(GetCursorWindow()); | 
|  | ASSERT_FALSE(client->IsCursorVisible()); | 
|  | client->ShowCursor(); | 
|  | ASSERT_TRUE(client->IsCursorVisible()); | 
|  |  | 
|  | // Cursor was shown. | 
|  | SetCursorCompositionEnabled(true); | 
|  | ASSERT_TRUE(GetCursorWindow()); | 
|  | EXPECT_TRUE(GetCursorWindow()->IsVisible()); | 
|  | } | 
|  |  | 
|  | // Make sure that composition cursor stays big even when | 
|  | // the DSF becomes 1x as a result of zooming out. | 
|  | TEST_F(CursorWindowControllerTest, DSF) { | 
|  | UpdateDisplay("1000x500*2"); | 
|  | int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); | 
|  |  | 
|  | test::ScopedSetInternalDisplayId set_internal(primary_id); | 
|  | SetCursorCompositionEnabled(true); | 
|  | ASSERT_EQ( | 
|  | 2.0f, | 
|  | display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor()); | 
|  | EXPECT_TRUE(GetCursorImage().HasRepresentation(2.0f)); | 
|  |  | 
|  | ASSERT_TRUE(SetDisplayUIScale(primary_id, 2.0f)); | 
|  | ASSERT_EQ( | 
|  | 1.0f, | 
|  | display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor()); | 
|  | EXPECT_TRUE(GetCursorImage().HasRepresentation(2.0f)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | }  // namespace ash |