9 #include "flutter/common/constants.h"
10 #include "flutter/fml/platform/win/wstring_conversion.h"
14 #include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
21 constexpr std::chrono::milliseconds kWindowResizeTimeout{100};
29 bool SurfaceWillUpdate(
size_t cur_width,
32 size_t target_height) {
35 bool non_zero_target_dims = target_height > 0 && target_width > 0;
37 (cur_height != target_height) || (cur_width != target_width);
38 return non_zero_target_dims && not_same_size;
43 void UpdateVsync(
const FlutterWindowsEngine& engine,
44 egl::WindowSurface* surface,
46 egl::Manager* egl_manager = engine.egl_manager();
51 auto update_vsync = [egl_manager, surface, needs_vsync]() {
52 if (!surface || !surface->IsValid()) {
56 if (!surface->MakeCurrent()) {
57 FML_LOG(ERROR) <<
"Unable to make the render surface current to update "
62 if (!surface->SetVSyncEnabled(needs_vsync)) {
63 FML_LOG(ERROR) <<
"Unable to update the render surface's swap interval";
66 if (!egl_manager->render_context()->ClearCurrent()) {
67 FML_LOG(ERROR) <<
"Unable to clear current surface after updating "
77 if (engine.running()) {
78 engine.PostRasterThreadTask(update_vsync);
89 std::unique_ptr<WindowBindingHandler> window_binding,
90 std::shared_ptr<WindowsProcTable> windows_proc_table)
93 windows_proc_table_(std::move(windows_proc_table)) {
94 if (windows_proc_table_ ==
nullptr) {
95 windows_proc_table_ = std::make_shared<WindowsProcTable>();
99 binding_handler_ = std::move(window_binding);
100 binding_handler_->SetView(
this);
117 std::unique_lock<std::mutex> lock(resize_mutex_);
119 if (surface_ ==
nullptr || !surface_->IsValid()) {
123 if (resize_status_ != ResizeState::kResizeStarted) {
127 if (!ResizeRenderSurface(resize_target_height_, resize_target_width_)) {
133 resize_status_ = ResizeState::kFrameGenerated;
139 std::unique_lock<std::mutex> lock(resize_mutex_);
141 if (surface_ ==
nullptr || !surface_->IsValid()) {
145 if (resize_status_ != ResizeState::kResizeStarted) {
149 if (resize_target_width_ != width || resize_target_height_ != height) {
153 if (!ResizeRenderSurface(resize_target_width_, resize_target_height_)) {
159 resize_status_ = ResizeState::kFrameGenerated;
164 binding_handler_->UpdateFlutterCursor(cursor_name);
168 binding_handler_->SetFlutterCursor(cursor);
172 if (resize_status_ == ResizeState::kDone) {
180 std::unique_lock<std::mutex> lock(resize_mutex_);
183 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
187 if (!surface_ || !surface_->IsValid()) {
188 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
194 bool surface_will_update =
195 SurfaceWillUpdate(surface_->width(), surface_->height(), width, height);
196 if (!surface_will_update) {
197 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
201 resize_status_ = ResizeState::kResizeStarted;
202 resize_target_width_ = width;
203 resize_target_height_ = height;
205 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
210 return resize_cv_.wait_for(lock, kWindowResizeTimeout,
211 [&resize_status = resize_status_] {
212 return resize_status == ResizeState::kDone;
222 FlutterPointerDeviceKind device_kind,
224 int modifiers_state) {
226 SendPointerMove(x, y, GetOrCreatePointerState(device_kind, device_id));
232 FlutterPointerDeviceKind device_kind,
234 FlutterPointerMouseButtons flutter_button) {
235 if (flutter_button != 0) {
236 auto state = GetOrCreatePointerState(device_kind, device_id);
237 state->buttons |= flutter_button;
238 SendPointerDown(x, y, state);
245 FlutterPointerDeviceKind device_kind,
247 FlutterPointerMouseButtons flutter_button) {
248 if (flutter_button != 0) {
249 auto state = GetOrCreatePointerState(device_kind, device_id);
250 state->buttons &= ~flutter_button;
251 SendPointerUp(x, y, state);
257 FlutterPointerDeviceKind device_kind,
259 SendPointerLeave(x, y, GetOrCreatePointerState(device_kind, device_id));
264 SendPointerPanZoomStart(device_id, point.
x, point.
y);
272 SendPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, rotation);
276 SendPointerPanZoomEnd(device_id);
307 SendComposeChange(
text, cursor_pos);
314 int scroll_offset_multiplier,
315 FlutterPointerDeviceKind device_kind,
317 SendScroll(x, y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
323 SendScrollInertiaCancel(device_id, point.
x, point.
y);
331 if (!accessibility_bridge_) {
335 return accessibility_bridge_->GetChildOfAXFragmentRoot();
339 binding_handler_->OnCursorRectUpdated(rect);
343 binding_handler_->OnResetImeComposing();
347 void FlutterWindowsView::SendWindowMetrics(
size_t width,
349 double dpiScale)
const {
350 FlutterWindowMetricsEvent
event = {};
351 event.struct_size =
sizeof(event);
353 event.height = height;
354 event.pixel_ratio = dpiScale;
355 event.view_id = view_id_;
363 binding_handler_->GetDpiScale());
366 FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
367 FlutterPointerDeviceKind device_kind,
372 int32_t pointer_id = (
static_cast<int32_t
>(device_kind) << 28) | device_id;
374 auto [it, added] = pointer_states_.try_emplace(pointer_id,
nullptr);
376 auto state = std::make_unique<PointerState>();
377 state->device_kind = device_kind;
378 state->pointer_id = pointer_id;
379 it->second = std::move(state);
382 return it->second.get();
387 void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
388 FlutterPointerEvent* event_data,
389 const PointerState* state)
const {
392 if (state->buttons == 0) {
393 event_data->phase = state->flutter_state_is_down
394 ? FlutterPointerPhase::kUp
395 : FlutterPointerPhase::kHover;
397 event_data->phase = state->flutter_state_is_down
398 ? FlutterPointerPhase::kMove
399 : FlutterPointerPhase::kDown;
403 void FlutterWindowsView::SendPointerMove(
double x,
405 PointerState* state) {
406 FlutterPointerEvent
event = {};
410 SetEventPhaseFromCursorButtonState(&event, state);
411 SendPointerEventWithData(event, state);
414 void FlutterWindowsView::SendPointerDown(
double x,
416 PointerState* state) {
417 FlutterPointerEvent
event = {};
421 SetEventPhaseFromCursorButtonState(&event, state);
422 SendPointerEventWithData(event, state);
424 state->flutter_state_is_down =
true;
427 void FlutterWindowsView::SendPointerUp(
double x,
429 PointerState* state) {
430 FlutterPointerEvent
event = {};
434 SetEventPhaseFromCursorButtonState(&event, state);
435 SendPointerEventWithData(event, state);
436 if (event.phase == FlutterPointerPhase::kUp) {
437 state->flutter_state_is_down =
false;
441 void FlutterWindowsView::SendPointerLeave(
double x,
443 PointerState* state) {
444 FlutterPointerEvent
event = {};
447 event.phase = FlutterPointerPhase::kRemove;
448 SendPointerEventWithData(event, state);
451 void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
455 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
456 state->pan_zoom_start_x = x;
457 state->pan_zoom_start_y = y;
458 FlutterPointerEvent
event = {};
461 event.phase = FlutterPointerPhase::kPanZoomStart;
462 SendPointerEventWithData(event, state);
465 void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
471 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
472 FlutterPointerEvent
event = {};
473 event.x = state->pan_zoom_start_x;
474 event.y = state->pan_zoom_start_y;
478 event.rotation = rotation;
479 event.phase = FlutterPointerPhase::kPanZoomUpdate;
480 SendPointerEventWithData(event, state);
483 void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
485 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
486 FlutterPointerEvent
event = {};
487 event.x = state->pan_zoom_start_x;
488 event.y = state->pan_zoom_start_y;
489 event.phase = FlutterPointerPhase::kPanZoomEnd;
490 SendPointerEventWithData(event, state);
493 void FlutterWindowsView::SendText(
const std::u16string&
text) {
497 void FlutterWindowsView::SendKey(
int key,
515 void FlutterWindowsView::SendComposeBegin() {
519 void FlutterWindowsView::SendComposeCommit() {
523 void FlutterWindowsView::SendComposeEnd() {
527 void FlutterWindowsView::SendComposeChange(
const std::u16string&
text,
532 void FlutterWindowsView::SendScroll(
double x,
536 int scroll_offset_multiplier,
537 FlutterPointerDeviceKind device_kind,
539 auto state = GetOrCreatePointerState(device_kind, device_id);
541 FlutterPointerEvent
event = {};
544 event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
545 event.scroll_delta_x = delta_x * scroll_offset_multiplier;
546 event.scroll_delta_y = delta_y * scroll_offset_multiplier;
547 SetEventPhaseFromCursorButtonState(&event, state);
548 SendPointerEventWithData(event, state);
551 void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
555 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
557 FlutterPointerEvent
event = {};
561 FlutterPointerSignalKind::kFlutterPointerSignalKindScrollInertiaCancel;
562 SetEventPhaseFromCursorButtonState(&event, state);
563 SendPointerEventWithData(event, state);
566 void FlutterWindowsView::SendPointerEventWithData(
567 const FlutterPointerEvent& event_data,
568 PointerState* state) {
571 if (!state->flutter_state_is_added &&
572 event_data.phase != FlutterPointerPhase::kAdd) {
573 FlutterPointerEvent
event = {};
574 event.phase = FlutterPointerPhase::kAdd;
575 event.x = event_data.x;
576 event.y = event_data.y;
578 SendPointerEventWithData(event, state);
583 if (state->flutter_state_is_added &&
584 event_data.phase == FlutterPointerPhase::kAdd) {
588 FlutterPointerEvent
event = event_data;
589 event.device_kind = state->device_kind;
590 event.device = state->pointer_id;
591 event.buttons = state->buttons;
592 event.view_id = view_id_;
595 event.struct_size =
sizeof(event);
597 std::chrono::duration_cast<std::chrono::microseconds>(
598 std::chrono::high_resolution_clock::now().time_since_epoch())
603 if (event_data.phase == FlutterPointerPhase::kAdd) {
604 state->flutter_state_is_added =
true;
605 }
else if (event_data.phase == FlutterPointerPhase::kRemove) {
606 auto it = pointer_states_.find(state->pointer_id);
607 if (it != pointer_states_.end()) {
608 pointer_states_.erase(it);
615 std::unique_lock<std::mutex> lock(resize_mutex_);
617 switch (resize_status_) {
618 case ResizeState::kResizeStarted:
629 case ResizeState::kFrameGenerated: {
632 resize_status_ = ResizeState::kDone;
634 resize_cv_.notify_all();
638 windows_proc_table_->DwmFlush();
640 case ResizeState::kDone:
646 return binding_handler_->OnBitmapSurfaceCleared();
652 return binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes,
661 FML_DCHECK(surface_ ==
nullptr);
668 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
670 resize_target_width_ = bounds.
width;
671 resize_target_height_ = bounds.
height;
675 bool FlutterWindowsView::ResizeRenderSurface(
size_t width,
size_t height) {
676 FML_DCHECK(surface_ !=
nullptr);
679 if (width == surface_->width() && height == surface_->height()) {
683 auto const existing_vsync = surface_->vsync_enabled();
688 if (!surface_->Destroy()) {
689 FML_LOG(ERROR) <<
"View resize failed to destroy surface";
693 std::unique_ptr<egl::WindowSurface> resized_surface =
696 if (!resized_surface) {
697 FML_LOG(ERROR) <<
"View resize failed to create surface";
701 if (!resized_surface->MakeCurrent() ||
702 !resized_surface->SetVSyncEnabled(existing_vsync)) {
706 FML_LOG(ERROR) <<
"View resize failed to set vsync";
709 surface_ = std::move(resized_surface);
720 return surface_.get();
728 return binding_handler_->GetWindowHandle();
736 auto alert_delegate = binding_handler_->GetAlertDelegate();
737 if (!alert_delegate) {
740 alert_delegate->SetText(fml::WideStringToUtf16(
text));
741 ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
746 ax::mojom::Event event) {
748 node->NotifyAccessibilityEvent(event);
753 return accessibility_bridge_.get();
757 return binding_handler_->GetAlert();
760 std::shared_ptr<AccessibilityBridgeWindows>
762 return std::make_shared<AccessibilityBridgeWindows>(
this);
766 if (semantics_enabled_ != enabled) {
767 semantics_enabled_ = enabled;
769 if (!semantics_enabled_ && accessibility_bridge_) {
770 accessibility_bridge_.reset();
771 }
else if (semantics_enabled_ && !accessibility_bridge_) {
778 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
785 bool FlutterWindowsView::NeedsVsync()
const {
789 return !windows_proc_table_->DwmIsCompositionEnabled();