9 #include "flutter/common/constants.h"
10 #include "flutter/fml/make_copyable.h"
11 #include "flutter/fml/platform/win/wstring_conversion.h"
12 #include "flutter/fml/synchronization/waitable_event.h"
16 #include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
23 constexpr std::chrono::milliseconds kWindowResizeTimeout{100};
31 bool SurfaceWillUpdate(
size_t cur_width,
34 size_t target_height) {
37 bool non_zero_target_dims = target_height > 0 && target_width > 0;
39 (cur_height != target_height) || (cur_width != target_width);
40 return non_zero_target_dims && not_same_size;
45 void UpdateVsync(
const FlutterWindowsEngine& engine,
46 egl::WindowSurface* surface,
48 egl::Manager* egl_manager = engine.egl_manager();
53 auto update_vsync = [egl_manager, surface, needs_vsync]() {
54 if (!surface || !surface->IsValid()) {
58 if (!surface->MakeCurrent()) {
59 FML_LOG(ERROR) <<
"Unable to make the render surface current to update "
64 if (!surface->SetVSyncEnabled(needs_vsync)) {
65 FML_LOG(ERROR) <<
"Unable to update the render surface's swap interval";
68 if (!egl_manager->render_context()->ClearCurrent()) {
69 FML_LOG(ERROR) <<
"Unable to clear current surface after updating "
79 if (engine.running()) {
80 engine.PostRasterThreadTask(update_vsync);
87 void DestroyWindowSurface(
const FlutterWindowsEngine& engine,
88 std::unique_ptr<egl::WindowSurface> surface) {
92 if (engine.running()) {
93 engine.PostRasterThreadTask(fml::MakeCopyable(
94 [surface = std::move(surface)] { surface->Destroy(); }));
107 std::unique_ptr<WindowBindingHandler> window_binding,
108 bool is_sized_to_content,
109 const BoxConstraints& box_constraints,
111 std::shared_ptr<WindowsProcTable> windows_proc_table)
114 is_sized_to_content_(is_sized_to_content),
115 box_constraints_(box_constraints),
116 sizing_delegate_(sizing_delegate),
117 windows_proc_table_(std::move(windows_proc_table)) {
118 if (windows_proc_table_ ==
nullptr) {
119 windows_proc_table_ = std::make_shared<WindowsProcTable>();
123 binding_handler_ = std::move(window_binding);
124 binding_handler_->SetView(
this);
133 DestroyWindowSurface(*engine_, std::move(surface_));
139 std::unique_lock<std::mutex> lock(resize_mutex_);
141 if (surface_ ==
nullptr || !surface_->IsValid()) {
145 if (resize_status_ != ResizeState::kResizeStarted) {
149 if (!ResizeRenderSurface(resize_target_height_, resize_target_width_)) {
155 resize_status_ = ResizeState::kFrameGenerated;
161 std::unique_lock<std::mutex> lock(resize_mutex_);
163 if (IsSizedToContent()) {
164 if (!ResizeRenderSurface(width, height)) {
172 if (surface_ ==
nullptr || !surface_->IsValid()) {
176 if (resize_status_ != ResizeState::kResizeStarted) {
180 if (resize_target_width_ != width || resize_target_height_ != height) {
184 if (!ResizeRenderSurface(resize_target_width_, resize_target_height_)) {
190 resize_status_ = ResizeState::kFrameGenerated;
195 if (resize_status_ == ResizeState::kDone) {
203 if (IsSizedToContent()) {
209 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
213 if (!surface_ || !surface_->IsValid()) {
214 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
220 bool surface_will_update =
221 SurfaceWillUpdate(surface_->width(), surface_->height(), width, height);
222 if (!surface_will_update) {
223 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
228 std::unique_lock<std::mutex> lock(resize_mutex_);
229 resize_status_ = ResizeState::kResizeStarted;
230 resize_target_width_ = width;
231 resize_target_height_ = height;
234 SendWindowMetrics(width, height, binding_handler_->GetDpiScale());
236 std::chrono::time_point<std::chrono::steady_clock> start_time =
237 std::chrono::steady_clock::now();
240 if (std::chrono::steady_clock::now() > start_time + kWindowResizeTimeout) {
243 std::unique_lock<std::mutex> lock(resize_mutex_);
244 if (resize_status_ == ResizeState::kDone) {
259 FlutterPointerDeviceKind device_kind,
261 int modifiers_state) {
263 SendPointerMove(x, y, GetOrCreatePointerState(device_kind, device_id));
269 FlutterPointerDeviceKind device_kind,
271 FlutterPointerMouseButtons flutter_button) {
272 if (flutter_button != 0) {
273 auto state = GetOrCreatePointerState(device_kind, device_id);
274 state->buttons |= flutter_button;
275 SendPointerDown(x, y, state);
282 FlutterPointerDeviceKind device_kind,
284 FlutterPointerMouseButtons flutter_button) {
285 if (flutter_button != 0) {
286 auto state = GetOrCreatePointerState(device_kind, device_id);
287 state->buttons &= ~flutter_button;
288 SendPointerUp(x, y, state);
294 FlutterPointerDeviceKind device_kind,
296 SendPointerLeave(x, y, GetOrCreatePointerState(device_kind, device_id));
301 SendPointerPanZoomStart(device_id, point.
x, point.
y);
309 SendPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, rotation);
313 SendPointerPanZoomEnd(device_id);
331 FlutterViewFocusDirection direction) {
332 SendFocus(focus_state, direction);
349 SendComposeChange(
text, cursor_pos);
356 int scroll_offset_multiplier,
357 FlutterPointerDeviceKind device_kind,
359 SendScroll(x, y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
365 SendScrollInertiaCancel(device_id, point.
x, point.
y);
373 if (!accessibility_bridge_) {
377 return accessibility_bridge_->GetChildOfAXFragmentRoot();
381 binding_handler_->OnCursorRectUpdated(rect);
385 binding_handler_->OnResetImeComposing();
389 void FlutterWindowsView::SendWindowMetrics(
size_t width,
391 double pixel_ratio)
const {
392 FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
393 FlutterWindowMetricsEvent
event = {};
394 event.struct_size =
sizeof(event);
396 event.height = height;
397 event.has_constraints =
true;
398 auto const constraints = GetConstraints();
399 event.min_width_constraint =
400 static_cast<size_t>(constraints.smallest().width());
401 event.min_height_constraint =
402 static_cast<size_t>(constraints.smallest().height());
403 event.max_width_constraint =
404 static_cast<size_t>(constraints.biggest().width());
405 event.max_height_constraint =
406 static_cast<size_t>(constraints.biggest().height());
407 event.pixel_ratio = pixel_ratio;
408 event.display_id = display_id;
409 event.view_id = view_id_;
415 double pixel_ratio = binding_handler_->GetDpiScale();
416 FlutterEngineDisplayId display_id = binding_handler_->GetDisplayId();
418 FlutterWindowMetricsEvent
event = {};
419 event.struct_size =
sizeof(event);
420 event.width = bounds.
width;
421 event.height = bounds.
height;
422 auto constraints = GetConstraints();
423 event.has_constraints =
true;
424 event.min_width_constraint =
425 static_cast<size_t>(constraints.smallest().width());
426 event.min_height_constraint =
427 static_cast<size_t>(constraints.smallest().height());
428 event.max_width_constraint =
429 static_cast<size_t>(constraints.biggest().width());
430 event.max_height_constraint =
431 static_cast<size_t>(constraints.biggest().height());
432 event.pixel_ratio = pixel_ratio;
433 event.display_id = display_id;
434 event.view_id = view_id_;
449 FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
450 FlutterPointerDeviceKind device_kind,
455 int32_t pointer_id = (
static_cast<int32_t
>(device_kind) << 28) | device_id;
457 auto [it, added] = pointer_states_.try_emplace(pointer_id,
nullptr);
459 auto state = std::make_unique<PointerState>();
460 state->device_kind = device_kind;
461 state->pointer_id = pointer_id;
462 it->second = std::move(state);
465 return it->second.get();
470 void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
471 FlutterPointerEvent* event_data,
472 const PointerState* state)
const {
475 if (state->buttons == 0) {
476 event_data->phase = state->flutter_state_is_down
477 ? FlutterPointerPhase::kUp
478 : FlutterPointerPhase::kHover;
480 event_data->phase = state->flutter_state_is_down
481 ? FlutterPointerPhase::kMove
482 : FlutterPointerPhase::kDown;
486 void FlutterWindowsView::SendPointerMove(
double x,
488 PointerState* state) {
489 FlutterPointerEvent
event = {};
493 SetEventPhaseFromCursorButtonState(&event, state);
494 SendPointerEventWithData(event, state);
497 void FlutterWindowsView::SendPointerDown(
double x,
499 PointerState* state) {
500 FlutterPointerEvent
event = {};
504 SetEventPhaseFromCursorButtonState(&event, state);
505 SendPointerEventWithData(event, state);
507 state->flutter_state_is_down =
true;
510 void FlutterWindowsView::SendPointerUp(
double x,
512 PointerState* state) {
513 FlutterPointerEvent
event = {};
517 SetEventPhaseFromCursorButtonState(&event, state);
518 SendPointerEventWithData(event, state);
519 if (event.phase == FlutterPointerPhase::kUp) {
520 state->flutter_state_is_down =
false;
524 void FlutterWindowsView::SendPointerLeave(
double x,
526 PointerState* state) {
527 FlutterPointerEvent
event = {};
530 event.phase = FlutterPointerPhase::kRemove;
531 SendPointerEventWithData(event, state);
534 void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
538 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
539 state->pan_zoom_start_x = x;
540 state->pan_zoom_start_y = y;
541 FlutterPointerEvent
event = {};
544 event.phase = FlutterPointerPhase::kPanZoomStart;
545 SendPointerEventWithData(event, state);
548 void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
554 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
555 FlutterPointerEvent
event = {};
556 event.x = state->pan_zoom_start_x;
557 event.y = state->pan_zoom_start_y;
561 event.rotation = rotation;
562 event.phase = FlutterPointerPhase::kPanZoomUpdate;
563 SendPointerEventWithData(event, state);
566 void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
568 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
569 FlutterPointerEvent
event = {};
570 event.x = state->pan_zoom_start_x;
571 event.y = state->pan_zoom_start_y;
572 event.phase = FlutterPointerPhase::kPanZoomEnd;
573 SendPointerEventWithData(event, state);
576 void FlutterWindowsView::SendText(
const std::u16string&
text) {
580 void FlutterWindowsView::SendKey(
int key,
592 engine->text_input_plugin()->KeyboardHook(
601 void FlutterWindowsView::SendFocus(FlutterViewFocusState focus_state,
602 FlutterViewFocusDirection direction) {
603 FlutterViewFocusEvent
event = {};
604 event.struct_size =
sizeof(event);
605 event.view_id = view_id_;
606 event.state = focus_state;
607 event.direction = direction;
611 void FlutterWindowsView::SendComposeBegin() {
615 void FlutterWindowsView::SendComposeCommit() {
619 void FlutterWindowsView::SendComposeEnd() {
623 void FlutterWindowsView::SendComposeChange(
const std::u16string&
text,
628 void FlutterWindowsView::SendScroll(
double x,
632 int scroll_offset_multiplier,
633 FlutterPointerDeviceKind device_kind,
635 auto state = GetOrCreatePointerState(device_kind, device_id);
637 FlutterPointerEvent
event = {};
640 event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
641 event.scroll_delta_x = delta_x * scroll_offset_multiplier;
642 event.scroll_delta_y = delta_y * scroll_offset_multiplier;
643 SetEventPhaseFromCursorButtonState(&event, state);
644 SendPointerEventWithData(event, state);
647 void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
651 GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id);
653 FlutterPointerEvent
event = {};
657 FlutterPointerSignalKind::kFlutterPointerSignalKindScrollInertiaCancel;
658 SetEventPhaseFromCursorButtonState(&event, state);
659 SendPointerEventWithData(event, state);
662 void FlutterWindowsView::SendPointerEventWithData(
663 const FlutterPointerEvent& event_data,
664 PointerState* state) {
667 if (!state->flutter_state_is_added &&
668 event_data.phase != FlutterPointerPhase::kAdd) {
669 FlutterPointerEvent
event = {};
670 event.phase = FlutterPointerPhase::kAdd;
671 event.x = event_data.x;
672 event.y = event_data.y;
674 SendPointerEventWithData(event, state);
679 if (state->flutter_state_is_added &&
680 event_data.phase == FlutterPointerPhase::kAdd) {
684 FlutterPointerEvent
event = event_data;
685 event.device_kind = state->device_kind;
686 event.device = state->pointer_id;
687 event.buttons = state->buttons;
688 event.view_id = view_id_;
691 event.struct_size =
sizeof(event);
693 std::chrono::duration_cast<std::chrono::microseconds>(
694 std::chrono::high_resolution_clock::now().time_since_epoch())
699 if (event_data.phase == FlutterPointerPhase::kAdd) {
700 state->flutter_state_is_added =
true;
701 }
else if (event_data.phase == FlutterPointerPhase::kRemove) {
702 auto it = pointer_states_.find(state->pointer_id);
703 if (it != pointer_states_.end()) {
704 pointer_states_.erase(it);
711 std::unique_lock<std::mutex> lock(resize_mutex_);
713 switch (resize_status_) {
714 case ResizeState::kResizeStarted:
725 case ResizeState::kFrameGenerated: {
727 resize_status_ = ResizeState::kDone;
735 windows_proc_table_->DwmFlush();
737 case ResizeState::kDone:
743 return binding_handler_->OnBitmapSurfaceCleared();
749 return binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes,
762 FML_DCHECK(surface_ ==
nullptr);
769 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
771 resize_target_width_ = bounds.
width;
772 resize_target_height_ = bounds.
height;
776 bool FlutterWindowsView::ResizeRenderSurface(
size_t width,
size_t height) {
777 FML_DCHECK(surface_ !=
nullptr);
780 if (width == surface_->width() && height == surface_->height()) {
784 auto const existing_vsync = surface_->vsync_enabled();
789 if (!surface_->Destroy()) {
790 FML_LOG(ERROR) <<
"View resize failed to destroy surface";
794 std::unique_ptr<egl::WindowSurface> resized_surface =
797 if (!resized_surface) {
798 FML_LOG(ERROR) <<
"View resize failed to create surface";
802 if (!resized_surface->MakeCurrent() ||
803 !resized_surface->SetVSyncEnabled(existing_vsync)) {
807 FML_LOG(ERROR) <<
"View resize failed to set vsync";
810 surface_ = std::move(resized_surface);
815 return surface_.get();
823 return binding_handler_->GetWindowHandle();
831 auto alert_delegate = binding_handler_->GetAlertDelegate();
832 if (!alert_delegate) {
835 alert_delegate->SetText(fml::WideStringToUtf16(
text));
836 ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
841 ax::mojom::Event event) {
843 node->NotifyAccessibilityEvent(event);
848 return accessibility_bridge_.get();
852 return binding_handler_->GetAlert();
855 std::shared_ptr<AccessibilityBridgeWindows>
857 return std::make_shared<AccessibilityBridgeWindows>(
this);
861 if (semantics_enabled_ != enabled) {
862 semantics_enabled_ = enabled;
864 if (!semantics_enabled_ && accessibility_bridge_) {
865 accessibility_bridge_.reset();
866 }
else if (semantics_enabled_ && !accessibility_bridge_) {
873 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
881 return binding_handler_->Focus();
884 bool FlutterWindowsView::NeedsVsync()
const {
888 return !windows_proc_table_->DwmIsCompositionEnabled();
891 bool FlutterWindowsView::IsSizedToContent()
const {
892 return is_sized_to_content_;
895 BoxConstraints FlutterWindowsView::GetConstraints()
const {
896 if (!is_sized_to_content_) {
897 PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
898 return BoxConstraints(Size(bounds.width, bounds.height),
899 Size(bounds.width, bounds.height));
902 Size smallest = box_constraints_.smallest();
903 Size biggest = box_constraints_.biggest();
904 if (sizing_delegate_) {
905 auto const work_area = sizing_delegate_->
GetWorkArea();
906 double const width = std::min(
static_cast<double>(work_area.width),
907 box_constraints_.biggest().width());
908 double const height = std::min(
static_cast<double>(work_area.height),
909 box_constraints_.biggest().height());
910 biggest = Size(width, height);
912 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()
void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button) override
virtual void UpdateSemanticsEnabled(bool enabled)
virtual ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate() override
void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state) override
bool IsImplicitView() const
void OnScrollInertiaCancel(int32_t device_id) override
virtual std::shared_ptr< AccessibilityBridgeWindows > CreateAccessibilityBridge()
void OnComposeBegin() override
ui::AXPlatformNodeWin * AlertNode() const
virtual void OnResetImeComposing()
virtual void OnUpdateSemanticsEnabled(bool enabled) override
void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id=0) override
virtual void NotifyWinEventWrapper(ui::AXPlatformNodeWin *node, ax::mojom::Event event)
FlutterWindowsEngine * GetEngine() const
void OnScroll(double x, double y, double delta_x, double delta_y, int scroll_offset_multiplier, FlutterPointerDeviceKind device_kind, int32_t device_id) override
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event) override
virtual void OnPointerPanZoomEnd(int32_t device_id) override
FlutterWindowMetricsEvent CreateWindowMetricsEvent() const
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, bool is_sized_to_content, const BoxConstraints &box_constraints, FlutterWindowsViewSizingDelegate *sizing_delegate=nullptr, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
FlutterViewId view_id() const
virtual void AnnounceAlert(const std::wstring &text)
virtual bool PresentSoftwareBitmap(const void *allocation, size_t row_bytes, size_t height)
void OnFocus(FlutterViewFocusState focus_state, FlutterViewFocusDirection direction) override
virtual HWND GetWindowHandle() const
egl::WindowSurface * surface() const
bool OnWindowSizeChanged(size_t width, size_t height) override
void OnText(const std::u16string &) override
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()
void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button) override
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