// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/display/unified_mouse_warp_controller.h"
#include <sstream>
#include "ash/display/display_util.h"
#include "ash/display/mirror_window_controller.h"
#include "ash/display/mouse_cursor_event_filter.h"
#include "ash/display/window_tree_host_manager.h"
#include "ash/host/ash_window_tree_host.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ui/aura/env.h"
#include "ui/aura/window_tree_host.h"
#include "ui/display/display.h"
#include "ui/display/display_finder.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/events/test/event_generator.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace ash {
namespace {
struct WarpGroup {
// Native point at a warp edge before warping.
gfx::Point native_point_at_edge;
// Expected DIP point after warping.
gfx::Point expected_point_after_warp;
// Expected display ID after warping.
int64_t expected_target_display_id;
};
} // namespace
class UnifiedMouseWarpControllerTest : public AshTestBase {
public:
UnifiedMouseWarpControllerTest() = default;
UnifiedMouseWarpControllerTest(const UnifiedMouseWarpControllerTest&) =
delete;
UnifiedMouseWarpControllerTest& operator=(
const UnifiedMouseWarpControllerTest&) = delete;
~UnifiedMouseWarpControllerTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
display_manager()->SetUnifiedDesktopEnabled(true);
}
protected:
bool MoveMouseToNativePoint(const gfx::Point& point_in_native,
int64_t* out_original_mirroring_display_id) {
for (auto display : display_manager()->software_mirroring_display_list()) {
display::ManagedDisplayInfo info =
display_manager()->GetDisplayInfo(display.id());
if (info.bounds_in_native().Contains(point_in_native)) {
*out_original_mirroring_display_id = info.id();
gfx::Point point_in_mirroring_host = point_in_native;
const gfx::Point& origin = info.bounds_in_native().origin();
// Convert to mirroring host.
point_in_mirroring_host.Offset(-origin.x(), -origin.y());
// Move the mouse inside the host.
AshWindowTreeHost* ash_host =
Shell::Get()
->window_tree_host_manager()
->mirror_window_controller()
->GetAshWindowTreeHostForDisplayId(info.id());
ui::test::EventGenerator gen(ash_host->AsWindowTreeHost()->window());
gen.MoveMouseToWithNative(point_in_mirroring_host,
point_in_mirroring_host);
return true;
}
}
return false;
}
bool TestIfMouseWarpsAt(const gfx::Point& point_in_native) {
static_cast<UnifiedMouseWarpController*>(
Shell::Get()->mouse_cursor_filter()->mouse_warp_controller_for_test())
->update_location_for_test();
int64_t orig_mirroring_display_id;
if (!MoveMouseToNativePoint(point_in_native, &orig_mirroring_display_id))
return false;
aura::Window* root = Shell::GetPrimaryRootWindow();
gfx::Point new_location_in_unified_host =
aura::Env::GetInstance()->last_mouse_location();
// Convert screen to the host.
root->GetHost()->ConvertDIPToPixels(&new_location_in_unified_host);
auto iter = display::FindDisplayContainingPoint(
display_manager()->software_mirroring_display_list(),
new_location_in_unified_host);
if (iter == display_manager()->software_mirroring_display_list().end())
return false;
return orig_mirroring_display_id != iter->id();
}
MouseCursorEventFilter* event_filter() {
return Shell::Get()->mouse_cursor_filter();
}
UnifiedMouseWarpController* mouse_warp_controller() {
return static_cast<UnifiedMouseWarpController*>(
event_filter()->mouse_warp_controller_for_test());
}
// |expected_edges| should have a row for each display which contains the
// expected native bounds of the shared edges with that display in the order
// "top", "left", "right", "bottom".
// If |matrix| is empty, default unified layout will be used.
void BoundaryTestBody(
const std::string& displays_specs,
const display::UnifiedDesktopLayoutMatrix& matrix,
const std::vector<std::vector<std::string>>& expected_edges) {
UpdateDisplay(displays_specs);
display_manager()->SetUnifiedDesktopMatrix(matrix);
// Let the UnifiedMouseWarpController compute the bounds by
// generating a mouse move event.
GetEventGenerator()->MoveMouseTo(gfx::Point(0, 0));
const display::Displays& mirroring_displays =
display_manager()->software_mirroring_display_list();
ASSERT_EQ(expected_edges.size(), mirroring_displays.size());
int index = 0;
for (const auto& display : mirroring_displays) {
const int64_t id = display.id();
std::stringstream scoped_trace_message;
scoped_trace_message << "Edges of display with ID: " << id
<< " at index: " << index;
SCOPED_TRACE(scoped_trace_message.str());
const auto& display_expected_edges = expected_edges[index++];
const auto& display_actual_edges =
mouse_warp_controller()->displays_edges_map_.at(id);
ASSERT_EQ(display_expected_edges.size(), display_actual_edges.size());
for (size_t i = 0; i < display_expected_edges.size(); ++i) {
EXPECT_EQ(display_expected_edges[i],
display_actual_edges[i]
.edge_native_bounds_in_source_display.ToString());
}
}
}
void WarpTestBody(const std::vector<WarpGroup>& warp_groups) {
for (const auto& group : warp_groups) {
EXPECT_TRUE(TestIfMouseWarpsAt(group.native_point_at_edge));
gfx::Point new_location = aura::Env::GetInstance()->last_mouse_location();
EXPECT_EQ(group.expected_point_after_warp, new_location);
// Convert screen to the host.
aura::Window* root = Shell::GetPrimaryRootWindow();
root->GetHost()->ConvertDIPToPixels(&new_location);
auto iter = display::FindDisplayContainingPoint(
display_manager()->software_mirroring_display_list(), new_location);
EXPECT_FALSE(iter ==
display_manager()->software_mirroring_display_list().end());
EXPECT_EQ(group.expected_target_display_id, iter->id());
}
}
void NoWarpTestBody() {
// Touch the left edge of the first display.
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(0, 10)));
// Touch the top edge of the first display.
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(10, 0)));
// Touch the bottom edge of the first display.
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(10, 499)));
// Touch the right edge of the second display.
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(1099, 10)));
// Touch the top edge of the second display.
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(610, 0)));
// Touch the bottom edge of the second display.
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(610, 499)));
}
};
// Verifies if MouseCursorEventFilter's bounds calculation works correctly.
TEST_F(UnifiedMouseWarpControllerTest, BoundaryTest) {
{
SCOPED_TRACE("1x1");
BoundaryTestBody("500x400,0+450-700x400",
{}, // Empty matrix (use horizontal layout).
{{"499,0 1x400"}, {"0,450 1x400"}});
BoundaryTestBody("500x400,0+450-700x600",
{}, // Empty matrix (use horizontal layout).
{{"499,0 1x400"}, {"0,450 1x600"}});
}
{
SCOPED_TRACE("2x1");
BoundaryTestBody("500x400*2,0+450-700x400",
{}, // Empty matrix (use horizontal layout).
{{"499,0 1x400"}, {"0,450 1x400"}});
BoundaryTestBody("500x400*2,0+450-700x600",
{}, // Empty matrix (use horizontal layout).
{{"499,0 1x400"}, {"0,450 1x600"}});
}
{
SCOPED_TRACE("1x2");
BoundaryTestBody("500x400,0+450-700x400*2",
{}, // Empty matrix (use horizontal layout).
{{"499,0 1x400"}, {"0,450 1x400"}});
BoundaryTestBody("500x400,0+450-700x600*2",
{}, // Empty matrix (use horizontal layout).
{{"499,0 1x400"}, {"0,450 1x600"}});
}
{
SCOPED_TRACE("2x2");
BoundaryTestBody("500x400*2,0+450-700x400*2",
{}, // Empty matrix (use horizontal layout).
{{"499,0 1x400"}, {"0,450 1x400"}});
BoundaryTestBody("500x400*2,0+450-700x600*2",
{}, // Empty matrix (use horizontal layout).
{{"499,0 1x400"}, {"0,450 1x600"}});
}
}
TEST_F(UnifiedMouseWarpControllerTest, BoundaryAndWarpSimpleTest) {
const std::vector<std::vector<std::string>> expected_edges = {
// Display 0 edges.
{
"1919,0 1x1080", // Right with display 1.
},
// Display 1 edges.
{
"1930,0 1x1200", // Left with display 0.
},
};
BoundaryTestBody("0+0-1920x1080,1930+0-1920x1200",
{} /* empty matrix = default */, expected_edges);
display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
ASSERT_EQ(2u, list.size());
// Assert mouse warps in all bounds to the correct display.
const std::vector<WarpGroup> warp_groups = {
{{1919, 500}, {1920, 499}, list[1]}, // Display 0 --> 1.
{{1930, 600}, {1918, 540}, list[0]}, // Display 1 --> 0.
};
WarpTestBody(warp_groups);
}
TEST_F(UnifiedMouseWarpControllerTest, BoundaryTestGrid) {
// Update displays here first so we get the correct display IDs list. The
// below are the native bounds.
const std::string display_specs =
"0+0-500x300,510+0-400x500,920+0-300x600,"
"0+600-200x300,210+600-700x200,920+600-350x480,"
"0+1080-300x500,310+1080-600x599,920+1080-400x450";
UpdateDisplay(display_specs);
display_manager()->SetUnifiedDesktopEnabled(true);
display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
ASSERT_EQ(9u, list.size());
// Test a very general case of a 3 x 3 matrix.
// 0:[500 x 300] 1:[400 x 500] 2:[300 x 600]
// 3:[200 x 300] 4:[700 x 200] 5:[350 x 480]
// 6:[300 x 500] 7:[600 x 599] 8:[400 x 450]
display::UnifiedDesktopLayoutMatrix matrix;
matrix.resize(3u);
matrix[0].emplace_back(list[0]);
matrix[0].emplace_back(list[1]);
matrix[0].emplace_back(list[2]);
matrix[1].emplace_back(list[3]);
matrix[1].emplace_back(list[4]);
matrix[1].emplace_back(list[5]);
matrix[2].emplace_back(list[6]);
matrix[2].emplace_back(list[7]);
matrix[2].emplace_back(list[8]);
const std::vector<std::vector<std::string>> expected_edges = {
// Display 0 edges.
{
"499,0 1x300", // Right with display 1.
"0,299 121x1", // Bottom with display 3.
"121,299 379x1", // Bottom with display 4.
},
// Display 1 edges.
{
"510,0 1x500", // Left with display 0.
"909,0 1x500", // Right with display 2.
"510,499 400x1", // Bottom with display 4.
},
// Display 2 edges.
{
"920,0 1x600", // Left with display 1.
"920,599 34x1", // Bottom with display 4.
"954,599 266x1", // Bottom with display 5.
},
// Display 3 edges.
{
"0,600 199x1", // Top with display 0.
"199,600 1x300", // Right with display 4.
"0,899 199x1", // Bottom with display 6.
},
// Display 4 edges.
{
"210,600 416x1", // Top with display 0.
"626,600 264x1", // Top with display 1.
"890,600 18x1", // Top with display 2.
"210,600 1x200", // Left with display 3.
"909,600 1x200", // Right with display 5.
"210,799 102x1", // Bottom with display 6.
"312,799 393x1", // Bottom with display 7.
"705,799 203x1", // Bottom with display 8.
},
// Display 5 edges.
{
"920,600 350x1", // Top with display 2.
"920,600 1x480", // Left with display 4.
"920,1079 350x1", // Bottom with display 8.
},
// Display 6 edges.
{
"0,1080 169x1", // Top with display 3.
"169,1080 130x1", // Top with display 4.
"299,1080 1x500", // Right with display 7.
},
// Display 7 edges.
{
"310,1080 600x1", // Top with display 4.
"310,1080 1x599", // Left with display 6.
"909,1080 1x599", // Right with display 8.
},
// Display 8 edges.
{
"920,1080 233x1", // Top with display 4.
"1153,1080 167x1", // Top with display 5.
"920,1080 1x450", // Left with display 7.
},
};
BoundaryTestBody(display_specs, matrix, expected_edges);
ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
// Assert mouse warps in all bounds to the correct display.
const std::vector<WarpGroup> warp_groups = {
{{499, 10}, {500, 9}, list[1]}, // Display 0 --> 1.
{{10, 299}, {9, 300}, list[3]}, // Display 0 --> 3.
{{130, 299}, {129, 300}, list[4]}, // Display 0 --> 4.
{{510, 10}, {498, 6}, list[0]}, // Display 1 --> 0.
{{909, 50}, {740, 30}, list[2]}, // Display 1 --> 2.
{{600, 499}, {553, 300}, list[4]}, // Display 1 --> 4.
{{920, 50}, {738, 24}, list[1]}, // Display 2 --> 1.
{{930, 599}, {744, 300}, list[4]}, // Display 2 --> 4.
{{970, 599}, {764, 300}, list[5]}, // Display 2 --> 5.
{{10, 600}, {6, 298}, list[0]}, // Display 3 --> 0.
{{199, 700}, {121, 359}, list[4]}, // Display 3 --> 4.
{{100, 899}, {59, 482}, list[6]}, // Display 3 --> 6.
{{250, 600}, {157, 298}, list[0]}, // Display 4 --> 0.
{{700, 600}, {566, 298}, list[1]}, // Display 4 --> 1.
{{900, 600}, {748, 299}, list[2]}, // Display 4 --> 2.
{{210, 700}, {120, 391}, list[3]}, // Display 4 --> 3.
{{909, 650}, {757, 344}, list[5]}, // Display 4 --> 5.
{{250, 799}, {156, 482}, list[6]}, // Display 4 --> 6.
{{500, 799}, {383, 482}, list[7]}, // Display 4 --> 7.
{{800, 799}, {656, 482}, list[8]}, // Display 4 --> 8.
{{950, 600}, {768, 299}, list[2]}, // Display 5 --> 2.
{{920, 750}, {756, 355}, list[4]}, // Display 5 --> 4.
{{1000, 1079}, {786, 482}, list[8]}, // Display 5 --> 8.
{{100, 1080}, {70, 480}, list[3]}, // Display 6 --> 3.
{{200, 1080}, {141, 480}, list[4]}, // Display 6 --> 4.
{{299, 1200}, {214, 566}, list[7]}, // Display 6 --> 7.
{{500, 1080}, {326, 480}, list[4]}, // Display 7 --> 4.
{{310, 1500}, {212, 731}, list[6]}, // Display 7 --> 6.
{{909, 1500}, {572, 731}, list[8]}, // Display 7 --> 8.
{{1000, 1080}, {634, 480}, list[4]}, // Display 8 --> 4.
{{1200, 1080}, {793, 481}, list[5]}, // Display 8 --> 5.
{{920, 1500}, {570, 814}, list[7]}, // Display 8 --> 7.
};
WarpTestBody(warp_groups);
}
// Verifies if the mouse pointer correctly moves to another display in
// unified desktop mode.
TEST_F(UnifiedMouseWarpControllerTest, WarpMouse) {
UpdateDisplay("600x500,700+0-600x500");
ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(10, 10)));
// Touch the right edge of the first display. Pointer should warp.
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(599, 10)));
EXPECT_EQ("601,10", // by 2px.
aura::Env::GetInstance()->last_mouse_location().ToString());
// Touch the left edge of the second display. Pointer should warp.
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(700, 10)));
EXPECT_EQ("598,10", // by 2px.
aura::Env::GetInstance()->last_mouse_location().ToString());
{
SCOPED_TRACE("1x1 NO WARP");
NoWarpTestBody();
}
// With 2X and 1X displays
UpdateDisplay("600x500*2,700+0-600x500");
ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(10, 10)));
// Touch the right edge of the first display. Pointer should warp.
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(599, 10)));
EXPECT_EQ("300,5", // moved to 601 by 2px, divided by 2 (dsf).
aura::Env::GetInstance()->last_mouse_location().ToString());
// Touch the left edge of the second display. Pointer should warp.
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(700, 10)));
EXPECT_EQ("299,5", // moved to 598 by 2px, divided by 2 (dsf).
aura::Env::GetInstance()->last_mouse_location().ToString());
{
SCOPED_TRACE("2x1 NO WARP");
NoWarpTestBody();
}
// With 1X and 2X displays
UpdateDisplay("600x500,700+0-600x500*2");
ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(10, 10)));
// Touch the right edge of the first display. Pointer should warp.
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(599, 10)));
EXPECT_EQ("601,10", // by 2px.
aura::Env::GetInstance()->last_mouse_location().ToString());
// Touch the left edge of the second display. Pointer should warp.
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(700, 10)));
EXPECT_EQ("598,10", // by 2px.
aura::Env::GetInstance()->last_mouse_location().ToString());
{
SCOPED_TRACE("1x2 NO WARP");
NoWarpTestBody();
}
// With two 2X displays
UpdateDisplay("600x500*2,700+0-600x500*2");
ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(10, 10)));
// Touch the right edge of the first display. Pointer should warp.
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(599, 10)));
EXPECT_EQ("300,5", // by 2px.
aura::Env::GetInstance()->last_mouse_location().ToString());
// Touch the left edge of the second display. Pointer should warp.
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(700, 10)));
EXPECT_EQ("299,5", // moved to 598 by 2px, divided by 2 (dsf).
aura::Env::GetInstance()->last_mouse_location().ToString());
{
SCOPED_TRACE("1x2 NO WARP");
NoWarpTestBody();
}
}
} // namespace aura