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->view()) {
57  return false;
58  }
59  return host->view()->MakeCurrent();
60  };
61  config.open_gl.clear_current = [](void* user_data) -> bool {
62  auto host = static_cast<FlutterWindowsEngine*>(user_data);
63  if (!host->view()) {
64  return false;
65  }
66  return host->view()->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->view()) {
93  return false;
94  }
95  return host->view()->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  : project_(std::make_unique<FlutterProjectBundle>(project)),
162  aot_data_(nullptr, nullptr),
163  lifecycle_manager_(std::make_unique<WindowsLifecycleManager>(this)) {
164  embedder_api_.struct_size = sizeof(FlutterEngineProcTable);
165  FlutterEngineGetProcAddresses(&embedder_api_);
166 
167  task_runner_ =
168  std::make_unique<TaskRunner>(
169  embedder_api_.GetCurrentTime, [this](const auto* task) {
170  if (!engine_) {
171  FML_LOG(ERROR)
172  << "Cannot post an engine task when engine is not running.";
173  return;
174  }
175  if (embedder_api_.RunTask(engine_, task) != kSuccess) {
176  FML_LOG(ERROR) << "Failed to post an engine task.";
177  }
178  });
179 
180  // Set up the legacy structs backing the API handles.
181  messenger_ =
182  fml::RefPtr<FlutterDesktopMessenger>(new FlutterDesktopMessenger());
183  messenger_->SetEngine(this);
184  plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
185  plugin_registrar_->engine = this;
186 
187  messenger_wrapper_ =
188  std::make_unique<BinaryMessengerImpl>(messenger_->ToRef());
189  message_dispatcher_ =
190  std::make_unique<IncomingMessageDispatcher>(messenger_->ToRef());
191  message_dispatcher_->SetMessageCallback(
193  [](FlutterDesktopMessengerRef messenger,
194  const FlutterDesktopMessage* message, void* data) {
195  FlutterWindowsEngine* engine = static_cast<FlutterWindowsEngine*>(data);
196  engine->HandleAccessibilityMessage(messenger, message);
197  },
198  static_cast<void*>(this));
199 
201  texture_registrar_ =
202  std::make_unique<FlutterWindowsTextureRegistrar>(this, gl_procs_);
203 
204  // Check for impeller support.
205  auto& switches = project_->GetSwitches();
206  enable_impeller_ = std::find(switches.begin(), switches.end(),
207  "--enable-impeller=true") != switches.end();
208 
209  surface_manager_ = AngleSurfaceManager::Create(enable_impeller_);
210  window_proc_delegate_manager_ = std::make_unique<WindowProcDelegateManager>();
211  window_proc_delegate_manager_->RegisterTopLevelWindowProcDelegate(
212  [](HWND hwnd, UINT msg, WPARAM wpar, LPARAM lpar, void* user_data,
213  LRESULT* result) {
214  BASE_DCHECK(user_data);
215  FlutterWindowsEngine* that =
216  static_cast<FlutterWindowsEngine*>(user_data);
217  BASE_DCHECK(that->lifecycle_manager_);
218  return that->lifecycle_manager_->WindowProc(hwnd, msg, wpar, lpar,
219  result);
220  },
221  static_cast<void*>(this));
222 
223  // Set up internal channels.
224  // TODO: Replace this with an embedder.h API. See
225  // https://github.com/flutter/flutter/issues/71099
226  internal_plugin_registrar_ =
227  std::make_unique<PluginRegistrar>(plugin_registrar_.get());
228  cursor_handler_ =
229  std::make_unique<CursorHandler>(messenger_wrapper_.get(), this);
230  platform_handler_ =
231  std::make_unique<PlatformHandler>(messenger_wrapper_.get(), this);
232  settings_plugin_ = std::make_unique<SettingsPlugin>(messenger_wrapper_.get(),
233  task_runner_.get());
234 }
235 
236 FlutterWindowsEngine::~FlutterWindowsEngine() {
237  messenger_->SetEngine(nullptr);
238  Stop();
239 }
240 
241 void FlutterWindowsEngine::SetSwitches(
242  const std::vector<std::string>& switches) {
243  project_->SetSwitches(switches);
244 }
245 
246 bool FlutterWindowsEngine::Run() {
247  return Run("");
248 }
249 
250 bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
251  if (!project_->HasValidPaths()) {
252  FML_LOG(ERROR) << "Missing or unresolvable paths to assets.";
253  return false;
254  }
255  std::string assets_path_string = project_->assets_path().u8string();
256  std::string icu_path_string = project_->icu_path().u8string();
257  if (embedder_api_.RunsAOTCompiledDartCode()) {
258  aot_data_ = project_->LoadAotData(embedder_api_);
259  if (!aot_data_) {
260  FML_LOG(ERROR) << "Unable to start engine without AOT data.";
261  return false;
262  }
263  }
264 
265  // FlutterProjectArgs is expecting a full argv, so when processing it for
266  // flags the first item is treated as the executable and ignored. Add a dummy
267  // value so that all provided arguments are used.
268  std::string executable_name = GetExecutableName();
269  std::vector<const char*> argv = {executable_name.c_str()};
270  std::vector<std::string> switches = project_->GetSwitches();
271  std::transform(
272  switches.begin(), switches.end(), std::back_inserter(argv),
273  [](const std::string& arg) -> const char* { return arg.c_str(); });
274 
275  const std::vector<std::string>& entrypoint_args =
276  project_->dart_entrypoint_arguments();
277  std::vector<const char*> entrypoint_argv;
278  std::transform(
279  entrypoint_args.begin(), entrypoint_args.end(),
280  std::back_inserter(entrypoint_argv),
281  [](const std::string& arg) -> const char* { return arg.c_str(); });
282 
283  // Configure task runners.
284  FlutterTaskRunnerDescription platform_task_runner = {};
285  platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
286  platform_task_runner.user_data = task_runner_.get();
287  platform_task_runner.runs_task_on_current_thread_callback =
288  [](void* user_data) -> bool {
289  return static_cast<TaskRunner*>(user_data)->RunsTasksOnCurrentThread();
290  };
291  platform_task_runner.post_task_callback = [](FlutterTask task,
292  uint64_t target_time_nanos,
293  void* user_data) -> void {
294  static_cast<TaskRunner*>(user_data)->PostFlutterTask(task,
295  target_time_nanos);
296  };
297  FlutterCustomTaskRunners custom_task_runners = {};
298  custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
299  custom_task_runners.platform_task_runner = &platform_task_runner;
300  custom_task_runners.thread_priority_setter =
302 
303  FlutterProjectArgs args = {};
304  args.struct_size = sizeof(FlutterProjectArgs);
305  args.shutdown_dart_vm_when_done = true;
306  args.assets_path = assets_path_string.c_str();
307  args.icu_data_path = icu_path_string.c_str();
308  args.command_line_argc = static_cast<int>(argv.size());
309  args.command_line_argv = argv.empty() ? nullptr : argv.data();
310 
311  // Fail if conflicting non-default entrypoints are specified in the method
312  // argument and the project.
313  //
314  // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
315  // The entrypoint method parameter should eventually be removed from this
316  // method and only the entrypoint specified in project_ should be used.
317  if (!project_->dart_entrypoint().empty() && !entrypoint.empty() &&
318  project_->dart_entrypoint() != entrypoint) {
319  FML_LOG(ERROR) << "Conflicting entrypoints were specified in "
320  "FlutterDesktopEngineProperties.dart_entrypoint and "
321  "FlutterDesktopEngineRun(engine, entry_point). ";
322  return false;
323  }
324  if (!entrypoint.empty()) {
325  args.custom_dart_entrypoint = entrypoint.data();
326  } else if (!project_->dart_entrypoint().empty()) {
327  args.custom_dart_entrypoint = project_->dart_entrypoint().c_str();
328  }
329  args.dart_entrypoint_argc = static_cast<int>(entrypoint_argv.size());
330  args.dart_entrypoint_argv =
331  entrypoint_argv.empty() ? nullptr : entrypoint_argv.data();
332  args.platform_message_callback =
333  [](const FlutterPlatformMessage* engine_message,
334  void* user_data) -> void {
335  auto host = static_cast<FlutterWindowsEngine*>(user_data);
336  return host->HandlePlatformMessage(engine_message);
337  };
338  args.vsync_callback = [](void* user_data, intptr_t baton) -> void {
339  auto host = static_cast<FlutterWindowsEngine*>(user_data);
340  host->OnVsync(baton);
341  };
342  args.on_pre_engine_restart_callback = [](void* user_data) {
343  auto host = static_cast<FlutterWindowsEngine*>(user_data);
344  host->OnPreEngineRestart();
345  };
346  args.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
347  void* user_data) {
348  auto host = static_cast<FlutterWindowsEngine*>(user_data);
349  auto view = host->view();
350  if (!view) {
351  return;
352  }
353 
354  auto accessibility_bridge = view->accessibility_bridge().lock();
355  if (!accessibility_bridge) {
356  return;
357  }
358 
359  for (size_t i = 0; i < update->node_count; i++) {
360  const FlutterSemanticsNode2* node = update->nodes[i];
361  accessibility_bridge->AddFlutterSemanticsNodeUpdate(*node);
362  }
363 
364  for (size_t i = 0; i < update->custom_action_count; i++) {
365  const FlutterSemanticsCustomAction2* action = update->custom_actions[i];
366  accessibility_bridge->AddFlutterSemanticsCustomActionUpdate(*action);
367  }
368 
369  accessibility_bridge->CommitUpdates();
370  };
371  args.root_isolate_create_callback = [](void* user_data) {
372  auto host = static_cast<FlutterWindowsEngine*>(user_data);
373  if (host->root_isolate_create_callback_) {
374  host->root_isolate_create_callback_();
375  }
376  };
377  args.channel_update_callback = [](const FlutterChannelUpdate* update,
378  void* user_data) {
379  auto host = static_cast<FlutterWindowsEngine*>(user_data);
380  if (SAFE_ACCESS(update, channel, nullptr) != nullptr) {
381  std::string channel_name(update->channel);
382  host->OnChannelUpdate(std::move(channel_name),
383  SAFE_ACCESS(update, listening, false));
384  }
385  };
386 
387  args.custom_task_runners = &custom_task_runners;
388 
389  if (aot_data_) {
390  args.aot_data = aot_data_.get();
391  }
392 
393  // The platform thread creates OpenGL contexts. These
394  // must be released to be used by the engine's threads.
395  FML_DCHECK(!surface_manager_ || !surface_manager_->HasContextCurrent());
396 
397  FlutterRendererConfig renderer_config;
398 
399  if (enable_impeller_) {
400  // Impeller does not support a Software backend. Avoid falling back and
401  // confusing the engine on which renderer is selected.
402  if (!surface_manager_) {
403  FML_LOG(ERROR) << "Could not create surface manager. Impeller backend "
404  "does not support software rendering.";
405  return false;
406  }
407  renderer_config = GetOpenGLRendererConfig();
408  } else {
409  renderer_config = surface_manager_ ? GetOpenGLRendererConfig()
410  : GetSoftwareRendererConfig();
411  }
412 
413  auto result = embedder_api_.Run(FLUTTER_ENGINE_VERSION, &renderer_config,
414  &args, this, &engine_);
415  if (result != kSuccess || engine_ == nullptr) {
416  FML_LOG(ERROR) << "Failed to start Flutter engine: error " << result;
417  return false;
418  }
419 
420  // Configure device frame rate displayed via devtools.
421  FlutterEngineDisplay display = {};
422  display.struct_size = sizeof(FlutterEngineDisplay);
423  display.display_id = 0;
424  display.single_display = true;
425  display.refresh_rate =
426  1.0 / (static_cast<double>(FrameInterval().count()) / 1000000000.0);
427 
428  std::vector<FlutterEngineDisplay> displays = {display};
429  embedder_api_.NotifyDisplayUpdate(engine_,
430  kFlutterEngineDisplaysUpdateTypeStartup,
431  displays.data(), displays.size());
432 
433  SendSystemLocales();
434  SetLifecycleState(flutter::AppLifecycleState::kResumed);
435 
436  settings_plugin_->StartWatching();
437  settings_plugin_->SendSettings();
438 
439  return true;
440 }
441 
442 bool FlutterWindowsEngine::Stop() {
443  if (engine_) {
444  for (const auto& [callback, registrar] :
445  plugin_registrar_destruction_callbacks_) {
446  callback(registrar);
447  }
448  FlutterEngineResult result = embedder_api_.Shutdown(engine_);
449  engine_ = nullptr;
450  return (result == kSuccess);
451  }
452  return false;
453 }
454 
455 void FlutterWindowsEngine::SetView(FlutterWindowsView* view) {
456  view_ = view;
457  InitializeKeyboard();
458 }
459 
460 void FlutterWindowsEngine::OnVsync(intptr_t baton) {
461  std::chrono::nanoseconds current_time =
462  std::chrono::nanoseconds(embedder_api_.GetCurrentTime());
463  std::chrono::nanoseconds frame_interval = FrameInterval();
464  auto next = SnapToNextTick(current_time, start_time_, frame_interval);
465  embedder_api_.OnVsync(engine_, baton, next.count(),
466  (next + frame_interval).count());
467 }
468 
469 std::chrono::nanoseconds FlutterWindowsEngine::FrameInterval() {
470  if (frame_interval_override_.has_value()) {
471  return frame_interval_override_.value();
472  }
473  uint64_t interval = 16600000;
474 
475  DWM_TIMING_INFO timing_info = {};
476  timing_info.cbSize = sizeof(timing_info);
477  HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
478  if (result == S_OK && timing_info.rateRefresh.uiDenominator > 0 &&
479  timing_info.rateRefresh.uiNumerator > 0) {
480  interval = static_cast<double>(timing_info.rateRefresh.uiDenominator *
481  1000000000.0) /
482  static_cast<double>(timing_info.rateRefresh.uiNumerator);
483  }
484 
485  return std::chrono::nanoseconds(interval);
486 }
487 
488 // Returns the currently configured Plugin Registrar.
489 FlutterDesktopPluginRegistrarRef FlutterWindowsEngine::GetRegistrar() {
490  return plugin_registrar_.get();
491 }
492 
493 void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
496  plugin_registrar_destruction_callbacks_[callback] = registrar;
497 }
498 
499 void FlutterWindowsEngine::SendWindowMetricsEvent(
500  const FlutterWindowMetricsEvent& event) {
501  if (engine_) {
502  embedder_api_.SendWindowMetricsEvent(engine_, &event);
503  }
504 }
505 
506 void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) {
507  if (engine_) {
508  embedder_api_.SendPointerEvent(engine_, &event, 1);
509  }
510 }
511 
512 void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event,
513  FlutterKeyEventCallback callback,
514  void* user_data) {
515  if (engine_) {
516  embedder_api_.SendKeyEvent(engine_, &event, callback, user_data);
517  }
518 }
519 
520 bool FlutterWindowsEngine::SendPlatformMessage(
521  const char* channel,
522  const uint8_t* message,
523  const size_t message_size,
524  const FlutterDesktopBinaryReply reply,
525  void* user_data) {
526  FlutterPlatformMessageResponseHandle* response_handle = nullptr;
527  if (reply != nullptr && user_data != nullptr) {
528  FlutterEngineResult result =
529  embedder_api_.PlatformMessageCreateResponseHandle(
530  engine_, reply, user_data, &response_handle);
531  if (result != kSuccess) {
532  FML_LOG(ERROR) << "Failed to create response handle";
533  return false;
534  }
535  }
536 
537  FlutterPlatformMessage platform_message = {
538  sizeof(FlutterPlatformMessage),
539  channel,
540  message,
541  message_size,
542  response_handle,
543  };
544 
545  FlutterEngineResult message_result =
546  embedder_api_.SendPlatformMessage(engine_, &platform_message);
547  if (response_handle != nullptr) {
548  embedder_api_.PlatformMessageReleaseResponseHandle(engine_,
549  response_handle);
550  }
551  return message_result == kSuccess;
552 }
553 
554 void FlutterWindowsEngine::SendPlatformMessageResponse(
556  const uint8_t* data,
557  size_t data_length) {
558  embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
559 }
560 
561 void FlutterWindowsEngine::HandlePlatformMessage(
562  const FlutterPlatformMessage* engine_message) {
563  if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
564  FML_LOG(ERROR) << "Invalid message size received. Expected: "
565  << sizeof(FlutterPlatformMessage) << " but received "
566  << engine_message->struct_size;
567  return;
568  }
569 
570  auto message = ConvertToDesktopMessage(*engine_message);
571 
572  message_dispatcher_->HandleMessage(
573  message, [this] {}, [this] {});
574 }
575 
576 void FlutterWindowsEngine::ReloadSystemFonts() {
577  embedder_api_.ReloadSystemFonts(engine_);
578 }
579 
580 void FlutterWindowsEngine::ScheduleFrame() {
581  embedder_api_.ScheduleFrame(engine_);
582 }
583 
584 void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) {
585  next_frame_callback_ = std::move(callback);
586 
587  embedder_api_.SetNextFrameCallback(
588  engine_,
589  [](void* user_data) {
590  // Embedder callback runs on raster thread. Switch back to platform
591  // thread.
592  FlutterWindowsEngine* self =
593  static_cast<FlutterWindowsEngine*>(user_data);
594 
595  self->task_runner_->PostTask(std::move(self->next_frame_callback_));
596  },
597  this);
598 }
599 
600 void FlutterWindowsEngine::SetLifecycleState(flutter::AppLifecycleState state) {
601  if (lifecycle_manager_) {
602  lifecycle_manager_->SetLifecycleState(state);
603  }
604 }
605 
606 void FlutterWindowsEngine::SendSystemLocales() {
607  std::vector<LanguageInfo> languages =
608  GetPreferredLanguageInfo(windows_proc_table_);
609  std::vector<FlutterLocale> flutter_locales;
610  flutter_locales.reserve(languages.size());
611  for (const auto& info : languages) {
612  flutter_locales.push_back(CovertToFlutterLocale(info));
613  }
614  // Convert the locale list to the locale pointer list that must be provided.
615  std::vector<const FlutterLocale*> flutter_locale_list;
616  flutter_locale_list.reserve(flutter_locales.size());
617  std::transform(flutter_locales.begin(), flutter_locales.end(),
618  std::back_inserter(flutter_locale_list),
619  [](const auto& arg) -> const auto* { return &arg; });
620  embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
621  flutter_locale_list.size());
622 }
623 
624 void FlutterWindowsEngine::InitializeKeyboard() {
625  if (view_ == nullptr) {
626  FML_LOG(ERROR) << "Cannot initialize keyboard on Windows headless mode.";
627  }
628 
629  auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
630  KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
631  KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan =
632  [](UINT virtual_key, bool extended) {
633  return MapVirtualKey(virtual_key,
634  extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
635  };
636  keyboard_key_handler_ = std::move(CreateKeyboardKeyHandler(
637  internal_plugin_messenger, get_key_state, map_vk_to_scan));
638  text_input_plugin_ =
639  std::move(CreateTextInputPlugin(internal_plugin_messenger));
640 }
641 
642 std::unique_ptr<KeyboardHandlerBase>
643 FlutterWindowsEngine::CreateKeyboardKeyHandler(
644  BinaryMessenger* messenger,
647  auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>(messenger);
648  keyboard_key_handler->AddDelegate(
649  std::make_unique<KeyboardKeyEmbedderHandler>(
650  [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback,
651  void* user_data) {
652  return SendKeyEvent(event, callback, user_data);
653  },
654  get_key_state, map_vk_to_scan));
655  keyboard_key_handler->AddDelegate(
656  std::make_unique<KeyboardKeyChannelHandler>(messenger));
657  keyboard_key_handler->InitKeyboardChannel();
658  return keyboard_key_handler;
659 }
660 
661 std::unique_ptr<TextInputPlugin> FlutterWindowsEngine::CreateTextInputPlugin(
662  BinaryMessenger* messenger) {
663  return std::make_unique<TextInputPlugin>(messenger, view_);
664 }
665 
666 bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) {
667  return (embedder_api_.RegisterExternalTexture(engine_, texture_id) ==
668  kSuccess);
669 }
670 
671 bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) {
672  return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) ==
673  kSuccess);
674 }
675 
676 bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
677  int64_t texture_id) {
678  return (embedder_api_.MarkExternalTextureFrameAvailable(
679  engine_, texture_id) == kSuccess);
680 }
681 
682 bool FlutterWindowsEngine::PostRasterThreadTask(fml::closure callback) {
683  struct Captures {
684  fml::closure callback;
685  };
686  auto captures = new Captures();
687  captures->callback = std::move(callback);
688  if (embedder_api_.PostRenderThreadTask(
689  engine_,
690  [](void* opaque) {
691  auto captures = reinterpret_cast<Captures*>(opaque);
692  captures->callback();
693  delete captures;
694  },
695  captures) == kSuccess) {
696  return true;
697  }
698  delete captures;
699  return false;
700 }
701 
702 bool FlutterWindowsEngine::DispatchSemanticsAction(
703  uint64_t target,
704  FlutterSemanticsAction action,
705  fml::MallocMapping data) {
706  return (embedder_api_.DispatchSemanticsAction(engine_, target, action,
707  data.GetMapping(),
708  data.GetSize()) == kSuccess);
709 }
710 
711 void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) {
712  if (engine_ && semantics_enabled_ != enabled) {
713  semantics_enabled_ = enabled;
714  embedder_api_.UpdateSemanticsEnabled(engine_, enabled);
715  view_->UpdateSemanticsEnabled(enabled);
716  }
717 }
718 
719 void FlutterWindowsEngine::OnPreEngineRestart() {
720  // Reset the keyboard's state on hot restart.
721  if (view_) {
722  InitializeKeyboard();
723  }
724 }
725 
726 std::string FlutterWindowsEngine::GetExecutableName() const {
727  std::pair<bool, std::string> result = fml::paths::GetExecutablePath();
728  if (result.first) {
729  const std::string& executable_path = result.second;
730  size_t last_separator = executable_path.find_last_of("/\\");
731  if (last_separator == std::string::npos ||
732  last_separator == executable_path.size() - 1) {
733  return executable_path;
734  }
735  return executable_path.substr(last_separator + 1);
736  }
737  return "Flutter";
738 }
739 
740 void FlutterWindowsEngine::UpdateAccessibilityFeatures(
741  FlutterAccessibilityFeature flags) {
742  embedder_api_.UpdateAccessibilityFeatures(engine_, flags);
743 }
744 
745 void FlutterWindowsEngine::UpdateHighContrastEnabled(bool enabled) {
746  high_contrast_enabled_ = enabled;
747  int flags = EnabledAccessibilityFeatures();
748  if (enabled) {
749  flags |=
750  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
751  } else {
752  flags &=
753  ~FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
754  }
755  UpdateAccessibilityFeatures(static_cast<FlutterAccessibilityFeature>(flags));
756  settings_plugin_->UpdateHighContrastMode(enabled);
757 }
758 
759 int FlutterWindowsEngine::EnabledAccessibilityFeatures() const {
760  int flags = 0;
761  if (high_contrast_enabled()) {
762  flags |=
763  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast;
764  }
765  // As more accessibility features are enabled for Windows,
766  // the corresponding checks and flags should be added here.
767  return 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:460
user_data
void * user_data
Definition: flutter_windows_view_unittests.cc:47
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:78
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler
std::function< SHORT(int)> GetKeyStateHandler
Definition: keyboard_key_embedder_handler.h:41
flutter::FlutterWindowsEngine::FlutterWindowsEngine
FlutterWindowsEngine(const FlutterProjectBundle &project)
Definition: flutter_windows_engine.cc:160
flutter::FlutterWindowsView::accessibility_bridge
std::weak_ptr< AccessibilityBridgeWindows > accessibility_bridge()
Definition: flutter_windows_view.h:210
type
enum flutter::testing::@66::KeyboardChange::Type type
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:719
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
binary_messenger_impl.h
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::TaskRunner
Definition: task_runner.h:26
FlutterDesktopOnPluginRegistrarDestroyed
void(* FlutterDesktopOnPluginRegistrarDestroyed)(FlutterDesktopPluginRegistrarRef)
Definition: flutter_plugin_registrar.h:23
flutter::EncodableValue
Definition: encodable_value.h:165
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:115
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::FlutterWindowsTextureRegistrar::ResolveGlFunctions
static void ResolveGlFunctions(GlProcs &gl_procs)
Definition: flutter_windows_texture_registrar.cc:129
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:31
keyboard_key_channel_handler.h
flutter::FlutterWindowsEngine::HandlePlatformMessage
void HandlePlatformMessage(const FlutterPlatformMessage *)
Definition: flutter_windows_engine.cc:561
flutter::WindowsPlatformThreadPrioritySetter
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
Definition: flutter_windows_engine.h:48
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:46