25static const int kMaxPenPressure = 1024;
29constexpr std::chrono::milliseconds kWindowResizeTimeout{100};
37bool 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;
51void 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()) {
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 "
86 engine.PostRasterThreadTask(update_vsync);
93void DestroyWindowSurface(
const FlutterWindowsEngine&
engine,
94 std::unique_ptr<egl::WindowSurface> surface) {
100 [surface = std::move(surface)] {
surface->Destroy(); }));
113 std::unique_ptr<WindowBindingHandler> window_binding,
114 bool is_sized_to_content,
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_);
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) {
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);
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);
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);
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);
351 SendFocus(focus_state, direction);
368 SendComposeChange(
text, cursor_pos);
375 int scroll_offset_multiplier,
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();
408void FlutterWindowsView::SendWindowMetrics(
size_t width,
410 double pixel_ratio)
const {
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();
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_;
468FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
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();
489void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
491 const PointerState* state)
const {
494 if (state->buttons == 0) {
495 event_data->
phase = state->flutter_state_is_down
499 event_data->
phase = state->flutter_state_is_down
505void FlutterWindowsView::SendPointerMove(
double x,
507 PointerState* state) {
512 SetEventPhaseFromCursorButtonState(&event, state);
513 SendPointerEventWithData(event, state);
516void FlutterWindowsView::SendPointerDown(
double x,
518 PointerState* state) {
523 SetEventPhaseFromCursorButtonState(&event, state);
524 SendPointerEventWithData(event, state);
526 state->flutter_state_is_down =
true;
529void FlutterWindowsView::SendPointerUp(
double x,
531 PointerState* state) {
536 SetEventPhaseFromCursorButtonState(&event, state);
537 SendPointerEventWithData(event, state);
539 state->flutter_state_is_down =
false;
543void FlutterWindowsView::SendPointerLeave(
double x,
545 PointerState* state) {
550 SendPointerEventWithData(event, state);
553void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
558 state->pan_zoom_start_x =
x;
559 state->pan_zoom_start_y =
y;
564 SendPointerEventWithData(event, state);
567void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
575 event.
x = state->pan_zoom_start_x;
576 event.y = state->pan_zoom_start_y;
580 event.rotation = rotation;
582 SendPointerEventWithData(event, state);
585void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
589 event.
x = state->pan_zoom_start_x;
590 event.y = state->pan_zoom_start_y;
592 SendPointerEventWithData(event, state);
595void FlutterWindowsView::SendText(
const std::u16string&
text) {
599void FlutterWindowsView::SendKey(
int key,
611 engine->text_input_plugin()->KeyboardHook(
624 event.view_id = view_id_;
625 event.state = focus_state;
626 event.direction = direction;
630void FlutterWindowsView::SendComposeBegin() {
634void FlutterWindowsView::SendComposeCommit() {
638void FlutterWindowsView::SendComposeEnd() {
642void FlutterWindowsView::SendComposeChange(
const std::u16string&
text,
647void FlutterWindowsView::SendScroll(
double x,
651 int scroll_offset_multiplier,
654 auto state = GetOrCreatePointerState(device_kind, device_id);
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);
666void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
677 SetEventPhaseFromCursorButtonState(&event, state);
678 SendPointerEventWithData(event, state);
681void FlutterWindowsView::SendPointerEventWithData(
683 PointerState* state) {
686 if (!state->flutter_state_is_added &&
690 event.x = event_data.
x;
691 event.y = event_data.
y;
693 SendPointerEventWithData(event, state);
698 if (state->flutter_state_is_added &&
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())
724 state->flutter_state_is_added =
true;
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);
738void FlutterWindowsView::FireFirstFrameCallbackIfSet() {
739 std::scoped_lock lock(first_frame_callback_mutex_);
740 if (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();
815 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
817 resize_target_width_ = bounds.
width;
818 resize_target_height_ = bounds.
height;
822bool FlutterWindowsView::ResizeRenderSurface(
size_t width,
size_t height) {
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) {
882 ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
889 node->NotifyAccessibilityEvent(event);
894 return accessibility_bridge_.get();
898 return binding_handler_->GetAlert();
901std::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();
930bool FlutterWindowsView::NeedsVsync()
const {
934 return !windows_proc_table_->DwmIsCompositionEnabled();
938 return is_sized_to_content_;
942 is_sized_to_content_ = sized_to_content;
947 if (!is_sized_to_content_) {
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),
959 double const height = std::min(
static_cast<double>(work_area.height),
963 return BoxConstraints(smallest, biggest);
void UpdateHighContrastMode()
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)
void SendViewFocusEvent(const FlutterViewFocusEvent &event)
void UpdateSemanticsEnabled(bool enabled)
TaskRunner * task_runner()
egl::Manager * egl_manager() const
TextInputPlugin * text_input_plugin()
void SendPointerEvent(const FlutterPointerEvent &event)
void SendWindowMetricsEvent(const FlutterWindowMetricsEvent &event)
KeyboardHandlerBase * keyboard_key_handler()
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)
uint64_t FlutterEngineDisplayId
FlutterViewFocusState
Represents the focus state of a given [FlutterView].
FlutterViewFocusDirection
@ kPanZoomUpdate
The pan/zoom updated.
@ kHover
The pointer moved while up.
@ kPanZoomStart
A pan/zoom started on this pointer.
@ kPanZoomEnd
The pan/zoom ended.
@ kFlutterPointerSignalKindScrollInertiaCancel
@ kFlutterPointerSignalKindScroll
FlutterPointerDeviceKind
The device type that created a pointer event.
@ kFlutterPointerDeviceKindTrackpad
G_BEGIN_DECLS FlutterViewId view_id
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
#define FML_DCHECK(condition)
WindowStateEvent
An event representing a change in window state that may update the.
constexpr FlutterViewId kImplicitViewId
internal::CopyableLambda< T > MakeCopyable(T lambda)
std::function< void()> closure
std::u16string WideStringToUtf16(const std::wstring_view str)
UnimplementedNativeViewAccessible * NativeViewAccessible
double y
The y coordinate of the pointer event in physical pixels.
double x
The x coordinate of the pointer event in physical pixels.
FlutterPointerDeviceKind device_kind
FlutterPointerPhase phase
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).