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 <sstream>
11 
12 #include "flutter/fml/logging.h"
13 #include "flutter/fml/paths.h"
14 #include "flutter/fml/platform/win/wstring_conversion.h"
18 #include "flutter/shell/platform/embedder/embedder_struct_macros.h"
24 #include "flutter/third_party/accessibility/ax/ax_node.h"
25 
26 // winbase.h defines GetCurrentTime as a macro.
27 #undef GetCurrentTime
28 
29 static constexpr char kAccessibilityChannelName[] = "flutter/accessibility";
30 
31 namespace flutter {
32 
33 namespace {
34 
35 // Lifted from vsync_waiter_fallback.cc
36 static std::chrono::nanoseconds SnapToNextTick(
37  std::chrono::nanoseconds value,
38  std::chrono::nanoseconds tick_phase,
39  std::chrono::nanoseconds tick_interval) {
40  std::chrono::nanoseconds offset = (tick_phase - value) % tick_interval;
41  if (offset != std::chrono::nanoseconds::zero())
42  offset = offset + tick_interval;
43  return value + offset;
44 }
45 
46 // Creates and returns a FlutterRendererConfig that renders to the view (if any)
47 // of a FlutterWindowsEngine, using OpenGL (via ANGLE).
48 // The user_data received by the render callbacks refers to the
49 // FlutterWindowsEngine.
50 FlutterRendererConfig GetOpenGLRendererConfig() {
51  FlutterRendererConfig config = {};
52  config.type = kOpenGL;
53  config.open_gl.struct_size = sizeof(config.open_gl);
54  config.open_gl.make_current = [](void* user_data) -> bool {
55  auto host = static_cast<FlutterWindowsEngine*>(user_data);
56  if (!host->surface_manager()) {
57  return false;
58  }
59  return host->surface_manager()->MakeCurrent();
60  };
61  config.open_gl.clear_current = [](void* user_data) -> bool {
62  auto host = static_cast<FlutterWindowsEngine*>(user_data);
63  if (!host->surface_manager()) {
64  return false;
65  }
66  return host->surface_manager()->ClearContext();
67  };
68  config.open_gl.present = [](void* user_data) -> bool {
69  auto host = static_cast<FlutterWindowsEngine*>(user_data);
70  if (!host->view()) {
71  return false;
72  }
73  return host->view()->SwapBuffers();
74  };
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  auto host = static_cast<FlutterWindowsEngine*>(user_data);
79  if (host->view()) {
80  return host->view()->GetFrameBufferId(info->size.width,
81  info->size.height);
82  } else {
83  return kWindowFrameBufferID;
84  }
85  };
86  config.open_gl.gl_proc_resolver = [](void* user_data,
87  const char* what) -> void* {
88  return reinterpret_cast<void*>(eglGetProcAddress(what));
89  };
90  config.open_gl.make_resource_current = [](void* user_data) -> bool {
91  auto host = static_cast<FlutterWindowsEngine*>(user_data);
92  if (!host->surface_manager()) {
93  return false;
94  }
95  return host->surface_manager()->MakeResourceCurrent();
96  };
97  config.open_gl.gl_external_texture_frame_callback =
98  [](void* user_data, int64_t texture_id, size_t width, size_t height,
99  FlutterOpenGLTexture* texture) -> bool {
100  auto host = static_cast<FlutterWindowsEngine*>(user_data);
101  if (!host->texture_registrar()) {
102  return false;
103  }
104  return host->texture_registrar()->PopulateTexture(texture_id, width, height,
105  texture);
106  };
107  return config;
108 }
109 
110 // Creates and returns a FlutterRendererConfig that renders to the view (if any)
111 // of a FlutterWindowsEngine, using software rasterization.
112 // The user_data received by the render callbacks refers to the
113 // FlutterWindowsEngine.
114 FlutterRendererConfig GetSoftwareRendererConfig() {
115  FlutterRendererConfig config = {};
116  config.type = kSoftware;
117  config.software.struct_size = sizeof(config.software);
118  config.software.surface_present_callback = [](void* user_data,
119  const void* allocation,
120  size_t row_bytes,
121  size_t height) {
122  auto host = static_cast<FlutterWindowsEngine*>(user_data);
123  if (!host->view()) {
124  return false;
125  }
126  return host->view()->PresentSoftwareBitmap(allocation, row_bytes, height);
127  };
128  return config;
129 }
130 
131 // Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage.
132 static FlutterDesktopMessage ConvertToDesktopMessage(
133  const FlutterPlatformMessage& engine_message) {
135  message.struct_size = sizeof(message);
136  message.channel = engine_message.channel;
137  message.message = engine_message.message;
138  message.message_size = engine_message.message_size;
139  message.response_handle = engine_message.response_handle;
140  return message;
141 }
142 
143 // Converts a LanguageInfo struct to a FlutterLocale struct. |info| must outlive
144 // the returned value, since the returned FlutterLocale has pointers into it.
145 FlutterLocale CovertToFlutterLocale(const LanguageInfo& info) {
146  FlutterLocale locale = {};
147  locale.struct_size = sizeof(FlutterLocale);
148  locale.language_code = info.language.c_str();
149  if (!info.region.empty()) {
150  locale.country_code = info.region.c_str();
151  }
152  if (!info.script.empty()) {
153  locale.script_code = info.script.c_str();
154  }
155  return locale;
156 }
157 
158 } // namespace
159 
161  const FlutterProjectBundle& project,
162  std::shared_ptr<WindowsProcTable> windows_proc_table)
163  : project_(std::make_unique<FlutterProjectBundle>(project)),
164  windows_proc_table_(std::move(windows_proc_table)),
165  aot_data_(nullptr, nullptr),
166  lifecycle_manager_(std::make_unique<WindowsLifecycleManager>(this)) {
167  if (windows_proc_table_ == nullptr) {
168  windows_proc_table_ = std::make_shared<WindowsProcTable>();
169  }
170 
171  gl_ = GlProcTable::Create();
172 
173  embedder_api_.struct_size = sizeof(FlutterEngineProcTable);
174  FlutterEngineGetProcAddresses(&embedder_api_);
175 
176  task_runner_ =
177  std::make_unique<TaskRunner>(
178  embedder_api_.GetCurrentTime, [this](const auto* task) {
179  if (!engine_) {
180  FML_LOG(ERROR)
181  << "Cannot post an engine task when engine is not running.";
182  return;
183  }
184  if (embedder_api_.RunTask(engine_, task) != kSuccess) {
185  FML_LOG(ERROR) << "Failed to post an engine task.";
186  }
187  });
188 
189  // Set up the legacy structs backing the API handles.
190  messenger_ =
191  fml::RefPtr<FlutterDesktopMessenger>(new FlutterDesktopMessenger());
192  messenger_->SetEngine(this);
193  plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
194  plugin_registrar_->engine = this;
195 
196  messenger_wrapper_ =
197  std::make_unique<BinaryMessengerImpl>(messenger_->ToRef());
198  message_dispatcher_ =
199  std::make_unique<IncomingMessageDispatcher>(messenger_->ToRef());
200  message_dispatcher_->SetMessageCallback(
202  [](FlutterDesktopMessengerRef messenger,
203  const FlutterDesktopMessage* message, void* data) {
204  FlutterWindowsEngine* engine = static_cast<FlutterWindowsEngine*>(data);
205  engine->HandleAccessibilityMessage(messenger, message);
206  },
207  static_cast<void*>(this));
208 
209  texture_registrar_ =
210  std::make_unique<FlutterWindowsTextureRegistrar>(this, gl_);
211 
212  // Check for impeller support.
213  auto& switches = project_->GetSwitches();
214  enable_impeller_ = std::find(switches.begin(), switches.end(),
215  "--enable-impeller=true") != switches.end();
216 
217  surface_manager_ = AngleSurfaceManager::Create(enable_impeller_);
218  window_proc_delegate_manager_ = std::make_unique<WindowProcDelegateManager>();
219  window_proc_delegate_manager_->RegisterTopLevelWindowProcDelegate(
220  [](HWND hwnd, UINT msg, WPARAM wpar, LPARAM lpar, void* user_data,
221  LRESULT* result) {
222  BASE_DCHECK(user_data);
223  FlutterWindowsEngine* that =
224  static_cast<FlutterWindowsEngine*>(user_data);
225  BASE_DCHECK(that->lifecycle_manager_);
226  return that->lifecycle_manager_->WindowProc(hwnd, msg, wpar, lpar,
227  result);
228  },
229  static_cast<void*>(this));
230 
231  // Set up internal channels.
232  // TODO: Replace this with an embedder.h API. See
233  // https://github.com/flutter/flutter/issues/71099
234  internal_plugin_registrar_ =
235  std::make_unique<PluginRegistrar>(plugin_registrar_.get());
236  cursor_handler_ =
237  std::make_unique<CursorHandler>(messenger_wrapper_.get(), this);
238  platform_handler_ =
239  std::make_unique<PlatformHandler>(messenger_wrapper_.get(), this);
240  settings_plugin_ = std::make_unique<SettingsPlugin>(messenger_wrapper_.get(),
241  task_runner_.get());
242 }
243 
244 FlutterWindowsEngine::~FlutterWindowsEngine() {
245  messenger_->SetEngine(nullptr);
246  Stop();
247 }
248 
249 void FlutterWindowsEngine::SetSwitches(
250  const std::vector<std::string>& switches) {
251  project_->SetSwitches(switches);
252 }
253 
254 bool FlutterWindowsEngine::Run() {
255  return Run("");
256 }
257 
258 bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
259  if (!project_->HasValidPaths()) {
260  FML_LOG(ERROR) << "Missing or unresolvable paths to assets.";
261  return false;
262  }
263  std::string assets_path_string = project_->assets_path().u8string();
264  std::string icu_path_string = project_->icu_path().u8string();
265  if (embedder_api_.RunsAOTCompiledDartCode()) {
266  aot_data_ = project_->LoadAotData(embedder_api_);
267  if (!aot_data_) {
268  FML_LOG(ERROR) << "Unable to start engine without AOT data.";
269  return false;
270  }
271  }
272 
273  // FlutterProjectArgs is expecting a full argv, so when processing it for
274  // flags the first item is treated as the executable and ignored. Add a dummy
275  // value so that all provided arguments are used.
276  std::string executable_name = GetExecutableName();
277  std::vector<const char*> argv = {executable_name.c_str()};
278  std::vector<std::string> switches = project_->GetSwitches();
279  std::transform(
280  switches.begin(), switches.end(), std::back_inserter(argv),
281  [](const std::string& arg) -> const char* { return arg.c_str(); });
282 
283  const std::vector<std::string>& entrypoint_args =
284  project_->dart_entrypoint_arguments();
285  std::vector<const char*> entrypoint_argv;
286  std::transform(
287  entrypoint_args.begin(), entrypoint_args.end(),
288  std::back_inserter(entrypoint_argv),
289  [](const std::string& arg) -> const char* { return arg.c_str(); });
290 
291  // Configure task runners.
292  FlutterTaskRunnerDescription platform_task_runner = {};
293  platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
294  platform_task_runner.user_data = task_runner_.get();
295  platform_task_runner.runs_task_on_current_thread_callback =
296  [](void* user_data) -> bool {
297  return static_cast<TaskRunner*>(user_data)->RunsTasksOnCurrentThread();
298  };
299  platform_task_runner.post_task_callback = [](FlutterTask task,
300  uint64_t target_time_nanos,
301  void* user_data) -> void {
302  static_cast<TaskRunner*>(user_data)->PostFlutterTask(task,
303  target_time_nanos);
304  };
305  FlutterCustomTaskRunners custom_task_runners = {};
306  custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
307  custom_task_runners.platform_task_runner = &platform_task_runner;
308  custom_task_runners.thread_priority_setter =
310 
311  FlutterProjectArgs args = {};
312  args.struct_size = sizeof(FlutterProjectArgs);
313  args.shutdown_dart_vm_when_done = true;
314  args.assets_path = assets_path_string.c_str();
315  args.icu_data_path = icu_path_string.c_str();
316  args.command_line_argc = static_cast<int>(argv.size());
317  args.command_line_argv = argv.empty() ? nullptr : argv.data();
318 
319  // Fail if conflicting non-default entrypoints are specified in the method
320  // argument and the project.
321  //
322  // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
323  // The entrypoint method parameter should eventually be removed from this
324  // method and only the entrypoint specified in project_ should be used.
325  if (!project_->dart_entrypoint().empty() && !entrypoint.empty() &&
326  project_->dart_entrypoint() != entrypoint) {
327  FML_LOG(ERROR) << "Conflicting entrypoints were specified in "
328  "FlutterDesktopEngineProperties.dart_entrypoint and "
329  "FlutterDesktopEngineRun(engine, entry_point). ";
330  return false;
331  }
332  if (!entrypoint.empty()) {
333  args.custom_dart_entrypoint = entrypoint.data();
334  } else if (!project_->dart_entrypoint().empty()) {
335  args.custom_dart_entrypoint = project_->dart_entrypoint().c_str();
336  }
337  args.dart_entrypoint_argc = static_cast<int>(entrypoint_argv.size());
338  args.dart_entrypoint_argv =
339  entrypoint_argv.empty() ? nullptr : entrypoint_argv.data();
340  args.platform_message_callback =
341  [](const FlutterPlatformMessage* engine_message,
342  void* user_data) -> void {
343  auto host = static_cast<FlutterWindowsEngine*>(user_data);
344  return host->HandlePlatformMessage(engine_message);
345  };
346  args.vsync_callback = [](void* user_data, intptr_t baton) -> void {
347  auto host = static_cast<FlutterWindowsEngine*>(user_data);
348  host->OnVsync(baton);
349  };
350  args.on_pre_engine_restart_callback = [](void* user_data) {
351  auto host = static_cast<FlutterWindowsEngine*>(user_data);
352  host->OnPreEngineRestart();
353  };
354  args.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
355  void* user_data) {
356  auto host = static_cast<FlutterWindowsEngine*>(user_data);
357  auto view = host->view();
358  if (!view) {
359  return;
360  }
361 
362  auto accessibility_bridge = view->accessibility_bridge().lock();
363  if (!accessibility_bridge) {
364  return;
365  }
366 
367  for (size_t i = 0; i < update->node_count; i++) {
368  const FlutterSemanticsNode2* node = update->nodes[i];
369  accessibility_bridge->AddFlutterSemanticsNodeUpdate(*node);
370  }
371 
372  for (size_t i = 0; i < update->custom_action_count; i++) {
373  const FlutterSemanticsCustomAction2* action = update->custom_actions[i];
374  accessibility_bridge->AddFlutterSemanticsCustomActionUpdate(*action);
375  }
376 
377  accessibility_bridge->CommitUpdates();
378  };
379  args.root_isolate_create_callback = [](void* user_data) {
380  auto host = static_cast<FlutterWindowsEngine*>(user_data);
381  if (host->root_isolate_create_callback_) {
382  host->root_isolate_create_callback_();
383  }
384  };
385  args.channel_update_callback = [](const FlutterChannelUpdate* update,
386  void* user_data) {
387  auto host = static_cast<FlutterWindowsEngine*>(user_data);
388  if (SAFE_ACCESS(update, channel, nullptr) != nullptr) {
389  std::string channel_name(update->channel);
390  host->OnChannelUpdate(std::move(channel_name),
391  SAFE_ACCESS(update, listening, false));
392  }
393  };
394 
395  args.custom_task_runners = &custom_task_runners;
396 
397  if (aot_data_) {
398  args.aot_data = aot_data_.get();
399  }
400 
401  // The platform thread creates OpenGL contexts. These
402  // must be released to be used by the engine's threads.
403  FML_DCHECK(!surface_manager_ || !surface_manager_->HasContextCurrent());
404 
405  FlutterRendererConfig renderer_config;
406 
407  if (enable_impeller_) {
408  // Impeller does not support a Software backend. Avoid falling back and
409  // confusing the engine on which renderer is selected.
410  if (!surface_manager_) {
411  FML_LOG(ERROR) << "Could not create surface manager. Impeller backend "
412  "does not support software rendering.";
413  return false;
414  }
415  renderer_config = GetOpenGLRendererConfig();
416  } else {
417  renderer_config = surface_manager_ ? GetOpenGLRendererConfig()
418  : GetSoftwareRendererConfig();
419  }
420 
421  auto result = embedder_api_.Run(FLUTTER_ENGINE_VERSION, &renderer_config,
422  &args, this, &engine_);
423  if (result != kSuccess || engine_ == nullptr) {
424  FML_LOG(ERROR) << "Failed to start Flutter engine: error " << result;
425  return false;
426  }
427 
428  // Configure device frame rate displayed via devtools.
429  FlutterEngineDisplay display = {};
430  display.struct_size = sizeof(FlutterEngineDisplay);
431  display.display_id = 0;
432  display.single_display = true;
433  display.refresh_rate =
434  1.0 / (static_cast<double>(FrameInterval().count()) / 1000000000.0);
435 
436  std::vector<FlutterEngineDisplay> displays = {display};
437  embedder_api_.NotifyDisplayUpdate(engine_,
438  kFlutterEngineDisplaysUpdateTypeStartup,
439  displays.data(), displays.size());
440 
441  SendSystemLocales();
442  SetLifecycleState(flutter::AppLifecycleState::kResumed);
443 
444  settings_plugin_->StartWatching();
445  settings_plugin_->SendSettings();
446 
447  return true;
448 }
449 
450 bool FlutterWindowsEngine::Stop() {
451  if (engine_) {
452  for (const auto& [callback, registrar] :
453  plugin_registrar_destruction_callbacks_) {
454  callback(registrar);
455  }
456  FlutterEngineResult result = embedder_api_.Shutdown(engine_);
457  engine_ = nullptr;
458  return (result == kSuccess);
459  }
460  return false;
461 }
462 
463 void FlutterWindowsEngine::SetView(FlutterWindowsView* view) {
464  view_ = view;
465  InitializeKeyboard();
466 }
467 
468 void FlutterWindowsEngine::OnVsync(intptr_t baton) {
469  std::chrono::nanoseconds current_time =
470  std::chrono::nanoseconds(embedder_api_.GetCurrentTime());
471  std::chrono::nanoseconds frame_interval = FrameInterval();
472  auto next = SnapToNextTick(current_time, start_time_, frame_interval);
473  embedder_api_.OnVsync(engine_, baton, next.count(),
474  (next + frame_interval).count());
475 }
476 
477 std::chrono::nanoseconds FlutterWindowsEngine::FrameInterval() {
478  if (frame_interval_override_.has_value()) {
479  return frame_interval_override_.value();
480  }
481  uint64_t interval = 16600000;
482 
483  DWM_TIMING_INFO timing_info = {};
484  timing_info.cbSize = sizeof(timing_info);
485  HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
486  if (result == S_OK && timing_info.rateRefresh.uiDenominator > 0 &&
487  timing_info.rateRefresh.uiNumerator > 0) {
488  interval = static_cast<double>(timing_info.rateRefresh.uiDenominator *
489  1000000000.0) /
490  static_cast<double>(timing_info.rateRefresh.uiNumerator);
491  }
492 
493  return std::chrono::nanoseconds(interval);
494 }
495 
496 // Returns the currently configured Plugin Registrar.
497 FlutterDesktopPluginRegistrarRef FlutterWindowsEngine::GetRegistrar() {
498  return plugin_registrar_.get();
499 }
500 
501 void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
504  plugin_registrar_destruction_callbacks_[callback] = registrar;
505 }
506 
507 void FlutterWindowsEngine::SendWindowMetricsEvent(
508  const FlutterWindowMetricsEvent& event) {
509  if (engine_) {
510  embedder_api_.SendWindowMetricsEvent(engine_, &event);
511  }
512 }
513 
514 void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) {
515  if (engine_) {
516  embedder_api_.SendPointerEvent(engine_, &event, 1);
517  }
518 }
519 
520 void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event,
521  FlutterKeyEventCallback callback,
522  void* user_data) {
523  if (engine_) {
524  embedder_api_.SendKeyEvent(engine_, &event, callback, user_data);
525  }
526 }
527 
528 bool FlutterWindowsEngine::SendPlatformMessage(
529  const char* channel,
530  const uint8_t* message,
531  const size_t message_size,
532  const FlutterDesktopBinaryReply reply,
533  void* user_data) {
534  FlutterPlatformMessageResponseHandle* response_handle = nullptr;
535  if (reply != nullptr && user_data != nullptr) {
536  FlutterEngineResult result =
537  embedder_api_.PlatformMessageCreateResponseHandle(
538  engine_, reply, user_data, &response_handle);
539  if (result != kSuccess) {
540  FML_LOG(ERROR) << "Failed to create response handle";
541  return false;
542  }
543  }
544 
545  FlutterPlatformMessage platform_message = {
546  sizeof(FlutterPlatformMessage),
547  channel,
548  message,
549  message_size,
550  response_handle,
551  };
552 
553  FlutterEngineResult message_result =
554  embedder_api_.SendPlatformMessage(engine_, &platform_message);
555  if (response_handle != nullptr) {
556  embedder_api_.PlatformMessageReleaseResponseHandle(engine_,
557  response_handle);
558  }
559  return message_result == kSuccess;
560 }
561 
562 void FlutterWindowsEngine::SendPlatformMessageResponse(
564  const uint8_t* data,
565  size_t data_length) {
566  embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
567 }
568 
569 void FlutterWindowsEngine::HandlePlatformMessage(
570  const FlutterPlatformMessage* engine_message) {
571  if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
572  FML_LOG(ERROR) << "Invalid message size received. Expected: "
573  << sizeof(FlutterPlatformMessage) << " but received "
574  << engine_message->struct_size;
575  return;
576  }
577 
578  auto message = ConvertToDesktopMessage(*engine_message);
579 
580  message_dispatcher_->HandleMessage(message, [this] {}, [this] {});
581 }
582 
583 void FlutterWindowsEngine::ReloadSystemFonts() {
584  embedder_api_.ReloadSystemFonts(engine_);
585 }
586 
587 void FlutterWindowsEngine::ScheduleFrame() {
588  embedder_api_.ScheduleFrame(engine_);
589 }
590 
591 void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) {
592  next_frame_callback_ = std::move(callback);
593 
594  embedder_api_.SetNextFrameCallback(
595  engine_,
596  [](void* user_data) {
597  // Embedder callback runs on raster thread. Switch back to platform
598  // thread.
599  FlutterWindowsEngine* self =
600  static_cast<FlutterWindowsEngine*>(user_data);
601 
602  self->task_runner_->PostTask(std::move(self->next_frame_callback_));
603  },
604  this);
605 }
606 
607 void FlutterWindowsEngine::SetLifecycleState(flutter::AppLifecycleState state) {
608  if (lifecycle_manager_) {
609  lifecycle_manager_->SetLifecycleState(state);
610  }
611 }
612 
613 void FlutterWindowsEngine::SendSystemLocales() {
614  std::vector<LanguageInfo> languages =
615  GetPreferredLanguageInfo(*windows_proc_table_);
616  std::vector<FlutterLocale> flutter_locales;
617  flutter_locales.reserve(languages.size());
618  for (const auto& info : languages) {
619  flutter_locales.push_back(CovertToFlutterLocale(info));
620  }
621  // Convert the locale list to the locale pointer list that must be provided.
622  std::vector<const FlutterLocale*> flutter_locale_list;
623  flutter_locale_list.reserve(flutter_locales.size());
624  std::transform(flutter_locales.begin(), flutter_locales.end(),
625  std::back_inserter(flutter_locale_list),
626  [](const auto& arg) -> const auto* { return &arg; });
627  embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
628  flutter_locale_list.size());
629 }
630 
631 void FlutterWindowsEngine::InitializeKeyboard() {
632  if (view_ == nullptr) {
633  FML_LOG(ERROR) << "Cannot initialize keyboard on Windows headless mode.";
634  }
635 
636  auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
637  KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
638  KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan =
639  [](UINT virtual_key, bool extended) {
640  return MapVirtualKey(virtual_key,
641  extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
642  };
643  keyboard_key_handler_ = std::move(CreateKeyboardKeyHandler(
644  internal_plugin_messenger, get_key_state, map_vk_to_scan));
645  text_input_plugin_ =
646  std::move(CreateTextInputPlugin(internal_plugin_messenger));
647 }
648 
649 std::unique_ptr<KeyboardHandlerBase>
650 FlutterWindowsEngine::CreateKeyboardKeyHandler(
651  BinaryMessenger* messenger,
654  auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>(messenger);
655  keyboard_key_handler->AddDelegate(
656  std::make_unique<KeyboardKeyEmbedderHandler>(
657  [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback,
658  void* user_data) {
659  return SendKeyEvent(event, callback, user_data);
660  },
661  get_key_state, map_vk_to_scan));
662  keyboard_key_handler->AddDelegate(
663  std::make_unique<KeyboardKeyChannelHandler>(messenger));
664  keyboard_key_handler->InitKeyboardChannel();
665  return keyboard_key_handler;
666 }
667 
668 std::unique_ptr<TextInputPlugin> FlutterWindowsEngine::CreateTextInputPlugin(
669  BinaryMessenger* messenger) {
670  return std::make_unique<TextInputPlugin>(messenger, this);
671 }
672 
673 bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) {
674  return (embedder_api_.RegisterExternalTexture(engine_, texture_id) ==
675  kSuccess);
676 }
677 
678 bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) {
679  return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) ==
680  kSuccess);
681 }
682 
683 bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
684  int64_t texture_id) {
685  return (embedder_api_.MarkExternalTextureFrameAvailable(
686  engine_, texture_id) == kSuccess);
687 }
688 
689 bool FlutterWindowsEngine::PostRasterThreadTask(fml::closure callback) const {
690  struct Captures {
691  fml::closure callback;
692  };
693  auto captures = new Captures();
694  captures->callback = std::move(callback);
695  if (embedder_api_.PostRenderThreadTask(
696  engine_,
697  [](void* opaque) {
698  auto captures = reinterpret_cast<Captures*>(opaque);
699  captures->callback();
700  delete captures;
701  },
702  captures) == kSuccess) {
703  return true;
704  }
705  delete captures;
706  return false;
707 }
708 
709 bool FlutterWindowsEngine::DispatchSemanticsAction(
710  uint64_t target,
711  FlutterSemanticsAction action,
712  fml::MallocMapping data) {
713  return (embedder_api_.DispatchSemanticsAction(engine_, target, action,
714  data.GetMapping(),
715  data.GetSize()) == kSuccess);
716 }
717 
718 void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) {
719  if (engine_ && semantics_enabled_ != enabled) {
720  semantics_enabled_ = enabled;
721  embedder_api_.UpdateSemanticsEnabled(engine_, enabled);
722  view_->UpdateSemanticsEnabled(enabled);
723  }
724 }
725 
726 void FlutterWindowsEngine::OnPreEngineRestart() {
727  // Reset the keyboard's state on hot restart.
728  if (view_) {
729  InitializeKeyboard();
730  }
731 }
732 
733 std::string FlutterWindowsEngine::GetExecutableName() const {
734  std::pair<bool, std::string> result = fml::paths::GetExecutablePath();
735  if (result.first) {
736  const std::string& executable_path = result.second;
737  size_t last_separator = executable_path.find_last_of("/\\");
738  if (last_separator == std::string::npos ||
739  last_separator == executable_path.size() - 1) {
740  return executable_path;
741  }
742  return executable_path.substr(last_separator + 1);
743  }
744  return "Flutter";
745 }
746 
747 void FlutterWindowsEngine::UpdateAccessibilityFeatures() {
748  UpdateHighContrastMode();
749 }
750 
751 void FlutterWindowsEngine::UpdateHighContrastMode() {
752  high_contrast_enabled_ = windows_proc_table_->GetHighContrastEnabled();
753 
754  SendAccessibilityFeatures();
755  settings_plugin_->UpdateHighContrastMode(high_contrast_enabled_);
756 }
757 
758 void FlutterWindowsEngine::SendAccessibilityFeatures() {
759  int flags = 0;
760 
761  if (high_contrast_enabled_) {
762  flags |=
763  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
764  }
765 
766  embedder_api_.UpdateAccessibilityFeatures(
767  engine_, static_cast<FlutterAccessibilityFeature>(flags));
768 }
769 
770 void FlutterWindowsEngine::HandleAccessibilityMessage(
771  FlutterDesktopMessengerRef messenger,
773  const auto& codec = StandardMessageCodec::GetInstance();
774  auto data = codec.DecodeMessage(message->message, message->message_size);
775  EncodableMap map = std::get<EncodableMap>(*data);
776  std::string type = std::get<std::string>(map.at(EncodableValue("type")));
777  if (type.compare("announce") == 0) {
778  if (semantics_enabled_) {
779  EncodableMap data_map =
780  std::get<EncodableMap>(map.at(EncodableValue("data")));
781  std::string text =
782  std::get<std::string>(data_map.at(EncodableValue("message")));
783  std::wstring wide_text = fml::Utf8ToWideString(text);
784  view_->AnnounceAlert(wide_text);
785  }
786  }
787  SendPlatformMessageResponse(message->response_handle,
788  reinterpret_cast<const uint8_t*>(""), 0);
789 }
790 
791 void FlutterWindowsEngine::RequestApplicationQuit(HWND hwnd,
792  WPARAM wparam,
793  LPARAM lparam,
794  AppExitType exit_type) {
795  platform_handler_->RequestAppExit(hwnd, wparam, lparam, exit_type, 0);
796 }
797 
798 void FlutterWindowsEngine::OnQuit(std::optional<HWND> hwnd,
799  std::optional<WPARAM> wparam,
800  std::optional<LPARAM> lparam,
801  UINT exit_code) {
802  lifecycle_manager_->Quit(hwnd, wparam, lparam, exit_code);
803 }
804 
805 void FlutterWindowsEngine::OnDwmCompositionChanged() {
806  view_->OnDwmCompositionChanged();
807 }
808 
809 void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,
810  WindowStateEvent event) {
811  lifecycle_manager_->OnWindowStateEvent(hwnd, event);
812 }
813 
814 std::optional<LRESULT> FlutterWindowsEngine::ProcessExternalWindowMessage(
815  HWND hwnd,
816  UINT message,
817  WPARAM wparam,
818  LPARAM lparam) {
819  if (lifecycle_manager_) {
820  return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam,
821  lparam);
822  }
823  return std::nullopt;
824 }
825 
826 void FlutterWindowsEngine::OnChannelUpdate(std::string name, bool listening) {
827  if (name == "flutter/platform" && listening) {
828  lifecycle_manager_->BeginProcessingExit();
829  } else if (name == "flutter/lifecycle" && listening) {
830  lifecycle_manager_->BeginProcessingLifecycle();
831  }
832 }
833 
834 } // namespace flutter
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::FlutterWindowsView
Definition: flutter_windows_view.h:35
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:468
user_data
void * user_data
Definition: flutter_windows_view_unittests.cc:49
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:78
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:207
flutter::FlutterWindowsEngine::FlutterWindowsEngine
FlutterWindowsEngine(const FlutterProjectBundle &project, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
Definition: flutter_windows_engine.cc:160
FlutterDesktopMessageResponseHandle
struct _FlutterPlatformMessageResponseHandle FlutterDesktopMessageResponseHandle
Definition: flutter_messenger.h:22
kAccessibilityChannelName
static constexpr char kAccessibilityChannelName[]
Definition: flutter_windows_engine.cc:29
system_utils.h
flutter::kWindowFrameBufferID
constexpr uint32_t kWindowFrameBufferID
Definition: flutter_windows_view.h:31
flutter::FlutterWindowsEngine::OnPreEngineRestart
void OnPreEngineRestart()
Definition: flutter_windows_engine.cc:726
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
text
std::u16string text
Definition: keyboard_unittests.cc:332
standard_message_codec.h
accessibility_bridge_windows.h
type
enum flutter::testing::@69::KeyboardChange::Type type
binary_messenger_impl.h
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::TaskRunner
Definition: task_runner.h:26
flutter::GlProcTable::Create
static std::shared_ptr< GlProcTable > Create()
Definition: gl_proc_table.cc:11
FlutterDesktopOnPluginRegistrarDestroyed
void(* FlutterDesktopOnPluginRegistrarDestroyed)(FlutterDesktopPluginRegistrarRef)
Definition: flutter_plugin_registrar.h:23
flutter_windows_engine.h
FlutterDesktopMessengerRef
struct FlutterDesktopMessenger * FlutterDesktopMessengerRef
Definition: flutter_messenger.h:19
FlutterDesktopMessage
Definition: flutter_messenger.h:31
flutter::FlutterWindowsEngine::view
FlutterWindowsView * view()
Definition: flutter_windows_engine.h:117
flutter::AppLifecycleState::kResumed
@ kResumed
message
Win32Message message
Definition: keyboard_unittests.cc:137
action
int action
Definition: keyboard_key_handler_unittests.cc:116
flutter::AngleSurfaceManager::Create
static std::unique_ptr< AngleSurfaceManager > Create(bool enable_impeller)
Definition: angle_surface_manager.cc:23
flutter::AppLifecycleState
AppLifecycleState
Definition: app_lifecycle_state.h:32
task_runner.h
flutter::WindowsLifecycleManager
Definition: windows_lifecycle_manager.h:37
flutter::EncodableMap
std::map< EncodableValue, EncodableValue > EncodableMap
Definition: encodable_value.h:95
texture_id
int64_t texture_id
Definition: texture_registrar_unittests.cc:24
FlutterDesktopPluginRegistrar
Definition: window_state.h:23
keyboard_key_channel_handler.h
flutter::FlutterWindowsEngine::HandlePlatformMessage
void HandlePlatformMessage(const FlutterPlatformMessage *)
Definition: flutter_windows_engine.cc:569
flutter::WindowsPlatformThreadPrioritySetter
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
Definition: flutter_windows_engine.h:48
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:48