Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/glfw/FlutterEmbedderGLFW.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand All @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions examples/glfw_drm/FlutterEmbedderGLFW.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 4 additions & 7 deletions lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -414,12 +414,8 @@ 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.
static const int _kPointerDataFieldCount = 36;

static PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
const int kStride = Int64List.bytesPerElement;
Expand All @@ -430,7 +426,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)],
Expand Down Expand Up @@ -466,6 +462,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);
}
Expand Down
3 changes: 2 additions & 1 deletion lib/ui/pointer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,8 @@ class PointerData {
'panDeltaX: $panDeltaX, '
'panDeltaY: $panDeltaY, '
'scale: $scale, '
'rotation: $rotation'
'rotation: $rotation, '
'viewId: $viewId'
')';
}
}
Expand Down
7 changes: 7 additions & 0 deletions lib/ui/window/pointer_data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

namespace flutter {

// If kPointerDataFieldCount changes, update the corresponding values in:
//
// * platform_dispatcher.dart
// * AndroidTouchProcessor.java
static constexpr int kPointerDataFieldCount = 36;
static constexpr int kBytesPerField = sizeof(int64_t);

static_assert(sizeof(PointerData) == kBytesPerField * kPointerDataFieldCount,
"PointerData has the wrong size");

Expand Down
13 changes: 8 additions & 5 deletions lib/ui/window/pointer_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 unpacking code in platform_dispatcher.dart
// * the packing code in AndroidTouchProcessor.java
struct alignas(8) PointerData {
// Must match the PointerChange enum in pointer.dart.
enum class Change : int64_t {
Expand Down Expand Up @@ -100,6 +102,7 @@ struct alignas(8) PointerData {
double pan_delta_y;
double scale;
double rotation;
int64_t view_id;

void Clear();
};
Expand Down
1 change: 1 addition & 0 deletions lib/ui/window/pointer_data_packet_converter_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,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
Expand Down
3 changes: 2 additions & 1 deletion lib/web_ui/lib/pointer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ class PointerData {
'panDeltaX: $panDeltaX, '
'panDeltaY: $panDeltaY, '
'scale: $scale, '
'rotation: $rotation'
'rotation: $rotation, '
'viewId: $viewId'
')';
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ 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.
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.
Expand All @@ -92,6 +92,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;

Expand Down Expand Up @@ -134,6 +137,8 @@ public boolean onTouchEvent(@NonNull MotionEvent event) {
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);
Expand Down Expand Up @@ -253,6 +258,10 @@ private void addPointerForIndex(
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));
Expand Down Expand Up @@ -411,6 +420,7 @@ private void addPointerForIndex(
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -58,6 +59,11 @@
// 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, assign) int64_t viewId;

@property(nonatomic, readwrite, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI;
@property(nonatomic, assign) BOOL isHomeIndicatorHidden;
@property(nonatomic, assign) BOOL isPresentingViewControllerAnimating;
Expand Down Expand Up @@ -148,6 +154,7 @@ @implementation FlutterViewController {

@synthesize displayingFlutterUI = _displayingFlutterUI;
@synthesize prefersStatusBarHidden = _flutterPrefersStatusBarHidden;
@dynamic viewId;

#pragma mark - Manage and override all designated initializers

Expand Down Expand Up @@ -642,6 +649,12 @@ - (void)installFirstFrameCallback {

#pragma mark - Properties

- (int64_t)viewId {
// 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;
Expand Down Expand Up @@ -928,6 +941,7 @@ - (void)flushOngoingTouches {
pointer_data.change = flutter::PointerData::Change::kCancel;
pointer_data.device = device.longLongValue;
pointer_data.pointer_identifier = 0;
pointer_data.view_id = self.viewId;

// Anything we put here will be arbitrary since there are no touches.
pointer_data.physical_x = 0;
Expand Down Expand Up @@ -1176,6 +1190,8 @@ - (void)dispatchTouches:(NSSet*)touches

pointer_data.device = reinterpret_cast<int64_t>(touch);

pointer_data.view_id = self.viewId;

// Pointer will be generated in pointer_data_packet_converter.cc.
pointer_data.pointer_identifier = 0;

Expand Down Expand Up @@ -2393,6 +2409,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
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.viewId;

if (event.timestamp < _scrollInertiaEventAppKitDeadline) {
// Only send the event if it occured before the expected natural end of gesture momentum.
Expand All @@ -2416,6 +2433,7 @@ - (void)hoverEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4))
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.viewId;

switch (_hoverGestureRecognizer.state) {
case UIGestureRecognizerStateBegan:
Expand Down Expand Up @@ -2454,6 +2472,7 @@ - (void)hoverEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4))
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.viewId;
packet->SetPointerData(/*i=*/1, inertia_cancel);
[_engine.get() dispatchPointerDataPacket:std::move(packet)];
_scrollInertiaEventStartline = DBL_MAX;
Expand All @@ -2477,6 +2496,7 @@ - (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(io
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.viewId;

// The translation reported by UIPanGestureRecognizer is the total translation
// generated by the pan gesture since the gesture began. We need to be able
Expand All @@ -2500,6 +2520,7 @@ - (void)continuousScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(
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.viewId;
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
pointer_data.change = flutter::PointerData::Change::kPanZoomStart;
Expand Down Expand Up @@ -2548,6 +2569,7 @@ - (void)pinchEvent:(UIPinchGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4)
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.viewId;
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
pointer_data.change = flutter::PointerData::Change::kPanZoomStart;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase {
.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) {
Expand Down Expand Up @@ -1049,6 +1050,7 @@ - (void)touchesBeganWithEvent:(NSEvent*)event {
.device = kPointerPanZoomDeviceId,
.signal_kind = kFlutterPointerSignalKindScrollInertiaCancel,
.device_kind = kFlutterPointerDeviceKindTrackpad,
.view_id = static_cast<FlutterViewId>(_viewId),
};

[_engine sendPointerEvent:flutterEvent];
Expand Down
3 changes: 2 additions & 1 deletion shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -2326,6 +2326,7 @@ 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, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use kFlutterImplicitViewId here? (FYI in the future the embedder will control the implicit view ID, so this will need to updated later)

Suggested change
pointer_data.view_id = SAFE_ACCESS(current, view_id, 0);
pointer_data.view_id = SAFE_ACCESS(current, view_id, kFlutterImplicitViewId);

Copy link
Contributor Author

@dkwingsmt dkwingsmt Dec 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm kind of hesitant here. The benefit of using 0 as the default value is that we can flush the struct to be all 0s and be sure that it doesn't break even when new fields are added (in fact I'm planning to propose a macro that initiate an embedder API struct that with flushed bytes). But I also agree that the default value should likely be the implicit view. In this way, we should probably NOT allow embedders to customize the implicit view ID to be non-0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benefit of using 0 as the default value is that we can flush the struct to be all 0s and be sure that it doesn't break even when new fields are added

I don't follow. The embedder API - including the new multi-view APIs - is designed for forwards/backwards ABI compatibility. The struct_size member lets us determine how to handle the view_id without needing to fallback to 0.

In this way, we should probably NOT allow embedders to customize the implicit view ID to be non-0.

I don't understand this either, can you expand on this? The embedder API design doc had a section that allows the embedder to customize the implicit view ID: https://docs.google.com/document/d/1kegGJ235y1778-vzyyv90MgSHmYQcROTupMuLtEDBq0/edit#heading=h.1enoc2wkpbna

Copy link
Contributor Author

@dkwingsmt dkwingsmt Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The struct_size member lets us determine how to handle the view_id without needing to fallback to 0.

Let me rephrase the problem (because I realized Flutter is currently doing it correctly. My point is still valid though).

I'm talking about recompiling the code that creates such a struct. For example, check out the following code from the GLFW example (simplified):

void GLFWcursorPositionCallbackAtPhase(GLFWwindow* window,
                                       FlutterPointerPhase phase,
                                       double x,
                                       double y) {
  FlutterPointerEvent event = {};
  event.struct_size = sizeof(event);
  event.phase = phase;
  event.x = x * g_pixelRatio;
  event.y = y * g_pixelRatio;
  FlutterEngineSendPointerEvent(&event, 1);
}

The line FlutterPointerEvent event = {}; zeros out the struct. This is important, because old apps do not assign event.view_id, and without zeroing out properties, once Flutter adds this new field view_id and this custom embedder is recompiled without editing, every FlutterPointerEvent Flutter received would have an uninitialized view_id, which would be a random value. And because FlutterPointerEvent is a C struct, this is the only way to initialize future properties as C structs don't accept explicit default values. (We have encountered similar issues, which was fixed by this PR.)

The struct_size guard is used for compatibility of compiled libraries.

Therefore, when we add new fields to a struct, we're assuming that all old code will send a zero value for this field, which should be the default value. And therefore the default value, the implicit view ID, must be 0.

I understand that the doc said that the implicit view ID should be customizable, and I agreed to it. But this is a new problem I just realized.

Copy link
Member

@loic-sharma loic-sharma Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's two cases that are handled differently:

  1. Embedders in the engine repo - These automatically use the latest embedder API whenever the API changes.
  2. Custom embedders outside the engine repo - These must update their embedder API manually:
    1. Generate a flutter_embedder.h file
    2. Copy the flutter_embedder.h file into the embedder's project
    3. Update the embedder's logic to use the latest embedder API
    4. Recompile the embedder

See Chinmay's minimal embedder example.

The struct_size guard allows us to detect whether a custom embedder has updated their flutter_embedder.h file. If a custom embedder sends a default value for the view ID, it means the custom embedder updated their flutter_embedder.h file without updating their embedder logic - this would be an error by the custom embedder.

Copy link
Member

@loic-sharma loic-sharma Dec 13, 2023

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. In that case we don't have to be limited to use 0 as the default value. And we don't have to use disable_implicit_view, since we can make it enable_implicit_view and make the default value (by struct_size) true.

Copy link
Member

@loic-sharma loic-sharma Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least I don't think it's not worth the "customizability" that I doubt if many people would use.

It feels a bit late to change this aspect of the design. This design has been out for over a month and has been reviewed by several stakeholders. We can still pivot if this is truly not worth the effort, I'm not convinced that's the case (and it takes effort to review design updates!)

Copy link
Member

@loic-sharma loic-sharma Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we don't have to use disable_implicit_view, since we can make it enable_implicit_view and make the default value (by struct_size) true.

We discussed this with Chinmay but decided to stick to disable_implicit_view regardless. The poor name does minimize the impact to a custom embedder that is updated incorrectly (assuming they zero initialize their args)

Copy link
Contributor Author

@dkwingsmt dkwingsmt Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a bit late to change this aspect of the design. This design has been out for over a month and has been reviewed by several stakeholders. We can still pivot if this is truly not worth the effort, I'm not convinced that's the case (and it takes effort to review design updates!)

It's never too late to make improvement as long as it has not been submitted into embedder.h. Not to mention the fact that the implicit view ID and its customization are probably among last features of this project to add. If we worry about the minimizing the impact to a custom embedder that is updated incorrectly for disable_implicit_view, then we probably want to make it similar for the view ID.

For this particular change though, I'll change it to kFlutterImplicitViewId since I agree that the default value should be the implicit view ID, no matter what the value is.

packet->SetPointerData(i, pointer_data);
current = reinterpret_cast<const FlutterPointerEvent*>(
reinterpret_cast<const uint8_t*>(current) + current->struct_size);
Expand Down
11 changes: 11 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,15 @@ typedef enum {

typedef struct _FlutterEngine* FLUTTER_API_SYMBOL(FlutterEngine);

/// Identifier for views.
///
/// How view IDs are generated is decided by the embedding. The engine does not
/// assume any specifics about the generation algorithm, including whether it
/// utilizes the entire range of int64_t or permits the use of zero or negative
/// IDs. The only requirement is that at any given moment, no two views share
/// the same ID.
typedef int64_t FlutterViewId;

typedef struct {
/// horizontal scale factor
double scaleX;
Expand Down Expand Up @@ -961,6 +970,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 {
Expand Down
15 changes: 14 additions & 1 deletion shell/platform/embedder/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1303,14 +1303,27 @@ 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());
}
};

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',
Expand Down
Loading