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_height_, resize_target_width_)) {
161 resize_status_ = ResizeState::kFrameGenerated;
167 std::unique_lock<std::mutex> lock(resize_mutex_);
169 if (IsSizedToContent()) {
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) {
209 if (IsSizedToContent()) {
215 SendWindowMetrics(
width,
height, binding_handler_->GetDpiScale());
219 if (!surface_ || !surface_->IsValid()) {
220 SendWindowMetrics(
width,
height, binding_handler_->GetDpiScale());
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());
234 std::unique_lock<std::mutex> lock(resize_mutex_);
235 resize_status_ = ResizeState::kResizeStarted;
236 resize_target_width_ =
width;
237 resize_target_height_ =
height;
240 SendWindowMetrics(
width,
height, binding_handler_->GetDpiScale());
242 std::chrono::time_point<std::chrono::steady_clock> start_time =
243 std::chrono::steady_clock::now();
246 if (std::chrono::steady_clock::now() > start_time + kWindowResizeTimeout) {
249 std::unique_lock<std::mutex> lock(resize_mutex_);
250 if (resize_status_ == ResizeState::kDone) {
269 int modifiers_state) {
271 auto state = GetOrCreatePointerState(device_kind, device_id);
272 state->rotation = rotation;
273 state->pressure = pressure;
274 SendPointerMove(
x,
y, state);
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);
300 if (flutter_button != 0) {
301 auto state = GetOrCreatePointerState(device_kind, device_id);
302 state->buttons &= ~flutter_button;
303 SendPointerUp(
x,
y, state);
311 SendPointerLeave(
x,
y, GetOrCreatePointerState(device_kind, device_id));
316 SendPointerPanZoomStart(device_id, point.
x, point.
y);
324 SendPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, rotation);
328 SendPointerPanZoomEnd(device_id);
347 SendFocus(focus_state, direction);
364 SendComposeChange(
text, cursor_pos);
371 int scroll_offset_multiplier,
374 SendScroll(
x,
y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
380 SendScrollInertiaCancel(device_id, point.
x, point.
y);
388 if (!accessibility_bridge_) {
392 return accessibility_bridge_->GetChildOfAXFragmentRoot();
396 binding_handler_->OnCursorRectUpdated(rect);
400 binding_handler_->OnResetImeComposing();
404void FlutterWindowsView::SendWindowMetrics(
size_t width,
406 double pixel_ratio)
const {
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_;
430 double pixel_ratio = binding_handler_->GetDpiScale();
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_;
464FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
470 int32_t pointer_id = (
static_cast<int32_t
>(device_kind) << 28) | device_id;
472 auto [it, added] = pointer_states_.try_emplace(pointer_id,
nullptr);
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);
480 return it->second.get();
485void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
487 const PointerState* state)
const {
490 if (state->buttons == 0) {
491 event_data->
phase = state->flutter_state_is_down
495 event_data->
phase = state->flutter_state_is_down
501void FlutterWindowsView::SendPointerMove(
double x,
503 PointerState* state) {
508 SetEventPhaseFromCursorButtonState(&event, state);
509 SendPointerEventWithData(event, state);
512void FlutterWindowsView::SendPointerDown(
double x,
514 PointerState* state) {
519 SetEventPhaseFromCursorButtonState(&event, state);
520 SendPointerEventWithData(event, state);
522 state->flutter_state_is_down =
true;
525void FlutterWindowsView::SendPointerUp(
double x,
527 PointerState* state) {
532 SetEventPhaseFromCursorButtonState(&event, state);
533 SendPointerEventWithData(event, state);
535 state->flutter_state_is_down =
false;
539void FlutterWindowsView::SendPointerLeave(
double x,
541 PointerState* state) {
546 SendPointerEventWithData(event, state);
549void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
554 state->pan_zoom_start_x =
x;
555 state->pan_zoom_start_y =
y;
560 SendPointerEventWithData(event, state);
563void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
571 event.
x = state->pan_zoom_start_x;
572 event.y = state->pan_zoom_start_y;
576 event.rotation = rotation;
578 SendPointerEventWithData(event, state);
581void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
585 event.
x = state->pan_zoom_start_x;
586 event.y = state->pan_zoom_start_y;
588 SendPointerEventWithData(event, state);
591void FlutterWindowsView::SendText(
const std::u16string&
text) {
595void FlutterWindowsView::SendKey(
int key,
607 engine->text_input_plugin()->KeyboardHook(
620 event.view_id = view_id_;
621 event.state = focus_state;
622 event.direction = direction;
626void FlutterWindowsView::SendComposeBegin() {
630void FlutterWindowsView::SendComposeCommit() {
634void FlutterWindowsView::SendComposeEnd() {
638void FlutterWindowsView::SendComposeChange(
const std::u16string&
text,
643void FlutterWindowsView::SendScroll(
double x,
647 int scroll_offset_multiplier,
650 auto state = GetOrCreatePointerState(device_kind, device_id);
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);
662void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
673 SetEventPhaseFromCursorButtonState(&event, state);
674 SendPointerEventWithData(event, state);
677void FlutterWindowsView::SendPointerEventWithData(
679 PointerState* state) {
682 if (!state->flutter_state_is_added &&
686 event.x = event_data.
x;
687 event.y = event_data.
y;
689 SendPointerEventWithData(event, state);
694 if (state->flutter_state_is_added &&
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;
707 event.pressure_min = 0;
708 event.pressure_max = kMaxPenPressure;
711 event.struct_size =
sizeof(event);
713 std::chrono::duration_cast<std::chrono::microseconds>(
714 std::chrono::high_resolution_clock::now().time_since_epoch())
720 state->flutter_state_is_added =
true;
722 auto it = pointer_states_.find(state->pointer_id);
723 if (it != pointer_states_.end()) {
724 pointer_states_.erase(it);
730 std::scoped_lock lock(first_frame_callback_mutex_);
731 first_frame_callback_ = std::move(
callback);
734void FlutterWindowsView::FireFirstFrameCallbackIfSet() {
735 std::scoped_lock lock(first_frame_callback_mutex_);
736 if (first_frame_callback_) {
738 first_frame_callback_ =
nullptr;
745 FireFirstFrameCallbackIfSet();
747 std::unique_lock<std::mutex> lock(resize_mutex_);
749 switch (resize_status_) {
750 case ResizeState::kResizeStarted:
761 case ResizeState::kFrameGenerated: {
763 resize_status_ = ResizeState::kDone;
771 windows_proc_table_->DwmFlush();
773 case ResizeState::kDone:
779 return binding_handler_->OnBitmapSurfaceCleared();
786 binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes,
height);
790 FireFirstFrameCallbackIfSet();
811 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
813 resize_target_width_ = bounds.
width;
814 resize_target_height_ = bounds.
height;
818bool FlutterWindowsView::ResizeRenderSurface(
size_t width,
size_t height) {
822 if (
width == surface_->width() &&
height == surface_->height()) {
826 auto const existing_vsync = surface_->vsync_enabled();
831 if (!surface_->Destroy()) {
832 FML_LOG(ERROR) <<
"View resize failed to destroy surface";
836 std::unique_ptr<egl::WindowSurface> resized_surface =
839 if (!resized_surface) {
840 FML_LOG(ERROR) <<
"View resize failed to create surface";
844 if (!resized_surface->MakeCurrent() ||
845 !resized_surface->SetVSyncEnabled(existing_vsync)) {
849 FML_LOG(ERROR) <<
"View resize failed to set vsync";
852 surface_ = std::move(resized_surface);
857 return surface_.get();
865 return binding_handler_->GetWindowHandle();
873 auto alert_delegate = binding_handler_->GetAlertDelegate();
874 if (!alert_delegate) {
878 ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
885 node->NotifyAccessibilityEvent(event);
890 return accessibility_bridge_.get();
894 return binding_handler_->GetAlert();
897std::shared_ptr<AccessibilityBridgeWindows>
899 return std::make_shared<AccessibilityBridgeWindows>(
this);
903 if (semantics_enabled_ != enabled) {
904 semantics_enabled_ = enabled;
906 if (!semantics_enabled_ && accessibility_bridge_) {
907 accessibility_bridge_.reset();
908 }
else if (semantics_enabled_ && !accessibility_bridge_) {
915 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
923 return binding_handler_->Focus();
926bool FlutterWindowsView::NeedsVsync()
const {
930 return !windows_proc_table_->DwmIsCompositionEnabled();
933bool FlutterWindowsView::IsSizedToContent()
const {
934 return is_sized_to_content_;
937BoxConstraints 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));
946 if (sizing_delegate_) {
947 auto const work_area = sizing_delegate_->
GetWorkArea();
948 double const width = std::min(
static_cast<double>(work_area.width),
950 double const height = std::min(
static_cast<double>(work_area.height),
954 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()
void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint32_t rotation, uint32_t pressure, int modifiers_state) override
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
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)
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
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
void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button, uint32_t rotation, uint32_t pressure) override
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()
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.
FlutterPointerMouseButtons
@ 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).