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  bool is_sized_to_content,
109  const BoxConstraints& box_constraints,
110  FlutterWindowsViewSizingDelegate* sizing_delegate,
111  std::shared_ptr<WindowsProcTable> windows_proc_table)
112  : view_id_(view_id),
113  engine_(engine),
114  is_sized_to_content_(is_sized_to_content),
115  box_constraints_(box_constraints),
116  sizing_delegate_(sizing_delegate),
117  windows_proc_table_(std::move(windows_proc_table)) {
118  if (windows_proc_table_ == nullptr) {
119  windows_proc_table_ = std::make_shared<WindowsProcTable>();
120  }
121 
122  // Take the binding handler, and give it a pointer back to self.
123  binding_handler_ = std::move(window_binding);
124  binding_handler_->SetView(this);
125 }
126 
128  // The view owns the child window.
129  // Notify the engine the view's child window will no longer be visible.
131 
132  if (surface_) {
133  DestroyWindowSurface(*engine_, std::move(surface_));
134  }
135 }
136 
138  // Called on the raster thread.
139  std::unique_lock<std::mutex> lock(resize_mutex_);
140 
141  if (surface_ == nullptr || !surface_->IsValid()) {
142  return false;
143  }
144 
145  if (resize_status_ != ResizeState::kResizeStarted) {
146  return true;
147  }
148 
149  if (!ResizeRenderSurface(resize_target_height_, resize_target_width_)) {
150  return false;
151  }
152 
153  // Platform thread is blocked for the entire duration until the
154  // resize_status_ is set to kDone by |OnFramePresented|.
155  resize_status_ = ResizeState::kFrameGenerated;
156  return true;
157 }
158 
159 bool FlutterWindowsView::OnFrameGenerated(size_t width, size_t height) {
160  // Called on the raster thread.
161  std::unique_lock<std::mutex> lock(resize_mutex_);
162 
163  if (IsSizedToContent()) {
164  if (!ResizeRenderSurface(width, height)) {
165  return false;
166  }
167 
168  sizing_delegate_->DidUpdateViewSize(width, height);
169  return true;
170  }
171 
172  if (surface_ == nullptr || !surface_->IsValid()) {
173  return false;
174  }
175 
176  if (resize_status_ != ResizeState::kResizeStarted) {
177  return true;
178  }
179 
180  if (resize_target_width_ != width || resize_target_height_ != height) {
181  return false;
182  }
183 
184  if (!ResizeRenderSurface(resize_target_width_, resize_target_height_)) {
185  return false;
186  }
187 
188  // Platform thread is blocked for the entire duration until the
189  // resize_status_ is set to kDone by |OnFramePresented|.
190  resize_status_ = ResizeState::kFrameGenerated;
191  return true;
192 }
193 
195  if (resize_status_ == ResizeState::kDone) {
196  // Request new frame.
197  engine_->ScheduleFrame();
198  }
199 }
200 
201 // Called on the platform thread.
202 bool FlutterWindowsView::OnWindowSizeChanged(size_t width, size_t height) {
203  if (IsSizedToContent()) {
204  // No resize synchronization needed for views sized to content.
205  return true;
206  }
207 
208  if (!engine_->egl_manager()) {
209  SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
210  return true;
211  }
212 
213  if (!surface_ || !surface_->IsValid()) {
214  SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
215  return true;
216  }
217 
218  // We're using OpenGL rendering. Resizing the surface must happen on the
219  // raster thread.
220  bool surface_will_update =
221  SurfaceWillUpdate(surface_->width(), surface_->height(), width, height);
222  if (!surface_will_update) {
223  SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
224  return true;
225  }
226 
227  {
228  std::unique_lock<std::mutex> lock(resize_mutex_);
229  resize_status_ = ResizeState::kResizeStarted;
230  resize_target_width_ = width;
231  resize_target_height_ = height;
232  }
233 
234  SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
235 
236  std::chrono::time_point<std::chrono::steady_clock> start_time =
237  std::chrono::steady_clock::now();
238 
239  while (true) {
240  if (std::chrono::steady_clock::now() > start_time + kWindowResizeTimeout) {
241  return false;
242  }
243  std::unique_lock<std::mutex> lock(resize_mutex_);
244  if (resize_status_ == ResizeState::kDone) {
245  break;
246  }
247  lock.unlock();
248  engine_->task_runner()->PollOnce(kWindowResizeTimeout);
249  }
250  return true;
251 }
252 
254  ForceRedraw();
255 }
256 
258  double y,
259  FlutterPointerDeviceKind device_kind,
260  int32_t device_id,
261  int modifiers_state) {
262  engine_->keyboard_key_handler()->SyncModifiersIfNeeded(modifiers_state);
263  SendPointerMove(x, y, GetOrCreatePointerState(device_kind, device_id));
264 }
265 
267  double x,
268  double y,
269  FlutterPointerDeviceKind device_kind,
270  int32_t device_id,
271  FlutterPointerMouseButtons flutter_button) {
272  if (flutter_button != 0) {
273  auto state = GetOrCreatePointerState(device_kind, device_id);
274  state->buttons |= flutter_button;
275  SendPointerDown(x, y, state);
276  }
277 }
278 
280  double x,
281  double y,
282  FlutterPointerDeviceKind device_kind,
283  int32_t device_id,
284  FlutterPointerMouseButtons flutter_button) {
285  if (flutter_button != 0) {
286  auto state = GetOrCreatePointerState(device_kind, device_id);
287  state->buttons &= ~flutter_button;
288  SendPointerUp(x, y, state);
289  }
290 }
291 
293  double y,
294  FlutterPointerDeviceKind device_kind,
295  int32_t device_id) {
296  SendPointerLeave(x, y, GetOrCreatePointerState(device_kind, device_id));
297 }
298 
300  PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
301  SendPointerPanZoomStart(device_id, point.x, point.y);
302 }
303 
305  double pan_x,
306  double pan_y,
307  double scale,
308  double rotation) {
309  SendPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, rotation);
310 }
311 
313  SendPointerPanZoomEnd(device_id);
314 }
315 
316 void FlutterWindowsView::OnText(const std::u16string& text) {
317  SendText(text);
318 }
319 
321  int scancode,
322  int action,
323  char32_t character,
324  bool extended,
325  bool was_down,
328 }
329 
330 void FlutterWindowsView::OnFocus(FlutterViewFocusState focus_state,
331  FlutterViewFocusDirection direction) {
332  SendFocus(focus_state, direction);
333 }
334 
336  SendComposeBegin();
337 }
338 
340  SendComposeCommit();
341 }
342 
344  SendComposeEnd();
345 }
346 
347 void FlutterWindowsView::OnComposeChange(const std::u16string& text,
348  int cursor_pos) {
349  SendComposeChange(text, cursor_pos);
350 }
351 
353  double y,
354  double delta_x,
355  double delta_y,
356  int scroll_offset_multiplier,
357  FlutterPointerDeviceKind device_kind,
358  int32_t device_id) {
359  SendScroll(x, y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
360  device_id);
361 }
362 
364  PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
365  SendScrollInertiaCancel(device_id, point.x, point.y);
366 }
367 
369  engine_->UpdateSemanticsEnabled(enabled);
370 }
371 
372 gfx::NativeViewAccessible FlutterWindowsView::GetNativeViewAccessible() {
373  if (!accessibility_bridge_) {
374  return nullptr;
375  }
376 
377  return accessibility_bridge_->GetChildOfAXFragmentRoot();
378 }
379 
381  binding_handler_->OnCursorRectUpdated(rect);
382 }
383 
385  binding_handler_->OnResetImeComposing();
386 }
387 
388 // Sends new size information to FlutterEngine.
389 void FlutterWindowsView::SendWindowMetrics(size_t width,
390  size_t height,
391  double pixel_ratio) const {
392  FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
393  FlutterWindowMetricsEvent event = {};
394  event.struct_size = sizeof(event);
395  event.width = width;
396  event.height = height;
397  event.has_constraints = true;
398  auto const constraints = GetConstraints();
399  event.min_width_constraint =
400  static_cast<size_t>(constraints.smallest().width());
401  event.min_height_constraint =
402  static_cast<size_t>(constraints.smallest().height());
403  event.max_width_constraint =
404  static_cast<size_t>(constraints.biggest().width());
405  event.max_height_constraint =
406  static_cast<size_t>(constraints.biggest().height());
407  event.pixel_ratio = pixel_ratio;
408  event.display_id = display_id;
409  event.view_id = view_id_;
410  engine_->SendWindowMetricsEvent(event);
411 }
412 
413 FlutterWindowMetricsEvent FlutterWindowsView::CreateWindowMetricsEvent() const {
414  PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
415  double pixel_ratio = binding_handler_->GetDpiScale();
416  FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
417 
418  FlutterWindowMetricsEvent event = {};
419  event.struct_size = sizeof(event);
420  event.width = bounds.width;
421  event.height = bounds.height;
422  auto constraints = GetConstraints();
423  event.has_constraints = true;
424  event.min_width_constraint =
425  static_cast<size_t>(constraints.smallest().width());
426  event.min_height_constraint =
427  static_cast<size_t>(constraints.smallest().height());
428  event.max_width_constraint =
429  static_cast<size_t>(constraints.biggest().width());
430  event.max_height_constraint =
431  static_cast<size_t>(constraints.biggest().height());
432  event.pixel_ratio = pixel_ratio;
433  event.display_id = display_id;
434  event.view_id = view_id_;
435 
436  return event;
437 }
438 
440  // Non-implicit views' initial window metrics are sent when the view is added
441  // to the engine.
442  if (!IsImplicitView()) {
443  return;
444  }
445 
447 }
448 
449 FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
450  FlutterPointerDeviceKind device_kind,
451  int32_t device_id) {
452  // Create a virtual pointer ID that is unique across all device types
453  // to prevent pointers from clashing in the engine's converter
454  // (lib/ui/window/pointer_data_packet_converter.cc)
455  int32_t pointer_id = (static_cast<int32_t>(device_kind) << 28) | device_id;
456 
457  auto [it, added] = pointer_states_.try_emplace(pointer_id, nullptr);
458  if (added) {
459  auto state = std::make_unique<PointerState>();
460  state->device_kind = device_kind;
461  state->pointer_id = pointer_id;
462  it->second = std::move(state);
463  }
464 
465  return it->second.get();
466 }
467 
468 // Set's |event_data|'s phase to either kMove or kHover depending on the current
469 // primary mouse button state.
470 void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
471  FlutterPointerEvent* event_data,
472  const PointerState* state) const {
473  // For details about this logic, see FlutterPointerPhase in the embedder.h
474  // file.
475  if (state->buttons == 0) {
476  event_data->phase = state->flutter_state_is_down
477  ? FlutterPointerPhase::kUp
478  : FlutterPointerPhase::kHover;
479  } else {
480  event_data->phase = state->flutter_state_is_down
481  ? FlutterPointerPhase::kMove
482  : FlutterPointerPhase::kDown;
483  }
484 }
485 
486 void FlutterWindowsView::SendPointerMove(double x,
487  double y,
488  PointerState* state) {
489  FlutterPointerEvent event = {};
490  event.x = x;
491  event.y = y;
492 
493  SetEventPhaseFromCursorButtonState(&event, state);
494  SendPointerEventWithData(event, state);
495 }
496 
497 void FlutterWindowsView::SendPointerDown(double x,
498  double y,
499  PointerState* state) {
500  FlutterPointerEvent event = {};
501  event.x = x;
502  event.y = y;
503 
504  SetEventPhaseFromCursorButtonState(&event, state);
505  SendPointerEventWithData(event, state);
506 
507  state->flutter_state_is_down = true;
508 }
509 
510 void FlutterWindowsView::SendPointerUp(double x,
511  double y,
512  PointerState* state) {
513  FlutterPointerEvent event = {};
514  event.x = x;
515  event.y = y;
516 
517  SetEventPhaseFromCursorButtonState(&event, state);
518  SendPointerEventWithData(event, state);
519  if (event.phase == FlutterPointerPhase::kUp) {
520  state->flutter_state_is_down = false;
521  }
522 }
523 
524 void FlutterWindowsView::SendPointerLeave(double x,
525  double y,
526  PointerState* state) {
527  FlutterPointerEvent event = {};
528  event.x = x;
529  event.y = y;
530  event.phase = FlutterPointerPhase::kRemove;
531  SendPointerEventWithData(event, state);
532 }
533 
534 void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
535  double x,
536  double y) {
537  auto state =
538  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
539  state->pan_zoom_start_x = x;
540  state->pan_zoom_start_y = y;
541  FlutterPointerEvent event = {};
542  event.x = x;
543  event.y = y;
544  event.phase = FlutterPointerPhase::kPanZoomStart;
545  SendPointerEventWithData(event, state);
546 }
547 
548 void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
549  double pan_x,
550  double pan_y,
551  double scale,
552  double rotation) {
553  auto state =
554  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
555  FlutterPointerEvent event = {};
556  event.x = state->pan_zoom_start_x;
557  event.y = state->pan_zoom_start_y;
558  event.pan_x = pan_x;
559  event.pan_y = pan_y;
560  event.scale = scale;
561  event.rotation = rotation;
562  event.phase = FlutterPointerPhase::kPanZoomUpdate;
563  SendPointerEventWithData(event, state);
564 }
565 
566 void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
567  auto state =
568  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
569  FlutterPointerEvent event = {};
570  event.x = state->pan_zoom_start_x;
571  event.y = state->pan_zoom_start_y;
572  event.phase = FlutterPointerPhase::kPanZoomEnd;
573  SendPointerEventWithData(event, state);
574 }
575 
576 void FlutterWindowsView::SendText(const std::u16string& text) {
577  engine_->text_input_plugin()->TextHook(text);
578 }
579 
580 void FlutterWindowsView::SendKey(int key,
581  int scancode,
582  int action,
583  char32_t character,
584  bool extended,
585  bool was_down,
586  KeyEventCallback callback) {
589  [engine = engine_, view_id = view_id_, key, scancode, action, character,
590  extended, was_down, callback = std::move(callback)](bool handled) {
591  if (!handled) {
592  engine->text_input_plugin()->KeyboardHook(
594  }
595  if (engine->view(view_id)) {
596  callback(handled);
597  }
598  });
599 }
600 
601 void FlutterWindowsView::SendFocus(FlutterViewFocusState focus_state,
602  FlutterViewFocusDirection direction) {
603  FlutterViewFocusEvent event = {};
604  event.struct_size = sizeof(event);
605  event.view_id = view_id_;
606  event.state = focus_state;
607  event.direction = direction;
608  engine_->SendViewFocusEvent(event);
609 }
610 
611 void FlutterWindowsView::SendComposeBegin() {
612  engine_->text_input_plugin()->ComposeBeginHook();
613 }
614 
615 void FlutterWindowsView::SendComposeCommit() {
616  engine_->text_input_plugin()->ComposeCommitHook();
617 }
618 
619 void FlutterWindowsView::SendComposeEnd() {
620  engine_->text_input_plugin()->ComposeEndHook();
621 }
622 
623 void FlutterWindowsView::SendComposeChange(const std::u16string& text,
624  int cursor_pos) {
625  engine_->text_input_plugin()->ComposeChangeHook(text, cursor_pos);
626 }
627 
628 void FlutterWindowsView::SendScroll(double x,
629  double y,
630  double delta_x,
631  double delta_y,
632  int scroll_offset_multiplier,
633  FlutterPointerDeviceKind device_kind,
634  int32_t device_id) {
635  auto state = GetOrCreatePointerState(device_kind, device_id);
636 
637  FlutterPointerEvent event = {};
638  event.x = x;
639  event.y = y;
640  event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
641  event.scroll_delta_x = delta_x * scroll_offset_multiplier;
642  event.scroll_delta_y = delta_y * scroll_offset_multiplier;
643  SetEventPhaseFromCursorButtonState(&event, state);
644  SendPointerEventWithData(event, state);
645 }
646 
647 void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
648  double x,
649  double y) {
650  auto state =
651  GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
652 
653  FlutterPointerEvent event = {};
654  event.x = x;
655  event.y = y;
656  event.signal_kind =
657  FlutterPointerSignalKind::kFlutterPointerSignalKindScrollInertiaCancel;
658  SetEventPhaseFromCursorButtonState(&event, state);
659  SendPointerEventWithData(event, state);
660 }
661 
662 void FlutterWindowsView::SendPointerEventWithData(
663  const FlutterPointerEvent& event_data,
664  PointerState* state) {
665  // If sending anything other than an add, and the pointer isn't already added,
666  // synthesize an add to satisfy Flutter's expectations about events.
667  if (!state->flutter_state_is_added &&
668  event_data.phase != FlutterPointerPhase::kAdd) {
669  FlutterPointerEvent event = {};
670  event.phase = FlutterPointerPhase::kAdd;
671  event.x = event_data.x;
672  event.y = event_data.y;
673  event.buttons = 0;
674  SendPointerEventWithData(event, state);
675  }
676 
677  // Don't double-add (e.g., if events are delivered out of order, so an add has
678  // already been synthesized).
679  if (state->flutter_state_is_added &&
680  event_data.phase == FlutterPointerPhase::kAdd) {
681  return;
682  }
683 
684  FlutterPointerEvent event = event_data;
685  event.device_kind = state->device_kind;
686  event.device = state->pointer_id;
687  event.buttons = state->buttons;
688  event.view_id = view_id_;
689 
690  // Set metadata that's always the same regardless of the event.
691  event.struct_size = sizeof(event);
692  event.timestamp =
693  std::chrono::duration_cast<std::chrono::microseconds>(
694  std::chrono::high_resolution_clock::now().time_since_epoch())
695  .count();
696 
697  engine_->SendPointerEvent(event);
698 
699  if (event_data.phase == FlutterPointerPhase::kAdd) {
700  state->flutter_state_is_added = true;
701  } else if (event_data.phase == FlutterPointerPhase::kRemove) {
702  auto it = pointer_states_.find(state->pointer_id);
703  if (it != pointer_states_.end()) {
704  pointer_states_.erase(it);
705  }
706  }
707 }
708 
710  // Called on the engine's raster thread.
711  std::unique_lock<std::mutex> lock(resize_mutex_);
712 
713  switch (resize_status_) {
714  case ResizeState::kResizeStarted:
715  // The caller must first call |OnFrameGenerated| or
716  // |OnEmptyFrameGenerated| before calling this method. This
717  // indicates one of the following:
718  //
719  // 1. The caller did not call these methods.
720  // 2. The caller ignored these methods' result.
721  // 3. The platform thread started a resize after the caller called these
722  // methods. We might have presented a frame of the wrong size to the
723  // view.
724  return;
725  case ResizeState::kFrameGenerated: {
726  // A frame was generated for a pending resize.
727  resize_status_ = ResizeState::kDone;
728  // Unblock the platform thread.
729  engine_->task_runner()->PostTask([this] {});
730 
731  lock.unlock();
732 
733  // Blocking the raster thread until DWM flushes alleviates glitches where
734  // previous size surface is stretched over current size view.
735  windows_proc_table_->DwmFlush();
736  }
737  case ResizeState::kDone:
738  return;
739  }
740 }
741 
743  return binding_handler_->OnBitmapSurfaceCleared();
744 }
745 
746 bool FlutterWindowsView::PresentSoftwareBitmap(const void* allocation,
747  size_t row_bytes,
748  size_t height) {
749  return binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes,
750  height);
751 }
752 
754  return view_id_;
755 }
756 
758  return view_id_ == kImplicitViewId;
759 }
760 
762  FML_DCHECK(surface_ == nullptr);
763 
764  if (engine_->egl_manager()) {
765  PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
766  surface_ = engine_->egl_manager()->CreateWindowSurface(
767  GetWindowHandle(), bounds.width, bounds.height);
768 
769  UpdateVsync(*engine_, surface_.get(), NeedsVsync());
770 
771  resize_target_width_ = bounds.width;
772  resize_target_height_ = bounds.height;
773  }
774 }
775 
776 bool FlutterWindowsView::ResizeRenderSurface(size_t width, size_t height) {
777  FML_DCHECK(surface_ != nullptr);
778 
779  // No-op if the surface is already the desired size.
780  if (width == surface_->width() && height == surface_->height()) {
781  return true;
782  }
783 
784  auto const existing_vsync = surface_->vsync_enabled();
785 
786  // TODO: Destroying the surface and re-creating it is expensive.
787  // Ideally this would use ANGLE's automatic surface sizing instead.
788  // See: https://github.com/flutter/flutter/issues/79427
789  if (!surface_->Destroy()) {
790  FML_LOG(ERROR) << "View resize failed to destroy surface";
791  return false;
792  }
793 
794  std::unique_ptr<egl::WindowSurface> resized_surface =
795  engine_->egl_manager()->CreateWindowSurface(GetWindowHandle(), width,
796  height);
797  if (!resized_surface) {
798  FML_LOG(ERROR) << "View resize failed to create surface";
799  return false;
800  }
801 
802  if (!resized_surface->MakeCurrent() ||
803  !resized_surface->SetVSyncEnabled(existing_vsync)) {
804  // Surfaces block until the v-blank by default.
805  // Failing to update the vsync might result in unnecessary blocking.
806  // This regresses performance but not correctness.
807  FML_LOG(ERROR) << "View resize failed to set vsync";
808  }
809 
810  surface_ = std::move(resized_surface);
811  return true;
812 }
813 
815  return surface_.get();
816 }
817 
819  engine_->UpdateHighContrastMode();
820 }
821 
823  return binding_handler_->GetWindowHandle();
824 }
825 
827  return engine_;
828 }
829 
830 void FlutterWindowsView::AnnounceAlert(const std::wstring& text) {
831  auto alert_delegate = binding_handler_->GetAlertDelegate();
832  if (!alert_delegate) {
833  return;
834  }
835  alert_delegate->SetText(fml::WideStringToUtf16(text));
836  ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
837  NotifyWinEventWrapper(alert_node, ax::mojom::Event::kAlert);
838 }
839 
840 void FlutterWindowsView::NotifyWinEventWrapper(ui::AXPlatformNodeWin* node,
841  ax::mojom::Event event) {
842  if (node) {
843  node->NotifyAccessibilityEvent(event);
844  }
845 }
846 
847 ui::AXFragmentRootDelegateWin* FlutterWindowsView::GetAxFragmentRootDelegate() {
848  return accessibility_bridge_.get();
849 }
850 
851 ui::AXPlatformNodeWin* FlutterWindowsView::AlertNode() const {
852  return binding_handler_->GetAlert();
853 }
854 
855 std::shared_ptr<AccessibilityBridgeWindows>
857  return std::make_shared<AccessibilityBridgeWindows>(this);
858 }
859 
861  if (semantics_enabled_ != enabled) {
862  semantics_enabled_ = enabled;
863 
864  if (!semantics_enabled_ && accessibility_bridge_) {
865  accessibility_bridge_.reset();
866  } else if (semantics_enabled_ && !accessibility_bridge_) {
867  accessibility_bridge_ = CreateAccessibilityBridge();
868  }
869  }
870 }
871 
873  UpdateVsync(*engine_, surface_.get(), NeedsVsync());
874 }
875 
877  engine_->OnWindowStateEvent(hwnd, event);
878 }
879 
881  return binding_handler_->Focus();
882 }
883 
884 bool FlutterWindowsView::NeedsVsync() const {
885  // If the Desktop Window Manager composition is enabled,
886  // the system itself synchronizes with vsync.
887  // See: https://learn.microsoft.com/windows/win32/dwm/composition-ovw
888  return !windows_proc_table_->DwmIsCompositionEnabled();
889 }
890 
891 bool FlutterWindowsView::IsSizedToContent() const {
892  return is_sized_to_content_;
893 }
894 
895 BoxConstraints FlutterWindowsView::GetConstraints() const {
896  if (!is_sized_to_content_) {
897  PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
898  return BoxConstraints(Size(bounds.width, bounds.height),
899  Size(bounds.width, bounds.height));
900  }
901 
902  Size smallest = box_constraints_.smallest();
903  Size biggest = box_constraints_.biggest();
904  if (sizing_delegate_) {
905  auto const work_area = sizing_delegate_->GetWorkArea();
906  double const width = std::min(static_cast<double>(work_area.width),
907  box_constraints_.biggest().width());
908  double const height = std::min(static_cast<double>(work_area.height),
909  box_constraints_.biggest().height());
910  biggest = Size(width, height);
911  }
912  return BoxConstraints(smallest, biggest);
913 }
914 
915 } // 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
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
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)
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 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: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