// 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 proc_library = #if OS_LINUX fml::NativeLibrary::CreateForCurrentProcess(); #elif OS_WIN // OS_LINUX fml::NativeLibrary::Create("opengl32.dll"); #endif // OS_WIN return static_cast( const_cast(proc_library->ResolveSymbol(name))); } #endif // OS_LINUX || OS_WIN static flutter::Shell::CreateCallback InferOpenGLPlatformViewCreationCallback( const FlutterRendererConfig* config, void* user_data, flutter::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table, std::unique_ptr 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 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 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( 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 InferSoftwarePlatformViewCreationCallback( const FlutterRendererConfig* config, void* user_data, flutter::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table, std::unique_ptr 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( 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 InferPlatformViewCreationCallback( const FlutterRendererConfig* config, void* user_data, flutter::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table, std::unique_ptr 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 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( 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 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( 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 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->destruction_callback = software->destruction_callback; captures->user_data = software->user_data; auto release_proc = [](void* pixels, void* context) { auto captures = reinterpret_cast(context); captures->destruction_callback(captures->user_data); }; auto surface = SkSurface::MakeRasterDirectReleaseProc( image_info, // image info const_cast(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 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 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( backing_store, std::move(render_surface), collect_callback.Release()); } static std::pair, 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(layers.data()), layers.size(), user_data); }; return {std::make_unique( create_render_target_callback, present_callback), false}; } struct _FlutterPlatformMessageResponseHandle { fml::RefPtr 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(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); } 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(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(node.flags), static_cast(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(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(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 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 on_create_rasterizer = [](flutter::Shell& shell) { return std::make_unique(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 { 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()) { 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); } 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); } // Create the engine but don't launch the shell or run the root isolate. auto embedder_engine = std::make_unique( 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( embedder_engine.release()); return kSuccess; } FlutterEngineResult FlutterEngineRunInitialized( FLUTTER_API_SYMBOL(FlutterEngine) engine) { if (!engine) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } auto embedder_engine = reinterpret_cast(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); } // Step 1: Launch the shell. if (!embedder_engine->LaunchShell()) { FML_LOG(ERROR) << "Could not launch the engine using supplied " "initialization arguments."; return LOG_EMBEDDER_ERROR(kInvalidArguments); } // Step 2: Tell the platform view to initialize itself. if (!embedder_engine->NotifyCreated()) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } // Step 3: Launch the root isolate. if (!embedder_engine->RunRootIsolate()) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } return kSuccess; } FLUTTER_EXPORT FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) engine) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } auto embedder_engine = reinterpret_cast(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(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); } 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) { FML_LOG(ERROR) << "Device pixel ratio invalid: " << metrics.device_pixel_ratio; return LOG_EMBEDDER_ERROR(kInvalidArguments); } return reinterpret_cast(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(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( reinterpret_cast(current) + current->struct_size); } return reinterpret_cast(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 response; if (response_handle && response_handle->message) { response = response_handle->message->response(); } fml::RefPtr message; if (message_size == 0) { message = fml::MakeRefCounted( flutter_message->channel, response); } else { message = fml::MakeRefCounted( flutter_message->channel, std::vector(message_data, message_data + message_size), response); } return reinterpret_cast(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(engine) ->GetTaskRunners() .GetPlatformTaskRunner(); auto handle = new FlutterPlatformMessageResponseHandle(); handle->message = fml::MakeRefCounted( "", // 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( 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( std::vector({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(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(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(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(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(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(action); if (!reinterpret_cast(engine) ->DispatchSemanticsAction( id, engine_action, std::vector({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(engine)->OnVsyncEvent( baton, start_time, target_time)) { return LOG_EMBEDDER_ERROR(kInternalInconsistency); } return kSuccess; } FlutterEngineResult FlutterEngineReloadSystemFonts( FLUTTER_API_SYMBOL(FlutterEngine) engine) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } TRACE_EVENT0("flutter", "FlutterEngineReloadSystemFonts"); if (!reinterpret_cast(engine) ->ReloadSystemFonts()) { 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(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(engine)->RunTask(task) ? kSuccess : LOG_EMBEDDER_ERROR(kInvalidArguments); }