Multi-view pointer event (flutter/engine#46213)
This PR adds a new field `view_id` to embedder API's `FlutterPointerEvent`, allowing platforms to specify the source view of pointer events. https://github.com/flutter/flutter/issues/112205 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
static double g_pixelRatio = 1.0;
|
||||
static const size_t kInitialWindowWidth = 800;
|
||||
static const size_t kInitialWindowHeight = 600;
|
||||
static constexpr FlutterViewId kImplicitViewId = 0;
|
||||
|
||||
static_assert(FLUTTER_ENGINE_VERSION == 1,
|
||||
"This Flutter Embedder was authored against the stable Flutter "
|
||||
@@ -33,6 +34,9 @@ void GLFWcursorPositionCallbackAtPhase(GLFWwindow* window,
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count();
|
||||
// This example only supports a single window, therefore we assume the pointer
|
||||
// event occurred in the only view, the implicit view.
|
||||
event.view_id = kImplicitViewId;
|
||||
FlutterEngineSendPointerEvent(
|
||||
reinterpret_cast<FlutterEngine>(glfwGetWindowUserPointer(window)), &event,
|
||||
1);
|
||||
|
||||
@@ -26,6 +26,7 @@ static const size_t kInitialWindowHeight = 600;
|
||||
// Maximum damage history - for triple buffering we need to store damage for
|
||||
// last two frames; Some Android devices (Pixel 4) use quad buffering.
|
||||
static const int kMaxHistorySize = 10;
|
||||
static constexpr FlutterViewId kImplicitViewId = 0;
|
||||
|
||||
// Keeps track of the most recent frame damages so that existing damage can
|
||||
// be easily computed.
|
||||
@@ -56,6 +57,9 @@ void GLFWcursorPositionCallbackAtPhase(GLFWwindow* window,
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count();
|
||||
// This example only supports a single window, therefore we assume the pointer
|
||||
// event occurred in the only view, the implicit view.
|
||||
event.view_id = kImplicitViewId;
|
||||
FlutterEngineSendPointerEvent(
|
||||
reinterpret_cast<FlutterEngine>(glfwGetWindowUserPointer(window)), &event,
|
||||
1);
|
||||
|
||||
@@ -414,12 +414,9 @@ class PlatformDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
// If this value changes, update the encoding code in the following files:
|
||||
//
|
||||
// * pointer_data.cc
|
||||
// * pointer.dart
|
||||
// * AndroidTouchProcessor.java
|
||||
static const int _kPointerDataFieldCount = 35;
|
||||
// This value must match kPointerDataFieldCount in pointer_data.cc. (The
|
||||
// pointer_data.cc also lists other locations that must be kept consistent.)
|
||||
static const int _kPointerDataFieldCount = 36;
|
||||
|
||||
static PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
|
||||
const int kStride = Int64List.bytesPerElement;
|
||||
@@ -430,7 +427,7 @@ class PlatformDispatcher {
|
||||
for (int i = 0; i < length; ++i) {
|
||||
int offset = i * _kPointerDataFieldCount;
|
||||
data.add(PointerData(
|
||||
// TODO(goderbauer): Wire up viewId.
|
||||
// The unpacking code must match the struct in pointer_data.h.
|
||||
embedderId: packet.getInt64(kStride * offset++, _kFakeHostEndian),
|
||||
timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)),
|
||||
change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
|
||||
@@ -466,6 +463,7 @@ class PlatformDispatcher {
|
||||
panDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
|
||||
scale: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
|
||||
rotation: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
|
||||
viewId: packet.getInt64(kStride * offset++, _kFakeHostEndian),
|
||||
));
|
||||
assert(offset == (i + 1) * _kPointerDataFieldCount);
|
||||
}
|
||||
|
||||
@@ -419,7 +419,8 @@ class PointerData {
|
||||
'panDeltaX: $panDeltaX, '
|
||||
'panDeltaY: $panDeltaY, '
|
||||
'scale: $scale, '
|
||||
'rotation: $rotation'
|
||||
'rotation: $rotation, '
|
||||
'viewId: $viewId'
|
||||
')';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// The number of fields of PointerData.
|
||||
//
|
||||
// If kPointerDataFieldCount changes, update the corresponding values to:
|
||||
//
|
||||
// * _kPointerDataFieldCount in platform_dispatcher.dart
|
||||
// * POINTER_DATA_FIELD_COUNT in AndroidTouchProcessor.java
|
||||
//
|
||||
// (This is a centralized list of all locations that should be kept up-to-date.)
|
||||
static constexpr int kPointerDataFieldCount = 36;
|
||||
static constexpr int kBytesPerField = sizeof(int64_t);
|
||||
|
||||
static_assert(sizeof(PointerData) == kBytesPerField * kPointerDataFieldCount,
|
||||
"PointerData has the wrong size");
|
||||
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// If this value changes, update the pointer data unpacking code in
|
||||
// platform_dispatcher.dart.
|
||||
static constexpr int kPointerDataFieldCount = 35;
|
||||
static constexpr int kBytesPerField = sizeof(int64_t);
|
||||
// Must match the button constants in events.dart.
|
||||
enum PointerButtonMouse : int64_t {
|
||||
kPointerButtonMousePrimary = 1 << 0,
|
||||
@@ -32,7 +28,13 @@ enum PointerButtonStylus : int64_t {
|
||||
kPointerButtonStylusSecondary = 1 << 2,
|
||||
};
|
||||
|
||||
// This structure is unpacked by hooks.dart.
|
||||
// This structure is unpacked by platform_dispatcher.dart.
|
||||
//
|
||||
// If this struct changes, update:
|
||||
// * kPointerDataFieldCount in pointer_data.cc. (The pointer_data.cc also
|
||||
// lists out other locations that must be kept consistent.)
|
||||
// * The functions to create simulated data in
|
||||
// pointer_data_packet_converter_unittests.cc.
|
||||
struct alignas(8) PointerData {
|
||||
// Must match the PointerChange enum in pointer.dart.
|
||||
enum class Change : int64_t {
|
||||
@@ -100,6 +102,7 @@ struct alignas(8) PointerData {
|
||||
double pan_delta_y;
|
||||
double scale;
|
||||
double rotation;
|
||||
int64_t view_id;
|
||||
|
||||
void Clear();
|
||||
};
|
||||
|
||||
@@ -45,6 +45,7 @@ void CreateSimulatedPointerData(PointerData& data, // NOLINT
|
||||
data.platformData = 0;
|
||||
data.scroll_delta_x = 0.0;
|
||||
data.scroll_delta_y = 0.0;
|
||||
data.view_id = 0;
|
||||
}
|
||||
|
||||
void CreateSimulatedMousePointerData(PointerData& data, // NOLINT
|
||||
@@ -84,6 +85,7 @@ void CreateSimulatedMousePointerData(PointerData& data, // NOLINT
|
||||
data.platformData = 0;
|
||||
data.scroll_delta_x = scroll_delta_x;
|
||||
data.scroll_delta_y = scroll_delta_y;
|
||||
data.view_id = 0;
|
||||
}
|
||||
|
||||
void CreateSimulatedTrackpadGestureData(PointerData& data, // NOLINT
|
||||
@@ -129,6 +131,7 @@ void CreateSimulatedTrackpadGestureData(PointerData& data, // NOLINT
|
||||
data.pan_delta_y = 0.0;
|
||||
data.scale = scale;
|
||||
data.rotation = rotation;
|
||||
data.view_id = 0;
|
||||
}
|
||||
|
||||
void UnpackPointerPacket(std::vector<PointerData>& output, // NOLINT
|
||||
@@ -713,5 +716,25 @@ TEST(PointerDataPacketConverterTest, CanConvertTrackpadGesture) {
|
||||
ASSERT_EQ(result[3].synthesized, 0);
|
||||
}
|
||||
|
||||
TEST(PointerDataPacketConverterTest, CanConvertViewId) {
|
||||
PointerDataPacketConverter converter;
|
||||
auto packet = std::make_unique<PointerDataPacket>(2);
|
||||
PointerData data;
|
||||
CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0);
|
||||
data.view_id = 100;
|
||||
packet->SetPointerData(0, data);
|
||||
CreateSimulatedPointerData(data, PointerData::Change::kHover, 0, 1.0, 0.0, 0);
|
||||
data.view_id = 200;
|
||||
packet->SetPointerData(1, data);
|
||||
auto converted_packet = converter.Convert(std::move(packet));
|
||||
|
||||
std::vector<PointerData> result;
|
||||
UnpackPointerPacket(result, std::move(converted_packet));
|
||||
|
||||
ASSERT_EQ(result.size(), (size_t)2);
|
||||
ASSERT_EQ(result[0].view_id, 100);
|
||||
ASSERT_EQ(result[1].view_id, 200);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@@ -147,7 +147,8 @@ class PointerData {
|
||||
'panDeltaX: $panDeltaX, '
|
||||
'panDeltaY: $panDeltaY, '
|
||||
'scale: $scale, '
|
||||
'rotation: $rotation'
|
||||
'rotation: $rotation, '
|
||||
'viewId: $viewId'
|
||||
')';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +80,9 @@ public class AndroidTouchProcessor {
|
||||
int UNKNOWN = 4;
|
||||
}
|
||||
|
||||
// Must match the unpacking code in hooks.dart.
|
||||
private static final int POINTER_DATA_FIELD_COUNT = 35;
|
||||
// This value must match kPointerDataFieldCount in pointer_data.cc. (The
|
||||
// pointer_data.cc also lists other locations that must be kept consistent.)
|
||||
private static final int POINTER_DATA_FIELD_COUNT = 36;
|
||||
@VisibleForTesting static final int BYTES_PER_FIELD = 8;
|
||||
|
||||
// Default if context is null, chosen to ensure reasonable speed scrolling.
|
||||
@@ -92,6 +93,9 @@ public class AndroidTouchProcessor {
|
||||
// This flag indicates whether the original Android pointer events were batched together.
|
||||
private static final int POINTER_DATA_FLAG_BATCHED = 1;
|
||||
|
||||
// The view ID for the only view in a single-view Flutter app.
|
||||
private static final int IMPLICIT_VIEW_ID = 0;
|
||||
|
||||
@NonNull private final FlutterRenderer renderer;
|
||||
@NonNull private final MotionEventTracker motionEventTracker;
|
||||
|
||||
@@ -134,6 +138,8 @@ public class AndroidTouchProcessor {
|
||||
public boolean onTouchEvent(@NonNull MotionEvent event, @NonNull Matrix transformMatrix) {
|
||||
int pointerCount = event.getPointerCount();
|
||||
|
||||
// The following packing code must match the struct in pointer_data.h.
|
||||
|
||||
// Prepare a data packet of the appropriate size and order.
|
||||
ByteBuffer packet =
|
||||
ByteBuffer.allocateDirect(pointerCount * POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD);
|
||||
@@ -253,6 +259,10 @@ public class AndroidTouchProcessor {
|
||||
if (pointerChange == -1) {
|
||||
return;
|
||||
}
|
||||
// TODO(dkwingsmt): Use the correct source view ID once Android supports
|
||||
// multiple views.
|
||||
// https://github.com/flutter/flutter/issues/134405
|
||||
final int viewId = IMPLICIT_VIEW_ID;
|
||||
final int pointerId = event.getPointerId(pointerIndex);
|
||||
|
||||
int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex));
|
||||
@@ -411,6 +421,7 @@ public class AndroidTouchProcessor {
|
||||
packet.putDouble(0.0); // pan_delta_y
|
||||
packet.putDouble(1.0); // scale
|
||||
packet.putDouble(0.0); // rotation
|
||||
packet.putLong(viewId); // view_id
|
||||
|
||||
if (isTrackpadPan && (panZoomType == PointerChange.PAN_ZOOM_END)) {
|
||||
ongoingPans.remove(pointerId);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import <os/log.h>
|
||||
#include <memory>
|
||||
|
||||
#include "flutter/common/constants.h"
|
||||
#include "flutter/fml/memory/weak_ptr.h"
|
||||
#include "flutter/fml/message_loop.h"
|
||||
#include "flutter/fml/platform/darwin/platform_version.h"
|
||||
@@ -58,6 +59,11 @@ typedef struct MouseState {
|
||||
// change. Unfortunately unless you have Werror turned on, incompatible pointers as arguments are
|
||||
// just a warning.
|
||||
@interface FlutterViewController () <FlutterBinaryMessenger, UIScrollViewDelegate>
|
||||
// TODO(dkwingsmt): Make the view ID property public once the iOS shell
|
||||
// supports multiple views.
|
||||
// https://github.com/flutter/flutter/issues/138168
|
||||
@property(nonatomic, readonly) int64_t viewIdentifier;
|
||||
|
||||
@property(nonatomic, readwrite, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI;
|
||||
@property(nonatomic, assign) BOOL isHomeIndicatorHidden;
|
||||
@property(nonatomic, assign) BOOL isPresentingViewControllerAnimating;
|
||||
@@ -148,6 +154,7 @@ typedef struct MouseState {
|
||||
|
||||
@synthesize displayingFlutterUI = _displayingFlutterUI;
|
||||
@synthesize prefersStatusBarHidden = _flutterPrefersStatusBarHidden;
|
||||
@dynamic viewIdentifier;
|
||||
|
||||
#pragma mark - Manage and override all designated initializers
|
||||
|
||||
@@ -642,6 +649,12 @@ static void SendFakeTouchEvent(UIScreen* screen,
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (int64_t)viewIdentifier {
|
||||
// TODO(dkwingsmt): Fill the view ID property with the correct value once the
|
||||
// iOS shell supports multiple views.
|
||||
return flutter::kFlutterImplicitViewId;
|
||||
}
|
||||
|
||||
- (UIView*)splashScreenView {
|
||||
if (!_splashScreenView) {
|
||||
return nil;
|
||||
@@ -928,6 +941,7 @@ static void SendFakeTouchEvent(UIScreen* screen,
|
||||
pointer_data.change = flutter::PointerData::Change::kCancel;
|
||||
pointer_data.device = device.longLongValue;
|
||||
pointer_data.pointer_identifier = 0;
|
||||
pointer_data.view_id = self.viewIdentifier;
|
||||
|
||||
// Anything we put here will be arbitrary since there are no touches.
|
||||
pointer_data.physical_x = 0;
|
||||
@@ -1176,6 +1190,8 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
|
||||
pointer_data.device = reinterpret_cast<int64_t>(touch);
|
||||
|
||||
pointer_data.view_id = self.viewIdentifier;
|
||||
|
||||
// Pointer will be generated in pointer_data_packet_converter.cc.
|
||||
pointer_data.pointer_identifier = 0;
|
||||
|
||||
@@ -2393,6 +2409,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
pointer_data.device = reinterpret_cast<int64_t>(_continuousScrollingPanGestureRecognizer);
|
||||
pointer_data.kind = flutter::PointerData::DeviceKind::kTrackpad;
|
||||
pointer_data.signal_kind = flutter::PointerData::SignalKind::kScrollInertiaCancel;
|
||||
pointer_data.view_id = self.viewIdentifier;
|
||||
|
||||
if (event.timestamp < _scrollInertiaEventAppKitDeadline) {
|
||||
// Only send the event if it occured before the expected natural end of gesture momentum.
|
||||
@@ -2416,6 +2433,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
flutter::PointerData pointer_data = [self generatePointerDataAtLastMouseLocation];
|
||||
pointer_data.device = reinterpret_cast<int64_t>(recognizer);
|
||||
pointer_data.kind = flutter::PointerData::DeviceKind::kMouse;
|
||||
pointer_data.view_id = self.viewIdentifier;
|
||||
|
||||
switch (_hoverGestureRecognizer.state) {
|
||||
case UIGestureRecognizerStateBegan:
|
||||
@@ -2454,6 +2472,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
inertia_cancel.device = reinterpret_cast<int64_t>(_continuousScrollingPanGestureRecognizer);
|
||||
inertia_cancel.kind = flutter::PointerData::DeviceKind::kTrackpad;
|
||||
inertia_cancel.signal_kind = flutter::PointerData::SignalKind::kScrollInertiaCancel;
|
||||
inertia_cancel.view_id = self.viewIdentifier;
|
||||
packet->SetPointerData(/*i=*/1, inertia_cancel);
|
||||
[_engine.get() dispatchPointerDataPacket:std::move(packet)];
|
||||
_scrollInertiaEventStartline = DBL_MAX;
|
||||
@@ -2477,6 +2496,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
pointer_data.signal_kind = flutter::PointerData::SignalKind::kScroll;
|
||||
pointer_data.scroll_delta_x = (translation.x - _mouseState.last_translation.x);
|
||||
pointer_data.scroll_delta_y = -(translation.y - _mouseState.last_translation.y);
|
||||
pointer_data.view_id = self.viewIdentifier;
|
||||
|
||||
// The translation reported by UIPanGestureRecognizer is the total translation
|
||||
// generated by the pan gesture since the gesture began. We need to be able
|
||||
@@ -2500,6 +2520,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
flutter::PointerData pointer_data = [self generatePointerDataAtLastMouseLocation];
|
||||
pointer_data.device = reinterpret_cast<int64_t>(recognizer);
|
||||
pointer_data.kind = flutter::PointerData::DeviceKind::kTrackpad;
|
||||
pointer_data.view_id = self.viewIdentifier;
|
||||
switch (recognizer.state) {
|
||||
case UIGestureRecognizerStateBegan:
|
||||
pointer_data.change = flutter::PointerData::Change::kPanZoomStart;
|
||||
@@ -2548,6 +2569,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
flutter::PointerData pointer_data = [self generatePointerDataAtLastMouseLocation];
|
||||
pointer_data.device = reinterpret_cast<int64_t>(recognizer);
|
||||
pointer_data.kind = flutter::PointerData::DeviceKind::kTrackpad;
|
||||
pointer_data.view_id = self.viewIdentifier;
|
||||
switch (recognizer.state) {
|
||||
case UIGestureRecognizerStateBegan:
|
||||
pointer_data.change = flutter::PointerData::Change::kPanZoomStart;
|
||||
|
||||
@@ -743,6 +743,7 @@ static void CommonInit(FlutterViewController* controller, FlutterEngine* engine)
|
||||
.device_kind = deviceKind,
|
||||
// If a click triggered a synthesized kAdd, don't pass the buttons in that event.
|
||||
.buttons = phase == kAdd ? 0 : _mouseState.buttons,
|
||||
.view_id = static_cast<FlutterViewId>(_viewId),
|
||||
};
|
||||
|
||||
if (phase == kPanZoomUpdate) {
|
||||
@@ -1049,6 +1050,7 @@ static void CommonInit(FlutterViewController* controller, FlutterEngine* engine)
|
||||
.device = kPointerPanZoomDeviceId,
|
||||
.signal_kind = kFlutterPointerSignalKindScrollInertiaCancel,
|
||||
.device_kind = kFlutterPointerDeviceKindTrackpad,
|
||||
.view_id = static_cast<FlutterViewId>(_viewId),
|
||||
};
|
||||
|
||||
[_engine sendPointerEvent:flutterEvent];
|
||||
|
||||
@@ -102,7 +102,7 @@ extern const intptr_t kPlatformStrongDillSize;
|
||||
const int32_t kFlutterSemanticsNodeIdBatchEnd = -1;
|
||||
const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1;
|
||||
|
||||
static constexpr int64_t kFlutterImplicitViewId = 0;
|
||||
static constexpr FlutterViewId kFlutterImplicitViewId = 0;
|
||||
|
||||
// A message channel to send platform-independent FlutterKeyData to the
|
||||
// framework.
|
||||
@@ -2326,6 +2326,8 @@ FlutterEngineResult FlutterEngineSendPointerEvent(
|
||||
pointer_data.pan_delta_y = 0.0;
|
||||
pointer_data.scale = SAFE_ACCESS(current, scale, 0.0);
|
||||
pointer_data.rotation = SAFE_ACCESS(current, rotation, 0.0);
|
||||
pointer_data.view_id =
|
||||
SAFE_ACCESS(current, view_id, kFlutterImplicitViewId);
|
||||
packet->SetPointerData(i, pointer_data);
|
||||
current = reinterpret_cast<const FlutterPointerEvent*>(
|
||||
reinterpret_cast<const uint8_t*>(current) + current->struct_size);
|
||||
|
||||
@@ -266,6 +266,12 @@ typedef enum {
|
||||
|
||||
typedef struct _FlutterEngine* FLUTTER_API_SYMBOL(FlutterEngine);
|
||||
|
||||
/// Unique identifier for views.
|
||||
///
|
||||
/// View IDs are generated by the embedder and are
|
||||
/// opaque to the engine; the engine does not interpret view IDs in any way.
|
||||
typedef int64_t FlutterViewId;
|
||||
|
||||
typedef struct {
|
||||
/// horizontal scale factor
|
||||
double scaleX;
|
||||
@@ -961,6 +967,8 @@ typedef struct {
|
||||
double scale;
|
||||
/// The rotation of the pan/zoom in radians, where 0.0 is the initial angle.
|
||||
double rotation;
|
||||
/// The identifier of the view that received the pointer event.
|
||||
FlutterViewId view_id;
|
||||
} FlutterPointerEvent;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -1308,7 +1308,7 @@ void pointer_data_packet() {
|
||||
(PointerDataPacket packet) {
|
||||
signalNativeCount(packet.data.length);
|
||||
|
||||
for (final pointerData in packet.data) {
|
||||
for (final PointerData pointerData in packet.data) {
|
||||
signalNativeMessage(pointerData.toString());
|
||||
}
|
||||
};
|
||||
@@ -1316,6 +1316,19 @@ void pointer_data_packet() {
|
||||
signalNativeTest();
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void pointer_data_packet_view_id() {
|
||||
PlatformDispatcher.instance.onPointerDataPacket = (PointerDataPacket packet) {
|
||||
assert(packet.data.length == 1);
|
||||
|
||||
for (final PointerData pointerData in packet.data) {
|
||||
signalNativeMessage('ViewID: ${pointerData.viewId}');
|
||||
}
|
||||
};
|
||||
|
||||
signalNativeTest();
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> channel_listener_response() async {
|
||||
channelBuffers.setListener('test/listen',
|
||||
|
||||
@@ -2666,6 +2666,48 @@ TEST_F(EmbedderTest, CanSendPointer) {
|
||||
message_latch.Wait();
|
||||
}
|
||||
|
||||
/// Send a pointer event to Dart and wait until the Dart code echos with the
|
||||
/// view ID.
|
||||
TEST_F(EmbedderTest, CanSendPointerWithViewId) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetSoftwareRendererConfig();
|
||||
builder.SetDartEntrypoint("pointer_data_packet_view_id");
|
||||
|
||||
fml::AutoResetWaitableEvent ready_latch, count_latch, message_latch;
|
||||
context.AddNativeCallback(
|
||||
"SignalNativeTest",
|
||||
CREATE_NATIVE_ENTRY(
|
||||
[&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
|
||||
context.AddNativeCallback(
|
||||
"SignalNativeMessage",
|
||||
CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
|
||||
auto message = tonic::DartConverter<std::string>::FromDart(
|
||||
Dart_GetNativeArgument(args, 0));
|
||||
ASSERT_EQ("ViewID: 2", message);
|
||||
message_latch.Signal();
|
||||
}));
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
|
||||
ready_latch.Wait();
|
||||
|
||||
FlutterPointerEvent pointer_event = {};
|
||||
pointer_event.struct_size = sizeof(FlutterPointerEvent);
|
||||
pointer_event.phase = FlutterPointerPhase::kAdd;
|
||||
pointer_event.x = 123;
|
||||
pointer_event.y = 456;
|
||||
pointer_event.timestamp = static_cast<size_t>(1234567890);
|
||||
pointer_event.view_id = 2;
|
||||
|
||||
FlutterEngineResult result =
|
||||
FlutterEngineSendPointerEvent(engine.get(), &pointer_event, 1);
|
||||
ASSERT_EQ(result, kSuccess);
|
||||
|
||||
message_latch.Wait();
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, RegisterChannelListener) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "flutter/common/constants.h"
|
||||
#include "flutter/shell/platform/common/client_wrapper/include/flutter/plugin_registrar.h"
|
||||
#include "flutter/shell/platform/common/incoming_message_dispatcher.h"
|
||||
#include "flutter/shell/platform/common/path_utils.h"
|
||||
@@ -391,6 +392,9 @@ static void SendPointerEventWithData(GLFWwindow* window,
|
||||
event.y *= pixels_per_coordinate;
|
||||
event.scroll_delta_x *= pixels_per_coordinate;
|
||||
event.scroll_delta_y *= pixels_per_coordinate;
|
||||
// The GLFW embedder doesn't support multiple views. We assume all pointer
|
||||
// events come from the only view, the implicit view.
|
||||
event.view_id = flutter::kFlutterImplicitViewId;
|
||||
|
||||
FlutterEngineSendPointerEvent(controller->engine->flutter_engine, &event, 1);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/common/constants.h"
|
||||
#include "flutter/shell/platform/common/app_lifecycle_state.h"
|
||||
#include "flutter/shell/platform/common/engine_switches.h"
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
@@ -802,6 +803,10 @@ void fl_engine_send_mouse_pointer_event(FlEngine* self,
|
||||
fl_event.device_kind = kFlutterPointerDeviceKindMouse;
|
||||
fl_event.buttons = buttons;
|
||||
fl_event.device = kMousePointerDeviceId;
|
||||
// TODO(dkwingsmt): Assign the correct view ID once the Linux embedder
|
||||
// supports multiple views.
|
||||
// https://github.com/flutter/flutter/issues/138178
|
||||
fl_event.view_id = flutter::kFlutterImplicitViewId;
|
||||
self->embedder_api.SendPointerEvent(self->engine, &fl_event, 1);
|
||||
}
|
||||
|
||||
@@ -832,6 +837,10 @@ void fl_engine_send_pointer_pan_zoom_event(FlEngine* self,
|
||||
fl_event.rotation = rotation;
|
||||
fl_event.device = kPointerPanZoomDeviceId;
|
||||
fl_event.device_kind = kFlutterPointerDeviceKindTrackpad;
|
||||
// TODO(dkwingsmt): Assign the correct view ID once the Linux embedder
|
||||
// supports multiple views.
|
||||
// https://github.com/flutter/flutter/issues/138178
|
||||
fl_event.view_id = flutter::kFlutterImplicitViewId;
|
||||
self->embedder_api.SendPointerEvent(self->engine, &fl_event, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "flutter/common/constants.h"
|
||||
#include "flutter/fml/platform/win/wstring_conversion.h"
|
||||
#include "flutter/shell/platform/common/accessibility_bridge.h"
|
||||
#include "flutter/shell/platform/windows/keyboard_key_channel_handler.h"
|
||||
@@ -547,6 +548,10 @@ void FlutterWindowsView::SendPointerEventWithData(
|
||||
event.device_kind = state->device_kind;
|
||||
event.device = state->pointer_id;
|
||||
event.buttons = state->buttons;
|
||||
// TODO(dkwingsmt): Use the correct view ID for pointer events once the
|
||||
// Windows embedder supports multiple views.
|
||||
// https://github.com/flutter/flutter/issues/138179
|
||||
event.view_id = flutter::kFlutterImplicitViewId;
|
||||
|
||||
// Set metadata that's always the same regardless of the event.
|
||||
event.struct_size = sizeof(event);
|
||||
|
||||
Reference in New Issue
Block a user