Flutter Windows Embedder
flutter_windows_view.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 <chrono>
8 
9 #include "flutter/common/constants.h"
10 #include "flutter/fml/make_copyable.h"
11 #include "flutter/fml/platform/win/wstring_conversion.h"
12 #include "flutter/fml/synchronization/waitable_event.h"
16 #include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
17 
18 namespace flutter {
19 
20 namespace {
21 // The maximum duration to block the Windows event loop while waiting
22 // for a window resize operation to complete.
23 constexpr std::chrono::milliseconds kWindowResizeTimeout{100};
24 
25 /// Returns true if the surface will be updated as part of the resize process.
26 ///
27 /// This is called on window resize to determine if the platform thread needs
28 /// to be blocked until the frame with the right size has been rendered. It
29 /// should be kept in-sync with how the engine deals with a new surface request
30 /// as seen in `CreateOrUpdateSurface` in `GPUSurfaceGL`.
31 bool SurfaceWillUpdate(size_t cur_width,
32  size_t cur_height,
33  size_t target_width,
34  size_t target_height) {
35  // TODO (https://github.com/flutter/flutter/issues/65061) : Avoid special
36  // handling for zero dimensions.
37  bool non_zero_target_dims = target_height > 0 && target_width > 0;
38  bool not_same_size =
39  (cur_height != target_height) || (cur_width != target_width);
40  return non_zero_target_dims && not_same_size;
41 }
42 
43 /// Update the surface's swap interval to block until the v-blank iff
44 /// the system compositor is disabled.
45 void UpdateVsync(const FlutterWindowsEngine& engine,
46  egl::WindowSurface* surface,
47  bool needs_vsync) {
48  egl::Manager* egl_manager = engine.egl_manager();
49  if (!egl_manager) {
50  return;
51  }
52 
53  auto update_vsync = [egl_manager, surface, needs_vsync]() {
54  if (!surface || !surface->IsValid()) {
55  return;
56  }
57 
58  if (!surface->MakeCurrent()) {
59  FML_LOG(ERROR) << "Unable to make the render surface current to update "
60  "the swap interval";
61  return;
62  }
63 
64  if (!surface->SetVSyncEnabled(needs_vsync)) {
65  FML_LOG(ERROR) << "Unable to update the render surface's swap interval";
66  }
67 
68  if (!egl_manager->render_context()->ClearCurrent()) {
69  FML_LOG(ERROR) << "Unable to clear current surface after updating "
70  "the swap interval";
71  }
72  };
73 
74  // Updating the vsync makes the EGL context and render surface current.
75  // If the engine is running, the render surface should only be made current on
76  // the raster thread. If the engine is initializing, the raster thread doesn't
77  // exist yet and the render surface can be made current on the platform
78  // thread.
79  if (engine.running()) {
80  engine.PostRasterThreadTask(update_vsync);
81  } else {
82  update_vsync();
83  }
84 }
85 
86 /// Destroys a rendering surface that backs a Flutter view.
87 void DestroyWindowSurface(const FlutterWindowsEngine& engine,
88  std::unique_ptr<egl::WindowSurface> surface) {
89  // EGL surfaces are used on the raster thread if the engine is running.
90  // There may be pending raster tasks that use this surface. Destroy the
91  // surface on the raster thread to avoid concurrent uses.
92  if (engine.running()) {
93  engine.PostRasterThreadTask(fml::MakeCopyable(
94  [surface = std::move(surface)] { surface->Destroy(); }));
95  } else {
96  // There's no raster thread if engine isn't running. The surface can be
97  // destroyed on the platform thread.
98  surface->Destroy();
99  }
100 }
101 
102 } // namespace
103 
105  FlutterViewId view_id,
106  FlutterWindowsEngine* engine,
107  std::unique_ptr<WindowBindingHandler> window_binding,
108  std::shared_ptr<WindowsProcTable> windows_proc_table)
109  : view_id_(view_id),
110  engine_(engine),
111  windows_proc_table_(std::move(windows_proc_table)) {
112  if (windows_proc_table_ == nullptr) {
113  windows_proc_table_ = std::make_shared<WindowsProcTable>();
114  }
115 
116  // Take the binding handler, and give it a pointer back to self.
117  binding_handler_ = std::move(window_binding);
118  binding_handler_->SetView(this);
119 }
120 
122  // The view owns the child window.
123  // Notify the engine the view's child window will no longer be visible.
125 
126  if (surface_) {
127  DestroyWindowSurface(*engine_, std::move(surface_));
128  }
129 }
130 
132  // Called on the raster thread.
133  std::unique_lock<std::mutex> lock(resize_mutex_);
134 
135  if (surface_ == nullptr || !surface_->IsValid()) {
136  return false;
137  }
138 
139  if (resize_status_ != ResizeState::kResizeStarted) {
140  return true;
141  }
142 
143  if (!ResizeRenderSurface(resize_target_height_, resize_target_width_)) {
144  return false;
145  }
146 
147  // Platform thread is blocked for the entire duration until the
148  // resize_status_ is set to kDone by |OnFramePresented|.
149  resize_status_ = ResizeState::kFrameGenerated;
150  return true;
151 }
152 
153 bool FlutterWindowsView::OnFrameGenerated(size_t width, size_t height) {
154  // Called on the raster thread.
155  std::unique_lock<std::mutex> lock(resize_mutex_);
156 
157  if (surface_ == nullptr || !surface_->IsValid()) {
158  return false;
159  }
160 
161  if (resize_status_ != ResizeState::kResizeStarted) {
162  return true;
163  }
164 
165  if (resize_target_width_ != width || resize_target_height_ != height) {
166  return false;
167  }
168 
169  if (!ResizeRenderSurface(resize_target_width_, resize_target_height_)) {
170  return false;
171  }
172 
173  // Platform thread is blocked for the entire duration until the
174  // resize_status_ is set to kDone by |OnFramePresented|.
175  resize_status_ = ResizeState::kFrameGenerated;
176  return true;
177 }
178 
180  if (resize_status_ == ResizeState::kDone) {
181  // Request new frame.
182  engine_->ScheduleFrame();
183  }
184 }
185 
186 // Called on the platform thread.
187 bool FlutterWindowsView::OnWindowSizeChanged(size_t width, size_t height) {
188  if (!engine_->egl_manager()) {
189  SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
190  return true;
191  }
192 
193  if (!surface_ || !surface_->IsValid()) {
194  SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
195  return true;
196  }
197 
198  // We're using OpenGL rendering. Resizing the surface must happen on the
199  // raster thread.
200  bool surface_will_update =
201  SurfaceWillUpdate(surface_->width(), surface_->height(), width, height);
202  if (!surface_will_update) {
203  SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
204  return true;
205  }
206 
207  {
208  std::unique_lock<std::mutex> lock(resize_mutex_);
209  resize_status_ = ResizeState::kResizeStarted;
210  resize_target_width_ = width;
211  resize_target_height_ = height;
212  }
213 
214  SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
215 
216  std::chrono::time_point<std::chrono::steady_clock> start_time =
217  std::chrono::steady_clock::now();
218 
219  while (true) {
220  if (std::chrono::steady_clock::now() > start_time + kWindowResizeTimeout) {
221  return false;
222  }
223  std::unique_lock<std::mutex> lock(resize_mutex_);
224  if (resize_status_ == ResizeState::kDone) {
225  break;
226  }
227  lock.unlock();
228  engine_->task_runner()->PollOnce(kWindowResizeTimeout);
229  }
230  return true;
231 }
232 
234  ForceRedraw();
235 }
236 
238  double y,
239  FlutterPointerDeviceKind device_kind,
240  int32_t device_id,
241  int modifiers_state) {
242  engine_->keyboard_key_handler()->SyncModifiersIfNeeded(modifiers_state);
243  SendPointerMove(x, y, GetOrCreatePointerState(device_kind, device_id));
244 }
245 
247  double x,
248  double y,
249  FlutterPointerDeviceKind device_kind,
250  int32_t device_id,
251  FlutterPointerMouseButtons flutter_button) {
252  if (flutter_button != 0) {
253  auto state = GetOrCreatePointerState(device_kind, device_id);
254  state->buttons |= flutter_button;
255  SendPointerDown(x, y, state);
256  }
257 }
258 
260  double x,
261  double y,
262  FlutterPointerDeviceKind device_kind,
263  int32_t device_id,
264  FlutterPointerMouseButtons flutter_button) {
265  if (flutter_button != 0) {
266  auto state = GetOrCreatePointerState(device_kind, device_id);
267  state->buttons &= ~flutter_button;
268  SendPointerUp(x, y, state);
269  }
270 }
271 
273  double y,
274  FlutterPointerDeviceKind device_kind,
275  int32_t device_id) {
276  SendPointerLeave(x, y, GetOrCreatePointerState(device_kind, device_id));
277 }
278 
280  PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
281  SendPointerPanZoomStart(device_id, point.x, point.y);
282 }
283 
285  double pan_x,
286  double pan_y,
287  double scale,
288  double rotation) {
289  SendPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, rotation);
290 }
291 
293  SendPointerPanZoomEnd(device_id);
294 }
295 
296 void FlutterWindowsView::OnText(const std::u16string& text) {
297  SendText(text);
298 }
299 
301  int scancode,
302  int action,
303  char32_t character,
304  bool extended,
305  bool was_down,
308 }
309 
310 void FlutterWindowsView::OnFocus(FlutterViewFocusState focus_state,
311  FlutterViewFocusDirection direction) {
312  SendFocus(focus_state, direction);
313 }
314 
316  SendComposeBegin();
317 }
318 
320  SendComposeCommit();
321 }
322 
324  SendComposeEnd();
325 }
326 
327 void FlutterWindowsView::OnComposeChange(const std::u16string& text,
328  int cursor_pos) {
329  SendComposeChange(text, cursor_pos);
330 }
331 
333  double y,
334  double delta_x,
335  double delta_y,
336  int scroll_offset_multiplier,
337  FlutterPointerDeviceKind device_kind,
338  int32_t device_id) {
339  SendScroll(x, y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
340  device_id);
341 }
342 
344  PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
345  SendScrollInertiaCancel(device_id, point.x, point.y);
346 }
347 
349  engine_->UpdateSemanticsEnabled(enabled);
350 }
351 
352 gfx::NativeViewAccessible FlutterWindowsView::GetNativeViewAccessible() {
353  if (!accessibility_bridge_) {
354  return nullptr;
355  }
356 
357  return accessibility_bridge_->GetChildOfAXFragmentRoot();
358 }
359 
361  binding_handler_->OnCursorRectUpdated(rect);
362 }
363 
365  binding_handler_->OnResetImeComposing();
366 }
367 
368 // Sends new size information to FlutterEngine.
369 void FlutterWindowsView::SendWindowMetrics(size_t width,
370  size_t height,
371  double pixel_ratio) const {
372  FlutterWindowMetricsEvent event = {};
373  event.struct_size = sizeof(event);
374  event.width = width;
375  event.height = height;
376  event.pixel_ratio = pixel_ratio;
377  event.view_id = view_id_;
378  engine_->SendWindowMetricsEvent(event);
379 }
380 
381 FlutterWindowMetricsEvent FlutterWindowsView::CreateWindowMetricsEvent() const {
382  PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
383  double pixel_ratio = binding_handler_->GetDpiScale();
384 
385  FlutterWindowMetricsEvent event = {};
386  event.struct_size = sizeof(event);
387  event.width = bounds.width;
388  event.height = bounds.height;
389  event.pixel_ratio = pixel_ratio;
390  event.view_id = view_id_;
391 
392  return event;
393 }
394 
396  // Non-implicit views' initial window metrics are sent when the view is added
397  // to the engine.
398  if (!IsImplicitView()) {
399  return;
400  }
401 
403 }
404 
405 FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
406  FlutterPointerDeviceKind device_kind,
407  int32_t device_id) {
408  // Create a virtual pointer ID that is unique across all device types
409  // to prevent pointers from clashing in the engine's converter
410  // (lib/ui/window/pointer_data_packet_converter.cc)
411  int32_t pointer_id = (static_cast<int32_t>(device_kind) << 28) | device_id;
412 
413  auto [it, added] = pointer_states_.try_emplace(pointer_id, nullptr);
414  if (added) {
415  auto state = std::make_unique<PointerState>();
416  state->device_kind = device_kind;
417  state->pointer_id = pointer_id;
418  it->second = std::move(state);
419  }
420 
421  return it->second.get();
422 }
423 
424 // Set's |event_data|'s phase to either kMove or kHover depending on the current
425 // primary mouse button state.
426 void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
427  FlutterPointerEvent* event_data,
428  const PointerState* state) const {
429  // For details about this logic, see FlutterPointerPhase in the embedder.h
430  // file.
431  if (state->buttons == 0) {
432  event_data->phase = state->flutter_state_is_down
433  ? FlutterPointerPhase::kUp
434  : FlutterPointerPhase::kHover;
435  } else {
436  event_data->phase = state->flutter_state_is_down
437  ? FlutterPointerPhase::kMove
438  : FlutterPointerPhase::kDown;
439  }
440 }
441 
442 void FlutterWindowsView::SendPointerMove(double x,
443  double y,
444  PointerState* state) {
445  FlutterPointerEvent event = {};
446  event.x = x;
447  event.y = y;
448 
449  SetEventPhaseFromCursorButtonState(&event, state);
450  SendPointerEventWithData(event, state);
451 }
452 
453 void FlutterWindowsView::SendPointerDown(double x,
454  double y,
455  PointerState* state) {
456  FlutterPointerEvent event = {};
457  event.x = x;
458  event.y = y;
459 
460  SetEventPhaseFromCursorButtonState(&event, state);
461  SendPointerEventWithData(event, state);
462 
463  state->flutter_state_is_down = true;
464 }
465 
466 void FlutterWindowsView::SendPointerUp(double x,
467  double y,
468  PointerState* state) {
469  FlutterPointerEvent event = {};
470  event.x = x;
471  event.y = y;
472 
473  SetEventPhaseFromCursorButtonState(&event, state);
474  SendPointerEventWithData(event, state);
475  if (event.phase == FlutterPointerPhase::kUp) {
476  state->flutter_state_is_down = false;
477  }
478 }
479 
480 void FlutterWindowsView::SendPointerLeave(double x,
481  double y,
482  PointerState* state) {
483  FlutterPointerEvent event = {};
484  event.x = x;
485  event.y = y;
486  event.phase = FlutterPointerPhase::kRemove;
487  SendPointerEventWithData(event, state);
488 }
489 
490 void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
491  double x,
492  double y) {
493  auto state =
494  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
495  state->pan_zoom_start_x = x;
496  state->pan_zoom_start_y = y;
497  FlutterPointerEvent event = {};
498  event.x = x;
499  event.y = y;
500  event.phase = FlutterPointerPhase::kPanZoomStart;
501  SendPointerEventWithData(event, state);
502 }
503 
504 void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
505  double pan_x,
506  double pan_y,
507  double scale,
508  double rotation) {
509  auto state =
510  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
511  FlutterPointerEvent event = {};
512  event.x = state->pan_zoom_start_x;
513  event.y = state->pan_zoom_start_y;
514  event.pan_x = pan_x;
515  event.pan_y = pan_y;
516  event.scale = scale;
517  event.rotation = rotation;
518  event.phase = FlutterPointerPhase::kPanZoomUpdate;
519  SendPointerEventWithData(event, state);
520 }
521 
522 void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
523  auto state =
524  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
525  FlutterPointerEvent event = {};
526  event.x = state->pan_zoom_start_x;
527  event.y = state->pan_zoom_start_y;
528  event.phase = FlutterPointerPhase::kPanZoomEnd;
529  SendPointerEventWithData(event, state);
530 }
531 
532 void FlutterWindowsView::SendText(const std::u16string& text) {
533  engine_->text_input_plugin()->TextHook(text);
534 }
535 
536 void FlutterWindowsView::SendKey(int key,
537  int scancode,
538  int action,
539  char32_t character,
540  bool extended,
541  bool was_down,
542  KeyEventCallback callback) {
545  [engine = engine_, view_id = view_id_, key, scancode, action, character,
546  extended, was_down, callback = std::move(callback)](bool handled) {
547  if (!handled) {
548  engine->text_input_plugin()->KeyboardHook(
550  }
551  if (engine->view(view_id)) {
552  callback(handled);
553  }
554  });
555 }
556 
557 void FlutterWindowsView::SendFocus(FlutterViewFocusState focus_state,
558  FlutterViewFocusDirection direction) {
559  FlutterViewFocusEvent event = {};
560  event.struct_size = sizeof(event);
561  event.view_id = view_id_;
562  event.state = focus_state;
563  event.direction = direction;
564  engine_->SendViewFocusEvent(event);
565 }
566 
567 void FlutterWindowsView::SendComposeBegin() {
568  engine_->text_input_plugin()->ComposeBeginHook();
569 }
570 
571 void FlutterWindowsView::SendComposeCommit() {
572  engine_->text_input_plugin()->ComposeCommitHook();
573 }
574 
575 void FlutterWindowsView::SendComposeEnd() {
576  engine_->text_input_plugin()->ComposeEndHook();
577 }
578 
579 void FlutterWindowsView::SendComposeChange(const std::u16string& text,
580  int cursor_pos) {
581  engine_->text_input_plugin()->ComposeChangeHook(text, cursor_pos);
582 }
583 
584 void FlutterWindowsView::SendScroll(double x,
585  double y,
586  double delta_x,
587  double delta_y,
588  int scroll_offset_multiplier,
589  FlutterPointerDeviceKind device_kind,
590  int32_t device_id) {
591  auto state = GetOrCreatePointerState(device_kind, device_id);
592 
593  FlutterPointerEvent event = {};
594  event.x = x;
595  event.y = y;
596  event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
597  event.scroll_delta_x = delta_x * scroll_offset_multiplier;
598  event.scroll_delta_y = delta_y * scroll_offset_multiplier;
599  SetEventPhaseFromCursorButtonState(&event, state);
600  SendPointerEventWithData(event, state);
601 }
602 
603 void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
604  double x,
605  double y) {
606  auto state =
607  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
608 
609  FlutterPointerEvent event = {};
610  event.x = x;
611  event.y = y;
612  event.signal_kind =
613  FlutterPointerSignalKind::kFlutterPointerSignalKindScrollInertiaCancel;
614  SetEventPhaseFromCursorButtonState(&event, state);
615  SendPointerEventWithData(event, state);
616 }
617 
618 void FlutterWindowsView::SendPointerEventWithData(
619  const FlutterPointerEvent& event_data,
620  PointerState* state) {
621  // If sending anything other than an add, and the pointer isn't already added,
622  // synthesize an add to satisfy Flutter's expectations about events.
623  if (!state->flutter_state_is_added &&
624  event_data.phase != FlutterPointerPhase::kAdd) {
625  FlutterPointerEvent event = {};
626  event.phase = FlutterPointerPhase::kAdd;
627  event.x = event_data.x;
628  event.y = event_data.y;
629  event.buttons = 0;
630  SendPointerEventWithData(event, state);
631  }
632 
633  // Don't double-add (e.g., if events are delivered out of order, so an add has
634  // already been synthesized).
635  if (state->flutter_state_is_added &&
636  event_data.phase == FlutterPointerPhase::kAdd) {
637  return;
638  }
639 
640  FlutterPointerEvent event = event_data;
641  event.device_kind = state->device_kind;
642  event.device = state->pointer_id;
643  event.buttons = state->buttons;
644  event.view_id = view_id_;
645 
646  // Set metadata that's always the same regardless of the event.
647  event.struct_size = sizeof(event);
648  event.timestamp =
649  std::chrono::duration_cast<std::chrono::microseconds>(
650  std::chrono::high_resolution_clock::now().time_since_epoch())
651  .count();
652 
653  engine_->SendPointerEvent(event);
654 
655  if (event_data.phase == FlutterPointerPhase::kAdd) {
656  state->flutter_state_is_added = true;
657  } else if (event_data.phase == FlutterPointerPhase::kRemove) {
658  auto it = pointer_states_.find(state->pointer_id);
659  if (it != pointer_states_.end()) {
660  pointer_states_.erase(it);
661  }
662  }
663 }
664 
666  // Called on the engine's raster thread.
667  std::unique_lock<std::mutex> lock(resize_mutex_);
668 
669  switch (resize_status_) {
670  case ResizeState::kResizeStarted:
671  // The caller must first call |OnFrameGenerated| or
672  // |OnEmptyFrameGenerated| before calling this method. This
673  // indicates one of the following:
674  //
675  // 1. The caller did not call these methods.
676  // 2. The caller ignored these methods' result.
677  // 3. The platform thread started a resize after the caller called these
678  // methods. We might have presented a frame of the wrong size to the
679  // view.
680  return;
681  case ResizeState::kFrameGenerated: {
682  // A frame was generated for a pending resize.
683  resize_status_ = ResizeState::kDone;
684  // Unblock the platform thread.
685  engine_->task_runner()->PostTask([this] {});
686 
687  lock.unlock();
688 
689  // Blocking the raster thread until DWM flushes alleviates glitches where
690  // previous size surface is stretched over current size view.
691  windows_proc_table_->DwmFlush();
692  }
693  case ResizeState::kDone:
694  return;
695  }
696 }
697 
699  return binding_handler_->OnBitmapSurfaceCleared();
700 }
701 
702 bool FlutterWindowsView::PresentSoftwareBitmap(const void* allocation,
703  size_t row_bytes,
704  size_t height) {
705  return binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes,
706  height);
707 }
708 
710  return view_id_;
711 }
712 
714  return view_id_ == kImplicitViewId;
715 }
716 
718  FML_DCHECK(surface_ == nullptr);
719 
720  if (engine_->egl_manager()) {
721  PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
722  surface_ = engine_->egl_manager()->CreateWindowSurface(
723  GetWindowHandle(), bounds.width, bounds.height);
724 
725  UpdateVsync(*engine_, surface_.get(), NeedsVsync());
726 
727  resize_target_width_ = bounds.width;
728  resize_target_height_ = bounds.height;
729  }
730 }
731 
732 bool FlutterWindowsView::ResizeRenderSurface(size_t width, size_t height) {
733  FML_DCHECK(surface_ != nullptr);
734 
735  // No-op if the surface is already the desired size.
736  if (width == surface_->width() && height == surface_->height()) {
737  return true;
738  }
739 
740  auto const existing_vsync = surface_->vsync_enabled();
741 
742  // TODO: Destroying the surface and re-creating it is expensive.
743  // Ideally this would use ANGLE's automatic surface sizing instead.
744  // See: https://github.com/flutter/flutter/issues/79427
745  if (!surface_->Destroy()) {
746  FML_LOG(ERROR) << "View resize failed to destroy surface";
747  return false;
748  }
749 
750  std::unique_ptr<egl::WindowSurface> resized_surface =
751  engine_->egl_manager()->CreateWindowSurface(GetWindowHandle(), width,
752  height);
753  if (!resized_surface) {
754  FML_LOG(ERROR) << "View resize failed to create surface";
755  return false;
756  }
757 
758  if (!resized_surface->MakeCurrent() ||
759  !resized_surface->SetVSyncEnabled(existing_vsync)) {
760  // Surfaces block until the v-blank by default.
761  // Failing to update the vsync might result in unnecessary blocking.
762  // This regresses performance but not correctness.
763  FML_LOG(ERROR) << "View resize failed to set vsync";
764  }
765 
766  surface_ = std::move(resized_surface);
767  return true;
768 }
769 
771  return surface_.get();
772 }
773 
775  engine_->UpdateHighContrastMode();
776 }
777 
779  return binding_handler_->GetWindowHandle();
780 }
781 
783  return engine_;
784 }
785 
786 void FlutterWindowsView::AnnounceAlert(const std::wstring& text) {
787  auto alert_delegate = binding_handler_->GetAlertDelegate();
788  if (!alert_delegate) {
789  return;
790  }
791  alert_delegate->SetText(fml::WideStringToUtf16(text));
792  ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
793  NotifyWinEventWrapper(alert_node, ax::mojom::Event::kAlert);
794 }
795 
796 void FlutterWindowsView::NotifyWinEventWrapper(ui::AXPlatformNodeWin* node,
797  ax::mojom::Event event) {
798  if (node) {
799  node->NotifyAccessibilityEvent(event);
800  }
801 }
802 
803 ui::AXFragmentRootDelegateWin* FlutterWindowsView::GetAxFragmentRootDelegate() {
804  return accessibility_bridge_.get();
805 }
806 
807 ui::AXPlatformNodeWin* FlutterWindowsView::AlertNode() const {
808  return binding_handler_->GetAlert();
809 }
810 
811 std::shared_ptr<AccessibilityBridgeWindows>
813  return std::make_shared<AccessibilityBridgeWindows>(this);
814 }
815 
817  if (semantics_enabled_ != enabled) {
818  semantics_enabled_ = enabled;
819 
820  if (!semantics_enabled_ && accessibility_bridge_) {
821  accessibility_bridge_.reset();
822  } else if (semantics_enabled_ && !accessibility_bridge_) {
823  accessibility_bridge_ = CreateAccessibilityBridge();
824  }
825  }
826 }
827 
829  UpdateVsync(*engine_, surface_.get(), NeedsVsync());
830 }
831 
833  engine_->OnWindowStateEvent(hwnd, event);
834 }
835 
837  return binding_handler_->Focus();
838 }
839 
840 bool FlutterWindowsView::NeedsVsync() const {
841  // If the Desktop Window Manager composition is enabled,
842  // the system itself synchronizes with vsync.
843  // See: https://learn.microsoft.com/windows/win32/dwm/composition-ovw
844  return !windows_proc_table_->DwmIsCompositionEnabled();
845 }
846 
847 } // namespace flutter
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)
void SendViewFocusEvent(const FlutterViewFocusEvent &event)
KeyboardHandlerBase * keyboard_key_handler()
void SendPointerEvent(const FlutterPointerEvent &event)
void SendWindowMetricsEvent(const FlutterWindowMetricsEvent &event)
virtual void OnPointerPanZoomStart(int32_t device_id) override
void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button) override
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
virtual void UpdateSemanticsEnabled(bool enabled)
virtual ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate() override
void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state) override
void OnScrollInertiaCancel(int32_t device_id) override
virtual std::shared_ptr< AccessibilityBridgeWindows > CreateAccessibilityBridge()
ui::AXPlatformNodeWin * AlertNode() const
virtual void OnUpdateSemanticsEnabled(bool enabled) override
void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id=0) override
virtual void NotifyWinEventWrapper(ui::AXPlatformNodeWin *node, ax::mojom::Event event)
FlutterWindowsEngine * GetEngine() const
void OnScroll(double x, double y, double delta_x, double delta_y, int scroll_offset_multiplier, FlutterPointerDeviceKind device_kind, int32_t device_id) override
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event) override
virtual void OnPointerPanZoomEnd(int32_t device_id) override
FlutterWindowMetricsEvent CreateWindowMetricsEvent() const
void AnnounceAlert(const std::wstring &text)
virtual bool PresentSoftwareBitmap(const void *allocation, size_t row_bytes, size_t height)
void OnFocus(FlutterViewFocusState focus_state, FlutterViewFocusDirection direction) override
virtual HWND GetWindowHandle() const
egl::WindowSurface * surface() const
bool OnWindowSizeChanged(size_t width, size_t height) override
void OnText(const std::u16string &) override
virtual void OnPointerPanZoomUpdate(int32_t device_id, double pan_x, double pan_y, double scale, double rotation) override
void OnComposeChange(const std::u16string &text, int cursor_pos) override
void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button) override
virtual gfx::NativeViewAccessible GetNativeViewAccessible() override
virtual void OnCursorRectUpdated(const Rect &rect)
void OnKey(int key, int scancode, int action, char32_t character, bool extended, bool was_down, KeyEventCallback callback) override
bool OnFrameGenerated(size_t width, size_t height)
virtual void SyncModifiersIfNeeded(int modifiers_state)=0
virtual void KeyboardHook(int key, int scancode, int action, char32_t character, bool extended, bool was_down, KeyEventCallback callback)=0
void PollOnce(std::chrono::milliseconds timeout)
Definition: task_runner.cc:95
void PostTask(TaskClosure task)
Definition: task_runner.cc:88
virtual void ComposeChangeHook(const std::u16string &text, int cursor_pos)
virtual void TextHook(const std::u16string &text)
virtual std::unique_ptr< WindowSurface > CreateWindowSurface(HWND hwnd, size_t width, size_t height)
Definition: manager.cc:276
FlutterDesktopBinaryReply callback
std::u16string text
WindowStateEvent
An event representing a change in window state that may update the.
int64_t FlutterViewId
Definition: flutter_view.h:13
constexpr FlutterViewId kImplicitViewId