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) {
174 int modifiers_state) {
176 rotation, pressure, modifiers_state);
186 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
187 if (flutter_button != 0) {
189 x,
y, device_kind, device_id,
200 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
201 if (flutter_button != 0) {
203 x,
y, device_kind, device_id,
256 GetCursorPos(&point);
259 binding_handler_delegate_->
OnScroll(point.x, point.y, delta_x, delta_y,
278 bool result = ::PatBlt(dc, 0, 0, current_width_, current_height_, BLACKNESS);
288 bmi.bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
289 bmi.bmiHeader.biWidth = row_bytes / 4;
290 bmi.bmiHeader.biHeight = -
height;
291 bmi.bmiHeader.biPlanes = 1;
292 bmi.bmiHeader.biBitCount = 32;
293 bmi.bmiHeader.biCompression = BI_RGB;
294 bmi.bmiHeader.biSizeImage = 0;
295 int ret = ::SetDIBitsToDevice(dc, 0, 0, row_bytes / 4,
height, 0, 0, 0,
296 height, allocation, &bmi, DIB_RGB_COLORS);
302 if (binding_handler_delegate_ ==
nullptr) {
311 GetCursorPos(&point);
313 return {(size_t)point.x, (
size_t)point.y};
320 if (!display_manager_->FindById(display_id)) {
321 FML_LOG(ERROR) <<
"Current monitor not found in display list.";
335 CreateAxFragmentRoot();
340 CreateAxFragmentRoot();
355 if (binding_handler_delegate_) {
356 binding_handler_delegate_->
OnFocus(
363 if (binding_handler_delegate_) {
364 binding_handler_delegate_->
OnFocus(
371 if (hwnd && binding_handler_delegate_) {
376void FlutterWindow::TrackMouseLeaveEvent(HWND hwnd) {
377 if (!tracking_mouse_leave_) {
379 tme.cbSize =
sizeof(tme);
380 tme.hwndTrack = hwnd;
381 tme.dwFlags = TME_LEAVE;
382 TrackMouseEvent(&tme);
383 tracking_mouse_leave_ =
true;
388 current_width_ =
width;
396FlutterWindow* FlutterWindow::GetThisFromHandle(HWND
const window)
noexcept {
397 return reinterpret_cast<FlutterWindow*
>(
398 GetWindowLongPtr(
window, GWLP_USERDATA));
401void FlutterWindow::UpdateScrollOffsetMultiplier() {
402 UINT lines_per_scroll = kLinesPerScrollWindowsDefault;
405 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_scroll, 0);
409 scroll_offset_multiplier_ =
410 static_cast<float>(lines_per_scroll) * 100.0 / 3.0;
419 WNDCLASS window_class = RegisterWindowClass(converted_title);
421 auto* result = CreateWindowEx(
422 0, window_class.lpszClassName, converted_title.c_str(),
423 WS_CHILD | WS_VISIBLE, CW_DEFAULT, CW_DEFAULT,
width,
height,
424 HWND_MESSAGE,
nullptr, window_class.hInstance,
this);
426 if (result ==
nullptr) {
429 size_t size = FormatMessageW(
430 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
431 FORMAT_MESSAGE_IGNORE_INSERTS,
432 NULL,
error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
433 reinterpret_cast<LPWSTR
>(&
message), 0, NULL);
437 SetUserObjectInformationA(GetCurrentProcess(),
438 UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
443 SetTimer(result, kDirectManipulationTimer, 14,
nullptr);
449 return window_handle_;
456 return ::PeekMessage(lpMsg, window_handle_, wMsgFilterMin, wMsgFilterMax,
461 return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
467 return ::SendMessage(window_handle_, Msg, wParam, lParam);
471 size_t length = strlen(source);
473 std::wstring wideTitle(
length, L
'#');
474 mbstowcs_s(&outlen, &wideTitle[0],
length + 1, source,
length);
478WNDCLASS FlutterWindow::RegisterWindowClass(std::wstring& title) {
479 window_class_name_ = title;
481 WNDCLASS window_class{};
482 window_class.hCursor = LoadCursor(
nullptr, IDC_ARROW);
483 window_class.lpszClassName = title.c_str();
484 window_class.style = CS_HREDRAW | CS_VREDRAW;
485 window_class.cbClsExtra = 0;
486 window_class.cbWndExtra = 0;
487 window_class.hInstance = GetModuleHandle(
nullptr);
488 window_class.hIcon =
nullptr;
489 window_class.hbrBackground = 0;
490 window_class.lpszMenuName =
nullptr;
491 window_class.lpfnWndProc = WndProc;
492 RegisterClass(&window_class);
499 LPARAM const lparam)
noexcept {
501 auto cs =
reinterpret_cast<CREATESTRUCT*
>(lparam);
502 SetWindowLongPtr(
window, GWLP_USERDATA,
503 reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
505 auto that =
static_cast<FlutterWindow*
>(cs->lpCreateParams);
506 that->window_handle_ =
window;
507 that->text_input_manager_->SetWindowHandle(
window);
508 }
else if (FlutterWindow* that = GetThisFromHandle(
window)) {
509 return that->HandleMessage(
message, wparam, lparam);
518 LPARAM const lparam)
noexcept {
519 LPARAM result_lparam = lparam;
520 int x_pos = 0, y_pos = 0;
522 UINT button_pressed = 0;
526 case kWmDpiChangedBeforeParent:
528 OnDpiScale(current_dpi_);
531 width = LOWORD(lparam);
534 current_width_ =
width;
545 case WM_POINTERUPDATE:
547 case WM_POINTERLEAVE: {
548 POINT pt = {GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)};
549 ScreenToClient(window_handle_, &pt);
550 auto const x =
static_cast<double>(pt.x);
551 auto const y =
static_cast<double>(pt.y);
552 auto const pointerId = GET_POINTERID_WPARAM(wparam);
553 POINTER_INFO pointerInfo;
554 if (windows_proc_table_->GetPointerInfo(pointerId, &pointerInfo)) {
557 if (pointerInfo.pointerType == PT_PEN) {
558 POINTER_PEN_INFO penInfo;
559 if (windows_proc_table_->GetPointerPenInfo(pointerId, &penInfo)) {
560 pressure = penInfo.pressure;
561 rotation = penInfo.rotation;
564 auto touch_id = touch_id_generator_.GetGeneratedId(pointerId);
566 switch (pointerInfo.pointerType) {
580 FML_LOG(ERROR) <<
"Unrecognized device key "
581 << pointerInfo.pointerType;
584 if (
message == WM_POINTERDOWN) {
585 OnPointerDown(
x,
y, device_kind, touch_id, WM_LBUTTONDOWN, rotation,
587 }
else if (
message == WM_POINTERUPDATE) {
588 OnPointerMove(
x,
y, device_kind, touch_id, rotation, pressure,
590 }
else if (
message == WM_POINTERUP) {
591 OnPointerUp(
x,
y, device_kind, touch_id, WM_LBUTTONUP);
594 }
else if (
message == WM_POINTERLEAVE) {
595 OnPointerLeave(
x,
y, device_kind, touch_id);
596 touch_id_generator_.ReleaseNumber(pointerId);
602 device_kind = GetFlutterPointerDeviceKind();
604 TrackMouseLeaveEvent(window_handle_);
606 x_pos = GET_X_LPARAM(lparam);
607 y_pos = GET_Y_LPARAM(lparam);
608 mouse_x_ =
static_cast<double>(x_pos);
609 mouse_y_ =
static_cast<double>(y_pos);
612 if (wparam & MK_CONTROL) {
615 if (wparam & MK_SHIFT) {
618 OnPointerMove(mouse_x_, mouse_y_, device_kind, kDefaultPointerDeviceId,
623 device_kind = GetFlutterPointerDeviceKind();
625 OnPointerLeave(mouse_x_, mouse_y_, device_kind,
626 kDefaultPointerDeviceId);
632 tracking_mouse_leave_ =
false;
635 UINT hit_test_result = LOWORD(lparam);
636 if (hit_test_result == HTCLIENT) {
645 ::CreateCaret(window_handle_,
nullptr, 1, 1);
655 device_kind = GetFlutterPointerDeviceKind();
660 if (
message == WM_LBUTTONDOWN) {
666 SetCapture(window_handle_);
669 if (
message == WM_XBUTTONDOWN) {
670 button_pressed = GET_XBUTTON_WPARAM(wparam);
672 x_pos = GET_X_LPARAM(lparam);
673 y_pos = GET_Y_LPARAM(lparam);
674 OnPointerDown(
static_cast<double>(x_pos),
static_cast<double>(y_pos),
675 device_kind, kDefaultPointerDeviceId, button_pressed,
682 device_kind = GetFlutterPointerDeviceKind();
692 button_pressed = GET_XBUTTON_WPARAM(wparam);
694 x_pos = GET_X_LPARAM(lparam);
695 y_pos = GET_Y_LPARAM(lparam);
696 OnPointerUp(
static_cast<double>(x_pos),
static_cast<double>(y_pos),
697 device_kind, kDefaultPointerDeviceId, button_pressed);
701 -(
static_cast<short>(HIWORD(wparam)) /
702 static_cast<double>(WHEEL_DELTA)),
706 OnScroll((
static_cast<short>(HIWORD(wparam)) /
707 static_cast<double>(WHEEL_DELTA)),
718 if (wparam == kDirectManipulationTimer) {
719 direct_manipulation_owner_->Update();
723 case DM_POINTERHITTEST: {
724 if (direct_manipulation_owner_) {
725 UINT contact_id = GET_POINTERID_WPARAM(wparam);
726 POINTER_INPUT_TYPE pointer_type;
727 if (windows_proc_table_->GetPointerType(contact_id, &pointer_type) &&
728 pointer_type == PT_TOUCHPAD) {
729 direct_manipulation_owner_->SetContact(contact_id);
734 case WM_INPUTLANGCHANGE:
738 case WM_IME_SETCONTEXT:
739 OnImeSetContext(
message, wparam, lparam);
743 result_lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
745 case WM_IME_STARTCOMPOSITION:
746 OnImeStartComposition(
message, wparam, lparam);
751 case WM_IME_COMPOSITION:
752 OnImeComposition(
message, wparam, lparam);
753 if (lparam & GCS_RESULTSTR || lparam & GCS_COMPSTR) {
762 case WM_IME_ENDCOMPOSITION:
763 OnImeEndComposition(
message, wparam, lparam);
766 OnImeRequest(
message, wparam, lparam);
770 if (wparam == UNICODE_NOCHAR)
775 case WM_THEMECHANGED:
786 if (keyboard_manager_->HandleMessage(
message, wparam, lparam)) {
797 return Win32DefWindowProc(window_handle_,
message, wparam, result_lparam);
807 DWORD obj_id =
static_cast<DWORD>(
static_cast<DWORD_PTR
>(lparam));
809 bool is_uia_request =
static_cast<DWORD>(UiaRootObjectId) == obj_id;
810 bool is_msaa_request =
static_cast<DWORD>(OBJID_CLIENT) == obj_id;
812 if (is_uia_request || is_msaa_request) {
828 CreateAxFragmentRoot();
829 if (is_uia_request) {
830#ifdef FLUTTER_ENGINE_USE_UIA
832 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
834 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
835 IID_PPV_ARGS(&root)))) {
838 reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
841 FML_LOG(ERROR) <<
"Failed to query AX fragment root.";
844 }
else if (is_msaa_request) {
847 Microsoft::WRL::ComPtr<IAccessible> root;
848 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
849 IID_PPV_ARGS(&root));
850 reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
853 return reference_result;
860 text_input_manager_->CreateImeWindow();
867 text_input_manager_->CreateImeWindow();
875 text_input_manager_->UpdateImeWindow();
885 if (lparam & GCS_RESULTSTR) {
888 long pos = text_input_manager_->GetComposingCursorPosition();
889 std::optional<std::u16string>
text = text_input_manager_->GetResultString();
895 if (lparam & GCS_COMPSTR) {
897 long pos = text_input_manager_->GetComposingCursorPosition();
898 std::optional<std::u16string>
text =
899 text_input_manager_->GetComposingString();
909 text_input_manager_->DestroyImeWindow();
922 text_input_manager_->AbortComposing();
926 text_input_manager_->UpdateCaretRect(rect);
934 return current_width_;
938 return current_height_;
942 return scroll_offset_multiplier_;
949 return ::DefWindowProc(hWnd, Msg, wParam, lParam);
952void FlutterWindow::Destroy() {
953 if (window_handle_) {
954 text_input_manager_->SetWindowHandle(
nullptr);
955 DestroyWindow(window_handle_);
956 window_handle_ =
nullptr;
959 UnregisterClass(window_class_name_.c_str(),
nullptr);
962void FlutterWindow::CreateAxFragmentRoot() {
963 if (ax_fragment_root_) {
966 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
969 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
972 alert_node_.reset(
static_cast<ui::AXPlatformNodeWin*
>(alert_node));
973 ax_fragment_root_->SetAlertNode(
alert_node_.get());
virtual void OnCursorRectUpdated(const Rect &rect) override
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint32_t rotation, uint32_t pressure, int modifiers_state)
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 OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button, uint32_t rotation, uint32_t pressure)
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 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
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 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 OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint32_t rotation, uint32_t pressure, int modifiers_state)=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
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, FlutterPointerMouseButtons button, uint32_t rotation, uint32_t pressure)=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
@ kFlutterPointerDeviceKindTrackpad
@ kFlutterPointerDeviceKindStylus
@ kFlutterPointerDeviceKindMouse
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)