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  FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
373  FlutterWindowMetricsEvent event = {};
374  event.struct_size = sizeof(event);
375  event.width = width;
376  event.height = height;
377  event.pixel_ratio = pixel_ratio;
378  event.display_id = display_id;
379  event.view_id = view_id_;
380  engine_->SendWindowMetricsEvent(event);
381 }
382 
383 FlutterWindowMetricsEvent FlutterWindowsView::CreateWindowMetricsEvent() const {
384  PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
385  double pixel_ratio = binding_handler_->GetDpiScale();
386  FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
387 
388  FlutterWindowMetricsEvent event = {};
389  event.struct_size = sizeof(event);
390  event.width = bounds.width;
391  event.height = bounds.height;
392  event.pixel_ratio = pixel_ratio;
393  event.display_id = display_id;
394  event.view_id = view_id_;
395 
396  return event;
397 }
398 
400  // Non-implicit views' initial window metrics are sent when the view is added
401  // to the engine.
402  if (!IsImplicitView()) {
403  return;
404  }
405 
407 }
408 
409 FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
410  FlutterPointerDeviceKind device_kind,
411  int32_t device_id) {
412  // Create a virtual pointer ID that is unique across all device types
413  // to prevent pointers from clashing in the engine's converter
414  // (lib/ui/window/pointer_data_packet_converter.cc)
415  int32_t pointer_id = (static_cast<int32_t>(device_kind) << 28) | device_id;
416 
417  auto [it, added] = pointer_states_.try_emplace(pointer_id, nullptr);
418  if (added) {
419  auto state = std::make_unique<PointerState>();
420  state->device_kind = device_kind;
421  state->pointer_id = pointer_id;
422  it->second = std::move(state);
423  }
424 
425  return it->second.get();
426 }
427 
428 // Set's |event_data|'s phase to either kMove or kHover depending on the current
429 // primary mouse button state.
430 void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
431  FlutterPointerEvent* event_data,
432  const PointerState* state) const {
433  // For details about this logic, see FlutterPointerPhase in the embedder.h
434  // file.
435  if (state->buttons == 0) {
436  event_data->phase = state->flutter_state_is_down
437  ? FlutterPointerPhase::kUp
438  : FlutterPointerPhase::kHover;
439  } else {
440  event_data->phase = state->flutter_state_is_down
441  ? FlutterPointerPhase::kMove
442  : FlutterPointerPhase::kDown;
443  }
444 }
445 
446 void FlutterWindowsView::SendPointerMove(double x,
447  double y,
448  PointerState* state) {
449  FlutterPointerEvent event = {};
450  event.x = x;
451  event.y = y;
452 
453  SetEventPhaseFromCursorButtonState(&event, state);
454  SendPointerEventWithData(event, state);
455 }
456 
457 void FlutterWindowsView::SendPointerDown(double x,
458  double y,
459  PointerState* state) {
460  FlutterPointerEvent event = {};
461  event.x = x;
462  event.y = y;
463 
464  SetEventPhaseFromCursorButtonState(&event, state);
465  SendPointerEventWithData(event, state);
466 
467  state->flutter_state_is_down = true;
468 }
469 
470 void FlutterWindowsView::SendPointerUp(double x,
471  double y,
472  PointerState* state) {
473  FlutterPointerEvent event = {};
474  event.x = x;
475  event.y = y;
476 
477  SetEventPhaseFromCursorButtonState(&event, state);
478  SendPointerEventWithData(event, state);
479  if (event.phase == FlutterPointerPhase::kUp) {
480  state->flutter_state_is_down = false;
481  }
482 }
483 
484 void FlutterWindowsView::SendPointerLeave(double x,
485  double y,
486  PointerState* state) {
487  FlutterPointerEvent event = {};
488  event.x = x;
489  event.y = y;
490  event.phase = FlutterPointerPhase::kRemove;
491  SendPointerEventWithData(event, state);
492 }
493 
494 void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
495  double x,
496  double y) {
497  auto state =
498  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
499  state->pan_zoom_start_x = x;
500  state->pan_zoom_start_y = y;
501  FlutterPointerEvent event = {};
502  event.x = x;
503  event.y = y;
504  event.phase = FlutterPointerPhase::kPanZoomStart;
505  SendPointerEventWithData(event, state);
506 }
507 
508 void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
509  double pan_x,
510  double pan_y,
511  double scale,
512  double rotation) {
513  auto state =
514  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
515  FlutterPointerEvent event = {};
516  event.x = state->pan_zoom_start_x;
517  event.y = state->pan_zoom_start_y;
518  event.pan_x = pan_x;
519  event.pan_y = pan_y;
520  event.scale = scale;
521  event.rotation = rotation;
522  event.phase = FlutterPointerPhase::kPanZoomUpdate;
523  SendPointerEventWithData(event, state);
524 }
525 
526 void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
527  auto state =
528  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
529  FlutterPointerEvent event = {};
530  event.x = state->pan_zoom_start_x;
531  event.y = state->pan_zoom_start_y;
532  event.phase = FlutterPointerPhase::kPanZoomEnd;
533  SendPointerEventWithData(event, state);
534 }
535 
536 void FlutterWindowsView::SendText(const std::u16string& text) {
537  engine_->text_input_plugin()->TextHook(text);
538 }
539 
540 void FlutterWindowsView::SendKey(int key,
541  int scancode,
542  int action,
543  char32_t character,
544  bool extended,
545  bool was_down,
546  KeyEventCallback callback) {
549  [engine = engine_, view_id = view_id_, key, scancode, action, character,
550  extended, was_down, callback = std::move(callback)](bool handled) {
551  if (!handled) {
552  engine->text_input_plugin()->KeyboardHook(
554  }
555  if (engine->view(view_id)) {
556  callback(handled);
557  }
558  });
559 }
560 
561 void FlutterWindowsView::SendFocus(FlutterViewFocusState focus_state,
562  FlutterViewFocusDirection direction) {
563  FlutterViewFocusEvent event = {};
564  event.struct_size = sizeof(event);
565  event.view_id = view_id_;
566  event.state = focus_state;
567  event.direction = direction;
568  engine_->SendViewFocusEvent(event);
569 }
570 
571 void FlutterWindowsView::SendComposeBegin() {
572  engine_->text_input_plugin()->ComposeBeginHook();
573 }
574 
575 void FlutterWindowsView::SendComposeCommit() {
576  engine_->text_input_plugin()->ComposeCommitHook();
577 }
578 
579 void FlutterWindowsView::SendComposeEnd() {
580  engine_->text_input_plugin()->ComposeEndHook();
581 }
582 
583 void FlutterWindowsView::SendComposeChange(const std::u16string& text,
584  int cursor_pos) {
585  engine_->text_input_plugin()->ComposeChangeHook(text, cursor_pos);
586 }
587 
588 void FlutterWindowsView::SendScroll(double x,
589  double y,
590  double delta_x,
591  double delta_y,
592  int scroll_offset_multiplier,
593  FlutterPointerDeviceKind device_kind,
594  int32_t device_id) {
595  auto state = GetOrCreatePointerState(device_kind, device_id);
596 
597  FlutterPointerEvent event = {};
598  event.x = x;
599  event.y = y;
600  event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
601  event.scroll_delta_x = delta_x * scroll_offset_multiplier;
602  event.scroll_delta_y = delta_y * scroll_offset_multiplier;
603  SetEventPhaseFromCursorButtonState(&event, state);
604  SendPointerEventWithData(event, state);
605 }
606 
607 void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
608  double x,
609  double y) {
610  auto state =
611  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
612 
613  FlutterPointerEvent event = {};
614  event.x = x;
615  event.y = y;
616  event.signal_kind =
617  FlutterPointerSignalKind::kFlutterPointerSignalKindScrollInertiaCancel;
618  SetEventPhaseFromCursorButtonState(&event, state);
619  SendPointerEventWithData(event, state);
620 }
621 
622 void FlutterWindowsView::SendPointerEventWithData(
623  const FlutterPointerEvent& event_data,
624  PointerState* state) {
625  // If sending anything other than an add, and the pointer isn't already added,
626  // synthesize an add to satisfy Flutter's expectations about events.
627  if (!state->flutter_state_is_added &&
628  event_data.phase != FlutterPointerPhase::kAdd) {
629  FlutterPointerEvent event = {};
630  event.phase = FlutterPointerPhase::kAdd;
631  event.x = event_data.x;
632  event.y = event_data.y;
633  event.buttons = 0;
634  SendPointerEventWithData(event, state);
635  }
636 
637  // Don't double-add (e.g., if events are delivered out of order, so an add has
638  // already been synthesized).
639  if (state->flutter_state_is_added &&
640  event_data.phase == FlutterPointerPhase::kAdd) {
641  return;
642  }
643 
644  FlutterPointerEvent event = event_data;
645  event.device_kind = state->device_kind;
646  event.device = state->pointer_id;
647  event.buttons = state->buttons;
648  event.view_id = view_id_;
649 
650  // Set metadata that's always the same regardless of the event.
651  event.struct_size = sizeof(event);
652  event.timestamp =
653  std::chrono::duration_cast<std::chrono::microseconds>(
654  std::chrono::high_resolution_clock::now().time_since_epoch())
655  .count();
656 
657  engine_->SendPointerEvent(event);
658 
659  if (event_data.phase == FlutterPointerPhase::kAdd) {
660  state->flutter_state_is_added = true;
661  } else if (event_data.phase == FlutterPointerPhase::kRemove) {
662  auto it = pointer_states_.find(state->pointer_id);
663  if (it != pointer_states_.end()) {
664  pointer_states_.erase(it);
665  }
666  }
667 }
668 
670  // Called on the engine's raster thread.
671  std::unique_lock<std::mutex> lock(resize_mutex_);
672 
673  switch (resize_status_) {
674  case ResizeState::kResizeStarted:
675  // The caller must first call |OnFrameGenerated| or
676  // |OnEmptyFrameGenerated| before calling this method. This
677  // indicates one of the following:
678  //
679  // 1. The caller did not call these methods.
680  // 2. The caller ignored these methods' result.
681  // 3. The platform thread started a resize after the caller called these
682  // methods. We might have presented a frame of the wrong size to the
683  // view.
684  return;
685  case ResizeState::kFrameGenerated: {
686  // A frame was generated for a pending resize.
687  resize_status_ = ResizeState::kDone;
688  // Unblock the platform thread.
689  engine_->task_runner()->PostTask([this] {});
690 
691  lock.unlock();
692 
693  // Blocking the raster thread until DWM flushes alleviates glitches where
694  // previous size surface is stretched over current size view.
695  windows_proc_table_->DwmFlush();
696  }
697  case ResizeState::kDone:
698  return;
699  }
700 }
701 
703  return binding_handler_->OnBitmapSurfaceCleared();
704 }
705 
706 bool FlutterWindowsView::PresentSoftwareBitmap(const void* allocation,
707  size_t row_bytes,
708  size_t height) {
709  return binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes,
710  height);
711 }
712 
714  return view_id_;
715 }
716 
718  return view_id_ == kImplicitViewId;
719 }
720 
722  FML_DCHECK(surface_ == nullptr);
723 
724  if (engine_->egl_manager()) {
725  PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
726  surface_ = engine_->egl_manager()->CreateWindowSurface(
727  GetWindowHandle(), bounds.width, bounds.height);
728 
729  UpdateVsync(*engine_, surface_.get(), NeedsVsync());
730 
731  resize_target_width_ = bounds.width;
732  resize_target_height_ = bounds.height;
733  }
734 }
735 
736 bool FlutterWindowsView::ResizeRenderSurface(size_t width, size_t height) {
737  FML_DCHECK(surface_ != nullptr);
738 
739  // No-op if the surface is already the desired size.
740  if (width == surface_->width() && height == surface_->height()) {
741  return true;
742  }
743 
744  auto const existing_vsync = surface_->vsync_enabled();
745 
746  // TODO: Destroying the surface and re-creating it is expensive.
747  // Ideally this would use ANGLE's automatic surface sizing instead.
748  // See: https://github.com/flutter/flutter/issues/79427
749  if (!surface_->Destroy()) {
750  FML_LOG(ERROR) << "View resize failed to destroy surface";
751  return false;
752  }
753 
754  std::unique_ptr<egl::WindowSurface> resized_surface =
755  engine_->egl_manager()->CreateWindowSurface(GetWindowHandle(), width,
756  height);
757  if (!resized_surface) {
758  FML_LOG(ERROR) << "View resize failed to create surface";
759  return false;
760  }
761 
762  if (!resized_surface->MakeCurrent() ||
763  !resized_surface->SetVSyncEnabled(existing_vsync)) {
764  // Surfaces block until the v-blank by default.
765  // Failing to update the vsync might result in unnecessary blocking.
766  // This regresses performance but not correctness.
767  FML_LOG(ERROR) << "View resize failed to set vsync";
768  }
769 
770  surface_ = std::move(resized_surface);
771  return true;
772 }
773 
775  return surface_.get();
776 }
777 
779  engine_->UpdateHighContrastMode();
780 }
781 
783  return binding_handler_->GetWindowHandle();
784 }
785 
787  return engine_;
788 }
789 
790 void FlutterWindowsView::AnnounceAlert(const std::wstring& text) {
791  auto alert_delegate = binding_handler_->GetAlertDelegate();
792  if (!alert_delegate) {
793  return;
794  }
795  alert_delegate->SetText(fml::WideStringToUtf16(text));
796  ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
797  NotifyWinEventWrapper(alert_node, ax::mojom::Event::kAlert);
798 }
799 
800 void FlutterWindowsView::NotifyWinEventWrapper(ui::AXPlatformNodeWin* node,
801  ax::mojom::Event event) {
802  if (node) {
803  node->NotifyAccessibilityEvent(event);
804  }
805 }
806 
807 ui::AXFragmentRootDelegateWin* FlutterWindowsView::GetAxFragmentRootDelegate() {
808  return accessibility_bridge_.get();
809 }
810 
811 ui::AXPlatformNodeWin* FlutterWindowsView::AlertNode() const {
812  return binding_handler_->GetAlert();
813 }
814 
815 std::shared_ptr<AccessibilityBridgeWindows>
817  return std::make_shared<AccessibilityBridgeWindows>(this);
818 }
819 
821  if (semantics_enabled_ != enabled) {
822  semantics_enabled_ = enabled;
823 
824  if (!semantics_enabled_ && accessibility_bridge_) {
825  accessibility_bridge_.reset();
826  } else if (semantics_enabled_ && !accessibility_bridge_) {
827  accessibility_bridge_ = CreateAccessibilityBridge();
828  }
829  }
830 }
831 
833  UpdateVsync(*engine_, surface_.get(), NeedsVsync());
834 }
835 
837  engine_->OnWindowStateEvent(hwnd, event);
838 }
839 
841  return binding_handler_->Focus();
842 }
843 
844 bool FlutterWindowsView::NeedsVsync() const {
845  // If the Desktop Window Manager composition is enabled,
846  // the system itself synchronizes with vsync.
847  // See: https://learn.microsoft.com/windows/win32/dwm/composition-ovw
848  return !windows_proc_table_->DwmIsCompositionEnabled();
849 }
850 
851 } // 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
virtual 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:283
FlutterDesktopBinaryReply callback
std::u16string text
WindowStateEvent
An event representing a change in window state that may update the.
int64_t FlutterViewId
constexpr FlutterViewId kImplicitViewId