10 #include "flutter/common/constants.h"
11 #include "flutter/fml/make_copyable.h"
12 #include "flutter/fml/platform/win/wstring_conversion.h"
13 #include "flutter/fml/synchronization/waitable_event.h"
17 #include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
25 static const int kMaxPenPressure = 1024;
29 constexpr std::chrono::milliseconds kWindowResizeTimeout{100};
37 bool SurfaceWillUpdate(
size_t cur_width,
40 size_t target_height) {
43 bool non_zero_target_dims = target_height > 0 && target_width > 0;
45 (cur_height != target_height) || (cur_width != target_width);
46 return non_zero_target_dims && not_same_size;
51 void UpdateVsync(
const FlutterWindowsEngine& engine,
52 egl::WindowSurface* surface,
54 egl::Manager* egl_manager = engine.egl_manager();
59 auto update_vsync = [egl_manager, surface, needs_vsync]() {
60 if (!surface || !surface->IsValid()) {
64 if (!surface->MakeCurrent()) {
65 FML_LOG(ERROR) <<
"Unable to make the render surface current to update "
70 if (!surface->SetVSyncEnabled(needs_vsync)) {
71 FML_LOG(ERROR) <<
"Unable to update the render surface's swap interval";
74 if (!egl_manager->render_context()->ClearCurrent()) {
75 FML_LOG(ERROR) <<
"Unable to clear current surface after updating "
85 if (engine.running()) {
86 engine.PostRasterThreadTask(update_vsync);
93 void DestroyWindowSurface(
const FlutterWindowsEngine& engine,
94 std::unique_ptr<egl::WindowSurface> surface) {
98 if (engine.running()) {
99 engine.PostRasterThreadTask(fml::MakeCopyable(
100 [surface = std::move(surface)] { surface->Destroy(); }));
113 std::unique_ptr<WindowBindingHandler> window_binding,
114 bool is_sized_to_content,
115 const BoxConstraints& box_constraints,
117 std::shared_ptr<WindowsProcTable> windows_proc_table)
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>();
129 binding_handler_ = std::move(window_binding);
130 binding_handler_->SetView(
this);
139 DestroyWindowSurface(*engine_, std::move(surface_));
145 std::unique_lock<std::mutex> lock(resize_mutex_);
147 if (surface_ ==
nullptr || !surface_->IsValid()) {
151 if (resize_status_ != ResizeState::kResizeStarted) {
155 if (!ResizeRenderSurface(resize_target_width_, resize_target_height_)) {
161 resize_status_ = ResizeState::kFrameGenerated;
167 std::unique_lock<std::mutex> lock(resize_mutex_);
170 if (!ResizeRenderSurface(width, height)) {
178 if (surface_ ==
nullptr || !surface_->IsValid()) {
182 if (resize_status_ != ResizeState::kResizeStarted) {
186 if (resize_target_width_ != width || resize_target_height_ != height) {
190 if (!ResizeRenderSurface(resize_target_width_, resize_target_height_)) {
196 resize_status_ = ResizeState::kFrameGenerated;
201 if (resize_status_ == ResizeState::kDone) {
213 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
218 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
222 if (!surface_ || !surface_->IsValid()) {
223 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
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());
237 std::unique_lock<std::mutex> lock(resize_mutex_);
238 resize_status_ = ResizeState::kResizeStarted;
239 resize_target_width_ = width;
240 resize_target_height_ = height;
243 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
245 std::chrono::time_point<std::chrono::steady_clock> start_time =
246 std::chrono::steady_clock::now();
249 if (std::chrono::steady_clock::now() > start_time + kWindowResizeTimeout) {
252 std::unique_lock<std::mutex> lock(resize_mutex_);
253 if (resize_status_ == ResizeState::kDone) {
268 FlutterPointerDeviceKind device_kind,
273 int 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);
284 FlutterPointerDeviceKind device_kind,
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);
300 FlutterPointerDeviceKind device_kind,
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);
313 FlutterPointerDeviceKind device_kind,
315 SendPointerLeave(x, y, GetOrCreatePointerState(device_kind, device_id));
320 SendPointerPanZoomStart(device_id, point.
x, point.
y);
328 SendPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, rotation);
332 SendPointerPanZoomEnd(device_id);
350 FlutterViewFocusDirection direction) {
351 SendFocus(focus_state, direction);
368 SendComposeChange(
text, cursor_pos);
375 int scroll_offset_multiplier,
376 FlutterPointerDeviceKind device_kind,
378 SendScroll(x, y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
384 SendScrollInertiaCancel(device_id, point.
x, point.
y);
392 if (!accessibility_bridge_) {
396 return accessibility_bridge_->GetChildOfAXFragmentRoot();
400 binding_handler_->OnCursorRectUpdated(rect);
404 binding_handler_->OnResetImeComposing();
408 void FlutterWindowsView::SendWindowMetrics(
size_t width,
410 double pixel_ratio)
const {
411 FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
412 FlutterWindowMetricsEvent
event = {};
413 event.struct_size =
sizeof(event);
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_;
434 double pixel_ratio = binding_handler_->GetDpiScale();
435 FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
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_;
468 FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
469 FlutterPointerDeviceKind device_kind,
474 int32_t pointer_id = (
static_cast<int32_t
>(device_kind) << 28) | device_id;
476 auto [it, added] = pointer_states_.try_emplace(pointer_id,
nullptr);
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);
484 return it->second.get();
489 void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
490 FlutterPointerEvent* event_data,
491 const PointerState* state)
const {
494 if (state->buttons == 0) {
495 event_data->phase = state->flutter_state_is_down
496 ? FlutterPointerPhase::kUp
497 : FlutterPointerPhase::kHover;
499 event_data->phase = state->flutter_state_is_down
500 ? FlutterPointerPhase::kMove
501 : FlutterPointerPhase::kDown;
505 void FlutterWindowsView::SendPointerMove(
double x,
507 PointerState* state) {
508 FlutterPointerEvent
event = {};
512 SetEventPhaseFromCursorButtonState(&event, state);
513 SendPointerEventWithData(event, state);
516 void FlutterWindowsView::SendPointerDown(
double x,
518 PointerState* state) {
519 FlutterPointerEvent
event = {};
523 SetEventPhaseFromCursorButtonState(&event, state);
524 SendPointerEventWithData(event, state);
526 state->flutter_state_is_down =
true;
529 void FlutterWindowsView::SendPointerUp(
double x,
531 PointerState* state) {
532 FlutterPointerEvent
event = {};
536 SetEventPhaseFromCursorButtonState(&event, state);
537 SendPointerEventWithData(event, state);
538 if (event.phase == FlutterPointerPhase::kUp) {
539 state->flutter_state_is_down =
false;
543 void FlutterWindowsView::SendPointerLeave(
double x,
545 PointerState* state) {
546 FlutterPointerEvent
event = {};
549 event.phase = FlutterPointerPhase::kRemove;
550 SendPointerEventWithData(event, state);
553 void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
557 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
558 state->pan_zoom_start_x = x;
559 state->pan_zoom_start_y = y;
560 FlutterPointerEvent
event = {};
563 event.phase = FlutterPointerPhase::kPanZoomStart;
564 SendPointerEventWithData(event, state);
567 void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
573 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
574 FlutterPointerEvent
event = {};
575 event.x = state->pan_zoom_start_x;
576 event.y = state->pan_zoom_start_y;
580 event.rotation = rotation;
581 event.phase = FlutterPointerPhase::kPanZoomUpdate;
582 SendPointerEventWithData(event, state);
585 void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
587 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
588 FlutterPointerEvent
event = {};
589 event.x = state->pan_zoom_start_x;
590 event.y = state->pan_zoom_start_y;
591 event.phase = FlutterPointerPhase::kPanZoomEnd;
592 SendPointerEventWithData(event, state);
595 void FlutterWindowsView::SendText(
const std::u16string&
text) {
599 void FlutterWindowsView::SendKey(
int key,
611 engine->text_input_plugin()->KeyboardHook(
620 void 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;
630 void FlutterWindowsView::SendComposeBegin() {
634 void FlutterWindowsView::SendComposeCommit() {
638 void FlutterWindowsView::SendComposeEnd() {
642 void FlutterWindowsView::SendComposeChange(
const std::u16string&
text,
647 void FlutterWindowsView::SendScroll(
double x,
651 int scroll_offset_multiplier,
652 FlutterPointerDeviceKind device_kind,
654 auto state = GetOrCreatePointerState(device_kind, device_id);
656 FlutterPointerEvent
event = {};
659 event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
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);
666 void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
670 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
672 FlutterPointerEvent
event = {};
676 FlutterPointerSignalKind::kFlutterPointerSignalKindScrollInertiaCancel;
677 SetEventPhaseFromCursorButtonState(&event, state);
678 SendPointerEventWithData(event, state);
681 void FlutterWindowsView::SendPointerEventWithData(
682 const FlutterPointerEvent& event_data,
683 PointerState* state) {
686 if (!state->flutter_state_is_added &&
687 event_data.phase != FlutterPointerPhase::kAdd) {
688 FlutterPointerEvent
event = {};
689 event.phase = FlutterPointerPhase::kAdd;
690 event.x = event_data.x;
691 event.y = event_data.y;
693 SendPointerEventWithData(event, state);
698 if (state->flutter_state_is_added &&
699 event_data.phase == FlutterPointerPhase::kAdd) {
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;
711 event.pressure_min = 0;
712 event.pressure_max = kMaxPenPressure;
715 event.struct_size =
sizeof(event);
717 std::chrono::duration_cast<std::chrono::microseconds>(
718 std::chrono::high_resolution_clock::now().time_since_epoch())
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);
734 std::scoped_lock lock(first_frame_callback_mutex_);
735 first_frame_callback_ = std::move(
callback);
738 void 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;
749 FireFirstFrameCallbackIfSet();
751 std::unique_lock<std::mutex> lock(resize_mutex_);
753 switch (resize_status_) {
754 case ResizeState::kResizeStarted:
765 case ResizeState::kFrameGenerated: {
767 resize_status_ = ResizeState::kDone;
775 windows_proc_table_->DwmFlush();
777 case ResizeState::kDone:
783 return binding_handler_->OnBitmapSurfaceCleared();
790 binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes, height);
794 FireFirstFrameCallbackIfSet();
808 FML_DCHECK(surface_ ==
nullptr);
815 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
817 resize_target_width_ = bounds.
width;
818 resize_target_height_ = bounds.
height;
822 bool FlutterWindowsView::ResizeRenderSurface(
size_t width,
size_t height) {
823 FML_DCHECK(surface_ !=
nullptr);
826 if (width == surface_->width() && height == surface_->height()) {
830 auto const existing_vsync = surface_->vsync_enabled();
835 if (!surface_->Destroy()) {
836 FML_LOG(ERROR) <<
"View resize failed to destroy surface";
840 std::unique_ptr<egl::WindowSurface> resized_surface =
843 if (!resized_surface) {
844 FML_LOG(ERROR) <<
"View resize failed to create surface";
848 if (!resized_surface->MakeCurrent() ||
849 !resized_surface->SetVSyncEnabled(existing_vsync)) {
853 FML_LOG(ERROR) <<
"View resize failed to set vsync";
856 surface_ = std::move(resized_surface);
861 return surface_.get();
869 return binding_handler_->GetWindowHandle();
877 auto alert_delegate = binding_handler_->GetAlertDelegate();
878 if (!alert_delegate) {
881 alert_delegate->SetText(fml::WideStringToUtf16(
text));
882 ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
887 ax::mojom::Event event) {
889 node->NotifyAccessibilityEvent(event);
894 return accessibility_bridge_.get();
898 return binding_handler_->GetAlert();
901 std::shared_ptr<AccessibilityBridgeWindows>
903 return std::make_shared<AccessibilityBridgeWindows>(
this);
907 if (semantics_enabled_ != enabled) {
908 semantics_enabled_ = enabled;
910 if (!semantics_enabled_ && accessibility_bridge_) {
911 accessibility_bridge_.reset();
912 }
else if (semantics_enabled_ && !accessibility_bridge_) {
919 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
927 return binding_handler_->Focus();
930 bool FlutterWindowsView::NeedsVsync()
const {
934 return !windows_proc_table_->DwmIsCompositionEnabled();
938 return is_sized_to_content_;
942 is_sized_to_content_ = sized_to_content;
946 BoxConstraints FlutterWindowsView::GetConstraints()
const {
947 if (!is_sized_to_content_) {
949 return BoxConstraints(Size(bounds.
width, bounds.
height),
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);
963 return BoxConstraints(smallest, biggest);
void UpdateHighContrastMode()
TaskRunner * task_runner()
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)
void SendViewFocusEvent(const FlutterViewFocusEvent &event)
void UpdateSemanticsEnabled(bool enabled)
KeyboardHandlerBase * keyboard_key_handler()
TextInputPlugin * text_input_plugin()
void SendPointerEvent(const FlutterPointerEvent &event)
void SendWindowMetricsEvent(const FlutterWindowMetricsEvent &event)
egl::Manager * egl_manager() const
virtual void OnFramePresented()
virtual void OnPointerPanZoomStart(int32_t device_id) override
void CreateRenderSurface()
virtual void UpdateSemanticsEnabled(bool enabled)
virtual ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate() override
bool IsImplicitView() const
void OnScrollInertiaCancel(int32_t device_id) override
virtual std::shared_ptr< AccessibilityBridgeWindows > CreateAccessibilityBridge()
void OnComposeBegin() override
void SetSizedToContent(bool sized_to_content)
ui::AXPlatformNodeWin * AlertNode() const
virtual void OnResetImeComposing()
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)
FlutterViewId view_id() const
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
bool IsSizedToContent() const
virtual HWND GetWindowHandle() const
egl::WindowSurface * surface() const
bool OnWindowSizeChanged(size_t width, size_t height) override
void OnText(const std::u16string &) override
void OnWindowRepaint() 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 OnDwmCompositionChanged()
void OnComposeCommit() override
virtual bool ClearSoftwareBitmap()
virtual gfx::NativeViewAccessible GetNativeViewAccessible() override
virtual ~FlutterWindowsView()
void OnHighContrastChanged() 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
void OnComposeEnd() override
bool OnEmptyFrameGenerated()
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)
void PostTask(TaskClosure task)
virtual void ComposeCommitHook()
virtual void ComposeChangeHook(const std::u16string &text, int cursor_pos)
virtual void TextHook(const std::u16string &text)
virtual void ComposeEndHook()
virtual void ComposeBeginHook()
std::function< void(bool)> KeyEventCallback
virtual std::unique_ptr< WindowSurface > CreateWindowSurface(HWND hwnd, size_t width, size_t height)
FlutterDesktopBinaryReply callback
WindowStateEvent
An event representing a change in window state that may update the.
constexpr FlutterViewId kImplicitViewId