blob: bb020d52f4cccec424b583cfb48d6042e2e60abf [file] [log] [blame]
// 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/client_wrapper/binary_messenger_impl.h"
#include "flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h"
#include "flutter/shell/platform/common/json_message_codec.h"
#include "flutter/shell/platform/common/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"
#include "flutter/shell/platform/windows/task_runner.h"
#include "third_party/rapidjson/include/rapidjson/document.h"
namespace flutter {
namespace {
// Creates and returns a FlutterRendererConfig that renders to the view (if any)
// of a FlutterWindowsEngine, using OpenGL (via ANGLE).
// The user_data received by the render callbacks refers to the
// FlutterWindowsEngine.
FlutterRendererConfig GetOpenGLRendererConfig() {
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_reset_after_present = true;
config.open_gl.fbo_with_frame_info_callback =
[](void* user_data, const FlutterFrameInfo* info) -> uint32_t {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
if (host->view()) {
return host->view()->GetFrameBufferId(info->size.width,
info->size.height);
} else {
return kWindowFrameBufferID;
}
};
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();
};
config.open_gl.gl_external_texture_frame_callback =
[](void* user_data, int64_t texture_id, size_t width, size_t height,
FlutterOpenGLTexture* texture) -> bool {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
if (!host->texture_registrar()) {
return false;
}
return host->texture_registrar()->PopulateTexture(texture_id, width, height,
texture);
};
return config;
}
// Creates and returns a FlutterRendererConfig that renders to the view (if any)
// of a FlutterWindowsEngine, using software rasterization.
// The user_data received by the render callbacks refers to the
// FlutterWindowsEngine.
FlutterRendererConfig GetSoftwareRendererConfig() {
FlutterRendererConfig config = {};
config.type = kSoftware;
config.software.struct_size = sizeof(config.software);
config.software.surface_present_callback = [](void* user_data,
const void* allocation,
size_t row_bytes,
size_t height) {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
if (!host->view()) {
return false;
}
return host->view()->PresentSoftwareBitmap(allocation, row_bytes, height);
};
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_ = TaskRunner::Create(
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;
messenger_wrapper_ = std::make_unique<BinaryMessengerImpl>(messenger_.get());
message_dispatcher_ =
std::make_unique<IncomingMessageDispatcher>(messenger_.get());
FlutterWindowsTextureRegistrar::ResolveGlFunctions(gl_procs_);
texture_registrar_ =
std::make_unique<FlutterWindowsTextureRegistrar>(this, gl_procs_);
surface_manager_ = AngleSurfaceManager::Create();
#ifndef WINUWP
window_proc_delegate_manager_ =
std::make_unique<WindowProcDelegateManagerWin32>();
#endif
// Set up internal channels.
// TODO: Replace this with an embedder.h API. See
// https://github.com/flutter/flutter/issues/71099
settings_channel_ =
std::make_unique<BasicMessageChannel<rapidjson::Document>>(
messenger_wrapper_.get(), "flutter/settings",
&JsonMessageCodec::GetInstance());
}
FlutterWindowsEngine::~FlutterWindowsEngine() {
Stop();
}
void FlutterWindowsEngine::SetSwitches(
const std::vector<std::string>& switches) {
project_->SetSwitches(switches);
}
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<TaskRunner*>(user_data)->RunsTasksOnCurrentThread();
};
platform_task_runner.post_task_callback = [](FlutterTask task,
uint64_t target_time_nanos,
void* user_data) -> void {
static_cast<TaskRunner*>(user_data)->PostFlutterTask(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.on_pre_engine_restart_callback = [](void* user_data) {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
host->view()->OnPreEngineRestart();
};
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 = surface_manager_
? GetOpenGLRendererConfig()
: GetSoftwareRendererConfig();
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);
}
}
void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event,
FlutterKeyEventCallback callback,
void* user_data) {
if (engine_) {
embedder_api_.SendKeyEvent(engine_, &event, callback, user_data);
}
}
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::ReloadPlatformBrightness() {
if (engine_) {
SendSystemSettings();
}
}
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());
rapidjson::Document settings(rapidjson::kObjectType);
auto& allocator = settings.GetAllocator();
settings.AddMember("alwaysUse24HourFormat",
Prefer24HourTime(GetUserTimeFormat()), allocator);
settings.AddMember("textScaleFactor", 1.0, allocator);
settings.AddMember("platformBrightness",
Utf8FromUtf16(GetPreferredBrightness()), allocator);
settings_channel_->Send(settings);
}
bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) {
return (embedder_api_.RegisterExternalTexture(engine_, texture_id) ==
kSuccess);
}
bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) {
return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) ==
kSuccess);
}
bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
int64_t texture_id) {
return (embedder_api_.MarkExternalTextureFrameAvailable(
engine_, texture_id) == kSuccess);
}
bool FlutterWindowsEngine::DispatchSemanticsAction(
uint64_t target,
FlutterSemanticsAction action,
const std::vector<uint8_t>& data) {
return (embedder_api_.DispatchSemanticsAction(
engine_, target, action, data.data(), data.size()) == kSuccess);
}
void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) {
if (engine_ && semantics_enabled_ != enabled) {
semantics_enabled_ = enabled;
embedder_api_.UpdateSemanticsEnabled(engine_, enabled);
}
}
} // namespace flutter