blob: 8fcd336a8ced77379707623b437f541ab96860a3 [file] [log] [blame]
// 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 <iostream>
#include "flutter/fml/build_config.h"
#include "flutter/fml/closure.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/native_library.h"
#include "third_party/dart/runtime/include/dart_native_api.h"
#if OS_WIN
#define FLUTTER_EXPORT __declspec(dllexport)
#else // OS_WIN
#define FLUTTER_EXPORT __attribute__((visibility("default")))
#endif // OS_WIN
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/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/persistent_cache.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_platform_message_response.h"
#include "flutter/shell/platform/embedder/embedder_render_target.h"
#include "flutter/shell/platform/embedder/embedder_safe_access.h"
#include "flutter/shell/platform/embedder/embedder_task_runner.h"
#include "flutter/shell/platform/embedder/embedder_thread_host.h"
#include "flutter/shell/platform/embedder/platform_view_embedder.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/writer.h"
const int32_t kFlutterSemanticsNodeIdBatchEnd = -1;
const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1;
static FlutterEngineResult LogEmbedderError(FlutterEngineResult code,
const char* reason,
const char* code_name,
const char* function,
const char* file,
int line) {
#if 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_ACCESS(open_gl_config, make_current, nullptr) == nullptr ||
SAFE_ACCESS(open_gl_config, clear_current, nullptr) == nullptr ||
SAFE_ACCESS(open_gl_config, present, nullptr) == nullptr ||
SAFE_ACCESS(open_gl_config, fbo_callback, nullptr) == nullptr) {
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 IsRendererValid(const FlutterRendererConfig* config) {
if (config == nullptr) {
return false;
}
switch (config->type) {
case kOpenGL:
return IsOpenGLRendererConfigValid(config);
case kSoftware:
return IsSoftwareRendererConfigValid(config);
default:
return false;
}
return false;
}
#if OS_LINUX || OS_WIN
static void* DefaultGLProcResolver(const char* name) {
static fml::RefPtr<fml::NativeLibrary> proc_library =
#if OS_LINUX
fml::NativeLibrary::CreateForCurrentProcess();
#elif OS_WIN // OS_LINUX
fml::NativeLibrary::Create("opengl32.dll");
#endif // OS_WIN
return static_cast<void*>(
const_cast<uint8_t*>(proc_library->ResolveSymbol(name)));
}
#endif // OS_LINUX || OS_WIN
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferOpenGLPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
flutter::PlatformViewEmbedder::PlatformDispatchTable
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
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 = [ptr = config->open_gl.present, user_data]() -> bool {
return ptr(user_data);
};
auto gl_fbo_callback = [ptr = config->open_gl.fbo_callback,
user_data]() -> intptr_t { return ptr(user_data); };
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 OS_LINUX || 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
};
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
);
});
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferSoftwarePlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
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,
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));
default:
return nullptr;
}
return nullptr;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrContext* context,
const FlutterBackingStoreConfig& config,
const FlutterOpenGLTexture* texture) {
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(
SkSurfaceProps::InitType::kLegacyFontHost_InitType);
auto surface = SkSurface::MakeFromBackendTexture(
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<SkSurface::TextureReleaseProc>(
texture->destruction_callback), // release proc
texture->user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied render texture.";
texture->destruction_callback(texture->user_data);
return nullptr;
}
return surface;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrContext* context,
const FlutterBackingStoreConfig& config,
const FlutterOpenGLFramebuffer* framebuffer) {
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(
SkSurfaceProps::InitType::kLegacyFontHost_InitType);
auto surface = SkSurface::MakeFromBackendRenderTarget(
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.";
framebuffer->destruction_callback(framebuffer->user_data);
return nullptr;
}
return surface;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrContext* 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);
captures->destruction_callback(captures->user_data);
};
auto surface = SkSurface::MakeRasterDirectReleaseProc(
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.";
software->destruction_callback(software->user_data);
return nullptr;
}
return surface;
}
static std::unique_ptr<flutter::EmbedderRenderTarget>
CreateEmbedderRenderTarget(const FlutterCompositor* compositor,
const FlutterBackingStoreConfig& config,
GrContext* 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;
};
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);
// 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](GrContext* 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>(
create_render_target_callback, present_callback),
false};
}
struct _FlutterPlatformMessageResponseHandle {
fml::RefPtr<flutter::PlatformMessage> message;
};
void PopulateSnapshotMappingCallbacks(const FlutterProjectArgs* args,
flutter::Settings& settings) {
// 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 (flutter::DartVM::IsRunningPrecompiledCode()) {
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));
}
}
#if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
settings.dart_library_sources_kernel =
make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
#endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
}
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);
PopulateSnapshotMappingCallbacks(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, fml::closure callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(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]() {
callback(user_data);
};
}
flutter::PlatformViewEmbedder::UpdateSemanticsNodesCallback
update_semantics_nodes_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr) {
update_semantics_nodes_callback =
[ptr = args->update_semantics_node_callback,
user_data](flutter::SemanticsNodeUpdates update) {
for (const auto& value : update) {
const auto& node = value.second;
SkMatrix transform = static_cast<SkMatrix>(node.transform);
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)};
const FlutterSemanticsNode embedder_node{
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[0],
&node.childrenInHitTestOrder[0],
node.customAccessibilityActions.size(),
&node.customAccessibilityActions[0],
node.platformViewId,
};
ptr(&embedder_node, user_data);
}
const FlutterSemanticsNode batch_end_sentinel = {
sizeof(FlutterSemanticsNode),
kFlutterSemanticsNodeIdBatchEnd,
};
ptr(&batch_end_sentinel, user_data);
};
}
flutter::PlatformViewEmbedder::UpdateSemanticsCustomActionsCallback
update_semantics_custom_actions_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) !=
nullptr) {
update_semantics_custom_actions_callback =
[ptr = args->update_semantics_custom_action_callback,
user_data](flutter::CustomAccessibilityActionUpdates actions) {
for (const auto& value : actions) {
const auto& action = value.second;
const FlutterSemanticsCustomAction embedder_action = {
sizeof(FlutterSemanticsCustomAction),
action.id,
static_cast<FlutterSemanticsAction>(action.overrideId),
action.label.c_str(),
action.hint.c_str(),
};
ptr(&embedder_action, user_data);
}
const FlutterSemanticsCustomAction batch_end_sentinel = {
sizeof(FlutterSemanticsCustomAction),
kFlutterSemanticsCustomActionIdBatchEnd,
};
ptr(&batch_end_sentinel, 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](fml::RefPtr<flutter::PlatformMessage> message) {
auto handle = new FlutterPlatformMessageResponseHandle();
const FlutterPlatformMessage incoming_message = {
sizeof(FlutterPlatformMessage), // struct_size
message->channel().c_str(), // channel
message->data().data(), // message
message->data().size(), // 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);
};
}
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_nodes_callback, //
update_semantics_custom_actions_callback, //
platform_message_response_callback, //
vsync_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,
shell.GetTaskRunners());
};
// TODO(chinmaygarde): This is the wrong spot for this. It belongs in the
// platform view jump table.
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, GrContext* context,
const SkISize& size) -> sk_sp<SkImage> {
FlutterOpenGLTexture texture = {};
if (!ptr(user_data, texture_identifier, size.width(), size.height(),
&texture)) {
return nullptr;
}
GrGLTextureInfo gr_texture_info = {texture.target, texture.name,
texture.format};
size_t width = size.width();
size_t height = size.height();
if (texture.width != 0 && texture.height != 0) {
width = texture.width;
height = texture.height;
}
GrBackendTexture gr_backend_texture(width, height, GrMipMapped::kNo,
gr_texture_info);
SkImage::TextureReleaseProc release_proc = texture.destruction_callback;
auto image = SkImage::MakeFromTexture(
context, // context
gr_backend_texture, // texture handle
kTopLeft_GrSurfaceOrigin, // origin
kRGBA_8888_SkColorType, // color type
kPremul_SkAlphaType, // alpha type
nullptr, // colorspace
release_proc, // texture release proc
texture.user_data // texture release context
);
if (!image) {
// In case Skia rejects the image, call the release proc so that
// embedders can perform collection of intermediates.
if (release_proc) {
release_proc(texture.user_data);
}
FML_LOG(ERROR) << "Could not create external texture.";
return nullptr;
}
return image;
};
}
}
auto thread_host =
flutter::EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost(
SAFE_ACCESS(args, custom_task_runners, nullptr));
if (!thread_host || !thread_host->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not setup 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.size() != 0) {
run_configuration.SetEntrypoint(std::move(dart_entrypoint));
}
}
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, //
external_texture_callback //
);
// 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);
if (metrics.device_pixel_ratio <= 0.0) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Device pixel ratio was invalid. It must be greater than zero.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetViewportMetrics(
std::move(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;
}
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;
}
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;
}
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:
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();
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);
}
}
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.");
}
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();
}
fml::RefPtr<flutter::PlatformMessage> message;
if (message_size == 0) {
message = fml::MakeRefCounted<flutter::PlatformMessage>(
flutter_message->channel, response);
} else {
message = fml::MakeRefCounted<flutter::PlatformMessage>(
flutter_message->channel,
std::vector<uint8_t>(message_data, 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 = fml::MakeRefCounted<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;
}
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 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(
id, engine_action,
std::vector<uint8_t>({data, 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,
rapidjson::Document document,
const std::string& channel_name) {
if (channel_name.size() == 0) {
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 = fml::MakeRefCounted<flutter::PlatformMessage>(
channel_name.c_str(), // channel
std::vector<uint8_t>{message, 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, std::move(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,
Dart_WeakPersistentHandle unused_handle, 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, std::move(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.");
}