mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
As of Dart 2, running from Dart source is no longer supported. Dart code should now be compiled to kernel form and will be loaded by from kernel.blob in the assets directory. We retain the struct members for ABI stability. package_path is also not required since kernel blobs are self-contained.
629 lines
22 KiB
C++
629 lines
22 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/native_library.h"
|
|
|
|
#if OS_WIN
|
|
#define FLUTTER_EXPORT __declspec(dllexport)
|
|
#else // OS_WIN
|
|
#define FLUTTER_EXPORT __attribute__((visibility("default")))
|
|
#endif // OS_WIN
|
|
|
|
#include "flutter/shell/platform/embedder/embedder.h"
|
|
|
|
#include <type_traits>
|
|
|
|
#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/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/platform_view_embedder.h"
|
|
|
|
#define SAFE_ACCESS(pointer, member, default_value) \
|
|
([=]() { \
|
|
if (offsetof(std::remove_pointer<decltype(pointer)>::type, member) + \
|
|
sizeof(pointer->member) <= \
|
|
pointer->struct_size) { \
|
|
return pointer->member; \
|
|
} \
|
|
return static_cast<decltype(pointer->member)>((default_value)); \
|
|
})()
|
|
|
|
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 shell::Shell::CreateCallback<shell::PlatformView>
|
|
InferOpenGLPlatformViewCreationCallback(
|
|
const FlutterRendererConfig* config,
|
|
void* user_data,
|
|
shell::PlatformViewEmbedder::PlatformDispatchTable
|
|
platform_dispatch_table) {
|
|
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 //
|
|
);
|
|
};
|
|
}
|
|
|
|
shell::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);
|
|
|
|
shell::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 [gl_dispatch_table, fbo_reset_after_present,
|
|
platform_dispatch_table](shell::Shell& shell) {
|
|
return std::make_unique<shell::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
|
|
);
|
|
};
|
|
}
|
|
|
|
static shell::Shell::CreateCallback<shell::PlatformView>
|
|
InferSoftwarePlatformViewCreationCallback(
|
|
const FlutterRendererConfig* config,
|
|
void* user_data,
|
|
shell::PlatformViewEmbedder::PlatformDispatchTable
|
|
platform_dispatch_table) {
|
|
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);
|
|
};
|
|
|
|
shell::EmbedderSurfaceSoftware::SoftwareDispatchTable
|
|
software_dispatch_table = {
|
|
software_present_backing_store, // required
|
|
};
|
|
|
|
return
|
|
[software_dispatch_table, platform_dispatch_table](shell::Shell& shell) {
|
|
return std::make_unique<shell::PlatformViewEmbedder>(
|
|
shell, // delegate
|
|
shell.GetTaskRunners(), // task runners
|
|
software_dispatch_table, // software dispatch table
|
|
platform_dispatch_table // platform dispatch table
|
|
);
|
|
};
|
|
}
|
|
|
|
static shell::Shell::CreateCallback<shell::PlatformView>
|
|
InferPlatformViewCreationCallback(
|
|
const FlutterRendererConfig* config,
|
|
void* user_data,
|
|
shell::PlatformViewEmbedder::PlatformDispatchTable
|
|
platform_dispatch_table) {
|
|
if (config == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
switch (config->type) {
|
|
case kOpenGL:
|
|
return InferOpenGLPlatformViewCreationCallback(config, user_data,
|
|
platform_dispatch_table);
|
|
case kSoftware:
|
|
return InferSoftwarePlatformViewCreationCallback(config, user_data,
|
|
platform_dispatch_table);
|
|
default:
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
struct _FlutterPlatformMessageResponseHandle {
|
|
fml::RefPtr<blink::PlatformMessage> message;
|
|
};
|
|
|
|
FlutterResult FlutterEngineRun(size_t version,
|
|
const FlutterRendererConfig* config,
|
|
const FlutterProjectArgs* args,
|
|
void* user_data,
|
|
FlutterEngine* engine_out) {
|
|
// Step 0: Figure out arguments for shell creation.
|
|
if (version != FLUTTER_ENGINE_VERSION) {
|
|
return kInvalidLibraryVersion;
|
|
}
|
|
|
|
if (engine_out == nullptr) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
if (args == nullptr) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr) {
|
|
return 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)) {
|
|
return 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);
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
blink::Settings settings = shell::SettingsFromCommandLine(command_line);
|
|
settings.icu_data_path = icu_data_path;
|
|
settings.assets_path = args->assets_path;
|
|
|
|
// 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 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);
|
|
};
|
|
|
|
// Create a thread host with the current thread as the platform thread and all
|
|
// other threads managed.
|
|
shell::ThreadHost thread_host("io.flutter", shell::ThreadHost::Type::GPU |
|
|
shell::ThreadHost::Type::IO |
|
|
shell::ThreadHost::Type::UI);
|
|
fml::MessageLoop::EnsureInitializedForCurrentThread();
|
|
blink::TaskRunners task_runners(
|
|
"io.flutter",
|
|
fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
|
|
thread_host.gpu_thread->GetTaskRunner(), // gpu
|
|
thread_host.ui_thread->GetTaskRunner(), // ui
|
|
thread_host.io_thread->GetTaskRunner() // io
|
|
);
|
|
|
|
shell::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<blink::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);
|
|
};
|
|
}
|
|
|
|
shell::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table = {
|
|
platform_message_response_callback, // platform_message_response_callback
|
|
};
|
|
|
|
auto on_create_platform_view = InferPlatformViewCreationCallback(
|
|
config, user_data, platform_dispatch_table);
|
|
|
|
if (!on_create_platform_view) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
shell::Shell::CreateCallback<shell::Rasterizer> on_create_rasterizer =
|
|
[](shell::Shell& shell) {
|
|
return std::make_unique<shell::Rasterizer>(shell.GetTaskRunners());
|
|
};
|
|
|
|
// TODO(chinmaygarde): This is the wrong spot for this. It belongs in the
|
|
// platform view jump table.
|
|
shell::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;
|
|
};
|
|
}
|
|
}
|
|
|
|
// Step 1: Create the engine.
|
|
auto embedder_engine =
|
|
std::make_unique<shell::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 kInvalidArguments;
|
|
}
|
|
|
|
// Step 2: Setup the rendering surface.
|
|
if (!embedder_engine->NotifyCreated()) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
// Step 3: Run the engine.
|
|
auto run_configuration = shell::RunConfiguration::InferFromSettings(settings);
|
|
|
|
run_configuration.AddAssetResolver(
|
|
std::make_unique<blink::DirectoryAssetBundle>(
|
|
fml::Duplicate(settings.assets_dir)));
|
|
|
|
run_configuration.AddAssetResolver(
|
|
std::make_unique<blink::DirectoryAssetBundle>(fml::OpenDirectory(
|
|
settings.assets_path.c_str(), false, fml::FilePermission::kRead)));
|
|
if (!run_configuration.IsValid()) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
if (!embedder_engine->Run(std::move(run_configuration))) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
// Finally! Release the ownership of the embedder engine to the caller.
|
|
*engine_out = reinterpret_cast<FlutterEngine>(embedder_engine.release());
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterResult FlutterEngineShutdown(FlutterEngine engine) {
|
|
if (engine == nullptr) {
|
|
return kInvalidArguments;
|
|
}
|
|
auto embedder_engine = reinterpret_cast<shell::EmbedderEngine*>(engine);
|
|
embedder_engine->NotifyDestroyed();
|
|
delete embedder_engine;
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterResult FlutterEngineSendWindowMetricsEvent(
|
|
FlutterEngine engine,
|
|
const FlutterWindowMetricsEvent* flutter_metrics) {
|
|
if (engine == nullptr || flutter_metrics == nullptr) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
blink::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<shell::EmbedderEngine*>(engine)->SetViewportMetrics(
|
|
std::move(metrics))
|
|
? kSuccess
|
|
: kInvalidArguments;
|
|
}
|
|
|
|
inline blink::PointerData::Change ToPointerDataChange(
|
|
FlutterPointerPhase phase) {
|
|
switch (phase) {
|
|
case kCancel:
|
|
return blink::PointerData::Change::kCancel;
|
|
case kUp:
|
|
return blink::PointerData::Change::kUp;
|
|
case kDown:
|
|
return blink::PointerData::Change::kDown;
|
|
case kMove:
|
|
return blink::PointerData::Change::kMove;
|
|
}
|
|
return blink::PointerData::Change::kCancel;
|
|
}
|
|
|
|
FlutterResult FlutterEngineSendPointerEvent(FlutterEngine engine,
|
|
const FlutterPointerEvent* pointers,
|
|
size_t events_count) {
|
|
if (engine == nullptr || pointers == nullptr || events_count == 0) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
auto packet = std::make_unique<blink::PointerDataPacket>(events_count);
|
|
|
|
const FlutterPointerEvent* current = pointers;
|
|
|
|
for (size_t i = 0; i < events_count; ++i) {
|
|
blink::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.kind = blink::PointerData::DeviceKind::kMouse;
|
|
pointer_data.physical_x = SAFE_ACCESS(current, x, 0.0);
|
|
pointer_data.physical_y = SAFE_ACCESS(current, y, 0.0);
|
|
packet->SetPointerData(i, pointer_data);
|
|
current = reinterpret_cast<const FlutterPointerEvent*>(
|
|
reinterpret_cast<const uint8_t*>(current) + current->struct_size);
|
|
}
|
|
|
|
return reinterpret_cast<shell::EmbedderEngine*>(engine)
|
|
->DispatchPointerDataPacket(std::move(packet))
|
|
? kSuccess
|
|
: kInvalidArguments;
|
|
}
|
|
|
|
FlutterResult FlutterEngineSendPlatformMessage(
|
|
FlutterEngine engine,
|
|
const FlutterPlatformMessage* flutter_message) {
|
|
if (engine == nullptr || flutter_message == nullptr) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
if (SAFE_ACCESS(flutter_message, channel, nullptr) == nullptr ||
|
|
SAFE_ACCESS(flutter_message, message, nullptr) == nullptr) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
auto message = fml::MakeRefCounted<blink::PlatformMessage>(
|
|
flutter_message->channel,
|
|
std::vector<uint8_t>(
|
|
flutter_message->message,
|
|
flutter_message->message + flutter_message->message_size),
|
|
nullptr);
|
|
|
|
return reinterpret_cast<shell::EmbedderEngine*>(engine)->SendPlatformMessage(
|
|
std::move(message))
|
|
? kSuccess
|
|
: kInvalidArguments;
|
|
}
|
|
|
|
FlutterResult FlutterEngineSendPlatformMessageResponse(
|
|
FlutterEngine engine,
|
|
const FlutterPlatformMessageResponseHandle* handle,
|
|
const uint8_t* data,
|
|
size_t data_length) {
|
|
if (data_length != 0 && data == nullptr) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
auto response = handle->message->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;
|
|
}
|
|
|
|
FlutterResult __FlutterEngineFlushPendingTasksNow() {
|
|
fml::MessageLoop::GetCurrent().RunExpiredTasksNow();
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterResult FlutterEngineRegisterExternalTexture(FlutterEngine engine,
|
|
int64_t texture_identifier) {
|
|
if (engine == nullptr || texture_identifier == 0) {
|
|
return kInvalidArguments;
|
|
}
|
|
if (!reinterpret_cast<shell::EmbedderEngine*>(engine)->RegisterTexture(
|
|
texture_identifier)) {
|
|
return kInternalInconsistency;
|
|
}
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterResult FlutterEngineUnregisterExternalTexture(
|
|
FlutterEngine engine,
|
|
int64_t texture_identifier) {
|
|
if (engine == nullptr || texture_identifier == 0) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
if (!reinterpret_cast<shell::EmbedderEngine*>(engine)->UnregisterTexture(
|
|
texture_identifier)) {
|
|
return kInternalInconsistency;
|
|
}
|
|
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterResult FlutterEngineMarkExternalTextureFrameAvailable(
|
|
FlutterEngine engine,
|
|
int64_t texture_identifier) {
|
|
if (engine == nullptr || texture_identifier == 0) {
|
|
return kInvalidArguments;
|
|
}
|
|
if (!reinterpret_cast<shell::EmbedderEngine*>(engine)
|
|
->MarkTextureFrameAvailable(texture_identifier)) {
|
|
return kInternalInconsistency;
|
|
}
|
|
return kSuccess;
|
|
}
|