12 #include "flutter/fml/logging.h"
13 #include "flutter/fml/paths.h"
14 #include "flutter/fml/platform/win/wstring_conversion.h"
18 #include "flutter/shell/platform/embedder/embedder_struct_macros.h"
24 #include "flutter/third_party/accessibility/ax/ax_node.h"
36 static std::chrono::nanoseconds SnapToNextTick(
37 std::chrono::nanoseconds value,
38 std::chrono::nanoseconds tick_phase,
39 std::chrono::nanoseconds tick_interval) {
40 std::chrono::nanoseconds offset = (tick_phase - value) % tick_interval;
41 if (offset != std::chrono::nanoseconds::zero())
42 offset = offset + tick_interval;
43 return value + offset;
50 FlutterRendererConfig GetOpenGLRendererConfig() {
51 FlutterRendererConfig config = {};
52 config.type = kOpenGL;
53 config.open_gl.struct_size =
sizeof(config.open_gl);
54 config.open_gl.make_current = [](
void*
user_data) ->
bool {
55 auto host =
static_cast<FlutterWindowsEngine*
>(
user_data);
59 return host->view()->MakeCurrent();
61 config.open_gl.clear_current = [](
void*
user_data) ->
bool {
62 auto host =
static_cast<FlutterWindowsEngine*
>(
user_data);
66 return host->view()->ClearContext();
68 config.open_gl.present = [](
void*
user_data) ->
bool {
69 auto host =
static_cast<FlutterWindowsEngine*
>(
user_data);
73 return host->view()->SwapBuffers();
75 config.open_gl.fbo_reset_after_present =
true;
76 config.open_gl.fbo_with_frame_info_callback =
77 [](
void*
user_data,
const FlutterFrameInfo* info) -> uint32_t {
78 auto host =
static_cast<FlutterWindowsEngine*
>(
user_data);
80 return host->view()->GetFrameBufferId(info->size.width,
86 config.open_gl.gl_proc_resolver = [](
void*
user_data,
87 const char* what) ->
void* {
88 return reinterpret_cast<void*
>(eglGetProcAddress(what));
90 config.open_gl.make_resource_current = [](
void*
user_data) ->
bool {
91 auto host =
static_cast<FlutterWindowsEngine*
>(
user_data);
95 return host->view()->MakeResourceCurrent();
97 config.open_gl.gl_external_texture_frame_callback =
99 FlutterOpenGLTexture* texture) ->
bool {
100 auto host =
static_cast<FlutterWindowsEngine*
>(
user_data);
101 if (!host->texture_registrar()) {
104 return host->texture_registrar()->PopulateTexture(
texture_id, width, height,
114 FlutterRendererConfig GetSoftwareRendererConfig() {
115 FlutterRendererConfig config = {};
116 config.type = kSoftware;
117 config.software.struct_size =
sizeof(config.software);
118 config.software.surface_present_callback = [](
void*
user_data,
119 const void* allocation,
122 auto host =
static_cast<FlutterWindowsEngine*
>(
user_data);
126 return host->view()->PresentSoftwareBitmap(allocation, row_bytes, height);
133 const FlutterPlatformMessage& engine_message) {
136 message.channel = engine_message.channel;
137 message.message = engine_message.message;
138 message.message_size = engine_message.message_size;
139 message.response_handle = engine_message.response_handle;
145 FlutterLocale CovertToFlutterLocale(
const LanguageInfo& info) {
146 FlutterLocale locale = {};
147 locale.struct_size =
sizeof(FlutterLocale);
148 locale.language_code = info.language.c_str();
149 if (!info.region.empty()) {
150 locale.country_code = info.region.c_str();
152 if (!info.script.empty()) {
153 locale.script_code = info.script.c_str();
162 aot_data_(nullptr, nullptr),
164 embedder_api_.struct_size =
sizeof(FlutterEngineProcTable);
165 FlutterEngineGetProcAddresses(&embedder_api_);
168 std::make_unique<TaskRunner>(
169 embedder_api_.GetCurrentTime, [
this](
const auto* task) {
172 <<
"Cannot post an engine task when engine is not running.";
175 if (embedder_api_.RunTask(engine_, task) != kSuccess) {
176 FML_LOG(ERROR) <<
"Failed to post an engine task.";
183 messenger_->SetEngine(
this);
184 plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
185 plugin_registrar_->engine =
this;
188 std::make_unique<BinaryMessengerImpl>(messenger_->ToRef());
189 message_dispatcher_ =
190 std::make_unique<IncomingMessageDispatcher>(messenger_->ToRef());
191 message_dispatcher_->SetMessageCallback(
196 engine->HandleAccessibilityMessage(messenger,
message);
198 static_cast<void*
>(
this));
202 std::make_unique<FlutterWindowsTextureRegistrar>(
this, gl_procs_);
205 auto& switches = project_->GetSwitches();
206 enable_impeller_ = std::find(switches.begin(), switches.end(),
207 "--enable-impeller=true") != switches.end();
210 window_proc_delegate_manager_ = std::make_unique<WindowProcDelegateManager>();
211 window_proc_delegate_manager_->RegisterTopLevelWindowProcDelegate(
212 [](HWND hwnd, UINT msg, WPARAM wpar, LPARAM lpar,
void*
user_data,
215 FlutterWindowsEngine* that =
216 static_cast<FlutterWindowsEngine*
>(
user_data);
217 BASE_DCHECK(that->lifecycle_manager_);
218 return that->lifecycle_manager_->WindowProc(hwnd, msg, wpar, lpar,
221 static_cast<void*
>(
this));
226 internal_plugin_registrar_ =
227 std::make_unique<PluginRegistrar>(plugin_registrar_.get());
229 std::make_unique<CursorHandler>(messenger_wrapper_.get(),
this);
231 std::make_unique<PlatformHandler>(messenger_wrapper_.get(),
this);
232 settings_plugin_ = std::make_unique<SettingsPlugin>(messenger_wrapper_.get(),
236 FlutterWindowsEngine::~FlutterWindowsEngine() {
237 messenger_->SetEngine(
nullptr);
241 void FlutterWindowsEngine::SetSwitches(
242 const std::vector<std::string>& switches) {
243 project_->SetSwitches(switches);
246 bool FlutterWindowsEngine::Run() {
250 bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
251 if (!project_->HasValidPaths()) {
252 FML_LOG(ERROR) <<
"Missing or unresolvable paths to assets.";
255 std::string assets_path_string = project_->assets_path().u8string();
256 std::string icu_path_string = project_->icu_path().u8string();
257 if (embedder_api_.RunsAOTCompiledDartCode()) {
258 aot_data_ = project_->LoadAotData(embedder_api_);
260 FML_LOG(ERROR) <<
"Unable to start engine without AOT data.";
268 std::string executable_name = GetExecutableName();
269 std::vector<const char*> argv = {executable_name.c_str()};
270 std::vector<std::string> switches = project_->GetSwitches();
272 switches.begin(), switches.end(), std::back_inserter(argv),
273 [](
const std::string& arg) ->
const char* { return arg.c_str(); });
275 const std::vector<std::string>& entrypoint_args =
276 project_->dart_entrypoint_arguments();
277 std::vector<const char*> entrypoint_argv;
279 entrypoint_args.begin(), entrypoint_args.end(),
280 std::back_inserter(entrypoint_argv),
281 [](
const std::string& arg) ->
const char* { return arg.c_str(); });
284 FlutterTaskRunnerDescription platform_task_runner = {};
285 platform_task_runner.struct_size =
sizeof(FlutterTaskRunnerDescription);
286 platform_task_runner.user_data = task_runner_.get();
287 platform_task_runner.runs_task_on_current_thread_callback =
291 platform_task_runner.post_task_callback = [](FlutterTask task,
292 uint64_t target_time_nanos,
297 FlutterCustomTaskRunners custom_task_runners = {};
298 custom_task_runners.struct_size =
sizeof(FlutterCustomTaskRunners);
299 custom_task_runners.platform_task_runner = &platform_task_runner;
300 custom_task_runners.thread_priority_setter =
303 FlutterProjectArgs args = {};
304 args.struct_size =
sizeof(FlutterProjectArgs);
305 args.shutdown_dart_vm_when_done =
true;
306 args.assets_path = assets_path_string.c_str();
307 args.icu_data_path = icu_path_string.c_str();
308 args.command_line_argc =
static_cast<int>(argv.size());
309 args.command_line_argv = argv.empty() ? nullptr : argv.data();
317 if (!project_->dart_entrypoint().empty() && !entrypoint.empty() &&
318 project_->dart_entrypoint() != entrypoint) {
319 FML_LOG(ERROR) <<
"Conflicting entrypoints were specified in "
320 "FlutterDesktopEngineProperties.dart_entrypoint and "
321 "FlutterDesktopEngineRun(engine, entry_point). ";
324 if (!entrypoint.empty()) {
325 args.custom_dart_entrypoint = entrypoint.data();
326 }
else if (!project_->dart_entrypoint().empty()) {
327 args.custom_dart_entrypoint = project_->dart_entrypoint().c_str();
329 args.dart_entrypoint_argc =
static_cast<int>(entrypoint_argv.size());
330 args.dart_entrypoint_argv =
331 entrypoint_argv.empty() ? nullptr : entrypoint_argv.data();
332 args.platform_message_callback =
333 [](
const FlutterPlatformMessage* engine_message,
338 args.vsync_callback = [](
void*
user_data, intptr_t baton) ->
void {
342 args.on_pre_engine_restart_callback = [](
void*
user_data) {
346 args.update_semantics_callback2 = [](
const FlutterSemanticsUpdate2* update,
349 auto view = host->
view();
355 if (!accessibility_bridge) {
359 for (
size_t i = 0; i < update->node_count; i++) {
360 const FlutterSemanticsNode2* node = update->nodes[i];
361 accessibility_bridge->AddFlutterSemanticsNodeUpdate(*node);
364 for (
size_t i = 0; i < update->custom_action_count; i++) {
365 const FlutterSemanticsCustomAction2*
action = update->custom_actions[i];
366 accessibility_bridge->AddFlutterSemanticsCustomActionUpdate(*
action);
369 accessibility_bridge->CommitUpdates();
371 args.root_isolate_create_callback = [](
void*
user_data) {
373 if (host->root_isolate_create_callback_) {
374 host->root_isolate_create_callback_();
377 args.channel_update_callback = [](
const FlutterChannelUpdate* update,
380 if (SAFE_ACCESS(update, channel,
nullptr) !=
nullptr) {
381 std::string channel_name(update->channel);
382 host->OnChannelUpdate(std::move(channel_name),
383 SAFE_ACCESS(update, listening,
false));
387 args.custom_task_runners = &custom_task_runners;
390 args.aot_data = aot_data_.get();
395 FML_DCHECK(!surface_manager_ || !surface_manager_->HasContextCurrent());
397 FlutterRendererConfig renderer_config;
399 if (enable_impeller_) {
402 if (!surface_manager_) {
403 FML_LOG(ERROR) <<
"Could not create surface manager. Impeller backend "
404 "does not support software rendering.";
407 renderer_config = GetOpenGLRendererConfig();
409 renderer_config = surface_manager_ ? GetOpenGLRendererConfig()
410 : GetSoftwareRendererConfig();
413 auto result = embedder_api_.Run(FLUTTER_ENGINE_VERSION, &renderer_config,
414 &args,
this, &engine_);
415 if (result != kSuccess || engine_ ==
nullptr) {
416 FML_LOG(ERROR) <<
"Failed to start Flutter engine: error " << result;
421 FlutterEngineDisplay display = {};
422 display.struct_size =
sizeof(FlutterEngineDisplay);
423 display.display_id = 0;
424 display.single_display =
true;
425 display.refresh_rate =
426 1.0 / (
static_cast<double>(FrameInterval().count()) / 1000000000.0);
428 std::vector<FlutterEngineDisplay> displays = {display};
429 embedder_api_.NotifyDisplayUpdate(engine_,
430 kFlutterEngineDisplaysUpdateTypeStartup,
431 displays.data(), displays.size());
436 settings_plugin_->StartWatching();
437 settings_plugin_->SendSettings();
442 bool FlutterWindowsEngine::Stop() {
444 for (
const auto& [
callback, registrar] :
445 plugin_registrar_destruction_callbacks_) {
448 FlutterEngineResult result = embedder_api_.Shutdown(engine_);
450 return (result == kSuccess);
457 InitializeKeyboard();
460 void FlutterWindowsEngine::OnVsync(intptr_t baton) {
461 std::chrono::nanoseconds current_time =
462 std::chrono::nanoseconds(embedder_api_.GetCurrentTime());
463 std::chrono::nanoseconds frame_interval = FrameInterval();
464 auto next = SnapToNextTick(current_time, start_time_, frame_interval);
465 embedder_api_.OnVsync(engine_, baton, next.count(),
466 (next + frame_interval).count());
469 std::chrono::nanoseconds FlutterWindowsEngine::FrameInterval() {
470 if (frame_interval_override_.has_value()) {
471 return frame_interval_override_.value();
473 uint64_t interval = 16600000;
475 DWM_TIMING_INFO timing_info = {};
476 timing_info.cbSize =
sizeof(timing_info);
477 HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
478 if (result == S_OK && timing_info.rateRefresh.uiDenominator > 0 &&
479 timing_info.rateRefresh.uiNumerator > 0) {
480 interval =
static_cast<double>(timing_info.rateRefresh.uiDenominator *
482 static_cast<double>(timing_info.rateRefresh.uiNumerator);
485 return std::chrono::nanoseconds(interval);
490 return plugin_registrar_.get();
493 void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
496 plugin_registrar_destruction_callbacks_[
callback] = registrar;
499 void FlutterWindowsEngine::SendWindowMetricsEvent(
500 const FlutterWindowMetricsEvent& event) {
502 embedder_api_.SendWindowMetricsEvent(engine_, &event);
506 void FlutterWindowsEngine::SendPointerEvent(
const FlutterPointerEvent& event) {
508 embedder_api_.SendPointerEvent(engine_, &event, 1);
512 void FlutterWindowsEngine::SendKeyEvent(
const FlutterKeyEvent& event,
520 bool FlutterWindowsEngine::SendPlatformMessage(
523 const size_t message_size,
526 FlutterPlatformMessageResponseHandle* response_handle =
nullptr;
527 if (reply !=
nullptr &&
user_data !=
nullptr) {
528 FlutterEngineResult result =
529 embedder_api_.PlatformMessageCreateResponseHandle(
530 engine_, reply,
user_data, &response_handle);
531 if (result != kSuccess) {
532 FML_LOG(ERROR) <<
"Failed to create response handle";
537 FlutterPlatformMessage platform_message = {
538 sizeof(FlutterPlatformMessage),
545 FlutterEngineResult message_result =
546 embedder_api_.SendPlatformMessage(engine_, &platform_message);
547 if (response_handle !=
nullptr) {
548 embedder_api_.PlatformMessageReleaseResponseHandle(engine_,
551 return message_result == kSuccess;
554 void FlutterWindowsEngine::SendPlatformMessageResponse(
557 size_t data_length) {
558 embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
561 void FlutterWindowsEngine::HandlePlatformMessage(
562 const FlutterPlatformMessage* engine_message) {
563 if (engine_message->struct_size !=
sizeof(FlutterPlatformMessage)) {
564 FML_LOG(ERROR) <<
"Invalid message size received. Expected: "
565 <<
sizeof(FlutterPlatformMessage) <<
" but received "
566 << engine_message->struct_size;
570 auto message = ConvertToDesktopMessage(*engine_message);
572 message_dispatcher_->HandleMessage(
573 message, [
this] {}, [
this] {});
576 void FlutterWindowsEngine::ReloadSystemFonts() {
577 embedder_api_.ReloadSystemFonts(engine_);
580 void FlutterWindowsEngine::ScheduleFrame() {
581 embedder_api_.ScheduleFrame(engine_);
584 void FlutterWindowsEngine::SetNextFrameCallback(fml::closure
callback) {
585 next_frame_callback_ = std::move(
callback);
587 embedder_api_.SetNextFrameCallback(
595 self->task_runner_->PostTask(std::move(self->next_frame_callback_));
601 if (lifecycle_manager_) {
602 lifecycle_manager_->SetLifecycleState(state);
606 void FlutterWindowsEngine::SendSystemLocales() {
607 std::vector<LanguageInfo> languages =
609 std::vector<FlutterLocale> flutter_locales;
610 flutter_locales.reserve(languages.size());
611 for (
const auto& info : languages) {
612 flutter_locales.push_back(CovertToFlutterLocale(info));
615 std::vector<const FlutterLocale*> flutter_locale_list;
616 flutter_locale_list.reserve(flutter_locales.size());
617 std::transform(flutter_locales.begin(), flutter_locales.end(),
618 std::back_inserter(flutter_locale_list),
619 [](
const auto& arg) ->
const auto* { return &arg; });
620 embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
621 flutter_locale_list.size());
624 void FlutterWindowsEngine::InitializeKeyboard() {
625 if (view_ ==
nullptr) {
626 FML_LOG(ERROR) <<
"Cannot initialize keyboard on Windows headless mode.";
629 auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
630 KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
631 KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan =
632 [](UINT virtual_key,
bool extended) {
633 return MapVirtualKey(virtual_key,
634 extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
636 keyboard_key_handler_ = std::move(CreateKeyboardKeyHandler(
637 internal_plugin_messenger, get_key_state, map_vk_to_scan));
639 std::move(CreateTextInputPlugin(internal_plugin_messenger));
642 std::unique_ptr<KeyboardHandlerBase>
643 FlutterWindowsEngine::CreateKeyboardKeyHandler(
647 auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>(messenger);
648 keyboard_key_handler->AddDelegate(
649 std::make_unique<KeyboardKeyEmbedderHandler>(
650 [
this](
const FlutterKeyEvent& event, FlutterKeyEventCallback
callback,
654 get_key_state, map_vk_to_scan));
655 keyboard_key_handler->AddDelegate(
656 std::make_unique<KeyboardKeyChannelHandler>(messenger));
657 keyboard_key_handler->InitKeyboardChannel();
658 return keyboard_key_handler;
661 std::unique_ptr<TextInputPlugin> FlutterWindowsEngine::CreateTextInputPlugin(
663 return std::make_unique<TextInputPlugin>(messenger, view_);
666 bool FlutterWindowsEngine::RegisterExternalTexture(int64_t
texture_id) {
667 return (embedder_api_.RegisterExternalTexture(engine_,
texture_id) ==
671 bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t
texture_id) {
672 return (embedder_api_.UnregisterExternalTexture(engine_,
texture_id) ==
676 bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
678 return (embedder_api_.MarkExternalTextureFrameAvailable(
682 bool FlutterWindowsEngine::PostRasterThreadTask(fml::closure
callback) {
686 auto captures =
new Captures();
687 captures->callback = std::move(
callback);
688 if (embedder_api_.PostRenderThreadTask(
691 auto captures = reinterpret_cast<Captures*>(opaque);
692 captures->callback();
695 captures) == kSuccess) {
702 bool FlutterWindowsEngine::DispatchSemanticsAction(
704 FlutterSemanticsAction
action,
705 fml::MallocMapping data) {
706 return (embedder_api_.DispatchSemanticsAction(engine_, target,
action,
708 data.GetSize()) == kSuccess);
711 void FlutterWindowsEngine::UpdateSemanticsEnabled(
bool enabled) {
712 if (engine_ && semantics_enabled_ != enabled) {
713 semantics_enabled_ = enabled;
714 embedder_api_.UpdateSemanticsEnabled(engine_, enabled);
715 view_->UpdateSemanticsEnabled(enabled);
719 void FlutterWindowsEngine::OnPreEngineRestart() {
722 InitializeKeyboard();
726 std::string FlutterWindowsEngine::GetExecutableName()
const {
727 std::pair<bool, std::string> result = fml::paths::GetExecutablePath();
729 const std::string& executable_path = result.second;
730 size_t last_separator = executable_path.find_last_of(
"/\\");
731 if (last_separator == std::string::npos ||
732 last_separator == executable_path.size() - 1) {
733 return executable_path;
735 return executable_path.substr(last_separator + 1);
740 void FlutterWindowsEngine::UpdateAccessibilityFeatures(
741 FlutterAccessibilityFeature flags) {
742 embedder_api_.UpdateAccessibilityFeatures(engine_, flags);
745 void FlutterWindowsEngine::UpdateHighContrastEnabled(
bool enabled) {
746 high_contrast_enabled_ = enabled;
747 int flags = EnabledAccessibilityFeatures();
750 FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
753 ~FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
755 UpdateAccessibilityFeatures(
static_cast<FlutterAccessibilityFeature
>(flags));
756 settings_plugin_->UpdateHighContrastMode(enabled);
759 int FlutterWindowsEngine::EnabledAccessibilityFeatures()
const {
761 if (high_contrast_enabled()) {
763 FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
770 void FlutterWindowsEngine::HandleAccessibilityMessage(
773 const auto& codec = StandardMessageCodec::GetInstance();
774 auto data = codec.DecodeMessage(
message->message,
message->message_size);
777 if (
type.compare(
"announce") == 0) {
778 if (semantics_enabled_) {
783 std::wstring wide_text = fml::Utf8ToWideString(
text);
784 view_->AnnounceAlert(wide_text);
787 SendPlatformMessageResponse(
message->response_handle,
788 reinterpret_cast<const uint8_t*
>(
""), 0);
791 void FlutterWindowsEngine::RequestApplicationQuit(HWND hwnd,
795 platform_handler_->RequestAppExit(hwnd, wparam, lparam, exit_type, 0);
798 void FlutterWindowsEngine::OnQuit(std::optional<HWND> hwnd,
799 std::optional<WPARAM> wparam,
800 std::optional<LPARAM> lparam,
802 lifecycle_manager_->Quit(hwnd, wparam, lparam, exit_code);
805 void FlutterWindowsEngine::OnDwmCompositionChanged() {
806 view_->OnDwmCompositionChanged();
809 void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,
811 lifecycle_manager_->OnWindowStateEvent(hwnd, event);
814 std::optional<LRESULT> FlutterWindowsEngine::ProcessExternalWindowMessage(
819 if (lifecycle_manager_) {
820 return lifecycle_manager_->ExternalWindowMessage(hwnd,
message, wparam,
826 void FlutterWindowsEngine::OnChannelUpdate(std::string name,
bool listening) {
827 if (name ==
"flutter/platform" && listening) {
828 lifecycle_manager_->BeginProcessingExit();
829 }
else if (name ==
"flutter/lifecycle" && listening) {
830 lifecycle_manager_->BeginProcessingLifecycle();