Flutter Windows Embedder
flutter_windows_engine.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <dwmapi.h>
8 
9 #include <filesystem>
10 #include <shared_mutex>
11 #include <sstream>
12 
13 #include "flutter/fml/logging.h"
14 #include "flutter/fml/paths.h"
15 #include "flutter/fml/platform/win/wstring_conversion.h"
16 #include "flutter/fml/synchronization/waitable_event.h"
20 #include "flutter/shell/platform/embedder/embedder_struct_macros.h"
30 #include "flutter/third_party/accessibility/ax/ax_node.h"
32 
33 // winbase.h defines GetCurrentTime as a macro.
34 #undef GetCurrentTime
35 
36 static constexpr char kAccessibilityChannelName[] = "flutter/accessibility";
37 
38 namespace flutter {
39 
40 namespace {
41 
42 // Lifted from vsync_waiter_fallback.cc
43 static std::chrono::nanoseconds SnapToNextTick(
44  std::chrono::nanoseconds value,
45  std::chrono::nanoseconds tick_phase,
46  std::chrono::nanoseconds tick_interval) {
47  std::chrono::nanoseconds offset = (tick_phase - value) % tick_interval;
48  if (offset != std::chrono::nanoseconds::zero())
49  offset = offset + tick_interval;
50  return value + offset;
51 }
52 
53 // Creates and returns a FlutterRendererConfig that renders to the view (if any)
54 // of a FlutterWindowsEngine, using OpenGL (via ANGLE).
55 // The user_data received by the render callbacks refers to the
56 // FlutterWindowsEngine.
57 FlutterRendererConfig GetOpenGLRendererConfig() {
58  FlutterRendererConfig config = {};
59  config.type = kOpenGL;
60  config.open_gl.struct_size = sizeof(config.open_gl);
61  config.open_gl.make_current = [](void* user_data) -> bool {
62  auto host = static_cast<FlutterWindowsEngine*>(user_data);
63  if (!host->egl_manager()) {
64  return false;
65  }
66  return host->egl_manager()->render_context()->MakeCurrent();
67  };
68  config.open_gl.clear_current = [](void* user_data) -> bool {
69  auto host = static_cast<FlutterWindowsEngine*>(user_data);
70  if (!host->egl_manager()) {
71  return false;
72  }
73  return host->egl_manager()->render_context()->ClearCurrent();
74  };
75  config.open_gl.present = [](void* user_data) -> bool { FML_UNREACHABLE(); };
76  config.open_gl.fbo_reset_after_present = true;
77  config.open_gl.fbo_with_frame_info_callback =
78  [](void* user_data, const FlutterFrameInfo* info) -> uint32_t {
79  FML_UNREACHABLE();
80  };
81  config.open_gl.gl_proc_resolver = [](void* user_data,
82  const char* what) -> void* {
83  return reinterpret_cast<void*>(eglGetProcAddress(what));
84  };
85  config.open_gl.make_resource_current = [](void* user_data) -> bool {
86  auto host = static_cast<FlutterWindowsEngine*>(user_data);
87  if (!host->egl_manager()) {
88  return false;
89  }
90  return host->egl_manager()->resource_context()->MakeCurrent();
91  };
92  config.open_gl.gl_external_texture_frame_callback =
93  [](void* user_data, int64_t texture_id, size_t width, size_t height,
94  FlutterOpenGLTexture* texture) -> bool {
95  auto host = static_cast<FlutterWindowsEngine*>(user_data);
96  if (!host->texture_registrar()) {
97  return false;
98  }
99  return host->texture_registrar()->PopulateTexture(texture_id, width, height,
100  texture);
101  };
102  return config;
103 }
104 
105 // Creates and returns a FlutterRendererConfig that renders to the view (if any)
106 // of a FlutterWindowsEngine, using software rasterization.
107 // The user_data received by the render callbacks refers to the
108 // FlutterWindowsEngine.
109 FlutterRendererConfig GetSoftwareRendererConfig() {
110  FlutterRendererConfig config = {};
111  config.type = kSoftware;
112  config.software.struct_size = sizeof(config.software);
113  config.software.surface_present_callback =
114  [](void* user_data, const void* allocation, size_t row_bytes,
115  size_t height) {
116  FML_UNREACHABLE();
117  return false;
118  };
119  return config;
120 }
121 
122 // Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage.
123 static FlutterDesktopMessage ConvertToDesktopMessage(
124  const FlutterPlatformMessage& engine_message) {
126  message.struct_size = sizeof(message);
127  message.channel = engine_message.channel;
128  message.message = engine_message.message;
129  message.message_size = engine_message.message_size;
130  message.response_handle = engine_message.response_handle;
131  return message;
132 }
133 
134 // Converts a LanguageInfo struct to a FlutterLocale struct. |info| must outlive
135 // the returned value, since the returned FlutterLocale has pointers into it.
136 FlutterLocale CovertToFlutterLocale(const LanguageInfo& info) {
137  FlutterLocale locale = {};
138  locale.struct_size = sizeof(FlutterLocale);
139  locale.language_code = info.language.c_str();
140  if (!info.region.empty()) {
141  locale.country_code = info.region.c_str();
142  }
143  if (!info.script.empty()) {
144  locale.script_code = info.script.c_str();
145  }
146  return locale;
147 }
148 
149 } // namespace
150 
152  const FlutterProjectBundle& project,
153  std::shared_ptr<WindowsProcTable> windows_proc_table)
154  : project_(std::make_unique<FlutterProjectBundle>(project)),
155  windows_proc_table_(std::move(windows_proc_table)),
156  aot_data_(nullptr, nullptr),
157  lifecycle_manager_(std::make_unique<WindowsLifecycleManager>(this)) {
158  if (windows_proc_table_ == nullptr) {
159  windows_proc_table_ = std::make_shared<WindowsProcTable>();
160  }
161 
162  gl_ = egl::ProcTable::Create();
163 
164  embedder_api_.struct_size = sizeof(FlutterEngineProcTable);
165  FlutterEngineGetProcAddresses(&embedder_api_);
166 
167  task_runner_ =
168  std::make_unique<TaskRunner>(
169  embedder_api_.GetCurrentTime, [this](const auto* task) {
170  if (!engine_) {
171  FML_LOG(ERROR)
172  << "Cannot post an engine task when engine is not running.";
173  return;
174  }
175  if (embedder_api_.RunTask(engine_, task) != kSuccess) {
176  FML_LOG(ERROR) << "Failed to post an engine task.";
177  }
178  });
179 
180  // Set up the legacy structs backing the API handles.
181  messenger_ =
182  fml::RefPtr<FlutterDesktopMessenger>(new FlutterDesktopMessenger());
183  messenger_->SetEngine(this);
184  plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
185  plugin_registrar_->engine = this;
186 
187  messenger_wrapper_ =
188  std::make_unique<BinaryMessengerImpl>(messenger_->ToRef());
189  message_dispatcher_ =
190  std::make_unique<IncomingMessageDispatcher>(messenger_->ToRef());
191 
192  texture_registrar_ =
193  std::make_unique<FlutterWindowsTextureRegistrar>(this, gl_);
194 
195  // Check for impeller support.
196  auto& switches = project_->GetSwitches();
197  bool enable_impeller = true;
198  if (project_->impeller_switch() == FlutterImpellerSwitch::Enabled) {
199  enable_impeller = true;
200  } else if (project_->impeller_switch() == FlutterImpellerSwitch::Disabled) {
201  enable_impeller = false;
202  }
203  for (const auto& env_switch : switches) {
204  if (env_switch == "--enable-impeller" ||
205  env_switch == "--enable-impeller=true") {
206  enable_impeller = true;
207  } else if (env_switch == "--enable-impeller=false") {
208  enable_impeller = false;
209  }
210  }
211  enable_impeller_ = enable_impeller;
212 
213  egl_manager_ = egl::Manager::Create(
214  static_cast<egl::GpuPreference>(project_->gpu_preference()));
215  window_proc_delegate_manager_ = std::make_unique<WindowProcDelegateManager>();
216 
217  display_manager_ = std::make_shared<DisplayManagerWin32>(this);
218 
219  window_proc_delegate_manager_->RegisterTopLevelWindowProcDelegate(
220  [](HWND hwnd, UINT msg, WPARAM wpar, LPARAM lpar, void* user_data,
221  LRESULT* result) {
222  BASE_DCHECK(user_data);
223  FlutterWindowsEngine* that =
224  static_cast<FlutterWindowsEngine*>(user_data);
225 
226  BASE_DCHECK(that->display_manager_);
227  if (that->display_manager_->HandleWindowMessage(hwnd, msg, wpar, lpar,
228  result)) {
229  return true;
230  }
231 
232  BASE_DCHECK(that->lifecycle_manager_);
233  bool handled =
234  that->lifecycle_manager_->WindowProc(hwnd, msg, wpar, lpar, result);
235  if (handled) {
236  return true;
237  }
238  auto message_result =
239  that->window_manager_->HandleMessage(hwnd, msg, wpar, lpar);
240  if (message_result) {
241  *result = *message_result;
242  return true;
243  }
244  return false;
245  },
246  static_cast<void*>(this));
247 
248  // Set up internal channels.
249  // TODO: Replace this with an embedder.h API. See
250  // https://github.com/flutter/flutter/issues/71099
251  internal_plugin_registrar_ =
252  std::make_unique<PluginRegistrar>(plugin_registrar_.get());
253 
254  accessibility_plugin_ = std::make_unique<AccessibilityPlugin>(this);
255  AccessibilityPlugin::SetUp(messenger_wrapper_.get(),
256  accessibility_plugin_.get());
257 
258  cursor_handler_ =
259  std::make_unique<CursorHandler>(messenger_wrapper_.get(), this);
260  platform_handler_ =
261  std::make_unique<PlatformHandler>(messenger_wrapper_.get(), this);
262  window_manager_ = std::make_unique<WindowManager>(this);
263  settings_plugin_ = std::make_unique<SettingsPlugin>(messenger_wrapper_.get(),
264  task_runner_.get());
265 }
266 
267 FlutterWindowsEngine::~FlutterWindowsEngine() {
268  messenger_->SetEngine(nullptr);
269  Stop();
270 }
271 
272 FlutterWindowsEngine* FlutterWindowsEngine::GetEngineForId(int64_t engine_id) {
273  return reinterpret_cast<FlutterWindowsEngine*>(engine_id);
274 }
275 
276 void FlutterWindowsEngine::SetSwitches(
277  const std::vector<std::string>& switches) {
278  project_->SetSwitches(switches);
279 }
280 
281 bool FlutterWindowsEngine::Run() {
282  return Run("");
283 }
284 
285 bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
286  if (!project_->HasValidPaths()) {
287  FML_LOG(ERROR) << "Missing or unresolvable paths to assets.";
288  return false;
289  }
290  std::string assets_path_string = fml::PathToUtf8(project_->assets_path());
291  std::string icu_path_string = fml::PathToUtf8(project_->icu_path());
292  if (embedder_api_.RunsAOTCompiledDartCode()) {
293  aot_data_ = project_->LoadAotData(embedder_api_);
294  if (!aot_data_) {
295  FML_LOG(ERROR) << "Unable to start engine without AOT data.";
296  return false;
297  }
298  }
299 
300  // FlutterProjectArgs is expecting a full argv, so when processing it for
301  // flags the first item is treated as the executable and ignored. Add a dummy
302  // value so that all provided arguments are used.
303  std::string executable_name = GetExecutableName();
304  std::vector<const char*> argv = {executable_name.c_str()};
305  std::vector<std::string> switches = project_->GetSwitches();
306  if (enable_impeller_) {
307  if (std::find(switches.begin(), switches.end(),
308  "--impeller-use-sdfs=true") == switches.end() &&
309  std::find(switches.begin(), switches.end(),
310  "--impeller-use-sdfs=false") == switches.end()) {
311  switches.push_back("--impeller-use-sdfs=true");
312  }
313  if (std::find(switches.begin(), switches.end(), "--enable-impeller") ==
314  switches.end() &&
315  std::find(switches.begin(), switches.end(), "--enable-impeller=true") ==
316  switches.end()) {
317  // Impeller was enabled programmatically, so forward the switch to the
318  // engine.
319  switches.push_back("--enable-impeller");
320  }
321  } else if (project_->impeller_switch() == FlutterImpellerSwitch::Disabled) {
322  if (std::find(switches.begin(), switches.end(),
323  "--enable-impeller=false") == switches.end()) {
324  // Impeller was disabled programmatically, so forward the switch to the
325  // engine.
326  switches.push_back("--enable-impeller=false");
327  }
328  }
329  std::transform(
330  switches.begin(), switches.end(), std::back_inserter(argv),
331  [](const std::string& arg) -> const char* { return arg.c_str(); });
332 
333  const std::vector<std::string>& entrypoint_args =
334  project_->dart_entrypoint_arguments();
335  std::vector<const char*> entrypoint_argv;
336  std::transform(
337  entrypoint_args.begin(), entrypoint_args.end(),
338  std::back_inserter(entrypoint_argv),
339  [](const std::string& arg) -> const char* { return arg.c_str(); });
340 
341  // Configure task runners.
342  FlutterTaskRunnerDescription platform_task_runner = {};
343  platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
344  platform_task_runner.user_data = task_runner_.get();
345  platform_task_runner.runs_task_on_current_thread_callback =
346  [](void* user_data) -> bool {
347  return static_cast<TaskRunner*>(user_data)->RunsTasksOnCurrentThread();
348  };
349  platform_task_runner.post_task_callback = [](FlutterTask task,
350  uint64_t target_time_nanos,
351  void* user_data) -> void {
352  static_cast<TaskRunner*>(user_data)->PostFlutterTask(task,
353  target_time_nanos);
354  };
355  FlutterCustomTaskRunners custom_task_runners = {};
356  custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
357  custom_task_runners.platform_task_runner = &platform_task_runner;
358  custom_task_runners.thread_priority_setter =
360 
361  if (project_->ui_thread_policy() !=
363  custom_task_runners.ui_task_runner = &platform_task_runner;
364  } else {
365  FML_LOG(WARNING) << "Running with unmerged platform and UI threads. This "
366  "will be removed in future.";
367  }
368 
369  FlutterProjectArgs args = {};
370  args.struct_size = sizeof(FlutterProjectArgs);
371  args.shutdown_dart_vm_when_done = true;
372  args.assets_path = assets_path_string.c_str();
373  args.icu_data_path = icu_path_string.c_str();
374  args.command_line_argc = static_cast<int>(argv.size());
375  args.command_line_argv = argv.empty() ? nullptr : argv.data();
376  args.engine_id = reinterpret_cast<int64_t>(this);
377 
378  // Fail if conflicting non-default entrypoints are specified in the method
379  // argument and the project.
380  //
381  // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
382  // The entrypoint method parameter should eventually be removed from this
383  // method and only the entrypoint specified in project_ should be used.
384  if (!project_->dart_entrypoint().empty() && !entrypoint.empty() &&
385  project_->dart_entrypoint() != entrypoint) {
386  FML_LOG(ERROR) << "Conflicting entrypoints were specified in "
387  "FlutterDesktopEngineProperties.dart_entrypoint and "
388  "FlutterDesktopEngineRun(engine, entry_point). ";
389  return false;
390  }
391  if (!entrypoint.empty()) {
392  args.custom_dart_entrypoint = entrypoint.data();
393  } else if (!project_->dart_entrypoint().empty()) {
394  args.custom_dart_entrypoint = project_->dart_entrypoint().c_str();
395  }
396  args.dart_entrypoint_argc = static_cast<int>(entrypoint_argv.size());
397  args.dart_entrypoint_argv =
398  entrypoint_argv.empty() ? nullptr : entrypoint_argv.data();
399  args.platform_message_callback =
400  [](const FlutterPlatformMessage* engine_message,
401  void* user_data) -> void {
402  auto host = static_cast<FlutterWindowsEngine*>(user_data);
403  return host->HandlePlatformMessage(engine_message);
404  };
405  args.vsync_callback = [](void* user_data, intptr_t baton) -> void {
406  auto host = static_cast<FlutterWindowsEngine*>(user_data);
407  host->OnVsync(baton);
408  };
409  args.on_pre_engine_restart_callback = [](void* user_data) {
410  auto host = static_cast<FlutterWindowsEngine*>(user_data);
411  host->OnPreEngineRestart();
412  };
413  args.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
414  void* user_data) {
415  auto host = static_cast<FlutterWindowsEngine*>(user_data);
416 
417  auto view = host->view(update->view_id);
418  if (!view) {
419  return;
420  }
421 
422  auto accessibility_bridge = view->accessibility_bridge().lock();
423  if (!accessibility_bridge) {
424  return;
425  }
426 
427  for (size_t i = 0; i < update->node_count; i++) {
428  const FlutterSemanticsNode2* node = update->nodes[i];
429  accessibility_bridge->AddFlutterSemanticsNodeUpdate(*node);
430  }
431 
432  for (size_t i = 0; i < update->custom_action_count; i++) {
433  const FlutterSemanticsCustomAction2* action = update->custom_actions[i];
434  accessibility_bridge->AddFlutterSemanticsCustomActionUpdate(*action);
435  }
436 
437  accessibility_bridge->CommitUpdates();
438  };
439  args.root_isolate_create_callback = [](void* user_data) {
440  auto host = static_cast<FlutterWindowsEngine*>(user_data);
441  if (host->root_isolate_create_callback_) {
442  host->root_isolate_create_callback_();
443  }
444  };
445  args.channel_update_callback = [](const FlutterChannelUpdate* update,
446  void* user_data) {
447  auto host = static_cast<FlutterWindowsEngine*>(user_data);
448  if (SAFE_ACCESS(update, channel, nullptr) != nullptr) {
449  std::string channel_name(update->channel);
450  host->OnChannelUpdate(std::move(channel_name),
451  SAFE_ACCESS(update, listening, false));
452  }
453  };
454  args.view_focus_change_request_callback =
455  [](const FlutterViewFocusChangeRequest* request, void* user_data) {
456  auto host = static_cast<FlutterWindowsEngine*>(user_data);
457  host->OnViewFocusChangeRequest(request);
458  };
459 
460  args.custom_task_runners = &custom_task_runners;
461 
462  if (!platform_view_plugin_) {
463  platform_view_plugin_ = std::make_unique<PlatformViewPlugin>(
464  messenger_wrapper_.get(), task_runner_.get());
465  }
466  if (egl_manager_) {
467  auto resolver = [](const char* name) -> void* {
468  return reinterpret_cast<void*>(::eglGetProcAddress(name));
469  };
470 
471  // TODO(schectman) Pass the platform view manager to the compositor
472  // constructors: https://github.com/flutter/flutter/issues/143375
473  compositor_ =
474  std::make_unique<CompositorOpenGL>(this, resolver, enable_impeller_);
475  } else {
476  compositor_ = std::make_unique<CompositorSoftware>();
477  }
478 
479  FlutterCompositor compositor = {};
480  compositor.struct_size = sizeof(FlutterCompositor);
481  compositor.user_data = this;
482  compositor.create_backing_store_callback =
483  [](const FlutterBackingStoreConfig* config,
484  FlutterBackingStore* backing_store_out, void* user_data) -> bool {
485  auto host = static_cast<FlutterWindowsEngine*>(user_data);
486 
487  return host->compositor_->CreateBackingStore(*config, backing_store_out);
488  };
489 
490  compositor.collect_backing_store_callback =
491  [](const FlutterBackingStore* backing_store, void* user_data) -> bool {
492  auto host = static_cast<FlutterWindowsEngine*>(user_data);
493 
494  return host->compositor_->CollectBackingStore(backing_store);
495  };
496 
497  compositor.present_view_callback =
498  [](const FlutterPresentViewInfo* info) -> bool {
499  auto host = static_cast<FlutterWindowsEngine*>(info->user_data);
500 
501  return host->Present(info);
502  };
503  args.compositor = &compositor;
504 
505  if (aot_data_) {
506  args.aot_data = aot_data_.get();
507  }
508 
509  // The platform thread creates OpenGL contexts. These
510  // must be released to be used by the engine's threads.
511  FML_DCHECK(!egl_manager_ || !egl_manager_->HasContextCurrent());
512 
513  FlutterRendererConfig renderer_config;
514 
515  if (enable_impeller_) {
516  // Impeller does not support a Software backend. Avoid falling back and
517  // confusing the engine on which renderer is selected.
518  if (!egl_manager_) {
519  FML_LOG(ERROR) << "Could not create surface manager. Impeller backend "
520  "does not support software rendering.";
521  return false;
522  }
523  renderer_config = GetOpenGLRendererConfig();
524  } else {
525  renderer_config =
526  egl_manager_ ? GetOpenGLRendererConfig() : GetSoftwareRendererConfig();
527  }
528 
529  auto result = embedder_api_.Run(FLUTTER_ENGINE_VERSION, &renderer_config,
530  &args, this, &engine_);
531  if (result != kSuccess || engine_ == nullptr) {
532  FML_LOG(ERROR) << "Failed to start Flutter engine: error " << result;
533  return false;
534  }
535 
536  display_manager_->UpdateDisplays();
537 
538  SendSystemLocales();
539 
540  settings_plugin_->StartWatching();
541  settings_plugin_->SendSettings();
542 
543  InitializeKeyboard();
544 
545  return true;
546 }
547 
548 bool FlutterWindowsEngine::Stop() {
549  if (engine_) {
550  window_manager_->OnEngineShutdown();
551  for (const auto& [callback, registrar] :
552  plugin_registrar_destruction_callbacks_) {
553  callback(registrar);
554  }
555  FlutterEngineResult result = embedder_api_.Shutdown(engine_);
556  engine_ = nullptr;
557  return (result == kSuccess);
558  }
559  return false;
560 }
561 
562 std::unique_ptr<FlutterWindowsView> FlutterWindowsEngine::CreateView(
563  std::unique_ptr<WindowBindingHandler> window,
564  bool is_sized_to_content,
565  const BoxConstraints& box_constraints,
566  FlutterWindowsViewSizingDelegate* sizing_delegate) {
567  auto view_id = next_view_id_;
568  auto view = std::make_unique<FlutterWindowsView>(
569  view_id, this, std::move(window), is_sized_to_content, box_constraints,
570  sizing_delegate, windows_proc_table_);
571 
572  view->CreateRenderSurface();
573  view->UpdateSemanticsEnabled(semantics_enabled_);
574 
575  next_view_id_++;
576 
577  {
578  // Add the view to the embedder. This must happen before the engine
579  // is notified the view exists and starts presenting to it.
580  std::unique_lock write_lock(views_mutex_);
581  FML_DCHECK(views_.find(view_id) == views_.end());
582  views_[view_id] = view.get();
583  }
584 
585  if (!view->IsImplicitView()) {
586  FML_DCHECK(running());
587 
588  struct Captures {
589  fml::AutoResetWaitableEvent latch;
590  bool added;
591  };
592  Captures captures = {};
593 
594  FlutterWindowMetricsEvent metrics = view->CreateWindowMetricsEvent();
595 
596  FlutterAddViewInfo info = {};
597  info.struct_size = sizeof(FlutterAddViewInfo);
598  info.view_id = view_id;
599  info.view_metrics = &metrics;
600  info.user_data = &captures;
601  info.add_view_callback = [](const FlutterAddViewResult* result) {
602  Captures* captures = reinterpret_cast<Captures*>(result->user_data);
603  captures->added = result->added;
604  captures->latch.Signal();
605  };
606 
607  FlutterEngineResult result = embedder_api_.AddView(engine_, &info);
608  if (result != kSuccess) {
609  FML_LOG(ERROR)
610  << "Starting the add view operation failed. FlutterEngineAddView "
611  "returned an unexpected result: "
612  << result << ". This indicates a bug in the Windows embedder.";
613  FML_DCHECK(false);
614  return nullptr;
615  }
616 
617  // Block the platform thread until the engine has added the view.
618  // TODO(loicsharma): This blocks the platform thread eagerly and can
619  // cause unnecessary delay in input processing. Instead, this should block
620  // lazily only when the app does an operation which needs the view.
621  // https://github.com/flutter/flutter/issues/146248
622  captures.latch.Wait();
623 
624  if (!captures.added) {
625  // Adding the view failed. Update the embedder's state to match the
626  // engine's state. This is unexpected and indicates a bug in the Windows
627  // embedder.
628  FML_LOG(ERROR) << "FlutterEngineAddView failed to add view";
629  std::unique_lock write_lock(views_mutex_);
630  views_.erase(view_id);
631  return nullptr;
632  }
633  }
634 
635  return std::move(view);
636 }
637 
638 void FlutterWindowsEngine::RemoveView(FlutterViewId view_id) {
639  FML_DCHECK(running());
640 
641  // Notify the engine to stop rendering to the view if it isn't the implicit
642  // view. The engine and framework assume the implicit view always exists and
643  // can continue presenting.
644  if (view_id != kImplicitViewId) {
645  struct Captures {
646  fml::AutoResetWaitableEvent latch;
647  bool removed;
648  };
649  Captures captures = {};
650 
651  FlutterRemoveViewInfo info = {};
652  info.struct_size = sizeof(FlutterRemoveViewInfo);
653  info.view_id = view_id;
654  info.user_data = &captures;
655  info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
656  // This is invoked on an engine thread. If
657  // |FlutterRemoveViewResult.removed| is `true`, the engine guarantees the
658  // view won't be presented.
659  Captures* captures = reinterpret_cast<Captures*>(result->user_data);
660  captures->removed = result->removed;
661  captures->latch.Signal();
662  };
663 
664  FlutterEngineResult result = embedder_api_.RemoveView(engine_, &info);
665  if (result != kSuccess) {
666  FML_LOG(ERROR) << "Starting the remove view operation failed. "
667  "FlutterEngineRemoveView "
668  "returned an unexpected result: "
669  << result
670  << ". This indicates a bug in the Windows embedder.";
671  FML_DCHECK(false);
672  return;
673  }
674 
675  // Block the platform thread until the engine has removed the view.
676  // TODO(loicsharma): This blocks the platform thread eagerly and can
677  // cause unnecessary delay in input processing. Instead, this should block
678  // lazily only when an operation needs the view.
679  // https://github.com/flutter/flutter/issues/146248
680  captures.latch.Wait();
681 
682  if (!captures.removed) {
683  // Removing the view failed. This is unexpected and indicates a bug in the
684  // Windows embedder.
685  FML_LOG(ERROR) << "FlutterEngineRemoveView failed to remove view";
686  return;
687  }
688  }
689 
690  {
691  // The engine no longer presents to the view. Remove the view from the
692  // embedder.
693  std::unique_lock write_lock(views_mutex_);
694 
695  FML_DCHECK(views_.find(view_id) != views_.end());
696 
697  // Reset text input state if the removed view is the active text input
698  // view, to prevent stale view references.
699  if (text_input_plugin_) {
700  text_input_plugin_->OnViewRemoved(view_id);
701  }
702 
703  views_.erase(view_id);
704  }
705 }
706 
707 void FlutterWindowsEngine::OnVsync(intptr_t baton) {
708  std::chrono::nanoseconds current_time =
709  std::chrono::nanoseconds(embedder_api_.GetCurrentTime());
710  std::chrono::nanoseconds frame_interval = FrameInterval();
711  auto next = SnapToNextTick(current_time, start_time_, frame_interval);
712  embedder_api_.OnVsync(engine_, baton, next.count(),
713  (next + frame_interval).count());
714 }
715 
716 std::chrono::nanoseconds FlutterWindowsEngine::FrameInterval() {
717  if (frame_interval_override_.has_value()) {
718  return frame_interval_override_.value();
719  }
720  uint64_t interval = 16600000;
721 
722  DWM_TIMING_INFO timing_info = {};
723  timing_info.cbSize = sizeof(timing_info);
724  HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
725  if (result == S_OK && timing_info.rateRefresh.uiDenominator > 0 &&
726  timing_info.rateRefresh.uiNumerator > 0) {
727  interval = static_cast<double>(timing_info.rateRefresh.uiDenominator *
728  1000000000.0) /
729  static_cast<double>(timing_info.rateRefresh.uiNumerator);
730  }
731 
732  return std::chrono::nanoseconds(interval);
733 }
734 
735 FlutterWindowsView* FlutterWindowsEngine::view(FlutterViewId view_id) const {
736  std::shared_lock read_lock(views_mutex_);
737 
738  auto iterator = views_.find(view_id);
739  if (iterator == views_.end()) {
740  return nullptr;
741  }
742 
743  return iterator->second;
744 }
745 
746 // Returns the currently configured Plugin Registrar.
747 FlutterDesktopPluginRegistrarRef FlutterWindowsEngine::GetRegistrar() {
748  return plugin_registrar_.get();
749 }
750 
751 void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
754  plugin_registrar_destruction_callbacks_[callback] = registrar;
755 }
756 
757 void FlutterWindowsEngine::UpdateDisplay(
758  const std::vector<FlutterEngineDisplay>& displays) {
759  if (engine_) {
760  embedder_api_.NotifyDisplayUpdate(engine_,
761  kFlutterEngineDisplaysUpdateTypeStartup,
762  displays.data(), displays.size());
763  }
764 }
765 
766 void FlutterWindowsEngine::SendWindowMetricsEvent(
767  const FlutterWindowMetricsEvent& event) {
768  if (engine_) {
769  embedder_api_.SendWindowMetricsEvent(engine_, &event);
770  }
771 }
772 
773 void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) {
774  if (engine_) {
775  embedder_api_.SendPointerEvent(engine_, &event, 1);
776  }
777 }
778 
779 void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event,
780  FlutterKeyEventCallback callback,
781  void* user_data) {
782  if (engine_) {
783  embedder_api_.SendKeyEvent(engine_, &event, callback, user_data);
784  }
785 }
786 
787 void FlutterWindowsEngine::SendViewFocusEvent(
788  const FlutterViewFocusEvent& event) {
789  if (engine_) {
790  embedder_api_.SendViewFocusEvent(engine_, &event);
791  }
792 }
793 
794 bool FlutterWindowsEngine::SendPlatformMessage(
795  const char* channel,
796  const uint8_t* message,
797  const size_t message_size,
798  const FlutterDesktopBinaryReply reply,
799  void* user_data) {
800  FlutterPlatformMessageResponseHandle* response_handle = nullptr;
801  if (reply != nullptr && user_data != nullptr) {
802  FlutterEngineResult result =
803  embedder_api_.PlatformMessageCreateResponseHandle(
804  engine_, reply, user_data, &response_handle);
805  if (result != kSuccess) {
806  FML_LOG(ERROR) << "Failed to create response handle";
807  return false;
808  }
809  }
810 
811  FlutterPlatformMessage platform_message = {
812  sizeof(FlutterPlatformMessage),
813  channel,
814  message,
815  message_size,
816  response_handle,
817  };
818 
819  FlutterEngineResult message_result =
820  embedder_api_.SendPlatformMessage(engine_, &platform_message);
821  if (response_handle != nullptr) {
822  embedder_api_.PlatformMessageReleaseResponseHandle(engine_,
823  response_handle);
824  }
825  return message_result == kSuccess;
826 }
827 
828 void FlutterWindowsEngine::SendPlatformMessageResponse(
830  const uint8_t* data,
831  size_t data_length) {
832  embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
833 }
834 
835 void FlutterWindowsEngine::HandlePlatformMessage(
836  const FlutterPlatformMessage* engine_message) {
837  if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
838  FML_LOG(ERROR) << "Invalid message size received. Expected: "
839  << sizeof(FlutterPlatformMessage) << " but received "
840  << engine_message->struct_size;
841  return;
842  }
843 
844  auto message = ConvertToDesktopMessage(*engine_message);
845 
846  message_dispatcher_->HandleMessage(message, [this] {}, [this] {});
847 }
848 
849 void FlutterWindowsEngine::ReloadSystemFonts() {
850  embedder_api_.ReloadSystemFonts(engine_);
851 }
852 
853 void FlutterWindowsEngine::ScheduleFrame() {
854  embedder_api_.ScheduleFrame(engine_);
855 }
856 
857 void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) {
858  next_frame_callback_ = std::move(callback);
859 
860  embedder_api_.SetNextFrameCallback(
861  engine_,
862  [](void* user_data) {
863  // Embedder callback runs on raster thread. Switch back to platform
864  // thread.
865  FlutterWindowsEngine* self =
866  static_cast<FlutterWindowsEngine*>(user_data);
867 
868  self->task_runner_->PostTask(std::move(self->next_frame_callback_));
869  },
870  this);
871 }
872 
873 HCURSOR FlutterWindowsEngine::GetCursorByName(
874  const std::string& cursor_name) const {
875  static auto* cursors = new std::map<std::string, const wchar_t*>{
876  {"allScroll", IDC_SIZEALL},
877  {"basic", IDC_ARROW},
878  {"click", IDC_HAND},
879  {"forbidden", IDC_NO},
880  {"help", IDC_HELP},
881  {"move", IDC_SIZEALL},
882  {"none", nullptr},
883  {"noDrop", IDC_NO},
884  {"precise", IDC_CROSS},
885  {"progress", IDC_APPSTARTING},
886  {"text", IDC_IBEAM},
887  {"resizeColumn", IDC_SIZEWE},
888  {"resizeDown", IDC_SIZENS},
889  {"resizeDownLeft", IDC_SIZENESW},
890  {"resizeDownRight", IDC_SIZENWSE},
891  {"resizeLeft", IDC_SIZEWE},
892  {"resizeLeftRight", IDC_SIZEWE},
893  {"resizeRight", IDC_SIZEWE},
894  {"resizeRow", IDC_SIZENS},
895  {"resizeUp", IDC_SIZENS},
896  {"resizeUpDown", IDC_SIZENS},
897  {"resizeUpLeft", IDC_SIZENWSE},
898  {"resizeUpRight", IDC_SIZENESW},
899  {"resizeUpLeftDownRight", IDC_SIZENWSE},
900  {"resizeUpRightDownLeft", IDC_SIZENESW},
901  {"wait", IDC_WAIT},
902  };
903  const wchar_t* idc_name = IDC_ARROW;
904  auto it = cursors->find(cursor_name);
905  if (it != cursors->end()) {
906  idc_name = it->second;
907  }
908  return windows_proc_table_->LoadCursor(nullptr, idc_name);
909 }
910 
911 FlutterWindowsView* FlutterWindowsEngine::GetViewFromTopLevelWindow(
912  HWND hwnd) const {
913  std::shared_lock read_lock(views_mutex_);
914  auto const iterator =
915  std::find_if(views_.begin(), views_.end(), [hwnd](auto const& pair) {
916  FlutterWindowsView* const view = pair.second;
917  return GetAncestor(view->GetWindowHandle(), GA_ROOT) == hwnd;
918  });
919  if (iterator != views_.end()) {
920  return iterator->second;
921  }
922  return nullptr;
923 }
924 
925 void FlutterWindowsEngine::SendSystemLocales() {
926  std::vector<LanguageInfo> languages =
927  GetPreferredLanguageInfo(*windows_proc_table_);
928  std::vector<FlutterLocale> flutter_locales;
929  flutter_locales.reserve(languages.size());
930  for (const auto& info : languages) {
931  flutter_locales.push_back(CovertToFlutterLocale(info));
932  }
933  // Convert the locale list to the locale pointer list that must be provided.
934  std::vector<const FlutterLocale*> flutter_locale_list;
935  flutter_locale_list.reserve(flutter_locales.size());
936  std::transform(flutter_locales.begin(), flutter_locales.end(),
937  std::back_inserter(flutter_locale_list),
938  [](const auto& arg) -> const auto* { return &arg; });
939  embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
940  flutter_locale_list.size());
941 }
942 
943 void FlutterWindowsEngine::InitializeKeyboard() {
944  auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
945  KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
946  KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan =
947  [](UINT virtual_key, bool extended) {
948  return MapVirtualKey(virtual_key,
949  extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
950  };
951  keyboard_key_handler_ = std::move(CreateKeyboardKeyHandler(
952  internal_plugin_messenger, get_key_state, map_vk_to_scan));
953  text_input_plugin_ =
954  std::move(CreateTextInputPlugin(internal_plugin_messenger));
955 }
956 
957 std::unique_ptr<KeyboardHandlerBase>
958 FlutterWindowsEngine::CreateKeyboardKeyHandler(
959  BinaryMessenger* messenger,
962  auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>(messenger);
963  keyboard_key_handler->AddDelegate(
964  std::make_unique<KeyboardKeyEmbedderHandler>(
965  [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback,
966  void* user_data) {
967  return SendKeyEvent(event, callback, user_data);
968  },
969  get_key_state, map_vk_to_scan));
970  keyboard_key_handler->AddDelegate(
971  std::make_unique<KeyboardKeyChannelHandler>(messenger));
972  keyboard_key_handler->InitKeyboardChannel();
973  return keyboard_key_handler;
974 }
975 
976 std::unique_ptr<TextInputPlugin> FlutterWindowsEngine::CreateTextInputPlugin(
977  BinaryMessenger* messenger) {
978  return std::make_unique<TextInputPlugin>(messenger, this);
979 }
980 
981 bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) {
982  return (embedder_api_.RegisterExternalTexture(engine_, texture_id) ==
983  kSuccess);
984 }
985 
986 bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) {
987  return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) ==
988  kSuccess);
989 }
990 
991 bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
992  int64_t texture_id) {
993  return (embedder_api_.MarkExternalTextureFrameAvailable(
994  engine_, texture_id) == kSuccess);
995 }
996 
997 bool FlutterWindowsEngine::PostRasterThreadTask(fml::closure callback) const {
998  struct Captures {
999  fml::closure callback;
1000  };
1001  auto captures = new Captures();
1002  captures->callback = std::move(callback);
1003  if (embedder_api_.PostRenderThreadTask(
1004  engine_,
1005  [](void* opaque) {
1006  auto captures = reinterpret_cast<Captures*>(opaque);
1007  captures->callback();
1008  delete captures;
1009  },
1010  captures) == kSuccess) {
1011  return true;
1012  }
1013  delete captures;
1014  return false;
1015 }
1016 
1017 bool FlutterWindowsEngine::DispatchSemanticsAction(
1018  FlutterViewId view_id,
1019  uint64_t target,
1020  FlutterSemanticsAction action,
1021  fml::MallocMapping data) {
1022  FlutterSendSemanticsActionInfo info{
1023  .struct_size = sizeof(FlutterSendSemanticsActionInfo),
1024  .view_id = view_id,
1025  .node_id = target,
1026  .action = action,
1027  .data = data.GetMapping(),
1028  .data_length = data.GetSize(),
1029  };
1030  return (embedder_api_.SendSemanticsAction(engine_, &info));
1031 }
1032 
1033 void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) {
1034  if (engine_ && semantics_enabled_ != enabled) {
1035  std::shared_lock read_lock(views_mutex_);
1036 
1037  semantics_enabled_ = enabled;
1038  embedder_api_.UpdateSemanticsEnabled(engine_, enabled);
1039  for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
1040  iterator->second->UpdateSemanticsEnabled(enabled);
1041  }
1042  }
1043 }
1044 
1045 void FlutterWindowsEngine::OnPreEngineRestart() {
1046  // Reset the keyboard's state on hot restart.
1047  InitializeKeyboard();
1048 }
1049 
1050 std::string FlutterWindowsEngine::GetExecutableName() const {
1051  std::pair<bool, std::string> result = fml::paths::GetExecutablePath();
1052  if (result.first) {
1053  const std::string& executable_path = result.second;
1054  size_t last_separator = executable_path.find_last_of("/\\");
1055  if (last_separator == std::string::npos ||
1056  last_separator == executable_path.size() - 1) {
1057  return executable_path;
1058  }
1059  return executable_path.substr(last_separator + 1);
1060  }
1061  return "Flutter";
1062 }
1063 
1064 void FlutterWindowsEngine::UpdateAccessibilityFeatures() {
1065  UpdateHighContrastMode();
1066 }
1067 
1068 void FlutterWindowsEngine::UpdateHighContrastMode() {
1069  high_contrast_enabled_ = windows_proc_table_->GetHighContrastEnabled();
1070 
1071  SendAccessibilityFeatures();
1072  settings_plugin_->UpdateHighContrastMode(high_contrast_enabled_);
1073 }
1074 
1075 void FlutterWindowsEngine::SendAccessibilityFeatures() {
1076  int flags = 0;
1077 
1078  if (high_contrast_enabled_) {
1079  flags |=
1080  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
1081  }
1082 
1083  embedder_api_.UpdateAccessibilityFeatures(
1084  engine_, static_cast<FlutterAccessibilityFeature>(flags));
1085 }
1086 
1087 void FlutterWindowsEngine::RequestApplicationQuit(HWND hwnd,
1088  WPARAM wparam,
1089  LPARAM lparam,
1090  AppExitType exit_type) {
1091  platform_handler_->RequestAppExit(hwnd, wparam, lparam, exit_type, 0);
1092 }
1093 
1094 void FlutterWindowsEngine::OnQuit(std::optional<HWND> hwnd,
1095  std::optional<WPARAM> wparam,
1096  std::optional<LPARAM> lparam,
1097  UINT exit_code) {
1098  lifecycle_manager_->Quit(hwnd, wparam, lparam, exit_code);
1099 }
1100 
1101 void FlutterWindowsEngine::OnDwmCompositionChanged() {
1102  if (display_manager_) {
1103  display_manager_->UpdateDisplays();
1104  }
1105 
1106  std::shared_lock read_lock(views_mutex_);
1107  for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
1108  iterator->second->OnDwmCompositionChanged();
1109  }
1110 }
1111 
1112 void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,
1113  WindowStateEvent event) {
1114  lifecycle_manager_->OnWindowStateEvent(hwnd, event);
1115 }
1116 
1117 std::optional<LRESULT> FlutterWindowsEngine::ProcessExternalWindowMessage(
1118  HWND hwnd,
1119  UINT message,
1120  WPARAM wparam,
1121  LPARAM lparam) {
1122  if (lifecycle_manager_) {
1123  return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam,
1124  lparam);
1125  }
1126  return std::nullopt;
1127 }
1128 
1129 void FlutterWindowsEngine::UpdateFlutterCursor(
1130  const std::string& cursor_name) const {
1131  SetFlutterCursor(GetCursorByName(cursor_name));
1132 }
1133 
1134 void FlutterWindowsEngine::SetFlutterCursor(HCURSOR cursor) const {
1135  windows_proc_table_->SetCursor(cursor);
1136 }
1137 
1138 void FlutterWindowsEngine::OnChannelUpdate(std::string name, bool listening) {
1139  if (name == "flutter/platform" && listening) {
1140  lifecycle_manager_->BeginProcessingExit();
1141  } else if (name == "flutter/lifecycle" && listening) {
1142  lifecycle_manager_->BeginProcessingLifecycle();
1143  }
1144 }
1145 
1146 void FlutterWindowsEngine::OnViewFocusChangeRequest(
1147  const FlutterViewFocusChangeRequest* request) {
1148  std::shared_lock read_lock(views_mutex_);
1149 
1150  auto iterator = views_.find(request->view_id);
1151  if (iterator == views_.end()) {
1152  return;
1153  }
1154 
1155  FlutterWindowsView* view = iterator->second;
1156  view->Focus();
1157 }
1158 
1159 bool FlutterWindowsEngine::Present(const FlutterPresentViewInfo* info) {
1160  // This runs on the raster thread. Lock the views map for the entirety of the
1161  // present operation to block the platform thread from destroying the
1162  // view during the present.
1163  std::shared_lock read_lock(views_mutex_);
1164 
1165  auto iterator = views_.find(info->view_id);
1166  if (iterator == views_.end()) {
1167  return false;
1168  }
1169 
1170  FlutterWindowsView* view = iterator->second;
1171 
1172  return compositor_->Present(view, info->layers, info->layers_count);
1173 }
1174 
1175 bool FlutterWindowsEngine::HandleDisplayMonitorMessage(HWND hwnd,
1176  UINT message,
1177  WPARAM wparam,
1178  LPARAM lparam,
1179  LRESULT* result) {
1180  if (!display_manager_) {
1181  return false;
1182  }
1183 
1184  return display_manager_->HandleWindowMessage(hwnd, message, wparam, lparam,
1185  result);
1186 }
1187 
1188 } // namespace flutter
static void SetUp(BinaryMessenger *binary_messenger, AccessibilityPlugin *plugin)
FlutterWindowsEngine(const FlutterProjectBundle &project, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
FlutterWindowsView * view(FlutterViewId view_id) const
virtual void OnViewFocusChangeRequest(const FlutterViewFocusChangeRequest *request)
void HandlePlatformMessage(const FlutterPlatformMessage *)
void SetSwitches(const std::vector< std::string > &switches)
std::weak_ptr< AccessibilityBridgeWindows > accessibility_bridge()
std::function< SHORT(UINT, bool)> MapVirtualKeyToScanCode
static std::unique_ptr< Manager > Create(GpuPreference gpu_preference)
Definition: manager.cc:17
static std::shared_ptr< ProcTable > Create()
Definition: proc_table.cc:12
uint32_t texture_id
std::vector< FlutterEngineDisplay > * displays
void(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
struct _FlutterPlatformMessageResponseHandle FlutterDesktopMessageResponseHandle
void(* FlutterDesktopOnPluginRegistrarDestroyed)(FlutterDesktopPluginRegistrarRef)
@ RunOnSeparateThread
static constexpr char kAccessibilityChannelName[]
FlutterDesktopBinaryReply callback
Win32Message message
std::vector< LanguageInfo > GetPreferredLanguageInfo(const WindowsProcTable &windows_proc_table)
Definition: system_utils.cc:15
WindowStateEvent
An event representing a change in window state that may update the.
int64_t FlutterViewId
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
constexpr FlutterViewId kImplicitViewId