Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 72ca2e1

Browse files
authored
Multi-view View Metrics (#46174)
This PR adds `FlutterWindowMetricsEvent.viewId` to the embedder API. This PR only tests the ability to send metrics event for the implicit view. Once multiple views can be added via embedder API, we should test the ability to send different IDs. Part of flutter/flutter#144806 Part of flutter/flutter#142845 ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 6710d10 commit 72ca2e1

File tree

11 files changed

+161
-13
lines changed

11 files changed

+161
-13
lines changed

examples/glfw/FlutterEmbedderGLFW.cc

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ void GLFWwindowSizeCallback(GLFWwindow* window, int width, int height) {
8181
event.width = width * g_pixelRatio;
8282
event.height = height * g_pixelRatio;
8383
event.pixel_ratio = g_pixelRatio;
84+
// This example only supports a single window, therefore we assume the event
85+
// occurred in the only view, the implicit view.
86+
event.view_id = kImplicitViewId;
8487
FlutterEngineSendWindowMetricsEvent(
8588
reinterpret_cast<FlutterEngine>(glfwGetWindowUserPointer(window)),
8689
&event);

examples/glfw_drm/FlutterEmbedderGLFW.cc

+3
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ void GLFWwindowSizeCallback(GLFWwindow* window, int width, int height) {
104104
event.width = width * g_pixelRatio;
105105
event.height = height * g_pixelRatio;
106106
event.pixel_ratio = g_pixelRatio;
107+
// This example only supports a single window, therefore we assume the event
108+
// occurred in the only view, the implicit view.
109+
event.view_id = kImplicitViewId;
107110
FlutterEngineSendWindowMetricsEvent(
108111
reinterpret_cast<FlutterEngine>(glfwGetWindowUserPointer(window)),
109112
&event);

examples/vulkan_glfw/src/main.cc

+7
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ static const size_t kInitialWindowHeight = 600;
3232
// `VK_PRESENT_MODE_MAILBOX_KHR` for continual swap without horizontal tearing,
3333
// or `VK_PRESENT_MODE_IMMEDIATE_KHR` for no vsync.
3434
static const VkPresentModeKHR kPreferredPresentMode = VK_PRESENT_MODE_FIFO_KHR;
35+
static constexpr FlutterViewId kImplicitViewId = 0;
3536

3637
static_assert(FLUTTER_ENGINE_VERSION == 1,
3738
"This Flutter Embedder was authored against the stable Flutter "
@@ -86,6 +87,9 @@ void GLFWcursorPositionCallbackAtPhase(GLFWwindow* window,
8687
std::chrono::duration_cast<std::chrono::microseconds>(
8788
std::chrono::high_resolution_clock::now().time_since_epoch())
8889
.count();
90+
// This example only supports a single window, therefore we assume the event
91+
// occurred in the only view, the implicit view.
92+
event.view_id = kImplicitViewId;
8993
FlutterEngineSendPointerEvent(g_state.engine, &event, 1);
9094
}
9195

@@ -130,6 +134,9 @@ void GLFWframebufferSizeCallback(GLFWwindow* window, int width, int height) {
130134
event.width = width;
131135
event.height = height;
132136
event.pixel_ratio = g_pixelRatio;
137+
// This example only supports a single window, therefore we assume the event
138+
// occurred in the only view, the implicit view.
139+
event.view_id = kImplicitViewId;
133140
FlutterEngineSendWindowMetricsEvent(g_state.engine, &event);
134141
}
135142

shell/platform/darwin/macos/framework/Source/FlutterEngine.mm

+1-6
Original file line numberDiff line numberDiff line change
@@ -962,12 +962,6 @@ - (nonnull NSString*)executableName {
962962
}
963963

964964
- (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController {
965-
if (viewController.viewId != kFlutterImplicitViewId) {
966-
// TODO(dkwingsmt): The embedder API only supports single-view for now. As
967-
// embedder APIs are converted to multi-view, this method should support any
968-
// views.
969-
return;
970-
}
971965
if (!_engine || !viewController || !viewController.viewLoaded) {
972966
return;
973967
}
@@ -986,6 +980,7 @@ - (void)updateWindowMetricsForViewController:(FlutterViewController*)viewControl
986980
.left = static_cast<size_t>(scaledBounds.origin.x),
987981
.top = static_cast<size_t>(scaledBounds.origin.y),
988982
.display_id = static_cast<uint64_t>(displayId),
983+
.view_id = viewController.viewId,
989984
};
990985
_embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
991986
}

shell/platform/embedder/embedder.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -2141,8 +2141,8 @@ FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
21412141
if (engine == nullptr || flutter_metrics == nullptr) {
21422142
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
21432143
}
2144-
// TODO(dkwingsmt): Use a real view ID when multiview is supported.
2145-
int64_t view_id = kFlutterImplicitViewId;
2144+
FlutterViewId view_id =
2145+
SAFE_ACCESS(flutter_metrics, view_id, kFlutterImplicitViewId);
21462146

21472147
flutter::ViewportMetrics metrics;
21482148

shell/platform/embedder/embedder.h

+2
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,8 @@ typedef struct {
859859
double physical_view_inset_left;
860860
/// The identifier of the display the view is rendering on.
861861
FlutterEngineDisplayId display_id;
862+
/// The view that this event is describing.
863+
int64_t view_id;
862864
} FlutterWindowMetricsEvent;
863865

864866
/// The phase of the pointer event.

shell/platform/embedder/fixtures/main.dart

+36
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,42 @@ void pointer_data_packet_view_id() {
13341334
signalNativeTest();
13351335
}
13361336

1337+
Map<int, Size> _getAllViewSizes() {
1338+
final Map<int, Size> result = <int, Size>{};
1339+
for (final FlutterView view in PlatformDispatcher.instance.views) {
1340+
result[view.viewId] = view.physicalSize;
1341+
}
1342+
return result;
1343+
}
1344+
1345+
List<int> _findDifferences(Map<int, Size> a, Map<int, Size> b) {
1346+
final Set<int> result = <int>{};
1347+
a.forEach((int viewId, Size sizeA) {
1348+
if (!b.containsKey(viewId) || b[viewId] != sizeA) {
1349+
result.add(viewId);
1350+
}
1351+
});
1352+
b.forEach((int viewId, Size sizeB) {
1353+
if (!a.containsKey(viewId)) {
1354+
result.add(viewId);
1355+
}
1356+
});
1357+
return result.toList()..sort();
1358+
}
1359+
1360+
@pragma('vm:entry-point')
1361+
void window_metrics_event_view_id() {
1362+
Map<int, Size> sizes = _getAllViewSizes();
1363+
PlatformDispatcher.instance.onMetricsChanged = () {
1364+
final Map<int, Size> newSizes = _getAllViewSizes();
1365+
final List<int> differences = _findDifferences(sizes, newSizes);
1366+
sizes = newSizes;
1367+
signalNativeMessage('Changed: $differences');
1368+
};
1369+
1370+
signalNativeTest();
1371+
}
1372+
13371373
@pragma('vm:entry-point')
13381374
Future<void> channel_listener_response() async {
13391375
channelBuffers.setListener('test/listen',

shell/platform/embedder/tests/embedder_unittests.cc

+98-1
Original file line numberDiff line numberDiff line change
@@ -2727,7 +2727,7 @@ TEST_F(EmbedderTest, CanSendPointer) {
27272727

27282728
/// Send a pointer event to Dart and wait until the Dart code echos with the
27292729
/// view ID.
2730-
TEST_F(EmbedderTest, CanSendPointerWithViewId) {
2730+
TEST_F(EmbedderTest, CanSendPointerEventWithViewId) {
27312731
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
27322732
EmbedderConfigBuilder builder(context);
27332733
builder.SetSoftwareRendererConfig();
@@ -2767,6 +2767,103 @@ TEST_F(EmbedderTest, CanSendPointerWithViewId) {
27672767
message_latch.Wait();
27682768
}
27692769

2770+
TEST_F(EmbedderTest, WindowMetricsEventDefaultsToImplicitView) {
2771+
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2772+
EmbedderConfigBuilder builder(context);
2773+
builder.SetSoftwareRendererConfig();
2774+
builder.SetDartEntrypoint("window_metrics_event_view_id");
2775+
2776+
fml::AutoResetWaitableEvent ready_latch, message_latch;
2777+
context.AddNativeCallback(
2778+
"SignalNativeTest",
2779+
CREATE_NATIVE_ENTRY(
2780+
[&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
2781+
context.AddNativeCallback(
2782+
"SignalNativeMessage",
2783+
CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
2784+
auto message = tonic::DartConverter<std::string>::FromDart(
2785+
Dart_GetNativeArgument(args, 0));
2786+
ASSERT_EQ("Changed: [0]", message);
2787+
message_latch.Signal();
2788+
}));
2789+
2790+
auto engine = builder.LaunchEngine();
2791+
ASSERT_TRUE(engine.is_valid());
2792+
2793+
ready_latch.Wait();
2794+
2795+
FlutterWindowMetricsEvent event = {};
2796+
// Simulate an event that comes from an old version of embedder.h that doesn't
2797+
// have the view_id field.
2798+
event.struct_size = offsetof(FlutterWindowMetricsEvent, view_id);
2799+
event.width = 200;
2800+
event.height = 300;
2801+
event.pixel_ratio = 1.5;
2802+
// Skip assigning event.view_id here to test the default behavior.
2803+
2804+
FlutterEngineResult result =
2805+
FlutterEngineSendWindowMetricsEvent(engine.get(), &event);
2806+
ASSERT_EQ(result, kSuccess);
2807+
2808+
message_latch.Wait();
2809+
}
2810+
2811+
TEST_F(EmbedderTest, IgnoresWindowMetricsEventForUnknownView) {
2812+
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
2813+
EmbedderConfigBuilder builder(context);
2814+
builder.SetSoftwareRendererConfig();
2815+
builder.SetDartEntrypoint("window_metrics_event_view_id");
2816+
2817+
fml::AutoResetWaitableEvent ready_latch, message_latch;
2818+
context.AddNativeCallback(
2819+
"SignalNativeTest",
2820+
CREATE_NATIVE_ENTRY(
2821+
[&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
2822+
2823+
context.AddNativeCallback(
2824+
"SignalNativeMessage",
2825+
CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
2826+
auto message = tonic::DartConverter<std::string>::FromDart(
2827+
Dart_GetNativeArgument(args, 0));
2828+
// Message latch should only be signaled once as the bad
2829+
// view metric should be dropped by the engine.
2830+
ASSERT_FALSE(message_latch.IsSignaledForTest());
2831+
ASSERT_EQ("Changed: [0]", message);
2832+
message_latch.Signal();
2833+
}));
2834+
2835+
auto engine = builder.LaunchEngine();
2836+
ASSERT_TRUE(engine.is_valid());
2837+
2838+
ready_latch.Wait();
2839+
2840+
// Send a window metric for a nonexistent view, which should be dropped by the
2841+
// engine.
2842+
FlutterWindowMetricsEvent bad_event = {};
2843+
bad_event.struct_size = sizeof(FlutterWindowMetricsEvent);
2844+
bad_event.width = 200;
2845+
bad_event.height = 300;
2846+
bad_event.pixel_ratio = 1.5;
2847+
bad_event.view_id = 100;
2848+
2849+
FlutterEngineResult result =
2850+
FlutterEngineSendWindowMetricsEvent(engine.get(), &bad_event);
2851+
ASSERT_EQ(result, kSuccess);
2852+
2853+
// Send a window metric for a valid view. The engine notifies the Dart app.
2854+
FlutterWindowMetricsEvent event = {};
2855+
event.struct_size = sizeof(FlutterWindowMetricsEvent);
2856+
event.width = 200;
2857+
event.height = 300;
2858+
event.pixel_ratio = 1.5;
2859+
event.view_id = 0;
2860+
2861+
result = FlutterEngineSendWindowMetricsEvent(engine.get(), &event);
2862+
ASSERT_EQ(result, kSuccess);
2863+
2864+
message_latch.Wait();
2865+
}
2866+
27702867
TEST_F(EmbedderTest, RegisterChannelListener) {
27712868
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
27722869

shell/platform/glfw/flutter_glfw.cc

+3
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ static void SendWindowMetrics(FlutterDesktopWindowControllerState* controller,
297297
} else {
298298
event.pixel_ratio = controller->window_wrapper->pixel_ratio_override;
299299
}
300+
// The GLFW embedder doesn't support multiple views. We assume all pointer
301+
// events come from the only view, the implicit view.
302+
event.view_id = flutter::kFlutterImplicitViewId;
300303
FlutterEngineSendWindowMetricsEvent(controller->engine->flutter_engine,
301304
&event);
302305
}

shell/platform/linux/fl_engine.cc

+4
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,10 @@ void fl_engine_send_window_metrics_event(FlEngine* self,
760760
event.width = width;
761761
event.height = height;
762762
event.pixel_ratio = pixel_ratio;
763+
// TODO(dkwingsmt): Assign the correct view ID once the Linux embedder
764+
// supports multiple views.
765+
// https://github.com/flutter/flutter/issues/138178
766+
event.view_id = flutter::kFlutterImplicitViewId;
763767
self->embedder_api.SendWindowMetricsEvent(self->engine, &event);
764768
}
765769

shell/platform/windows/flutter_windows_view.cc

+2-4
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ void FlutterWindowsView::SendWindowMetrics(size_t width,
352352
event.width = width;
353353
event.height = height;
354354
event.pixel_ratio = dpiScale;
355+
event.view_id = view_id_;
355356
engine_->SendWindowMetricsEvent(event);
356357
}
357358

@@ -588,10 +589,7 @@ void FlutterWindowsView::SendPointerEventWithData(
588589
event.device_kind = state->device_kind;
589590
event.device = state->pointer_id;
590591
event.buttons = state->buttons;
591-
// TODO(dkwingsmt): Use the correct view ID for pointer events once the
592-
// Windows embedder supports multiple views.
593-
// https://github.com/flutter/flutter/issues/138179
594-
event.view_id = flutter::kFlutterImplicitViewId;
592+
event.view_id = view_id_;
595593

596594
// Set metadata that's always the same regardless of the event.
597595
event.struct_size = sizeof(event);

0 commit comments

Comments
 (0)