mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
e8f954409d
This patch allows embedders to split the Flutter layer tree into multiple chunks. These chunks are meant to be composed one on top of another. This gives embedders a chance to interleave their own contents between these chunks. The Flutter embedder API already provides hooks for the specification of textures for the Flutter engine to compose within its own hierarchy (for camera feeds, video, etc..). However, not all embedders can render the contents of such sources into textures the Flutter engine can accept. Moreover, this composition model may have overheads that are non-trivial for certain use cases. In such cases, the embedder may choose to specify multiple render target for Flutter to render into instead of just one. The use of this API allows embedders to perform composition very similar to the iOS embedder. This composition model is used on that platform for the embedding of UIKit view such and web view and map views within the Flutter hierarchy. However, do note that iOS also has threading configurations that are currently not available to custom embedders. The embedder API updates in this patch are ABI stable and existing embedders will continue to work are normal. For embedders that want to enable this composition mode, the API is designed to make it easy to opt into the same in an incremental manner. Rendering of contents into the “root” rendering surface remains unchanged. However, now the application can push “platform views” via a scene builder. These platform views need to handled by a FlutterCompositor specified in a new field at the end of the FlutterProjectArgs struct. When a new platform view in introduced within the layer tree, the compositor will ask the embedder to create a new render target for that platform view. Render targets can currently be OpenGL framebuffers, OpenGL textures or software buffers. The type of the render target returned by the embedder must be compatible with the root render surface. That is, if the root render surface is an OpenGL framebuffer, the render target for each platform view must either be a texture or a framebuffer in the same OpenGL context. New render target types as well as root renderers for newer APIs like Metal & Vulkan can and will be added in the future. The addition of these APIs will be done in an ABI & API stable manner. As Flutter renders frames, it gives the embedder a callback with information about the position of the various platform views in the effective hierarchy. The embedder is then meant to put the contents of the render targets that it setup and had previously given to the engine onto the screen (of course interleaving the contents of the platform views). Unit-tests have been added that test not only the structure and properties of layer hierarchy given to the compositor, but also the contents of the texels rendered by a test compositor using both the OpenGL and software rendering backends. Fixes b/132812775 Fixes flutter/flutter#35410
1318 lines
48 KiB
C++
1318 lines
48 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
|
|
|
|
#include "flutter/fml/build_config.h"
|
|
#include "flutter/fml/make_copyable.h"
|
|
#include "flutter/fml/native_library.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"
|
|
|
|
const int32_t kFlutterSemanticsNodeIdBatchEnd = -1;
|
|
const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1;
|
|
|
|
static FlutterEngineResult LogEmbedderError(FlutterEngineResult code,
|
|
const char* name,
|
|
const char* function,
|
|
const char* file,
|
|
int line) {
|
|
FML_LOG(ERROR) << "Returning error '" << name << "' (" << code
|
|
<< ") from Flutter Embedder API call to '" << function
|
|
<< "'. Origin: " << file << ":" << line;
|
|
return code;
|
|
}
|
|
|
|
#define LOG_EMBEDDER_ERROR(code) \
|
|
LogEmbedderError(code, #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 //
|
|
);
|
|
};
|
|
}
|
|
|
|
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
|
|
kTopLeft_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
|
|
kTopLeft_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;
|
|
|
|
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]() {
|
|
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) {
|
|
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) {
|
|
// Step 0: Figure out arguments for shell creation.
|
|
if (version != FLUTTER_ENGINE_VERSION) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidLibraryVersion);
|
|
}
|
|
|
|
if (engine_out == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
if (args == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
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)) {
|
|
FML_LOG(WARNING) << "Invalid renderer config.";
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
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);
|
|
|
|
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)) {
|
|
FML_LOG(ERROR) << "Not running in AOT mode but could not resolve the "
|
|
"kernel binary.";
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
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],
|
|
};
|
|
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);
|
|
}
|
|
|
|
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(kInvalidArguments);
|
|
}
|
|
|
|
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};
|
|
|
|
GrBackendTexture gr_backend_texture(size.width(), size.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()) {
|
|
FML_LOG(ERROR) << "Could not setup or infer thread configuration to run "
|
|
"the Flutter engine on.";
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
auto task_runners = thread_host->GetTaskRunners();
|
|
|
|
if (!task_runners.IsValid()) {
|
|
FML_LOG(ERROR) << "Task runner configuration specified is invalid.";
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
// Step 1: Create the engine.
|
|
auto embedder_engine =
|
|
std::make_unique<flutter::EmbedderEngine>(std::move(thread_host), //
|
|
std::move(task_runners), //
|
|
settings, //
|
|
on_create_platform_view, //
|
|
on_create_rasterizer, //
|
|
external_texture_callback //
|
|
);
|
|
|
|
if (!embedder_engine->IsValid()) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
// Step 2: Setup the rendering surface.
|
|
if (!embedder_engine->NotifyCreated()) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
// Step 3: Run the engine.
|
|
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);
|
|
}
|
|
|
|
if (!embedder_engine->Run(std::move(run_configuration))) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
// Finally! Release the ownership of the embedder engine to the caller.
|
|
*engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(
|
|
embedder_engine.release());
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine)
|
|
engine) {
|
|
if (engine == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
|
|
embedder_engine->NotifyDestroyed();
|
|
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);
|
|
}
|
|
|
|
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);
|
|
|
|
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetViewportMetrics(
|
|
std::move(metrics))
|
|
? kSuccess
|
|
: LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
// 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 || pointers == nullptr || events_count == 0) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
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);
|
|
pointer_data.device = SAFE_ACCESS(current, device, 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(kInvalidArguments);
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineSendPlatformMessage(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
const FlutterPlatformMessage* flutter_message) {
|
|
if (engine == nullptr || flutter_message == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
if (SAFE_ACCESS(flutter_message, channel, nullptr) == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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(kInvalidArguments);
|
|
}
|
|
|
|
FlutterEngineResult FlutterPlatformMessageCreateResponseHandle(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
FlutterDataCallback data_callback,
|
|
void* user_data,
|
|
FlutterPlatformMessageResponseHandle** response_out) {
|
|
if (engine == nullptr || data_callback == nullptr ||
|
|
response_out == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
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 || response == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
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);
|
|
}
|
|
|
|
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 || texture_identifier == 0) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->RegisterTexture(
|
|
texture_identifier)) {
|
|
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
|
|
}
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineUnregisterExternalTexture(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
int64_t texture_identifier) {
|
|
if (engine == nullptr || texture_identifier == 0) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->UnregisterTexture(
|
|
texture_identifier)) {
|
|
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
|
|
}
|
|
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
int64_t texture_identifier) {
|
|
if (engine == nullptr || texture_identifier == 0) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
|
|
->MarkTextureFrameAvailable(texture_identifier)) {
|
|
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
|
|
}
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
bool enabled) {
|
|
if (engine == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetSemanticsEnabled(
|
|
enabled)) {
|
|
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
|
|
}
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
FlutterAccessibilityFeature flags) {
|
|
if (engine == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
|
|
->SetAccessibilityFeatures(flags)) {
|
|
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 || callback == nullptr) {
|
|
return LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|
|
|
|
auto task = [callback, baton]() { callback(baton); };
|
|
|
|
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
|
|
->PostRenderThreadTask(task)
|
|
? kSuccess
|
|
: LOG_EMBEDDER_ERROR(kInternalInconsistency);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->RunTask(task)
|
|
? kSuccess
|
|
: LOG_EMBEDDER_ERROR(kInvalidArguments);
|
|
}
|