Kevin Lubick 0946d27add Migrate uses of SkSurface factories (flutter/engine#41978)
In http://review.skia.org/687639, many SkSurface static methods were moved and one was deleted (SkSurface::MakeRasterN32Premul). (SkSurface::MakeNull was omitted accidentally and http://review.skia.org/696537glesource.com/c/skia/+/696537 has not rolled into Flutter yet)

This changes the calls in Flutter to match those moved functions. There should be no functional difference and everything was done pretty mechanically (e.g. find and replace)

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
2023-05-15 16:41:10 +00:00

3116 lines
116 KiB
C++

// 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.
#define FML_USED_ON_EMBEDDER
#define RAPIDJSON_HAS_STDSTRING 1
#include <cstring>
#include <iostream>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "flutter/fml/build_config.h"
#include "flutter/fml/closure.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/thread.h"
#include "third_party/dart/runtime/bin/elf_loader.h"
#include "third_party/dart/runtime/include/dart_native_api.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
#if !defined(FLUTTER_NO_EXPORT)
#if FML_OS_WIN
#define FLUTTER_EXPORT __declspec(dllexport)
#else // FML_OS_WIN
#define FLUTTER_EXPORT __attribute__((visibility("default")))
#endif // FML_OS_WIN
#endif // !FLUTTER_NO_EXPORT
extern "C" {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// Used for debugging dart:* sources.
extern const uint8_t kPlatformStrongDill[];
extern const intptr_t kPlatformStrongDillSize;
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
}
#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/common/graphics/persistent_cache.h"
#include "flutter/common/task_runners.h"
#include "flutter/fml/command_line.h"
#include "flutter/fml/file.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/trace_event.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/switches.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/embedder_engine.h"
#include "flutter/shell/platform/embedder/embedder_external_texture_resolver.h"
#include "flutter/shell/platform/embedder/embedder_platform_message_response.h"
#include "flutter/shell/platform/embedder/embedder_render_target.h"
#include "flutter/shell/platform/embedder/embedder_struct_macros.h"
#include "flutter/shell/platform/embedder/embedder_task_runner.h"
#include "flutter/shell/platform/embedder/embedder_thread_host.h"
#include "flutter/shell/platform/embedder/pixel_formats.h"
#include "flutter/shell/platform/embedder/platform_view_embedder.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/writer.h"
#ifdef SHELL_ENABLE_GL
#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h"
#endif
#ifdef SHELL_ENABLE_METAL
#include "flutter/shell/platform/embedder/embedder_surface_metal.h"
#endif
const int32_t kFlutterSemanticsNodeIdBatchEnd = -1;
const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1;
// A message channel to send platform-independent FlutterKeyData to the
// framework.
//
// This should be kept in sync with the following variables:
//
// - lib/ui/platform_dispatcher.dart, _kFlutterKeyDataChannel
// - shell/platform/darwin/ios/framework/Source/FlutterEngine.mm,
// FlutterKeyDataChannel
// - io/flutter/embedding/android/KeyData.java,
// CHANNEL
//
// Not to be confused with "flutter/keyevent", which is used to send raw
// key event data in a platform-dependent format.
//
// ## Format
//
// Send: KeyDataPacket.data().
//
// Expected reply: Whether the event is handled. Exactly 1 byte long, with value
// 1 for handled, and 0 for not. Malformed value is considered false.
const char* kFlutterKeyDataChannel = "flutter/keydata";
static FlutterEngineResult LogEmbedderError(FlutterEngineResult code,
const char* reason,
const char* code_name,
const char* function,
const char* file,
int line) {
#if FML_OS_WIN
constexpr char kSeparator = '\\';
#else
constexpr char kSeparator = '/';
#endif
const auto file_base =
(::strrchr(file, kSeparator) ? strrchr(file, kSeparator) + 1 : file);
char error[256] = {};
snprintf(error, (sizeof(error) / sizeof(char)),
"%s (%d): '%s' returned '%s'. %s", file_base, line, function,
code_name, reason);
std::cerr << error << std::endl;
return code;
}
#define LOG_EMBEDDER_ERROR(code, reason) \
LogEmbedderError(code, reason, #code, __FUNCTION__, __FILE__, __LINE__)
static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kOpenGL) {
return false;
}
const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl;
if (!SAFE_EXISTS(open_gl_config, make_current) ||
!SAFE_EXISTS(open_gl_config, clear_current) ||
!SAFE_EXISTS_ONE_OF(open_gl_config, fbo_callback,
fbo_with_frame_info_callback) ||
!SAFE_EXISTS_ONE_OF(open_gl_config, present, present_with_info)) {
return false;
}
return true;
}
static bool IsSoftwareRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kSoftware) {
return false;
}
const FlutterSoftwareRendererConfig* software_config = &config->software;
if (SAFE_ACCESS(software_config, surface_present_callback, nullptr) ==
nullptr) {
return false;
}
return true;
}
static bool IsMetalRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kMetal) {
return false;
}
const FlutterMetalRendererConfig* metal_config = &config->metal;
bool device = SAFE_ACCESS(metal_config, device, nullptr);
bool command_queue =
SAFE_ACCESS(metal_config, present_command_queue, nullptr);
bool present = SAFE_ACCESS(metal_config, present_drawable_callback, nullptr);
bool get_texture =
SAFE_ACCESS(metal_config, get_next_drawable_callback, nullptr);
return device && command_queue && present && get_texture;
}
static bool IsVulkanRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kVulkan) {
return false;
}
const FlutterVulkanRendererConfig* vulkan_config = &config->vulkan;
if (!SAFE_EXISTS(vulkan_config, instance) ||
!SAFE_EXISTS(vulkan_config, physical_device) ||
!SAFE_EXISTS(vulkan_config, device) ||
!SAFE_EXISTS(vulkan_config, queue) ||
!SAFE_EXISTS(vulkan_config, get_instance_proc_address_callback) ||
!SAFE_EXISTS(vulkan_config, get_next_image_callback) ||
!SAFE_EXISTS(vulkan_config, present_image_callback)) {
return false;
}
return true;
}
static bool IsRendererValid(const FlutterRendererConfig* config) {
if (config == nullptr) {
return false;
}
switch (config->type) {
case kOpenGL:
return IsOpenGLRendererConfigValid(config);
case kSoftware:
return IsSoftwareRendererConfigValid(config);
case kMetal:
return IsMetalRendererConfigValid(config);
case kVulkan:
return IsVulkanRendererConfigValid(config);
default:
return false;
}
return false;
}
#if FML_OS_LINUX || FML_OS_WIN
static void* DefaultGLProcResolver(const char* name) {
static fml::RefPtr<fml::NativeLibrary> proc_library =
#if FML_OS_LINUX
fml::NativeLibrary::CreateForCurrentProcess();
#elif FML_OS_WIN // FML_OS_LINUX
fml::NativeLibrary::Create("opengl32.dll");
#endif // FML_OS_WIN
return static_cast<void*>(
const_cast<uint8_t*>(proc_library->ResolveSymbol(name)));
}
#endif // FML_OS_LINUX || FML_OS_WIN
#ifdef SHELL_ENABLE_GL
// Auxiliary function used to translate rectangles of type SkIRect to
// FlutterRect.
static FlutterRect SkIRectToFlutterRect(const SkIRect sk_rect) {
FlutterRect flutter_rect = {static_cast<double>(sk_rect.fLeft),
static_cast<double>(sk_rect.fTop),
static_cast<double>(sk_rect.fRight),
static_cast<double>(sk_rect.fBottom)};
return flutter_rect;
}
// Auxiliary function used to translate rectangles of type FlutterRect to
// SkIRect.
static const SkIRect FlutterRectToSkIRect(FlutterRect flutter_rect) {
SkIRect rect = {static_cast<int32_t>(flutter_rect.left),
static_cast<int32_t>(flutter_rect.top),
static_cast<int32_t>(flutter_rect.right),
static_cast<int32_t>(flutter_rect.bottom)};
return rect;
}
#endif
static inline flutter::Shell::CreateCallback<flutter::PlatformView>
InferOpenGLPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
#ifdef SHELL_ENABLE_GL
if (config->type != kOpenGL) {
return nullptr;
}
auto gl_make_current = [ptr = config->open_gl.make_current,
user_data]() -> bool { return ptr(user_data); };
auto gl_clear_current = [ptr = config->open_gl.clear_current,
user_data]() -> bool { return ptr(user_data); };
auto gl_present =
[present = config->open_gl.present,
present_with_info = config->open_gl.present_with_info,
user_data](flutter::GLPresentInfo gl_present_info) -> bool {
if (present) {
return present(user_data);
} else {
// Format the frame and buffer damages accordingly. Note that, since the
// current compute damage algorithm only returns one rectangle for damage
// we are assuming the number of rectangles provided in frame and buffer
// damage are always 1. Once the function that computes damage implements
// support for multiple damage rectangles, GLPresentInfo should also
// contain the number of damage rectangles.
const size_t num_rects = 1;
std::array<FlutterRect, num_rects> frame_damage_rect = {
SkIRectToFlutterRect(*(gl_present_info.frame_damage))};
std::array<FlutterRect, num_rects> buffer_damage_rect = {
SkIRectToFlutterRect(*(gl_present_info.buffer_damage))};
FlutterDamage frame_damage{
.struct_size = sizeof(FlutterDamage),
.num_rects = frame_damage_rect.size(),
.damage = frame_damage_rect.data(),
};
FlutterDamage buffer_damage{
.struct_size = sizeof(FlutterDamage),
.num_rects = buffer_damage_rect.size(),
.damage = buffer_damage_rect.data(),
};
// Construct the present information concerning the frame being rendered.
FlutterPresentInfo present_info = {
.struct_size = sizeof(FlutterPresentInfo),
.fbo_id = gl_present_info.fbo_id,
.frame_damage = frame_damage,
.buffer_damage = buffer_damage,
};
return present_with_info(user_data, &present_info);
}
};
auto gl_fbo_callback =
[fbo_callback = config->open_gl.fbo_callback,
fbo_with_frame_info_callback =
config->open_gl.fbo_with_frame_info_callback,
user_data](flutter::GLFrameInfo gl_frame_info) -> intptr_t {
if (fbo_callback) {
return fbo_callback(user_data);
} else {
FlutterFrameInfo frame_info = {};
frame_info.struct_size = sizeof(FlutterFrameInfo);
frame_info.size = {gl_frame_info.width, gl_frame_info.height};
return fbo_with_frame_info_callback(user_data, &frame_info);
}
};
auto gl_populate_existing_damage =
[populate_existing_damage = config->open_gl.populate_existing_damage,
user_data](intptr_t id) -> flutter::GLFBOInfo {
// If no populate_existing_damage was provided, disable partial
// repaint.
if (!populate_existing_damage) {
return flutter::GLFBOInfo{
.fbo_id = static_cast<uint32_t>(id),
.partial_repaint_enabled = false,
.existing_damage = SkIRect::MakeEmpty(),
};
}
// Given the FBO's ID, get its existing damage.
FlutterDamage existing_damage;
populate_existing_damage(user_data, id, &existing_damage);
bool partial_repaint_enabled = true;
SkIRect existing_damage_rect;
// Verify that at least one damage rectangle was provided.
if (existing_damage.num_rects <= 0 || existing_damage.damage == nullptr) {
FML_LOG(INFO) << "No damage was provided. Forcing full repaint.";
existing_damage_rect = SkIRect::MakeEmpty();
partial_repaint_enabled = false;
} else if (existing_damage.num_rects > 1) {
// Log message notifying users that multi-damage is not yet available in
// case they try to make use of it.
FML_LOG(INFO) << "Damage with multiple rectangles not yet supported. "
"Repainting the whole frame.";
existing_damage_rect = SkIRect::MakeEmpty();
partial_repaint_enabled = false;
} else {
existing_damage_rect = FlutterRectToSkIRect(*(existing_damage.damage));
}
// Pass the information about this FBO to the rendering backend.
return flutter::GLFBOInfo{
.fbo_id = static_cast<uint32_t>(id),
.partial_repaint_enabled = partial_repaint_enabled,
.existing_damage = existing_damage_rect,
};
};
const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl;
std::function<bool()> gl_make_resource_current_callback = nullptr;
if (SAFE_ACCESS(open_gl_config, make_resource_current, nullptr) != nullptr) {
gl_make_resource_current_callback =
[ptr = config->open_gl.make_resource_current, user_data]() {
return ptr(user_data);
};
}
std::function<SkMatrix(void)> gl_surface_transformation_callback = nullptr;
if (SAFE_ACCESS(open_gl_config, surface_transformation, nullptr) != nullptr) {
gl_surface_transformation_callback =
[ptr = config->open_gl.surface_transformation, user_data]() {
FlutterTransformation transformation = ptr(user_data);
return SkMatrix::MakeAll(transformation.scaleX, //
transformation.skewX, //
transformation.transX, //
transformation.skewY, //
transformation.scaleY, //
transformation.transY, //
transformation.pers0, //
transformation.pers1, //
transformation.pers2 //
);
};
// If there is an external view embedder, ask it to apply the surface
// transformation to its surfaces as well.
if (external_view_embedder) {
external_view_embedder->SetSurfaceTransformationCallback(
gl_surface_transformation_callback);
}
}
flutter::GPUSurfaceGLDelegate::GLProcResolver gl_proc_resolver = nullptr;
if (SAFE_ACCESS(open_gl_config, gl_proc_resolver, nullptr) != nullptr) {
gl_proc_resolver = [ptr = config->open_gl.gl_proc_resolver,
user_data](const char* gl_proc_name) {
return ptr(user_data, gl_proc_name);
};
} else {
#if FML_OS_LINUX || FML_OS_WIN
gl_proc_resolver = DefaultGLProcResolver;
#endif
}
bool fbo_reset_after_present =
SAFE_ACCESS(open_gl_config, fbo_reset_after_present, false);
flutter::EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table = {
gl_make_current, // gl_make_current_callback
gl_clear_current, // gl_clear_current_callback
gl_present, // gl_present_callback
gl_fbo_callback, // gl_fbo_callback
gl_make_resource_current_callback, // gl_make_resource_current_callback
gl_surface_transformation_callback, // gl_surface_transformation_callback
gl_proc_resolver, // gl_proc_resolver
gl_populate_existing_damage, // gl_populate_existing_damage
};
return fml::MakeCopyable(
[gl_dispatch_table, fbo_reset_after_present, platform_dispatch_table,
external_view_embedder =
std::move(external_view_embedder)](flutter::Shell& shell) mutable {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
gl_dispatch_table, // embedder GL dispatch table
fbo_reset_after_present, // fbo reset after present
platform_dispatch_table, // embedder platform dispatch table
std::move(external_view_embedder) // external view embedder
);
});
#else
return nullptr;
#endif
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferMetalPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
if (config->type != kMetal) {
return nullptr;
}
#ifdef SHELL_ENABLE_METAL
std::function<bool(flutter::GPUMTLTextureInfo texture)> metal_present =
[ptr = config->metal.present_drawable_callback,
user_data](flutter::GPUMTLTextureInfo texture) {
FlutterMetalTexture embedder_texture;
embedder_texture.struct_size = sizeof(FlutterMetalTexture);
embedder_texture.texture = texture.texture;
embedder_texture.texture_id = texture.texture_id;
embedder_texture.user_data = texture.destruction_context;
embedder_texture.destruction_callback = texture.destruction_callback;
return ptr(user_data, &embedder_texture);
};
auto metal_get_texture =
[ptr = config->metal.get_next_drawable_callback,
user_data](const SkISize& frame_size) -> flutter::GPUMTLTextureInfo {
FlutterFrameInfo frame_info = {};
frame_info.struct_size = sizeof(FlutterFrameInfo);
frame_info.size = {static_cast<uint32_t>(frame_size.width()),
static_cast<uint32_t>(frame_size.height())};
flutter::GPUMTLTextureInfo texture_info;
FlutterMetalTexture metal_texture = ptr(user_data, &frame_info);
texture_info.texture_id = metal_texture.texture_id;
texture_info.texture = metal_texture.texture;
texture_info.destruction_callback = metal_texture.destruction_callback;
texture_info.destruction_context = metal_texture.user_data;
return texture_info;
};
flutter::EmbedderSurfaceMetal::MetalDispatchTable metal_dispatch_table = {
.present = metal_present,
.get_texture = metal_get_texture,
};
std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder =
std::move(external_view_embedder);
std::unique_ptr<flutter::EmbedderSurfaceMetal> embedder_surface =
std::make_unique<flutter::EmbedderSurfaceMetal>(
const_cast<flutter::GPUMTLDeviceHandle>(config->metal.device),
const_cast<flutter::GPUMTLCommandQueueHandle>(
config->metal.present_command_queue),
metal_dispatch_table, view_embedder);
// The static leak checker gets confused by the use of fml::MakeCopyable.
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
return fml::MakeCopyable(
[embedder_surface = std::move(embedder_surface), platform_dispatch_table,
external_view_embedder = view_embedder](flutter::Shell& shell) mutable {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
std::move(embedder_surface), // embedder surface
platform_dispatch_table, // platform dispatch table
std::move(external_view_embedder) // external view embedder
);
});
#else
return nullptr;
#endif
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferVulkanPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
if (config->type != kVulkan) {
return nullptr;
}
#ifdef SHELL_ENABLE_VULKAN
std::function<void*(VkInstance, const char*)>
vulkan_get_instance_proc_address =
[ptr = config->vulkan.get_instance_proc_address_callback, user_data](
VkInstance instance, const char* proc_name) -> void* {
return ptr(user_data, instance, proc_name);
};
auto vulkan_get_next_image =
[ptr = config->vulkan.get_next_image_callback,
user_data](const SkISize& frame_size) -> FlutterVulkanImage {
FlutterFrameInfo frame_info = {
.struct_size = sizeof(FlutterFrameInfo),
.size = {static_cast<uint32_t>(frame_size.width()),
static_cast<uint32_t>(frame_size.height())},
};
return ptr(user_data, &frame_info);
};
auto vulkan_present_image_callback =
[ptr = config->vulkan.present_image_callback, user_data](
VkImage image, VkFormat format) -> bool {
FlutterVulkanImage image_desc = {
.struct_size = sizeof(FlutterVulkanImage),
.image = reinterpret_cast<uint64_t>(image),
.format = static_cast<uint32_t>(format),
};
return ptr(user_data, &image_desc);
};
auto vk_instance = static_cast<VkInstance>(config->vulkan.instance);
auto proc_addr =
vulkan_get_instance_proc_address(vk_instance, "vkGetInstanceProcAddr");
flutter::EmbedderSurfaceVulkan::VulkanDispatchTable vulkan_dispatch_table = {
.get_instance_proc_address =
reinterpret_cast<PFN_vkGetInstanceProcAddr>(proc_addr),
.get_next_image = vulkan_get_next_image,
.present_image = vulkan_present_image_callback,
};
std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder =
std::move(external_view_embedder);
std::unique_ptr<flutter::EmbedderSurfaceVulkan> embedder_surface =
std::make_unique<flutter::EmbedderSurfaceVulkan>(
config->vulkan.version, vk_instance,
config->vulkan.enabled_instance_extension_count,
config->vulkan.enabled_instance_extensions,
config->vulkan.enabled_device_extension_count,
config->vulkan.enabled_device_extensions,
static_cast<VkPhysicalDevice>(config->vulkan.physical_device),
static_cast<VkDevice>(config->vulkan.device),
config->vulkan.queue_family_index,
static_cast<VkQueue>(config->vulkan.queue), vulkan_dispatch_table,
view_embedder);
return fml::MakeCopyable(
[embedder_surface = std::move(embedder_surface), platform_dispatch_table,
external_view_embedder =
std::move(view_embedder)](flutter::Shell& shell) mutable {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
std::move(embedder_surface), // embedder surface
platform_dispatch_table, // platform dispatch table
std::move(external_view_embedder) // external view embedder
);
});
#else
return nullptr;
#endif
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferSoftwarePlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
if (config->type != kSoftware) {
return nullptr;
}
auto software_present_backing_store =
[ptr = config->software.surface_present_callback, user_data](
const void* allocation, size_t row_bytes, size_t height) -> bool {
return ptr(user_data, allocation, row_bytes, height);
};
flutter::EmbedderSurfaceSoftware::SoftwareDispatchTable
software_dispatch_table = {
software_present_backing_store, // required
};
return fml::MakeCopyable(
[software_dispatch_table, platform_dispatch_table,
external_view_embedder =
std::move(external_view_embedder)](flutter::Shell& shell) mutable {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
software_dispatch_table, // software dispatch table
platform_dispatch_table, // platform dispatch table
std::move(external_view_embedder) // external view embedder
);
});
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
if (config == nullptr) {
return nullptr;
}
switch (config->type) {
case kOpenGL:
return InferOpenGLPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder));
case kSoftware:
return InferSoftwarePlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder));
case kMetal:
return InferMetalPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder));
case kVulkan:
return InferVulkanPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder));
default:
return nullptr;
}
return nullptr;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterOpenGLTexture* texture) {
#ifdef SHELL_ENABLE_GL
GrGLTextureInfo texture_info;
texture_info.fTarget = texture->target;
texture_info.fID = texture->name;
texture_info.fFormat = texture->format;
GrBackendTexture backend_texture(config.size.width, //
config.size.height, //
GrMipMapped::kNo, //
texture_info //
);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendTexture(
context, // context
backend_texture, // back-end texture
kBottomLeft_GrSurfaceOrigin, // surface origin
1, // sample count
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurfaces::TextureReleaseProc>(
texture->destruction_callback), // release proc
texture->user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied render texture.";
return nullptr;
}
return surface;
#else
return nullptr;
#endif
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterOpenGLFramebuffer* framebuffer) {
#ifdef SHELL_ENABLE_GL
GrGLFramebufferInfo framebuffer_info = {};
framebuffer_info.fFormat = framebuffer->target;
framebuffer_info.fFBOID = framebuffer->name;
GrBackendRenderTarget backend_render_target(
config.size.width, // width
config.size.height, // height
1, // sample count
0, // stencil bits
framebuffer_info // framebuffer info
);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendRenderTarget(
context, // context
backend_render_target, // backend render target
kBottomLeft_GrSurfaceOrigin, // surface origin
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurface::RenderTargetReleaseProc>(
framebuffer->destruction_callback), // release proc
framebuffer->user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied frame-buffer.";
return nullptr;
}
return surface;
#else
return nullptr;
#endif
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterSoftwareBackingStore* software) {
const auto image_info =
SkImageInfo::MakeN32Premul(config.size.width, config.size.height);
struct Captures {
VoidCallback destruction_callback;
void* user_data;
};
auto captures = std::make_unique<Captures>();
captures->destruction_callback = software->destruction_callback;
captures->user_data = software->user_data;
auto release_proc = [](void* pixels, void* context) {
auto captures = reinterpret_cast<Captures*>(context);
if (captures->destruction_callback) {
captures->destruction_callback(captures->user_data);
}
delete captures;
};
auto surface =
SkSurfaces::WrapPixels(image_info, // image info
const_cast<void*>(software->allocation), // pixels
software->row_bytes, // row bytes
release_proc, // release proc
captures.get() // get context
);
if (!surface) {
FML_LOG(ERROR)
<< "Could not wrap embedder supplied software render buffer.";
if (software->destruction_callback) {
software->destruction_callback(software->user_data);
}
return nullptr;
}
if (surface) {
captures.release(); // Skia has assumed ownership of the struct.
}
return surface;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterSoftwareBackingStore2* software) {
const auto color_info = getSkColorInfo(software->pixel_format);
if (!color_info) {
return nullptr;
}
const auto image_info = SkImageInfo::Make(
SkISize::Make(config.size.width, config.size.height), *color_info);
struct Captures {
VoidCallback destruction_callback;
void* user_data;
};
auto captures = std::make_unique<Captures>();
captures->destruction_callback = software->destruction_callback;
captures->user_data = software->user_data;
auto release_proc = [](void* pixels, void* context) {
auto captures = reinterpret_cast<Captures*>(context);
if (captures->destruction_callback) {
captures->destruction_callback(captures->user_data);
}
};
auto surface =
SkSurfaces::WrapPixels(image_info, // image info
const_cast<void*>(software->allocation), // pixels
software->row_bytes, // row bytes
release_proc, // release proc
captures.release() // release context
);
if (!surface) {
FML_LOG(ERROR)
<< "Could not wrap embedder supplied software render buffer.";
if (software->destruction_callback) {
software->destruction_callback(software->user_data);
}
return nullptr;
}
return surface;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterMetalBackingStore* metal) {
#ifdef SHELL_ENABLE_METAL
GrMtlTextureInfo texture_info;
if (!metal->texture.texture) {
FML_LOG(ERROR) << "Embedder supplied null Metal texture.";
return nullptr;
}
sk_cfp<FlutterMetalTextureHandle> mtl_texture;
mtl_texture.retain(metal->texture.texture);
texture_info.fTexture = mtl_texture;
GrBackendTexture backend_texture(config.size.width, //
config.size.height, //
GrMipMapped::kNo, //
texture_info //
);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendTexture(
context, // context
backend_texture, // back-end texture
kTopLeft_GrSurfaceOrigin, // surface origin
// TODO(dnfield): Update this when embedders support MSAA, see
// https://github.com/flutter/flutter/issues/100392
1, // sample count
kBGRA_8888_SkColorType, // color type
nullptr, // color space
&surface_properties, // surface properties
static_cast<SkSurfaces::TextureReleaseProc>(
metal->texture.destruction_callback), // release proc
metal->texture.user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied Metal render texture.";
return nullptr;
}
return surface;
#else
return nullptr;
#endif
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterVulkanBackingStore* vulkan) {
#ifdef SHELL_ENABLE_VULKAN
if (!vulkan->image) {
FML_LOG(ERROR) << "Embedder supplied null Vulkan image.";
return nullptr;
}
GrVkImageInfo image_info = {
.fImage = reinterpret_cast<VkImage>(vulkan->image->image),
.fImageTiling = VK_IMAGE_TILING_OPTIMAL,
.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.fFormat = static_cast<VkFormat>(vulkan->image->format),
.fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
.fSampleCount = 1,
.fLevelCount = 1,
};
GrBackendTexture backend_texture(config.size.width, //
config.size.height, //
image_info //
);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendTexture(
context, // context
backend_texture, // back-end texture
kTopLeft_GrSurfaceOrigin, // surface origin
1, // sample count
flutter::GPUSurfaceVulkan::ColorTypeFromFormat(
static_cast<VkFormat>(vulkan->image->format)), // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurfaces::TextureReleaseProc>(
vulkan->destruction_callback), // release proc
vulkan->user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied Vulkan render texture.";
return nullptr;
}
return surface;
#else
return nullptr;
#endif
}
static std::unique_ptr<flutter::EmbedderRenderTarget>
CreateEmbedderRenderTarget(const FlutterCompositor* compositor,
const FlutterBackingStoreConfig& config,
GrDirectContext* context) {
FlutterBackingStore backing_store = {};
backing_store.struct_size = sizeof(backing_store);
// Safe access checks on the compositor struct have been performed in
// InferExternalViewEmbedderFromArgs and are not necessary here.
auto c_create_callback = compositor->create_backing_store_callback;
auto c_collect_callback = compositor->collect_backing_store_callback;
{
TRACE_EVENT0("flutter", "FlutterCompositorCreateBackingStore");
if (!c_create_callback(&config, &backing_store, compositor->user_data)) {
FML_LOG(ERROR) << "Could not create the embedder backing store.";
return nullptr;
}
}
if (backing_store.struct_size != sizeof(backing_store)) {
FML_LOG(ERROR) << "Embedder modified the backing store struct size.";
return nullptr;
}
// In case we return early without creating an embedder render target, the
// embedder has still given us ownership of its baton which we must return
// back to it. If this method is successful, the closure is released when the
// render target is eventually released.
fml::ScopedCleanupClosure collect_callback(
[c_collect_callback, backing_store, user_data = compositor->user_data]() {
TRACE_EVENT0("flutter", "FlutterCompositorCollectBackingStore");
c_collect_callback(&backing_store, user_data);
});
// No safe access checks on the renderer are necessary since we allocated
// the struct.
sk_sp<SkSurface> render_surface;
switch (backing_store.type) {
case kFlutterBackingStoreTypeOpenGL:
switch (backing_store.open_gl.type) {
case kFlutterOpenGLTargetTypeTexture:
render_surface = MakeSkSurfaceFromBackingStore(
context, config, &backing_store.open_gl.texture);
break;
case kFlutterOpenGLTargetTypeFramebuffer:
render_surface = MakeSkSurfaceFromBackingStore(
context, config, &backing_store.open_gl.framebuffer);
break;
}
break;
case kFlutterBackingStoreTypeSoftware:
render_surface = MakeSkSurfaceFromBackingStore(context, config,
&backing_store.software);
break;
case kFlutterBackingStoreTypeSoftware2:
render_surface = MakeSkSurfaceFromBackingStore(context, config,
&backing_store.software2);
break;
case kFlutterBackingStoreTypeMetal:
render_surface =
MakeSkSurfaceFromBackingStore(context, config, &backing_store.metal);
break;
case kFlutterBackingStoreTypeVulkan:
render_surface =
MakeSkSurfaceFromBackingStore(context, config, &backing_store.vulkan);
break;
};
if (!render_surface) {
FML_LOG(ERROR) << "Could not create a surface from an embedder provided "
"render target.";
return nullptr;
}
return std::make_unique<flutter::EmbedderRenderTarget>(
backing_store, std::move(render_surface), collect_callback.Release());
}
static std::pair<std::unique_ptr<flutter::EmbedderExternalViewEmbedder>,
bool /* halt engine launch if true */>
InferExternalViewEmbedderFromArgs(const FlutterCompositor* compositor) {
if (compositor == nullptr) {
return {nullptr, false};
}
auto c_create_callback =
SAFE_ACCESS(compositor, create_backing_store_callback, nullptr);
auto c_collect_callback =
SAFE_ACCESS(compositor, collect_backing_store_callback, nullptr);
auto c_present_callback =
SAFE_ACCESS(compositor, present_layers_callback, nullptr);
bool avoid_backing_store_cache =
SAFE_ACCESS(compositor, avoid_backing_store_cache, false);
// Make sure the required callbacks are present
if (!c_create_callback || !c_collect_callback || !c_present_callback) {
FML_LOG(ERROR) << "Required compositor callbacks absent.";
return {nullptr, true};
}
FlutterCompositor captured_compositor = *compositor;
flutter::EmbedderExternalViewEmbedder::CreateRenderTargetCallback
create_render_target_callback =
[captured_compositor](GrDirectContext* context, const auto& config) {
return CreateEmbedderRenderTarget(&captured_compositor, config,
context);
};
flutter::EmbedderExternalViewEmbedder::PresentCallback present_callback =
[c_present_callback,
user_data = compositor->user_data](const auto& layers) {
TRACE_EVENT0("flutter", "FlutterCompositorPresentLayers");
return c_present_callback(
const_cast<const FlutterLayer**>(layers.data()), layers.size(),
user_data);
};
return {std::make_unique<flutter::EmbedderExternalViewEmbedder>(
avoid_backing_store_cache, create_render_target_callback,
present_callback),
false};
}
struct _FlutterPlatformMessageResponseHandle {
std::unique_ptr<flutter::PlatformMessage> message;
};
struct LoadedElfDeleter {
void operator()(Dart_LoadedElf* elf) {
if (elf) {
::Dart_UnloadELF(elf);
}
}
};
using UniqueLoadedElf = std::unique_ptr<Dart_LoadedElf, LoadedElfDeleter>;
struct _FlutterEngineAOTData {
UniqueLoadedElf loaded_elf = nullptr;
const uint8_t* vm_snapshot_data = nullptr;
const uint8_t* vm_snapshot_instrs = nullptr;
const uint8_t* vm_isolate_data = nullptr;
const uint8_t* vm_isolate_instrs = nullptr;
};
FlutterEngineResult FlutterEngineCreateAOTData(
const FlutterEngineAOTDataSource* source,
FlutterEngineAOTData* data_out) {
if (!flutter::DartVM::IsRunningPrecompiledCode()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"AOT data can only be created in AOT mode.");
} else if (!source) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null source specified.");
} else if (!data_out) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null data_out specified.");
}
switch (source->type) {
case kFlutterEngineAOTDataSourceTypeElfPath: {
if (!source->elf_path || !fml::IsFile(source->elf_path)) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Invalid ELF path specified.");
}
auto aot_data = std::make_unique<_FlutterEngineAOTData>();
const char* error = nullptr;
#if OS_FUCHSIA
// TODO(gw280): https://github.com/flutter/flutter/issues/50285
// Dart doesn't implement Dart_LoadELF on Fuchsia
Dart_LoadedElf* loaded_elf = nullptr;
#else
Dart_LoadedElf* loaded_elf = Dart_LoadELF(
source->elf_path, // file path
0, // file offset
&error, // error (out)
&aot_data->vm_snapshot_data, // vm snapshot data (out)
&aot_data->vm_snapshot_instrs, // vm snapshot instr (out)
&aot_data->vm_isolate_data, // vm isolate data (out)
&aot_data->vm_isolate_instrs // vm isolate instr (out)
);
#endif
if (loaded_elf == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, error);
}
aot_data->loaded_elf.reset(loaded_elf);
*data_out = aot_data.release();
return kSuccess;
}
}
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Invalid FlutterEngineAOTDataSourceType type specified.");
}
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data) {
if (!data) {
// Deleting a null object should be a no-op.
return kSuccess;
}
// Created in a unique pointer in `FlutterEngineCreateAOTData`.
delete data;
return kSuccess;
}
// Constructs appropriate mapping callbacks if JIT snapshot locations have been
// explictly specified.
void PopulateJITSnapshotMappingCallbacks(const FlutterProjectArgs* args,
flutter::Settings& settings) {
auto make_mapping_callback = [](const char* path, bool executable) {
return [path, executable]() {
if (executable) {
return fml::FileMapping::CreateReadExecute(path);
} else {
return fml::FileMapping::CreateReadOnly(path);
}
};
};
// Users are allowed to specify only certain snapshots if they so desire.
if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) != nullptr) {
settings.vm_snapshot_data = make_mapping_callback(
reinterpret_cast<const char*>(args->vm_snapshot_data), false);
}
if (SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) != nullptr) {
settings.vm_snapshot_instr = make_mapping_callback(
reinterpret_cast<const char*>(args->vm_snapshot_instructions), true);
}
if (SAFE_ACCESS(args, isolate_snapshot_data, nullptr) != nullptr) {
settings.isolate_snapshot_data = make_mapping_callback(
reinterpret_cast<const char*>(args->isolate_snapshot_data), false);
}
if (SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr) != nullptr) {
settings.isolate_snapshot_instr = make_mapping_callback(
reinterpret_cast<const char*>(args->isolate_snapshot_instructions),
true);
}
#if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
settings.dart_library_sources_kernel = []() {
return std::make_unique<fml::NonOwnedMapping>(kPlatformStrongDill,
kPlatformStrongDillSize);
};
#endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE ==
// FLUTTER_RUNTIME_MODE_DEBUG)
}
void PopulateAOTSnapshotMappingCallbacks(
const FlutterProjectArgs* args,
flutter::Settings& settings) { // NOLINT(google-runtime-references)
// There are no ownership concerns here as all mappings are owned by the
// embedder and not the engine.
auto make_mapping_callback = [](const uint8_t* mapping, size_t size) {
return [mapping, size]() {
return std::make_unique<fml::NonOwnedMapping>(mapping, size);
};
};
if (SAFE_ACCESS(args, aot_data, nullptr) != nullptr) {
settings.vm_snapshot_data =
make_mapping_callback(args->aot_data->vm_snapshot_data, 0);
settings.vm_snapshot_instr =
make_mapping_callback(args->aot_data->vm_snapshot_instrs, 0);
settings.isolate_snapshot_data =
make_mapping_callback(args->aot_data->vm_isolate_data, 0);
settings.isolate_snapshot_instr =
make_mapping_callback(args->aot_data->vm_isolate_instrs, 0);
}
if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) != nullptr) {
settings.vm_snapshot_data = make_mapping_callback(
args->vm_snapshot_data, SAFE_ACCESS(args, vm_snapshot_data_size, 0));
}
if (SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) != nullptr) {
settings.vm_snapshot_instr = make_mapping_callback(
args->vm_snapshot_instructions,
SAFE_ACCESS(args, vm_snapshot_instructions_size, 0));
}
if (SAFE_ACCESS(args, isolate_snapshot_data, nullptr) != nullptr) {
settings.isolate_snapshot_data =
make_mapping_callback(args->isolate_snapshot_data,
SAFE_ACCESS(args, isolate_snapshot_data_size, 0));
}
if (SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr) != nullptr) {
settings.isolate_snapshot_instr = make_mapping_callback(
args->isolate_snapshot_instructions,
SAFE_ACCESS(args, isolate_snapshot_instructions_size, 0));
}
}
// Translates engine semantic nodes to embedder semantic nodes.
FlutterSemanticsNode CreateEmbedderSemanticsNode(
const flutter::SemanticsNode& node) {
SkMatrix transform = node.transform.asM33();
FlutterTransformation flutter_transform{
transform.get(SkMatrix::kMScaleX), transform.get(SkMatrix::kMSkewX),
transform.get(SkMatrix::kMTransX), transform.get(SkMatrix::kMSkewY),
transform.get(SkMatrix::kMScaleY), transform.get(SkMatrix::kMTransY),
transform.get(SkMatrix::kMPersp0), transform.get(SkMatrix::kMPersp1),
transform.get(SkMatrix::kMPersp2)};
// Do not add new members to FlutterSemanticsNode.
// This would break the forward compatibility of FlutterSemanticsUpdate.
// All new members must be added to FlutterSemanticsNode2 instead.
return {
sizeof(FlutterSemanticsNode),
node.id,
static_cast<FlutterSemanticsFlag>(node.flags),
static_cast<FlutterSemanticsAction>(node.actions),
node.textSelectionBase,
node.textSelectionExtent,
node.scrollChildren,
node.scrollIndex,
node.scrollPosition,
node.scrollExtentMax,
node.scrollExtentMin,
node.elevation,
node.thickness,
node.label.c_str(),
node.hint.c_str(),
node.value.c_str(),
node.increasedValue.c_str(),
node.decreasedValue.c_str(),
static_cast<FlutterTextDirection>(node.textDirection),
FlutterRect{node.rect.fLeft, node.rect.fTop, node.rect.fRight,
node.rect.fBottom},
flutter_transform,
node.childrenInTraversalOrder.size(),
node.childrenInTraversalOrder.data(),
node.childrenInHitTestOrder.data(),
node.customAccessibilityActions.size(),
node.customAccessibilityActions.data(),
node.platformViewId,
node.tooltip.c_str(),
};
}
// Translates engine semantic nodes to embedder semantic nodes.
FlutterSemanticsNode2 CreateEmbedderSemanticsNode2(
const flutter::SemanticsNode& node) {
SkMatrix transform = node.transform.asM33();
FlutterTransformation flutter_transform{
transform.get(SkMatrix::kMScaleX), transform.get(SkMatrix::kMSkewX),
transform.get(SkMatrix::kMTransX), transform.get(SkMatrix::kMSkewY),
transform.get(SkMatrix::kMScaleY), transform.get(SkMatrix::kMTransY),
transform.get(SkMatrix::kMPersp0), transform.get(SkMatrix::kMPersp1),
transform.get(SkMatrix::kMPersp2)};
return {
sizeof(FlutterSemanticsNode2),
node.id,
static_cast<FlutterSemanticsFlag>(node.flags),
static_cast<FlutterSemanticsAction>(node.actions),
node.textSelectionBase,
node.textSelectionExtent,
node.scrollChildren,
node.scrollIndex,
node.scrollPosition,
node.scrollExtentMax,
node.scrollExtentMin,
node.elevation,
node.thickness,
node.label.c_str(),
node.hint.c_str(),
node.value.c_str(),
node.increasedValue.c_str(),
node.decreasedValue.c_str(),
static_cast<FlutterTextDirection>(node.textDirection),
FlutterRect{node.rect.fLeft, node.rect.fTop, node.rect.fRight,
node.rect.fBottom},
flutter_transform,
node.childrenInTraversalOrder.size(),
node.childrenInTraversalOrder.data(),
node.childrenInHitTestOrder.data(),
node.customAccessibilityActions.size(),
node.customAccessibilityActions.data(),
node.platformViewId,
node.tooltip.c_str(),
};
}
// Translates engine semantic custom actions to embedder semantic custom
// actions.
FlutterSemanticsCustomAction CreateEmbedderSemanticsCustomAction(
const flutter::CustomAccessibilityAction& action) {
// Do not add new members to FlutterSemanticsCustomAction.
// This would break the forward compatibility of FlutterSemanticsUpdate.
// All new members must be added to FlutterSemanticsCustomAction2 instead.
return {
sizeof(FlutterSemanticsCustomAction),
action.id,
static_cast<FlutterSemanticsAction>(action.overrideId),
action.label.c_str(),
action.hint.c_str(),
};
}
// Translates engine semantic custom actions to embedder semantic custom
// actions.
FlutterSemanticsCustomAction2 CreateEmbedderSemanticsCustomAction2(
const flutter::CustomAccessibilityAction& action) {
return {
sizeof(FlutterSemanticsCustomAction2),
action.id,
static_cast<FlutterSemanticsAction>(action.overrideId),
action.label.c_str(),
action.hint.c_str(),
};
}
// Create a callback to notify the embedder of semantic updates
// using the deprecated embedder callback 'update_semantics_callback'.
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
CreateNewEmbedderSemanticsUpdateCallback(
FlutterUpdateSemanticsCallback update_semantics_callback,
void* user_data) {
return [update_semantics_callback, user_data](
const flutter::SemanticsNodeUpdates& nodes,
const flutter::CustomAccessibilityActionUpdates& actions) {
std::vector<FlutterSemanticsNode> embedder_nodes;
for (const auto& value : nodes) {
embedder_nodes.push_back(CreateEmbedderSemanticsNode(value.second));
}
std::vector<FlutterSemanticsCustomAction> embedder_custom_actions;
for (const auto& value : actions) {
embedder_custom_actions.push_back(
CreateEmbedderSemanticsCustomAction(value.second));
}
FlutterSemanticsUpdate update{
.struct_size = sizeof(FlutterSemanticsUpdate),
.nodes_count = embedder_nodes.size(),
.nodes = embedder_nodes.data(),
.custom_actions_count = embedder_custom_actions.size(),
.custom_actions = embedder_custom_actions.data(),
};
update_semantics_callback(&update, user_data);
};
}
// Create a callback to notify the embedder of semantic updates
// using the new embedder callback 'update_semantics_callback2'.
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
CreateNewEmbedderSemanticsUpdateCallback2(
FlutterUpdateSemanticsCallback2 update_semantics_callback,
void* user_data) {
return [update_semantics_callback, user_data](
const flutter::SemanticsNodeUpdates& nodes,
const flutter::CustomAccessibilityActionUpdates& actions) {
std::vector<FlutterSemanticsNode2> embedder_nodes;
std::vector<FlutterSemanticsCustomAction2> embedder_custom_actions;
embedder_nodes.reserve(nodes.size());
embedder_custom_actions.reserve(actions.size());
for (const auto& value : nodes) {
embedder_nodes.push_back(CreateEmbedderSemanticsNode2(value.second));
}
for (const auto& value : actions) {
embedder_custom_actions.push_back(
CreateEmbedderSemanticsCustomAction2(value.second));
}
// Provide the embedder an array of pointers to maintain full forward and
// backward compatibility even if new members are added to semantic structs.
std::vector<FlutterSemanticsNode2*> embedder_node_pointers;
std::vector<FlutterSemanticsCustomAction2*> embedder_custom_action_pointers;
embedder_node_pointers.reserve(embedder_nodes.size());
embedder_custom_action_pointers.reserve(embedder_custom_actions.size());
for (auto& node : embedder_nodes) {
embedder_node_pointers.push_back(&node);
}
for (auto& action : embedder_custom_actions) {
embedder_custom_action_pointers.push_back(&action);
}
FlutterSemanticsUpdate2 update{
.struct_size = sizeof(FlutterSemanticsUpdate2),
.node_count = embedder_node_pointers.size(),
.nodes = embedder_node_pointers.data(),
.custom_action_count = embedder_custom_action_pointers.size(),
.custom_actions = embedder_custom_action_pointers.data(),
};
update_semantics_callback(&update, user_data);
};
}
// Create a callback to notify the embedder of semantic updates
// using the legacy embedder callbacks 'update_semantics_node_callback' and
// 'update_semantics_custom_action_callback'.
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
CreateLegacyEmbedderSemanticsUpdateCallback(
FlutterUpdateSemanticsNodeCallback update_semantics_node_callback,
FlutterUpdateSemanticsCustomActionCallback
update_semantics_custom_action_callback,
void* user_data) {
return [update_semantics_node_callback,
update_semantics_custom_action_callback,
user_data](const flutter::SemanticsNodeUpdates& nodes,
const flutter::CustomAccessibilityActionUpdates& actions) {
// First, queue all node and custom action updates.
if (update_semantics_node_callback != nullptr) {
for (const auto& value : nodes) {
const FlutterSemanticsNode embedder_node =
CreateEmbedderSemanticsNode(value.second);
update_semantics_node_callback(&embedder_node, user_data);
}
}
if (update_semantics_custom_action_callback != nullptr) {
for (const auto& value : actions) {
const FlutterSemanticsCustomAction embedder_action =
CreateEmbedderSemanticsCustomAction(value.second);
update_semantics_custom_action_callback(&embedder_action, user_data);
}
}
// Second, mark node and action batches completed now that all
// updates are queued.
if (update_semantics_node_callback != nullptr) {
const FlutterSemanticsNode batch_end_sentinel = {
sizeof(FlutterSemanticsNode),
kFlutterSemanticsNodeIdBatchEnd,
};
update_semantics_node_callback(&batch_end_sentinel, user_data);
}
if (update_semantics_custom_action_callback != nullptr) {
const FlutterSemanticsCustomAction batch_end_sentinel = {
sizeof(FlutterSemanticsCustomAction),
kFlutterSemanticsCustomActionIdBatchEnd,
};
update_semantics_custom_action_callback(&batch_end_sentinel, user_data);
}
};
}
// Creates a callback that receives semantic updates from the engine
// and notifies the embedder's callback(s). Returns null if the embedder
// did not register any callbacks.
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
CreateEmbedderSemanticsUpdateCallback(const FlutterProjectArgs* args,
void* user_data) {
// The embedder can register the new callback, or the legacy callbacks, or
// nothing at all. Handle the case where the embedder registered the 'new'
// callback.
if (SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr) {
return CreateNewEmbedderSemanticsUpdateCallback2(
args->update_semantics_callback2, user_data);
}
if (SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr) {
return CreateNewEmbedderSemanticsUpdateCallback(
args->update_semantics_callback, user_data);
}
// Handle the case where the embedder registered 'legacy' callbacks.
FlutterUpdateSemanticsNodeCallback update_semantics_node_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr) {
update_semantics_node_callback = args->update_semantics_node_callback;
}
FlutterUpdateSemanticsCustomActionCallback
update_semantics_custom_action_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) !=
nullptr) {
update_semantics_custom_action_callback =
args->update_semantics_custom_action_callback;
}
if (update_semantics_node_callback != nullptr ||
update_semantics_custom_action_callback != nullptr) {
return CreateLegacyEmbedderSemanticsUpdateCallback(
update_semantics_node_callback, update_semantics_custom_action_callback,
user_data);
}
// Handle the case where the embedder registered no callbacks.
return nullptr;
}
FlutterEngineResult FlutterEngineRun(size_t version,
const FlutterRendererConfig* config,
const FlutterProjectArgs* args,
void* user_data,
FLUTTER_API_SYMBOL(FlutterEngine) *
engine_out) {
auto result =
FlutterEngineInitialize(version, config, args, user_data, engine_out);
if (result != kSuccess) {
return result;
}
return FlutterEngineRunInitialized(*engine_out);
}
FlutterEngineResult FlutterEngineInitialize(size_t version,
const FlutterRendererConfig* config,
const FlutterProjectArgs* args,
void* user_data,
FLUTTER_API_SYMBOL(FlutterEngine) *
engine_out) {
// Step 0: Figure out arguments for shell creation.
if (version != FLUTTER_ENGINE_VERSION) {
return LOG_EMBEDDER_ERROR(
kInvalidLibraryVersion,
"Flutter embedder version mismatch. There has been a breaking change. "
"Please consult the changelog and update the embedder.");
}
if (engine_out == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"The engine out parameter was missing.");
}
if (args == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"The Flutter project arguments were missing.");
}
if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"The assets path in the Flutter project arguments was missing.");
}
if (SAFE_ACCESS(args, main_path__unused__, nullptr) != nullptr) {
FML_LOG(WARNING)
<< "FlutterProjectArgs.main_path is deprecated and should be set null.";
}
if (SAFE_ACCESS(args, packages_path__unused__, nullptr) != nullptr) {
FML_LOG(WARNING) << "FlutterProjectArgs.packages_path is deprecated and "
"should be set null.";
}
if (!IsRendererValid(config)) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"The renderer configuration was invalid.");
}
std::string icu_data_path;
if (SAFE_ACCESS(args, icu_data_path, nullptr) != nullptr) {
icu_data_path = SAFE_ACCESS(args, icu_data_path, nullptr);
}
if (SAFE_ACCESS(args, persistent_cache_path, nullptr) != nullptr) {
std::string persistent_cache_path =
SAFE_ACCESS(args, persistent_cache_path, nullptr);
flutter::PersistentCache::SetCacheDirectoryPath(persistent_cache_path);
}
if (SAFE_ACCESS(args, is_persistent_cache_read_only, false)) {
flutter::PersistentCache::gIsReadOnly = true;
}
fml::CommandLine command_line;
if (SAFE_ACCESS(args, command_line_argc, 0) != 0 &&
SAFE_ACCESS(args, command_line_argv, nullptr) != nullptr) {
command_line = fml::CommandLineFromArgcArgv(
SAFE_ACCESS(args, command_line_argc, 0),
SAFE_ACCESS(args, command_line_argv, nullptr));
}
flutter::Settings settings = flutter::SettingsFromCommandLine(command_line);
if (SAFE_ACCESS(args, aot_data, nullptr)) {
if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) ||
SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) ||
SAFE_ACCESS(args, isolate_snapshot_data, nullptr) ||
SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr)) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Multiple AOT sources specified. Embedders should provide either "
"*_snapshot_* buffers or aot_data, not both.");
}
}
if (flutter::DartVM::IsRunningPrecompiledCode()) {
PopulateAOTSnapshotMappingCallbacks(args, settings);
} else {
PopulateJITSnapshotMappingCallbacks(args, settings);
}
settings.icu_data_path = icu_data_path;
settings.assets_path = args->assets_path;
settings.leak_vm = !SAFE_ACCESS(args, shutdown_dart_vm_when_done, false);
settings.old_gen_heap_size = SAFE_ACCESS(args, dart_old_gen_heap_size, -1);
if (!flutter::DartVM::IsRunningPrecompiledCode()) {
// Verify the assets path contains Dart 2 kernel assets.
const std::string kApplicationKernelSnapshotFileName = "kernel_blob.bin";
std::string application_kernel_path = fml::paths::JoinPaths(
{settings.assets_path, kApplicationKernelSnapshotFileName});
if (!fml::IsFile(application_kernel_path)) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Not running in AOT mode but could not resolve the kernel binary.");
}
settings.application_kernel_asset = kApplicationKernelSnapshotFileName;
}
settings.task_observer_add = [](intptr_t key, const fml::closure& callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback);
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
};
if (SAFE_ACCESS(args, root_isolate_create_callback, nullptr) != nullptr) {
VoidCallback callback =
SAFE_ACCESS(args, root_isolate_create_callback, nullptr);
settings.root_isolate_create_callback =
[callback, user_data](const auto& isolate) { callback(user_data); };
}
if (SAFE_ACCESS(args, log_message_callback, nullptr) != nullptr) {
FlutterLogMessageCallback callback =
SAFE_ACCESS(args, log_message_callback, nullptr);
settings.log_message_callback = [callback, user_data](
const std::string& tag,
const std::string& message) {
callback(tag.c_str(), message.c_str(), user_data);
};
}
if (SAFE_ACCESS(args, log_tag, nullptr) != nullptr) {
settings.log_tag = SAFE_ACCESS(args, log_tag, nullptr);
}
bool has_update_semantics_2_callback =
SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr;
bool has_update_semantics_callback =
SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr;
bool has_legacy_update_semantics_callback =
SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr ||
SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) !=
nullptr;
int semantic_callback_count = (has_update_semantics_2_callback ? 1 : 0) +
(has_update_semantics_callback ? 1 : 0) +
(has_legacy_update_semantics_callback ? 1 : 0);
if (semantic_callback_count > 1) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Multiple semantics update callbacks provided. "
"Embedders should provide either `update_semantics_callback2`, "
"`update_semantics_callback`, or both "
"`update_semantics_node_callback` and "
"`update_semantics_custom_action_callback`.");
}
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
update_semantics_callback =
CreateEmbedderSemanticsUpdateCallback(args, user_data);
flutter::PlatformViewEmbedder::PlatformMessageResponseCallback
platform_message_response_callback = nullptr;
if (SAFE_ACCESS(args, platform_message_callback, nullptr) != nullptr) {
platform_message_response_callback =
[ptr = args->platform_message_callback,
user_data](std::unique_ptr<flutter::PlatformMessage> message) {
auto handle = new FlutterPlatformMessageResponseHandle();
const FlutterPlatformMessage incoming_message = {
sizeof(FlutterPlatformMessage), // struct_size
message->channel().c_str(), // channel
message->data().GetMapping(), // message
message->data().GetSize(), // message_size
handle, // response_handle
};
handle->message = std::move(message);
return ptr(&incoming_message, user_data);
};
}
flutter::VsyncWaiterEmbedder::VsyncCallback vsync_callback = nullptr;
if (SAFE_ACCESS(args, vsync_callback, nullptr) != nullptr) {
vsync_callback = [ptr = args->vsync_callback, user_data](intptr_t baton) {
return ptr(user_data, baton);
};
}
flutter::PlatformViewEmbedder::ComputePlatformResolvedLocaleCallback
compute_platform_resolved_locale_callback = nullptr;
if (SAFE_ACCESS(args, compute_platform_resolved_locale_callback, nullptr) !=
nullptr) {
compute_platform_resolved_locale_callback =
[ptr = args->compute_platform_resolved_locale_callback](
const std::vector<std::string>& supported_locales_data) {
const size_t number_of_strings_per_locale = 3;
size_t locale_count =
supported_locales_data.size() / number_of_strings_per_locale;
std::vector<FlutterLocale> supported_locales;
std::vector<const FlutterLocale*> supported_locales_ptr;
for (size_t i = 0; i < locale_count; ++i) {
supported_locales.push_back(
{.struct_size = sizeof(FlutterLocale),
.language_code =
supported_locales_data[i * number_of_strings_per_locale +
0]
.c_str(),
.country_code =
supported_locales_data[i * number_of_strings_per_locale +
1]
.c_str(),
.script_code =
supported_locales_data[i * number_of_strings_per_locale +
2]
.c_str(),
.variant_code = nullptr});
supported_locales_ptr.push_back(&supported_locales[i]);
}
const FlutterLocale* result =
ptr(supported_locales_ptr.data(), locale_count);
std::unique_ptr<std::vector<std::string>> out =
std::make_unique<std::vector<std::string>>();
if (result) {
std::string language_code(SAFE_ACCESS(result, language_code, ""));
if (language_code != "") {
out->push_back(language_code);
out->emplace_back(SAFE_ACCESS(result, country_code, ""));
out->emplace_back(SAFE_ACCESS(result, script_code, ""));
}
}
return out;
};
}
flutter::PlatformViewEmbedder::OnPreEngineRestartCallback
on_pre_engine_restart_callback = nullptr;
if (SAFE_ACCESS(args, on_pre_engine_restart_callback, nullptr) != nullptr) {
on_pre_engine_restart_callback = [ptr =
args->on_pre_engine_restart_callback,
user_data]() { return ptr(user_data); };
}
auto external_view_embedder_result =
InferExternalViewEmbedderFromArgs(SAFE_ACCESS(args, compositor, nullptr));
if (external_view_embedder_result.second) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Compositor arguments were invalid.");
}
flutter::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table =
{
update_semantics_callback, //
platform_message_response_callback, //
vsync_callback, //
compute_platform_resolved_locale_callback, //
on_pre_engine_restart_callback, //
};
auto on_create_platform_view = InferPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder_result.first));
if (!on_create_platform_view) {
return LOG_EMBEDDER_ERROR(
kInternalInconsistency,
"Could not infer platform view creation callback.");
}
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
[](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell);
};
using ExternalTextureResolver = flutter::EmbedderExternalTextureResolver;
std::unique_ptr<ExternalTextureResolver> external_texture_resolver;
external_texture_resolver = std::make_unique<ExternalTextureResolver>();
#ifdef SHELL_ENABLE_GL
flutter::EmbedderExternalTextureGL::ExternalTextureCallback
external_texture_callback;
if (config->type == kOpenGL) {
const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl;
if (SAFE_ACCESS(open_gl_config, gl_external_texture_frame_callback,
nullptr) != nullptr) {
external_texture_callback =
[ptr = open_gl_config->gl_external_texture_frame_callback, user_data](
int64_t texture_identifier, size_t width,
size_t height) -> std::unique_ptr<FlutterOpenGLTexture> {
std::unique_ptr<FlutterOpenGLTexture> texture =
std::make_unique<FlutterOpenGLTexture>();
if (!ptr(user_data, texture_identifier, width, height, texture.get())) {
return nullptr;
}
return texture;
};
external_texture_resolver =
std::make_unique<ExternalTextureResolver>(external_texture_callback);
}
}
#endif
#ifdef SHELL_ENABLE_METAL
flutter::EmbedderExternalTextureMetal::ExternalTextureCallback
external_texture_metal_callback;
if (config->type == kMetal) {
const FlutterMetalRendererConfig* metal_config = &config->metal;
if (SAFE_ACCESS(metal_config, external_texture_frame_callback, nullptr)) {
external_texture_metal_callback =
[ptr = metal_config->external_texture_frame_callback, user_data](
int64_t texture_identifier, size_t width,
size_t height) -> std::unique_ptr<FlutterMetalExternalTexture> {
std::unique_ptr<FlutterMetalExternalTexture> texture =
std::make_unique<FlutterMetalExternalTexture>();
texture->struct_size = sizeof(FlutterMetalExternalTexture);
if (!ptr(user_data, texture_identifier, width, height, texture.get())) {
return nullptr;
}
return texture;
};
external_texture_resolver = std::make_unique<ExternalTextureResolver>(
external_texture_metal_callback);
}
}
#endif
auto custom_task_runners = SAFE_ACCESS(args, custom_task_runners, nullptr);
auto thread_config_callback = [&custom_task_runners](
const fml::Thread::ThreadConfig& config) {
fml::Thread::SetCurrentThreadName(config);
if (!custom_task_runners || !custom_task_runners->thread_priority_setter) {
return;
}
FlutterThreadPriority priority = FlutterThreadPriority::kNormal;
switch (config.priority) {
case fml::Thread::ThreadPriority::BACKGROUND:
priority = FlutterThreadPriority::kBackground;
break;
case fml::Thread::ThreadPriority::NORMAL:
priority = FlutterThreadPriority::kNormal;
break;
case fml::Thread::ThreadPriority::DISPLAY:
priority = FlutterThreadPriority::kDisplay;
break;
case fml::Thread::ThreadPriority::RASTER:
priority = FlutterThreadPriority::kRaster;
break;
}
custom_task_runners->thread_priority_setter(priority);
};
auto thread_host =
flutter::EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost(
custom_task_runners, thread_config_callback);
if (!thread_host || !thread_host->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not set up or infer thread configuration "
"to run the Flutter engine on.");
}
auto task_runners = thread_host->GetTaskRunners();
if (!task_runners.IsValid()) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Task runner configuration was invalid.");
}
auto run_configuration =
flutter::RunConfiguration::InferFromSettings(settings);
if (SAFE_ACCESS(args, custom_dart_entrypoint, nullptr) != nullptr) {
auto dart_entrypoint = std::string{args->custom_dart_entrypoint};
if (!dart_entrypoint.empty()) {
run_configuration.SetEntrypoint(std::move(dart_entrypoint));
}
}
if (SAFE_ACCESS(args, dart_entrypoint_argc, 0) > 0) {
if (SAFE_ACCESS(args, dart_entrypoint_argv, nullptr) == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not determine Dart entrypoint arguments "
"as dart_entrypoint_argc "
"was set, but dart_entrypoint_argv was null.");
}
std::vector<std::string> arguments(args->dart_entrypoint_argc);
for (int i = 0; i < args->dart_entrypoint_argc; ++i) {
arguments[i] = std::string{args->dart_entrypoint_argv[i]};
}
run_configuration.SetEntrypointArgs(std::move(arguments));
}
if (!run_configuration.IsValid()) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Could not infer the Flutter project to run from given arguments.");
}
// Create the engine but don't launch the shell or run the root isolate.
auto embedder_engine = std::make_unique<flutter::EmbedderEngine>(
std::move(thread_host), //
std::move(task_runners), //
std::move(settings), //
std::move(run_configuration), //
on_create_platform_view, //
on_create_rasterizer, //
std::move(external_texture_resolver) //
);
// Release the ownership of the embedder engine to the caller.
*engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(
embedder_engine.release());
return kSuccess;
}
FlutterEngineResult FlutterEngineRunInitialized(
FLUTTER_API_SYMBOL(FlutterEngine) engine) {
if (!engine) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
// The engine must not already be running. Initialize may only be called
// once on an engine instance.
if (embedder_engine->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
// Step 1: Launch the shell.
if (!embedder_engine->LaunchShell()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not launch the engine using supplied "
"initialization arguments.");
}
// Step 2: Tell the platform view to initialize itself.
if (!embedder_engine->NotifyCreated()) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not create platform view components.");
}
// Step 3: Launch the root isolate.
if (!embedder_engine->RunRootIsolate()) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Could not run the root isolate of the Flutter application using the "
"project arguments specified.");
}
return kSuccess;
}
FLUTTER_EXPORT
FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine)
engine) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
embedder_engine->NotifyDestroyed();
embedder_engine->CollectShell();
return kSuccess;
}
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine)
engine) {
auto result = FlutterEngineDeinitialize(engine);
if (result != kSuccess) {
return result;
}
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
delete embedder_engine;
return kSuccess;
}
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterWindowMetricsEvent* flutter_metrics) {
if (engine == nullptr || flutter_metrics == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
flutter::ViewportMetrics metrics;
metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0);
metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0);
metrics.device_pixel_ratio = SAFE_ACCESS(flutter_metrics, pixel_ratio, 1.0);
metrics.physical_view_inset_top =
SAFE_ACCESS(flutter_metrics, physical_view_inset_top, 0.0);
metrics.physical_view_inset_right =
SAFE_ACCESS(flutter_metrics, physical_view_inset_right, 0.0);
metrics.physical_view_inset_bottom =
SAFE_ACCESS(flutter_metrics, physical_view_inset_bottom, 0.0);
metrics.physical_view_inset_left =
SAFE_ACCESS(flutter_metrics, physical_view_inset_left, 0.0);
if (metrics.device_pixel_ratio <= 0.0) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Device pixel ratio was invalid. It must be greater than zero.");
}
if (metrics.physical_view_inset_top < 0 ||
metrics.physical_view_inset_right < 0 ||
metrics.physical_view_inset_bottom < 0 ||
metrics.physical_view_inset_left < 0) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Physical view insets are invalid. They must be non-negative.");
}
if (metrics.physical_view_inset_top > metrics.physical_height ||
metrics.physical_view_inset_right > metrics.physical_width ||
metrics.physical_view_inset_bottom > metrics.physical_height ||
metrics.physical_view_inset_left > metrics.physical_width) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Physical view insets are invalid. They cannot "
"be greater than physical height or width.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetViewportMetrics(
metrics)
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments,
"Viewport metrics were invalid.");
}
// Returns the flutter::PointerData::Change for the given FlutterPointerPhase.
inline flutter::PointerData::Change ToPointerDataChange(
FlutterPointerPhase phase) {
switch (phase) {
case kCancel:
return flutter::PointerData::Change::kCancel;
case kUp:
return flutter::PointerData::Change::kUp;
case kDown:
return flutter::PointerData::Change::kDown;
case kMove:
return flutter::PointerData::Change::kMove;
case kAdd:
return flutter::PointerData::Change::kAdd;
case kRemove:
return flutter::PointerData::Change::kRemove;
case kHover:
return flutter::PointerData::Change::kHover;
case kPanZoomStart:
return flutter::PointerData::Change::kPanZoomStart;
case kPanZoomUpdate:
return flutter::PointerData::Change::kPanZoomUpdate;
case kPanZoomEnd:
return flutter::PointerData::Change::kPanZoomEnd;
}
return flutter::PointerData::Change::kCancel;
}
// Returns the flutter::PointerData::DeviceKind for the given
// FlutterPointerDeviceKind.
inline flutter::PointerData::DeviceKind ToPointerDataKind(
FlutterPointerDeviceKind device_kind) {
switch (device_kind) {
case kFlutterPointerDeviceKindMouse:
return flutter::PointerData::DeviceKind::kMouse;
case kFlutterPointerDeviceKindTouch:
return flutter::PointerData::DeviceKind::kTouch;
case kFlutterPointerDeviceKindStylus:
return flutter::PointerData::DeviceKind::kStylus;
case kFlutterPointerDeviceKindTrackpad:
return flutter::PointerData::DeviceKind::kTrackpad;
}
return flutter::PointerData::DeviceKind::kMouse;
}
// Returns the flutter::PointerData::SignalKind for the given
// FlutterPointerSignaKind.
inline flutter::PointerData::SignalKind ToPointerDataSignalKind(
FlutterPointerSignalKind kind) {
switch (kind) {
case kFlutterPointerSignalKindNone:
return flutter::PointerData::SignalKind::kNone;
case kFlutterPointerSignalKindScroll:
return flutter::PointerData::SignalKind::kScroll;
case kFlutterPointerSignalKindScrollInertiaCancel:
return flutter::PointerData::SignalKind::kScrollInertiaCancel;
case kFlutterPointerSignalKindScale:
return flutter::PointerData::SignalKind::kScale;
}
return flutter::PointerData::SignalKind::kNone;
}
// Returns the buttons to synthesize for a PointerData from a
// FlutterPointerEvent with no type or buttons set.
inline int64_t PointerDataButtonsForLegacyEvent(
flutter::PointerData::Change change) {
switch (change) {
case flutter::PointerData::Change::kDown:
case flutter::PointerData::Change::kMove:
// These kinds of change must have a non-zero `buttons`, otherwise
// gesture recognizers will ignore these events.
return flutter::kPointerButtonMousePrimary;
case flutter::PointerData::Change::kCancel:
case flutter::PointerData::Change::kAdd:
case flutter::PointerData::Change::kRemove:
case flutter::PointerData::Change::kHover:
case flutter::PointerData::Change::kUp:
case flutter::PointerData::Change::kPanZoomStart:
case flutter::PointerData::Change::kPanZoomUpdate:
case flutter::PointerData::Change::kPanZoomEnd:
return 0;
}
return 0;
}
FlutterEngineResult FlutterEngineSendPointerEvent(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterPointerEvent* pointers,
size_t events_count) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (pointers == nullptr || events_count == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid pointer events.");
}
auto packet = std::make_unique<flutter::PointerDataPacket>(events_count);
const FlutterPointerEvent* current = pointers;
for (size_t i = 0; i < events_count; ++i) {
flutter::PointerData pointer_data;
pointer_data.Clear();
// this is currely in use only on android embedding.
pointer_data.embedder_id = 0;
pointer_data.time_stamp = SAFE_ACCESS(current, timestamp, 0);
pointer_data.change = ToPointerDataChange(
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);
pointer_data.scroll_delta_y = SAFE_ACCESS(current, scroll_delta_y, 0.0);
FlutterPointerDeviceKind device_kind = SAFE_ACCESS(current, device_kind, 0);
// For backwards compatibility with embedders written before the device
// kind and buttons were exposed, if the device kind is not set treat it
// as a mouse, with a synthesized primary button state based on the phase.
if (device_kind == 0) {
pointer_data.kind = flutter::PointerData::DeviceKind::kMouse;
pointer_data.buttons =
PointerDataButtonsForLegacyEvent(pointer_data.change);
} else {
pointer_data.kind = ToPointerDataKind(device_kind);
if (pointer_data.kind == flutter::PointerData::DeviceKind::kTouch) {
// For touch events, set the button internally rather than requiring
// it at the API level, since it's a confusing construction to expose.
if (pointer_data.change == flutter::PointerData::Change::kDown ||
pointer_data.change == flutter::PointerData::Change::kMove) {
pointer_data.buttons = flutter::kPointerButtonTouchContact;
}
} else {
// Buttons use the same mask values, so pass them through directly.
pointer_data.buttons = SAFE_ACCESS(current, buttons, 0);
}
}
pointer_data.pan_x = SAFE_ACCESS(current, pan_x, 0.0);
pointer_data.pan_y = SAFE_ACCESS(current, pan_y, 0.0);
// Delta will be generated in pointer_data_packet_converter.cc.
pointer_data.pan_delta_x = 0.0;
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);
packet->SetPointerData(i, pointer_data);
current = reinterpret_cast<const FlutterPointerEvent*>(
reinterpret_cast<const uint8_t*>(current) + current->struct_size);
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->DispatchPointerDataPacket(std::move(packet))
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not dispatch pointer events to the "
"running Flutter application.");
}
static inline flutter::KeyEventType MapKeyEventType(
FlutterKeyEventType event_kind) {
switch (event_kind) {
case kFlutterKeyEventTypeUp:
return flutter::KeyEventType::kUp;
case kFlutterKeyEventTypeDown:
return flutter::KeyEventType::kDown;
case kFlutterKeyEventTypeRepeat:
return flutter::KeyEventType::kRepeat;
}
return flutter::KeyEventType::kUp;
}
// Send a platform message to the framework.
//
// The `data_callback` will be invoked with `user_data`, and must not be empty.
static FlutterEngineResult InternalSendPlatformMessage(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const char* channel,
const uint8_t* data,
size_t size,
FlutterDataCallback data_callback,
void* user_data) {
FlutterEngineResult result;
FlutterPlatformMessageResponseHandle* response_handle;
result = FlutterPlatformMessageCreateResponseHandle(
engine, data_callback, user_data, &response_handle);
if (result != kSuccess) {
return result;
}
const FlutterPlatformMessage message = {
sizeof(FlutterPlatformMessage), // struct_size
channel, // channel
data, // message
size, // message_size
response_handle, // response_handle
};
result = FlutterEngineSendPlatformMessage(engine, &message);
// Whether `SendPlatformMessage` succeeds or not, the response handle must be
// released.
FlutterEngineResult release_result =
FlutterPlatformMessageReleaseResponseHandle(engine, response_handle);
if (result != kSuccess) {
return result;
}
return release_result;
}
FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const FlutterKeyEvent* event,
FlutterKeyEventCallback callback,
void* user_data) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (event == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid key event.");
}
const char* character = SAFE_ACCESS(event, character, nullptr);
flutter::KeyData key_data;
key_data.Clear();
key_data.timestamp = static_cast<uint64_t>(SAFE_ACCESS(event, timestamp, 0));
key_data.type = MapKeyEventType(
SAFE_ACCESS(event, type, FlutterKeyEventType::kFlutterKeyEventTypeUp));
key_data.physical = SAFE_ACCESS(event, physical, 0);
key_data.logical = SAFE_ACCESS(event, logical, 0);
key_data.synthesized = SAFE_ACCESS(event, synthesized, false);
auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
struct MessageData {
FlutterKeyEventCallback callback;
void* user_data;
};
MessageData* message_data =
new MessageData{.callback = callback, .user_data = user_data};
return InternalSendPlatformMessage(
engine, kFlutterKeyDataChannel, packet->data().data(),
packet->data().size(),
[](const uint8_t* data, size_t size, void* user_data) {
auto message_data = std::unique_ptr<MessageData>(
reinterpret_cast<MessageData*>(user_data));
if (message_data->callback == nullptr) {
return;
}
bool handled = false;
if (size == 1) {
handled = *data != 0;
}
message_data->callback(handled, message_data->user_data);
},
message_data);
}
FlutterEngineResult FlutterEngineSendPlatformMessage(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterPlatformMessage* flutter_message) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (flutter_message == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid message argument.");
}
if (SAFE_ACCESS(flutter_message, channel, nullptr) == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments, "Message argument did not specify a valid channel.");
}
size_t message_size = SAFE_ACCESS(flutter_message, message_size, 0);
const uint8_t* message_data = SAFE_ACCESS(flutter_message, message, nullptr);
if (message_size != 0 && message_data == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Message size was non-zero but the message data was nullptr.");
}
const FlutterPlatformMessageResponseHandle* response_handle =
SAFE_ACCESS(flutter_message, response_handle, nullptr);
fml::RefPtr<flutter::PlatformMessageResponse> response;
if (response_handle && response_handle->message) {
response = response_handle->message->response();
}
std::unique_ptr<flutter::PlatformMessage> message;
if (message_size == 0) {
message = std::make_unique<flutter::PlatformMessage>(
flutter_message->channel, response);
} else {
message = std::make_unique<flutter::PlatformMessage>(
flutter_message->channel,
fml::MallocMapping::Copy(message_data, message_size), response);
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->SendPlatformMessage(std::move(message))
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not send a message to the running "
"Flutter application.");
}
FlutterEngineResult FlutterPlatformMessageCreateResponseHandle(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterDataCallback data_callback,
void* user_data,
FlutterPlatformMessageResponseHandle** response_out) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (data_callback == nullptr || response_out == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments, "Data callback or the response handle was invalid.");
}
flutter::EmbedderPlatformMessageResponse::Callback response_callback =
[user_data, data_callback](const uint8_t* data, size_t size) {
data_callback(data, size, user_data);
};
auto platform_task_runner = reinterpret_cast<flutter::EmbedderEngine*>(engine)
->GetTaskRunners()
.GetPlatformTaskRunner();
auto handle = new FlutterPlatformMessageResponseHandle();
handle->message = std::make_unique<flutter::PlatformMessage>(
"", // The channel is empty and unused as the response handle is going
// to referenced directly in the |FlutterEngineSendPlatformMessage|
// with the container message discarded.
fml::MakeRefCounted<flutter::EmbedderPlatformMessageResponse>(
std::move(platform_task_runner), response_callback));
*response_out = handle;
return kSuccess;
}
FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterPlatformMessageResponseHandle* response) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (response == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid response handle.");
}
delete response;
return kSuccess;
}
// Note: This can execute on any thread.
FlutterEngineResult FlutterEngineSendPlatformMessageResponse(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterPlatformMessageResponseHandle* handle,
const uint8_t* data,
size_t data_length) {
if (data_length != 0 && data == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Data size was non zero but the pointer to the data was null.");
}
auto response = handle->message->response();
if (response) {
if (data_length == 0) {
response->CompleteEmpty();
} else {
response->Complete(std::make_unique<fml::DataMapping>(
std::vector<uint8_t>({data, data + data_length})));
}
}
delete handle;
return kSuccess;
}
FlutterEngineResult __FlutterEngineFlushPendingTasksNow() {
fml::MessageLoop::GetCurrent().RunExpiredTasksNow();
return kSuccess;
}
FlutterEngineResult FlutterEngineRegisterExternalTexture(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
int64_t texture_identifier) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (texture_identifier == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Texture identifier was invalid.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->RegisterTexture(
texture_identifier)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not register the specified texture.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUnregisterExternalTexture(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
int64_t texture_identifier) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (texture_identifier == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Texture identifier was invalid.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->UnregisterTexture(
texture_identifier)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not un-register the specified texture.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
int64_t texture_identifier) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (texture_identifier == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid texture identifier.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->MarkTextureFrameAvailable(texture_identifier)) {
return LOG_EMBEDDER_ERROR(
kInternalInconsistency,
"Could not mark the texture frame as being available.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
bool enabled) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetSemanticsEnabled(
enabled)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not update semantics state.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterAccessibilityFeature flags) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->SetAccessibilityFeatures(flags)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not update accessibility features.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineDispatchSemanticsAction(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
uint64_t node_id,
FlutterSemanticsAction action,
const uint8_t* data,
size_t data_length) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
auto engine_action = static_cast<flutter::SemanticsAction>(action);
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->DispatchSemanticsAction(
node_id, engine_action,
fml::MallocMapping::Copy(data, data_length))) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not dispatch semantics action.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineOnVsync(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
intptr_t baton,
uint64_t frame_start_time_nanos,
uint64_t frame_target_time_nanos) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
TRACE_EVENT0("flutter", "FlutterEngineOnVsync");
auto start_time = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(frame_start_time_nanos));
auto target_time = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(frame_target_time_nanos));
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->OnVsyncEvent(
baton, start_time, target_time)) {
return LOG_EMBEDDER_ERROR(
kInternalInconsistency,
"Could not notify the running engine instance of a Vsync event.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineReloadSystemFonts(
FLUTTER_API_SYMBOL(FlutterEngine) engine) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
TRACE_EVENT0("flutter", "FlutterEngineReloadSystemFonts");
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->ReloadSystemFonts()) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not reload system fonts.");
}
return kSuccess;
}
void FlutterEngineTraceEventDurationBegin(const char* name) {
fml::tracing::TraceEvent0("flutter", name);
}
void FlutterEngineTraceEventDurationEnd(const char* name) {
fml::tracing::TraceEventEnd(name);
}
void FlutterEngineTraceEventInstant(const char* name) {
fml::tracing::TraceEventInstant0("flutter", name);
}
FlutterEngineResult FlutterEnginePostRenderThreadTask(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
VoidCallback callback,
void* baton) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (callback == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Render thread callback was null.");
}
auto task = [callback, baton]() { callback(baton); };
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->PostRenderThreadTask(task)
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not post the render thread task.");
}
uint64_t FlutterEngineGetCurrentTime() {
return fml::TimePoint::Now().ToEpochDelta().ToNanoseconds();
}
FlutterEngineResult FlutterEngineRunTask(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const FlutterTask* task) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->RunTask(task)
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not run the specified task.");
}
static bool DispatchJSONPlatformMessage(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const rapidjson::Document& document,
const std::string& channel_name) {
if (channel_name.empty()) {
return false;
}
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
if (!document.Accept(writer)) {
return false;
}
const char* message = buffer.GetString();
if (message == nullptr || buffer.GetSize() == 0) {
return false;
}
auto platform_message = std::make_unique<flutter::PlatformMessage>(
channel_name.c_str(), // channel
fml::MallocMapping::Copy(message,
buffer.GetSize()), // message
nullptr // response
);
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->SendPlatformMessage(std::move(platform_message));
}
FlutterEngineResult FlutterEngineUpdateLocales(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const FlutterLocale** locales,
size_t locales_count) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (locales_count == 0) {
return kSuccess;
}
if (locales == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "No locales were specified.");
}
rapidjson::Document document;
auto& allocator = document.GetAllocator();
document.SetObject();
document.AddMember("method", "setLocale", allocator);
rapidjson::Value args(rapidjson::kArrayType);
args.Reserve(locales_count * 4, allocator);
for (size_t i = 0; i < locales_count; ++i) {
const FlutterLocale* locale = locales[i];
const char* language_code_str = SAFE_ACCESS(locale, language_code, nullptr);
if (language_code_str == nullptr || ::strlen(language_code_str) == 0) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Language code is required but not present in FlutterLocale.");
}
const char* country_code_str = SAFE_ACCESS(locale, country_code, "");
const char* script_code_str = SAFE_ACCESS(locale, script_code, "");
const char* variant_code_str = SAFE_ACCESS(locale, variant_code, "");
rapidjson::Value language_code, country_code, script_code, variant_code;
language_code.SetString(language_code_str, allocator);
country_code.SetString(country_code_str ? country_code_str : "", allocator);
script_code.SetString(script_code_str ? script_code_str : "", allocator);
variant_code.SetString(variant_code_str ? variant_code_str : "", allocator);
// Required.
args.PushBack(language_code, allocator);
args.PushBack(country_code, allocator);
args.PushBack(script_code, allocator);
args.PushBack(variant_code, allocator);
}
document.AddMember("args", args, allocator);
return DispatchJSONPlatformMessage(engine, document, "flutter/localization")
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not send message to update locale of "
"a running Flutter application.");
}
bool FlutterEngineRunsAOTCompiledDartCode(void) {
return flutter::DartVM::IsRunningPrecompiledCode();
}
FlutterEngineResult FlutterEnginePostDartObject(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterEngineDartPort port,
const FlutterEngineDartObject* object) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine not running.");
}
if (port == ILLEGAL_PORT) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Attempted to post to an illegal port.");
}
if (object == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Invalid Dart object to post.");
}
Dart_CObject dart_object = {};
fml::ScopedCleanupClosure typed_data_finalizer;
switch (object->type) {
case kFlutterEngineDartObjectTypeNull:
dart_object.type = Dart_CObject_kNull;
break;
case kFlutterEngineDartObjectTypeBool:
dart_object.type = Dart_CObject_kBool;
dart_object.value.as_bool = object->bool_value;
break;
case kFlutterEngineDartObjectTypeInt32:
dart_object.type = Dart_CObject_kInt32;
dart_object.value.as_int32 = object->int32_value;
break;
case kFlutterEngineDartObjectTypeInt64:
dart_object.type = Dart_CObject_kInt64;
dart_object.value.as_int64 = object->int64_value;
break;
case kFlutterEngineDartObjectTypeDouble:
dart_object.type = Dart_CObject_kDouble;
dart_object.value.as_double = object->double_value;
break;
case kFlutterEngineDartObjectTypeString:
if (object->string_value == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"kFlutterEngineDartObjectTypeString must be "
"a null terminated string but was null.");
}
dart_object.type = Dart_CObject_kString;
dart_object.value.as_string = const_cast<char*>(object->string_value);
break;
case kFlutterEngineDartObjectTypeBuffer: {
auto* buffer = SAFE_ACCESS(object->buffer_value, buffer, nullptr);
if (buffer == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"kFlutterEngineDartObjectTypeBuffer must "
"specify a buffer but found nullptr.");
}
auto buffer_size = SAFE_ACCESS(object->buffer_value, buffer_size, 0);
auto callback =
SAFE_ACCESS(object->buffer_value, buffer_collect_callback, nullptr);
auto user_data = SAFE_ACCESS(object->buffer_value, user_data, nullptr);
// The user has provided a callback, let them manage the lifecycle of
// the underlying data. If not, copy it out from the provided buffer.
if (callback == nullptr) {
dart_object.type = Dart_CObject_kTypedData;
dart_object.value.as_typed_data.type = Dart_TypedData_kUint8;
dart_object.value.as_typed_data.length = buffer_size;
dart_object.value.as_typed_data.values = buffer;
} else {
struct ExternalTypedDataPeer {
void* user_data = nullptr;
VoidCallback trampoline = nullptr;
};
auto peer = new ExternalTypedDataPeer();
peer->user_data = user_data;
peer->trampoline = callback;
// This finalizer is set so that in case of failure of the
// Dart_PostCObject below, we collect the peer. The embedder is still
// responsible for collecting the buffer in case of non-kSuccess
// returns from this method. This finalizer must be released in case
// of kSuccess returns from this method.
typed_data_finalizer.SetClosure([peer]() {
// This is the tiny object we use as the peer to the Dart call so
// that we can attach the a trampoline to the embedder supplied
// callback. In case of error, we need to collect this object lest
// we introduce a tiny leak.
delete peer;
});
dart_object.type = Dart_CObject_kExternalTypedData;
dart_object.value.as_external_typed_data.type = Dart_TypedData_kUint8;
dart_object.value.as_external_typed_data.length = buffer_size;
dart_object.value.as_external_typed_data.data = buffer;
dart_object.value.as_external_typed_data.peer = peer;
dart_object.value.as_external_typed_data.callback =
+[](void* unused_isolate_callback_data, void* peer) {
auto typed_peer = reinterpret_cast<ExternalTypedDataPeer*>(peer);
typed_peer->trampoline(typed_peer->user_data);
delete typed_peer;
};
}
} break;
default:
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Invalid FlutterEngineDartObjectType type specified.");
}
if (!Dart_PostCObject(port, &dart_object)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not post the object to the Dart VM.");
}
// On a successful call, the VM takes ownership of and is responsible for
// invoking the finalizer.
typed_data_finalizer.Release();
return kSuccess;
}
FlutterEngineResult FlutterEngineNotifyLowMemoryWarning(
FLUTTER_API_SYMBOL(FlutterEngine) raw_engine) {
auto engine = reinterpret_cast<flutter::EmbedderEngine*>(raw_engine);
if (engine == nullptr || !engine->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine was invalid.");
}
engine->GetShell().NotifyLowMemoryWarning();
rapidjson::Document document;
auto& allocator = document.GetAllocator();
document.SetObject();
document.AddMember("type", "memoryPressure", allocator);
return DispatchJSONPlatformMessage(raw_engine, document, "flutter/system")
? kSuccess
: LOG_EMBEDDER_ERROR(
kInternalInconsistency,
"Could not dispatch the low memory notification message.");
}
FlutterEngineResult FlutterEnginePostCallbackOnAllNativeThreads(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterNativeThreadCallback callback,
void* user_data) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (callback == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Invalid native thread callback.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->PostTaskOnEngineManagedNativeThreads(
[callback, user_data](FlutterNativeThreadType type) {
callback(type, user_data);
})
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments,
"Internal error while attempting to post "
"tasks to all threads.");
}
namespace {
static bool ValidDisplayConfiguration(const FlutterEngineDisplay* displays,
size_t display_count) {
std::set<FlutterEngineDisplayId> display_ids;
for (size_t i = 0; i < display_count; i++) {
if (displays[i].single_display && display_count != 1) {
return false;
}
display_ids.insert(displays[i].display_id);
}
return display_ids.size() == display_count;
}
} // namespace
FlutterEngineResult FlutterEngineNotifyDisplayUpdate(
FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
const FlutterEngineDisplaysUpdateType update_type,
const FlutterEngineDisplay* embedder_displays,
size_t display_count) {
if (raw_engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (!ValidDisplayConfiguration(embedder_displays, display_count)) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Invalid FlutterEngineDisplay configuration specified.");
}
auto engine = reinterpret_cast<flutter::EmbedderEngine*>(raw_engine);
switch (update_type) {
case kFlutterEngineDisplaysUpdateTypeStartup: {
std::vector<std::unique_ptr<flutter::Display>> displays;
for (size_t i = 0; i < display_count; i++) {
displays.push_back(std::make_unique<flutter::Display>(
embedder_displays[i].display_id, embedder_displays[i].refresh_rate,
// TODO(dnfield): Supply real values
// https://github.com/flutter/flutter/issues/125939
-1, -1, -1));
}
engine->GetShell().OnDisplayUpdates(std::move(displays));
return kSuccess;
}
default:
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Invalid FlutterEngineDisplaysUpdateType type specified.");
}
}
FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine)
engine) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->ScheduleFrame()
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not schedule frame.");
}
FlutterEngineResult FlutterEngineSetNextFrameCallback(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
VoidCallback callback,
void* user_data) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (callback == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Next frame callback was null.");
}
flutter::EmbedderEngine* embedder_engine =
reinterpret_cast<flutter::EmbedderEngine*>(engine);
fml::WeakPtr<flutter::PlatformView> weak_platform_view =
embedder_engine->GetShell().GetPlatformView();
if (!weak_platform_view) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Platform view unavailable.");
}
weak_platform_view->SetNextFrameCallback(
[callback, user_data]() { callback(user_data); });
return kSuccess;
}
FlutterEngineResult FlutterEngineGetProcAddresses(
FlutterEngineProcTable* table) {
if (!table) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null table specified.");
}
#define SET_PROC(member, function) \
if (STRUCT_HAS_MEMBER(table, member)) { \
table->member = &function; \
}
SET_PROC(CreateAOTData, FlutterEngineCreateAOTData);
SET_PROC(CollectAOTData, FlutterEngineCollectAOTData);
SET_PROC(Run, FlutterEngineRun);
SET_PROC(Shutdown, FlutterEngineShutdown);
SET_PROC(Initialize, FlutterEngineInitialize);
SET_PROC(Deinitialize, FlutterEngineDeinitialize);
SET_PROC(RunInitialized, FlutterEngineRunInitialized);
SET_PROC(SendWindowMetricsEvent, FlutterEngineSendWindowMetricsEvent);
SET_PROC(SendPointerEvent, FlutterEngineSendPointerEvent);
SET_PROC(SendKeyEvent, FlutterEngineSendKeyEvent);
SET_PROC(SendPlatformMessage, FlutterEngineSendPlatformMessage);
SET_PROC(PlatformMessageCreateResponseHandle,
FlutterPlatformMessageCreateResponseHandle);
SET_PROC(PlatformMessageReleaseResponseHandle,
FlutterPlatformMessageReleaseResponseHandle);
SET_PROC(SendPlatformMessageResponse,
FlutterEngineSendPlatformMessageResponse);
SET_PROC(RegisterExternalTexture, FlutterEngineRegisterExternalTexture);
SET_PROC(UnregisterExternalTexture, FlutterEngineUnregisterExternalTexture);
SET_PROC(MarkExternalTextureFrameAvailable,
FlutterEngineMarkExternalTextureFrameAvailable);
SET_PROC(UpdateSemanticsEnabled, FlutterEngineUpdateSemanticsEnabled);
SET_PROC(UpdateAccessibilityFeatures,
FlutterEngineUpdateAccessibilityFeatures);
SET_PROC(DispatchSemanticsAction, FlutterEngineDispatchSemanticsAction);
SET_PROC(OnVsync, FlutterEngineOnVsync);
SET_PROC(ReloadSystemFonts, FlutterEngineReloadSystemFonts);
SET_PROC(TraceEventDurationBegin, FlutterEngineTraceEventDurationBegin);
SET_PROC(TraceEventDurationEnd, FlutterEngineTraceEventDurationEnd);
SET_PROC(TraceEventInstant, FlutterEngineTraceEventInstant);
SET_PROC(PostRenderThreadTask, FlutterEnginePostRenderThreadTask);
SET_PROC(GetCurrentTime, FlutterEngineGetCurrentTime);
SET_PROC(RunTask, FlutterEngineRunTask);
SET_PROC(UpdateLocales, FlutterEngineUpdateLocales);
SET_PROC(RunsAOTCompiledDartCode, FlutterEngineRunsAOTCompiledDartCode);
SET_PROC(PostDartObject, FlutterEnginePostDartObject);
SET_PROC(NotifyLowMemoryWarning, FlutterEngineNotifyLowMemoryWarning);
SET_PROC(PostCallbackOnAllNativeThreads,
FlutterEnginePostCallbackOnAllNativeThreads);
SET_PROC(NotifyDisplayUpdate, FlutterEngineNotifyDisplayUpdate);
SET_PROC(ScheduleFrame, FlutterEngineScheduleFrame);
SET_PROC(SetNextFrameCallback, FlutterEngineSetNextFrameCallback);
#undef SET_PROC
return kSuccess;
}