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