Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
6
7#include <WinUser.h>
8#include <dwmapi.h>
9
10#include <chrono>
11#include <cstdint>
12
13#include "flutter/fml/logging.h"
20
21namespace flutter {
22
23namespace {
24
25// The Windows DPI system is based on this
26// constant for machines running at 100% scaling.
27constexpr int base_dpi = 96;
28
29static const int kMinTouchDeviceId = 0;
30static const int kMaxTouchDeviceId = 128;
31
32static const int kLinesPerScrollWindowsDefault = 3;
33
34static constexpr int32_t kDefaultPointerDeviceId = 0;
35
36static int GetCursorPositionForComposition(const TextInputManager& manager,
37 LPARAM lparam,
38 size_t text_length) {
39 if (!(lparam & GCS_CURSORPOS)) {
40 // Some IMEs update the composition string without reporting an explicit
41 // cursor position. In that case, keep the framework caret at the end of
42 // the latest composition text.
43 return static_cast<int>(text_length);
44 }
45
46 int position = static_cast<int>(manager.GetComposingCursorPosition());
47 if (position < 0 || static_cast<size_t>(position) > text_length) {
48 return static_cast<int>(text_length);
49 }
50 return static_cast<int>(position);
51}
52
53// This method is only valid during a window message related to mouse/touch
54// input.
55// See
56// https://docs.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages?redirectedfrom=MSDN#distinguishing-pen-input-from-mouse-and-touch.
57static FlutterPointerDeviceKind GetFlutterPointerDeviceKind() {
58 constexpr LPARAM kTouchOrPenSignature = 0xFF515700;
59 constexpr LPARAM kTouchSignature = kTouchOrPenSignature | 0x80;
60 constexpr LPARAM kSignatureMask = 0xFFFFFF00;
61 LPARAM info = GetMessageExtraInfo();
62 if ((info & kSignatureMask) == kTouchOrPenSignature) {
63 if ((info & kTouchSignature) == kTouchSignature) {
65 }
67 }
69}
70
71// Translates button codes from Win32 API to FlutterPointerMouseButtons.
72static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
73 switch (button) {
74 case WM_LBUTTONDOWN:
75 case WM_LBUTTONUP:
77 case WM_RBUTTONDOWN:
78 case WM_RBUTTONUP:
80 case WM_MBUTTONDOWN:
81 case WM_MBUTTONUP:
83 case XBUTTON1:
85 case XBUTTON2:
87 }
88 FML_LOG(WARNING) << "Mouse button not recognized: " << button;
89 return 0;
90}
91
92// Translates mouse button state from Win32 API to FlutterPointerMouseButtons.
93static uint64_t ConvertWinMouseStateToFlutterButtons(WPARAM wparam) {
94 uint64_t flutter_buttons = 0;
95 if (wparam & MK_LBUTTON) {
96 flutter_buttons |= kFlutterPointerButtonMousePrimary;
97 }
98 if (wparam & MK_RBUTTON) {
99 flutter_buttons |= kFlutterPointerButtonMouseSecondary;
100 }
101 if (wparam & MK_MBUTTON) {
102 flutter_buttons |= kFlutterPointerButtonMouseMiddle;
103 }
104 if (wparam & MK_XBUTTON1) {
105 flutter_buttons |= kFlutterPointerButtonMouseBack;
106 }
107 if (wparam & MK_XBUTTON2) {
108 flutter_buttons |= kFlutterPointerButtonMouseForward;
109 }
110 return flutter_buttons;
111}
112
113// Translate stylus pointer flags from Win32 API to FlutterPointerStylusButtons.
114static uint64_t ConvertWinStylusFlagsToFlutterButtons(UINT pen_flags,
115 UINT pointer_flags) {
116 uint64_t flutter_buttons = 0;
117 if ((pointer_flags & POINTER_FLAG_INCONTACT) == 0) {
118 return flutter_buttons;
119 }
120 flutter_buttons |= kFlutterPointerButtonStylusContact;
121 if (pen_flags & PEN_FLAG_BARREL) {
122 flutter_buttons |= kFlutterPointerButtonStylusPrimary;
123 }
124 if (pen_flags & PEN_FLAG_ERASER) {
125 flutter_buttons |= kFlutterPointerButtonStylusSecondary;
126 }
127 return flutter_buttons;
128}
129
130// Translate pointer flags from Win32 API to Flutter pointer buttons.
131static uint64_t ConvertWinPointerFlagsToFlutterButtons(UINT flags) {
132 uint64_t flutter_buttons = 0;
133 if ((flags & POINTER_FLAG_INCONTACT) == 0) {
134 // If the pointer is not in contact, then no buttons should be considered
135 return flutter_buttons;
136 }
137 if (flags & POINTER_FLAG_FIRSTBUTTON) {
138 flutter_buttons |= kFlutterPointerButtonMousePrimary;
139 }
140 if (flags & POINTER_FLAG_SECONDBUTTON) {
141 flutter_buttons |= kFlutterPointerButtonMouseSecondary;
142 }
143 if (flags & POINTER_FLAG_THIRDBUTTON) {
144 flutter_buttons |= kFlutterPointerButtonMouseMiddle;
145 }
146 return flutter_buttons;
147}
148
149} // namespace
150
152 int width,
153 int height,
154 std::shared_ptr<DisplayManagerWin32> const& display_manager,
155 std::shared_ptr<WindowsProcTable> windows_proc_table,
156 std::unique_ptr<TextInputManager> text_input_manager)
157 : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId),
158 display_manager_(display_manager),
159 windows_proc_table_(std::move(windows_proc_table)),
160 text_input_manager_(std::move(text_input_manager)),
161 ax_fragment_root_(nullptr) {
162 // Get the DPI of the primary monitor as the initial DPI. If Per-Monitor V2 is
163 // supported, |current_dpi_| should be updated in the
164 // kWmDpiChangedBeforeParent message.
165 current_dpi_ = GetDpiForHWND(nullptr);
166
167 // Get initial value for wheel scroll lines
168 // TODO: Listen to changes for this value
169 // https://github.com/flutter/flutter/issues/107248
170 UpdateScrollOffsetMultiplier();
171
172 if (windows_proc_table_ == nullptr) {
173 windows_proc_table_ = std::make_unique<WindowsProcTable>();
174 }
175 if (text_input_manager_ == nullptr) {
176 text_input_manager_ = std::make_unique<TextInputManager>();
177 }
178 keyboard_manager_ = std::make_unique<KeyboardManager>(this);
179
180 InitializeChild("FLUTTERVIEW", width, height);
181}
182
183// Base constructor for mocks
186
188 Destroy();
189}
190
192 binding_handler_delegate_ = window;
194 direct_manipulation_owner_->SetBindingHandlerDelegate(window);
195 }
196 if (restored_ && window) {
198 }
199 if (focused_ && window) {
201 }
202}
203
205 return static_cast<float>(GetCurrentDPI()) / static_cast<float>(base_dpi);
206}
207
211
213 auto hwnd = GetWindowHandle();
214 if (hwnd == nullptr) {
215 return false;
216 }
217
218 HWND prevFocus = ::SetFocus(hwnd);
219 if (prevFocus == nullptr) {
220 return false;
221 }
222
223 return true;
224}
225
226void FlutterWindow::OnDpiScale(unsigned int dpi) {};
227
228// When DesktopWindow notifies that a WM_Size message has come in
229// lets FlutterEngine know about the new size.
230void FlutterWindow::OnResize(unsigned int width, unsigned int height) {
231 if (binding_handler_delegate_ != nullptr) {
232 binding_handler_delegate_->OnWindowSizeChanged(width, height);
233 }
234}
235
237 if (binding_handler_delegate_ != nullptr) {
238 binding_handler_delegate_->OnWindowRepaint();
239 }
240}
241
243 double y,
244 FlutterPointerDeviceKind device_kind,
245 int32_t device_id,
246 uint64_t buttons,
247 uint32_t rotation,
248 uint32_t pressure,
249 int modifiers_state) {
250 binding_handler_delegate_->OnPointerMove(x, y, device_kind, device_id,
251 buttons, rotation, pressure,
252 modifiers_state);
253}
254
256 double y,
257 FlutterPointerDeviceKind device_kind,
258 int32_t device_id,
259 uint64_t buttons,
260 uint32_t pressure,
261 uint32_t rotation) {
262 if (buttons != 0) {
263 binding_handler_delegate_->OnPointerDown(x, y, device_kind, device_id,
264 buttons, rotation, pressure);
265 }
266}
267
269 double y,
270 FlutterPointerDeviceKind device_kind,
271 int32_t device_id,
272 uint64_t buttons) {
273 binding_handler_delegate_->OnPointerUp(x, y, device_kind, device_id, buttons);
274}
275
277 double y,
278 FlutterPointerDeviceKind device_kind,
279 int32_t device_id) {
280 binding_handler_delegate_->OnPointerLeave(x, y, device_kind, device_id);
281}
282
283void FlutterWindow::OnText(const std::u16string& text) {
284 binding_handler_delegate_->OnText(text);
285}
286
288 int scancode,
289 int action,
290 char32_t character,
291 bool extended,
292 bool was_down,
294 binding_handler_delegate_->OnKey(key, scancode, action, character, extended,
295 was_down, std::move(callback));
296}
297
299 binding_handler_delegate_->OnComposeBegin();
300}
301
303 binding_handler_delegate_->OnComposeCommit();
304}
305
307 binding_handler_delegate_->OnComposeEnd();
308}
309
310void FlutterWindow::OnComposeChange(const std::u16string& text,
311 int cursor_pos) {
312 binding_handler_delegate_->OnComposeChange(text, cursor_pos);
313}
314
316 binding_handler_delegate_->OnUpdateSemanticsEnabled(enabled);
317}
318
319void FlutterWindow::OnScroll(double delta_x,
320 double delta_y,
321 FlutterPointerDeviceKind device_kind,
322 int32_t device_id) {
323 POINT point;
324 GetCursorPos(&point);
325
326 ScreenToClient(GetWindowHandle(), &point);
327 binding_handler_delegate_->OnScroll(point.x, point.y, delta_x, delta_y,
328 GetScrollOffsetMultiplier(), device_kind,
329 device_id);
330}
331
333 // Convert the rect from Flutter logical coordinates to device coordinates.
334 auto scale = GetDpiScale();
335 Point origin(rect.left() * scale, rect.top() * scale);
336 Size size(rect.width() * scale, rect.height() * scale);
337 UpdateCursorRect(Rect(origin, size));
338}
339
343
345 HDC dc = ::GetDC(GetWindowHandle());
346 bool result = ::PatBlt(dc, 0, 0, current_width_, current_height_, BLACKNESS);
347 ::ReleaseDC(GetWindowHandle(), dc);
348 return result;
349}
350
351bool FlutterWindow::OnBitmapSurfaceUpdated(const void* allocation,
352 size_t row_bytes,
353 size_t height) {
354 HDC dc = ::GetDC(GetWindowHandle());
355 BITMAPINFO bmi = {};
356 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
357 bmi.bmiHeader.biWidth = row_bytes / 4;
358 bmi.bmiHeader.biHeight = -height;
359 bmi.bmiHeader.biPlanes = 1;
360 bmi.bmiHeader.biBitCount = 32;
361 bmi.bmiHeader.biCompression = BI_RGB;
362 bmi.bmiHeader.biSizeImage = 0;
363 int ret = ::SetDIBitsToDevice(dc, 0, 0, row_bytes / 4, height, 0, 0, 0,
364 height, allocation, &bmi, DIB_RGB_COLORS);
365 ::ReleaseDC(GetWindowHandle(), dc);
366 return ret != 0;
367}
368
370 if (binding_handler_delegate_ == nullptr) {
371 return nullptr;
372 }
373
374 return binding_handler_delegate_->GetNativeViewAccessible();
375}
376
378 POINT point;
379 GetCursorPos(&point);
380 ScreenToClient(GetWindowHandle(), &point);
381 return {(size_t)point.x, (size_t)point.y};
382}
383
385 FlutterEngineDisplayId const display_id =
386 reinterpret_cast<FlutterEngineDisplayId>(
387 MonitorFromWindow(GetWindowHandle(), MONITOR_DEFAULTTONEAREST));
388 if (!display_manager_->FindById(display_id)) {
389 FML_LOG(ERROR) << "Current monitor not found in display list.";
390 }
391 return display_id;
392}
393
395 binding_handler_delegate_->OnHighContrastChanged();
396}
397
401
403 CreateAxFragmentRoot();
404 return alert_delegate_.get();
405}
406
407ui::AXPlatformNodeWin* FlutterWindow::GetAlert() {
408 CreateAxFragmentRoot();
409 return alert_node_.get();
410}
411
413 switch (event) {
415 restored_ = true;
416 break;
418 restored_ = false;
419 focused_ = false;
420 break;
422 focused_ = true;
423 if (binding_handler_delegate_) {
424 binding_handler_delegate_->OnFocus(
427 }
428 break;
430 focused_ = false;
431 if (binding_handler_delegate_) {
432 binding_handler_delegate_->OnFocus(
435 }
436 break;
437 }
438 HWND hwnd = GetWindowHandle();
439 if (hwnd && binding_handler_delegate_) {
440 binding_handler_delegate_->OnWindowStateEvent(hwnd, event);
441 }
442}
443
444void FlutterWindow::TrackMouseLeaveEvent(HWND hwnd) {
445 if (!tracking_mouse_leave_) {
446 TRACKMOUSEEVENT tme;
447 tme.cbSize = sizeof(tme);
448 tme.hwndTrack = hwnd;
449 tme.dwFlags = TME_LEAVE;
450 TrackMouseEvent(&tme);
451 tracking_mouse_leave_ = true;
452 }
453}
454
455void FlutterWindow::HandleResize(UINT width, UINT height) {
456 current_width_ = width;
457 current_height_ = height;
459 direct_manipulation_owner_->ResizeViewport(width, height);
460 }
462}
463
464FlutterWindow* FlutterWindow::GetThisFromHandle(HWND const window) noexcept {
465 return reinterpret_cast<FlutterWindow*>(
466 GetWindowLongPtr(window, GWLP_USERDATA));
467}
468
469void FlutterWindow::UpdateScrollOffsetMultiplier() {
470 UINT lines_per_scroll = kLinesPerScrollWindowsDefault;
471
472 // Get lines per scroll wheel value from Windows
473 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_scroll, 0);
474
475 // This logic is based off Chromium's implementation
476 // https://source.chromium.org/chromium/chromium/src/+/main:ui/events/blink/web_input_event_builders_win.cc;l=319-331
477 scroll_offset_multiplier_ =
478 static_cast<float>(lines_per_scroll) * 100.0 / 3.0;
479}
480
481void FlutterWindow::InitializeChild(const char* title,
482 unsigned int width,
483 unsigned int height) {
484 Destroy();
485 std::wstring converted_title = NarrowToWide(title);
486
487 WNDCLASS window_class = RegisterWindowClass(converted_title);
488
489 auto* result = CreateWindowEx(
490 0, window_class.lpszClassName, converted_title.c_str(),
491 WS_CHILD | WS_VISIBLE, CW_DEFAULT, CW_DEFAULT, width, height,
492 HWND_MESSAGE, nullptr, window_class.hInstance, this);
493
494 if (result == nullptr) {
495 auto error = GetLastError();
496 LPWSTR message = nullptr;
497 size_t size = FormatMessageW(
498 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
499 FORMAT_MESSAGE_IGNORE_INSERTS,
500 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
501 reinterpret_cast<LPWSTR>(&message), 0, NULL);
502 OutputDebugString(message);
503 LocalFree(message);
504 }
505 SetUserObjectInformationA(GetCurrentProcess(),
506 UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
507 // SetTimer is not precise, if a 16 ms interval is requested, it will instead
508 // often fire in an interval of 32 ms. Providing a value of 14 will ensure it
509 // runs every 16 ms, which will allow for 60 Hz trackpad gesture events, which
510 // is the maximal frequency supported by SetTimer.
511 SetTimer(result, kDirectManipulationTimer, 14, nullptr);
512 direct_manipulation_owner_ = std::make_unique<DirectManipulationOwner>(this);
514}
515
517 return window_handle_;
518}
519
521 UINT wMsgFilterMin,
522 UINT wMsgFilterMax,
523 UINT wRemoveMsg) {
524 return ::PeekMessage(lpMsg, window_handle_, wMsgFilterMin, wMsgFilterMax,
525 wRemoveMsg);
526}
527
528uint32_t FlutterWindow::Win32MapVkToChar(uint32_t virtual_key) {
529 return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
530}
531
533 WPARAM wParam,
534 LPARAM lParam) {
535 return ::SendMessage(window_handle_, Msg, wParam, lParam);
536}
537
538std::wstring FlutterWindow::NarrowToWide(const char* source) {
539 size_t length = strlen(source);
540 size_t outlen = 0;
541 std::wstring wideTitle(length, L'#');
542 mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length);
543 return wideTitle;
544}
545
546WNDCLASS FlutterWindow::RegisterWindowClass(std::wstring& title) {
547 window_class_name_ = title;
548
549 WNDCLASS window_class{};
550 window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
551 window_class.lpszClassName = title.c_str();
552 window_class.style = CS_HREDRAW | CS_VREDRAW;
553 window_class.cbClsExtra = 0;
554 window_class.cbWndExtra = 0;
555 window_class.hInstance = GetModuleHandle(nullptr);
556 window_class.hIcon = nullptr;
557 window_class.hbrBackground = 0;
558 window_class.lpszMenuName = nullptr;
559 window_class.lpfnWndProc = WndProc;
560 RegisterClass(&window_class);
561 return window_class;
562}
563
564LRESULT CALLBACK FlutterWindow::WndProc(HWND const window,
565 UINT const message,
566 WPARAM const wparam,
567 LPARAM const lparam) noexcept {
568 if (message == WM_NCCREATE) {
569 auto cs = reinterpret_cast<CREATESTRUCT*>(lparam);
570 SetWindowLongPtr(window, GWLP_USERDATA,
571 reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
572
573 auto that = static_cast<FlutterWindow*>(cs->lpCreateParams);
574 that->window_handle_ = window;
575 that->text_input_manager_->SetWindowHandle(window);
576 } else if (FlutterWindow* that = GetThisFromHandle(window)) {
577 return that->HandleMessage(message, wparam, lparam);
578 }
579
580 return DefWindowProc(window, message, wparam, lparam);
581}
582
585 WPARAM const wparam,
586 LPARAM const lparam) noexcept {
587 LPARAM result_lparam = lparam;
588 int x_pos = 0, y_pos = 0;
589 UINT width = 0, height = 0;
590 UINT button_pressed = 0;
591 uint64_t flutter_button = 0;
592 FlutterPointerDeviceKind device_kind;
593
594 switch (message) {
595 case kWmDpiChangedBeforeParent:
596 current_dpi_ = GetDpiForHWND(window_handle_);
597 OnDpiScale(current_dpi_);
598 return 0;
599 case WM_SIZE:
600 width = LOWORD(lparam);
601 height = HIWORD(lparam);
602
603 current_width_ = width;
604 current_height_ = height;
605 HandleResize(width, height);
606
607 OnWindowStateEvent(width == 0 && height == 0 ? WindowStateEvent::kHide
609 break;
610 case WM_PAINT:
611 OnPaint();
612 break;
613 case WM_POINTERDOWN:
614 case WM_POINTERUPDATE:
615 case WM_POINTERUP:
616 case WM_POINTERLEAVE: {
617 POINT pt = {GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)};
618 ScreenToClient(window_handle_, &pt);
619 auto const x = static_cast<double>(pt.x);
620 auto const y = static_cast<double>(pt.y);
621 auto const pointerId = GET_POINTERID_WPARAM(wparam);
622 POINTER_INFO pointerInfo;
623 if (windows_proc_table_->GetPointerInfo(pointerId, &pointerInfo)) {
624 UINT32 pressure = 0;
625 UINT32 rotation = 0;
626 bool is_inverted = false;
627 flutter_button =
628 ConvertWinPointerFlagsToFlutterButtons(pointerInfo.pointerFlags);
629 if (pointerInfo.pointerType == PT_PEN) {
630 POINTER_PEN_INFO penInfo;
631 if (windows_proc_table_->GetPointerPenInfo(pointerId, &penInfo)) {
632 pressure = penInfo.pressure;
633 rotation = penInfo.rotation;
634 is_inverted = (penInfo.penFlags & PEN_FLAG_INVERTED) != 0;
635 flutter_button = ConvertWinStylusFlagsToFlutterButtons(
636 penInfo.penFlags, pointerInfo.pointerFlags);
637 }
638 }
639 auto touch_id = touch_id_generator_.GetGeneratedId(pointerId);
641 switch (pointerInfo.pointerType) {
642 case PT_TOUCH:
643 device_kind = kFlutterPointerDeviceKindTouch;
644 break;
645 case PT_PEN:
646 device_kind = is_inverted ? kFlutterPointerDeviceKindInvertedStylus
648 break;
649 case PT_MOUSE:
650 device_kind = kFlutterPointerDeviceKindMouse;
651 break;
652 case PT_TOUCHPAD:
654 break;
655 default:
656 FML_LOG(ERROR) << "Unrecognized device key "
657 << pointerInfo.pointerType;
658 break;
659 }
660 if (message == WM_POINTERDOWN) {
661 OnPointerDown(x, y, device_kind, touch_id, flutter_button, rotation,
662 pressure);
663 } else if (message == WM_POINTERUPDATE) {
664 OnPointerMove(x, y, device_kind, touch_id, flutter_button, rotation,
665 pressure,
666 /* modifiers_state=*/0);
667 } else if (message == WM_POINTERUP) {
668 OnPointerUp(x, y, device_kind, touch_id, flutter_button);
669 // keep tracking the pointer (especially important for stylus)
670 // This allows a stylus to "hover" over the window
671 } else if (message == WM_POINTERLEAVE) {
672 OnPointerLeave(x, y, device_kind, touch_id);
673 touch_id_generator_.ReleaseNumber(pointerId);
674 }
675 }
676 break;
677 }
678 case WM_MOUSEMOVE:
679 device_kind = GetFlutterPointerDeviceKind();
680 if (device_kind == kFlutterPointerDeviceKindMouse) {
681 TrackMouseLeaveEvent(window_handle_);
682
683 x_pos = GET_X_LPARAM(lparam);
684 y_pos = GET_Y_LPARAM(lparam);
685 mouse_x_ = static_cast<double>(x_pos);
686 mouse_y_ = static_cast<double>(y_pos);
687
688 int mods = 0;
689 if (wparam & MK_CONTROL) {
690 mods |= kControl;
691 }
692 if (wparam & MK_SHIFT) {
693 mods |= kShift;
694 }
695 OnPointerMove(mouse_x_, mouse_y_, device_kind, kDefaultPointerDeviceId,
696 ConvertWinMouseStateToFlutterButtons(wparam),
697 /*rotation=*/0, /*pressure=*/0, mods);
698 }
699 break;
700 case WM_MOUSELEAVE:
701 device_kind = GetFlutterPointerDeviceKind();
702 if (device_kind == kFlutterPointerDeviceKindMouse) {
703 OnPointerLeave(mouse_x_, mouse_y_, device_kind,
704 kDefaultPointerDeviceId);
705 }
706
707 // Once the tracked event is received, the TrackMouseEvent function
708 // resets. Set to false to make sure it's called once mouse movement is
709 // detected again.
710 tracking_mouse_leave_ = false;
711 break;
712 case WM_SETCURSOR: {
713 UINT hit_test_result = LOWORD(lparam);
714 if (hit_test_result == HTCLIENT) {
715 // Halt further processing to prevent DefWindowProc from setting the
716 // cursor back to the registered class cursor.
717 return TRUE;
718 }
719 break;
720 }
721 case WM_SETFOCUS:
722 OnWindowStateEvent(WindowStateEvent::kFocus);
723 ::CreateCaret(window_handle_, nullptr, 1, 1);
724 break;
725 case WM_KILLFOCUS:
726 OnWindowStateEvent(WindowStateEvent::kUnfocus);
727 ::DestroyCaret();
728 break;
729 case WM_LBUTTONDOWN:
730 case WM_RBUTTONDOWN:
731 case WM_MBUTTONDOWN:
732 case WM_XBUTTONDOWN:
733 device_kind = GetFlutterPointerDeviceKind();
734 if (device_kind != kFlutterPointerDeviceKindMouse) {
735 break;
736 }
737
738 if (message == WM_LBUTTONDOWN) {
739 // Capture the pointer in case the user drags outside the client area.
740 // In this case, the "mouse leave" event is delayed until the user
741 // releases the button. It's only activated on left click given that
742 // it's more common for apps to handle dragging with only the left
743 // button.
744 SetCapture(window_handle_);
745 }
746 button_pressed = message;
747 if (message == WM_XBUTTONDOWN) {
748 button_pressed = GET_XBUTTON_WPARAM(wparam);
749 }
750 x_pos = GET_X_LPARAM(lparam);
751 y_pos = GET_Y_LPARAM(lparam);
752 flutter_button = ConvertWinButtonToFlutterButton(button_pressed);
753
754 OnPointerDown(static_cast<double>(x_pos), static_cast<double>(y_pos),
755 device_kind, kDefaultPointerDeviceId, flutter_button,
756 /*rotation=*/0, /*pressure=*/0);
757 break;
758 case WM_LBUTTONUP:
759 case WM_RBUTTONUP:
760 case WM_MBUTTONUP:
761 case WM_XBUTTONUP:
762 device_kind = GetFlutterPointerDeviceKind();
763 if (device_kind != kFlutterPointerDeviceKindMouse) {
764 break;
765 }
766
767 if (message == WM_LBUTTONUP) {
768 ReleaseCapture();
769 }
770 button_pressed = message;
771 if (message == WM_XBUTTONUP) {
772 button_pressed = GET_XBUTTON_WPARAM(wparam);
773 }
774 x_pos = GET_X_LPARAM(lparam);
775 y_pos = GET_Y_LPARAM(lparam);
776 flutter_button = ConvertWinButtonToFlutterButton(button_pressed);
777
778 OnPointerUp(static_cast<double>(x_pos), static_cast<double>(y_pos),
779 device_kind, kDefaultPointerDeviceId, flutter_button);
780 break;
781 case WM_MOUSEWHEEL:
782 OnScroll(0.0,
783 -(static_cast<short>(HIWORD(wparam)) /
784 static_cast<double>(WHEEL_DELTA)),
785 kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
786 break;
787 case WM_MOUSEHWHEEL:
788 OnScroll((static_cast<short>(HIWORD(wparam)) /
789 static_cast<double>(WHEEL_DELTA)),
790 0.0, kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
791 break;
792 case WM_GETOBJECT: {
793 LRESULT lresult = OnGetObject(message, wparam, lparam);
794 if (lresult) {
795 return lresult;
796 }
797 break;
798 }
799 case WM_TIMER:
800 if (wparam == kDirectManipulationTimer) {
801 direct_manipulation_owner_->Update();
802 return 0;
803 }
804 break;
805 case DM_POINTERHITTEST: {
806 if (direct_manipulation_owner_) {
807 UINT contact_id = GET_POINTERID_WPARAM(wparam);
808 POINTER_INPUT_TYPE pointer_type;
809 if (windows_proc_table_->GetPointerType(contact_id, &pointer_type) &&
810 pointer_type == PT_TOUCHPAD) {
811 direct_manipulation_owner_->SetContact(contact_id);
812 }
813 }
814 break;
815 }
816 case WM_INPUTLANGCHANGE:
817 // TODO(cbracken): pass this to TextInputManager to aid with
818 // language-specific issues.
819 break;
820 case WM_IME_SETCONTEXT:
821 OnImeSetContext(message, wparam, lparam);
822 // Strip the ISC_SHOWUICOMPOSITIONWINDOW bit from lparam before passing
823 // it to DefWindowProc() so that the composition window is hidden since
824 // Flutter renders the composing string itself.
825 result_lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
826 break;
827 case WM_IME_STARTCOMPOSITION:
828 OnImeStartComposition(message, wparam, lparam);
829 // Suppress further processing by DefWindowProc() so that the default
830 // system IME style isn't used, but rather the one set in the
831 // WM_IME_SETCONTEXT handler.
832 return TRUE;
833 case WM_IME_COMPOSITION:
834 OnImeComposition(message, wparam, lparam);
835 if (lparam & GCS_RESULTSTR || lparam & GCS_COMPSTR) {
836 // Suppress further processing by DefWindowProc() since otherwise it
837 // will emit the result string as WM_CHAR messages on commit. Instead,
838 // committing the composing text to the EditableText string is handled
839 // in TextInputModel::CommitComposing, triggered by
840 // OnImeEndComposition().
841 return TRUE;
842 }
843 break;
844 case WM_IME_ENDCOMPOSITION:
845 OnImeEndComposition(message, wparam, lparam);
846 return TRUE;
847 case WM_IME_REQUEST:
848 OnImeRequest(message, wparam, lparam);
849 break;
850 case WM_UNICHAR: {
851 // Tell third-pary app, we can support Unicode.
852 if (wparam == UNICODE_NOCHAR)
853 return TRUE;
854 // DefWindowProc will send WM_CHAR for this WM_UNICHAR.
855 break;
856 }
857 case WM_THEMECHANGED:
858 OnThemeChange();
859 break;
860 case WM_DEADCHAR:
861 case WM_SYSDEADCHAR:
862 case WM_CHAR:
863 case WM_SYSCHAR:
864 case WM_KEYDOWN:
865 case WM_SYSKEYDOWN:
866 case WM_KEYUP:
867 case WM_SYSKEYUP:
868 if (keyboard_manager_->HandleMessage(message, wparam, lparam)) {
869 return 0;
870 }
871 // Prevent default proc for WM_SYSKEYUP which unfocuses the window
872 // and sends WM_MOUSELEAVE.
873 if (message == WM_SYSKEYUP) {
874 return 0;
875 }
876 break;
877 }
878
879 return Win32DefWindowProc(window_handle_, message, wparam, result_lparam);
880}
881
883 WPARAM const wparam,
884 LPARAM const lparam) {
885 LRESULT reference_result = static_cast<LRESULT>(0L);
886
887 // Only the lower 32 bits of lparam are valid when checking the object id
888 // because it sometimes gets sign-extended incorrectly (but not always).
889 DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lparam));
890
891 bool is_uia_request = static_cast<DWORD>(UiaRootObjectId) == obj_id;
892 bool is_msaa_request = static_cast<DWORD>(OBJID_CLIENT) == obj_id;
893
894 if (is_uia_request || is_msaa_request) {
895 // On Windows, we don't get a notification that the screen reader has been
896 // enabled or disabled. There is an API to query for screen reader state,
897 // but that state isn't set by all screen readers, including by Narrator,
898 // the screen reader that ships with Windows:
899 // https://docs.microsoft.com/en-us/windows/win32/winauto/screen-reader-parameter
900 //
901 // Instead, we enable semantics in Flutter if Windows issues queries for
902 // Microsoft Active Accessibility (MSAA) COM objects.
904 }
905
907 // TODO(schectman): UIA is currently disabled by default.
908 // https://github.com/flutter/flutter/issues/114547
909 if (root_view) {
910 CreateAxFragmentRoot();
911 if (is_uia_request) {
912#ifdef FLUTTER_ENGINE_USE_UIA
913 // Retrieve UIA object for the root view.
914 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
915 if (SUCCEEDED(
916 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
917 IID_PPV_ARGS(&root)))) {
918 // Return the UIA object via UiaReturnRawElementProvider(). See:
919 // https://docs.microsoft.com/en-us/windows/win32/winauto/wm-getobject
920 reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
921 lparam, root.Get());
922 } else {
923 FML_LOG(ERROR) << "Failed to query AX fragment root.";
924 }
925#endif // FLUTTER_ENGINE_USE_UIA
926 } else if (is_msaa_request) {
927 // Create the accessibility root if it does not already exist.
928 // Return the IAccessible for the root view.
929 Microsoft::WRL::ComPtr<IAccessible> root;
930 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
931 IID_PPV_ARGS(&root));
932 reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
933 }
934 }
935 return reference_result;
936}
937
939 WPARAM const wparam,
940 LPARAM const lparam) {
941 if (wparam != 0) {
942 text_input_manager_->CreateImeWindow();
943 }
944}
945
947 WPARAM const wparam,
948 LPARAM const lparam) {
949 text_input_manager_->CreateImeWindow();
951}
952
954 WPARAM const wparam,
955 LPARAM const lparam) {
956 // Update the IME window position.
957 text_input_manager_->UpdateImeWindow();
958
959 if (lparam == 0) {
960 OnComposeChange(u"", 0);
962 }
963
964 // Process GCS_RESULTSTR at fisrt, because Google Japanese Input and ATOK
965 // send both GCS_RESULTSTR and GCS_COMPSTR to commit composed text and send
966 // new composing text.
967 if (lparam & GCS_RESULTSTR) {
968 // Commit but don't end composing.
969 // Read the committed composing string.
970 std::optional<std::u16string> text = text_input_manager_->GetResultString();
971 if (text) {
972 int pos = GetCursorPositionForComposition(*text_input_manager_, lparam,
973 text->length());
974 OnComposeChange(text.value(), pos);
976 }
977 }
978 if (lparam & GCS_COMPSTR) {
979 // Read the in-progress composing string.
980 std::optional<std::u16string> text =
981 text_input_manager_->GetComposingString();
982 if (text) {
983 int pos = GetCursorPositionForComposition(*text_input_manager_, lparam,
984 text->length());
985 OnComposeChange(text.value(), pos);
986 }
987 }
988}
989
991 WPARAM const wparam,
992 LPARAM const lparam) {
993 text_input_manager_->DestroyImeWindow();
994 OnComposeEnd();
995}
996
998 WPARAM const wparam,
999 LPARAM const lparam) {
1000 // TODO(cbracken): Handle IMR_RECONVERTSTRING, IMR_DOCUMENTFEED,
1001 // and IMR_QUERYCHARPOSITION messages.
1002 // https://github.com/flutter/flutter/issues/74547
1003}
1004
1006 text_input_manager_->AbortComposing();
1007}
1008
1010 text_input_manager_->UpdateCaretRect(rect);
1011}
1012
1014 return current_dpi_;
1015}
1016
1018 return current_width_;
1019}
1020
1022 return current_height_;
1023}
1024
1026 return scroll_offset_multiplier_;
1027}
1028
1030 UINT Msg,
1031 WPARAM wParam,
1032 LPARAM lParam) {
1033 return ::DefWindowProc(hWnd, Msg, wParam, lParam);
1034}
1035
1036void FlutterWindow::Destroy() {
1037 if (window_handle_) {
1038 text_input_manager_->SetWindowHandle(nullptr);
1039 DestroyWindow(window_handle_);
1040 window_handle_ = nullptr;
1041 }
1042
1043 UnregisterClass(window_class_name_.c_str(), nullptr);
1044}
1045
1046void FlutterWindow::CreateAxFragmentRoot() {
1047 if (ax_fragment_root_) {
1048 return;
1049 }
1050 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
1051 window_handle_, GetAxFragmentRootDelegate());
1053 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
1054 ui::AXPlatformNode* alert_node =
1055 ui::AXPlatformNodeWin::Create(alert_delegate_.get());
1056 alert_node_.reset(static_cast<ui::AXPlatformNodeWin*>(alert_node));
1057 ax_fragment_root_->SetAlertNode(alert_node_.get());
1058}
1059
1060} // namespace flutter
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 OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons, uint32_t rotation, uint32_t pressure)
virtual void OnImeRequest(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons, uint32_t rotation, uint32_t pressure, int modifiers_state)
virtual LRESULT Win32DefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
virtual void OnImeStartComposition(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons)
std::wstring NarrowToWide(const char *source)
virtual FlutterEngineDisplayId GetDisplayId() override
virtual ui::AXFragmentRootDelegateWin * GetAxFragmentRootDelegate()
virtual void OnScroll(double delta_x, double delta_y, FlutterPointerDeviceKind device_kind, int32_t device_id)
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 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
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
double top() const
Definition geometry.h:64
double height() const
Definition geometry.h:68
double left() const
Definition geometry.h:63
double width() const
Definition geometry.h:67
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 OnWindowStateEvent(HWND hwnd, WindowStateEvent event)=0
virtual gfx::NativeViewAccessible GetNativeViewAccessible()=0
virtual bool OnWindowSizeChanged(size_t width, size_t height)=0
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons)=0
virtual void OnFocus(FlutterViewFocusState focus_state, FlutterViewFocusDirection direction)=0
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons, uint32_t rotation, uint32_t pressure)=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 OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint64_t buttons, uint32_t rotation, uint32_t pressure, int modifiers_state)=0
int32_t x
uint64_t FlutterEngineDisplayId
Definition embedder.h:1050
@ kUnfocused
Specifies that a view does not have platform focus.
Definition embedder.h:1221
@ kFocused
Specifies that a view has platform focus.
Definition embedder.h:1224
@ kUndefined
Definition embedder.h:1205
@ kFlutterPointerButtonMousePrimary
Definition embedder.h:1320
@ kFlutterPointerButtonMouseMiddle
Definition embedder.h:1322
@ kFlutterPointerButtonMouseForward
Definition embedder.h:1324
@ kFlutterPointerButtonMouseBack
Definition embedder.h:1323
@ kFlutterPointerButtonMouseSecondary
Definition embedder.h:1321
@ kFlutterPointerButtonStylusSecondary
Definition embedder.h:1341
@ kFlutterPointerButtonStylusPrimary
Definition embedder.h:1338
@ kFlutterPointerButtonStylusContact
Definition embedder.h:1335
FlutterPointerDeviceKind
The device type that created a pointer event.
Definition embedder.h:1309
@ kFlutterPointerDeviceKindTouch
Definition embedder.h:1311
@ kFlutterPointerDeviceKindInvertedStylus
Definition embedder.h:1314
@ kFlutterPointerDeviceKindTrackpad
Definition embedder.h:1313
@ kFlutterPointerDeviceKindStylus
Definition embedder.h:1312
@ kFlutterPointerDeviceKindMouse
Definition embedder.h:1310
GLFWwindow * window
Definition main.cc:60
const char * message
return TRUE
const uint8_t uint32_t uint32_t GError ** error
static const int kMaxTouchDeviceId
static const int kMinTouchDeviceId
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
size_t length
std::u16string text
double y
constexpr int kShift
UINT GetDpiForHWND(HWND hwnd)
Definition dpi_utils.cc:128
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all 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
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 use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
constexpr int kControl
UnimplementedNativeViewAccessible * NativeViewAccessible
Definition ref_ptr.h:261
const DisplayManagerWin32 * display_manager
int32_t height
int32_t width
int BOOL
struct tagMSG * LPMSG
#define SUCCEEDED(hr)
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
__w64 long LONG_PTR
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)
UINT_PTR WPARAM
unsigned long DWORD
#define CALLBACK