5#include "flutter/shell/platform/windows/flutter_windows_view.h"
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"
13#include "flutter/shell/platform/common/accessibility_bridge.h"
14#include "flutter/shell/platform/windows/keyboard_key_channel_handler.h"
15#include "flutter/shell/platform/windows/text_input_plugin.h"
16#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
23constexpr std::chrono::milliseconds kWindowResizeTimeout{100};
31bool 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;
45void UpdateVsync(
const FlutterWindowsEngine&
engine,
48 egl::Manager* egl_manager =
engine.egl_manager();
53 auto update_vsync = [egl_manager,
surface, needs_vsync]() {
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 "
80 engine.PostRasterThreadTask(update_vsync);
87void DestroyWindowSurface(
const FlutterWindowsEngine&
engine,
88 std::unique_ptr<egl::WindowSurface>
surface) {
107 std::unique_ptr<WindowBindingHandler> window_binding,
108 std::shared_ptr<WindowsProcTable> windows_proc_table)
111 windows_proc_table_(
std::move(windows_proc_table)) {
112 if (windows_proc_table_ ==
nullptr) {
113 windows_proc_table_ = std::make_shared<WindowsProcTable>();
117 binding_handler_ = std::move(window_binding);
118 binding_handler_->SetView(
this);
127 DestroyWindowSurface(*engine_, std::move(surface_));
133 std::unique_lock<std::mutex> lock(resize_mutex_);
135 if (surface_ ==
nullptr || !surface_->IsValid()) {
139 if (resize_status_ != ResizeState::kResizeStarted) {
143 if (!ResizeRenderSurface(resize_target_height_, resize_target_width_)) {
149 resize_status_ = ResizeState::kFrameGenerated;
155 std::unique_lock<std::mutex> lock(resize_mutex_);
157 if (surface_ ==
nullptr || !surface_->IsValid()) {
161 if (resize_status_ != ResizeState::kResizeStarted) {
165 if (resize_target_width_ !=
width || resize_target_height_ !=
height) {
169 if (!ResizeRenderSurface(resize_target_width_, resize_target_height_)) {
175 resize_status_ = ResizeState::kFrameGenerated;
180 binding_handler_->UpdateFlutterCursor(cursor_name);
184 binding_handler_->SetFlutterCursor(cursor);
188 if (resize_status_ == ResizeState::kDone) {
196 std::unique_lock<std::mutex> lock(resize_mutex_);
199 SendWindowMetrics(
width,
height, binding_handler_->GetDpiScale());
203 if (!surface_ || !surface_->IsValid()) {
204 SendWindowMetrics(
width,
height, binding_handler_->GetDpiScale());
210 bool surface_will_update =
211 SurfaceWillUpdate(surface_->width(), surface_->height(),
width,
height);
212 if (!surface_will_update) {
213 SendWindowMetrics(
width,
height, binding_handler_->GetDpiScale());
217 resize_status_ = ResizeState::kResizeStarted;
218 resize_target_width_ =
width;
219 resize_target_height_ =
height;
221 SendWindowMetrics(
width,
height, binding_handler_->GetDpiScale());
226 return resize_cv_.wait_for(lock, kWindowResizeTimeout,
227 [&resize_status = resize_status_] {
228 return resize_status == ResizeState::kDone;
240 int modifiers_state) {
242 SendPointerMove(
x,
y, GetOrCreatePointerState(device_kind, device_id));
251 if (flutter_button != 0) {
252 auto state = GetOrCreatePointerState(device_kind, device_id);
253 state->buttons |= flutter_button;
264 if (flutter_button != 0) {
265 auto state = GetOrCreatePointerState(device_kind, device_id);
266 state->buttons &= ~flutter_button;
275 SendPointerLeave(
x,
y, GetOrCreatePointerState(device_kind, device_id));
280 SendPointerPanZoomStart(device_id, point.
x, point.
y);
288 SendPointerPanZoomUpdate(device_id, pan_x, pan_y,
scale, rotation);
292 SendPointerPanZoomEnd(device_id);
323 SendComposeChange(
text, cursor_pos);
330 int scroll_offset_multiplier,
333 SendScroll(
x,
y, delta_x, delta_y, scroll_offset_multiplier, device_kind,
339 SendScrollInertiaCancel(device_id, point.
x, point.
y);
347 if (!accessibility_bridge_) {
351 return accessibility_bridge_->GetChildOfAXFragmentRoot();
355 binding_handler_->OnCursorRectUpdated(
rect);
359 binding_handler_->OnResetImeComposing();
363void FlutterWindowsView::SendWindowMetrics(
size_t width,
365 double pixel_ratio)
const {
370 event.pixel_ratio = pixel_ratio;
371 event.view_id = view_id_;
377 double pixel_ratio = binding_handler_->GetDpiScale();
381 event.width =
bounds.width;
382 event.height =
bounds.height;
383 event.pixel_ratio = pixel_ratio;
384 event.view_id = view_id_;
399FlutterWindowsView::PointerState* FlutterWindowsView::GetOrCreatePointerState(
405 int32_t pointer_id = (
static_cast<int32_t
>(device_kind) << 28) | device_id;
407 auto [it, added] = pointer_states_.try_emplace(pointer_id,
nullptr);
409 auto state = std::make_unique<PointerState>();
410 state->device_kind = device_kind;
411 state->pointer_id = pointer_id;
412 it->second = std::move(
state);
415 return it->second.get();
420void FlutterWindowsView::SetEventPhaseFromCursorButtonState(
422 const PointerState*
state)
const {
425 if (
state->buttons == 0) {
426 event_data->
phase =
state->flutter_state_is_down
430 event_data->
phase =
state->flutter_state_is_down
436void FlutterWindowsView::SendPointerMove(
double x,
438 PointerState*
state) {
443 SetEventPhaseFromCursorButtonState(&
event,
state);
447void FlutterWindowsView::SendPointerDown(
double x,
449 PointerState*
state) {
454 SetEventPhaseFromCursorButtonState(&
event,
state);
457 state->flutter_state_is_down =
true;
460void FlutterWindowsView::SendPointerUp(
double x,
462 PointerState*
state) {
467 SetEventPhaseFromCursorButtonState(&
event,
state);
470 state->flutter_state_is_down =
false;
474void FlutterWindowsView::SendPointerLeave(
double x,
476 PointerState*
state) {
484void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id,
489 state->pan_zoom_start_x =
x;
490 state->pan_zoom_start_y =
y;
498void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id,
506 event.
x =
state->pan_zoom_start_x;
507 event.y =
state->pan_zoom_start_y;
511 event.rotation = rotation;
516void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) {
520 event.
x =
state->pan_zoom_start_x;
521 event.y =
state->pan_zoom_start_y;
526void FlutterWindowsView::SendText(
const std::u16string&
text) {
530void FlutterWindowsView::SendKey(
int key,
548void FlutterWindowsView::SendComposeBegin() {
552void FlutterWindowsView::SendComposeCommit() {
556void FlutterWindowsView::SendComposeEnd() {
560void FlutterWindowsView::SendComposeChange(
const std::u16string&
text,
565void FlutterWindowsView::SendScroll(
double x,
569 int scroll_offset_multiplier,
572 auto state = GetOrCreatePointerState(device_kind, device_id);
578 event.scroll_delta_x = delta_x * scroll_offset_multiplier;
579 event.scroll_delta_y = delta_y * scroll_offset_multiplier;
580 SetEventPhaseFromCursorButtonState(&
event,
state);
584void FlutterWindowsView::SendScrollInertiaCancel(int32_t device_id,
595 SetEventPhaseFromCursorButtonState(&
event,
state);
599void FlutterWindowsView::SendPointerEventWithData(
601 PointerState*
state) {
604 if (!
state->flutter_state_is_added &&
608 event.x = event_data.
x;
609 event.y = event_data.
y;
616 if (
state->flutter_state_is_added &&
623 event.device =
state->pointer_id;
624 event.buttons =
state->buttons;
625 event.view_id = view_id_;
628 event.struct_size =
sizeof(
event);
630 std::chrono::duration_cast<std::chrono::microseconds>(
631 std::chrono::high_resolution_clock::now().time_since_epoch())
637 state->flutter_state_is_added =
true;
639 auto it = pointer_states_.find(
state->pointer_id);
640 if (it != pointer_states_.end()) {
641 pointer_states_.erase(it);
648 std::unique_lock<std::mutex> lock(resize_mutex_);
650 switch (resize_status_) {
651 case ResizeState::kResizeStarted:
662 case ResizeState::kFrameGenerated: {
665 resize_status_ = ResizeState::kDone;
667 resize_cv_.notify_all();
671 windows_proc_table_->DwmFlush();
673 case ResizeState::kDone:
679 return binding_handler_->OnBitmapSurfaceCleared();
685 return binding_handler_->OnBitmapSurfaceUpdated(allocation, row_bytes,
705 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
707 resize_target_width_ =
bounds.width;
708 resize_target_height_ =
bounds.height;
712bool FlutterWindowsView::ResizeRenderSurface(
size_t width,
size_t height) {
716 if (
width == surface_->width() &&
height == surface_->height()) {
720 auto const existing_vsync = surface_->vsync_enabled();
725 if (!surface_->Destroy()) {
726 FML_LOG(
ERROR) <<
"View resize failed to destroy surface";
730 std::unique_ptr<egl::WindowSurface> resized_surface =
733 if (!resized_surface) {
734 FML_LOG(
ERROR) <<
"View resize failed to create surface";
738 if (!resized_surface->MakeCurrent() ||
739 !resized_surface->SetVSyncEnabled(existing_vsync)) {
746 surface_ = std::move(resized_surface);
751 return surface_.get();
759 return binding_handler_->GetWindowHandle();
767 auto alert_delegate = binding_handler_->GetAlertDelegate();
768 if (!alert_delegate) {
772 ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
779 node->NotifyAccessibilityEvent(
event);
784 return accessibility_bridge_.get();
788 return binding_handler_->GetAlert();
791std::shared_ptr<AccessibilityBridgeWindows>
793 return std::make_shared<AccessibilityBridgeWindows>(
this);
797 if (semantics_enabled_ != enabled) {
798 semantics_enabled_ = enabled;
800 if (!semantics_enabled_ && accessibility_bridge_) {
801 accessibility_bridge_.reset();
802 }
else if (semantics_enabled_ && !accessibility_bridge_) {
809 UpdateVsync(*engine_, surface_.get(), NeedsVsync());
816bool FlutterWindowsView::NeedsVsync()
const {
820 return !windows_proc_table_->DwmIsCompositionEnabled();
void UpdateHighContrastMode()
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)
void UpdateSemanticsEnabled(bool enabled)
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()
void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button) override
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
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 UpdateFlutterCursor(const std::string &cursor_name)
void OnWindowStateEvent(HWND hwnd, WindowStateEvent event) override
virtual void OnPointerPanZoomEnd(int32_t device_id) override
FlutterWindowMetricsEvent CreateWindowMetricsEvent() const
FlutterViewId view_id() const
void AnnounceAlert(const std::wstring &text)
void SetFlutterCursor(HCURSOR cursor)
virtual bool PresentSoftwareBitmap(const void *allocation, size_t row_bytes, size_t height)
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 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
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)
@ 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
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
#define FML_LOG(severity)
#define FML_DCHECK(condition)
Optional< SkRect > bounds
sk_sp< SkBlender > blender SkRect rect
WindowStateEvent
An event representing a change in window state that may update the.
constexpr FlutterViewId kImplicitViewId
internal::CopyableLambda< T > MakeCopyable(T lambda)
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).