| 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
5 #include "flutter/shell/platform/windows/flutter_windows_view.h"
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"
14 #include "flutter/shell/platform/common/accessibility_bridge.h"
15 #include "flutter/shell/platform/windows/keyboard_key_channel_handler.h"
16 #include "flutter/shell/platform/windows/text_input_plugin.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
110 FlutterWindowsView::FlutterWindowsView(
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
133 FlutterWindowsView::~FlutterWindowsView() {
134 // The view owns the child window.
135 // Notify the engine the view's child window will no longer be visible.
136 engine_->OnWindowStateEvent(GetWindowHandle(), WindowStateEvent::kHide);
137
138 if (surface_) {
139 DestroyWindowSurface(*engine_, std::move(surface_));
140 }
141 }
142
143 bool FlutterWindowsView::OnEmptyFrameGenerated() {
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
200 void FlutterWindowsView::ForceRedraw() {
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
259 void FlutterWindowsView::OnWindowRepaint() {
260 ForceRedraw();
261 }
262
263 void FlutterWindowsView::OnPointerMove(double x,
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
277 void FlutterWindowsView::OnPointerDown(
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
294 void FlutterWindowsView::OnPointerUp(
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
307 void FlutterWindowsView::OnPointerLeave(double x,
308 double y,
309 FlutterPointerDeviceKind device_kind,
310 int32_t device_id) {
311 SendPointerLeave(x, y, GetOrCreatePointerState(device_kind, device_id));
312 }
313
314 void FlutterWindowsView::OnPointerPanZoomStart(int32_t device_id) {
315 PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
316 SendPointerPanZoomStart(device_id, point.x, point.y);
317 }
318
319 void FlutterWindowsView::OnPointerPanZoomUpdate(int32_t device_id,
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
327 void FlutterWindowsView::OnPointerPanZoomEnd(int32_t device_id) {
328 SendPointerPanZoomEnd(device_id);
329 }
330
331 void FlutterWindowsView::OnText(const std::u16string& text) {
332 SendText(text);
333 }
334
335 void FlutterWindowsView::OnKey(int key,
336 int scancode,
337 int action,
338 char32_t character,
339 bool extended,
340 bool was_down,
341 KeyEventCallback callback) {
342 SendKey(key, scancode, action, character, extended, was_down, callback);
343 }
344
345 void FlutterWindowsView::OnFocus(FlutterViewFocusState focus_state,
346 FlutterViewFocusDirection direction) {
347 SendFocus(focus_state, direction);
348 }
349
350 void FlutterWindowsView::OnComposeBegin() {
351 SendComposeBegin();
352 }
353
354 void FlutterWindowsView::OnComposeCommit() {
355 SendComposeCommit();
356 }
357
358 void FlutterWindowsView::OnComposeEnd() {
359 SendComposeEnd();
360 }
361
362 void FlutterWindowsView::OnComposeChange(const std::u16string& text,
363 int cursor_pos) {
364 SendComposeChange(text, cursor_pos);
365 }
366
367 void FlutterWindowsView::OnScroll(double x,
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
378 void FlutterWindowsView::OnScrollInertiaCancel(int32_t device_id) {
379 PointerLocation point = binding_handler_->GetPrimaryPointerLocation();
380 SendScrollInertiaCancel(device_id, point.x, point.y);
381 }
382
383 void FlutterWindowsView::OnUpdateSemanticsEnabled(bool enabled) {
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
395 void FlutterWindowsView::OnCursorRectUpdated(const Rect& rect) {
396 binding_handler_->OnCursorRectUpdated(rect);
397 }
398
399 void FlutterWindowsView::OnResetImeComposing() {
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
454 void FlutterWindowsView::SendInitialBounds() {
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
461 engine_->SendWindowMetricsEvent(CreateWindowMetricsEvent());
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) {
602 engine_->keyboard_key_handler()->KeyboardHook(
603 key, scancode, action, character, extended, was_down,
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(
608 key, scancode, action, character, extended, was_down);
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
729 void FlutterWindowsView::SetFirstFrameCallback(fml::closure callback) {
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
743 void FlutterWindowsView::OnFramePresented() {
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
778 bool FlutterWindowsView::ClearSoftwareBitmap() {
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
795 FlutterViewId FlutterWindowsView::view_id() const {
796 return view_id_;
797 }
798
799 bool FlutterWindowsView::IsImplicitView() const {
800 return view_id_ == kImplicitViewId;
801 }
802
803 void FlutterWindowsView::CreateRenderSurface() {
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
856 egl::WindowSurface* FlutterWindowsView::surface() const {
857 return surface_.get();
858 }
859
860 void FlutterWindowsView::OnHighContrastChanged() {
861 engine_->UpdateHighContrastMode();
862 }
863
864 HWND FlutterWindowsView::GetWindowHandle() const {
865 return binding_handler_->GetWindowHandle();
866 }
867
868 FlutterWindowsEngine* FlutterWindowsView::GetEngine() const {
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>
898 FlutterWindowsView::CreateAccessibilityBridge() {
899 return std::make_shared<AccessibilityBridgeWindows>(this);
900 }
901
902 void FlutterWindowsView::UpdateSemanticsEnabled(bool enabled) {
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
914 void FlutterWindowsView::OnDwmCompositionChanged() {
915 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
916 }
917
918 void FlutterWindowsView::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) {
919 engine_->OnWindowStateEvent(hwnd, event);
920 }
921
922 bool FlutterWindowsView::Focus() {
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
accessibility_bridge.h
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:92
flutter::FlutterWindowsEngine::UpdateHighContrastMode
void UpdateHighContrastMode()
Definition: flutter_windows_engine.cc:1025
flutter::FlutterWindowsEngine::task_runner
TaskRunner * task_runner()
Definition: flutter_windows_engine.h:173
flutter::FlutterWindowsEngine::OnWindowStateEvent
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)
Definition: flutter_windows_engine.cc:1069
flutter::FlutterWindowsEngine::SendViewFocusEvent
void SendViewFocusEvent(const FlutterViewFocusEvent &event)
Definition: flutter_windows_engine.cc:744
flutter::FlutterWindowsEngine::UpdateSemanticsEnabled
void UpdateSemanticsEnabled(bool enabled)
Definition: flutter_windows_engine.cc:990
flutter::FlutterWindowsEngine::keyboard_key_handler
KeyboardHandlerBase * keyboard_key_handler()
Definition: flutter_windows_engine.h:203
flutter::FlutterWindowsEngine::ScheduleFrame
void ScheduleFrame()
Definition: flutter_windows_engine.cc:810
flutter::FlutterWindowsEngine::text_input_plugin
TextInputPlugin * text_input_plugin()
Definition: flutter_windows_engine.h:206
flutter::FlutterWindowsEngine::SendPointerEvent
void SendPointerEvent(const FlutterPointerEvent &event)
Definition: flutter_windows_engine.cc:730
flutter::FlutterWindowsEngine::SendWindowMetricsEvent
void SendWindowMetricsEvent(const FlutterWindowMetricsEvent &event)
Definition: flutter_windows_engine.cc:723
flutter::FlutterWindowsEngine::egl_manager
egl::Manager * egl_manager() const
Definition: flutter_windows_engine.h:183
flutter::FlutterWindowsView::OnFramePresented
virtual void OnFramePresented()
Definition: flutter_windows_view.cc:743
flutter::FlutterWindowsView::OnPointerMove
void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint32_t rotation, uint32_t pressure, int modifiers_state) override
Definition: flutter_windows_view.cc:263
flutter::FlutterWindowsView::OnPointerPanZoomStart
virtual void OnPointerPanZoomStart(int32_t device_id) override
Definition: flutter_windows_view.cc:314
flutter::FlutterWindowsView::CreateRenderSurface
void CreateRenderSurface()
Definition: flutter_windows_view.cc:803
flutter::FlutterWindowsView::OnPointerUp
void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button) override
Definition: flutter_windows_view.cc:294
flutter::FlutterWindowsView::Focus
virtual bool Focus()
Definition: flutter_windows_view.cc:922
flutter::FlutterWindowsView::UpdateSemanticsEnabled
virtual void UpdateSemanticsEnabled(bool enabled)
Definition: flutter_windows_view.cc:902
flutter::FlutterWindowsView::GetAxFragmentRootDelegate
virtual ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate() override
Definition: flutter_windows_view.cc:889
flutter::FlutterWindowsView::IsImplicitView
bool IsImplicitView() const
Definition: flutter_windows_view.cc:799
flutter::FlutterWindowsView::OnScrollInertiaCancel
void OnScrollInertiaCancel(int32_t device_id) override
Definition: flutter_windows_view.cc:378
flutter::FlutterWindowsView::CreateAccessibilityBridge
virtual std::shared_ptr< AccessibilityBridgeWindows > CreateAccessibilityBridge()
Definition: flutter_windows_view.cc:898
flutter::FlutterWindowsView::OnComposeBegin
void OnComposeBegin() override
Definition: flutter_windows_view.cc:350
flutter::FlutterWindowsView::AlertNode
ui::AXPlatformNodeWin * AlertNode() const
Definition: flutter_windows_view.cc:893
flutter::FlutterWindowsView::OnResetImeComposing
virtual void OnResetImeComposing()
Definition: flutter_windows_view.cc:399
flutter::FlutterWindowsView::OnUpdateSemanticsEnabled
virtual void OnUpdateSemanticsEnabled(bool enabled) override
Definition: flutter_windows_view.cc:383
flutter::FlutterWindowsView::OnPointerLeave
void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id=0) override
Definition: flutter_windows_view.cc:307
flutter::FlutterWindowsView::NotifyWinEventWrapper
virtual void NotifyWinEventWrapper(ui::AXPlatformNodeWin *node, ax::mojom::Event event)
Definition: flutter_windows_view.cc:882
flutter::FlutterWindowsView::SetFirstFrameCallback
void SetFirstFrameCallback(fml::closure callback)
Definition: flutter_windows_view.cc:729
flutter::FlutterWindowsView::GetEngine
FlutterWindowsEngine * GetEngine() const
Definition: flutter_windows_view.cc:868
flutter::FlutterWindowsView::OnScroll
void OnScroll(double x, double y, double delta_x, double delta_y, int scroll_offset_multiplier, FlutterPointerDeviceKind device_kind, int32_t device_id) override
Definition: flutter_windows_view.cc:367
flutter::FlutterWindowsView::OnWindowStateEvent
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event) override
Definition: flutter_windows_view.cc:918
flutter::FlutterWindowsView::OnPointerPanZoomEnd
virtual void OnPointerPanZoomEnd(int32_t device_id) override
Definition: flutter_windows_view.cc:327
flutter::FlutterWindowsView::CreateWindowMetricsEvent
FlutterWindowMetricsEvent CreateWindowMetricsEvent() const
Definition: flutter_windows_view.cc:428
flutter::FlutterWindowsView::FlutterWindowsView
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)
Definition: flutter_windows_view.cc:110
flutter::FlutterWindowsView::view_id
FlutterViewId view_id() const
Definition: flutter_windows_view.cc:795
flutter::FlutterWindowsView::OnPointerDown
void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button, uint32_t rotation, uint32_t pressure) override
Definition: flutter_windows_view.cc:277
flutter::FlutterWindowsView::AnnounceAlert
virtual void AnnounceAlert(const std::wstring &text)
Definition: flutter_windows_view.cc:872
flutter::FlutterWindowsView::SendInitialBounds
void SendInitialBounds()
Definition: flutter_windows_view.cc:454
flutter::FlutterWindowsView::PresentSoftwareBitmap
virtual bool PresentSoftwareBitmap(const void *allocation, size_t row_bytes, size_t height)
Definition: flutter_windows_view.cc:782
flutter::FlutterWindowsView::OnFocus
void OnFocus(FlutterViewFocusState focus_state, FlutterViewFocusDirection direction) override
Definition: flutter_windows_view.cc:345
flutter::FlutterWindowsView::GetWindowHandle
virtual HWND GetWindowHandle() const
Definition: flutter_windows_view.cc:864
flutter::FlutterWindowsView::surface
egl::WindowSurface * surface() const
Definition: flutter_windows_view.cc:856
flutter::FlutterWindowsView::OnWindowSizeChanged
bool OnWindowSizeChanged(size_t width, size_t height) override
Definition: flutter_windows_view.cc:208
flutter::FlutterWindowsView::OnText
void OnText(const std::u16string &) override
Definition: flutter_windows_view.cc:331
flutter::FlutterWindowsView::OnWindowRepaint
void OnWindowRepaint() override
Definition: flutter_windows_view.cc:259
flutter::FlutterWindowsView::OnPointerPanZoomUpdate
virtual void OnPointerPanZoomUpdate(int32_t device_id, double pan_x, double pan_y, double scale, double rotation) override
Definition: flutter_windows_view.cc:319
flutter::FlutterWindowsView::OnComposeChange
void OnComposeChange(const std::u16string &text, int cursor_pos) override
Definition: flutter_windows_view.cc:362
flutter::FlutterWindowsView::ForceRedraw
void ForceRedraw()
Definition: flutter_windows_view.cc:200
flutter::FlutterWindowsView::OnDwmCompositionChanged
void OnDwmCompositionChanged()
Definition: flutter_windows_view.cc:914
flutter::FlutterWindowsView::OnComposeCommit
void OnComposeCommit() override
Definition: flutter_windows_view.cc:354
flutter::FlutterWindowsView::ClearSoftwareBitmap
virtual bool ClearSoftwareBitmap()
Definition: flutter_windows_view.cc:778
flutter::FlutterWindowsView::GetNativeViewAccessible
virtual gfx::NativeViewAccessible GetNativeViewAccessible() override
Definition: flutter_windows_view.cc:387
flutter::FlutterWindowsView::~FlutterWindowsView
virtual ~FlutterWindowsView()
Definition: flutter_windows_view.cc:133
flutter::FlutterWindowsView::OnHighContrastChanged
void OnHighContrastChanged() override
Definition: flutter_windows_view.cc:860
flutter::FlutterWindowsView::OnCursorRectUpdated
virtual void OnCursorRectUpdated(const Rect &rect)
Definition: flutter_windows_view.cc:395
flutter::FlutterWindowsView::OnKey
void OnKey(int key, int scancode, int action, char32_t character, bool extended, bool was_down, KeyEventCallback callback) override
Definition: flutter_windows_view.cc:335
flutter::FlutterWindowsView::OnComposeEnd
void OnComposeEnd() override
Definition: flutter_windows_view.cc:358
flutter::FlutterWindowsView::OnEmptyFrameGenerated
bool OnEmptyFrameGenerated()
Definition: flutter_windows_view.cc:143
flutter::FlutterWindowsView::OnFrameGenerated
bool OnFrameGenerated(size_t width, size_t height)
Definition: flutter_windows_view.cc:165
flutter::FlutterWindowsViewSizingDelegate
Definition: flutter_windows_view.h:34
flutter::FlutterWindowsViewSizingDelegate::GetWorkArea
virtual WindowRect GetWorkArea() const =0
flutter::FlutterWindowsViewSizingDelegate::DidUpdateViewSize
virtual void DidUpdateViewSize(int32_t width, int32_t height)=0
flutter::KeyboardHandlerBase::SyncModifiersIfNeeded
virtual void SyncModifiersIfNeeded(int modifiers_state)=0
flutter::KeyboardHandlerBase::KeyboardHook
virtual void KeyboardHook(int key, int scancode, int action, char32_t character, bool extended, bool was_down, KeyEventCallback callback)=0
flutter::TaskRunner::PollOnce
void PollOnce(std::chrono::milliseconds timeout)
Definition: task_runner.cc:96
flutter::TaskRunner::PostTask
void PostTask(TaskClosure task)
Definition: task_runner.cc:89
flutter::TextInputPlugin::ComposeCommitHook
virtual void ComposeCommitHook()
Definition: text_input_plugin.cc:141
flutter::TextInputPlugin::ComposeChangeHook
virtual void ComposeChangeHook(const std::u16string &text, int cursor_pos)
Definition: text_input_plugin.cc:193
flutter::TextInputPlugin::TextHook
virtual void TextHook(const std::u16string &text)
Definition: text_input_plugin.cc:68
flutter::TextInputPlugin::ComposeEndHook
virtual void ComposeEndHook()
Definition: text_input_plugin.cc:176
flutter::TextInputPlugin::ComposeBeginHook
virtual void ComposeBeginHook()
Definition: text_input_plugin.cc:126
flutter::WindowBindingHandlerDelegate::KeyEventCallback
std::function< void(bool)> KeyEventCallback
Definition: window_binding_handler_delegate.h:20
flutter::egl::Manager::CreateWindowSurface
virtual std::unique_ptr< WindowSurface > CreateWindowSurface(HWND hwnd, size_t width, size_t height)
Definition: manager.cc:283
flutter::egl::WindowSurface
Definition: window_surface.h:19
flutter_windows_view.h
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:52
keyboard_key_channel_handler.h
extended
bool extended
Definition: keyboard_key_handler_unittests.cc:118
key
int key
Definition: keyboard_key_handler_unittests.cc:114
character
char32_t character
Definition: keyboard_key_handler_unittests.cc:117
action
int action
Definition: keyboard_key_handler_unittests.cc:116
scancode
int scancode
Definition: keyboard_key_handler_unittests.cc:115
was_down
bool was_down
Definition: keyboard_key_handler_unittests.cc:119
text
std::u16string text
Definition: keyboard_unittests.cc:332
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::WindowStateEvent
WindowStateEvent
An event representing a change in window state that may update the.
Definition: windows_lifecycle_manager.h:24
flutter::WindowStateEvent::kHide
@ kHide
flutter::FlutterViewId
int64_t FlutterViewId
Definition: accessibility_plugin.h:15
flutter::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:56
flutter::PhysicalWindowBounds
Definition: window_binding_handler.h:28
flutter::PhysicalWindowBounds::height
size_t height
Definition: window_binding_handler.h:30
flutter::PhysicalWindowBounds::width
size_t width
Definition: window_binding_handler.h:29
flutter::PointerLocation
Definition: window_binding_handler.h:35
flutter::PointerLocation::x
size_t x
Definition: window_binding_handler.h:36
flutter::PointerLocation::y
size_t y
Definition: window_binding_handler.h:37
text_input_plugin.h