// 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 #include #include #include "flutter/shell/platform/common/cpp/client_wrapper/binary_messenger_impl.h" #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h" #include "flutter/shell/platform/common/cpp/json_message_codec.h" #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" #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, 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(user_data); if (!host->view()) { return false; } return host->view()->MakeCurrent(); }; config.open_gl.clear_current = [](void* user_data) -> bool { auto host = static_cast(user_data); if (!host->view()) { return false; } return host->view()->ClearContext(); }; config.open_gl.present = [](void* user_data) -> bool { auto host = static_cast(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(eglGetProcAddress(what)); }; config.open_gl.make_resource_current = [](void* user_data) -> bool { auto host = static_cast(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(project)), aot_data_(nullptr, nullptr) { embedder_api_.struct_size = sizeof(FlutterEngineProcTable); FlutterEngineGetProcAddresses(&embedder_api_); task_runner_ = TaskRunner::Create( 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(); messenger_->engine = this; plugin_registrar_ = std::make_unique(); plugin_registrar_->engine = this; messenger_wrapper_ = std::make_unique(messenger_.get()); message_dispatcher_ = std::make_unique(messenger_.get()); #ifndef WINUWP window_proc_delegate_manager_ = std::make_unique(); #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>( messenger_wrapper_.get(), "flutter/settings", &JsonMessageCodec::GetInstance()); } 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 switches = project_->GetSwitches(); std::vector 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& entrypoint_args = project_->dart_entrypoint_arguments(); std::vector 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(user_data)->RunsTasksOnCurrentThread(); }; platform_task_runner.post_task_callback = [](FlutterTask task, uint64_t target_time_nanos, void* user_data) -> void { static_cast(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(argv.size()); args.command_line_argv = argv.size() > 0 ? argv.data() : nullptr; args.dart_entrypoint_argc = static_cast(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(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 languages = GetPreferredLanguageInfo(); std::vector 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 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); // TODO: Implement dark mode support. // https://github.com/flutter/flutter/issues/54612 settings.AddMember("platformBrightness", "light", allocator); settings_channel_->Send(settings); } } // namespace flutter