27constexpr int base_dpi = 96;
32static const int kLinesPerScrollWindowsDefault = 3;
34static constexpr int32_t kDefaultPointerDeviceId = 0;
41 constexpr LPARAM kTouchOrPenSignature = 0xFF515700;
42 constexpr LPARAM kTouchSignature = kTouchOrPenSignature | 0x80;
43 constexpr LPARAM kSignatureMask = 0xFFFFFF00;
44 LPARAM info = GetMessageExtraInfo();
45 if ((info & kSignatureMask) == kTouchOrPenSignature) {
46 if ((info & kTouchSignature) == kTouchSignature) {
55static uint64_t ConvertWinButtonToFlutterButton(
UINT button) {
71 FML_LOG(WARNING) <<
"Mouse button not recognized: " << button;
81 std::shared_ptr<WindowsProcTable> windows_proc_table,
82 std::unique_ptr<TextInputManager> text_input_manager)
85 windows_proc_table_(
std::move(windows_proc_table)),
86 text_input_manager_(
std::move(text_input_manager)),
87 ax_fragment_root_(nullptr) {
96 UpdateScrollOffsetMultiplier();
98 if (windows_proc_table_ ==
nullptr) {
99 windows_proc_table_ = std::make_unique<WindowsProcTable>();
101 if (text_input_manager_ ==
nullptr) {
102 text_input_manager_ = std::make_unique<TextInputManager>();
104 keyboard_manager_ = std::make_unique<KeyboardManager>(
this);
118 binding_handler_delegate_ =
window;
122 if (restored_ &&
window) {
131 return static_cast<float>(
GetCurrentDPI()) /
static_cast<float>(base_dpi);
140 if (hwnd ==
nullptr) {
144 HWND prevFocus = ::SetFocus(hwnd);
145 if (prevFocus ==
nullptr) {
157 if (binding_handler_delegate_ !=
nullptr) {
163 if (binding_handler_delegate_ !=
nullptr) {
172 int modifiers_state) {
182 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
183 if (flutter_button != 0) {
185 x,
y, device_kind, device_id,
195 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
196 if (flutter_button != 0) {
198 x,
y, device_kind, device_id,
251 GetCursorPos(&point);
254 binding_handler_delegate_->
OnScroll(point.x, point.y, delta_x, delta_y,
273 bool result = ::PatBlt(dc, 0, 0, current_width_, current_height_, BLACKNESS);
283 bmi.bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
284 bmi.bmiHeader.biWidth = row_bytes / 4;
285 bmi.bmiHeader.biHeight = -
height;
286 bmi.bmiHeader.biPlanes = 1;
287 bmi.bmiHeader.biBitCount = 32;
288 bmi.bmiHeader.biCompression = BI_RGB;
289 bmi.bmiHeader.biSizeImage = 0;
290 int ret = ::SetDIBitsToDevice(dc, 0, 0, row_bytes / 4,
height, 0, 0, 0,
291 height, allocation, &bmi, DIB_RGB_COLORS);
297 if (binding_handler_delegate_ ==
nullptr) {
306 GetCursorPos(&point);
308 return {(size_t)point.x, (
size_t)point.y};
315 if (!display_manager_->FindById(display_id)) {
316 FML_LOG(ERROR) <<
"Current monitor not found in display list.";
330 CreateAxFragmentRoot();
335 CreateAxFragmentRoot();
350 if (binding_handler_delegate_) {
351 binding_handler_delegate_->
OnFocus(
358 if (binding_handler_delegate_) {
359 binding_handler_delegate_->
OnFocus(
366 if (hwnd && binding_handler_delegate_) {
371void FlutterWindow::TrackMouseLeaveEvent(HWND hwnd) {
372 if (!tracking_mouse_leave_) {
374 tme.cbSize =
sizeof(tme);
375 tme.hwndTrack = hwnd;
376 tme.dwFlags = TME_LEAVE;
377 TrackMouseEvent(&tme);
378 tracking_mouse_leave_ =
true;
383 current_width_ =
width;
391FlutterWindow* FlutterWindow::GetThisFromHandle(HWND
const window)
noexcept {
392 return reinterpret_cast<FlutterWindow*
>(
393 GetWindowLongPtr(
window, GWLP_USERDATA));
396void FlutterWindow::UpdateScrollOffsetMultiplier() {
397 UINT lines_per_scroll = kLinesPerScrollWindowsDefault;
400 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_scroll, 0);
404 scroll_offset_multiplier_ =
405 static_cast<float>(lines_per_scroll) * 100.0 / 3.0;
414 WNDCLASS window_class = RegisterWindowClass(converted_title);
416 auto* result = CreateWindowEx(
417 0, window_class.lpszClassName, converted_title.c_str(),
418 WS_CHILD | WS_VISIBLE, CW_DEFAULT, CW_DEFAULT,
width,
height,
419 HWND_MESSAGE,
nullptr, window_class.hInstance,
this);
421 if (result ==
nullptr) {
424 size_t size = FormatMessageW(
425 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
426 FORMAT_MESSAGE_IGNORE_INSERTS,
427 NULL,
error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
428 reinterpret_cast<LPWSTR
>(&
message), 0, NULL);
432 SetUserObjectInformationA(GetCurrentProcess(),
433 UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
438 SetTimer(result, kDirectManipulationTimer, 14,
nullptr);
444 return window_handle_;
451 return ::PeekMessage(lpMsg, window_handle_, wMsgFilterMin, wMsgFilterMax,
456 return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
462 return ::SendMessage(window_handle_, Msg, wParam, lParam);
466 size_t length = strlen(source);
468 std::wstring wideTitle(
length, L
'#');
469 mbstowcs_s(&outlen, &wideTitle[0],
length + 1, source,
length);
473WNDCLASS FlutterWindow::RegisterWindowClass(std::wstring& title) {
474 window_class_name_ = title;
476 WNDCLASS window_class{};
477 window_class.hCursor = LoadCursor(
nullptr, IDC_ARROW);
478 window_class.lpszClassName = title.c_str();
479 window_class.style = CS_HREDRAW | CS_VREDRAW;
480 window_class.cbClsExtra = 0;
481 window_class.cbWndExtra = 0;
482 window_class.hInstance = GetModuleHandle(
nullptr);
483 window_class.hIcon =
nullptr;
484 window_class.hbrBackground = 0;
485 window_class.lpszMenuName =
nullptr;
486 window_class.lpfnWndProc = WndProc;
487 RegisterClass(&window_class);
494 LPARAM const lparam)
noexcept {
496 auto cs =
reinterpret_cast<CREATESTRUCT*
>(lparam);
497 SetWindowLongPtr(
window, GWLP_USERDATA,
498 reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
500 auto that =
static_cast<FlutterWindow*
>(cs->lpCreateParams);
501 that->window_handle_ =
window;
502 that->text_input_manager_->SetWindowHandle(
window);
503 RegisterTouchWindow(
window, 0);
504 }
else if (FlutterWindow* that = GetThisFromHandle(
window)) {
505 return that->HandleMessage(
message, wparam, lparam);
514 LPARAM const lparam)
noexcept {
515 LPARAM result_lparam = lparam;
516 int xPos = 0, yPos = 0;
518 UINT button_pressed = 0;
522 case kWmDpiChangedBeforeParent:
524 OnDpiScale(current_dpi_);
527 width = LOWORD(lparam);
530 current_width_ =
width;
541 UINT num_points = LOWORD(wparam);
542 touch_points_.resize(num_points);
543 auto touch_input_handle =
reinterpret_cast<HTOUCHINPUT
>(lparam);
544 if (GetTouchInputInfo(touch_input_handle, num_points,
545 touch_points_.data(),
sizeof(TOUCHINPUT))) {
546 for (
const auto& touch : touch_points_) {
548 auto touch_id = touch_id_generator_.GetGeneratedId(touch.dwID);
550 POINT pt = {TOUCH_COORD_TO_PIXEL(touch.x),
551 TOUCH_COORD_TO_PIXEL(touch.y)};
552 ScreenToClient(window_handle_, &pt);
553 auto x =
static_cast<double>(pt.x);
554 auto y =
static_cast<double>(pt.y);
556 if (touch.dwFlags & TOUCHEVENTF_DOWN) {
559 }
else if (touch.dwFlags & TOUCHEVENTF_MOVE) {
561 }
else if (touch.dwFlags & TOUCHEVENTF_UP) {
565 touch_id_generator_.ReleaseNumber(touch.dwID);
568 CloseTouchInputHandle(touch_input_handle);
573 device_kind = GetFlutterPointerDeviceKind();
575 TrackMouseLeaveEvent(window_handle_);
577 xPos = GET_X_LPARAM(lparam);
578 yPos = GET_Y_LPARAM(lparam);
579 mouse_x_ =
static_cast<double>(xPos);
580 mouse_y_ =
static_cast<double>(yPos);
583 if (wparam & MK_CONTROL) {
586 if (wparam & MK_SHIFT) {
589 OnPointerMove(mouse_x_, mouse_y_, device_kind, kDefaultPointerDeviceId,
594 device_kind = GetFlutterPointerDeviceKind();
596 OnPointerLeave(mouse_x_, mouse_y_, device_kind,
597 kDefaultPointerDeviceId);
603 tracking_mouse_leave_ =
false;
606 UINT hit_test_result = LOWORD(lparam);
607 if (hit_test_result == HTCLIENT) {
616 ::CreateCaret(window_handle_,
nullptr, 1, 1);
626 device_kind = GetFlutterPointerDeviceKind();
631 if (
message == WM_LBUTTONDOWN) {
637 SetCapture(window_handle_);
640 if (
message == WM_XBUTTONDOWN) {
641 button_pressed = GET_XBUTTON_WPARAM(wparam);
643 xPos = GET_X_LPARAM(lparam);
644 yPos = GET_Y_LPARAM(lparam);
645 OnPointerDown(
static_cast<double>(xPos),
static_cast<double>(yPos),
646 device_kind, kDefaultPointerDeviceId, button_pressed);
652 device_kind = GetFlutterPointerDeviceKind();
662 button_pressed = GET_XBUTTON_WPARAM(wparam);
664 xPos = GET_X_LPARAM(lparam);
665 yPos = GET_Y_LPARAM(lparam);
666 OnPointerUp(
static_cast<double>(xPos),
static_cast<double>(yPos),
667 device_kind, kDefaultPointerDeviceId, button_pressed);
671 -(
static_cast<short>(HIWORD(wparam)) /
672 static_cast<double>(WHEEL_DELTA)),
676 OnScroll((
static_cast<short>(HIWORD(wparam)) /
677 static_cast<double>(WHEEL_DELTA)),
688 if (wparam == kDirectManipulationTimer) {
689 direct_manipulation_owner_->Update();
693 case DM_POINTERHITTEST: {
694 if (direct_manipulation_owner_) {
695 UINT contact_id = GET_POINTERID_WPARAM(wparam);
696 POINTER_INPUT_TYPE pointer_type;
697 if (windows_proc_table_->GetPointerType(contact_id, &pointer_type) &&
698 pointer_type == PT_TOUCHPAD) {
699 direct_manipulation_owner_->SetContact(contact_id);
704 case WM_INPUTLANGCHANGE:
708 case WM_IME_SETCONTEXT:
709 OnImeSetContext(
message, wparam, lparam);
713 result_lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
715 case WM_IME_STARTCOMPOSITION:
716 OnImeStartComposition(
message, wparam, lparam);
721 case WM_IME_COMPOSITION:
722 OnImeComposition(
message, wparam, lparam);
723 if (lparam & GCS_RESULTSTR || lparam & GCS_COMPSTR) {
732 case WM_IME_ENDCOMPOSITION:
733 OnImeEndComposition(
message, wparam, lparam);
736 OnImeRequest(
message, wparam, lparam);
740 if (wparam == UNICODE_NOCHAR)
745 case WM_THEMECHANGED:
756 if (keyboard_manager_->HandleMessage(
message, wparam, lparam)) {
762 return Win32DefWindowProc(window_handle_,
message, wparam, result_lparam);
772 DWORD obj_id =
static_cast<DWORD>(
static_cast<DWORD_PTR
>(lparam));
774 bool is_uia_request =
static_cast<DWORD>(UiaRootObjectId) == obj_id;
775 bool is_msaa_request =
static_cast<DWORD>(OBJID_CLIENT) == obj_id;
777 if (is_uia_request || is_msaa_request) {
793 CreateAxFragmentRoot();
794 if (is_uia_request) {
795#ifdef FLUTTER_ENGINE_USE_UIA
797 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
799 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
800 IID_PPV_ARGS(&root)))) {
803 reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
806 FML_LOG(ERROR) <<
"Failed to query AX fragment root.";
809 }
else if (is_msaa_request) {
812 Microsoft::WRL::ComPtr<IAccessible> root;
813 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
814 IID_PPV_ARGS(&root));
815 reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
818 return reference_result;
825 text_input_manager_->CreateImeWindow();
832 text_input_manager_->CreateImeWindow();
840 text_input_manager_->UpdateImeWindow();
850 if (lparam & GCS_RESULTSTR) {
853 long pos = text_input_manager_->GetComposingCursorPosition();
854 std::optional<std::u16string>
text = text_input_manager_->GetResultString();
860 if (lparam & GCS_COMPSTR) {
862 long pos = text_input_manager_->GetComposingCursorPosition();
863 std::optional<std::u16string>
text =
864 text_input_manager_->GetComposingString();
874 text_input_manager_->DestroyImeWindow();
887 text_input_manager_->AbortComposing();
891 text_input_manager_->UpdateCaretRect(rect);
899 return current_width_;
903 return current_height_;
907 return scroll_offset_multiplier_;
914 return ::DefWindowProc(hWnd, Msg, wParam, lParam);
917void FlutterWindow::Destroy() {
918 if (window_handle_) {
919 text_input_manager_->SetWindowHandle(
nullptr);
920 DestroyWindow(window_handle_);
921 window_handle_ =
nullptr;
924 UnregisterClass(window_class_name_.c_str(),
nullptr);
927void FlutterWindow::CreateAxFragmentRoot() {
928 if (ax_fragment_root_) {
931 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
934 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
937 alert_node_.reset(
static_cast<ui::AXPlatformNodeWin*
>(alert_node));
938 ax_fragment_root_->SetAlertNode(
alert_node_.get());
virtual void OnCursorRectUpdated(const Rect &rect) override
virtual void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id)
virtual float GetScrollOffsetMultiplier()
virtual bool Focus() override
virtual ui::AXPlatformNodeWin * GetAlert() override
virtual void OnText(const std::u16string &text) override
virtual UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam) override
virtual BOOL Win32PeekMessage(LPMSG lpMsg, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg) override
virtual void OnThemeChange()
std::unique_ptr< AlertPlatformNodeDelegate > alert_delegate_
virtual bool OnBitmapSurfaceUpdated(const void *allocation, size_t row_bytes, size_t height) override
virtual void OnImeRequest(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual LRESULT Win32DefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
virtual void OnImeStartComposition(UINT const message, WPARAM const wparam, LPARAM const lparam)
std::wstring NarrowToWide(const char *source)
virtual FlutterEngineDisplayId GetDisplayId() override
virtual ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate()
virtual void OnScroll(double delta_x, double delta_y, FlutterPointerDeviceKind device_kind, int32_t device_id)
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
void InitializeChild(const char *title, unsigned int width, unsigned int height)
virtual AlertPlatformNodeDelegate * GetAlertDelegate() override
std::unique_ptr< DirectManipulationOwner > direct_manipulation_owner_
virtual HWND GetWindowHandle() override
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
virtual void OnComposeCommit()
virtual void UpdateCursorRect(const Rect &rect)
virtual PhysicalWindowBounds GetPhysicalWindowBounds() override
virtual void OnImeSetContext(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual void OnWindowStateEvent(WindowStateEvent event)
virtual void OnKey(int key, int scancode, int action, char32_t character, bool extended, bool was_down, KeyEventCallback callback) override
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state)
std::unique_ptr< ui::AXPlatformNodeWin > alert_node_
virtual PointerLocation GetPrimaryPointerLocation() override
virtual void OnComposeEnd()
virtual void SetView(WindowBindingHandlerDelegate *view) override
LRESULT HandleMessage(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
virtual void OnComposeChange(const std::u16string &text, int cursor_pos)
virtual void OnResetImeComposing() override
virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) override
virtual void OnImeComposition(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual void OnDpiScale(unsigned int dpi)
virtual void AbortImeComposing()
virtual float GetDpiScale() override
virtual void OnUpdateSemanticsEnabled(bool enabled)
virtual LRESULT OnGetObject(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual gfx::NativeViewAccessible GetNativeViewAccessible()
virtual void OnComposeBegin()
virtual bool OnBitmapSurfaceCleared() override
virtual void OnResize(unsigned int width, unsigned int height)
virtual void OnImeEndComposition(UINT const message, WPARAM const wparam, LPARAM const lparam)
std::function< void(bool)> KeyEventCallback
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button)=0
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state)=0
virtual void OnComposeChange(const std::u16string &text, int cursor_pos)=0
virtual void OnHighContrastChanged()=0
virtual void OnText(const std::u16string &)=0
virtual void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id)=0
virtual void OnKey(int key, int scancode, int action, char32_t character, bool extended, bool was_down, KeyEventCallback callback)=0
virtual void OnUpdateSemanticsEnabled(bool enabled)=0
virtual ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate()=0
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button)=0
virtual void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)=0
virtual void OnComposeEnd()=0
virtual void OnWindowRepaint()=0
virtual gfx::NativeViewAccessible GetNativeViewAccessible()=0
virtual bool OnWindowSizeChanged(size_t width, size_t height)=0
virtual void OnComposeBegin()=0
virtual void OnFocus(FlutterViewFocusState focus_state, FlutterViewFocusDirection direction)=0
virtual void OnScroll(double x, double y, double delta_x, double delta_y, int scroll_offset_multiplier, FlutterPointerDeviceKind device_kind, int32_t device_id)=0
virtual void OnComposeCommit()=0
uint64_t FlutterEngineDisplayId
@ kUnfocused
Specifies that a view does not have platform focus.
@ kFocused
Specifies that a view has platform focus.
FlutterPointerMouseButtons
@ kFlutterPointerButtonMousePrimary
@ kFlutterPointerButtonMouseMiddle
@ kFlutterPointerButtonMouseForward
@ kFlutterPointerButtonMouseBack
@ kFlutterPointerButtonMouseSecondary
FlutterPointerDeviceKind
The device type that created a pointer event.
@ kFlutterPointerDeviceKindTouch
@ kFlutterPointerDeviceKindStylus
@ kFlutterPointerDeviceKindMouse
G_BEGIN_DECLS GBytes * message
const uint8_t uint32_t uint32_t GError ** error
static const int kMaxTouchDeviceId
static const int kMinTouchDeviceId
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
UINT GetDpiForHWND(HWND hwnd)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
WindowStateEvent
An event representing a change in window state that may update the.
UnimplementedNativeViewAccessible * NativeViewAccessible
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)