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 all commits
Commits
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 runtime/runtime_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ void RuntimeController::AddView(int64_t view_id,

platform_data_.viewport_metrics_for_views[view_id] = view_metrics;
bool added = platform_configuration->AddView(view_id, view_metrics);
if (added) {
ScheduleFrame();
}

callback(added);
}

Expand Down
1 change: 1 addition & 0 deletions runtime/runtime_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class RuntimeController : public PlatformConfigurationClient {
/// flushed to the isolate when it starts. Calling `RemoveView`
/// before the isolate is launched cancels the add operation.
///
/// If the isolate is running, a frame will be scheduled.
///
/// @param[in] view_id The ID of the new view.
/// @param[in] viewport_metrics The initial viewport metrics for the view.
Expand Down
64 changes: 64 additions & 0 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2183,6 +2183,66 @@ FlutterEngineResult FlutterEngineRunInitialized(
return kSuccess;
}

FLUTTER_EXPORT
FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const FlutterAddViewInfo* info) {
if (!engine) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (!info || !info->view_metrics || !info->add_view_callback) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Add view info handle was invalid.");
}

FlutterViewId view_id = info->view_id;
if (view_id == kFlutterImplicitViewId) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Add view info was invalid. The implicit view cannot be added.");
}
if (SAFE_ACCESS(info->view_metrics, view_id, kFlutterImplicitViewId) !=
view_id) {
if (view_id == kFlutterImplicitViewId) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Add view info was invalid. The info and "
"window metric view IDs must match.");
}
}

// TODO(loicsharma): Return an error if the engine was initialized with
// callbacks that are incompatible with multiple views.
// https://github.com/flutter/flutter/issues/144806

std::variant<flutter::ViewportMetrics, std::string> metrics_or_error =
MakeViewportMetricsFromWindowMetrics(info->view_metrics);

if (const std::string* error = std::get_if<std::string>(&metrics_or_error)) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, error->c_str());
}

auto metrics = std::get<flutter::ViewportMetrics>(metrics_or_error);

// The engine must be running to add a view.
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
if (!embedder_engine->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}

flutter::Shell::AddViewCallback callback =
[c_callback = info->add_view_callback,
user_data = info->user_data](bool added) {
FlutterAddViewResult result = {};
result.struct_size = sizeof(FlutterAddViewResult);
result.added = added;
result.user_data = user_data;
c_callback(&result);
};

embedder_engine->GetShell().AddView(view_id, metrics, callback);
return kSuccess;
}

FLUTTER_EXPORT
FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
Expand All @@ -2201,6 +2261,10 @@ FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine)
"Remove view info was invalid. The implicit view cannot be removed.");
}

// TODO(loicsharma): Return an error if the engine was initialized with
// callbacks that are incompatible with multiple views.
// https://github.com/flutter/flutter/issues/144806

// The engine must be running to remove a view.
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
if (!embedder_engine->IsValid()) {
Expand Down
150 changes: 116 additions & 34 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,86 @@ typedef struct {
};
} FlutterRendererConfig;

/// Display refers to a graphics hardware system consisting of a framebuffer,
/// typically a monitor or a screen. This ID is unique per display and is
/// stable until the Flutter application restarts.
typedef uint64_t FlutterEngineDisplayId;

typedef struct {
/// The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
size_t struct_size;
/// Physical width of the window.
size_t width;
/// Physical height of the window.
size_t height;
/// Scale factor for the physical screen.
double pixel_ratio;
/// Horizontal physical location of the left side of the window on the screen.
size_t left;
/// Vertical physical location of the top of the window on the screen.
size_t top;
/// Top inset of window.
double physical_view_inset_top;
/// Right inset of window.
double physical_view_inset_right;
/// Bottom inset of window.
double physical_view_inset_bottom;
/// Left inset of window.
double physical_view_inset_left;
/// The identifier of the display the view is rendering on.
FlutterEngineDisplayId display_id;
/// The view that this event is describing.
int64_t view_id;
} FlutterWindowMetricsEvent;
Copy link
Member

@cbracken cbracken Apr 2, 2024

Choose a reason for hiding this comment

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

Note to future archaeologists - this is just code moving around; there are no diffs here.


typedef struct {
/// The size of this struct.
/// Must be sizeof(FlutterAddViewResult).
size_t struct_size;

/// True if the add view operation succeeded.
bool added;

/// The |FlutterAddViewInfo.user_data|.
void* user_data;
} FlutterAddViewResult;

/// The callback invoked by the engine when the engine has attempted to add a
/// view.
///
/// The |FlutterAddViewResult| is only guaranteed to be valid during this
/// callback.
typedef void (*FlutterAddViewCallback)(const FlutterAddViewResult* result);

typedef struct {
/// The size of this struct.
/// Must be sizeof(FlutterAddViewInfo).
size_t struct_size;

/// The identifier for the view to add. This must be unique.
FlutterViewId view_id;

/// The view's properties.
///
/// The metric's |view_id| must match this struct's |view_id|.
const FlutterWindowMetricsEvent* view_metrics;

/// A baton that is not interpreted by the engine in any way. It will be given
/// back to the embedder in |add_view_callback|. Embedder resources may be
/// associated with this baton.
void* user_data;

/// Called once the engine has attempted to add the view. This callback is
/// required.
///
/// The embedder/app must not use the view until the callback is invoked with
/// an `added` value of `true`.
///
/// This callback is invoked on an internal engine managed thread. Embedders
/// must re-thread if necessary.
FlutterAddViewCallback add_view_callback;
} FlutterAddViewInfo;

typedef struct {
/// The size of this struct.
/// Must be sizeof(FlutterRemoveViewResult).
Expand All @@ -846,7 +926,8 @@ typedef struct {
/// The callback invoked by the engine when the engine has attempted to remove
/// a view.
///
/// The |FlutterRemoveViewResult| will be deallocated once the callback returns.
/// The |FlutterRemoveViewResult| is only guaranteed to be valid during this
/// callback.
typedef void (*FlutterRemoveViewCallback)(
const FlutterRemoveViewResult* /* result */);

Expand Down Expand Up @@ -878,38 +959,6 @@ typedef struct {
FlutterRemoveViewCallback remove_view_callback;
} FlutterRemoveViewInfo;

/// Display refers to a graphics hardware system consisting of a framebuffer,
/// typically a monitor or a screen. This ID is unique per display and is
/// stable until the Flutter application restarts.
typedef uint64_t FlutterEngineDisplayId;

typedef struct {
/// The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
size_t struct_size;
/// Physical width of the window.
size_t width;
/// Physical height of the window.
size_t height;
/// Scale factor for the physical screen.
double pixel_ratio;
/// Horizontal physical location of the left side of the window on the screen.
size_t left;
/// Vertical physical location of the top of the window on the screen.
size_t top;
/// Top inset of window.
double physical_view_inset_top;
/// Right inset of window.
double physical_view_inset_right;
/// Bottom inset of window.
double physical_view_inset_bottom;
/// Left inset of window.
double physical_view_inset_left;
/// The identifier of the display the view is rendering on.
FlutterEngineDisplayId display_id;
/// The view that this event is describing.
int64_t view_id;
} FlutterWindowMetricsEvent;

/// The phase of the pointer event.
typedef enum {
kCancel,
Expand Down Expand Up @@ -1851,7 +1900,9 @@ typedef struct {
/// Callback invoked by the engine to composite the contents of each layer
/// onto the implicit view.
///
/// DEPRECATED: Use |present_view_callback| to support multiple views.
/// DEPRECATED: Use `present_view_callback` to support multiple views.
/// If this callback is provided, `FlutterEngineAddView` and
/// `FlutterEngineRemoveView` should not be used.
///
/// Only one of `present_layers_callback` and `present_view_callback` may be
/// provided. Providing both is an error and engine initialization will
Expand Down Expand Up @@ -2175,6 +2226,10 @@ typedef struct {
/// `update_semantics_callback`, and
/// `update_semantics_callback2` may be provided; the others
/// should be set to null.
///
/// This callback is incompatible with multiple views. If this
/// callback is provided, `FlutterEngineAddView` and
/// `FlutterEngineRemoveView` should not be used.
FlutterUpdateSemanticsNodeCallback update_semantics_node_callback;
/// The legacy callback invoked by the engine in order to give the embedder
/// the chance to respond to updates to semantics custom actions from the Dart
Expand All @@ -2191,6 +2246,10 @@ typedef struct {
/// `update_semantics_callback`, and
/// `update_semantics_callback2` may be provided; the others
/// should be set to null.
///
/// This callback is incompatible with multiple views. If this
/// callback is provided, `FlutterEngineAddView` and
/// `FlutterEngineRemoveView` should not be used.
FlutterUpdateSemanticsCustomActionCallback
update_semantics_custom_action_callback;
/// Path to a directory used to store data that is cached across runs of a
Expand Down Expand Up @@ -2340,6 +2399,10 @@ typedef struct {
/// `update_semantics_callback`, and
/// `update_semantics_callback2` may be provided; the others
/// must be set to null.
///
/// This callback is incompatible with multiple views. If this
/// callback is provided, `FlutterEngineAddView` and
/// `FlutterEngineRemoveView` should not be used.
FlutterUpdateSemanticsCallback update_semantics_callback;

/// The callback invoked by the engine in order to give the embedder the
Expand Down Expand Up @@ -2505,6 +2568,25 @@ FLUTTER_EXPORT
FlutterEngineResult FlutterEngineRunInitialized(
FLUTTER_API_SYMBOL(FlutterEngine) engine);

//------------------------------------------------------------------------------
/// @brief Adds a view.
///
/// This is an asynchronous operation. The view should not be used
/// until the |add_view_callback| is invoked with an `added` of
/// `true`.
///
/// @param[in] engine A running engine instance.
/// @param[in] info The add view arguments. This can be deallocated
/// once |FlutterEngineAddView| returns, before
/// |add_view_callback| is invoked.
///
/// @return The result of *starting* the asynchronous operation. If
/// `kSuccess`, the |add_view_callback| will be invoked.
FLUTTER_EXPORT
FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const FlutterAddViewInfo* info);

//------------------------------------------------------------------------------
/// @brief Removes a view.
///
Expand Down
48 changes: 48 additions & 0 deletions shell/platform/embedder/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,28 @@ void render_implicit_view() {
PlatformDispatcher.instance.scheduleFrame();
}

@pragma('vm:entry-point')
void render_all_views() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
for (final FlutterView view in PlatformDispatcher.instance.views) {
final Size size = Size(800.0, 600.0);
final Color red = Color.fromARGB(127, 255, 0, 0);

final SceneBuilder builder = SceneBuilder();

builder.pushOffset(0.0, 0.0);

builder.addPicture(
Offset(0.0, 0.0), CreateColoredBox(red, size));

builder.pop();

view.render(builder.build());
}
};
PlatformDispatcher.instance.scheduleFrame();
}

@pragma('vm:entry-point')
void render_gradient() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
Expand Down Expand Up @@ -1307,6 +1329,18 @@ void can_schedule_frame() {
signalNativeTest();
}

@pragma('vm:entry-point')
void add_view_schedules_frame() {
PlatformDispatcher.instance.onBeginFrame = (Duration beginTime) {
for (final FlutterView view in PlatformDispatcher.instance.views) {
if (view.viewId == 123) {
signalNativeCount(beginTime.inMicroseconds);
}
}
};
signalNativeTest();
}

void drawSolidColor(Color c) {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
final SceneBuilder builder = SceneBuilder();
Expand Down Expand Up @@ -1399,6 +1433,20 @@ void window_metrics_event_view_id() {
signalNativeTest();
}

@pragma('vm:entry-point')
void window_metrics_event_all_view_ids() {
PlatformDispatcher.instance.onMetricsChanged = () {
final List<int> viewIds =
PlatformDispatcher.instance.views.map((view) => view.viewId).toList();

viewIds.sort();

signalNativeMessage('View IDs: [${viewIds.join(', ')}]');
};

signalNativeTest();
}

@pragma('vm:entry-point')
Future<void> channel_listener_response() async {
channelBuffers.setListener('test/listen',
Expand Down
Loading