Flutter Engine
The Flutter Engine
flutter_window.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/shell/platform/windows/flutter_window.h"
6
7#include <WinUser.h>
8#include <dwmapi.h>
9
10#include <chrono>
11#include <map>
12
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"
19
20namespace flutter {
21
22namespace {
23
24// The Windows DPI system is based on this
25// constant for machines running at 100% scaling.
26constexpr int base_dpi = 96;
27
28static const int kMinTouchDeviceId = 0;
29static const int kMaxTouchDeviceId = 128;
30
31static const int kLinesPerScrollWindowsDefault = 3;
32
33// Maps a Flutter cursor name to an HCURSOR.
34//
35// Returns the arrow cursor for unknown constants.
36//
37// This map must be kept in sync with Flutter framework's
38// services/mouse_cursor.dart.
39static HCURSOR GetCursorByName(const std::string& cursor_name) {
40 static auto* cursors = new std::map<std::string, const wchar_t*>{
41 {"allScroll", IDC_SIZEALL},
42 {"basic", IDC_ARROW},
43 {"click", IDC_HAND},
44 {"forbidden", IDC_NO},
45 {"help", IDC_HELP},
46 {"move", IDC_SIZEALL},
47 {"none", nullptr},
48 {"noDrop", IDC_NO},
49 {"precise", IDC_CROSS},
50 {"progress", IDC_APPSTARTING},
51 {"text", IDC_IBEAM},
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},
66 {"wait", IDC_WAIT},
67 };
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;
72 }
73 return ::LoadCursor(nullptr, idc_name);
74}
75
76static constexpr int32_t kDefaultPointerDeviceId = 0;
77
78// This method is only valid during a window message related to mouse/touch
79// input.
80// See
81// https://docs.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages?redirectedfrom=MSDN#distinguishing-pen-input-from-mouse-and-touch.
82static 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) {
90 }
92 }
94}
95
96// Translates button codes from Win32 API to FlutterPointerMouseButtons.
97static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
98 switch (button) {
99 case WM_LBUTTONDOWN:
100 case WM_LBUTTONUP:
102 case WM_RBUTTONDOWN:
103 case WM_RBUTTONUP:
105 case WM_MBUTTONDOWN:
106 case WM_MBUTTONUP:
108 case XBUTTON1:
110 case XBUTTON2:
112 }
113 FML_LOG(WARNING) << "Mouse button not recognized: " << button;
114 return 0;
115}
116
117} // namespace
118
120 int width,
121 int height,
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) {
128 // Get the DPI of the primary monitor as the initial DPI. If Per-Monitor V2 is
129 // supported, |current_dpi_| should be updated in the
130 // kWmDpiChangedBeforeParent message.
131 current_dpi_ = GetDpiForHWND(nullptr);
132
133 // Get initial value for wheel scroll lines
134 // TODO: Listen to changes for this value
135 // https://github.com/flutter/flutter/issues/107248
136 UpdateScrollOffsetMultiplier();
137
138 if (windows_proc_table_ == nullptr) {
139 windows_proc_table_ = std::make_unique<WindowsProcTable>();
140 }
141 if (text_input_manager_ == nullptr) {
142 text_input_manager_ = std::make_unique<TextInputManager>();
143 }
144 keyboard_manager_ = std::make_unique<KeyboardManager>(this);
145
146 InitializeChild("FLUTTERVIEW", width, height);
147 current_cursor_ = ::LoadCursor(nullptr, IDC_ARROW);
148}
149
150// Base constructor for mocks
152 : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId) {}
153
155 Destroy();
156}
157
159 binding_handler_delegate_ = window;
161 direct_manipulation_owner_->SetBindingHandlerDelegate(window);
162 }
163 if (restored_ && window) {
165 }
166 if (focused_ && window) {
168 }
169}
170
172 return static_cast<float>(GetCurrentDPI()) / static_cast<float>(base_dpi);
173}
174
176 return {GetCurrentWidth(), GetCurrentHeight()};
177}
178
179void FlutterWindow::UpdateFlutterCursor(const std::string& cursor_name) {
180 SetFlutterCursor(GetCursorByName(cursor_name));
181}
182
184 current_cursor_ = cursor;
185 ::SetCursor(current_cursor_);
186}
187
188void FlutterWindow::OnDpiScale(unsigned int dpi) {};
189
190// When DesktopWindow notifies that a WM_Size message has come in
191// lets FlutterEngine know about the new size.
192void FlutterWindow::OnResize(unsigned int width, unsigned int height) {
193 if (binding_handler_delegate_ != nullptr) {
194 binding_handler_delegate_->OnWindowSizeChanged(width, height);
195 }
196}
197
199 if (binding_handler_delegate_ != nullptr) {
200 binding_handler_delegate_->OnWindowRepaint();
201 }
202}
203
205 double y,
206 FlutterPointerDeviceKind device_kind,
207 int32_t device_id,
208 int modifiers_state) {
209 binding_handler_delegate_->OnPointerMove(x, y, device_kind, device_id,
210 modifiers_state);
211}
212
214 double y,
215 FlutterPointerDeviceKind device_kind,
216 int32_t device_id,
217 UINT button) {
218 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
219 if (flutter_button != 0) {
220 binding_handler_delegate_->OnPointerDown(
221 x, y, device_kind, device_id,
222 static_cast<FlutterPointerMouseButtons>(flutter_button));
223 }
224}
225
227 double y,
228 FlutterPointerDeviceKind device_kind,
229 int32_t device_id,
230 UINT button) {
231 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
232 if (flutter_button != 0) {
233 binding_handler_delegate_->OnPointerUp(
234 x, y, device_kind, device_id,
235 static_cast<FlutterPointerMouseButtons>(flutter_button));
236 }
237}
238
240 double y,
241 FlutterPointerDeviceKind device_kind,
242 int32_t device_id) {
243 binding_handler_delegate_->OnPointerLeave(x, y, device_kind, device_id);
244}
245
247 ::SetCursor(current_cursor_);
248}
249
250void FlutterWindow::OnText(const std::u16string& text) {
251 binding_handler_delegate_->OnText(text);
252}
253
255 int scancode,
256 int action,
257 char32_t character,
258 bool extended,
259 bool was_down,
261 binding_handler_delegate_->OnKey(key, scancode, action, character, extended,
262 was_down, std::move(callback));
263}
264
266 binding_handler_delegate_->OnComposeBegin();
267}
268
270 binding_handler_delegate_->OnComposeCommit();
271}
272
274 binding_handler_delegate_->OnComposeEnd();
275}
276
277void FlutterWindow::OnComposeChange(const std::u16string& text,
278 int cursor_pos) {
279 binding_handler_delegate_->OnComposeChange(text, cursor_pos);
280}
281
283 binding_handler_delegate_->OnUpdateSemanticsEnabled(enabled);
284}
285
286void FlutterWindow::OnScroll(double delta_x,
287 double delta_y,
288 FlutterPointerDeviceKind device_kind,
289 int32_t device_id) {
290 POINT point;
291 GetCursorPos(&point);
292
293 ScreenToClient(GetWindowHandle(), &point);
294 binding_handler_delegate_->OnScroll(point.x, point.y, delta_x, delta_y,
295 GetScrollOffsetMultiplier(), device_kind,
296 device_id);
297}
298
300 // Convert the rect from Flutter logical coordinates to device coordinates.
301 auto scale = GetDpiScale();
302 Point origin(rect.left() * scale, rect.top() * scale);
303 Size size(rect.width() * scale, rect.height() * scale);
304 UpdateCursorRect(Rect(origin, size));
305}
306
309}
310
312 HDC dc = ::GetDC(GetWindowHandle());
313 bool result = ::PatBlt(dc, 0, 0, current_width_, current_height_, BLACKNESS);
314 ::ReleaseDC(GetWindowHandle(), dc);
315 return result;
316}
317
318bool FlutterWindow::OnBitmapSurfaceUpdated(const void* allocation,
319 size_t row_bytes,
320 size_t height) {
321 HDC dc = ::GetDC(GetWindowHandle());
322 BITMAPINFO bmi = {};
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);
332 ::ReleaseDC(GetWindowHandle(), dc);
333 return ret != 0;
334}
335
337 if (binding_handler_delegate_ == nullptr) {
338 return nullptr;
339 }
340
341 return binding_handler_delegate_->GetNativeViewAccessible();
342}
343
345 POINT point;
346 GetCursorPos(&point);
347 ScreenToClient(GetWindowHandle(), &point);
348 return {(size_t)point.x, (size_t)point.y};
349}
350
352 binding_handler_delegate_->OnHighContrastChanged();
353}
354
356 return binding_handler_delegate_->GetAxFragmentRootDelegate();
357}
358
360 CreateAxFragmentRoot();
361 return alert_delegate_.get();
362}
363
364ui::AXPlatformNodeWin* FlutterWindow::GetAlert() {
365 CreateAxFragmentRoot();
366 return alert_node_.get();
367}
368
370 switch (event) {
372 restored_ = true;
373 break;
375 restored_ = false;
376 focused_ = false;
377 break;
379 focused_ = true;
380 break;
382 focused_ = false;
383 break;
384 }
385 HWND hwnd = GetWindowHandle();
386 if (hwnd && binding_handler_delegate_) {
387 binding_handler_delegate_->OnWindowStateEvent(hwnd, event);
388 }
389}
390
391void FlutterWindow::TrackMouseLeaveEvent(HWND hwnd) {
392 if (!tracking_mouse_leave_) {
393 TRACKMOUSEEVENT tme;
394 tme.cbSize = sizeof(tme);
395 tme.hwndTrack = hwnd;
396 tme.dwFlags = TME_LEAVE;
397 TrackMouseEvent(&tme);
398 tracking_mouse_leave_ = true;
399 }
400}
401
402void FlutterWindow::HandleResize(UINT width, UINT height) {
403 current_width_ = width;
404 current_height_ = height;
406 direct_manipulation_owner_->ResizeViewport(width, height);
407 }
409}
410
411FlutterWindow* FlutterWindow::GetThisFromHandle(HWND const window) noexcept {
412 return reinterpret_cast<FlutterWindow*>(
413 GetWindowLongPtr(window, GWLP_USERDATA));
414}
415
416void FlutterWindow::UpdateScrollOffsetMultiplier() {
417 UINT lines_per_scroll = kLinesPerScrollWindowsDefault;
418
419 // Get lines per scroll wheel value from Windows
420 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_scroll, 0);
421
422 // This logic is based off Chromium's implementation
423 // https://source.chromium.org/chromium/chromium/src/+/main:ui/events/blink/web_input_event_builders_win.cc;l=319-331
424 scroll_offset_multiplier_ =
425 static_cast<float>(lines_per_scroll) * 100.0 / 3.0;
426}
427
428void FlutterWindow::InitializeChild(const char* title,
429 unsigned int width,
430 unsigned int height) {
431 Destroy();
432 std::wstring converted_title = NarrowToWide(title);
433
434 WNDCLASS window_class = RegisterWindowClass(converted_title);
435
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);
440
441 if (result == nullptr) {
442 auto error = GetLastError();
443 LPWSTR message = nullptr;
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);
449 OutputDebugString(message);
450 LocalFree(message);
451 }
452 SetUserObjectInformationA(GetCurrentProcess(),
453 UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
454 // SetTimer is not precise, if a 16 ms interval is requested, it will instead
455 // often fire in an interval of 32 ms. Providing a value of 14 will ensure it
456 // runs every 16 ms, which will allow for 60 Hz trackpad gesture events, which
457 // is the maximal frequency supported by SetTimer.
458 SetTimer(result, kDirectManipulationTimer, 14, nullptr);
459 direct_manipulation_owner_ = std::make_unique<DirectManipulationOwner>(this);
461}
462
464 return window_handle_;
465}
466
468 UINT wMsgFilterMin,
469 UINT wMsgFilterMax,
470 UINT wRemoveMsg) {
471 return ::PeekMessage(lpMsg, window_handle_, wMsgFilterMin, wMsgFilterMax,
472 wRemoveMsg);
473}
474
475uint32_t FlutterWindow::Win32MapVkToChar(uint32_t virtual_key) {
476 return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
477}
478
480 WPARAM wParam,
481 LPARAM lParam) {
482 return ::SendMessage(window_handle_, Msg, wParam, lParam);
483}
484
485std::wstring FlutterWindow::NarrowToWide(const char* source) {
486 size_t length = strlen(source);
487 size_t outlen = 0;
488 std::wstring wideTitle(length, L'#');
489 mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length);
490 return wideTitle;
491}
492
493WNDCLASS FlutterWindow::RegisterWindowClass(std::wstring& title) {
494 window_class_name_ = title;
495
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);
508 return window_class;
509}
510
511LRESULT CALLBACK FlutterWindow::WndProc(HWND const window,
512 UINT const message,
513 WPARAM const wparam,
514 LPARAM const lparam) noexcept {
515 if (message == WM_NCCREATE) {
516 auto cs = reinterpret_cast<CREATESTRUCT*>(lparam);
517 SetWindowLongPtr(window, GWLP_USERDATA,
518 reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
519
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);
526 }
527
528 return DefWindowProc(window, message, wparam, lparam);
529}
530
533 WPARAM const wparam,
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;
540
541 switch (message) {
542 case kWmDpiChangedBeforeParent:
543 current_dpi_ = GetDpiForHWND(window_handle_);
544 OnDpiScale(current_dpi_);
545 return 0;
546 case WM_SIZE:
547 width = LOWORD(lparam);
548 height = HIWORD(lparam);
549
550 current_width_ = width;
551 current_height_ = height;
552 HandleResize(width, height);
553
554 OnWindowStateEvent(width == 0 && height == 0 ? WindowStateEvent::kHide
556 break;
557 case WM_PAINT:
558 OnPaint();
559 break;
560 case WM_TOUCH: {
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_) {
567 // Generate a mapped ID for the Windows-provided touch ID
568 auto touch_id = touch_id_generator_.GetGeneratedId(touch.dwID);
569
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);
575
576 if (touch.dwFlags & TOUCHEVENTF_DOWN) {
577 OnPointerDown(x, y, kFlutterPointerDeviceKindTouch, touch_id,
578 WM_LBUTTONDOWN);
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,
583 WM_LBUTTONDOWN);
584 OnPointerLeave(x, y, kFlutterPointerDeviceKindTouch, touch_id);
585 touch_id_generator_.ReleaseNumber(touch.dwID);
586 }
587 }
588 CloseTouchInputHandle(touch_input_handle);
589 }
590 return 0;
591 }
592 case WM_MOUSEMOVE:
593 device_kind = GetFlutterPointerDeviceKind();
594 if (device_kind == kFlutterPointerDeviceKindMouse) {
595 TrackMouseLeaveEvent(window_handle_);
596
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);
601
602 int mods = 0;
603 if (wparam & MK_CONTROL) {
604 mods |= kControl;
605 }
606 if (wparam & MK_SHIFT) {
607 mods |= kShift;
608 }
609 OnPointerMove(mouse_x_, mouse_y_, device_kind, kDefaultPointerDeviceId,
610 mods);
611 }
612 break;
613 case WM_MOUSELEAVE:
614 device_kind = GetFlutterPointerDeviceKind();
615 if (device_kind == kFlutterPointerDeviceKindMouse) {
616 OnPointerLeave(mouse_x_, mouse_y_, device_kind,
617 kDefaultPointerDeviceId);
618 }
619
620 // Once the tracked event is received, the TrackMouseEvent function
621 // resets. Set to false to make sure it's called once mouse movement is
622 // detected again.
623 tracking_mouse_leave_ = false;
624 break;
625 case WM_SETCURSOR: {
626 UINT hit_test_result = LOWORD(lparam);
627 if (hit_test_result == HTCLIENT) {
628 OnSetCursor();
629 return TRUE;
630 }
631 break;
632 }
633 case WM_SETFOCUS:
634 OnWindowStateEvent(WindowStateEvent::kFocus);
635 ::CreateCaret(window_handle_, nullptr, 1, 1);
636 break;
637 case WM_KILLFOCUS:
638 OnWindowStateEvent(WindowStateEvent::kUnfocus);
639 ::DestroyCaret();
640 break;
641 case WM_LBUTTONDOWN:
642 case WM_RBUTTONDOWN:
643 case WM_MBUTTONDOWN:
644 case WM_XBUTTONDOWN:
645 device_kind = GetFlutterPointerDeviceKind();
646 if (device_kind != kFlutterPointerDeviceKindMouse) {
647 break;
648 }
649
650 if (message == WM_LBUTTONDOWN) {
651 // Capture the pointer in case the user drags outside the client area.
652 // In this case, the "mouse leave" event is delayed until the user
653 // releases the button. It's only activated on left click given that
654 // it's more common for apps to handle dragging with only the left
655 // button.
656 SetCapture(window_handle_);
657 }
658 button_pressed = message;
659 if (message == WM_XBUTTONDOWN) {
660 button_pressed = GET_XBUTTON_WPARAM(wparam);
661 }
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);
666 break;
667 case WM_LBUTTONUP:
668 case WM_RBUTTONUP:
669 case WM_MBUTTONUP:
670 case WM_XBUTTONUP:
671 device_kind = GetFlutterPointerDeviceKind();
672 if (device_kind != kFlutterPointerDeviceKindMouse) {
673 break;
674 }
675
676 if (message == WM_LBUTTONUP) {
677 ReleaseCapture();
678 }
679 button_pressed = message;
680 if (message == WM_XBUTTONUP) {
681 button_pressed = GET_XBUTTON_WPARAM(wparam);
682 }
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);
687 break;
688 case WM_MOUSEWHEEL:
689 OnScroll(0.0,
690 -(static_cast<short>(HIWORD(wparam)) /
691 static_cast<double>(WHEEL_DELTA)),
692 kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
693 break;
694 case WM_MOUSEHWHEEL:
695 OnScroll((static_cast<short>(HIWORD(wparam)) /
696 static_cast<double>(WHEEL_DELTA)),
697 0.0, kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
698 break;
699 case WM_GETOBJECT: {
700 LRESULT lresult = OnGetObject(message, wparam, lparam);
701 if (lresult) {
702 return lresult;
703 }
704 break;
705 }
706 case WM_TIMER:
707 if (wparam == kDirectManipulationTimer) {
708 direct_manipulation_owner_->Update();
709 return 0;
710 }
711 break;
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);
719 }
720 }
721 break;
722 }
723 case WM_INPUTLANGCHANGE:
724 // TODO(cbracken): pass this to TextInputManager to aid with
725 // language-specific issues.
726 break;
727 case WM_IME_SETCONTEXT:
728 OnImeSetContext(message, wparam, lparam);
729 // Strip the ISC_SHOWUICOMPOSITIONWINDOW bit from lparam before passing it
730 // to DefWindowProc() so that the composition window is hidden since
731 // Flutter renders the composing string itself.
732 result_lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
733 break;
734 case WM_IME_STARTCOMPOSITION:
735 OnImeStartComposition(message, wparam, lparam);
736 // Suppress further processing by DefWindowProc() so that the default
737 // system IME style isn't used, but rather the one set in the
738 // WM_IME_SETCONTEXT handler.
739 return TRUE;
740 case WM_IME_COMPOSITION:
741 OnImeComposition(message, wparam, lparam);
742 if (lparam & GCS_RESULTSTR || lparam & GCS_COMPSTR) {
743 // Suppress further processing by DefWindowProc() since otherwise it
744 // will emit the result string as WM_CHAR messages on commit. Instead,
745 // committing the composing text to the EditableText string is handled
746 // in TextInputModel::CommitComposing, triggered by
747 // OnImeEndComposition().
748 return TRUE;
749 }
750 break;
751 case WM_IME_ENDCOMPOSITION:
752 OnImeEndComposition(message, wparam, lparam);
753 return TRUE;
754 case WM_IME_REQUEST:
755 OnImeRequest(message, wparam, lparam);
756 break;
757 case WM_UNICHAR: {
758 // Tell third-pary app, we can support Unicode.
759 if (wparam == UNICODE_NOCHAR)
760 return TRUE;
761 // DefWindowProc will send WM_CHAR for this WM_UNICHAR.
762 break;
763 }
764 case WM_THEMECHANGED:
765 OnThemeChange();
766 break;
767 case WM_DEADCHAR:
768 case WM_SYSDEADCHAR:
769 case WM_CHAR:
770 case WM_SYSCHAR:
771 case WM_KEYDOWN:
772 case WM_SYSKEYDOWN:
773 case WM_KEYUP:
774 case WM_SYSKEYUP:
775 if (keyboard_manager_->HandleMessage(message, wparam, lparam)) {
776 return 0;
777 }
778 break;
779 }
780
781 return Win32DefWindowProc(window_handle_, message, wparam, result_lparam);
782}
783
785 WPARAM const wparam,
786 LPARAM const lparam) {
787 LRESULT reference_result = static_cast<LRESULT>(0L);
788
789 // Only the lower 32 bits of lparam are valid when checking the object id
790 // because it sometimes gets sign-extended incorrectly (but not always).
791 DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lparam));
792
793 bool is_uia_request = static_cast<DWORD>(UiaRootObjectId) == obj_id;
794 bool is_msaa_request = static_cast<DWORD>(OBJID_CLIENT) == obj_id;
795
796 if (is_uia_request || is_msaa_request) {
797 // On Windows, we don't get a notification that the screen reader has been
798 // enabled or disabled. There is an API to query for screen reader state,
799 // but that state isn't set by all screen readers, including by Narrator,
800 // the screen reader that ships with Windows:
801 // https://docs.microsoft.com/en-us/windows/win32/winauto/screen-reader-parameter
802 //
803 // Instead, we enable semantics in Flutter if Windows issues queries for
804 // Microsoft Active Accessibility (MSAA) COM objects.
806 }
807
809 // TODO(schectman): UIA is currently disabled by default.
810 // https://github.com/flutter/flutter/issues/114547
811 if (root_view) {
812 CreateAxFragmentRoot();
813 if (is_uia_request) {
814#ifdef FLUTTER_ENGINE_USE_UIA
815 // Retrieve UIA object for the root view.
816 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
817 if (SUCCEEDED(
818 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
819 IID_PPV_ARGS(&root)))) {
820 // Return the UIA object via UiaReturnRawElementProvider(). See:
821 // https://docs.microsoft.com/en-us/windows/win32/winauto/wm-getobject
822 reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
823 lparam, root.Get());
824 } else {
825 FML_LOG(ERROR) << "Failed to query AX fragment root.";
826 }
827#endif // FLUTTER_ENGINE_USE_UIA
828 } else if (is_msaa_request) {
829 // Create the accessibility root if it does not already exist.
830 // Return the IAccessible for the root view.
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());
835 }
836 }
837 return reference_result;
838}
839
841 WPARAM const wparam,
842 LPARAM const lparam) {
843 if (wparam != 0) {
844 text_input_manager_->CreateImeWindow();
845 }
846}
847
849 WPARAM const wparam,
850 LPARAM const lparam) {
851 text_input_manager_->CreateImeWindow();
853}
854
856 WPARAM const wparam,
857 LPARAM const lparam) {
858 // Update the IME window position.
859 text_input_manager_->UpdateImeWindow();
860
861 if (lparam == 0) {
862 OnComposeChange(u"", 0);
864 }
865
866 // Process GCS_RESULTSTR at fisrt, because Google Japanese Input and ATOK send
867 // both GCS_RESULTSTR and GCS_COMPSTR to commit composed text and send new
868 // composing text.
869 if (lparam & GCS_RESULTSTR) {
870 // Commit but don't end composing.
871 // Read the committed composing string.
872 long pos = text_input_manager_->GetComposingCursorPosition();
873 std::optional<std::u16string> text = text_input_manager_->GetResultString();
874 if (text) {
875 OnComposeChange(text.value(), pos);
877 }
878 }
879 if (lparam & GCS_COMPSTR) {
880 // Read the in-progress composing string.
881 long pos = text_input_manager_->GetComposingCursorPosition();
882 std::optional<std::u16string> text =
883 text_input_manager_->GetComposingString();
884 if (text) {
885 OnComposeChange(text.value(), pos);
886 }
887 }
888}
889
891 WPARAM const wparam,
892 LPARAM const lparam) {
893 text_input_manager_->DestroyImeWindow();
894 OnComposeEnd();
895}
896
898 WPARAM const wparam,
899 LPARAM const lparam) {
900 // TODO(cbracken): Handle IMR_RECONVERTSTRING, IMR_DOCUMENTFEED,
901 // and IMR_QUERYCHARPOSITION messages.
902 // https://github.com/flutter/flutter/issues/74547
903}
904
906 text_input_manager_->AbortComposing();
907}
908
910 text_input_manager_->UpdateCaretRect(rect);
911}
912
914 return current_dpi_;
915}
916
918 return current_width_;
919}
920
922 return current_height_;
923}
924
926 return scroll_offset_multiplier_;
927}
928
930 UINT Msg,
931 WPARAM wParam,
932 LPARAM lParam) {
933 return ::DefWindowProc(hWnd, Msg, wParam, lParam);
934}
935
936void FlutterWindow::Destroy() {
937 if (window_handle_) {
938 text_input_manager_->SetWindowHandle(nullptr);
939 DestroyWindow(window_handle_);
940 window_handle_ = nullptr;
941 }
942
943 UnregisterClass(window_class_name_.c_str(), nullptr);
944}
945
946void FlutterWindow::CreateAxFragmentRoot() {
947 if (ax_fragment_root_) {
948 return;
949 }
950 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
951 window_handle_, GetAxFragmentRootDelegate());
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());
958}
959
960} // namespace flutter
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
SkPoint pos
static sk_sp< Effect > Create()
Definition: RefCntTest.cpp:117
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 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 gfx::NativeViewAccessible GetNativeViewAccessible()=0
virtual bool OnWindowSizeChanged(size_t width, size_t height)=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
FlutterPointerMouseButtons
Definition: embedder.h:1016
@ kFlutterPointerButtonMousePrimary
Definition: embedder.h:1017
@ kFlutterPointerButtonMouseMiddle
Definition: embedder.h:1019
@ kFlutterPointerButtonMouseForward
Definition: embedder.h:1021
@ kFlutterPointerButtonMouseBack
Definition: embedder.h:1020
@ kFlutterPointerButtonMouseSecondary
Definition: embedder.h:1018
FlutterPointerDeviceKind
The device type that created a pointer event.
Definition: embedder.h:1007
@ kFlutterPointerDeviceKindTouch
Definition: embedder.h:1009
@ kFlutterPointerDeviceKindStylus
Definition: embedder.h:1010
@ kFlutterPointerDeviceKindMouse
Definition: embedder.h:1008
GLFWwindow * window
Definition: main.cc:45
SkBitmap source
Definition: examples.cpp:28
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:82
size_t length
std::u16string text
Win32Message message
return FALSE
double y
double x
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
constexpr int kShift
UINT GetDpiForHWND(HWND hwnd)
Definition: dpi_utils.cc:130
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
Definition: switches.h:259
constexpr int kControl
UnimplementedNativeViewAccessible * NativeViewAccessible
TRect< Scalar > Rect
Definition: rect.h:769
string root
Definition: scale_cpu.py:20
Definition: ref_ptr.h:256
int32_t height
int32_t width
const Scalar scale
#define ERROR(message)
Definition: elf_loader.cc:260
int BOOL
Definition: windows_types.h:37
struct tagMSG * LPMSG
#define SUCCEEDED(hr)
LONG_PTR LRESULT
Definition: windows_types.h:61
unsigned int UINT
Definition: windows_types.h:32
LONG_PTR LPARAM
Definition: windows_types.h:60
__w64 long LONG_PTR
Definition: windows_types.h:55
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)
#define SendMessage
UINT_PTR WPARAM
Definition: windows_types.h:59
unsigned long DWORD
Definition: windows_types.h:22
#define CALLBACK