flutter_flutter/shell/platform/windows/flutter_windows_engine.cc
stuartmorgan 91eab23eb3
Switch Windows embedding to proc table embedder API (#22211)
Switches the Windows embedding from the standard C API to the new proctable version, to allow for unit testing of the embedding layer separately from the embedder APIs implementation. This includes moving some engine messaging that was still in flutter_windows to the C++ engine class to better encapsulate the proc table.
2020-11-03 14:36:18 -08:00

334 lines
12 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.
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
#include <filesystem>
#include <iostream>
#include <sstream>
#include "flutter/shell/platform/common/cpp/path_utils.h"
#include "flutter/shell/platform/windows/flutter_windows_view.h"
#include "flutter/shell/platform/windows/string_conversion.h"
#include "flutter/shell/platform/windows/system_utils.h"
namespace flutter {
namespace {
// Creates and returns a FlutterRendererConfig that renders to the view (if any)
// of a FlutterWindowsEngine, which should be the user_data received by the
// render callbacks.
FlutterRendererConfig GetRendererConfig() {
FlutterRendererConfig config = {};
config.type = kOpenGL;
config.open_gl.struct_size = sizeof(config.open_gl);
config.open_gl.make_current = [](void* user_data) -> bool {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
if (!host->view()) {
return false;
}
return host->view()->MakeCurrent();
};
config.open_gl.clear_current = [](void* user_data) -> bool {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
if (!host->view()) {
return false;
}
return host->view()->ClearContext();
};
config.open_gl.present = [](void* user_data) -> bool {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
if (!host->view()) {
return false;
}
return host->view()->SwapBuffers();
};
config.open_gl.fbo_callback = [](void* user_data) -> uint32_t { return 0; };
config.open_gl.gl_proc_resolver = [](void* user_data,
const char* what) -> void* {
return reinterpret_cast<void*>(eglGetProcAddress(what));
};
config.open_gl.make_resource_current = [](void* user_data) -> bool {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
if (!host->view()) {
return false;
}
return host->view()->MakeResourceCurrent();
};
return config;
}
// Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage.
static FlutterDesktopMessage ConvertToDesktopMessage(
const FlutterPlatformMessage& engine_message) {
FlutterDesktopMessage message = {};
message.struct_size = sizeof(message);
message.channel = engine_message.channel;
message.message = engine_message.message;
message.message_size = engine_message.message_size;
message.response_handle = engine_message.response_handle;
return message;
}
// Converts a LanguageInfo struct to a FlutterLocale struct. |info| must outlive
// the returned value, since the returned FlutterLocale has pointers into it.
FlutterLocale CovertToFlutterLocale(const LanguageInfo& info) {
FlutterLocale locale = {};
locale.struct_size = sizeof(FlutterLocale);
locale.language_code = info.language.c_str();
if (!info.region.empty()) {
locale.country_code = info.region.c_str();
}
if (!info.script.empty()) {
locale.script_code = info.script.c_str();
}
return locale;
}
} // namespace
FlutterWindowsEngine::FlutterWindowsEngine(const FlutterProjectBundle& project)
: project_(std::make_unique<FlutterProjectBundle>(project)),
aot_data_(nullptr, nullptr) {
embedder_api_.struct_size = sizeof(FlutterEngineProcTable);
FlutterEngineGetProcAddresses(&embedder_api_);
task_runner_ = std::make_unique<Win32TaskRunner>(
GetCurrentThreadId(), embedder_api_.GetCurrentTime,
[this](const auto* task) {
if (!engine_) {
std::cerr << "Cannot post an engine task when engine is not running."
<< std::endl;
return;
}
if (embedder_api_.RunTask(engine_, task) != kSuccess) {
std::cerr << "Failed to post an engine task." << std::endl;
}
});
// Set up the legacy structs backing the API handles.
messenger_ = std::make_unique<FlutterDesktopMessenger>();
messenger_->engine = this;
plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
plugin_registrar_->engine = this;
message_dispatcher_ =
std::make_unique<IncomingMessageDispatcher>(messenger_.get());
window_proc_delegate_manager_ =
std::make_unique<Win32WindowProcDelegateManager>();
}
FlutterWindowsEngine::~FlutterWindowsEngine() {
Stop();
}
bool FlutterWindowsEngine::RunWithEntrypoint(const char* entrypoint) {
if (!project_->HasValidPaths()) {
std::cerr << "Missing or unresolvable paths to assets." << std::endl;
return false;
}
std::string assets_path_string = project_->assets_path().u8string();
std::string icu_path_string = project_->icu_path().u8string();
if (embedder_api_.RunsAOTCompiledDartCode()) {
aot_data_ = project_->LoadAotData(embedder_api_);
if (!aot_data_) {
std::cerr << "Unable to start engine without AOT data." << std::endl;
return false;
}
}
// FlutterProjectArgs is expecting a full argv, so when processing it for
// flags the first item is treated as the executable and ignored. Add a dummy
// value so that all provided arguments are used.
std::vector<std::string> switches = project_->GetSwitches();
std::vector<const char*> argv = {"placeholder"};
std::transform(
switches.begin(), switches.end(), std::back_inserter(argv),
[](const std::string& arg) -> const char* { return arg.c_str(); });
const std::vector<std::string>& entrypoint_args =
project_->dart_entrypoint_arguments();
std::vector<const char*> entrypoint_argv;
std::transform(
entrypoint_args.begin(), entrypoint_args.end(),
std::back_inserter(entrypoint_argv),
[](const std::string& arg) -> const char* { return arg.c_str(); });
// Configure task runners.
FlutterTaskRunnerDescription platform_task_runner = {};
platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
platform_task_runner.user_data = task_runner_.get();
platform_task_runner.runs_task_on_current_thread_callback =
[](void* user_data) -> bool {
return static_cast<Win32TaskRunner*>(user_data)->RunsTasksOnCurrentThread();
};
platform_task_runner.post_task_callback = [](FlutterTask task,
uint64_t target_time_nanos,
void* user_data) -> void {
static_cast<Win32TaskRunner*>(user_data)->PostTask(task, target_time_nanos);
};
FlutterCustomTaskRunners custom_task_runners = {};
custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
custom_task_runners.platform_task_runner = &platform_task_runner;
FlutterProjectArgs args = {};
args.struct_size = sizeof(FlutterProjectArgs);
args.assets_path = assets_path_string.c_str();
args.icu_data_path = icu_path_string.c_str();
args.command_line_argc = static_cast<int>(argv.size());
args.command_line_argv = argv.size() > 0 ? argv.data() : nullptr;
args.dart_entrypoint_argc = static_cast<int>(entrypoint_argv.size());
args.dart_entrypoint_argv =
entrypoint_argv.size() > 0 ? entrypoint_argv.data() : nullptr;
args.platform_message_callback =
[](const FlutterPlatformMessage* engine_message,
void* user_data) -> void {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
return host->HandlePlatformMessage(engine_message);
};
args.custom_task_runners = &custom_task_runners;
if (aot_data_) {
args.aot_data = aot_data_.get();
}
if (entrypoint) {
args.custom_dart_entrypoint = entrypoint;
}
FlutterRendererConfig renderer_config = GetRendererConfig();
auto result = embedder_api_.Run(FLUTTER_ENGINE_VERSION, &renderer_config,
&args, this, &engine_);
if (result != kSuccess || engine_ == nullptr) {
std::cerr << "Failed to start Flutter engine: error " << result
<< std::endl;
return false;
}
SendSystemSettings();
return true;
}
bool FlutterWindowsEngine::Stop() {
if (engine_) {
if (plugin_registrar_destruction_callback_) {
plugin_registrar_destruction_callback_(plugin_registrar_.get());
}
FlutterEngineResult result = embedder_api_.Shutdown(engine_);
engine_ = nullptr;
return (result == kSuccess);
}
return false;
}
void FlutterWindowsEngine::SetView(FlutterWindowsView* view) {
view_ = view;
}
// Returns the currently configured Plugin Registrar.
FlutterDesktopPluginRegistrarRef FlutterWindowsEngine::GetRegistrar() {
return plugin_registrar_.get();
}
void FlutterWindowsEngine::SetPluginRegistrarDestructionCallback(
FlutterDesktopOnPluginRegistrarDestroyed callback) {
plugin_registrar_destruction_callback_ = callback;
}
void FlutterWindowsEngine::SendWindowMetricsEvent(
const FlutterWindowMetricsEvent& event) {
if (engine_) {
embedder_api_.SendWindowMetricsEvent(engine_, &event);
}
}
void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) {
if (engine_) {
embedder_api_.SendPointerEvent(engine_, &event, 1);
}
}
bool FlutterWindowsEngine::SendPlatformMessage(
const char* channel,
const uint8_t* message,
const size_t message_size,
const FlutterDesktopBinaryReply reply,
void* user_data) {
FlutterPlatformMessageResponseHandle* response_handle = nullptr;
if (reply != nullptr && user_data != nullptr) {
FlutterEngineResult result =
embedder_api_.PlatformMessageCreateResponseHandle(
engine_, reply, user_data, &response_handle);
if (result != kSuccess) {
std::cout << "Failed to create response handle\n";
return false;
}
}
FlutterPlatformMessage platform_message = {
sizeof(FlutterPlatformMessage),
channel,
message,
message_size,
response_handle,
};
FlutterEngineResult message_result =
embedder_api_.SendPlatformMessage(engine_, &platform_message);
if (response_handle != nullptr) {
embedder_api_.PlatformMessageReleaseResponseHandle(engine_,
response_handle);
}
return message_result == kSuccess;
}
void FlutterWindowsEngine::SendPlatformMessageResponse(
const FlutterDesktopMessageResponseHandle* handle,
const uint8_t* data,
size_t data_length) {
embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
}
void FlutterWindowsEngine::HandlePlatformMessage(
const FlutterPlatformMessage* engine_message) {
if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
std::cerr << "Invalid message size received. Expected: "
<< sizeof(FlutterPlatformMessage) << " but received "
<< engine_message->struct_size << std::endl;
return;
}
auto message = ConvertToDesktopMessage(*engine_message);
message_dispatcher_->HandleMessage(
message, [this] {}, [this] {});
}
void FlutterWindowsEngine::ReloadSystemFonts() {
embedder_api_.ReloadSystemFonts(engine_);
}
void FlutterWindowsEngine::SendSystemSettings() {
std::vector<LanguageInfo> languages = GetPreferredLanguageInfo();
std::vector<FlutterLocale> flutter_locales;
flutter_locales.reserve(languages.size());
for (const auto& info : languages) {
flutter_locales.push_back(CovertToFlutterLocale(info));
}
// Convert the locale list to the locale pointer list that must be provided.
std::vector<const FlutterLocale*> flutter_locale_list;
flutter_locale_list.reserve(flutter_locales.size());
std::transform(
flutter_locales.begin(), flutter_locales.end(),
std::back_inserter(flutter_locale_list),
[](const auto& arg) -> const auto* { return &arg; });
embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
flutter_locale_list.size());
// TODO: Send 'flutter/settings' channel settings here as well.
}
} // namespace flutter