13 #include "flutter/fml/logging.h"
14 #include "flutter/shell/platform/embedder/embedder.h"
27 constexpr
int base_dpi = 96;
29 static const int kMinTouchDeviceId = 0;
30 static const int kMaxTouchDeviceId = 128;
32 static const int kLinesPerScrollWindowsDefault = 3;
34 static constexpr int32_t kDefaultPointerDeviceId = 0;
40 static FlutterPointerDeviceKind GetFlutterPointerDeviceKind() {
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) {
47 return kFlutterPointerDeviceKindTouch;
49 return kFlutterPointerDeviceKindStylus;
51 return kFlutterPointerDeviceKindMouse;
55 static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
59 return kFlutterPointerButtonMousePrimary;
62 return kFlutterPointerButtonMouseSecondary;
65 return kFlutterPointerButtonMouseMiddle;
67 return kFlutterPointerButtonMouseBack;
69 return kFlutterPointerButtonMouseForward;
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)
83 : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId),
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);
111 : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId) {}
118 binding_handler_delegate_ = window;
122 if (restored_ && window) {
125 if (focused_ && 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) {
170 FlutterPointerDeviceKind device_kind,
172 int modifiers_state) {
173 binding_handler_delegate_->
OnPointerMove(x, y, device_kind, device_id,
179 FlutterPointerDeviceKind device_kind,
182 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
183 if (flutter_button != 0) {
185 x, y, device_kind, device_id,
186 static_cast<FlutterPointerMouseButtons
>(flutter_button));
192 FlutterPointerDeviceKind device_kind,
195 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
196 if (flutter_button != 0) {
198 x, y, device_kind, device_id,
199 static_cast<FlutterPointerMouseButtons
>(flutter_button));
205 FlutterPointerDeviceKind device_kind,
207 binding_handler_delegate_->
OnPointerLeave(x, y, device_kind, device_id);
248 FlutterPointerDeviceKind device_kind,
251 GetCursorPos(&point);
254 binding_handler_delegate_->
OnScroll(point.x, point.y, delta_x, delta_y,
262 Point origin(rect.left() * scale, rect.top() * scale);
263 Size size(rect.width() * scale, rect.height() * scale);
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};
312 FlutterEngineDisplayId
const display_id =
313 reinterpret_cast<FlutterEngineDisplayId
>(
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(
352 FlutterViewFocusState::kFocused,
353 FlutterViewFocusDirection::kUndefined);
358 if (binding_handler_delegate_) {
359 binding_handler_delegate_->
OnFocus(
360 FlutterViewFocusState::kUnfocused,
361 FlutterViewFocusDirection::kUndefined);
366 if (hwnd && binding_handler_delegate_) {
371 void 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;
382 void FlutterWindow::HandleResize(UINT width, UINT height) {
383 current_width_ = width;
384 current_height_ = height;
391 FlutterWindow* FlutterWindow::GetThisFromHandle(HWND
const window) noexcept {
392 return reinterpret_cast<FlutterWindow*
>(
393 GetWindowLongPtr(window, GWLP_USERDATA));
396 void 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;
410 unsigned int height) {
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) {
422 auto error = GetLastError();
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);
473 WNDCLASS 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);
491 LRESULT CALLBACK FlutterWindow::WndProc(HWND
const window,
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);
508 return DefWindowProc(window,
message, wparam, lparam);
514 LPARAM
const lparam) noexcept {
515 LPARAM result_lparam = lparam;
516 int xPos = 0, yPos = 0;
517 UINT width = 0, height = 0;
518 UINT button_pressed = 0;
519 FlutterPointerDeviceKind device_kind;
522 case kWmDpiChangedBeforeParent:
524 OnDpiScale(current_dpi_);
527 width = LOWORD(lparam);
528 height = HIWORD(lparam);
530 current_width_ = width;
531 current_height_ = height;
532 HandleResize(width, height);
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) {
557 OnPointerDown(x, y, kFlutterPointerDeviceKindTouch, touch_id,
559 }
else if (touch.dwFlags & TOUCHEVENTF_MOVE) {
560 OnPointerMove(x, y, kFlutterPointerDeviceKindTouch, touch_id, 0);
561 }
else if (touch.dwFlags & TOUCHEVENTF_UP) {
562 OnPointerUp(x, y, kFlutterPointerDeviceKindTouch, touch_id,
564 OnPointerLeave(x, y, kFlutterPointerDeviceKindTouch, touch_id);
565 touch_id_generator_.ReleaseNumber(touch.dwID);
568 CloseTouchInputHandle(touch_input_handle);
573 device_kind = GetFlutterPointerDeviceKind();
574 if (device_kind == kFlutterPointerDeviceKindMouse) {
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();
595 if (device_kind == kFlutterPointerDeviceKindMouse) {
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();
627 if (device_kind != kFlutterPointerDeviceKindMouse) {
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();
653 if (device_kind != kFlutterPointerDeviceKindMouse) {
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)),
673 kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
676 OnScroll((
static_cast<short>(HIWORD(wparam)) /
677 static_cast<double>(WHEEL_DELTA)),
678 0.0, kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
681 LRESULT lresult = OnGetObject(
message, wparam, lparam);
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);
767 LPARAM
const lparam) {
768 LRESULT reference_result =
static_cast<LRESULT
>(0L);
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;
823 LPARAM
const lparam) {
825 text_input_manager_->CreateImeWindow();
831 LPARAM
const lparam) {
832 text_input_manager_->CreateImeWindow();
838 LPARAM
const lparam) {
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();
873 LPARAM
const lparam) {
874 text_input_manager_->DestroyImeWindow();
880 LPARAM
const lparam) {
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);
917 void 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);
927 void FlutterWindow::CreateAxFragmentRoot() {
928 if (ax_fragment_root_) {
931 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
934 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
935 ui::AXPlatformNode* alert_node =
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 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 ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate()=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
const DisplayManagerWin32 * display_manager
FlutterDesktopBinaryReply callback
UINT GetDpiForHWND(HWND hwnd)
WindowStateEvent
An event representing a change in window state that may update the.