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  if (project_->enable_flutter_gpu()) {
330  if (std::find(switches.begin(), switches.end(), "--enable-flutter-gpu") ==
331  switches.end() &&
332  std::find(switches.begin(), switches.end(),
333  "--enable-flutter-gpu=true") == switches.end()) {
334  // Flutter GPU was enabled programmatically, so forward the switch to
335  // the engine.
336  switches.push_back("--enable-flutter-gpu");
337  }
338  }
339  std::transform(
340  switches.begin(), switches.end(), std::back_inserter(argv),
341  [](const std::string& arg) -> const char* { return arg.c_str(); });
342 
343  const std::vector<std::string>& entrypoint_args =
344  project_->dart_entrypoint_arguments();
345  std::vector<const char*> entrypoint_argv;
346  std::transform(
347  entrypoint_args.begin(), entrypoint_args.end(),
348  std::back_inserter(entrypoint_argv),
349  [](const std::string& arg) -> const char* { return arg.c_str(); });
350 
351  // Configure task runners.
352  FlutterTaskRunnerDescription platform_task_runner = {};
353  platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
354  platform_task_runner.user_data = task_runner_.get();
355  platform_task_runner.runs_task_on_current_thread_callback =
356  [](void* user_data) -> bool {
357  return static_cast<TaskRunner*>(user_data)->RunsTasksOnCurrentThread();
358  };
359  platform_task_runner.post_task_callback = [](FlutterTask task,
360  uint64_t target_time_nanos,
361  void* user_data) -> void {
362  static_cast<TaskRunner*>(user_data)->PostFlutterTask(task,
363  target_time_nanos);
364  };
365  FlutterCustomTaskRunners custom_task_runners = {};
366  custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
367  custom_task_runners.platform_task_runner = &platform_task_runner;
368  custom_task_runners.thread_priority_setter =
370 
371  if (project_->ui_thread_policy() !=
373  custom_task_runners.ui_task_runner = &platform_task_runner;
374  } else {
375  FML_LOG(WARNING) << "Running with unmerged platform and UI threads. This "
376  "will be removed in future.";
377  }
378 
379  FlutterProjectArgs args = {};
380  args.struct_size = sizeof(FlutterProjectArgs);
381  args.shutdown_dart_vm_when_done = true;
382  args.assets_path = assets_path_string.c_str();
383  args.icu_data_path = icu_path_string.c_str();
384  args.command_line_argc = static_cast<int>(argv.size());
385  args.command_line_argv = argv.empty() ? nullptr : argv.data();
386  args.engine_id = reinterpret_cast<int64_t>(this);
387 
388  // Fail if conflicting non-default entrypoints are specified in the method
389  // argument and the project.
390  //
391  // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
392  // The entrypoint method parameter should eventually be removed from this
393  // method and only the entrypoint specified in project_ should be used.
394  if (!project_->dart_entrypoint().empty() && !entrypoint.empty() &&
395  project_->dart_entrypoint() != entrypoint) {
396  FML_LOG(ERROR) << "Conflicting entrypoints were specified in "
397  "FlutterDesktopEngineProperties.dart_entrypoint and "
398  "FlutterDesktopEngineRun(engine, entry_point). ";
399  return false;
400  }
401  if (!entrypoint.empty()) {
402  args.custom_dart_entrypoint = entrypoint.data();
403  } else if (!project_->dart_entrypoint().empty()) {
404  args.custom_dart_entrypoint = project_->dart_entrypoint().c_str();
405  }
406  args.dart_entrypoint_argc = static_cast<int>(entrypoint_argv.size());
407  args.dart_entrypoint_argv =
408  entrypoint_argv.empty() ? nullptr : entrypoint_argv.data();
409  args.platform_message_callback =
410  [](const FlutterPlatformMessage* engine_message,
411  void* user_data) -> void {
412  auto host = static_cast<FlutterWindowsEngine*>(user_data);
413  return host->HandlePlatformMessage(engine_message);
414  };
415  args.vsync_callback = [](void* user_data, intptr_t baton) -> void {
416  auto host = static_cast<FlutterWindowsEngine*>(user_data);
417  host->OnVsync(baton);
418  };
419  args.on_pre_engine_restart_callback = [](void* user_data) {
420  auto host = static_cast<FlutterWindowsEngine*>(user_data);
421  host->OnPreEngineRestart();
422  };
423  args.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
424  void* user_data) {
425  auto host = static_cast<FlutterWindowsEngine*>(user_data);
426 
427  auto view = host->view(update->view_id);
428  if (!view) {
429  return;
430  }
431 
432  auto accessibility_bridge = view->accessibility_bridge().lock();
433  if (!accessibility_bridge) {
434  return;
435  }
436 
437  for (size_t i = 0; i < update->node_count; i++) {
438  const FlutterSemanticsNode2* node = update->nodes[i];
439  accessibility_bridge->AddFlutterSemanticsNodeUpdate(*node);
440  }
441 
442  for (size_t i = 0; i < update->custom_action_count; i++) {
443  const FlutterSemanticsCustomAction2* action = update->custom_actions[i];
444  accessibility_bridge->AddFlutterSemanticsCustomActionUpdate(*action);
445  }
446 
447  accessibility_bridge->CommitUpdates();
448  };
449  args.root_isolate_create_callback = [](void* user_data) {
450  auto host = static_cast<FlutterWindowsEngine*>(user_data);
451  if (host->root_isolate_create_callback_) {
452  host->root_isolate_create_callback_();
453  }
454  };
455  args.channel_update_callback = [](const FlutterChannelUpdate* update,
456  void* user_data) {
457  auto host = static_cast<FlutterWindowsEngine*>(user_data);
458  if (SAFE_ACCESS(update, channel, nullptr) != nullptr) {
459  std::string channel_name(update->channel);
460  host->OnChannelUpdate(std::move(channel_name),
461  SAFE_ACCESS(update, listening, false));
462  }
463  };
464  args.view_focus_change_request_callback =
465  [](const FlutterViewFocusChangeRequest* request, void* user_data) {
466  auto host = static_cast<FlutterWindowsEngine*>(user_data);
467  host->OnViewFocusChangeRequest(request);
468  };
469 
470  args.custom_task_runners = &custom_task_runners;
471 
472  if (!platform_view_plugin_) {
473  platform_view_plugin_ = std::make_unique<PlatformViewPlugin>(
474  messenger_wrapper_.get(), task_runner_.get());
475  }
476  if (egl_manager_) {
477  auto resolver = [](const char* name) -> void* {
478  return reinterpret_cast<void*>(::eglGetProcAddress(name));
479  };
480 
481  // TODO(schectman) Pass the platform view manager to the compositor
482  // constructors: https://github.com/flutter/flutter/issues/143375
483  compositor_ =
484  std::make_unique<CompositorOpenGL>(this, resolver, enable_impeller_);
485  } else {
486  compositor_ = std::make_unique<CompositorSoftware>();
487  }
488 
489  FlutterCompositor compositor = {};
490  compositor.struct_size = sizeof(FlutterCompositor);
491  compositor.user_data = this;
492  compositor.create_backing_store_callback =
493  [](const FlutterBackingStoreConfig* config,
494  FlutterBackingStore* backing_store_out, void* user_data) -> bool {
495  auto host = static_cast<FlutterWindowsEngine*>(user_data);
496 
497  return host->compositor_->CreateBackingStore(*config, backing_store_out);
498  };
499 
500  compositor.collect_backing_store_callback =
501  [](const FlutterBackingStore* backing_store, void* user_data) -> bool {
502  auto host = static_cast<FlutterWindowsEngine*>(user_data);
503 
504  return host->compositor_->CollectBackingStore(backing_store);
505  };
506 
507  compositor.present_view_callback =
508  [](const FlutterPresentViewInfo* info) -> bool {
509  auto host = static_cast<FlutterWindowsEngine*>(info->user_data);
510 
511  return host->Present(info);
512  };
513  args.compositor = &compositor;
514 
515  if (aot_data_) {
516  args.aot_data = aot_data_.get();
517  }
518 
519  // The platform thread creates OpenGL contexts. These
520  // must be released to be used by the engine's threads.
521  FML_DCHECK(!egl_manager_ || !egl_manager_->HasContextCurrent());
522 
523  FlutterRendererConfig renderer_config;
524 
525  if (enable_impeller_) {
526  // Impeller does not support a Software backend. Avoid falling back and
527  // confusing the engine on which renderer is selected.
528  if (!egl_manager_) {
529  FML_LOG(ERROR) << "Could not create surface manager. Impeller backend "
530  "does not support software rendering.";
531  return false;
532  }
533  renderer_config = GetOpenGLRendererConfig();
534  } else {
535  renderer_config =
536  egl_manager_ ? GetOpenGLRendererConfig() : GetSoftwareRendererConfig();
537  }
538 
539  auto result = embedder_api_.Run(FLUTTER_ENGINE_VERSION, &renderer_config,
540  &args, this, &engine_);
541  if (result != kSuccess || engine_ == nullptr) {
542  FML_LOG(ERROR) << "Failed to start Flutter engine: error " << result;
543  return false;
544  }
545 
546  display_manager_->UpdateDisplays();
547 
548  SendSystemLocales();
549 
550  settings_plugin_->StartWatching();
551  settings_plugin_->SendSettings();
552 
553  InitializeKeyboard();
554 
555  return true;
556 }
557 
558 bool FlutterWindowsEngine::Stop() {
559  if (engine_) {
560  window_manager_->OnEngineShutdown();
561  for (const auto& [callback, registrar] :
562  plugin_registrar_destruction_callbacks_) {
563  callback(registrar);
564  }
565  FlutterEngineResult result = embedder_api_.Shutdown(engine_);
566  engine_ = nullptr;
567  return (result == kSuccess);
568  }
569  return false;
570 }
571 
572 std::unique_ptr<FlutterWindowsView> FlutterWindowsEngine::CreateView(
573  std::unique_ptr<WindowBindingHandler> window,
574  bool is_sized_to_content,
575  const BoxConstraints& box_constraints,
576  FlutterWindowsViewSizingDelegate* sizing_delegate) {
577  auto view_id = next_view_id_;
578  auto view = std::make_unique<FlutterWindowsView>(
579  view_id, this, std::move(window), is_sized_to_content, box_constraints,
580  sizing_delegate, windows_proc_table_);
581 
582  view->CreateRenderSurface();
583  view->UpdateSemanticsEnabled(semantics_enabled_);
584 
585  next_view_id_++;
586 
587  {
588  // Add the view to the embedder. This must happen before the engine
589  // is notified the view exists and starts presenting to it.
590  std::unique_lock write_lock(views_mutex_);
591  FML_DCHECK(views_.find(view_id) == views_.end());
592  views_[view_id] = view.get();
593  }
594 
595  if (!view->IsImplicitView()) {
596  FML_DCHECK(running());
597 
598  struct Captures {
599  fml::AutoResetWaitableEvent latch;
600  bool added;
601  };
602  Captures captures = {};
603 
604  FlutterWindowMetricsEvent metrics = view->CreateWindowMetricsEvent();
605 
606  FlutterAddViewInfo info = {};
607  info.struct_size = sizeof(FlutterAddViewInfo);
608  info.view_id = view_id;
609  info.view_metrics = &metrics;
610  info.user_data = &captures;
611  info.add_view_callback = [](const FlutterAddViewResult* result) {
612  Captures* captures = reinterpret_cast<Captures*>(result->user_data);
613  captures->added = result->added;
614  captures->latch.Signal();
615  };
616 
617  FlutterEngineResult result = embedder_api_.AddView(engine_, &info);
618  if (result != kSuccess) {
619  FML_LOG(ERROR)
620  << "Starting the add view operation failed. FlutterEngineAddView "
621  "returned an unexpected result: "
622  << result << ". This indicates a bug in the Windows embedder.";
623  FML_DCHECK(false);
624  return nullptr;
625  }
626 
627  // Block the platform thread until the engine has added the view.
628  // TODO(loicsharma): This blocks the platform thread eagerly and can
629  // cause unnecessary delay in input processing. Instead, this should block
630  // lazily only when the app does an operation which needs the view.
631  // https://github.com/flutter/flutter/issues/146248
632  captures.latch.Wait();
633 
634  if (!captures.added) {
635  // Adding the view failed. Update the embedder's state to match the
636  // engine's state. This is unexpected and indicates a bug in the Windows
637  // embedder.
638  FML_LOG(ERROR) << "FlutterEngineAddView failed to add view";
639  std::unique_lock write_lock(views_mutex_);
640  views_.erase(view_id);
641  return nullptr;
642  }
643  }
644 
645  return std::move(view);
646 }
647 
648 void FlutterWindowsEngine::RemoveView(FlutterViewId view_id) {
649  FML_DCHECK(running());
650 
651  // Notify the engine to stop rendering to the view if it isn't the implicit
652  // view. The engine and framework assume the implicit view always exists and
653  // can continue presenting.
654  if (view_id != kImplicitViewId) {
655  struct Captures {
656  fml::AutoResetWaitableEvent latch;
657  bool removed;
658  };
659  Captures captures = {};
660 
661  FlutterRemoveViewInfo info = {};
662  info.struct_size = sizeof(FlutterRemoveViewInfo);
663  info.view_id = view_id;
664  info.user_data = &captures;
665  info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
666  // This is invoked on an engine thread. If
667  // |FlutterRemoveViewResult.removed| is `true`, the engine guarantees the
668  // view won't be presented.
669  Captures* captures = reinterpret_cast<Captures*>(result->user_data);
670  captures->removed = result->removed;
671  captures->latch.Signal();
672  };
673 
674  FlutterEngineResult result = embedder_api_.RemoveView(engine_, &info);
675  if (result != kSuccess) {
676  FML_LOG(ERROR) << "Starting the remove view operation failed. "
677  "FlutterEngineRemoveView "
678  "returned an unexpected result: "
679  << result
680  << ". This indicates a bug in the Windows embedder.";
681  FML_DCHECK(false);
682  return;
683  }
684 
685  // Block the platform thread until the engine has removed the view.
686  // TODO(loicsharma): This blocks the platform thread eagerly and can
687  // cause unnecessary delay in input processing. Instead, this should block
688  // lazily only when an operation needs the view.
689  // https://github.com/flutter/flutter/issues/146248
690  captures.latch.Wait();
691 
692  if (!captures.removed) {
693  // Removing the view failed. This is unexpected and indicates a bug in the
694  // Windows embedder.
695  FML_LOG(ERROR) << "FlutterEngineRemoveView failed to remove view";
696  return;
697  }
698  }
699 
700  {
701  // The engine no longer presents to the view. Remove the view from the
702  // embedder.
703  std::unique_lock write_lock(views_mutex_);
704 
705  FML_DCHECK(views_.find(view_id) != views_.end());
706 
707  // Reset text input state if the removed view is the active text input
708  // view, to prevent stale view references.
709  if (text_input_plugin_) {
710  text_input_plugin_->OnViewRemoved(view_id);
711  }
712 
713  views_.erase(view_id);
714  }
715 }
716 
717 void FlutterWindowsEngine::OnVsync(intptr_t baton) {
718  std::chrono::nanoseconds current_time =
719  std::chrono::nanoseconds(embedder_api_.GetCurrentTime());
720  std::chrono::nanoseconds frame_interval = FrameInterval();
721  auto next = SnapToNextTick(current_time, start_time_, frame_interval);
722  embedder_api_.OnVsync(engine_, baton, next.count(),
723  (next + frame_interval).count());
724 }
725 
726 std::chrono::nanoseconds FlutterWindowsEngine::FrameInterval() {
727  if (frame_interval_override_.has_value()) {
728  return frame_interval_override_.value();
729  }
730  uint64_t interval = 16600000;
731 
732  DWM_TIMING_INFO timing_info = {};
733  timing_info.cbSize = sizeof(timing_info);
734  HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
735  if (result == S_OK && timing_info.rateRefresh.uiDenominator > 0 &&
736  timing_info.rateRefresh.uiNumerator > 0) {
737  interval = static_cast<double>(timing_info.rateRefresh.uiDenominator *
738  1000000000.0) /
739  static_cast<double>(timing_info.rateRefresh.uiNumerator);
740  }
741 
742  return std::chrono::nanoseconds(interval);
743 }
744 
745 FlutterWindowsView* FlutterWindowsEngine::view(FlutterViewId view_id) const {
746  std::shared_lock read_lock(views_mutex_);
747 
748  auto iterator = views_.find(view_id);
749  if (iterator == views_.end()) {
750  return nullptr;
751  }
752 
753  return iterator->second;
754 }
755 
756 // Returns the currently configured Plugin Registrar.
757 FlutterDesktopPluginRegistrarRef FlutterWindowsEngine::GetRegistrar() {
758  return plugin_registrar_.get();
759 }
760 
761 void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
764  plugin_registrar_destruction_callbacks_[callback] = registrar;
765 }
766 
767 void FlutterWindowsEngine::UpdateDisplay(
768  const std::vector<FlutterEngineDisplay>& displays) {
769  if (engine_) {
770  embedder_api_.NotifyDisplayUpdate(engine_,
771  kFlutterEngineDisplaysUpdateTypeStartup,
772  displays.data(), displays.size());
773  }
774 }
775 
776 void FlutterWindowsEngine::SendWindowMetricsEvent(
777  const FlutterWindowMetricsEvent& event) {
778  if (engine_) {
779  embedder_api_.SendWindowMetricsEvent(engine_, &event);
780  }
781 }
782 
783 void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) {
784  if (engine_) {
785  embedder_api_.SendPointerEvent(engine_, &event, 1);
786  }
787 }
788 
789 void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event,
790  FlutterKeyEventCallback callback,
791  void* user_data) {
792  if (engine_) {
793  embedder_api_.SendKeyEvent(engine_, &event, callback, user_data);
794  }
795 }
796 
797 void FlutterWindowsEngine::SendViewFocusEvent(
798  const FlutterViewFocusEvent& event) {
799  if (engine_) {
800  embedder_api_.SendViewFocusEvent(engine_, &event);
801  }
802 }
803 
804 bool FlutterWindowsEngine::SendPlatformMessage(
805  const char* channel,
806  const uint8_t* message,
807  const size_t message_size,
808  const FlutterDesktopBinaryReply reply,
809  void* user_data) {
810  FlutterPlatformMessageResponseHandle* response_handle = nullptr;
811  if (reply != nullptr && user_data != nullptr) {
812  FlutterEngineResult result =
813  embedder_api_.PlatformMessageCreateResponseHandle(
814  engine_, reply, user_data, &response_handle);
815  if (result != kSuccess) {
816  FML_LOG(ERROR) << "Failed to create response handle";
817  return false;
818  }
819  }
820 
821  FlutterPlatformMessage platform_message = {
822  sizeof(FlutterPlatformMessage),
823  channel,
824  message,
825  message_size,
826  response_handle,
827  };
828 
829  FlutterEngineResult message_result =
830  embedder_api_.SendPlatformMessage(engine_, &platform_message);
831  if (response_handle != nullptr) {
832  embedder_api_.PlatformMessageReleaseResponseHandle(engine_,
833  response_handle);
834  }
835  return message_result == kSuccess;
836 }
837 
838 void FlutterWindowsEngine::SendPlatformMessageResponse(
840  const uint8_t* data,
841  size_t data_length) {
842  embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
843 }
844 
845 void FlutterWindowsEngine::HandlePlatformMessage(
846  const FlutterPlatformMessage* engine_message) {
847  if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
848  FML_LOG(ERROR) << "Invalid message size received. Expected: "
849  << sizeof(FlutterPlatformMessage) << " but received "
850  << engine_message->struct_size;
851  return;
852  }
853 
854  auto message = ConvertToDesktopMessage(*engine_message);
855 
856  message_dispatcher_->HandleMessage(message, [this] {}, [this] {});
857 }
858 
859 void FlutterWindowsEngine::ReloadSystemFonts() {
860  embedder_api_.ReloadSystemFonts(engine_);
861 }
862 
863 void FlutterWindowsEngine::ScheduleFrame() {
864  embedder_api_.ScheduleFrame(engine_);
865 }
866 
867 void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) {
868  next_frame_callback_ = std::move(callback);
869 
870  embedder_api_.SetNextFrameCallback(
871  engine_,
872  [](void* user_data) {
873  // Embedder callback runs on raster thread. Switch back to platform
874  // thread.
875  FlutterWindowsEngine* self =
876  static_cast<FlutterWindowsEngine*>(user_data);
877 
878  self->task_runner_->PostTask(std::move(self->next_frame_callback_));
879  },
880  this);
881 }
882 
883 HCURSOR FlutterWindowsEngine::GetCursorByName(
884  const std::string& cursor_name) const {
885  static auto* cursors = new std::map<std::string, const wchar_t*>{
886  {"allScroll", IDC_SIZEALL},
887  {"basic", IDC_ARROW},
888  {"click", IDC_HAND},
889  {"forbidden", IDC_NO},
890  {"help", IDC_HELP},
891  {"move", IDC_SIZEALL},
892  {"none", nullptr},
893  {"noDrop", IDC_NO},
894  {"precise", IDC_CROSS},
895  {"progress", IDC_APPSTARTING},
896  {"text", IDC_IBEAM},
897  {"resizeColumn", IDC_SIZEWE},
898  {"resizeDown", IDC_SIZENS},
899  {"resizeDownLeft", IDC_SIZENESW},
900  {"resizeDownRight", IDC_SIZENWSE},
901  {"resizeLeft", IDC_SIZEWE},
902  {"resizeLeftRight", IDC_SIZEWE},
903  {"resizeRight", IDC_SIZEWE},
904  {"resizeRow", IDC_SIZENS},
905  {"resizeUp", IDC_SIZENS},
906  {"resizeUpDown", IDC_SIZENS},
907  {"resizeUpLeft", IDC_SIZENWSE},
908  {"resizeUpRight", IDC_SIZENESW},
909  {"resizeUpLeftDownRight", IDC_SIZENWSE},
910  {"resizeUpRightDownLeft", IDC_SIZENESW},
911  {"wait", IDC_WAIT},
912  };
913  const wchar_t* idc_name = IDC_ARROW;
914  auto it = cursors->find(cursor_name);
915  if (it != cursors->end()) {
916  idc_name = it->second;
917  }
918  return windows_proc_table_->LoadCursor(nullptr, idc_name);
919 }
920 
921 FlutterWindowsView* FlutterWindowsEngine::GetViewFromTopLevelWindow(
922  HWND hwnd) const {
923  std::shared_lock read_lock(views_mutex_);
924  auto const iterator =
925  std::find_if(views_.begin(), views_.end(), [hwnd](auto const& pair) {
926  FlutterWindowsView* const view = pair.second;
927  return GetAncestor(view->GetWindowHandle(), GA_ROOT) == hwnd;
928  });
929  if (iterator != views_.end()) {
930  return iterator->second;
931  }
932  return nullptr;
933 }
934 
935 void FlutterWindowsEngine::SendSystemLocales() {
936  std::vector<LanguageInfo> languages =
937  GetPreferredLanguageInfo(*windows_proc_table_);
938  std::vector<FlutterLocale> flutter_locales;
939  flutter_locales.reserve(languages.size());
940  for (const auto& info : languages) {
941  flutter_locales.push_back(CovertToFlutterLocale(info));
942  }
943  // Convert the locale list to the locale pointer list that must be provided.
944  std::vector<const FlutterLocale*> flutter_locale_list;
945  flutter_locale_list.reserve(flutter_locales.size());
946  std::transform(flutter_locales.begin(), flutter_locales.end(),
947  std::back_inserter(flutter_locale_list),
948  [](const auto& arg) -> const auto* { return &arg; });
949  embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
950  flutter_locale_list.size());
951 }
952 
953 void FlutterWindowsEngine::InitializeKeyboard() {
954  auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
955  KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
956  KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan =
957  [](UINT virtual_key, bool extended) {
958  return MapVirtualKey(virtual_key,
959  extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
960  };
961  keyboard_key_handler_ = std::move(CreateKeyboardKeyHandler(
962  internal_plugin_messenger, get_key_state, map_vk_to_scan));
963  text_input_plugin_ =
964  std::move(CreateTextInputPlugin(internal_plugin_messenger));
965 }
966 
967 std::unique_ptr<KeyboardHandlerBase>
968 FlutterWindowsEngine::CreateKeyboardKeyHandler(
969  BinaryMessenger* messenger,
972  auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>(messenger);
973  keyboard_key_handler->AddDelegate(
974  std::make_unique<KeyboardKeyEmbedderHandler>(
975  [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback,
976  void* user_data) {
977  return SendKeyEvent(event, callback, user_data);
978  },
979  get_key_state, map_vk_to_scan));
980  keyboard_key_handler->AddDelegate(
981  std::make_unique<KeyboardKeyChannelHandler>(messenger));
982  keyboard_key_handler->InitKeyboardChannel();
983  return keyboard_key_handler;
984 }
985 
986 std::unique_ptr<TextInputPlugin> FlutterWindowsEngine::CreateTextInputPlugin(
987  BinaryMessenger* messenger) {
988  return std::make_unique<TextInputPlugin>(messenger, this);
989 }
990 
991 bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) {
992  return (embedder_api_.RegisterExternalTexture(engine_, texture_id) ==
993  kSuccess);
994 }
995 
996 bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) {
997  return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) ==
998  kSuccess);
999 }
1000 
1001 bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
1002  int64_t texture_id) {
1003  return (embedder_api_.MarkExternalTextureFrameAvailable(
1004  engine_, texture_id) == kSuccess);
1005 }
1006 
1007 bool FlutterWindowsEngine::PostRasterThreadTask(fml::closure callback) const {
1008  struct Captures {
1009  fml::closure callback;
1010  };
1011  auto captures = new Captures();
1012  captures->callback = std::move(callback);
1013  if (embedder_api_.PostRenderThreadTask(
1014  engine_,
1015  [](void* opaque) {
1016  auto captures = reinterpret_cast<Captures*>(opaque);
1017  captures->callback();
1018  delete captures;
1019  },
1020  captures) == kSuccess) {
1021  return true;
1022  }
1023  delete captures;
1024  return false;
1025 }
1026 
1027 bool FlutterWindowsEngine::DispatchSemanticsAction(
1028  FlutterViewId view_id,
1029  uint64_t target,
1030  FlutterSemanticsAction action,
1031  fml::MallocMapping data) {
1032  FlutterSendSemanticsActionInfo info{
1033  .struct_size = sizeof(FlutterSendSemanticsActionInfo),
1034  .view_id = view_id,
1035  .node_id = target,
1036  .action = action,
1037  .data = data.GetMapping(),
1038  .data_length = data.GetSize(),
1039  };
1040  return (embedder_api_.SendSemanticsAction(engine_, &info));
1041 }
1042 
1043 void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) {
1044  if (engine_ && semantics_enabled_ != enabled) {
1045  std::shared_lock read_lock(views_mutex_);
1046 
1047  semantics_enabled_ = enabled;
1048  embedder_api_.UpdateSemanticsEnabled(engine_, enabled);
1049  for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
1050  iterator->second->UpdateSemanticsEnabled(enabled);
1051  }
1052  }
1053 }
1054 
1055 void FlutterWindowsEngine::OnPreEngineRestart() {
1056  // Reset the keyboard's state on hot restart.
1057  InitializeKeyboard();
1058 }
1059 
1060 std::string FlutterWindowsEngine::GetExecutableName() const {
1061  std::pair<bool, std::string> result = fml::paths::GetExecutablePath();
1062  if (result.first) {
1063  const std::string& executable_path = result.second;
1064  size_t last_separator = executable_path.find_last_of("/\\");
1065  if (last_separator == std::string::npos ||
1066  last_separator == executable_path.size() - 1) {
1067  return executable_path;
1068  }
1069  return executable_path.substr(last_separator + 1);
1070  }
1071  return "Flutter";
1072 }
1073 
1074 void FlutterWindowsEngine::UpdateAccessibilityFeatures() {
1075  UpdateHighContrastMode();
1076 }
1077 
1078 void FlutterWindowsEngine::UpdateHighContrastMode() {
1079  high_contrast_enabled_ = windows_proc_table_->GetHighContrastEnabled();
1080 
1081  SendAccessibilityFeatures();
1082  settings_plugin_->UpdateHighContrastMode(high_contrast_enabled_);
1083 }
1084 
1085 void FlutterWindowsEngine::SendAccessibilityFeatures() {
1086  int flags = 0;
1087 
1088  if (high_contrast_enabled_) {
1089  flags |=
1090  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
1091  }
1092 
1093  embedder_api_.UpdateAccessibilityFeatures(
1094  engine_, static_cast<FlutterAccessibilityFeature>(flags));
1095 }
1096 
1097 void FlutterWindowsEngine::RequestApplicationQuit(HWND hwnd,
1098  WPARAM wparam,
1099  LPARAM lparam,
1100  AppExitType exit_type) {
1101  platform_handler_->RequestAppExit(hwnd, wparam, lparam, exit_type, 0);
1102 }
1103 
1104 void FlutterWindowsEngine::OnQuit(std::optional<HWND> hwnd,
1105  std::optional<WPARAM> wparam,
1106  std::optional<LPARAM> lparam,
1107  UINT exit_code) {
1108  lifecycle_manager_->Quit(hwnd, wparam, lparam, exit_code);
1109 }
1110 
1111 void FlutterWindowsEngine::OnDwmCompositionChanged() {
1112  if (display_manager_) {
1113  display_manager_->UpdateDisplays();
1114  }
1115 
1116  std::shared_lock read_lock(views_mutex_);
1117  for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
1118  iterator->second->OnDwmCompositionChanged();
1119  }
1120 }
1121 
1122 void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,
1123  WindowStateEvent event) {
1124  lifecycle_manager_->OnWindowStateEvent(hwnd, event);
1125 }
1126 
1127 std::optional<LRESULT> FlutterWindowsEngine::ProcessExternalWindowMessage(
1128  HWND hwnd,
1129  UINT message,
1130  WPARAM wparam,
1131  LPARAM lparam) {
1132  if (lifecycle_manager_) {
1133  return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam,
1134  lparam);
1135  }
1136  return std::nullopt;
1137 }
1138 
1139 void FlutterWindowsEngine::UpdateFlutterCursor(
1140  const std::string& cursor_name) const {
1141  SetFlutterCursor(GetCursorByName(cursor_name));
1142 }
1143 
1144 void FlutterWindowsEngine::SetFlutterCursor(HCURSOR cursor) const {
1145  windows_proc_table_->SetCursor(cursor);
1146 }
1147 
1148 void FlutterWindowsEngine::OnChannelUpdate(std::string name, bool listening) {
1149  if (name == "flutter/platform" && listening) {
1150  lifecycle_manager_->BeginProcessingExit();
1151  } else if (name == "flutter/lifecycle" && listening) {
1152  lifecycle_manager_->BeginProcessingLifecycle();
1153  }
1154 }
1155 
1156 void FlutterWindowsEngine::OnViewFocusChangeRequest(
1157  const FlutterViewFocusChangeRequest* request) {
1158  std::shared_lock read_lock(views_mutex_);
1159 
1160  auto iterator = views_.find(request->view_id);
1161  if (iterator == views_.end()) {
1162  return;
1163  }
1164 
1165  FlutterWindowsView* view = iterator->second;
1166  view->Focus();
1167 }
1168 
1169 bool FlutterWindowsEngine::Present(const FlutterPresentViewInfo* info) {
1170  // This runs on the raster thread. Lock the views map for the entirety of the
1171  // present operation to block the platform thread from destroying the
1172  // view during the present.
1173  std::shared_lock read_lock(views_mutex_);
1174 
1175  auto iterator = views_.find(info->view_id);
1176  if (iterator == views_.end()) {
1177  return false;
1178  }
1179 
1180  FlutterWindowsView* view = iterator->second;
1181 
1182  return compositor_->Present(view, info->layers, info->layers_count);
1183 }
1184 
1185 bool FlutterWindowsEngine::HandleDisplayMonitorMessage(HWND hwnd,
1186  UINT message,
1187  WPARAM wparam,
1188  LPARAM lparam,
1189  LRESULT* result) {
1190  if (!display_manager_) {
1191  return false;
1192  }
1193 
1194  return display_manager_->HandleWindowMessage(hwnd, message, wparam, lparam,
1195  result);
1196 }
1197 
1198 } // 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