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_);
190 if (hwnd ==
nullptr) {
194 HWND prevFocus = ::SetFocus(hwnd);
195 if (prevFocus ==
nullptr) {
207 if (binding_handler_delegate_ !=
nullptr) {
213 if (binding_handler_delegate_ !=
nullptr) {
220 FlutterPointerDeviceKind device_kind,
222 int modifiers_state) {
223 binding_handler_delegate_->
OnPointerMove(x, y, device_kind, device_id,
229 FlutterPointerDeviceKind device_kind,
232 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
233 if (flutter_button != 0) {
235 x, y, device_kind, device_id,
236 static_cast<FlutterPointerMouseButtons
>(flutter_button));
242 FlutterPointerDeviceKind device_kind,
245 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
246 if (flutter_button != 0) {
248 x, y, device_kind, device_id,
249 static_cast<FlutterPointerMouseButtons
>(flutter_button));
255 FlutterPointerDeviceKind device_kind,
257 binding_handler_delegate_->
OnPointerLeave(x, y, device_kind, device_id);
261 ::SetCursor(current_cursor_);
302 FlutterPointerDeviceKind device_kind,
305 GetCursorPos(&point);
308 binding_handler_delegate_->
OnScroll(point.x, point.y, delta_x, delta_y,
327 bool result = ::PatBlt(dc, 0, 0, current_width_, current_height_, BLACKNESS);
337 bmi.bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
338 bmi.bmiHeader.biWidth = row_bytes / 4;
339 bmi.bmiHeader.biHeight = -height;
340 bmi.bmiHeader.biPlanes = 1;
341 bmi.bmiHeader.biBitCount = 32;
342 bmi.bmiHeader.biCompression = BI_RGB;
343 bmi.bmiHeader.biSizeImage = 0;
344 int ret = ::SetDIBitsToDevice(dc, 0, 0, row_bytes / 4, height, 0, 0, 0,
345 height, allocation, &bmi, DIB_RGB_COLORS);
351 if (binding_handler_delegate_ ==
nullptr) {
360 GetCursorPos(&point);
362 return {(size_t)point.x, (
size_t)point.y};
374 CreateAxFragmentRoot();
379 CreateAxFragmentRoot();
394 if (binding_handler_delegate_) {
395 binding_handler_delegate_->
OnFocus(
396 FlutterViewFocusState::kFocused,
397 FlutterViewFocusDirection::kUndefined);
402 if (binding_handler_delegate_) {
403 binding_handler_delegate_->
OnFocus(
404 FlutterViewFocusState::kUnfocused,
405 FlutterViewFocusDirection::kUndefined);
410 if (hwnd && binding_handler_delegate_) {
415 void FlutterWindow::TrackMouseLeaveEvent(HWND hwnd) {
416 if (!tracking_mouse_leave_) {
418 tme.cbSize =
sizeof(tme);
419 tme.hwndTrack = hwnd;
420 tme.dwFlags = TME_LEAVE;
421 TrackMouseEvent(&tme);
422 tracking_mouse_leave_ =
true;
426 void FlutterWindow::HandleResize(UINT width, UINT height) {
427 current_width_ = width;
428 current_height_ = height;
435 FlutterWindow* FlutterWindow::GetThisFromHandle(HWND
const window) noexcept {
436 return reinterpret_cast<FlutterWindow*
>(
437 GetWindowLongPtr(window, GWLP_USERDATA));
440 void FlutterWindow::UpdateScrollOffsetMultiplier() {
441 UINT lines_per_scroll = kLinesPerScrollWindowsDefault;
444 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_scroll, 0);
448 scroll_offset_multiplier_ =
449 static_cast<float>(lines_per_scroll) * 100.0 / 3.0;
454 unsigned int height) {
458 WNDCLASS window_class = RegisterWindowClass(converted_title);
460 auto* result = CreateWindowEx(
461 0, window_class.lpszClassName, converted_title.c_str(),
462 WS_CHILD | WS_VISIBLE, CW_DEFAULT, CW_DEFAULT, width, height,
463 HWND_MESSAGE,
nullptr, window_class.hInstance,
this);
465 if (result ==
nullptr) {
466 auto error = GetLastError();
468 size_t size = FormatMessageW(
469 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
470 FORMAT_MESSAGE_IGNORE_INSERTS,
471 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
472 reinterpret_cast<LPWSTR
>(&
message), 0, NULL);
476 SetUserObjectInformationA(GetCurrentProcess(),
477 UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
482 SetTimer(result, kDirectManipulationTimer, 14,
nullptr);
488 return window_handle_;
495 return ::PeekMessage(lpMsg, window_handle_, wMsgFilterMin, wMsgFilterMax,
500 return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
506 return ::SendMessage(window_handle_, Msg, wParam, lParam);
510 size_t length = strlen(source);
512 std::wstring wideTitle(length, L
'#');
513 mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length);
517 WNDCLASS FlutterWindow::RegisterWindowClass(std::wstring& title) {
518 window_class_name_ = title;
520 WNDCLASS window_class{};
521 window_class.hCursor = LoadCursor(
nullptr, IDC_ARROW);
522 window_class.lpszClassName = title.c_str();
523 window_class.style = CS_HREDRAW | CS_VREDRAW;
524 window_class.cbClsExtra = 0;
525 window_class.cbWndExtra = 0;
526 window_class.hInstance = GetModuleHandle(
nullptr);
527 window_class.hIcon =
nullptr;
528 window_class.hbrBackground = 0;
529 window_class.lpszMenuName =
nullptr;
530 window_class.lpfnWndProc = WndProc;
531 RegisterClass(&window_class);
535 LRESULT CALLBACK FlutterWindow::WndProc(HWND
const window,
538 LPARAM
const lparam) noexcept {
540 auto cs =
reinterpret_cast<CREATESTRUCT*
>(lparam);
541 SetWindowLongPtr(window, GWLP_USERDATA,
542 reinterpret_cast<LONG_PTR
>(cs->lpCreateParams));
544 auto that =
static_cast<FlutterWindow*
>(cs->lpCreateParams);
545 that->window_handle_ = window;
546 that->text_input_manager_->SetWindowHandle(window);
547 RegisterTouchWindow(window, 0);
548 }
else if (FlutterWindow* that = GetThisFromHandle(window)) {
549 return that->HandleMessage(
message, wparam, lparam);
552 return DefWindowProc(window,
message, wparam, lparam);
558 LPARAM
const lparam) noexcept {
559 LPARAM result_lparam = lparam;
560 int xPos = 0, yPos = 0;
561 UINT width = 0, height = 0;
562 UINT button_pressed = 0;
563 FlutterPointerDeviceKind device_kind;
566 case kWmDpiChangedBeforeParent:
568 OnDpiScale(current_dpi_);
571 width = LOWORD(lparam);
572 height = HIWORD(lparam);
574 current_width_ = width;
575 current_height_ = height;
576 HandleResize(width, height);
585 UINT num_points = LOWORD(wparam);
586 touch_points_.resize(num_points);
587 auto touch_input_handle =
reinterpret_cast<HTOUCHINPUT
>(lparam);
588 if (GetTouchInputInfo(touch_input_handle, num_points,
589 touch_points_.data(),
sizeof(TOUCHINPUT))) {
590 for (
const auto& touch : touch_points_) {
592 auto touch_id = touch_id_generator_.GetGeneratedId(touch.dwID);
594 POINT pt = {TOUCH_COORD_TO_PIXEL(touch.x),
595 TOUCH_COORD_TO_PIXEL(touch.y)};
596 ScreenToClient(window_handle_, &pt);
597 auto x =
static_cast<double>(pt.x);
598 auto y =
static_cast<double>(pt.y);
600 if (touch.dwFlags & TOUCHEVENTF_DOWN) {
601 OnPointerDown(x, y, kFlutterPointerDeviceKindTouch, touch_id,
603 }
else if (touch.dwFlags & TOUCHEVENTF_MOVE) {
604 OnPointerMove(x, y, kFlutterPointerDeviceKindTouch, touch_id, 0);
605 }
else if (touch.dwFlags & TOUCHEVENTF_UP) {
606 OnPointerUp(x, y, kFlutterPointerDeviceKindTouch, touch_id,
608 OnPointerLeave(x, y, kFlutterPointerDeviceKindTouch, touch_id);
609 touch_id_generator_.ReleaseNumber(touch.dwID);
612 CloseTouchInputHandle(touch_input_handle);
617 device_kind = GetFlutterPointerDeviceKind();
618 if (device_kind == kFlutterPointerDeviceKindMouse) {
619 TrackMouseLeaveEvent(window_handle_);
621 xPos = GET_X_LPARAM(lparam);
622 yPos = GET_Y_LPARAM(lparam);
623 mouse_x_ =
static_cast<double>(xPos);
624 mouse_y_ =
static_cast<double>(yPos);
627 if (wparam & MK_CONTROL) {
630 if (wparam & MK_SHIFT) {
633 OnPointerMove(mouse_x_, mouse_y_, device_kind, kDefaultPointerDeviceId,
638 device_kind = GetFlutterPointerDeviceKind();
639 if (device_kind == kFlutterPointerDeviceKindMouse) {
640 OnPointerLeave(mouse_x_, mouse_y_, device_kind,
641 kDefaultPointerDeviceId);
647 tracking_mouse_leave_ =
false;
650 UINT hit_test_result = LOWORD(lparam);
651 if (hit_test_result == HTCLIENT) {
659 ::CreateCaret(window_handle_,
nullptr, 1, 1);
669 device_kind = GetFlutterPointerDeviceKind();
670 if (device_kind != kFlutterPointerDeviceKindMouse) {
674 if (
message == WM_LBUTTONDOWN) {
680 SetCapture(window_handle_);
683 if (
message == WM_XBUTTONDOWN) {
684 button_pressed = GET_XBUTTON_WPARAM(wparam);
686 xPos = GET_X_LPARAM(lparam);
687 yPos = GET_Y_LPARAM(lparam);
688 OnPointerDown(
static_cast<double>(xPos),
static_cast<double>(yPos),
689 device_kind, kDefaultPointerDeviceId, button_pressed);
695 device_kind = GetFlutterPointerDeviceKind();
696 if (device_kind != kFlutterPointerDeviceKindMouse) {
705 button_pressed = GET_XBUTTON_WPARAM(wparam);
707 xPos = GET_X_LPARAM(lparam);
708 yPos = GET_Y_LPARAM(lparam);
709 OnPointerUp(
static_cast<double>(xPos),
static_cast<double>(yPos),
710 device_kind, kDefaultPointerDeviceId, button_pressed);
714 -(
static_cast<short>(HIWORD(wparam)) /
715 static_cast<double>(WHEEL_DELTA)),
716 kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
719 OnScroll((
static_cast<short>(HIWORD(wparam)) /
720 static_cast<double>(WHEEL_DELTA)),
721 0.0, kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
724 LRESULT lresult = OnGetObject(
message, wparam, lparam);
731 if (wparam == kDirectManipulationTimer) {
732 direct_manipulation_owner_->Update();
736 case DM_POINTERHITTEST: {
737 if (direct_manipulation_owner_) {
738 UINT contact_id = GET_POINTERID_WPARAM(wparam);
739 POINTER_INPUT_TYPE pointer_type;
740 if (windows_proc_table_->GetPointerType(contact_id, &pointer_type) &&
741 pointer_type == PT_TOUCHPAD) {
742 direct_manipulation_owner_->SetContact(contact_id);
747 case WM_INPUTLANGCHANGE:
751 case WM_IME_SETCONTEXT:
752 OnImeSetContext(
message, wparam, lparam);
756 result_lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
758 case WM_IME_STARTCOMPOSITION:
759 OnImeStartComposition(
message, wparam, lparam);
764 case WM_IME_COMPOSITION:
765 OnImeComposition(
message, wparam, lparam);
766 if (lparam & GCS_RESULTSTR || lparam & GCS_COMPSTR) {
775 case WM_IME_ENDCOMPOSITION:
776 OnImeEndComposition(
message, wparam, lparam);
779 OnImeRequest(
message, wparam, lparam);
783 if (wparam == UNICODE_NOCHAR)
788 case WM_THEMECHANGED:
799 if (keyboard_manager_->HandleMessage(
message, wparam, lparam)) {
805 return Win32DefWindowProc(window_handle_,
message, wparam, result_lparam);
810 LPARAM
const lparam) {
811 LRESULT reference_result =
static_cast<LRESULT
>(0L);
815 DWORD obj_id =
static_cast<DWORD
>(
static_cast<DWORD_PTR
>(lparam));
817 bool is_uia_request =
static_cast<DWORD
>(UiaRootObjectId) == obj_id;
818 bool is_msaa_request =
static_cast<DWORD
>(OBJID_CLIENT) == obj_id;
820 if (is_uia_request || is_msaa_request) {
836 CreateAxFragmentRoot();
837 if (is_uia_request) {
838 #ifdef FLUTTER_ENGINE_USE_UIA
840 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
842 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
843 IID_PPV_ARGS(&root)))) {
846 reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
849 FML_LOG(ERROR) <<
"Failed to query AX fragment root.";
851 #endif // FLUTTER_ENGINE_USE_UIA
852 }
else if (is_msaa_request) {
855 Microsoft::WRL::ComPtr<IAccessible> root;
856 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
857 IID_PPV_ARGS(&root));
858 reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
861 return reference_result;
866 LPARAM
const lparam) {
868 text_input_manager_->CreateImeWindow();
874 LPARAM
const lparam) {
875 text_input_manager_->CreateImeWindow();
881 LPARAM
const lparam) {
883 text_input_manager_->UpdateImeWindow();
893 if (lparam & GCS_RESULTSTR) {
896 long pos = text_input_manager_->GetComposingCursorPosition();
897 std::optional<std::u16string>
text = text_input_manager_->GetResultString();
903 if (lparam & GCS_COMPSTR) {
905 long pos = text_input_manager_->GetComposingCursorPosition();
906 std::optional<std::u16string>
text =
907 text_input_manager_->GetComposingString();
916 LPARAM
const lparam) {
917 text_input_manager_->DestroyImeWindow();
923 LPARAM
const lparam) {
930 text_input_manager_->AbortComposing();
934 text_input_manager_->UpdateCaretRect(rect);
942 return current_width_;
946 return current_height_;
950 return scroll_offset_multiplier_;
957 return ::DefWindowProc(hWnd, Msg, wParam, lParam);
960 void FlutterWindow::Destroy() {
961 if (window_handle_) {
962 text_input_manager_->SetWindowHandle(
nullptr);
963 DestroyWindow(window_handle_);
964 window_handle_ =
nullptr;
967 UnregisterClass(window_class_name_.c_str(),
nullptr);
970 void FlutterWindow::CreateAxFragmentRoot() {
971 if (ax_fragment_root_) {
974 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
977 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
978 ui::AXPlatformNode* alert_node =
980 alert_node_.reset(
static_cast<ui::AXPlatformNodeWin*
>(alert_node));
981 ax_fragment_root_->SetAlertNode(
alert_node_.get());