13 #include "flutter/fml/logging.h"
14 #include "flutter/shell/platform/embedder/embedder.h"
26 constexpr
int base_dpi = 96;
28 static const int kMinTouchDeviceId = 0;
29 static const int kMaxTouchDeviceId = 128;
31 static const int kLinesPerScrollWindowsDefault = 3;
39 static HCURSOR GetCursorByName(
const std::string& cursor_name) {
40 static auto* cursors =
new std::map<std::string, const wchar_t*>{
41 {
"allScroll", IDC_SIZEALL},
44 {
"forbidden", IDC_NO},
46 {
"move", IDC_SIZEALL},
49 {
"precise", IDC_CROSS},
50 {
"progress", IDC_APPSTARTING},
52 {
"resizeColumn", IDC_SIZEWE},
53 {
"resizeDown", IDC_SIZENS},
54 {
"resizeDownLeft", IDC_SIZENESW},
55 {
"resizeDownRight", IDC_SIZENWSE},
56 {
"resizeLeft", IDC_SIZEWE},
57 {
"resizeLeftRight", IDC_SIZEWE},
58 {
"resizeRight", IDC_SIZEWE},
59 {
"resizeRow", IDC_SIZENS},
60 {
"resizeUp", IDC_SIZENS},
61 {
"resizeUpDown", IDC_SIZENS},
62 {
"resizeUpLeft", IDC_SIZENWSE},
63 {
"resizeUpRight", IDC_SIZENESW},
64 {
"resizeUpLeftDownRight", IDC_SIZENWSE},
65 {
"resizeUpRightDownLeft", IDC_SIZENESW},
68 const wchar_t* idc_name = IDC_ARROW;
69 auto it = cursors->find(cursor_name);
70 if (it != cursors->end()) {
71 idc_name = it->second;
73 return ::LoadCursor(
nullptr, idc_name);
76 static constexpr int32_t kDefaultPointerDeviceId = 0;
82 static FlutterPointerDeviceKind GetFlutterPointerDeviceKind() {
83 constexpr LPARAM kTouchOrPenSignature = 0xFF515700;
84 constexpr LPARAM kTouchSignature = kTouchOrPenSignature | 0x80;
85 constexpr LPARAM kSignatureMask = 0xFFFFFF00;
86 LPARAM info = GetMessageExtraInfo();
87 if ((info & kSignatureMask) == kTouchOrPenSignature) {
88 if ((info & kTouchSignature) == kTouchSignature) {
89 return kFlutterPointerDeviceKindTouch;
91 return kFlutterPointerDeviceKindStylus;
93 return kFlutterPointerDeviceKindMouse;
97 static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
101 return kFlutterPointerButtonMousePrimary;
104 return kFlutterPointerButtonMouseSecondary;
107 return kFlutterPointerButtonMouseMiddle;
109 return kFlutterPointerButtonMouseBack;
111 return kFlutterPointerButtonMouseForward;
113 FML_LOG(WARNING) <<
"Mouse button not recognized: " << button;
122 std::shared_ptr<WindowsProcTable> windows_proc_table,
123 std::unique_ptr<TextInputManager> text_input_manager)
124 : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId),
125 windows_proc_table_(std::move(windows_proc_table)),
126 text_input_manager_(std::move(text_input_manager)),
127 ax_fragment_root_(nullptr) {
136 UpdateScrollOffsetMultiplier();
138 if (windows_proc_table_ ==
nullptr) {
139 windows_proc_table_ = std::make_unique<WindowsProcTable>();
141 if (text_input_manager_ ==
nullptr) {
142 text_input_manager_ = std::make_unique<TextInputManager>();
144 keyboard_manager_ = std::make_unique<KeyboardManager>(
this);
147 current_cursor_ = ::LoadCursor(
nullptr, IDC_ARROW);
152 : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId) {}
159 binding_handler_delegate_ = window;
163 if (restored_ && window) {
166 if (focused_ && window) {
172 return static_cast<float>(
GetCurrentDPI()) /
static_cast<float>(base_dpi);
184 current_cursor_ = cursor;
185 ::SetCursor(current_cursor_);
193 if (binding_handler_delegate_ !=
nullptr) {
199 if (binding_handler_delegate_ !=
nullptr) {
206 FlutterPointerDeviceKind device_kind,
208 int modifiers_state) {
209 binding_handler_delegate_->
OnPointerMove(x, y, device_kind, device_id,
215 FlutterPointerDeviceKind device_kind,
218 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
219 if (flutter_button != 0) {
221 x, y, device_kind, device_id,
222 static_cast<FlutterPointerMouseButtons
>(flutter_button));
228 FlutterPointerDeviceKind device_kind,
231 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
232 if (flutter_button != 0) {
234 x, y, device_kind, device_id,
235 static_cast<FlutterPointerMouseButtons
>(flutter_button));
241 FlutterPointerDeviceKind device_kind,
243 binding_handler_delegate_->
OnPointerLeave(x, y, device_kind, device_id);
247 ::SetCursor(current_cursor_);
288 FlutterPointerDeviceKind device_kind,
291 GetCursorPos(&point);
294 binding_handler_delegate_->
OnScroll(point.x, point.y, delta_x, delta_y,
313 bool result = ::PatBlt(dc, 0, 0, current_width_, current_height_, BLACKNESS);
323 bmi.bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
324 bmi.bmiHeader.biWidth = row_bytes / 4;
325 bmi.bmiHeader.biHeight = -height;
326 bmi.bmiHeader.biPlanes = 1;
327 bmi.bmiHeader.biBitCount = 32;
328 bmi.bmiHeader.biCompression = BI_RGB;
329 bmi.bmiHeader.biSizeImage = 0;
330 int ret = ::SetDIBitsToDevice(dc, 0, 0, row_bytes / 4, height, 0, 0, 0,
331 height, allocation, &bmi, DIB_RGB_COLORS);
337 if (binding_handler_delegate_ ==
nullptr) {
346 GetCursorPos(&point);
348 return {(size_t)point.x, (
size_t)point.y};
360 CreateAxFragmentRoot();
365 CreateAxFragmentRoot();
386 if (hwnd && binding_handler_delegate_) {
391 void FlutterWindow::TrackMouseLeaveEvent(HWND hwnd) {
392 if (!tracking_mouse_leave_) {
394 tme.cbSize =
sizeof(tme);
395 tme.hwndTrack = hwnd;
396 tme.dwFlags = TME_LEAVE;
397 TrackMouseEvent(&tme);
398 tracking_mouse_leave_ =
true;
402 void FlutterWindow::HandleResize(UINT width, UINT height) {
403 current_width_ = width;
404 current_height_ = height;
411 FlutterWindow* FlutterWindow::GetThisFromHandle(HWND
const window) noexcept {
412 return reinterpret_cast<FlutterWindow*
>(
413 GetWindowLongPtr(window, GWLP_USERDATA));
416 void FlutterWindow::UpdateScrollOffsetMultiplier() {
417 UINT lines_per_scroll = kLinesPerScrollWindowsDefault;
420 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_scroll, 0);
424 scroll_offset_multiplier_ =
425 static_cast<float>(lines_per_scroll) * 100.0 / 3.0;
430 unsigned int height) {
434 WNDCLASS window_class = RegisterWindowClass(converted_title);
436 auto* result = CreateWindowEx(
437 0, window_class.lpszClassName, converted_title.c_str(),
438 WS_CHILD | WS_VISIBLE, CW_DEFAULT, CW_DEFAULT, width, height,
439 HWND_MESSAGE,
nullptr, window_class.hInstance,
this);
441 if (result ==
nullptr) {
442 auto error = GetLastError();
444 size_t size = FormatMessageW(
445 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
446 FORMAT_MESSAGE_IGNORE_INSERTS,
447 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
448 reinterpret_cast<LPWSTR
>(&
message), 0, NULL);
452 SetUserObjectInformationA(GetCurrentProcess(),
453 UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
458 SetTimer(result, kDirectManipulationTimer, 14,
nullptr);
464 return window_handle_;
471 return ::PeekMessage(lpMsg, window_handle_, wMsgFilterMin, wMsgFilterMax,
476 return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
482 return ::SendMessage(window_handle_, Msg, wParam, lParam);
486 size_t length = strlen(source);
488 std::wstring wideTitle(length, L
'#');
489 mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length);
493 WNDCLASS FlutterWindow::RegisterWindowClass(std::wstring& title) {
494 window_class_name_ = title;
496 WNDCLASS window_class{};
497 window_class.hCursor = LoadCursor(
nullptr, IDC_ARROW);
498 window_class.lpszClassName = title.c_str();
499 window_class.style = CS_HREDRAW | CS_VREDRAW;
500 window_class.cbClsExtra = 0;
501 window_class.cbWndExtra = 0;
502 window_class.hInstance = GetModuleHandle(
nullptr);
503 window_class.hIcon =
nullptr;
504 window_class.hbrBackground = 0;
505 window_class.lpszMenuName =
nullptr;
506 window_class.lpfnWndProc = WndProc;
507 RegisterClass(&window_class);
511 LRESULT CALLBACK FlutterWindow::WndProc(HWND
const window,
514 LPARAM
const lparam) noexcept {
516 auto cs =
reinterpret_cast<CREATESTRUCT*
>(lparam);
517 SetWindowLongPtr(window, GWLP_USERDATA,
518 reinterpret_cast<LONG_PTR
>(cs->lpCreateParams));
520 auto that =
static_cast<FlutterWindow*
>(cs->lpCreateParams);
521 that->window_handle_ = window;
522 that->text_input_manager_->SetWindowHandle(window);
523 RegisterTouchWindow(window, 0);
524 }
else if (FlutterWindow* that = GetThisFromHandle(window)) {
525 return that->HandleMessage(
message, wparam, lparam);
528 return DefWindowProc(window,
message, wparam, lparam);
534 LPARAM
const lparam) noexcept {
535 LPARAM result_lparam = lparam;
536 int xPos = 0, yPos = 0;
537 UINT width = 0, height = 0;
538 UINT button_pressed = 0;
539 FlutterPointerDeviceKind device_kind;
542 case kWmDpiChangedBeforeParent:
544 OnDpiScale(current_dpi_);
547 width = LOWORD(lparam);
548 height = HIWORD(lparam);
550 current_width_ = width;
551 current_height_ = height;
552 HandleResize(width, height);
561 UINT num_points = LOWORD(wparam);
562 touch_points_.resize(num_points);
563 auto touch_input_handle =
reinterpret_cast<HTOUCHINPUT
>(lparam);
564 if (GetTouchInputInfo(touch_input_handle, num_points,
565 touch_points_.data(),
sizeof(TOUCHINPUT))) {
566 for (
const auto& touch : touch_points_) {
568 auto touch_id = touch_id_generator_.GetGeneratedId(touch.dwID);
570 POINT pt = {TOUCH_COORD_TO_PIXEL(touch.x),
571 TOUCH_COORD_TO_PIXEL(touch.y)};
572 ScreenToClient(window_handle_, &pt);
573 auto x =
static_cast<double>(pt.x);
574 auto y =
static_cast<double>(pt.y);
576 if (touch.dwFlags & TOUCHEVENTF_DOWN) {
577 OnPointerDown(x, y, kFlutterPointerDeviceKindTouch, touch_id,
579 }
else if (touch.dwFlags & TOUCHEVENTF_MOVE) {
580 OnPointerMove(x, y, kFlutterPointerDeviceKindTouch, touch_id, 0);
581 }
else if (touch.dwFlags & TOUCHEVENTF_UP) {
582 OnPointerUp(x, y, kFlutterPointerDeviceKindTouch, touch_id,
584 OnPointerLeave(x, y, kFlutterPointerDeviceKindTouch, touch_id);
585 touch_id_generator_.ReleaseNumber(touch.dwID);
588 CloseTouchInputHandle(touch_input_handle);
593 device_kind = GetFlutterPointerDeviceKind();
594 if (device_kind == kFlutterPointerDeviceKindMouse) {
595 TrackMouseLeaveEvent(window_handle_);
597 xPos = GET_X_LPARAM(lparam);
598 yPos = GET_Y_LPARAM(lparam);
599 mouse_x_ =
static_cast<double>(xPos);
600 mouse_y_ =
static_cast<double>(yPos);
603 if (wparam & MK_CONTROL) {
606 if (wparam & MK_SHIFT) {
609 OnPointerMove(mouse_x_, mouse_y_, device_kind, kDefaultPointerDeviceId,
614 device_kind = GetFlutterPointerDeviceKind();
615 if (device_kind == kFlutterPointerDeviceKindMouse) {
616 OnPointerLeave(mouse_x_, mouse_y_, device_kind,
617 kDefaultPointerDeviceId);
623 tracking_mouse_leave_ =
false;
626 UINT hit_test_result = LOWORD(lparam);
627 if (hit_test_result == HTCLIENT) {
635 ::CreateCaret(window_handle_,
nullptr, 1, 1);
645 device_kind = GetFlutterPointerDeviceKind();
646 if (device_kind != kFlutterPointerDeviceKindMouse) {
650 if (
message == WM_LBUTTONDOWN) {
656 SetCapture(window_handle_);
659 if (
message == WM_XBUTTONDOWN) {
660 button_pressed = GET_XBUTTON_WPARAM(wparam);
662 xPos = GET_X_LPARAM(lparam);
663 yPos = GET_Y_LPARAM(lparam);
664 OnPointerDown(
static_cast<double>(xPos),
static_cast<double>(yPos),
665 device_kind, kDefaultPointerDeviceId, button_pressed);
671 device_kind = GetFlutterPointerDeviceKind();
672 if (device_kind != kFlutterPointerDeviceKindMouse) {
681 button_pressed = GET_XBUTTON_WPARAM(wparam);
683 xPos = GET_X_LPARAM(lparam);
684 yPos = GET_Y_LPARAM(lparam);
685 OnPointerUp(
static_cast<double>(xPos),
static_cast<double>(yPos),
686 device_kind, kDefaultPointerDeviceId, button_pressed);
690 -(
static_cast<short>(HIWORD(wparam)) /
691 static_cast<double>(WHEEL_DELTA)),
692 kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
695 OnScroll((
static_cast<short>(HIWORD(wparam)) /
696 static_cast<double>(WHEEL_DELTA)),
697 0.0, kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
700 LRESULT lresult = OnGetObject(
message, wparam, lparam);
707 if (wparam == kDirectManipulationTimer) {
708 direct_manipulation_owner_->Update();
712 case DM_POINTERHITTEST: {
713 if (direct_manipulation_owner_) {
714 UINT contact_id = GET_POINTERID_WPARAM(wparam);
715 POINTER_INPUT_TYPE pointer_type;
716 if (windows_proc_table_->GetPointerType(contact_id, &pointer_type) &&
717 pointer_type == PT_TOUCHPAD) {
718 direct_manipulation_owner_->SetContact(contact_id);
723 case WM_INPUTLANGCHANGE:
727 case WM_IME_SETCONTEXT:
728 OnImeSetContext(
message, wparam, lparam);
732 result_lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
734 case WM_IME_STARTCOMPOSITION:
735 OnImeStartComposition(
message, wparam, lparam);
740 case WM_IME_COMPOSITION:
741 OnImeComposition(
message, wparam, lparam);
742 if (lparam & GCS_RESULTSTR || lparam & GCS_COMPSTR) {
751 case WM_IME_ENDCOMPOSITION:
752 OnImeEndComposition(
message, wparam, lparam);
755 OnImeRequest(
message, wparam, lparam);
759 if (wparam == UNICODE_NOCHAR)
764 case WM_THEMECHANGED:
775 if (keyboard_manager_->HandleMessage(
message, wparam, lparam)) {
781 return Win32DefWindowProc(window_handle_,
message, wparam, result_lparam);
786 LPARAM
const lparam) {
787 LRESULT reference_result =
static_cast<LRESULT
>(0L);
791 DWORD obj_id =
static_cast<DWORD
>(
static_cast<DWORD_PTR
>(lparam));
793 bool is_uia_request =
static_cast<DWORD
>(UiaRootObjectId) == obj_id;
794 bool is_msaa_request =
static_cast<DWORD
>(OBJID_CLIENT) == obj_id;
796 if (is_uia_request || is_msaa_request) {
812 CreateAxFragmentRoot();
813 if (is_uia_request) {
814 #ifdef FLUTTER_ENGINE_USE_UIA
816 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
818 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
819 IID_PPV_ARGS(&root)))) {
822 reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
825 FML_LOG(ERROR) <<
"Failed to query AX fragment root.";
827 #endif // FLUTTER_ENGINE_USE_UIA
828 }
else if (is_msaa_request) {
831 Microsoft::WRL::ComPtr<IAccessible> root;
832 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
833 IID_PPV_ARGS(&root));
834 reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
837 return reference_result;
842 LPARAM
const lparam) {
844 text_input_manager_->CreateImeWindow();
850 LPARAM
const lparam) {
851 text_input_manager_->CreateImeWindow();
857 LPARAM
const lparam) {
859 text_input_manager_->UpdateImeWindow();
869 if (lparam & GCS_RESULTSTR) {
872 long pos = text_input_manager_->GetComposingCursorPosition();
873 std::optional<std::u16string>
text = text_input_manager_->GetResultString();
879 if (lparam & GCS_COMPSTR) {
881 long pos = text_input_manager_->GetComposingCursorPosition();
882 std::optional<std::u16string>
text =
883 text_input_manager_->GetComposingString();
892 LPARAM
const lparam) {
893 text_input_manager_->DestroyImeWindow();
899 LPARAM
const lparam) {
906 text_input_manager_->AbortComposing();
910 text_input_manager_->UpdateCaretRect(rect);
918 return current_width_;
922 return current_height_;
926 return scroll_offset_multiplier_;
933 return ::DefWindowProc(hWnd, Msg, wParam, lParam);
936 void FlutterWindow::Destroy() {
937 if (window_handle_) {
938 text_input_manager_->SetWindowHandle(
nullptr);
939 DestroyWindow(window_handle_);
940 window_handle_ =
nullptr;
943 UnregisterClass(window_class_name_.c_str(),
nullptr);
946 void FlutterWindow::CreateAxFragmentRoot() {
947 if (ax_fragment_root_) {
950 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
953 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
954 ui::AXPlatformNode* alert_node =
956 alert_node_.reset(
static_cast<ui::AXPlatformNodeWin*
>(alert_node));
957 ax_fragment_root_->SetAlertNode(
alert_node_.get());