diff --git a/shell/platform/fuchsia/flutter/pointer_delegate.cc b/shell/platform/fuchsia/flutter/pointer_delegate.cc index f85f141a57100..71cdb63d73eb1 100644 --- a/shell/platform/fuchsia/flutter/pointer_delegate.cc +++ b/shell/platform/fuchsia/flutter/pointer_delegate.cc @@ -287,22 +287,40 @@ flutter::PointerData CreateMouseDraft(const fup_MouseEvent& event, ptr.buttons = flutter_buttons; } - // Fuchsia currently provides scroll data in "ticks", not physical pixels. - // However, Flutter expects scroll data in physical pixels. To compensate for - // lack of guidance, we make up a "reasonable amount". - // TODO(fxbug.dev/85388): Replace with physical pixel scroll. + // Fuchsia previously only provided scroll data in "ticks", not physical + // pixels. On legacy platforms, since Flutter expects scroll data in physical + // pixels, to compensate for lack of guidance, we make up a "reasonable + // amount". + // TODO(fxbug.dev/103443): Remove the tick based scrolling after the + // transition. const int kScrollOffsetMultiplier = 20; - if (sample.has_scroll_v()) { - ptr.signal_kind = flutter::PointerData::SignalKind::kScroll; - double dy = -sample.scroll_v() * kScrollOffsetMultiplier; // logical amount - ptr.scroll_delta_y = dy; // Not yet physical; adjusted in Platform View. + double dy = 0; + double dx = 0; + bool is_scroll = false; + + if (sample.has_scroll_v_physical_pixel()) { + dy = -sample.scroll_v_physical_pixel(); + is_scroll = true; + } else if (sample.has_scroll_v()) { + dy = -sample.scroll_v() * + kScrollOffsetMultiplier; // logical amount, not yet physical; adjusted + // in Platform View. + is_scroll = true; + } + + if (sample.has_scroll_h_physical_pixel()) { + dx = sample.scroll_h_physical_pixel(); + is_scroll = true; + } else if (sample.has_scroll_h()) { + dx = sample.scroll_h() * kScrollOffsetMultiplier; // logical amount + is_scroll = true; } - if (sample.has_scroll_h()) { + if (is_scroll) { ptr.signal_kind = flutter::PointerData::SignalKind::kScroll; - double dx = sample.scroll_h() * kScrollOffsetMultiplier; // logical amount - ptr.scroll_delta_x = dx; // Not yet physical; adjusted in Platform View. + ptr.scroll_delta_y = dy; + ptr.scroll_delta_x = dx; } return ptr; diff --git a/shell/platform/fuchsia/flutter/pointer_delegate_unittests.cc b/shell/platform/fuchsia/flutter/pointer_delegate_unittests.cc index 763958c37ebf2..112192bca3d3f 100644 --- a/shell/platform/fuchsia/flutter/pointer_delegate_unittests.cc +++ b/shell/platform/fuchsia/flutter/pointer_delegate_unittests.cc @@ -30,6 +30,7 @@ using fup_TouchPointerSample = fuchsia::ui::pointer::TouchPointerSample; using fup_TouchResponse = fuchsia::ui::pointer::TouchResponse; using fup_TouchResponseType = fuchsia::ui::pointer::TouchResponseType; using fup_ViewParameters = fuchsia::ui::pointer::ViewParameters; +using fup_MouseEvent = fuchsia::ui::pointer::MouseEvent; constexpr std::array, 2> kRect = {{{0, 0}, {20, 20}}}; constexpr std::array kIdentity = {1, 0, 0, 0, 1, 0, 0, 0, 1}; @@ -37,6 +38,11 @@ constexpr fup_TouchIxnId kIxnOne = {.device_id = 1u, .pointer_id = 1u, .interaction_id = 2u}; +constexpr uint32_t kMouseDeviceId = 123; +constexpr std::array kNoScrollInPhysicalPixelDelta = {0, 0}; +const bool kNotPrecisionScroll = false; +const bool kPrecisionScroll = true; + // Fixture to exercise Flutter runner's implementation for // fuchsia.ui.pointer.TouchSource. class PointerDelegateTest : public ::testing::Test { @@ -676,4 +682,163 @@ TEST_F(PointerDelegateTest, Protocol_PointersAreIndependent) { pointers = {}; } +TEST_F(PointerDelegateTest, MouseWheel_TickBased) { + std::optional> pointers; + pointer_delegate_->WatchLoop( + [&pointers](std::vector events) { + pointers = std::move(events); + }); + RunLoopUntilIdle(); // Server gets watch call. + + std::vector events = + MouseEventBuilder() + .AddTime(1111789u) + .AddViewParameters(kRect, kRect, kIdentity) + .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {0, 1}, + kNoScrollInPhysicalPixelDelta, kNotPrecisionScroll) + .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2}) + .BuildAsVector(); + mouse_source_->ScheduleCallback(std::move(events)); + RunLoopUntilIdle(); + + ASSERT_TRUE(pointers.has_value()); + ASSERT_EQ(pointers.value().size(), 1u); + EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover); + EXPECT_EQ(pointers.value()[0].signal_kind, + flutter::PointerData::SignalKind::kScroll); + EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse); + EXPECT_EQ(pointers.value()[0].buttons, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_x, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_y, -20); + pointers = {}; + + // receive a horizontal scroll + events = MouseEventBuilder() + .AddTime(1111789u) + .AddViewParameters(kRect, kRect, kIdentity) + .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {1, 0}, + kNoScrollInPhysicalPixelDelta, kNotPrecisionScroll) + .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2}) + .BuildAsVector(); + mouse_source_->ScheduleCallback(std::move(events)); + RunLoopUntilIdle(); + + ASSERT_TRUE(pointers.has_value()); + ASSERT_EQ(pointers.value().size(), 1u); + EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover); + EXPECT_EQ(pointers.value()[0].signal_kind, + flutter::PointerData::SignalKind::kScroll); + EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse); + EXPECT_EQ(pointers.value()[0].buttons, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_x, 20); + EXPECT_EQ(pointers.value()[0].scroll_delta_y, 0); + pointers = {}; +} + +TEST_F(PointerDelegateTest, MouseWheel_PixelBased) { + std::optional> pointers; + pointer_delegate_->WatchLoop( + [&pointers](std::vector events) { + pointers = std::move(events); + }); + RunLoopUntilIdle(); // Server gets watch call. + + std::vector events = + MouseEventBuilder() + .AddTime(1111789u) + .AddViewParameters(kRect, kRect, kIdentity) + .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {0, 1}, {0, 120}, + kNotPrecisionScroll) + .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2}) + .BuildAsVector(); + mouse_source_->ScheduleCallback(std::move(events)); + RunLoopUntilIdle(); + + ASSERT_TRUE(pointers.has_value()); + ASSERT_EQ(pointers.value().size(), 1u); + EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover); + EXPECT_EQ(pointers.value()[0].signal_kind, + flutter::PointerData::SignalKind::kScroll); + EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse); + EXPECT_EQ(pointers.value()[0].buttons, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_x, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_y, -120); + pointers = {}; + + // receive a horizontal scroll + events = MouseEventBuilder() + .AddTime(1111789u) + .AddViewParameters(kRect, kRect, kIdentity) + .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {1, 0}, {120, 0}, + kNotPrecisionScroll) + .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2}) + .BuildAsVector(); + mouse_source_->ScheduleCallback(std::move(events)); + RunLoopUntilIdle(); + + ASSERT_TRUE(pointers.has_value()); + ASSERT_EQ(pointers.value().size(), 1u); + EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover); + EXPECT_EQ(pointers.value()[0].signal_kind, + flutter::PointerData::SignalKind::kScroll); + EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse); + EXPECT_EQ(pointers.value()[0].buttons, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_x, 120); + EXPECT_EQ(pointers.value()[0].scroll_delta_y, 0); + pointers = {}; +} + +TEST_F(PointerDelegateTest, MouseWheel_TouchpadPixelBased) { + std::optional> pointers; + pointer_delegate_->WatchLoop( + [&pointers](std::vector events) { + pointers = std::move(events); + }); + RunLoopUntilIdle(); // Server gets watch call. + + std::vector events = + MouseEventBuilder() + .AddTime(1111789u) + .AddViewParameters(kRect, kRect, kIdentity) + .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {0, 1}, {0, 120}, + kPrecisionScroll) + .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2}) + .BuildAsVector(); + mouse_source_->ScheduleCallback(std::move(events)); + RunLoopUntilIdle(); + + ASSERT_TRUE(pointers.has_value()); + ASSERT_EQ(pointers.value().size(), 1u); + EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover); + EXPECT_EQ(pointers.value()[0].signal_kind, + flutter::PointerData::SignalKind::kScroll); + EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse); + EXPECT_EQ(pointers.value()[0].buttons, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_x, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_y, -120); + pointers = {}; + + // receive a horizontal scroll + events = MouseEventBuilder() + .AddTime(1111789u) + .AddViewParameters(kRect, kRect, kIdentity) + .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {1, 0}, {120, 0}, + kPrecisionScroll) + .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2}) + .BuildAsVector(); + mouse_source_->ScheduleCallback(std::move(events)); + RunLoopUntilIdle(); + + ASSERT_TRUE(pointers.has_value()); + ASSERT_EQ(pointers.value().size(), 1u); + EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover); + EXPECT_EQ(pointers.value()[0].signal_kind, + flutter::PointerData::SignalKind::kScroll); + EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse); + EXPECT_EQ(pointers.value()[0].buttons, 0); + EXPECT_EQ(pointers.value()[0].scroll_delta_x, 120); + EXPECT_EQ(pointers.value()[0].scroll_delta_y, 0); + pointers = {}; +} + } // namespace flutter_runner::testing diff --git a/shell/platform/fuchsia/flutter/tests/pointer_event_utility.cc b/shell/platform/fuchsia/flutter/tests/pointer_event_utility.cc index 6e003bfea0d56..a1e3023113089 100644 --- a/shell/platform/fuchsia/flutter/tests/pointer_event_utility.cc +++ b/shell/platform/fuchsia/flutter/tests/pointer_event_utility.cc @@ -12,6 +12,30 @@ using fup_TouchIxnId = fuchsia::ui::pointer::TouchInteractionId; using fup_TouchIxnResult = fuchsia::ui::pointer::TouchInteractionResult; using fup_TouchPointerSample = fuchsia::ui::pointer::TouchPointerSample; using fup_ViewParameters = fuchsia::ui::pointer::ViewParameters; +using fup_MouseEvent = fuchsia::ui::pointer::MouseEvent; +using fup_MousePointerSample = fuchsia::ui::pointer::MousePointerSample; +using fup_MouseDeviceInfo = fuchsia::ui::pointer::MouseDeviceInfo; + +namespace { + +fup_ViewParameters CreateViewParameters( + std::array, 2> view, + std::array, 2> viewport, + std::array transform) { + fup_ViewParameters params; + fuchsia::ui::pointer::Rectangle view_rect; + view_rect.min = view[0]; + view_rect.max = view[1]; + params.view = view_rect; + fuchsia::ui::pointer::Rectangle viewport_rect; + viewport_rect.min = viewport[0]; + viewport_rect.max = viewport[1]; + params.viewport = viewport_rect; + params.viewport_to_view_transform = transform; + return params; +} + +} // namespace TouchEventBuilder TouchEventBuilder::New() { return TouchEventBuilder(); @@ -36,16 +60,8 @@ TouchEventBuilder& TouchEventBuilder::AddViewParameters( std::array, 2> view, std::array, 2> viewport, std::array transform) { - params_ = std::make_optional(); - fuchsia::ui::pointer::Rectangle view_rect; - view_rect.min = view[0]; - view_rect.max = view[1]; - params_->view = view_rect; - fuchsia::ui::pointer::Rectangle viewport_rect; - viewport_rect.min = viewport[0]; - viewport_rect.max = viewport[1]; - params_->viewport = viewport_rect; - params_->viewport_to_view_transform = transform; + params_ = CreateViewParameters(std::move(view), std::move(viewport), + std::move(transform)); return *this; } @@ -77,4 +93,84 @@ std::vector TouchEventBuilder::BuildAsVector() { return events; } +MouseEventBuilder MouseEventBuilder::New() { + return MouseEventBuilder(); +} + +MouseEventBuilder& MouseEventBuilder::AddTime(zx_time_t time) { + time_ = time; + return *this; +} + +MouseEventBuilder& MouseEventBuilder::AddSample( + uint32_t id, + std::array position, + std::vector pressed_buttons, + std::array scroll, + std::array scroll_in_physical_pixel, + bool is_precision_scroll) { + sample_ = std::make_optional(); + sample_->set_device_id(id); + if (!pressed_buttons.empty()) { + sample_->set_pressed_buttons(pressed_buttons); + } + sample_->set_position_in_viewport(position); + if (scroll[0] != 0) { + sample_->set_scroll_h(scroll[0]); + } + if (scroll[1] != 0) { + sample_->set_scroll_v(scroll[1]); + } + if (scroll_in_physical_pixel[0] != 0) { + sample_->set_scroll_h_physical_pixel(scroll_in_physical_pixel[0]); + } + if (scroll_in_physical_pixel[1] != 0) { + sample_->set_scroll_v_physical_pixel(scroll_in_physical_pixel[1]); + } + sample_->set_is_precision_scroll(is_precision_scroll); + return *this; +} + +MouseEventBuilder& MouseEventBuilder::AddViewParameters( + std::array, 2> view, + std::array, 2> viewport, + std::array transform) { + params_ = CreateViewParameters(std::move(view), std::move(viewport), + std::move(transform)); + return *this; +} + +MouseEventBuilder& MouseEventBuilder::AddMouseDeviceInfo( + uint32_t id, + std::vector buttons) { + device_info_ = std::make_optional(); + device_info_->set_id(id); + device_info_->set_buttons(buttons); + return *this; +} + +fup_MouseEvent MouseEventBuilder::Build() { + fup_MouseEvent event; + if (time_) { + event.set_timestamp(time_.value()); + } + if (params_) { + event.set_view_parameters(std::move(params_.value())); + } + if (sample_) { + event.set_pointer_sample(std::move(sample_.value())); + } + if (device_info_) { + event.set_device_info(std::move(device_info_.value())); + } + event.set_trace_flow_id(123); + return event; +} + +std::vector MouseEventBuilder::BuildAsVector() { + std::vector events; + events.emplace_back(Build()); + return events; +} + } // namespace flutter_runner::testing diff --git a/shell/platform/fuchsia/flutter/tests/pointer_event_utility.h b/shell/platform/fuchsia/flutter/tests/pointer_event_utility.h index f1702a74c67ed..243638436bf27 100644 --- a/shell/platform/fuchsia/flutter/tests/pointer_event_utility.h +++ b/shell/platform/fuchsia/flutter/tests/pointer_event_utility.h @@ -40,6 +40,34 @@ class TouchEventBuilder { std::optional result_; }; +// A helper class for crafting a fuchsia.ui.pointer.MouseEventBuilder table. +class MouseEventBuilder { + public: + static MouseEventBuilder New(); + + MouseEventBuilder& AddTime(zx_time_t time); + MouseEventBuilder& AddSample(uint32_t id, + std::array position, + std::vector pressed_buttons, + std::array scroll, + std::array scroll_in_physical_pixel, + bool is_precision_scroll); + MouseEventBuilder& AddViewParameters( + std::array, 2> view, + std::array, 2> viewport, + std::array transform); + MouseEventBuilder& AddMouseDeviceInfo(uint32_t id, + std::vector buttons); + fuchsia::ui::pointer::MouseEvent Build(); + std::vector BuildAsVector(); + + private: + std::optional time_; + std::optional sample_; + std::optional params_; + std::optional device_info_; +}; + } // namespace flutter_runner::testing #endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_TESTS_POINTER_EVENT_UTILITY_H_