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