未验证 提交 132d38cd 编写于 作者: C chunhtai 提交者: GitHub

Moves pointer event sanitizing to engine. (#13697)

* Moves pointer event sanitizing to engine

* fix comment format

* fix formatting

* addressing comment

* fix format

* fix format

* addressing comment
上级 f4fba66c
......@@ -344,6 +344,9 @@ FILE: ../../../flutter/lib/ui/window/pointer_data.cc
FILE: ../../../flutter/lib/ui/window/pointer_data.h
FILE: ../../../flutter/lib/ui/window/pointer_data_packet.cc
FILE: ../../../flutter/lib/ui/window/pointer_data_packet.h
FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc
FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h
FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter_unittests.cc
FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc
FILE: ../../../flutter/lib/ui/window/viewport_metrics.h
FILE: ../../../flutter/lib/ui/window/window.cc
......
......@@ -101,6 +101,8 @@ source_set("ui") {
"window/pointer_data.h",
"window/pointer_data_packet.cc",
"window/pointer_data_packet.h",
"window/pointer_data_packet_converter.cc",
"window/pointer_data_packet_converter.h",
"window/viewport_metrics.cc",
"window/viewport_metrics.h",
"window/window.cc",
......@@ -161,6 +163,7 @@ if (current_toolchain == host_toolchain) {
sources = [
"painting/image_decoder_unittests.cc",
"window/pointer_data_packet_converter_unittests.cc",
]
deps = [
......
......@@ -308,8 +308,9 @@ void _invoke3<A1, A2, A3>(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1
// If this value changes, update the encoding code in the following files:
//
// * pointer_data.cc
// * FlutterView.java
const int _kPointerDataFieldCount = 24;
// * pointers.dart
// * AndroidTouchProcessor.java
const int _kPointerDataFieldCount = 28;
PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
const int kStride = Int64List.bytesPerElement;
......@@ -325,10 +326,14 @@ PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
signalKind: PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
device: packet.getInt64(kStride * offset++, _kFakeHostEndian),
pointerIdentifier: packet.getInt64(kStride * offset++, _kFakeHostEndian),
physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
physicalDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
physicalDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian),
obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0,
synthesized: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0,
pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
......
......@@ -75,10 +75,14 @@ class PointerData {
this.kind = PointerDeviceKind.touch,
this.signalKind,
this.device = 0,
this.pointerIdentifier = 0,
this.physicalX = 0.0,
this.physicalY = 0.0,
this.physicalDeltaX = 0.0,
this.physicalDeltaY = 0.0,
this.buttons = 0,
this.obscured = false,
this.synthesized = false,
this.pressure = 0.0,
this.pressureMin = 0.0,
this.pressureMax = 0.0,
......@@ -111,6 +115,12 @@ class PointerData {
/// Unique identifier for the pointing device, reused across interactions.
final int device;
/// Unique identifier for the pointer.
///
/// This field changes for each new pointer down event. Framework uses this
/// identifier to determine hit test result.
final int pointerIdentifier;
/// X coordinate of the position of the pointer, in physical pixels in the
/// global coordinate space.
final double physicalX;
......@@ -119,6 +129,12 @@ class PointerData {
/// global coordinate space.
final double physicalY;
/// The distance of pointer movement on X coordinate in physical pixels.
final double physicalDeltaX;
/// The distance of pointer movement on Y coordinate in physical pixels.
final double physicalDeltaY;
/// Bit field using the *Button constants (primaryMouseButton,
/// secondaryStylusButton, etc). For example, if this has the value 6 and the
/// [kind] is [PointerDeviceKind.invertedStylus], then this indicates an
......@@ -130,6 +146,14 @@ class PointerData {
/// implemented.)
final bool obscured;
/// Set if this pointer data was synthesized by pointer data packet converter.
/// pointer data packet converter will synthesize additional pointer datas if
/// the input sequence of pointer data is illegal.
///
/// For example, a down pointer data will be synthesized if the converter receives
/// a move pointer data while the pointer is not previously down.
final bool synthesized;
/// The pressure of the touch as a number ranging from 0.0, indicating a touch
/// with no discernible pressure, to 1.0, indicating a touch with "normal"
/// pressure, and possibly beyond, indicating a stronger touch. For devices
......@@ -242,9 +266,13 @@ class PointerData {
'kind: $kind, '
'signalKind: $signalKind, '
'device: $device, '
'pointerIdentifier: $pointerIdentifier, '
'physicalX: $physicalX, '
'physicalY: $physicalY, '
'physicalDeltaX: $physicalDeltaX, '
'physicalDeltaY: $physicalDeltaY, '
'buttons: $buttons, '
'synthesized: $synthesized, '
'pressure: $pressure, '
'pressureMin: $pressureMin, '
'pressureMax: $pressureMax, '
......
......@@ -8,10 +8,7 @@
namespace flutter {
// If this value changes, update the pointer data unpacking code in hooks.dart.
static constexpr int kPointerDataFieldCount = 24;
static_assert(sizeof(PointerData) == sizeof(int64_t) * kPointerDataFieldCount,
static_assert(sizeof(PointerData) == kBytesPerField * kPointerDataFieldCount,
"PointerData has the wrong size");
void PointerData::Clear() {
......
......@@ -9,6 +9,9 @@
namespace flutter {
// If this value changes, update the pointer data unpacking code in hooks.dart.
static constexpr int kPointerDataFieldCount = 28;
static constexpr int kBytesPerField = sizeof(int64_t);
// Must match the button constants in events.dart.
enum PointerButtonMouse : int64_t {
kPointerButtonMousePrimary = 1 << 0,
......@@ -60,10 +63,14 @@ struct alignas(8) PointerData {
DeviceKind kind;
SignalKind signal_kind;
int64_t device;
int64_t pointer_identifier;
double physical_x;
double physical_y;
double physical_delta_x;
double physical_delta_y;
int64_t buttons;
int64_t obscured;
int64_t synthesized;
double pressure;
double pressure_min;
double pressure_max;
......
// Copyright 2013 The Flutter 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 "flutter/lib/ui/window/pointer_data_packet_converter.h"
#include "flutter/fml/logging.h"
#include <string.h>
namespace flutter {
PointerDataPacketConverter::PointerDataPacketConverter() : pointer_(0) {}
PointerDataPacketConverter::~PointerDataPacketConverter() = default;
std::unique_ptr<PointerDataPacket> PointerDataPacketConverter::Convert(
std::unique_ptr<PointerDataPacket> packet) {
size_t kBytesPerPointerData = kPointerDataFieldCount * kBytesPerField;
auto buffer = packet->data();
size_t buffer_length = buffer.size();
std::vector<PointerData> converted_pointers;
// Converts each pointer data in the buffer and stores it in the
// converted_pointers.
for (size_t i = 0; i < buffer_length / kBytesPerPointerData; i++) {
PointerData pointer_data;
memcpy(&pointer_data, &buffer[i * kBytesPerPointerData],
sizeof(PointerData));
ConvertPointerData(pointer_data, converted_pointers);
}
// Writes converted_pointers into converted_packet.
auto converted_packet =
std::make_unique<flutter::PointerDataPacket>(converted_pointers.size());
size_t count = 0;
for (auto& converted_pointer : converted_pointers) {
converted_packet->SetPointerData(count++, converted_pointer);
}
return converted_packet;
}
void PointerDataPacketConverter::ConvertPointerData(
PointerData pointer_data,
std::vector<PointerData>& converted_pointers) {
if (pointer_data.signal_kind == PointerData::SignalKind::kNone) {
switch (pointer_data.change) {
case PointerData::Change::kCancel: {
// Android's three finger gesture will send a cancel event
// to a non-existing pointer. Drops the cancel if pointer
// is not previously added.
// https://github.com/flutter/flutter/issues/20517
auto iter = states_.find(pointer_data.device);
if (iter != states_.end()) {
PointerState state = iter->second;
FML_DCHECK(state.isDown);
UpdatePointerIdentifier(pointer_data, state, false);
if (LocationNeedsUpdate(pointer_data, state)) {
// Synthesizes a move event if the location does not match.
PointerData synthesized_move_event = pointer_data;
synthesized_move_event.change = PointerData::Change::kMove;
synthesized_move_event.synthesized = 1;
UpdateDeltaAndState(synthesized_move_event, state);
converted_pointers.push_back(synthesized_move_event);
}
state.isDown = false;
states_[pointer_data.device] = state;
converted_pointers.push_back(pointer_data);
}
break;
}
case PointerData::Change::kAdd: {
FML_DCHECK(states_.find(pointer_data.device) == states_.end());
EnsurePointerState(pointer_data);
converted_pointers.push_back(pointer_data);
break;
}
case PointerData::Change::kRemove: {
// Makes sure we have an existing pointer
auto iter = states_.find(pointer_data.device);
FML_DCHECK(iter != states_.end());
PointerState state = iter->second;
if (state.isDown) {
// Synthesizes cancel event if the pointer is down.
PointerData synthesized_cancel_event = pointer_data;
synthesized_cancel_event.change = PointerData::Change::kCancel;
synthesized_cancel_event.synthesized = 1;
UpdatePointerIdentifier(synthesized_cancel_event, state, false);
state.isDown = false;
states_[synthesized_cancel_event.device] = state;
converted_pointers.push_back(synthesized_cancel_event);
}
if (LocationNeedsUpdate(pointer_data, state)) {
// Synthesizes a hover event if the location does not match.
PointerData synthesized_hover_event = pointer_data;
synthesized_hover_event.change = PointerData::Change::kHover;
synthesized_hover_event.synthesized = 1;
UpdateDeltaAndState(synthesized_hover_event, state);
converted_pointers.push_back(synthesized_hover_event);
}
states_.erase(pointer_data.device);
converted_pointers.push_back(pointer_data);
break;
}
case PointerData::Change::kHover: {
auto iter = states_.find(pointer_data.device);
PointerState state;
if (iter == states_.end()) {
// Synthesizes add event if the pointer is not previously added.
PointerData synthesized_add_event = pointer_data;
synthesized_add_event.change = PointerData::Change::kAdd;
synthesized_add_event.synthesized = 1;
state = EnsurePointerState(synthesized_add_event);
converted_pointers.push_back(synthesized_add_event);
} else {
state = iter->second;
}
FML_DCHECK(!state.isDown);
if (LocationNeedsUpdate(pointer_data, state)) {
UpdateDeltaAndState(pointer_data, state);
converted_pointers.push_back(pointer_data);
}
break;
}
case PointerData::Change::kDown: {
auto iter = states_.find(pointer_data.device);
PointerState state;
if (iter == states_.end()) {
// Synthesizes a add event if the pointer is not previously added.
PointerData synthesized_add_event = pointer_data;
synthesized_add_event.change = PointerData::Change::kAdd;
synthesized_add_event.synthesized = 1;
state = EnsurePointerState(synthesized_add_event);
converted_pointers.push_back(synthesized_add_event);
} else {
state = iter->second;
}
FML_DCHECK(!state.isDown);
if (LocationNeedsUpdate(pointer_data, state)) {
// Synthesizes a hover event if the location does not match.
PointerData synthesized_hover_event = pointer_data;
synthesized_hover_event.change = PointerData::Change::kHover;
synthesized_hover_event.synthesized = 1;
UpdateDeltaAndState(synthesized_hover_event, state);
converted_pointers.push_back(synthesized_hover_event);
}
UpdatePointerIdentifier(pointer_data, state, true);
state.isDown = true;
states_[pointer_data.device] = state;
converted_pointers.push_back(pointer_data);
break;
}
case PointerData::Change::kMove: {
// Makes sure we have an existing pointer in down state
auto iter = states_.find(pointer_data.device);
FML_DCHECK(iter != states_.end());
PointerState state = iter->second;
FML_DCHECK(state.isDown);
if (LocationNeedsUpdate(pointer_data, state)) {
UpdatePointerIdentifier(pointer_data, state, false);
UpdateDeltaAndState(pointer_data, state);
converted_pointers.push_back(pointer_data);
}
break;
}
case PointerData::Change::kUp: {
// Makes sure we have an existing pointer in down state
auto iter = states_.find(pointer_data.device);
FML_DCHECK(iter != states_.end());
PointerState state = iter->second;
FML_DCHECK(state.isDown);
UpdatePointerIdentifier(pointer_data, state, false);
if (LocationNeedsUpdate(pointer_data, state)) {
// Synthesizes a move event if the location does not match.
PointerData synthesized_move_event = pointer_data;
synthesized_move_event.change = PointerData::Change::kMove;
synthesized_move_event.synthesized = 1;
UpdateDeltaAndState(synthesized_move_event, state);
converted_pointers.push_back(synthesized_move_event);
}
state.isDown = false;
states_[pointer_data.device] = state;
converted_pointers.push_back(pointer_data);
break;
}
default: {
converted_pointers.push_back(pointer_data);
break;
}
}
} else {
switch (pointer_data.signal_kind) {
case PointerData::SignalKind::kScroll: {
// Makes sure we have an existing pointer
auto iter = states_.find(pointer_data.device);
FML_DCHECK(iter != states_.end());
PointerState state = iter->second;
if (LocationNeedsUpdate(pointer_data, state)) {
if (state.isDown) {
// Synthesizes a move event if the pointer is down.
PointerData synthesized_move_event = pointer_data;
synthesized_move_event.signal_kind = PointerData::SignalKind::kNone;
synthesized_move_event.change = PointerData::Change::kMove;
synthesized_move_event.synthesized = 1;
UpdateDeltaAndState(synthesized_move_event, state);
converted_pointers.push_back(synthesized_move_event);
} else {
// Synthesizes a hover event if the pointer is up.
PointerData synthesized_hover_event = pointer_data;
synthesized_hover_event.signal_kind =
PointerData::SignalKind::kNone;
synthesized_hover_event.change = PointerData::Change::kHover;
synthesized_hover_event.synthesized = 1;
UpdateDeltaAndState(synthesized_hover_event, state);
converted_pointers.push_back(synthesized_hover_event);
}
}
converted_pointers.push_back(pointer_data);
break;
}
default: {
// Ignores unknown signal kind.
break;
}
}
}
}
PointerState PointerDataPacketConverter::EnsurePointerState(
PointerData pointer_data) {
PointerState state;
state.pointer_identifier = 0;
state.isDown = false;
state.physical_x = pointer_data.physical_x;
state.physical_y = pointer_data.physical_y;
states_[pointer_data.device] = state;
return state;
}
void PointerDataPacketConverter::UpdateDeltaAndState(PointerData& pointer_data,
PointerState& state) {
pointer_data.physical_delta_x = pointer_data.physical_x - state.physical_x;
pointer_data.physical_delta_y = pointer_data.physical_y - state.physical_y;
state.physical_x = pointer_data.physical_x;
state.physical_y = pointer_data.physical_y;
states_[pointer_data.device] = state;
}
bool PointerDataPacketConverter::LocationNeedsUpdate(
const PointerData pointer_data,
const PointerState state) {
return state.physical_x != pointer_data.physical_x ||
state.physical_y != pointer_data.physical_y;
}
void PointerDataPacketConverter::UpdatePointerIdentifier(
PointerData& pointer_data,
PointerState& state,
bool start_new_pointer) {
if (start_new_pointer) {
state.pointer_identifier = ++pointer_;
states_[pointer_data.device] = state;
}
pointer_data.pointer_identifier = state.pointer_identifier;
}
} // namespace flutter
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_CONVERTER_H_
#define FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_CONVERTER_H_
#include <string.h>
#include <map>
#include <memory>
#include <vector>
#include "flutter/fml/macros.h"
#include "flutter/lib/ui/window/pointer_data_packet.h"
namespace flutter {
//------------------------------------------------------------------------------
/// The current information about a pointer. This struct is used by
/// PointerDataPacketConverter to fill in necesarry information for raw pointer
/// packet sent from embedding.
///
struct PointerState {
int64_t pointer_identifier;
bool isDown;
double physical_x;
double physical_y;
};
//------------------------------------------------------------------------------
/// Converter to convert the raw pointer data packet from the platforms.
///
/// Framework requires certain information to process pointer data. e.g. pointer
/// identifier and the delta of pointer moment. The converter keeps track each
/// pointer state and fill in those information appropriately.
///
/// The converter is also resposible for providing a clean pointer data stream.
/// It will attempt to correct the stream if the it contains illegal pointer
/// transitions.
///
/// Example 1 Missing Add:
///
/// Down(position x) -> Up(position x)
///
/// ###After Conversion###
///
/// Synthesized_Add(position x) -> Down(position x) -> Up(position x)
///
/// Example 2 Missing another move:
///
/// Add(position x) -> Down(position x) -> Move(position y) ->
/// Up(position z)
///
/// ###After Conversion###
///
/// Add(position x) -> Down(position x) -> Move(position y) ->
/// Synthesized_Move(position z) -> Up(position z)
///
/// Platform view is the only client that uses this class to convert all the
/// incoming pointer packet and is responsible for the life cycle of its
/// instance.
///
class PointerDataPacketConverter {
public:
PointerDataPacketConverter();
~PointerDataPacketConverter();
//----------------------------------------------------------------------------
/// @brief Converts pointer data packet into a form that framework
/// understands. The raw pointer data packet from embedding does
/// not have sufficient information and may contain illegal
/// pointer transitions. This method will fill out that
/// information and attempt to correct pointer transitions.
///
/// @param[in] packet The raw pointer packet sent from
/// embedding.
///
/// @return A full converted packet with all the required information
/// filled.
/// It may contain synthetic pointer data as the result of
/// converter's attempt to correct illegal pointer transitions.
///
std::unique_ptr<PointerDataPacket> Convert(
std::unique_ptr<PointerDataPacket> packet);
private:
std::map<int64_t, PointerState> states_;
int64_t pointer_;
void ConvertPointerData(PointerData pointer_data,
std::vector<PointerData>& converted_pointers);
PointerState EnsurePointerState(PointerData pointer_data);
void UpdateDeltaAndState(PointerData& pointer_data, PointerState& state);
void UpdatePointerIdentifier(PointerData& pointer_data,
PointerState& state,
bool start_new_pointer);
bool LocationNeedsUpdate(const PointerData pointer_data,
const PointerState state);
FML_DISALLOW_COPY_AND_ASSIGN(PointerDataPacketConverter);
};
} // namespace flutter
#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_PACKET_CONVERTER_H_
......@@ -75,10 +75,14 @@ class PointerData {
this.kind = PointerDeviceKind.touch,
this.signalKind,
this.device = 0,
this.pointerIdentifier = 0,
this.physicalX = 0.0,
this.physicalY = 0.0,
this.physicalDeltaX = 0.0,
this.physicalDeltaY = 0.0,
this.buttons = 0,
this.obscured = false,
this.synthesized = false,
this.pressure = 0.0,
this.pressureMin = 0.0,
this.pressureMax = 0.0,
......@@ -111,6 +115,12 @@ class PointerData {
/// Unique identifier for the pointing device, reused across interactions.
final int device;
/// Unique identifier for the pointer.
///
/// This field changes for each new pointer down event. Framework uses this
/// identifier to determine hit test result.
final int pointerIdentifier;
/// X coordinate of the position of the pointer, in physical pixels in the
/// global coordinate space.
final double physicalX;
......@@ -119,6 +129,12 @@ class PointerData {
/// global coordinate space.
final double physicalY;
/// The distance of pointer movement on X coordinate in physical pixels.
final double physicalDeltaX;
/// The distance of pointer movement on Y coordinate in physical pixels.
final double physicalDeltaY;
/// Bit field using the *Button constants (primaryMouseButton,
/// secondaryStylusButton, etc). For example, if this has the value 6 and the
/// [kind] is [PointerDeviceKind.invertedStylus], then this indicates an
......@@ -130,6 +146,14 @@ class PointerData {
/// implemented.)
final bool obscured;
/// Set if this pointer data was synthesized by pointer data packet converter.
/// pointer data packet converter will synthesize additional pointer datas if
/// the input sequence of pointer data is illegal.
///
/// For example, a down pointer data will be synthesized if the converter receives
/// a move pointer data while the pointer is not previously down.
final bool synthesized;
/// The pressure of the touch as a number ranging from 0.0, indicating a touch
/// with no discernible pressure, to 1.0, indicating a touch with "normal"
/// pressure, and possibly beyond, indicating a stronger touch. For devices
......@@ -242,9 +266,13 @@ class PointerData {
'kind: $kind, '
'signalKind: $signalKind, '
'device: $device, '
'pointerIdentifier: $pointerIdentifier, '
'physicalX: $physicalX, '
'physicalY: $physicalY, '
'physicalDeltaX: $physicalDeltaX, '
'physicalDeltaY: $physicalDeltaY, '
'buttons: $buttons, '
'synthesized: $synthesized, '
'pressure: $pressure, '
'pressureMin: $pressureMin, '
'pressureMax: $pressureMax, '
......
......@@ -11,7 +11,7 @@ void main() {}
void nativeReportTimingsCallback(List<int> timings) native 'NativeReportTimingsCallback';
void nativeOnBeginFrame(int microseconds) native 'NativeOnBeginFrame';
void nativeOnPointerDataPacket() native 'NativeOnPointerDataPacket';
void nativeOnPointerDataPacket(List<int> sequences) native 'NativeOnPointerDataPacket';
@pragma('vm:entry-point')
void reportTimingsMain() {
......@@ -36,7 +36,11 @@ void onBeginFrameMain() {
@pragma('vm:entry-point')
void onPointerDataPacketMain() {
window.onPointerDataPacket = (PointerDataPacket packet) {
nativeOnPointerDataPacket();
List<int> sequence= <int>[];
for (PointerData data in packet.data) {
sequence.add(PointerChange.values.indexOf(data.change));
}
nativeOnPointerDataPacket(sequence);
};
}
......
......@@ -141,6 +141,40 @@ static void TestSimulatedInputEvents(
ASSERT_EQ(events_consumed_at_frame.back(), num_events);
}
void CreateSimulatedPointerData(PointerData& data,
PointerData::Change change,
double dx,
double dy) {
data.time_stamp = 0;
data.change = change;
data.kind = PointerData::DeviceKind::kTouch;
data.signal_kind = PointerData::SignalKind::kNone;
data.device = 0;
data.pointer_identifier = 0;
data.physical_x = dx;
data.physical_y = dy;
data.physical_delta_x = 0.0;
data.physical_delta_y = 0.0;
data.buttons = 0;
data.obscured = 0;
data.synthesized = 0;
data.pressure = 0.0;
data.pressure_min = 0.0;
data.pressure_max = 0.0;
data.distance = 0.0;
data.distance_max = 0.0;
data.size = 0.0;
data.radius_major = 0.0;
data.radius_minor = 0.0;
data.radius_min = 0.0;
data.radius_max = 0.0;
data.orientation = 0.0;
data.tilt = 0.0;
data.platformData = 0;
data.scroll_delta_x = 0.0;
data.scroll_delta_y = 0.0;
}
TEST_F(ShellTest, MissAtMostOneFrameForIrregularInputEvents) {
// We don't use `constexpr int frame_time` here because MSVC doesn't handle
// it well with lambda capture.
......@@ -259,5 +293,125 @@ TEST_F(ShellTest, HandlesActualIphoneXsInputEvents) {
}
}
TEST_F(ShellTest, CanCorrectlyPipePointerPacket) {
// Sets up shell with test fixture.
auto settings = CreateSettingsForFixture();
std::unique_ptr<Shell> shell = CreateShell(settings, true);
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("onPointerDataPacketMain");
// Sets up native handler.
fml::AutoResetWaitableEvent reportLatch;
std::vector<int64_t> result_sequence;
auto nativeOnPointerDataPacket = [&reportLatch, &result_sequence](
Dart_NativeArguments args) {
Dart_Handle exception = nullptr;
result_sequence = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
args, 0, exception);
reportLatch.Signal();
};
// Starts engine.
AddNativeCallback("NativeOnPointerDataPacket",
CREATE_NATIVE_ENTRY(nativeOnPointerDataPacket));
ASSERT_TRUE(configuration.IsValid());
RunEngine(shell.get(), std::move(configuration));
// Starts test.
auto packet = std::make_unique<PointerDataPacket>(6);
PointerData data;
CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0.0, 0.0);
packet->SetPointerData(0, data);
CreateSimulatedPointerData(data, PointerData::Change::kHover, 3.0, 0.0);
packet->SetPointerData(1, data);
CreateSimulatedPointerData(data, PointerData::Change::kDown, 3.0, 0.0);
packet->SetPointerData(2, data);
CreateSimulatedPointerData(data, PointerData::Change::kMove, 3.0, 4.0);
packet->SetPointerData(3, data);
CreateSimulatedPointerData(data, PointerData::Change::kUp, 3.0, 4.0);
packet->SetPointerData(4, data);
CreateSimulatedPointerData(data, PointerData::Change::kRemove, 3.0, 4.0);
packet->SetPointerData(5, data);
ShellTest::DispatchPointerData(shell.get(), std::move(packet));
bool will_draw_new_frame;
ShellTest::VSyncFlush(shell.get(), will_draw_new_frame);
reportLatch.Wait();
size_t expect_length = 6;
ASSERT_EQ(result_sequence.size(), expect_length);
ASSERT_EQ(PointerData::Change(result_sequence[0]), PointerData::Change::kAdd);
ASSERT_EQ(PointerData::Change(result_sequence[1]),
PointerData::Change::kHover);
ASSERT_EQ(PointerData::Change(result_sequence[2]),
PointerData::Change::kDown);
ASSERT_EQ(PointerData::Change(result_sequence[3]),
PointerData::Change::kMove);
ASSERT_EQ(PointerData::Change(result_sequence[4]), PointerData::Change::kUp);
ASSERT_EQ(PointerData::Change(result_sequence[5]),
PointerData::Change::kRemove);
// Cleans up shell.
ASSERT_TRUE(DartVMRef::IsInstanceRunning());
DestroyShell(std::move(shell));
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
}
TEST_F(ShellTest, CanCorrectlySynthesizePointerPacket) {
// Sets up shell with test fixture.
auto settings = CreateSettingsForFixture();
std::unique_ptr<Shell> shell = CreateShell(settings, true);
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("onPointerDataPacketMain");
// Sets up native handler.
fml::AutoResetWaitableEvent reportLatch;
std::vector<int64_t> result_sequence;
auto nativeOnPointerDataPacket = [&reportLatch, &result_sequence](
Dart_NativeArguments args) {
Dart_Handle exception = nullptr;
result_sequence = tonic::DartConverter<std::vector<int64_t>>::FromArguments(
args, 0, exception);
reportLatch.Signal();
};
// Starts engine.
AddNativeCallback("NativeOnPointerDataPacket",
CREATE_NATIVE_ENTRY(nativeOnPointerDataPacket));
ASSERT_TRUE(configuration.IsValid());
RunEngine(shell.get(), std::move(configuration));
// Starts test.
auto packet = std::make_unique<PointerDataPacket>(4);
PointerData data;
CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0.0, 0.0);
packet->SetPointerData(0, data);
CreateSimulatedPointerData(data, PointerData::Change::kDown, 3.0, 0.0);
packet->SetPointerData(1, data);
CreateSimulatedPointerData(data, PointerData::Change::kUp, 3.0, 4.0);
packet->SetPointerData(2, data);
CreateSimulatedPointerData(data, PointerData::Change::kRemove, 3.0, 4.0);
packet->SetPointerData(3, data);
ShellTest::DispatchPointerData(shell.get(), std::move(packet));
bool will_draw_new_frame;
ShellTest::VSyncFlush(shell.get(), will_draw_new_frame);
reportLatch.Wait();
size_t expect_length = 6;
ASSERT_EQ(result_sequence.size(), expect_length);
ASSERT_EQ(PointerData::Change(result_sequence[0]), PointerData::Change::kAdd);
// The pointer data packet converter should synthesize a hover event.
ASSERT_EQ(PointerData::Change(result_sequence[1]),
PointerData::Change::kHover);
ASSERT_EQ(PointerData::Change(result_sequence[2]),
PointerData::Change::kDown);
// The pointer data packet converter should synthesize a move event.
ASSERT_EQ(PointerData::Change(result_sequence[3]),
PointerData::Change::kMove);
ASSERT_EQ(PointerData::Change(result_sequence[4]), PointerData::Change::kUp);
ASSERT_EQ(PointerData::Change(result_sequence[5]),
PointerData::Change::kRemove);
// Cleans up shell.
ASSERT_TRUE(DartVMRef::IsInstanceRunning());
DestroyShell(std::move(shell));
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
}
} // namespace testing
} // namespace flutter
......@@ -38,7 +38,8 @@ void PlatformView::DispatchPlatformMessage(
void PlatformView::DispatchPointerDataPacket(
std::unique_ptr<PointerDataPacket> packet) {
delegate_.OnPlatformViewDispatchPointerDataPacket(std::move(packet));
delegate_.OnPlatformViewDispatchPointerDataPacket(
pointer_data_packet_converter_.Convert(std::move(packet)));
}
void PlatformView::DispatchSemanticsAction(int32_t id,
......
......@@ -15,6 +15,7 @@
#include "flutter/lib/ui/semantics/semantics_node.h"
#include "flutter/lib/ui/window/platform_message.h"
#include "flutter/lib/ui/window/pointer_data_packet.h"
#include "flutter/lib/ui/window/pointer_data_packet_converter.h"
#include "flutter/lib/ui/window/viewport_metrics.h"
#include "flutter/shell/common/pointer_data_dispatcher.h"
#include "flutter/shell/common/surface.h"
......@@ -544,6 +545,7 @@ class PlatformView {
PlatformView::Delegate& delegate_;
const TaskRunners task_runners_;
PointerDataPacketConverter pointer_data_packet_converter_;
SkISize size_;
fml::WeakPtrFactory<PlatformView> weak_factory_;
......
......@@ -173,12 +173,19 @@ void ShellTest::PumpOneFrame(Shell* shell,
}
void ShellTest::DispatchFakePointerData(Shell* shell) {
auto packet = std::make_unique<PointerDataPacket>(1);
DispatchPointerData(shell, std::move(packet));
}
void ShellTest::DispatchPointerData(Shell* shell,
std::unique_ptr<PointerDataPacket> packet) {
fml::AutoResetWaitableEvent latch;
shell->GetTaskRunners().GetPlatformTaskRunner()->PostTask([&latch, shell]() {
auto packet = std::make_unique<PointerDataPacket>(1);
shell->OnPlatformViewDispatchPointerDataPacket(std::move(packet));
latch.Signal();
});
shell->GetTaskRunners().GetPlatformTaskRunner()->PostTask(
[&latch, shell, &packet]() {
// Goes through PlatformView to ensure packet is corrected converted.
shell->GetPlatformView()->DispatchPointerDataPacket(std::move(packet));
latch.Signal();
});
latch.Wait();
}
......
......@@ -62,7 +62,8 @@ class ShellTest : public ThreadTest {
LayerTreeBuilder = {});
static void DispatchFakePointerData(Shell* shell);
static void DispatchPointerData(Shell* shell,
std::unique_ptr<PointerDataPacket> packet);
// Declare |UnreportedTimingsCount|, |GetNeedsReportTimings| and
// |SetNeedsReportTimings| inside |ShellTest| mainly for easier friend class
// declarations as shell unit tests and Shell are in different name spaces.
......
......@@ -66,7 +66,7 @@ public class AndroidTouchProcessor {
}
// Must match the unpacking code in hooks.dart.
private static final int POINTER_DATA_FIELD_COUNT = 24;
private static final int POINTER_DATA_FIELD_COUNT = 28;
private static final int BYTES_PER_FIELD = 8;
// This value must match the value in framework's platform_view.dart.
......@@ -198,8 +198,11 @@ public class AndroidTouchProcessor {
packet.putLong(pointerKind); // kind
packet.putLong(signalKind); // signal_kind
packet.putLong(event.getPointerId(pointerIndex)); // device
packet.putLong(0); // pointer_identifier, will be generated in pointer_data_packet_converter.cc.
packet.putDouble(event.getX(pointerIndex)); // physical_x
packet.putDouble(event.getY(pointerIndex)); // physical_y
packet.putDouble(0.0); // physical_delta_x, will be generated in pointer_data_packet_converter.cc.
packet.putDouble(0.0); // physical_delta_y, will be generated in pointer_data_packet_converter.cc.
long buttons;
if (pointerKind == PointerDeviceKind.MOUSE) {
......@@ -220,6 +223,8 @@ public class AndroidTouchProcessor {
packet.putLong(0); // obscured
packet.putLong(0); // synthesized
packet.putDouble(event.getPressure(pointerIndex)); // pressure
double pressureMin = 0.0;
double pressureMax = 1.0;
......
......@@ -507,10 +507,13 @@ typedef enum UIAccessibilityContrast : NSInteger {
pointer_data.change = flutter::PointerData::Change::kCancel;
pointer_data.kind = flutter::PointerData::DeviceKind::kTouch;
pointer_data.device = device.longLongValue;
pointer_data.pointer_identifier = 0;
// Anything we put here will be arbitrary since there are no touches.
pointer_data.physical_x = 0;
pointer_data.physical_y = 0;
pointer_data.physical_delta_x = 0.0;
pointer_data.physical_delta_y = 0.0;
pointer_data.pressure = 1.0;
pointer_data.pressure_max = 1.0;
......@@ -629,9 +632,16 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
pointer_data.device = reinterpret_cast<int64_t>(touch);
// Pointer will be generated in pointer_data_packet_converter.cc.
pointer_data.pointer_identifier = 0;
pointer_data.physical_x = windowCoordinates.x * scale;
pointer_data.physical_y = windowCoordinates.y * scale;
// Delta will be generated in pointer_data_packet_converter.cc.
pointer_data.physical_delta_x = 0.0;
pointer_data.physical_delta_y = 0.0;
NSNumber* deviceKey = [NSNumber numberWithLongLong:pointer_data.device];
// Track touches that began and not yet stopped so we can flush them
// if the view controller goes away.
......
......@@ -1121,7 +1121,12 @@ FlutterEngineResult FlutterEngineSendPointerEvent(
SAFE_ACCESS(current, phase, FlutterPointerPhase::kCancel));
pointer_data.physical_x = SAFE_ACCESS(current, x, 0.0);
pointer_data.physical_y = SAFE_ACCESS(current, y, 0.0);
// Delta will be generated in pointer_data_packet_converter.cc.
pointer_data.physical_delta_x = 0.0;
pointer_data.physical_delta_y = 0.0;
pointer_data.device = SAFE_ACCESS(current, device, 0);
// Pointer identifier will be generated in pointer_data_packet_converter.cc.
pointer_data.pointer_identifier = 0;
pointer_data.signal_kind = ToPointerDataSignalKind(
SAFE_ACCESS(current, signal_kind, kFlutterPointerSignalKindNone));
pointer_data.scroll_delta_x = SAFE_ACCESS(current, scroll_delta_x, 0.0);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册