5#include "flutter/shell/platform/windows/flutter_window.h"
13#include "flutter/fml/logging.h"
14#include "flutter/shell/platform/embedder/embedder.h"
15#include "flutter/shell/platform/windows/dpi_utils.h"
16#include "flutter/shell/platform/windows/flutter_windows_engine.h"
17#include "flutter/shell/platform/windows/flutter_windows_view.h"
18#include "flutter/shell/platform/windows/keyboard_utils.h"
26constexpr int base_dpi = 96;
28static const int kMinTouchDeviceId = 0;
29static const int kMaxTouchDeviceId = 128;
31static const int kLinesPerScrollWindowsDefault = 3;
39static 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);
76static constexpr int32_t kDefaultPointerDeviceId = 0;
83 constexpr LPARAM kTouchOrPenSignature = 0xFF515700;
84 constexpr LPARAM kTouchSignature = kTouchOrPenSignature | 0x80;
85 constexpr LPARAM kSignatureMask = 0xFFFFFF00;
87 if ((
info & kSignatureMask) == kTouchOrPenSignature) {
88 if ((
info & kTouchSignature) == kTouchSignature) {
97static uint64_t ConvertWinButtonToFlutterButton(
UINT button) {
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) {
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) {
208 int modifiers_state) {
218 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
219 if (flutter_button != 0) {
221 x,
y, device_kind, device_id,
231 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
232 if (flutter_button != 0) {
234 x,
y, device_kind, device_id,
247 ::SetCursor(current_cursor_);
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_) {
391void 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;
403 current_width_ =
width;
411FlutterWindow* FlutterWindow::GetThisFromHandle(HWND
const window)
noexcept {
412 return reinterpret_cast<FlutterWindow*
>(
413 GetWindowLongPtr(
window, GWLP_USERDATA));
416void 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;
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);
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);
488 std::wstring wideTitle(
length,
L'#');
493WNDCLASS 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);
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);
534 LPARAM const lparam)
noexcept {
535 LPARAM result_lparam = lparam;
536 int xPos = 0, yPos = 0;
538 UINT button_pressed = 0;
542 case kWmDpiChangedBeforeParent:
544 OnDpiScale(current_dpi_);
547 width = LOWORD(lparam);
550 current_width_ =
width;
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) {
579 }
else if (touch.dwFlags & TOUCHEVENTF_MOVE) {
581 }
else if (touch.dwFlags & TOUCHEVENTF_UP) {
585 touch_id_generator_.ReleaseNumber(touch.dwID);
588 CloseTouchInputHandle(touch_input_handle);
593 device_kind = GetFlutterPointerDeviceKind();
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();
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();
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();
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)),
695 OnScroll((
static_cast<short>(HIWORD(wparam)) /
696 static_cast<double>(WHEEL_DELTA)),
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);
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,
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;
844 text_input_manager_->CreateImeWindow();
851 text_input_manager_->CreateImeWindow();
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();
893 text_input_manager_->DestroyImeWindow();
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);
936void 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);
946void FlutterWindow::CreateAxFragmentRoot() {
947 if (ax_fragment_root_) {
950 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
953 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
956 alert_node_.reset(
static_cast<ui::AXPlatformNodeWin*
>(alert_node));
957 ax_fragment_root_->SetAlertNode(
alert_node_.get());
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
static sk_sp< Effect > Create()
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 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 void OnSetCursor()
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
virtual void SetFlutterCursor(HCURSOR cursor) 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 UpdateFlutterCursor(const std::string &cursor_name) override
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 ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate()=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 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
FlutterPointerMouseButtons
@ kFlutterPointerButtonMousePrimary
@ kFlutterPointerButtonMouseMiddle
@ kFlutterPointerButtonMouseForward
@ kFlutterPointerButtonMouseBack
@ kFlutterPointerButtonMouseSecondary
FlutterPointerDeviceKind
The device type that created a pointer event.
@ kFlutterPointerDeviceKindTouch
@ kFlutterPointerDeviceKindStylus
@ kFlutterPointerDeviceKindMouse
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
const uint8_t uint32_t uint32_t GError ** error
#define FML_LOG(severity)
sk_sp< SkBlender > blender SkRect rect
UINT GetDpiForHWND(HWND hwnd)
WindowStateEvent
An event representing a change in window state that may update the.
it will be possible to load the file into Perfetto s trace viewer 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
UnimplementedNativeViewAccessible * NativeViewAccessible
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)