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;
33 static constexpr int32_t kDefaultPointerDeviceId = 0;
39 static FlutterPointerDeviceKind GetFlutterPointerDeviceKind() {
40 constexpr LPARAM kTouchOrPenSignature = 0xFF515700;
41 constexpr LPARAM kTouchSignature = kTouchOrPenSignature | 0x80;
42 constexpr LPARAM kSignatureMask = 0xFFFFFF00;
43 LPARAM info = GetMessageExtraInfo();
44 if ((info & kSignatureMask) == kTouchOrPenSignature) {
45 if ((info & kTouchSignature) == kTouchSignature) {
46 return kFlutterPointerDeviceKindTouch;
48 return kFlutterPointerDeviceKindStylus;
50 return kFlutterPointerDeviceKindMouse;
54 static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
58 return kFlutterPointerButtonMousePrimary;
61 return kFlutterPointerButtonMouseSecondary;
64 return kFlutterPointerButtonMouseMiddle;
66 return kFlutterPointerButtonMouseBack;
68 return kFlutterPointerButtonMouseForward;
70 FML_LOG(WARNING) <<
"Mouse button not recognized: " << button;
79 std::shared_ptr<WindowsProcTable> windows_proc_table,
80 std::unique_ptr<TextInputManager> text_input_manager)
81 : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId),
82 windows_proc_table_(std::move(windows_proc_table)),
83 text_input_manager_(std::move(text_input_manager)),
84 ax_fragment_root_(nullptr) {
93 UpdateScrollOffsetMultiplier();
95 if (windows_proc_table_ ==
nullptr) {
96 windows_proc_table_ = std::make_unique<WindowsProcTable>();
98 if (text_input_manager_ ==
nullptr) {
99 text_input_manager_ = std::make_unique<TextInputManager>();
101 keyboard_manager_ = std::make_unique<KeyboardManager>(
this);
108 : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId) {}
115 binding_handler_delegate_ = window;
119 if (restored_ && window) {
122 if (focused_ && window) {
128 return static_cast<float>(
GetCurrentDPI()) /
static_cast<float>(base_dpi);
137 if (hwnd ==
nullptr) {
141 HWND prevFocus = ::SetFocus(hwnd);
142 if (prevFocus ==
nullptr) {
154 if (binding_handler_delegate_ !=
nullptr) {
160 if (binding_handler_delegate_ !=
nullptr) {
167 FlutterPointerDeviceKind device_kind,
169 int modifiers_state) {
170 binding_handler_delegate_->
OnPointerMove(x, y, device_kind, device_id,
176 FlutterPointerDeviceKind device_kind,
179 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
180 if (flutter_button != 0) {
182 x, y, device_kind, device_id,
183 static_cast<FlutterPointerMouseButtons
>(flutter_button));
189 FlutterPointerDeviceKind device_kind,
192 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
193 if (flutter_button != 0) {
195 x, y, device_kind, device_id,
196 static_cast<FlutterPointerMouseButtons
>(flutter_button));
202 FlutterPointerDeviceKind device_kind,
204 binding_handler_delegate_->
OnPointerLeave(x, y, device_kind, device_id);
245 FlutterPointerDeviceKind device_kind,
248 GetCursorPos(&point);
251 binding_handler_delegate_->
OnScroll(point.x, point.y, delta_x, delta_y,
270 bool result = ::PatBlt(dc, 0, 0, current_width_, current_height_, BLACKNESS);
280 bmi.bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
281 bmi.bmiHeader.biWidth = row_bytes / 4;
282 bmi.bmiHeader.biHeight = -height;
283 bmi.bmiHeader.biPlanes = 1;
284 bmi.bmiHeader.biBitCount = 32;
285 bmi.bmiHeader.biCompression = BI_RGB;
286 bmi.bmiHeader.biSizeImage = 0;
287 int ret = ::SetDIBitsToDevice(dc, 0, 0, row_bytes / 4, height, 0, 0, 0,
288 height, allocation, &bmi, DIB_RGB_COLORS);
294 if (binding_handler_delegate_ ==
nullptr) {
303 GetCursorPos(&point);
305 return {(size_t)point.x, (
size_t)point.y};
317 CreateAxFragmentRoot();
322 CreateAxFragmentRoot();
337 if (binding_handler_delegate_) {
338 binding_handler_delegate_->
OnFocus(
339 FlutterViewFocusState::kFocused,
340 FlutterViewFocusDirection::kUndefined);
345 if (binding_handler_delegate_) {
346 binding_handler_delegate_->
OnFocus(
347 FlutterViewFocusState::kUnfocused,
348 FlutterViewFocusDirection::kUndefined);
353 if (hwnd && binding_handler_delegate_) {
358 void FlutterWindow::TrackMouseLeaveEvent(HWND hwnd) {
359 if (!tracking_mouse_leave_) {
361 tme.cbSize =
sizeof(tme);
362 tme.hwndTrack = hwnd;
363 tme.dwFlags = TME_LEAVE;
364 TrackMouseEvent(&tme);
365 tracking_mouse_leave_ =
true;
369 void FlutterWindow::HandleResize(UINT width, UINT height) {
370 current_width_ = width;
371 current_height_ = height;
378 FlutterWindow* FlutterWindow::GetThisFromHandle(HWND
const window) noexcept {
379 return reinterpret_cast<FlutterWindow*
>(
380 GetWindowLongPtr(window, GWLP_USERDATA));
383 void FlutterWindow::UpdateScrollOffsetMultiplier() {
384 UINT lines_per_scroll = kLinesPerScrollWindowsDefault;
387 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_scroll, 0);
391 scroll_offset_multiplier_ =
392 static_cast<float>(lines_per_scroll) * 100.0 / 3.0;
397 unsigned int height) {
401 WNDCLASS window_class = RegisterWindowClass(converted_title);
403 auto* result = CreateWindowEx(
404 0, window_class.lpszClassName, converted_title.c_str(),
405 WS_CHILD | WS_VISIBLE, CW_DEFAULT, CW_DEFAULT, width, height,
406 HWND_MESSAGE,
nullptr, window_class.hInstance,
this);
408 if (result ==
nullptr) {
409 auto error = GetLastError();
411 size_t size = FormatMessageW(
412 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
413 FORMAT_MESSAGE_IGNORE_INSERTS,
414 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
415 reinterpret_cast<LPWSTR
>(&
message), 0, NULL);
419 SetUserObjectInformationA(GetCurrentProcess(),
420 UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
425 SetTimer(result, kDirectManipulationTimer, 14,
nullptr);
431 return window_handle_;
438 return ::PeekMessage(lpMsg, window_handle_, wMsgFilterMin, wMsgFilterMax,
443 return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
449 return ::SendMessage(window_handle_, Msg, wParam, lParam);
453 size_t length = strlen(source);
455 std::wstring wideTitle(length, L
'#');
456 mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length);
460 WNDCLASS FlutterWindow::RegisterWindowClass(std::wstring& title) {
461 window_class_name_ = title;
463 WNDCLASS window_class{};
464 window_class.hCursor = LoadCursor(
nullptr, IDC_ARROW);
465 window_class.lpszClassName = title.c_str();
466 window_class.style = CS_HREDRAW | CS_VREDRAW;
467 window_class.cbClsExtra = 0;
468 window_class.cbWndExtra = 0;
469 window_class.hInstance = GetModuleHandle(
nullptr);
470 window_class.hIcon =
nullptr;
471 window_class.hbrBackground = 0;
472 window_class.lpszMenuName =
nullptr;
473 window_class.lpfnWndProc = WndProc;
474 RegisterClass(&window_class);
478 LRESULT CALLBACK FlutterWindow::WndProc(HWND
const window,
481 LPARAM
const lparam) noexcept {
483 auto cs =
reinterpret_cast<CREATESTRUCT*
>(lparam);
484 SetWindowLongPtr(window, GWLP_USERDATA,
485 reinterpret_cast<LONG_PTR
>(cs->lpCreateParams));
487 auto that =
static_cast<FlutterWindow*
>(cs->lpCreateParams);
488 that->window_handle_ = window;
489 that->text_input_manager_->SetWindowHandle(window);
490 RegisterTouchWindow(window, 0);
491 }
else if (FlutterWindow* that = GetThisFromHandle(window)) {
492 return that->HandleMessage(
message, wparam, lparam);
495 return DefWindowProc(window,
message, wparam, lparam);
501 LPARAM
const lparam) noexcept {
502 LPARAM result_lparam = lparam;
503 int xPos = 0, yPos = 0;
504 UINT width = 0, height = 0;
505 UINT button_pressed = 0;
506 FlutterPointerDeviceKind device_kind;
509 case kWmDpiChangedBeforeParent:
511 OnDpiScale(current_dpi_);
514 width = LOWORD(lparam);
515 height = HIWORD(lparam);
517 current_width_ = width;
518 current_height_ = height;
519 HandleResize(width, height);
528 UINT num_points = LOWORD(wparam);
529 touch_points_.resize(num_points);
530 auto touch_input_handle =
reinterpret_cast<HTOUCHINPUT
>(lparam);
531 if (GetTouchInputInfo(touch_input_handle, num_points,
532 touch_points_.data(),
sizeof(TOUCHINPUT))) {
533 for (
const auto& touch : touch_points_) {
535 auto touch_id = touch_id_generator_.GetGeneratedId(touch.dwID);
537 POINT pt = {TOUCH_COORD_TO_PIXEL(touch.x),
538 TOUCH_COORD_TO_PIXEL(touch.y)};
539 ScreenToClient(window_handle_, &pt);
540 auto x =
static_cast<double>(pt.x);
541 auto y =
static_cast<double>(pt.y);
543 if (touch.dwFlags & TOUCHEVENTF_DOWN) {
544 OnPointerDown(x, y, kFlutterPointerDeviceKindTouch, touch_id,
546 }
else if (touch.dwFlags & TOUCHEVENTF_MOVE) {
547 OnPointerMove(x, y, kFlutterPointerDeviceKindTouch, touch_id, 0);
548 }
else if (touch.dwFlags & TOUCHEVENTF_UP) {
549 OnPointerUp(x, y, kFlutterPointerDeviceKindTouch, touch_id,
551 OnPointerLeave(x, y, kFlutterPointerDeviceKindTouch, touch_id);
552 touch_id_generator_.ReleaseNumber(touch.dwID);
555 CloseTouchInputHandle(touch_input_handle);
560 device_kind = GetFlutterPointerDeviceKind();
561 if (device_kind == kFlutterPointerDeviceKindMouse) {
562 TrackMouseLeaveEvent(window_handle_);
564 xPos = GET_X_LPARAM(lparam);
565 yPos = GET_Y_LPARAM(lparam);
566 mouse_x_ =
static_cast<double>(xPos);
567 mouse_y_ =
static_cast<double>(yPos);
570 if (wparam & MK_CONTROL) {
573 if (wparam & MK_SHIFT) {
576 OnPointerMove(mouse_x_, mouse_y_, device_kind, kDefaultPointerDeviceId,
581 device_kind = GetFlutterPointerDeviceKind();
582 if (device_kind == kFlutterPointerDeviceKindMouse) {
583 OnPointerLeave(mouse_x_, mouse_y_, device_kind,
584 kDefaultPointerDeviceId);
590 tracking_mouse_leave_ =
false;
593 UINT hit_test_result = LOWORD(lparam);
594 if (hit_test_result == HTCLIENT) {
603 ::CreateCaret(window_handle_,
nullptr, 1, 1);
613 device_kind = GetFlutterPointerDeviceKind();
614 if (device_kind != kFlutterPointerDeviceKindMouse) {
618 if (
message == WM_LBUTTONDOWN) {
624 SetCapture(window_handle_);
627 if (
message == WM_XBUTTONDOWN) {
628 button_pressed = GET_XBUTTON_WPARAM(wparam);
630 xPos = GET_X_LPARAM(lparam);
631 yPos = GET_Y_LPARAM(lparam);
632 OnPointerDown(
static_cast<double>(xPos),
static_cast<double>(yPos),
633 device_kind, kDefaultPointerDeviceId, button_pressed);
639 device_kind = GetFlutterPointerDeviceKind();
640 if (device_kind != kFlutterPointerDeviceKindMouse) {
649 button_pressed = GET_XBUTTON_WPARAM(wparam);
651 xPos = GET_X_LPARAM(lparam);
652 yPos = GET_Y_LPARAM(lparam);
653 OnPointerUp(
static_cast<double>(xPos),
static_cast<double>(yPos),
654 device_kind, kDefaultPointerDeviceId, button_pressed);
658 -(
static_cast<short>(HIWORD(wparam)) /
659 static_cast<double>(WHEEL_DELTA)),
660 kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
663 OnScroll((
static_cast<short>(HIWORD(wparam)) /
664 static_cast<double>(WHEEL_DELTA)),
665 0.0, kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
668 LRESULT lresult = OnGetObject(
message, wparam, lparam);
675 if (wparam == kDirectManipulationTimer) {
676 direct_manipulation_owner_->Update();
680 case DM_POINTERHITTEST: {
681 if (direct_manipulation_owner_) {
682 UINT contact_id = GET_POINTERID_WPARAM(wparam);
683 POINTER_INPUT_TYPE pointer_type;
684 if (windows_proc_table_->GetPointerType(contact_id, &pointer_type) &&
685 pointer_type == PT_TOUCHPAD) {
686 direct_manipulation_owner_->SetContact(contact_id);
691 case WM_INPUTLANGCHANGE:
695 case WM_IME_SETCONTEXT:
696 OnImeSetContext(
message, wparam, lparam);
700 result_lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
702 case WM_IME_STARTCOMPOSITION:
703 OnImeStartComposition(
message, wparam, lparam);
708 case WM_IME_COMPOSITION:
709 OnImeComposition(
message, wparam, lparam);
710 if (lparam & GCS_RESULTSTR || lparam & GCS_COMPSTR) {
719 case WM_IME_ENDCOMPOSITION:
720 OnImeEndComposition(
message, wparam, lparam);
723 OnImeRequest(
message, wparam, lparam);
727 if (wparam == UNICODE_NOCHAR)
732 case WM_THEMECHANGED:
743 if (keyboard_manager_->HandleMessage(
message, wparam, lparam)) {
749 return Win32DefWindowProc(window_handle_,
message, wparam, result_lparam);
754 LPARAM
const lparam) {
755 LRESULT reference_result =
static_cast<LRESULT
>(0L);
759 DWORD obj_id =
static_cast<DWORD
>(
static_cast<DWORD_PTR
>(lparam));
761 bool is_uia_request =
static_cast<DWORD
>(UiaRootObjectId) == obj_id;
762 bool is_msaa_request =
static_cast<DWORD
>(OBJID_CLIENT) == obj_id;
764 if (is_uia_request || is_msaa_request) {
780 CreateAxFragmentRoot();
781 if (is_uia_request) {
782 #ifdef FLUTTER_ENGINE_USE_UIA
784 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
786 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
787 IID_PPV_ARGS(&root)))) {
790 reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
793 FML_LOG(ERROR) <<
"Failed to query AX fragment root.";
796 }
else if (is_msaa_request) {
799 Microsoft::WRL::ComPtr<IAccessible> root;
800 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
801 IID_PPV_ARGS(&root));
802 reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
805 return reference_result;
810 LPARAM
const lparam) {
812 text_input_manager_->CreateImeWindow();
818 LPARAM
const lparam) {
819 text_input_manager_->CreateImeWindow();
825 LPARAM
const lparam) {
827 text_input_manager_->UpdateImeWindow();
837 if (lparam & GCS_RESULTSTR) {
840 long pos = text_input_manager_->GetComposingCursorPosition();
841 std::optional<std::u16string>
text = text_input_manager_->GetResultString();
847 if (lparam & GCS_COMPSTR) {
849 long pos = text_input_manager_->GetComposingCursorPosition();
850 std::optional<std::u16string>
text =
851 text_input_manager_->GetComposingString();
860 LPARAM
const lparam) {
861 text_input_manager_->DestroyImeWindow();
867 LPARAM
const lparam) {
874 text_input_manager_->AbortComposing();
878 text_input_manager_->UpdateCaretRect(rect);
886 return current_width_;
890 return current_height_;
894 return scroll_offset_multiplier_;
901 return ::DefWindowProc(hWnd, Msg, wParam, lParam);
904 void FlutterWindow::Destroy() {
905 if (window_handle_) {
906 text_input_manager_->SetWindowHandle(
nullptr);
907 DestroyWindow(window_handle_);
908 window_handle_ =
nullptr;
911 UnregisterClass(window_class_name_.c_str(),
nullptr);
914 void FlutterWindow::CreateAxFragmentRoot() {
915 if (ax_fragment_root_) {
918 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
921 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
922 ui::AXPlatformNode* alert_node =
924 alert_node_.reset(
static_cast<ui::AXPlatformNodeWin*
>(alert_node));
925 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 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
FlutterDesktopBinaryReply callback
UINT GetDpiForHWND(HWND hwnd)
WindowStateEvent
An event representing a change in window state that may update the.