Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
18
19namespace flutter {
20
21namespace {
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
25static 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.
29constexpr 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`.
37bool 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.
51void 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.
93void 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
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_width_, resize_target_height_)) {
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
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.
209 if (IsSizedToContent()) {
210 // No resize synchronization needed for views sized to content.
211 // Still send metrics so the engine renders at the correct viewport size
212 // (e.g. after the child window is resized to the host's client area).
213 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
214 return true;
215 }
216
217 if (!engine_->egl_manager()) {
218 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
219 return true;
220 }
221
222 if (!surface_ || !surface_->IsValid()) {
223 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
224 return true;
225 }
226
227 // We're using OpenGL rendering. Resizing the surface must happen on the
228 // raster thread.
229 bool surface_will_update =
230 SurfaceWillUpdate(surface_->width(), surface_->height(), width, height);
231 if (!surface_will_update) {
232 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
233 return true;
234 }
235
236 {
237 std::unique_lock<std::mutex> lock(resize_mutex_);
238 resize_status_ = ResizeState::kResizeStarted;
239 resize_target_width_ = width;
240 resize_target_height_ = height;
241 }
242
243 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
244
245 std::chrono::time_point<std::chrono::steady_clock> start_time =
246 std::chrono::steady_clock::now();
247
248 while (true) {
249 if (std::chrono::steady_clock::now() > start_time + kWindowResizeTimeout) {
250 return false;
251 }
252 std::unique_lock<std::mutex> lock(resize_mutex_);
253 if (resize_status_ == ResizeState::kDone) {
254 break;
255 }
256 lock.unlock();
257 engine_->task_runner()->PollOnce(kWindowResizeTimeout);
258 }
259 return true;
260}
261
265
267 double y,
268 FlutterPointerDeviceKind device_kind,
269 int32_t device_id,
270 uint64_t buttons,
271 uint32_t rotation,
272 uint32_t pressure,
273 int modifiers_state) {
274 engine_->keyboard_key_handler()->SyncModifiersIfNeeded(modifiers_state);
275 auto state = GetOrCreatePointerState(device_kind, device_id);
276 state->buttons = buttons;
277 state->rotation = rotation;
278 state->pressure = pressure;
279 SendPointerMove(x, y, state);
280}
281
283 double y,
284 FlutterPointerDeviceKind device_kind,
285 int32_t device_id,
286 uint64_t buttons,
287 uint32_t rotation,
288 uint32_t pressure) {
289 if (buttons != 0) {
290 auto state = GetOrCreatePointerState(device_kind, device_id);
291 state->buttons |= buttons;
292 state->rotation = rotation;
293 state->pressure = pressure;
294 SendPointerDown(x, y, state);
295 }
296}
297
299 double y,
300 FlutterPointerDeviceKind device_kind,
301 int32_t device_id,
302 uint64_t buttons) {
303 auto state = GetOrCreatePointerState(device_kind, device_id);
304 const uint64_t released_buttons = buttons == 0 ? state->buttons : buttons;
305 if (released_buttons != 0) {
306 state->buttons &= ~released_buttons;
307 SendPointerUp(x, y, state);
308 }
309}
310
312 double y,
313 FlutterPointerDeviceKind device_kind,
314 int32_t device_id) {
315 SendPointerLeave(x, y, GetOrCreatePointerState(device_kind, device_id));
316}
317
319 PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
320 SendPointerPanZoomStart(device_id, point.x, point.y);
321}
322
324 double pan_x,
325 double pan_y,
326 double scale,
327 double rotation) {
328 SendPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, rotation);
329}
330
332 SendPointerPanZoomEnd(device_id);
333}
334
335void FlutterWindowsView::OnText(const std::u16string& text) {
336 SendText(text);
337}
338
340 int scancode,
341 int action,
342 char32_t character,
343 bool extended,
344 bool was_down,
347}
348
350 FlutterViewFocusDirection direction) {
351 SendFocus(focus_state, direction);
352}
353
355 SendComposeBegin();
356}
357
359 SendComposeCommit();
360}
361
363 SendComposeEnd();
364}
365
366void FlutterWindowsView::OnComposeChange(const std::u16string& text,
367 int cursor_pos) {
368 SendComposeChange(text, cursor_pos);
369}
370
372 double y,
373 double delta_x,
374 double delta_y,
375 int scroll_offset_multiplier,
376 FlutterPointerDeviceKind device_kind,
377 int32_t device_id) {
378 SendScroll(x, y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
379 device_id);
380}
381
383 PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
384 SendScrollInertiaCancel(device_id, point.x, point.y);
385}
386
388 engine_->UpdateSemanticsEnabled(enabled);
389}
390
392 if (!accessibility_bridge_) {
393 return nullptr;
394 }
395
396 return accessibility_bridge_->GetChildOfAXFragmentRoot();
397}
398
400 binding_handler_->OnCursorRectUpdated(rect);
401}
402
404 binding_handler_->OnResetImeComposing();
405}
406
407// Sends new size information to FlutterEngine.
408void FlutterWindowsView::SendWindowMetrics(size_t width,
409 size_t height,
410 double pixel_ratio) const {
411 FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
412 FlutterWindowMetricsEvent event = {};
413 event.struct_size = sizeof(event);
414 event.width = width;
415 event.height = height;
416 event.has_constraints = true;
417 auto const constraints = GetConstraints();
418 event.min_width_constraint =
419 static_cast<size_t>(constraints.smallest().width());
420 event.min_height_constraint =
421 static_cast<size_t>(constraints.smallest().height());
422 event.max_width_constraint =
423 static_cast<size_t>(constraints.biggest().width());
424 event.max_height_constraint =
425 static_cast<size_t>(constraints.biggest().height());
426 event.pixel_ratio = pixel_ratio;
427 event.display_id = display_id;
428 event.view_id = view_id_;
429 engine_->SendWindowMetricsEvent(event);
430}
431
433 PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
434 double pixel_ratio = binding_handler_->GetDpiScale();
435 FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
436
437 FlutterWindowMetricsEvent event = {};
438 event.struct_size = sizeof(event);
439 event.width = bounds.width;
440 event.height = bounds.height;
441 auto constraints = GetConstraints();
442 event.has_constraints = true;
443 event.min_width_constraint =
444 static_cast<size_t>(constraints.smallest().width());
445 event.min_height_constraint =
446 static_cast<size_t>(constraints.smallest().height());
447 event.max_width_constraint =
448 static_cast<size_t>(constraints.biggest().width());
449 event.max_height_constraint =
450 static_cast<size_t>(constraints.biggest().height());
451 event.pixel_ratio = pixel_ratio;
452 event.display_id = display_id;
453 event.view_id = view_id_;
454
455 return event;
456}
457
459 // Non-implicit views' initial window metrics are sent when the view is added
460 // to the engine.
461 if (!IsImplicitView()) {
462 return;
463 }
464
466}
467
468FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
469 FlutterPointerDeviceKind device_kind,
470 int32_t device_id) {
471 // Create a virtual pointer ID that is unique across all device types
472 // to prevent pointers from clashing in the engine's converter
473 // (lib/ui/window/pointer_data_packet_converter.cc)
474 int32_t pointer_id = (static_cast<int32_t>(device_kind) << 28) | device_id;
475
476 auto [it, added] = pointer_states_.try_emplace(pointer_id, nullptr);
477 if (added) {
478 auto state = std::make_unique<PointerState>();
479 state->device_kind = device_kind;
480 state->pointer_id = pointer_id;
481 it->second = std::move(state);
482 }
483
484 return it->second.get();
485}
486
487// Set's |event_data|'s phase to either kMove or kHover depending on the current
488// primary mouse button state.
489void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
490 FlutterPointerEvent* event_data,
491 const PointerState* state) const {
492 // For details about this logic, see FlutterPointerPhase in the embedder.h
493 // file.
494 if (state->buttons == 0) {
495 event_data->phase = state->flutter_state_is_down
498 } else {
499 event_data->phase = state->flutter_state_is_down
502 }
503}
504
505void FlutterWindowsView::SendPointerMove(double x,
506 double y,
507 PointerState* state) {
508 FlutterPointerEvent event = {};
509 event.x = x;
510 event.y = y;
511
512 SetEventPhaseFromCursorButtonState(&event, state);
513 SendPointerEventWithData(event, state);
514}
515
516void FlutterWindowsView::SendPointerDown(double x,
517 double y,
518 PointerState* state) {
519 FlutterPointerEvent event = {};
520 event.x = x;
521 event.y = y;
522
523 SetEventPhaseFromCursorButtonState(&event, state);
524 SendPointerEventWithData(event, state);
525
526 state->flutter_state_is_down = true;
527}
528
529void FlutterWindowsView::SendPointerUp(double x,
530 double y,
531 PointerState* state) {
532 FlutterPointerEvent event = {};
533 event.x = x;
534 event.y = y;
535
536 SetEventPhaseFromCursorButtonState(&event, state);
537 SendPointerEventWithData(event, state);
538 if (event.phase == FlutterPointerPhase::kUp) {
539 state->flutter_state_is_down = false;
540 }
541}
542
543void FlutterWindowsView::SendPointerLeave(double x,
544 double y,
545 PointerState* state) {
546 FlutterPointerEvent event = {};
547 event.x = x;
548 event.y = y;
549 event.phase = FlutterPointerPhase::kRemove;
550 SendPointerEventWithData(event, state);
551}
552
553void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
554 double x,
555 double y) {
556 auto state =
557 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
558 state->pan_zoom_start_x = x;
559 state->pan_zoom_start_y = y;
560 FlutterPointerEvent event = {};
561 event.x = x;
562 event.y = y;
564 SendPointerEventWithData(event, state);
565}
566
567void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
568 double pan_x,
569 double pan_y,
570 double scale,
571 double rotation) {
572 auto state =
573 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
574 FlutterPointerEvent event = {};
575 event.x = state->pan_zoom_start_x;
576 event.y = state->pan_zoom_start_y;
577 event.pan_x = pan_x;
578 event.pan_y = pan_y;
579 event.scale = scale;
580 event.rotation = rotation;
582 SendPointerEventWithData(event, state);
583}
584
585void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
586 auto state =
587 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
588 FlutterPointerEvent event = {};
589 event.x = state->pan_zoom_start_x;
590 event.y = state->pan_zoom_start_y;
592 SendPointerEventWithData(event, state);
593}
594
595void FlutterWindowsView::SendText(const std::u16string& text) {
596 engine_->text_input_plugin()->TextHook(text);
597}
598
599void FlutterWindowsView::SendKey(int key,
600 int scancode,
601 int action,
602 char32_t character,
603 bool extended,
604 bool was_down,
605 KeyEventCallback callback) {
608 [engine = engine_, view_id = view_id_, key, scancode, action, character,
609 extended, was_down, callback = std::move(callback)](bool handled) {
610 if (!handled) {
611 engine->text_input_plugin()->KeyboardHook(
613 }
614 if (engine->view(view_id)) {
615 callback(handled);
616 }
617 });
618}
619
620void FlutterWindowsView::SendFocus(FlutterViewFocusState focus_state,
621 FlutterViewFocusDirection direction) {
622 FlutterViewFocusEvent event = {};
623 event.struct_size = sizeof(event);
624 event.view_id = view_id_;
625 event.state = focus_state;
626 event.direction = direction;
627 engine_->SendViewFocusEvent(event);
628}
629
630void FlutterWindowsView::SendComposeBegin() {
632}
633
634void FlutterWindowsView::SendComposeCommit() {
636}
637
638void FlutterWindowsView::SendComposeEnd() {
639 engine_->text_input_plugin()->ComposeEndHook();
640}
641
642void FlutterWindowsView::SendComposeChange(const std::u16string& text,
643 int cursor_pos) {
644 engine_->text_input_plugin()->ComposeChangeHook(text, cursor_pos);
645}
646
647void FlutterWindowsView::SendScroll(double x,
648 double y,
649 double delta_x,
650 double delta_y,
651 int scroll_offset_multiplier,
652 FlutterPointerDeviceKind device_kind,
653 int32_t device_id) {
654 auto state = GetOrCreatePointerState(device_kind, device_id);
655
656 FlutterPointerEvent event = {};
657 event.x = x;
658 event.y = y;
660 event.scroll_delta_x = delta_x * scroll_offset_multiplier;
661 event.scroll_delta_y = delta_y * scroll_offset_multiplier;
662 SetEventPhaseFromCursorButtonState(&event, state);
663 SendPointerEventWithData(event, state);
664}
665
666void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
667 double x,
668 double y) {
669 auto state =
670 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
671
672 FlutterPointerEvent event = {};
673 event.x = x;
674 event.y = y;
675 event.signal_kind =
677 SetEventPhaseFromCursorButtonState(&event, state);
678 SendPointerEventWithData(event, state);
679}
680
681void FlutterWindowsView::SendPointerEventWithData(
682 const FlutterPointerEvent& event_data,
683 PointerState* state) {
684 // If sending anything other than an add, and the pointer isn't already added,
685 // synthesize an add to satisfy Flutter's expectations about events.
686 if (!state->flutter_state_is_added &&
687 event_data.phase != FlutterPointerPhase::kAdd) {
688 FlutterPointerEvent event = {};
690 event.x = event_data.x;
691 event.y = event_data.y;
692 event.buttons = 0;
693 SendPointerEventWithData(event, state);
694 }
695
696 // Don't double-add (e.g., if events are delivered out of order, so an add has
697 // already been synthesized).
698 if (state->flutter_state_is_added &&
699 event_data.phase == FlutterPointerPhase::kAdd) {
700 return;
701 }
702
703 FlutterPointerEvent event = event_data;
704 event.device_kind = state->device_kind;
705 event.device = state->pointer_id;
706 event.buttons = state->buttons;
707 event.view_id = view_id_;
708 event.rotation = (double)state->rotation * (M_PI / 180);
709 event.pressure = state->pressure;
710 // Normalized between 0 and 1024 by the windows API
711 event.pressure_min = 0;
712 event.pressure_max = kMaxPenPressure;
713
714 // Set metadata that's always the same regardless of the event.
715 event.struct_size = sizeof(event);
716 event.timestamp =
717 std::chrono::duration_cast<std::chrono::microseconds>(
718 std::chrono::high_resolution_clock::now().time_since_epoch())
719 .count();
720
721 engine_->SendPointerEvent(event);
722
723 if (event_data.phase == FlutterPointerPhase::kAdd) {
724 state->flutter_state_is_added = true;
725 } else if (event_data.phase == FlutterPointerPhase::kRemove) {
726 auto it = pointer_states_.find(state->pointer_id);
727 if (it != pointer_states_.end()) {
728 pointer_states_.erase(it);
729 }
730 }
731}
732
734 std::scoped_lock lock(first_frame_callback_mutex_);
735 first_frame_callback_ = std::move(callback);
736}
737
738void FlutterWindowsView::FireFirstFrameCallbackIfSet() {
739 std::scoped_lock lock(first_frame_callback_mutex_);
740 if (first_frame_callback_) {
741 fml::closure callback = std::move(first_frame_callback_);
742 first_frame_callback_ = nullptr;
743 engine_->task_runner()->PostTask(std::move(callback));
744 }
745}
746
748 // Called on the engine's raster thread.
749 FireFirstFrameCallbackIfSet();
750
751 std::unique_lock<std::mutex> lock(resize_mutex_);
752
753 switch (resize_status_) {
754 case ResizeState::kResizeStarted:
755 // The caller must first call |OnFrameGenerated| or
756 // |OnEmptyFrameGenerated| before calling this method. This
757 // indicates one of the following:
758 //
759 // 1. The caller did not call these methods.
760 // 2. The caller ignored these methods' result.
761 // 3. The platform thread started a resize after the caller called these
762 // methods. We might have presented a frame of the wrong size to the
763 // view.
764 return;
765 case ResizeState::kFrameGenerated: {
766 // A frame was generated for a pending resize.
767 resize_status_ = ResizeState::kDone;
768 // Unblock the platform thread.
769 engine_->task_runner()->PostTask([this] {});
770
771 lock.unlock();
772
773 // Blocking the raster thread until DWM flushes alleviates glitches where
774 // previous size surface is stretched over current size view.
775 windows_proc_table_->DwmFlush();
776 }
777 case ResizeState::kDone:
778 return;
779 }
780}
781
783 return binding_handler_->OnBitmapSurfaceCleared();
784}
785
787 size_t row_bytes,
788 size_t height) {
789 bool result =
790 binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes, height);
791 if (result) {
792 // The software compositor does not call OnFramePresented, so fire the
793 // first frame callback here.
794 FireFirstFrameCallbackIfSet();
795 }
796 return result;
797}
798
800 return view_id_;
801}
802
804 return view_id_ == kImplicitViewId;
805}
806
808 FML_DCHECK(surface_ == nullptr);
809
810 if (engine_->egl_manager()) {
811 PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
812 surface_ = engine_->egl_manager()->CreateWindowSurface(
813 GetWindowHandle(), bounds.width, bounds.height);
814
815 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
816
817 resize_target_width_ = bounds.width;
818 resize_target_height_ = bounds.height;
819 }
820}
821
822bool FlutterWindowsView::ResizeRenderSurface(size_t width, size_t height) {
823 FML_DCHECK(surface_ != nullptr);
824
825 // No-op if the surface is already the desired size.
826 if (width == surface_->width() && height == surface_->height()) {
827 return true;
828 }
829
830 auto const existing_vsync = surface_->vsync_enabled();
831
832 // TODO: Destroying the surface and re-creating it is expensive.
833 // Ideally this would use ANGLE's automatic surface sizing instead.
834 // See: https://github.com/flutter/flutter/issues/79427
835 if (!surface_->Destroy()) {
836 FML_LOG(ERROR) << "View resize failed to destroy surface";
837 return false;
838 }
839
840 std::unique_ptr<egl::WindowSurface> resized_surface =
842 height);
843 if (!resized_surface) {
844 FML_LOG(ERROR) << "View resize failed to create surface";
845 return false;
846 }
847
848 if (!resized_surface->MakeCurrent() ||
849 !resized_surface->SetVSyncEnabled(existing_vsync)) {
850 // Surfaces block until the v-blank by default.
851 // Failing to update the vsync might result in unnecessary blocking.
852 // This regresses performance but not correctness.
853 FML_LOG(ERROR) << "View resize failed to set vsync";
854 }
855
856 surface_ = std::move(resized_surface);
857 return true;
858}
859
861 return surface_.get();
862}
863
867
869 return binding_handler_->GetWindowHandle();
870}
871
873 return engine_;
874}
875
876void FlutterWindowsView::AnnounceAlert(const std::wstring& text) {
877 auto alert_delegate = binding_handler_->GetAlertDelegate();
878 if (!alert_delegate) {
879 return;
880 }
881 alert_delegate->SetText(fml::WideStringToUtf16(text));
882 ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
884}
885
886void FlutterWindowsView::NotifyWinEventWrapper(ui::AXPlatformNodeWin* node,
887 ax::mojom::Event event) {
888 if (node) {
889 node->NotifyAccessibilityEvent(event);
890 }
891}
892
894 return accessibility_bridge_.get();
895}
896
897ui::AXPlatformNodeWin* FlutterWindowsView::AlertNode() const {
898 return binding_handler_->GetAlert();
899}
900
901std::shared_ptr<AccessibilityBridgeWindows>
903 return std::make_shared<AccessibilityBridgeWindows>(this);
904}
905
907 if (semantics_enabled_ != enabled) {
908 semantics_enabled_ = enabled;
909
910 if (!semantics_enabled_ && accessibility_bridge_) {
911 accessibility_bridge_.reset();
912 } else if (semantics_enabled_ && !accessibility_bridge_) {
913 accessibility_bridge_ = CreateAccessibilityBridge();
914 }
915 }
916}
917
919 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
920}
921
923 engine_->OnWindowStateEvent(hwnd, event);
924}
925
927 return binding_handler_->Focus();
928}
929
930bool FlutterWindowsView::NeedsVsync() const {
931 // If the Desktop Window Manager composition is enabled,
932 // the system itself synchronizes with vsync.
933 // See: https://learn.microsoft.com/windows/win32/dwm/composition-ovw
934 return !windows_proc_table_->DwmIsCompositionEnabled();
935}
936
938 return is_sized_to_content_;
939}
940
941void FlutterWindowsView::SetSizedToContent(bool sized_to_content) {
942 is_sized_to_content_ = sized_to_content;
944}
945
946BoxConstraints FlutterWindowsView::GetConstraints() const {
947 if (!is_sized_to_content_) {
948 PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
949 return BoxConstraints(Size(bounds.width, bounds.height),
950 Size(bounds.width, bounds.height));
951 }
952
953 Size smallest = box_constraints_.smallest();
954 Size biggest = box_constraints_.biggest();
955 if (sizing_delegate_) {
956 auto const work_area = sizing_delegate_->GetWorkArea();
957 double const width = std::min(static_cast<double>(work_area.width),
958 box_constraints_.biggest().width());
959 double const height = std::min(static_cast<double>(work_area.height),
960 box_constraints_.biggest().height());
961 biggest = Size(width, height);
962 }
963 return BoxConstraints(smallest, biggest);
964}
965
966} // namespace flutter
Size smallest() const
Definition geometry.h:94
Size biggest() const
Definition geometry.h:93
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)
void SendViewFocusEvent(const FlutterViewFocusEvent &event)
void SendPointerEvent(const FlutterPointerEvent &event)
void SendWindowMetricsEvent(const FlutterWindowMetricsEvent &event)
KeyboardHandlerBase * keyboard_key_handler()
virtual void OnPointerPanZoomStart(int32_t device_id) override
virtual void UpdateSemanticsEnabled(bool enabled)
virtual ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate() override
void OnScrollInertiaCancel(int32_t device_id) override
virtual std::shared_ptr< AccessibilityBridgeWindows > CreateAccessibilityBridge()
void SetSizedToContent(bool sized_to_content)
ui::AXPlatformNodeWin * AlertNode() const
void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons, uint32_t rotation, uint32_t pressure) override
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
void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons, uint32_t rotation, uint32_t pressure, int modifiers_state) 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)
void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons) override
virtual bool PresentSoftwareBitmap(const void *allocation, size_t row_bytes, size_t height)
void OnFocus(FlutterViewFocusState focus_state, FlutterViewFocusDirection direction) override
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
double height() const
Definition geometry.h:45
double width() const
Definition geometry.h:44
void PollOnce(std::chrono::milliseconds timeout)
void PostTask(TaskClosure task)
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
int32_t x
uint64_t FlutterEngineDisplayId
Definition embedder.h:1050
FlutterViewFocusState
Represents the focus state of a given [FlutterView].
Definition embedder.h:1219
FlutterViewFocusDirection
Definition embedder.h:1200
@ kPanZoomUpdate
The pan/zoom updated.
Definition embedder.h:1303
@ kHover
The pointer moved while up.
Definition embedder.h:1299
@ kUp
Definition embedder.h:1275
@ kPanZoomStart
A pan/zoom started on this pointer.
Definition embedder.h:1301
@ kRemove
Definition embedder.h:1297
@ kDown
Definition embedder.h:1282
@ kAdd
Definition embedder.h:1292
@ kMove
Definition embedder.h:1287
@ kPanZoomEnd
The pan/zoom ended.
Definition embedder.h:1305
@ kFlutterPointerSignalKindScrollInertiaCancel
Definition embedder.h:1348
@ kFlutterPointerSignalKindScroll
Definition embedder.h:1347
FlutterPointerDeviceKind
The device type that created a pointer event.
Definition embedder.h:1309
@ kFlutterPointerDeviceKindTrackpad
Definition embedder.h:1313
FlutterEngine engine
Definition main.cc:84
VkSurfaceKHR surface
Definition main.cc:65
G_BEGIN_DECLS FlutterViewId view_id
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_DCHECK(condition)
Definition logging.h:122
std::u16string text
double y
WindowStateEvent
An event representing a change in window state that may update the.
constexpr FlutterViewId kImplicitViewId
int64_t FlutterViewId
internal::CopyableLambda< T > MakeCopyable(T lambda)
std::function< void()> closure
Definition closure.h:14
std::u16string WideStringToUtf16(const std::wstring_view str)
UnimplementedNativeViewAccessible * NativeViewAccessible
Definition ref_ptr.h:261
int32_t height
int32_t width
double y
The y coordinate of the pointer event in physical pixels.
Definition embedder.h:1363
double x
The x coordinate of the pointer event in physical pixels.
Definition embedder.h:1361
FlutterPointerDeviceKind device_kind
Definition embedder.h:1377
FlutterPointerPhase phase
Definition embedder.h:1355
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:1054