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