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