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