| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <linux/input.h> |
| #include <wayland-server.h> |
| |
| #include <cmath> |
| #include <memory> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" |
| #include "ui/base/cursor/platform_cursor.h" |
| #include "ui/events/devices/device_data_manager.h" |
| #include "ui/events/event.h" |
| #include "ui/ozone/common/bitmap_cursor_factory.h" |
| #include "ui/ozone/platform/wayland/host/wayland_cursor.h" |
| #include "ui/ozone/platform/wayland/host/wayland_cursor_factory.h" |
| #include "ui/ozone/platform/wayland/host/wayland_seat.h" |
| #include "ui/ozone/platform/wayland/host/wayland_window.h" |
| #include "ui/ozone/platform/wayland/test/mock_pointer.h" |
| #include "ui/ozone/platform/wayland/test/mock_surface.h" |
| #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" |
| #include "ui/ozone/platform/wayland/test/wayland_test.h" |
| #include "ui/ozone/test/mock_platform_window_delegate.h" |
| #include "ui/platform_window/platform_window_init_properties.h" |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::Ge; |
| using ::testing::Le; |
| using ::testing::Mock; |
| using ::testing::Ne; |
| using ::testing::SaveArg; |
| using ::testing::Values; |
| |
| namespace ui { |
| |
| class WaylandPointerTest : public WaylandTestSimple { |
| public: |
| void SetUp() override { |
| WaylandTestSimple::SetUp(); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| wl_seat_send_capabilities(server->seat()->resource(), |
| WL_SEAT_CAPABILITY_POINTER); |
| }); |
| ASSERT_TRUE(connection_->seat()->pointer()); |
| |
| EXPECT_EQ(1u, DeviceDataManager::GetInstance()->GetMouseDevices().size()); |
| // Wayland doesn't expose touchpad devices separately. They are all |
| // WaylandPointers. |
| EXPECT_EQ(0u, |
| DeviceDataManager::GetInstance()->GetTouchpadDevices().size()); |
| } |
| |
| protected: |
| // The auxiliary convenience methods for entering and leaving the pointer into |
| // and from the surface. |
| void SendEnter(int x, int y) { |
| PostToServerAndWait( |
| [x, y, surface_id = window_->root_surface()->get_surface_id()]( |
| wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| auto* const surface = |
| server->GetObject<wl::MockSurface>(surface_id)->resource(); |
| |
| wl_pointer_send_enter(pointer, server->GetNextSerial(), surface, |
| wl_fixed_from_int(x), wl_fixed_from_int(y)); |
| wl_pointer_send_frame(pointer); |
| }); |
| } |
| void SendEnter() { SendEnter(0, 0); } |
| |
| void SendLeave() { |
| PostToServerAndWait( |
| [surface_id = window_->root_surface()->get_surface_id()]( |
| wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| auto* const surface = |
| server->GetObject<wl::MockSurface>(surface_id)->resource(); |
| |
| wl_pointer_send_leave(pointer, server->GetNextSerial(), surface); |
| wl_pointer_send_frame(pointer); |
| }); |
| } |
| |
| void SendAxisStopEvents() { |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| wl_pointer_send_axis_stop(pointer, server->GetNextTime(), |
| WL_POINTER_AXIS_VERTICAL_SCROLL); |
| wl_pointer_send_axis_stop(pointer, server->GetNextTime(), |
| WL_POINTER_AXIS_HORIZONTAL_SCROLL); |
| wl_pointer_send_frame(pointer); |
| }); |
| } |
| |
| void SendRightButtonPress() { |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| wl_pointer_send_button(pointer, server->GetNextSerial(), |
| server->GetNextTime(), BTN_RIGHT, |
| WL_POINTER_BUTTON_STATE_PRESSED); |
| wl_pointer_send_frame(pointer); |
| }); |
| } |
| |
| void CheckEventType( |
| ui::EventType event_type, |
| ui::Event* event, |
| ui::EventPointerType pointer_type = ui::EventPointerType::kMouse, |
| float force = std::numeric_limits<float>::quiet_NaN(), |
| float tilt_x = 0.0, |
| float tilt_y = 0.0) { |
| ASSERT_TRUE(event); |
| ASSERT_TRUE(event->IsMouseEvent()); |
| |
| auto* mouse_event = event->AsMouseEvent(); |
| EXPECT_EQ(event_type, mouse_event->type()); |
| } |
| }; |
| |
| void SendAxisEvents(struct wl_resource* resource, |
| uint32_t time_ms, |
| uint32_t axis_source, |
| uint32_t axis, |
| int offset) { |
| wl_pointer_send_axis_source(resource, axis_source); |
| wl_pointer_send_axis(resource, time_ms, axis, wl_fixed_from_int(offset)); |
| wl_pointer_send_frame(resource); |
| } |
| |
| void SendDiagonalAxisEvents(struct wl_resource* resource, |
| uint32_t time_ms, |
| uint32_t axis_source, |
| int offset_x, |
| int offset_y) { |
| wl_pointer_send_axis_source(resource, axis_source); |
| wl_pointer_send_axis(resource, time_ms, WL_POINTER_AXIS_VERTICAL_SCROLL, |
| wl_fixed_from_int(offset_y)); |
| wl_pointer_send_axis(resource, time_ms, WL_POINTER_AXIS_HORIZONTAL_SCROLL, |
| wl_fixed_from_int(offset_x)); |
| wl_pointer_send_frame(resource); |
| } |
| |
| ACTION_P(CloneEvent, ptr) { |
| *ptr = arg0->Clone(); |
| } |
| |
| TEST_F(WaylandPointerTest, Enter) { |
| std::unique_ptr<Event> event; |
| EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); |
| |
| SendEnter(); |
| |
| ASSERT_TRUE(event); |
| ASSERT_TRUE(event->IsMouseEvent()); |
| auto* mouse_event = event->AsMouseEvent(); |
| EXPECT_EQ(EventType::kMouseEntered, mouse_event->type()); |
| EXPECT_EQ(0, mouse_event->button_flags()); |
| EXPECT_EQ(0, mouse_event->changed_button_flags()); |
| EXPECT_EQ(gfx::PointF(0, 0), mouse_event->location_f()); |
| } |
| |
| TEST_F(WaylandPointerTest, Leave) { |
| MockPlatformWindowDelegate other_delegate; |
| gfx::AcceleratedWidget other_widget = gfx::kNullAcceleratedWidget; |
| EXPECT_CALL(other_delegate, OnAcceleratedWidgetAvailable(_)) |
| .WillOnce(SaveArg<0>(&other_widget)); |
| |
| PlatformWindowInitProperties properties; |
| properties.bounds = gfx::Rect(0, 0, 10, 10); |
| properties.type = PlatformWindowType::kWindow; |
| auto other_window = WaylandWindow::Create(&other_delegate, connection_.get(), |
| std::move(properties)); |
| ASSERT_NE(other_widget, gfx::kNullAcceleratedWidget); |
| |
| EXPECT_CALL(delegate_, DispatchEvent(_)).Times(2); |
| EXPECT_CALL(other_delegate, DispatchEvent(_)).Times(2); |
| |
| PostToServerAndWait( |
| [surface_id = window_->root_surface()->get_surface_id(), |
| other_surface_id = other_window->root_surface()->get_surface_id()]( |
| wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| auto* const surface = |
| server->GetObject<wl::MockSurface>(surface_id)->resource(); |
| auto* const other_surface = |
| server->GetObject<wl::MockSurface>(other_surface_id)->resource(); |
| |
| wl_pointer_send_enter(pointer, 1, surface, 0, 0); |
| wl_pointer_send_frame(pointer); |
| |
| wl_pointer_send_leave(pointer, 2, surface); |
| wl_pointer_send_frame(pointer); |
| |
| wl_pointer_send_enter(pointer, 3, other_surface, 0, 0); |
| wl_pointer_send_frame(pointer); |
| |
| wl_pointer_send_button(pointer, 4, 1004, BTN_LEFT, |
| WL_POINTER_BUTTON_STATE_PRESSED); |
| wl_pointer_send_frame(pointer); |
| }); |
| } |
| |
| ACTION_P3(CloneEventAndCheckCapture, window, result, ptr) { |
| ASSERT_TRUE(window->HasCapture() == result); |
| *ptr = arg0->Clone(); |
| } |
| |
| TEST_F(WaylandPointerTest, Motion) { |
| SendEnter(); |
| |
| std::unique_ptr<Event> event; |
| EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| wl_pointer_send_motion(pointer, 1002, wl_fixed_from_double(10.75), |
| wl_fixed_from_double(20.375)); |
| wl_pointer_send_frame(pointer); |
| }); |
| |
| ASSERT_TRUE(event); |
| ASSERT_TRUE(event->IsMouseEvent()); |
| auto* mouse_event = event->AsMouseEvent(); |
| EXPECT_EQ(EventType::kMouseMoved, mouse_event->type()); |
| EXPECT_EQ(0, mouse_event->button_flags()); |
| EXPECT_EQ(0, mouse_event->changed_button_flags()); |
| EXPECT_EQ(gfx::PointF(10.75, 20.375), mouse_event->location_f()); |
| EXPECT_EQ(gfx::PointF(10.75, 20.375), mouse_event->root_location_f()); |
| } |
| |
| TEST_F(WaylandPointerTest, MotionDragged) { |
| SendEnter(); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| wl_pointer_send_button(pointer, server->GetNextSerial(), |
| server->GetNextTime(), BTN_MIDDLE, |
| WL_POINTER_BUTTON_STATE_PRESSED); |
| wl_pointer_send_frame(pointer); |
| }); |
| |
| std::unique_ptr<Event> event; |
| EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| wl_pointer_send_motion(pointer, 1003, wl_fixed_from_int(400), |
| wl_fixed_from_int(500)); |
| wl_pointer_send_frame(pointer); |
| }); |
| |
| ASSERT_TRUE(event); |
| ASSERT_TRUE(event->IsMouseEvent()); |
| auto* mouse_event = event->AsMouseEvent(); |
| EXPECT_EQ(EventType::kMouseDragged, mouse_event->type()); |
| EXPECT_EQ(EF_MIDDLE_MOUSE_BUTTON, mouse_event->button_flags()); |
| EXPECT_EQ(0, mouse_event->changed_button_flags()); |
| EXPECT_EQ(gfx::PointF(400, 500), mouse_event->location_f()); |
| EXPECT_EQ(gfx::PointF(400, 500), mouse_event->root_location_f()); |
| } |
| |
| // Verifies whether the platform event source handles all types of axis sources. |
| // The actual behaviour of each axis source is not tested here. |
| TEST_F(WaylandPointerTest, AxisSourceTypes) { |
| SendEnter(); |
| |
| std::unique_ptr<Event> event1, event2, event3, event4; |
| EXPECT_CALL(delegate_, DispatchEvent(_)) |
| .Times(4) |
| .WillOnce(CloneEvent(&event1)) |
| .WillOnce(CloneEvent(&event2)) |
| .WillOnce(CloneEvent(&event3)) |
| .WillOnce(CloneEvent(&event4)); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| for (const auto source : |
| {WL_POINTER_AXIS_SOURCE_WHEEL, WL_POINTER_AXIS_SOURCE_FINGER, |
| WL_POINTER_AXIS_SOURCE_CONTINUOUS, |
| WL_POINTER_AXIS_SOURCE_WHEEL_TILT}) { |
| SendAxisEvents(pointer, server->GetNextTime(), source, |
| WL_POINTER_AXIS_VERTICAL_SCROLL, rand() % 20); |
| } |
| }); |
| |
| ASSERT_TRUE(event1); |
| ASSERT_TRUE(event1->IsMouseWheelEvent()); |
| ASSERT_TRUE(event2); |
| ASSERT_TRUE(event2->IsScrollEvent()); |
| ASSERT_TRUE(event3); |
| ASSERT_TRUE(event3->IsScrollEvent()); |
| ASSERT_TRUE(event4); |
| ASSERT_TRUE(event4->IsMouseWheelEvent()); |
| } |
| |
| TEST_F(WaylandPointerTest, Axis) { |
| SendEnter(); |
| |
| for (uint32_t axis : |
| {WL_POINTER_AXIS_VERTICAL_SCROLL, WL_POINTER_AXIS_HORIZONTAL_SCROLL}) { |
| for (bool send_axis_source : {false, true}) { |
| std::unique_ptr<Event> event; |
| EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); |
| |
| PostToServerAndWait([axis, send_axis_source]( |
| wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| if (send_axis_source) { |
| // The axis source event is optional. When it is not set within the |
| // event frame, we assume the mouse wheel. |
| wl_pointer_send_axis_source(pointer, WL_POINTER_AXIS_SOURCE_WHEEL); |
| } |
| |
| // Wayland servers typically send a value of 10 per mouse wheel click. |
| wl_pointer_send_axis(pointer, 1003, axis, wl_fixed_from_int(10)); |
| wl_pointer_send_frame(pointer); |
| }); |
| |
| ASSERT_TRUE(event); |
| ASSERT_TRUE(event->IsMouseWheelEvent()); |
| auto* mouse_wheel_event = event->AsMouseWheelEvent(); |
| EXPECT_EQ(axis == WL_POINTER_AXIS_VERTICAL_SCROLL |
| ? gfx::Vector2d(0, -MouseWheelEvent::kWheelDelta) |
| : gfx::Vector2d(-MouseWheelEvent::kWheelDelta, 0), |
| mouse_wheel_event->offset()); |
| EXPECT_EQ(gfx::PointF(), mouse_wheel_event->location_f()); |
| EXPECT_EQ(gfx::PointF(), mouse_wheel_event->root_location_f()); |
| } |
| } |
| } |
| |
| TEST_F(WaylandPointerTest, SetBitmap) { |
| SendEnter(); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer(); |
| |
| EXPECT_CALL(*pointer, SetCursor(nullptr, 0, 0)); |
| }); |
| |
| connection_->SetCursorBitmap({}, {}, 1.0); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer(); |
| |
| Mock::VerifyAndClearExpectations(pointer); |
| }); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer(); |
| |
| EXPECT_CALL(*pointer, SetCursor(Ne(nullptr), 5, 8)); |
| }); |
| |
| SkBitmap dummy_cursor; |
| dummy_cursor.setInfo( |
| SkImageInfo::Make(16, 16, kUnknown_SkColorType, kUnknown_SkAlphaType)); |
| connection_->SetCursorBitmap({dummy_cursor}, gfx::Point(5, 8), 1.0); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer(); |
| |
| Mock::VerifyAndClearExpectations(pointer); |
| }); |
| |
| SendLeave(); |
| } |
| |
| // Tests that bitmap is set on pointer focus and the pointer surface respects |
| // provided scale of the surface image. |
| TEST_F(WaylandPointerTest, SetBitmapAndScaleOnPointerFocus) { |
| for (float scale : {1.0, 1.2, 1.5, 1.75, 2.0, 2.5, 3.0}) { |
| gfx::Size size = {static_cast<int>(10 * scale), |
| static_cast<int>(10 * scale)}; |
| SkBitmap dummy_cursor; |
| SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), |
| SkColorType::kBGRA_8888_SkColorType, |
| SkAlphaType::kPremul_SkAlphaType); |
| dummy_cursor.allocPixels(info, size.width() * 4); |
| |
| const gfx::Point hotspot_px = {5, 8}; |
| const gfx::Point hotspot_dip = |
| gfx::ScaleToRoundedPoint(hotspot_px, 1 / scale); |
| |
| WaylandCursorFactory cursor_factory(connection_.get()); |
| auto cursor = cursor_factory.CreateImageCursor( |
| mojom::CursorType::kCustom, dummy_cursor, hotspot_px, scale); |
| |
| SendEnter(10, 10); |
| |
| // Set a cursor. |
| wl_resource* surface_resource = nullptr; |
| |
| PostToServerAndWait([&surface_resource, |
| hotspot_dip](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer(); |
| |
| // Allow up to 1 DIP of precision loss. |
| EXPECT_CALL( |
| *pointer, |
| SetCursor(Ne(nullptr), |
| AllOf(Ge(hotspot_dip.x() - 1), Le(hotspot_dip.x() + 1)), |
| AllOf(Ge(hotspot_dip.y() - 1), Le(hotspot_dip.y() + 1)))) |
| .WillOnce(SaveArg<0>(&surface_resource)); |
| }); |
| |
| window_->SetCursor(cursor); |
| connection_->Flush(); |
| |
| SendLeave(); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer(); |
| |
| Mock::VerifyAndClearExpectations(pointer); |
| }); |
| |
| ASSERT_TRUE(surface_resource); |
| |
| PostToServerAndWait([surface_resource, scale, |
| hotspot_dip](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer(); |
| |
| auto* mock_pointer_surface = |
| wl::MockSurface::FromResource(surface_resource); |
| EXPECT_EQ(mock_pointer_surface->buffer_scale(), std::ceil(scale - 0.2f)); |
| |
| // Update the focus. |
| EXPECT_CALL( |
| *pointer, |
| SetCursor(Ne(nullptr), |
| AllOf(Ge(hotspot_dip.x() - 1), Le(hotspot_dip.x() + 1)), |
| AllOf(Ge(hotspot_dip.y() - 1), Le(hotspot_dip.y() + 1)))); |
| }); |
| |
| SendEnter(50, 75); |
| |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer(); |
| |
| Mock::VerifyAndClearExpectations(pointer); |
| }); |
| |
| // Reset the focus for the next iteration. |
| SendLeave(); |
| } |
| } |
| |
| TEST_F(WaylandPointerTest, FlingVertical) { |
| SendEnter(50, 75); |
| |
| SendRightButtonPress(); |
| |
| std::unique_ptr<Event> event1, event2, event3; |
| EXPECT_CALL(delegate_, DispatchEvent(_)) |
| .Times(3) |
| .WillOnce(CloneEvent(&event1)) |
| .WillOnce(CloneEvent(&event2)) |
| .WillOnce(CloneEvent(&event3)); |
| |
| // Send two axis events. |
| for (int n = 0; n < 2; ++n) { |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| SendAxisEvents(pointer, server->GetNextTime(), |
| WL_POINTER_AXIS_SOURCE_FINGER, |
| WL_POINTER_AXIS_VERTICAL_SCROLL, 10); |
| }); |
| |
| // Advance time to emulate delay between events and allow the fling gesture |
| // to be recognised. |
| task_environment_.FastForwardBy(base::Milliseconds(1)); |
| } |
| |
| // axis_stop event which should trigger fling scroll. |
| SendAxisStopEvents(); |
| |
| // Usual axis events should follow before the fling event. |
| ASSERT_TRUE(event1); |
| ASSERT_TRUE(event1->IsScrollEvent()); |
| ASSERT_TRUE(event2); |
| ASSERT_TRUE(event2->IsScrollEvent()); |
| |
| // The third dispatched event should be FLING_START. |
| ASSERT_TRUE(event3); |
| ASSERT_TRUE(event3->IsScrollEvent()); |
| auto* scroll_event = event3->AsScrollEvent(); |
| EXPECT_EQ(EventType::kScrollFlingStart, scroll_event->type()); |
| EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); |
| EXPECT_EQ(0.0f, scroll_event->x_offset()); |
| EXPECT_EQ(0.0f, scroll_event->x_offset_ordinal()); |
| // Initial vertical velocity depends on the implementation outside of |
| // WaylandPointer, but it should be negative value based on the direction of |
| // recent two axis events. |
| EXPECT_GT(0.0f, scroll_event->y_offset()); |
| EXPECT_GT(0.0f, scroll_event->y_offset_ordinal()); |
| } |
| |
| TEST_F(WaylandPointerTest, FlingHorizontal) { |
| SendEnter(50, 75); |
| |
| SendRightButtonPress(); |
| |
| std::unique_ptr<Event> event1, event2, event3; |
| EXPECT_CALL(delegate_, DispatchEvent(_)) |
| .Times(3) |
| .WillOnce(CloneEvent(&event1)) |
| .WillOnce(CloneEvent(&event2)) |
| .WillOnce(CloneEvent(&event3)); |
| |
| // Send two axis events. |
| for (int n = 0; n < 2; ++n) { |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| SendAxisEvents(pointer, server->GetNextTime(), |
| WL_POINTER_AXIS_SOURCE_FINGER, |
| WL_POINTER_AXIS_HORIZONTAL_SCROLL, 10); |
| }); |
| |
| // Advance time to emulate delay between events and allow the fling gesture |
| // to be recognised. |
| task_environment_.FastForwardBy(base::Milliseconds(1)); |
| } |
| |
| // axis_stop event which should trigger fling scroll. |
| SendAxisStopEvents(); |
| |
| // Usual axis events should follow before the fling event. |
| ASSERT_TRUE(event1); |
| ASSERT_TRUE(event1->IsScrollEvent()); |
| ASSERT_TRUE(event2); |
| ASSERT_TRUE(event2->IsScrollEvent()); |
| |
| // The third dispatched event should be FLING_START. |
| ASSERT_TRUE(event3); |
| ASSERT_TRUE(event3->IsScrollEvent()); |
| auto* scroll_event = event3->AsScrollEvent(); |
| EXPECT_EQ(EventType::kScrollFlingStart, scroll_event->type()); |
| EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); |
| EXPECT_EQ(0.0f, scroll_event->y_offset()); |
| EXPECT_EQ(0.0f, scroll_event->y_offset_ordinal()); |
| // Initial horizontal velocity depends on the implementation outside of |
| // WaylandPointer, but it should be negative value based on the direction of |
| // recent two axis events. |
| EXPECT_GT(0.0f, scroll_event->x_offset()); |
| EXPECT_GT(0.0f, scroll_event->x_offset_ordinal()); |
| } |
| |
| TEST_F(WaylandPointerTest, FlingCancel) { |
| SendEnter(50, 75); |
| SendRightButtonPress(); |
| std::unique_ptr<Event> event1, event2, event3, event4, event5; |
| EXPECT_CALL(delegate_, DispatchEvent(_)) |
| .Times(5) |
| .WillOnce(CloneEvent(&event1)) |
| .WillOnce(CloneEvent(&event2)) |
| .WillOnce(CloneEvent(&event3)) |
| .WillOnce(CloneEvent(&event4)) |
| .WillOnce(CloneEvent(&event5)); |
| |
| // Send two axis events. |
| for (int n = 0; n < 2; ++n) { |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| SendAxisEvents(pointer, server->GetNextTime(), |
| WL_POINTER_AXIS_SOURCE_FINGER, |
| WL_POINTER_AXIS_VERTICAL_SCROLL, 10); |
| }); |
| // Advance time to emulate delay between events and allow the fling gesture |
| // to be recognised. |
| task_environment_.FastForwardBy(base::Milliseconds(1)); |
| } |
| |
| // axis_stop event which should trigger FLING_START. |
| SendAxisStopEvents(); |
| |
| // Another axis scroll event is added. In Linux, this must lead to a |
| // FLING_CANCEL being triggered before a usual scroll event occurs because a |
| // FLING_START was triggered beforehand. |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| SendAxisEvents(pointer, server->GetNextTime(), |
| WL_POINTER_AXIS_SOURCE_FINGER, |
| WL_POINTER_AXIS_VERTICAL_SCROLL, 10); |
| }); |
| |
| // Advance time to emulate delay between events and allow the fling gesture |
| // to be recognised. |
| task_environment_.FastForwardBy(base::Milliseconds(1)); |
| // Two usual axis events should follow before the fling event. |
| ASSERT_TRUE(event1); |
| ASSERT_TRUE(event1->IsScrollEvent()); |
| EXPECT_EQ(EventType::kScroll, event1->type()); |
| |
| ASSERT_TRUE(event2); |
| ASSERT_TRUE(event2->IsScrollEvent()); |
| EXPECT_EQ(EventType::kScroll, event2->type()); |
| |
| // The 3rd event will be fling start with vertical velocity. |
| ASSERT_TRUE(event3); |
| ASSERT_TRUE(event3->IsScrollEvent()); |
| auto* fling_start_event = event3->AsScrollEvent(); |
| EXPECT_EQ(EventType::kScrollFlingStart, fling_start_event->type()); |
| EXPECT_EQ(0.0f, fling_start_event->x_offset()); |
| EXPECT_GT(0.0f, fling_start_event->y_offset()); |
| EXPECT_EQ(0.0f, fling_start_event->x_offset_ordinal()); |
| EXPECT_GT(0.0f, fling_start_event->y_offset_ordinal()); |
| |
| // The 4th event should be FLING_CANCEL. |
| ASSERT_TRUE(event4); |
| ASSERT_TRUE(event4->IsScrollEvent()); |
| auto* fling_cancel_event = event4->AsScrollEvent(); |
| EXPECT_EQ(EventType::kScrollFlingCancel, fling_cancel_event->type()); |
| EXPECT_EQ(gfx::PointF(50, 75), fling_cancel_event->location_f()); |
| EXPECT_EQ(0.0f, fling_cancel_event->x_offset()); |
| EXPECT_EQ(0.0f, fling_cancel_event->y_offset()); |
| EXPECT_EQ(0.0f, fling_cancel_event->x_offset_ordinal()); |
| EXPECT_EQ(0.0f, fling_cancel_event->y_offset_ordinal()); |
| |
| // The 5th event will be yet another axis event. |
| ASSERT_TRUE(event5); |
| ASSERT_TRUE(event5); |
| EXPECT_EQ(EventType::kScroll, event5->type()); |
| } |
| |
| TEST_F(WaylandPointerTest, FlingDiagonal) { |
| SendEnter(50, 75); |
| |
| SendRightButtonPress(); |
| |
| std::unique_ptr<Event> event1, event2, event3; |
| EXPECT_CALL(delegate_, DispatchEvent(_)) |
| .Times(3) |
| .WillOnce(CloneEvent(&event1)) |
| .WillOnce(CloneEvent(&event2)) |
| .WillOnce(CloneEvent(&event3)); |
| |
| // Send two axis events that notify scrolls both in vertical and horizontal. |
| for (int n = 0; n < 2; ++n) { |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| SendDiagonalAxisEvents(pointer, server->GetNextTime(), |
| WL_POINTER_AXIS_SOURCE_FINGER, 20, 10); |
| }); |
| |
| // Advance time to emulate delay between events and allow the fling gesture |
| // to be recognised. |
| task_environment_.FastForwardBy(base::Milliseconds(1)); |
| } |
| |
| // axis_stop event which should trigger fling scroll. |
| SendAxisStopEvents(); |
| |
| // Usual axis events should follow before the fling event. |
| ASSERT_TRUE(event1); |
| ASSERT_TRUE(event1->IsScrollEvent()); |
| ASSERT_TRUE(event2); |
| ASSERT_TRUE(event2->IsScrollEvent()); |
| |
| // The third dispatched event should be FLING_START. |
| ASSERT_TRUE(event3); |
| ASSERT_TRUE(event3->IsScrollEvent()); |
| auto* scroll_event = event3->AsScrollEvent(); |
| EXPECT_EQ(EventType::kScrollFlingStart, scroll_event->type()); |
| EXPECT_EQ(gfx::PointF(50, 75), scroll_event->location_f()); |
| // Check the offset direction. It should non-zero in both axes. |
| EXPECT_GT(0.0f, scroll_event->x_offset()); |
| EXPECT_GT(0.0f, scroll_event->y_offset()); |
| EXPECT_GT(0.0f, scroll_event->x_offset_ordinal()); |
| EXPECT_GT(0.0f, scroll_event->y_offset_ordinal()); |
| // Horizontal offset should be larger than vertical one, given the scroll |
| // offset in each direction. |
| EXPECT_GT(std::abs(scroll_event->x_offset()), |
| std::abs(scroll_event->y_offset())); |
| EXPECT_GT(std::abs(scroll_event->x_offset_ordinal()), |
| std::abs(scroll_event->y_offset_ordinal())); |
| } |
| |
| // The case tested here should not actually occur in a good implementation of |
| // the Wayland protocol. |
| TEST_F(WaylandPointerTest, FlingVelocityWithoutLeadingAxis) { |
| SendEnter(50, 75); |
| |
| SendRightButtonPress(); |
| |
| std::unique_ptr<Event> event; |
| EXPECT_CALL(delegate_, DispatchEvent(_)) |
| .Times(1) |
| .WillOnce(CloneEvent(&event)); |
| |
| // Send axis_stop event without leading axis event. |
| SendAxisStopEvents(); |
| |
| // We expect only a FLING_START event. |
| ASSERT_TRUE(event); |
| ASSERT_TRUE(event->IsScrollEvent()); |
| auto* scroll_event = event->AsScrollEvent(); |
| EXPECT_EQ(EventType::kScrollFlingStart, scroll_event->type()); |
| |
| // Check the offset direction. It should be zero in both axes. |
| EXPECT_EQ(0.0f, scroll_event->x_offset()); |
| EXPECT_EQ(0.0f, scroll_event->y_offset()); |
| EXPECT_EQ(0.0f, scroll_event->x_offset_ordinal()); |
| EXPECT_EQ(0.0f, scroll_event->y_offset_ordinal()); |
| } |
| |
| TEST_F(WaylandPointerTest, FlingVelocityWithSingleLeadingAxis) { |
| SendEnter(50, 75); |
| |
| SendRightButtonPress(); |
| |
| std::unique_ptr<Event> event1, event2; |
| EXPECT_CALL(delegate_, DispatchEvent(_)) |
| .Times(2) |
| .WillOnce(CloneEvent(&event1)) |
| .WillOnce(CloneEvent(&event2)); |
| |
| // Send a single axis event. |
| PostToServerAndWait([](wl::TestWaylandServerThread* server) { |
| auto* const pointer = server->seat()->pointer()->resource(); |
| |
| SendDiagonalAxisEvents(pointer, server->GetNextTime(), |
| WL_POINTER_AXIS_SOURCE_FINGER, -20, 10); |
| }); |
| |
| // Advance time to emulate delay between events and allow the fling gesture |
| // to be recognised. |
| task_environment_.FastForwardBy(base::Milliseconds(1)); |
| |
| // One axis event should follow before the fling event. |
| SendAxisStopEvents(); |
| ASSERT_TRUE(event1); |
| ASSERT_TRUE(event1->IsScrollEvent()); |
| |
| // We expect a FLING_START event. |
| ASSERT_TRUE(event2); |
| ASSERT_TRUE(event2->IsScrollEvent()); |
| auto* scroll_event2 = event2->AsScrollEvent(); |
| EXPECT_EQ(EventType::kScrollFlingStart, scroll_event2->type()); |
| |
| // Check the offset direction. Horizontal axis should be negative. Vertical |
| // axis should be positive. |
| EXPECT_LT(0.0f, scroll_event2->x_offset()); |
| EXPECT_GT(0.0f, scroll_event2->y_offset()); |
| EXPECT_LT(0.0f, scroll_event2->x_offset_ordinal()); |
| EXPECT_GT(0.0f, scroll_event2->y_offset_ordinal()); |
| } |
| |
| } // namespace ui |