// 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 "lib/fxl/build_config.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 #include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/task_runners.h" #include "flutter/fml/file.h" #include "flutter/fml/message_loop.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" #include "lib/fxl/command_line.h" #include "lib/fxl/functional/make_copyable.h" #define SAFE_ACCESS(pointer, member, default_value) \ ({ \ auto _return_value = \ static_cast<__typeof__(pointer->member)>((default_value)); \ if (offsetof(std::remove_pointer::type, member) + \ sizeof(pointer->member) <= \ pointer->struct_size) { \ _return_value = pointer->member; \ } \ _return_value; \ }) bool IsRendererValid(const FlutterRendererConfig* config) { if (config == nullptr || 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; } struct _FlutterPlatformMessageResponseHandle { fxl::RefPtr 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; } auto make_current = [ptr = config->open_gl.make_current, user_data]() -> bool { return ptr(user_data); }; auto clear_current = [ptr = config->open_gl.clear_current, user_data]() -> bool { return ptr(user_data); }; auto present = [ptr = config->open_gl.present, user_data]() -> bool { return ptr(user_data); }; auto fbo_callback = [ptr = config->open_gl.fbo_callback, user_data]() -> intptr_t { return ptr(user_data); }; 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](fxl::RefPtr message) { auto handle = new FlutterPlatformMessageResponseHandle(); const FlutterPlatformMessage incoming_message = { .struct_size = sizeof(FlutterPlatformMessage), .channel = message->channel().c_str(), .message = message->data().data(), .message_size = message->data().size(), .response_handle = handle, }; handle->message = std::move(message); return ptr(&incoming_message, user_data); }; } const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; std::function make_resource_current_callback = nullptr; if (SAFE_ACCESS(open_gl_config, make_resource_current, nullptr) != nullptr) { make_resource_current_callback = [ptr = config->open_gl.make_resource_current, user_data]() { return ptr(user_data); }; } std::string icu_data_path; if (SAFE_ACCESS(args, icu_data_path, nullptr) != nullptr) { icu_data_path = SAFE_ACCESS(args, icu_data_path, nullptr); } fxl::CommandLine command_line; if (SAFE_ACCESS(args, command_line_argc, 0) != 0 && SAFE_ACCESS(args, command_line_argv, nullptr) != nullptr) { command_line = fxl::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.main_dart_file_path = args->main_path; settings.packages_file_path = args->packages_path; settings.assets_path = args->assets_path; settings.task_observer_add = [](intptr_t key, fxl::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::DispatchTable dispatch_table = { .gl_make_current_callback = make_current, .gl_clear_current_callback = clear_current, .gl_present_callback = present, .gl_fbo_callback = fbo_callback, .platform_message_response_callback = platform_message_response_callback, .gl_make_resource_current_callback = make_resource_current_callback, }; shell::Shell::CreateCallback on_create_platform_view = [dispatch_table](shell::Shell& shell) { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners dispatch_table // embedder dispatch table ); }; shell::Shell::CreateCallback on_create_rasterizer = [](shell::Shell& shell) { return std::make_unique(shell.GetTaskRunners()); }; // Step 1: Create the engine. auto embedder_engine = std::make_unique(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( fml::Duplicate(settings.assets_dir))); run_configuration.AddAssetResolver( std::make_unique(fml::OpenFile( settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); 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(embedder_engine.release()); return kSuccess; } FlutterResult FlutterEngineShutdown(FlutterEngine engine) { if (engine == nullptr) { return kInvalidArguments; } auto embedder_engine = reinterpret_cast(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(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(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( reinterpret_cast(current) + current->struct_size); } return reinterpret_cast(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 = fxl::MakeRefCounted( flutter_message->channel, std::vector( flutter_message->message, flutter_message->message + flutter_message->message_size), nullptr); return reinterpret_cast(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({data, data + data_length}); } delete handle; return kSuccess; } FlutterResult __FlutterEngineFlushPendingTasksNow() { fml::MessageLoop::GetCurrent().RunExpiredTasksNow(); return kSuccess; }