flutter_flutter/shell/platform/windows/flutter_windows_engine.cc
stuartmorgan 498dbc6dc1
[windows] Mostly eliminate the state structs (#20662)
The Windows embedding was based on the GLFW embedding, which grew
organically from a singe-file implementation that used structs to manage
all of the important state. It is in the process of being converted to a
cleaner object-based architecture, but currently it is a hybrid of
objects and structs that have redundant data, making it very prone to
errors of forgetting to update pointers in multiple locations.

This reduces the remaining structs to only a single pointer to the
larger object that manages the responsibilities that handle is
associated with, so that there is no need to wire things together in
multiple places.

For now they continue to exist as projections of the larger objects, but
that will be eliminated over time by having an object structure that
better reflects the API structure.

Fixes https://github.com/flutter/flutter/issues/64250
2020-08-20 16:50:06 -07:00

260 lines
9.2 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)) {
task_runner_ = std::make_unique<Win32TaskRunner>(
GetCurrentThreadId(), [this](const auto* task) {
if (!engine_) {
std::cerr << "Cannot post an engine task when engine is not running."
<< std::endl;
return;
}
if (FlutterEngineRunTask(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 (FlutterEngineRunsAOTCompiledDartCode()) {
aot_data_ = project_->LoadAotData();
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<const char*> argv = {"placeholder"};
std::transform(
project_->switches().begin(), project_->switches().end(),
std::back_inserter(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.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 = FlutterEngineRun(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 = FlutterEngineShutdown(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(
FlutterDesktopOnRegistrarDestroyed callback) {
plugin_registrar_destruction_callback_ = callback;
}
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::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; });
FlutterEngineUpdateLocales(engine_, flutter_locale_list.data(),
flutter_locale_list.size());
// TODO: Send 'flutter/settings' channel settings here as well.
}
} // namespace flutter