Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
40 changes: 29 additions & 11 deletions shell/platform/fuchsia/flutter/pointer_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
165 changes: 165 additions & 0 deletions shell/platform/fuchsia/flutter/pointer_delegate_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,19 @@ 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<std::array<float, 2>, 2> kRect = {{{0, 0}, {20, 20}}};
constexpr std::array<float, 9> kIdentity = {1, 0, 0, 0, 1, 0, 0, 0, 1};
constexpr fup_TouchIxnId kIxnOne = {.device_id = 1u,
.pointer_id = 1u,
.interaction_id = 2u};

constexpr uint32_t kMouseDeviceId = 123;
constexpr std::array<int64_t, 2> 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 {
Expand Down Expand Up @@ -676,4 +682,163 @@ TEST_F(PointerDelegateTest, Protocol_PointersAreIndependent) {
pointers = {};
}

TEST_F(PointerDelegateTest, MouseWheel_TickBased) {
std::optional<std::vector<flutter::PointerData>> pointers;
pointer_delegate_->WatchLoop(
[&pointers](std::vector<flutter::PointerData> events) {
pointers = std::move(events);
});
RunLoopUntilIdle(); // Server gets watch call.

std::vector<fup_MouseEvent> 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<std::vector<flutter::PointerData>> pointers;
pointer_delegate_->WatchLoop(
[&pointers](std::vector<flutter::PointerData> events) {
pointers = std::move(events);
});
RunLoopUntilIdle(); // Server gets watch call.

std::vector<fup_MouseEvent> 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<std::vector<flutter::PointerData>> pointers;
pointer_delegate_->WatchLoop(
[&pointers](std::vector<flutter::PointerData> events) {
pointers = std::move(events);
});
RunLoopUntilIdle(); // Server gets watch call.

std::vector<fup_MouseEvent> 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
116 changes: 106 additions & 10 deletions shell/platform/fuchsia/flutter/tests/pointer_event_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::array<float, 2>, 2> view,
std::array<std::array<float, 2>, 2> viewport,
std::array<float, 9> 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();
Expand All @@ -36,16 +60,8 @@ TouchEventBuilder& TouchEventBuilder::AddViewParameters(
std::array<std::array<float, 2>, 2> view,
std::array<std::array<float, 2>, 2> viewport,
std::array<float, 9> transform) {
params_ = std::make_optional<fup_ViewParameters>();
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;
}

Expand Down Expand Up @@ -77,4 +93,84 @@ std::vector<fup_TouchEvent> 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<float, 2> position,
std::vector<uint8_t> pressed_buttons,
std::array<int64_t, 2> scroll,
std::array<int64_t, 2> scroll_in_physical_pixel,
bool is_precision_scroll) {
sample_ = std::make_optional<fup_MousePointerSample>();
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<std::array<float, 2>, 2> view,
std::array<std::array<float, 2>, 2> viewport,
std::array<float, 9> transform) {
params_ = CreateViewParameters(std::move(view), std::move(viewport),
std::move(transform));
return *this;
}

MouseEventBuilder& MouseEventBuilder::AddMouseDeviceInfo(
uint32_t id,
std::vector<uint8_t> buttons) {
device_info_ = std::make_optional<fup_MouseDeviceInfo>();
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<fup_MouseEvent> MouseEventBuilder::BuildAsVector() {
std::vector<fup_MouseEvent> events;
events.emplace_back(Build());
return events;
}

} // namespace flutter_runner::testing
Loading