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