blob: d8e1854e6b5df49ba8475a4034225d8a44e63171 [file] [log] [blame]
// Copyright 2017 The Chromium 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 ||
SAFE_ACCESS(args, main_path, nullptr) == nullptr ||
SAFE_ACCESS(args, packages_path, nullptr) == nullptr) {
return kInvalidArguments;
}
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;
// Check whether 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)) {
// Run from a kernel snapshot.
settings.application_kernel_asset = kApplicationKernelSnapshotFileName;
} else {
// Run from a main Dart file.
settings.main_dart_file_path = args->main_path;
settings.packages_file_path = args->packages_path;
}
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());
};
// 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 //
);
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 (!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;
}