Flutter Engine
 
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 int modifiers_state) {
173 binding_handler_delegate_->OnPointerMove(x, y, device_kind, device_id,
174 modifiers_state);
175}
176
178 double y,
179 FlutterPointerDeviceKind device_kind,
180 int32_t device_id,
181 UINT button) {
182 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
183 if (flutter_button != 0) {
184 binding_handler_delegate_->OnPointerDown(
185 x, y, device_kind, device_id,
186 static_cast<FlutterPointerMouseButtons>(flutter_button));
187 }
188}
189
191 double y,
192 FlutterPointerDeviceKind device_kind,
193 int32_t device_id,
194 UINT button) {
195 uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
196 if (flutter_button != 0) {
197 binding_handler_delegate_->OnPointerUp(
198 x, y, device_kind, device_id,
199 static_cast<FlutterPointerMouseButtons>(flutter_button));
200 }
201}
202
204 double y,
205 FlutterPointerDeviceKind device_kind,
206 int32_t device_id) {
207 binding_handler_delegate_->OnPointerLeave(x, y, device_kind, device_id);
208}
209
210void FlutterWindow::OnText(const std::u16string& text) {
211 binding_handler_delegate_->OnText(text);
212}
213
215 int scancode,
216 int action,
217 char32_t character,
218 bool extended,
219 bool was_down,
221 binding_handler_delegate_->OnKey(key, scancode, action, character, extended,
222 was_down, std::move(callback));
223}
224
226 binding_handler_delegate_->OnComposeBegin();
227}
228
230 binding_handler_delegate_->OnComposeCommit();
231}
232
234 binding_handler_delegate_->OnComposeEnd();
235}
236
237void FlutterWindow::OnComposeChange(const std::u16string& text,
238 int cursor_pos) {
239 binding_handler_delegate_->OnComposeChange(text, cursor_pos);
240}
241
243 binding_handler_delegate_->OnUpdateSemanticsEnabled(enabled);
244}
245
246void FlutterWindow::OnScroll(double delta_x,
247 double delta_y,
248 FlutterPointerDeviceKind device_kind,
249 int32_t device_id) {
250 POINT point;
251 GetCursorPos(&point);
252
253 ScreenToClient(GetWindowHandle(), &point);
254 binding_handler_delegate_->OnScroll(point.x, point.y, delta_x, delta_y,
255 GetScrollOffsetMultiplier(), device_kind,
256 device_id);
257}
258
260 // Convert the rect from Flutter logical coordinates to device coordinates.
261 auto scale = GetDpiScale();
262 Point origin(rect.left() * scale, rect.top() * scale);
263 Size size(rect.width() * scale, rect.height() * scale);
264 UpdateCursorRect(Rect(origin, size));
265}
266
270
272 HDC dc = ::GetDC(GetWindowHandle());
273 bool result = ::PatBlt(dc, 0, 0, current_width_, current_height_, BLACKNESS);
274 ::ReleaseDC(GetWindowHandle(), dc);
275 return result;
276}
277
278bool FlutterWindow::OnBitmapSurfaceUpdated(const void* allocation,
279 size_t row_bytes,
280 size_t height) {
281 HDC dc = ::GetDC(GetWindowHandle());
282 BITMAPINFO bmi = {};
283 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
284 bmi.bmiHeader.biWidth = row_bytes / 4;
285 bmi.bmiHeader.biHeight = -height;
286 bmi.bmiHeader.biPlanes = 1;
287 bmi.bmiHeader.biBitCount = 32;
288 bmi.bmiHeader.biCompression = BI_RGB;
289 bmi.bmiHeader.biSizeImage = 0;
290 int ret = ::SetDIBitsToDevice(dc, 0, 0, row_bytes / 4, height, 0, 0, 0,
291 height, allocation, &bmi, DIB_RGB_COLORS);
292 ::ReleaseDC(GetWindowHandle(), dc);
293 return ret != 0;
294}
295
297 if (binding_handler_delegate_ == nullptr) {
298 return nullptr;
299 }
300
301 return binding_handler_delegate_->GetNativeViewAccessible();
302}
303
305 POINT point;
306 GetCursorPos(&point);
307 ScreenToClient(GetWindowHandle(), &point);
308 return {(size_t)point.x, (size_t)point.y};
309}
310
312 FlutterEngineDisplayId const display_id =
313 reinterpret_cast<FlutterEngineDisplayId>(
314 MonitorFromWindow(GetWindowHandle(), MONITOR_DEFAULTTONEAREST));
315 if (!display_manager_->FindById(display_id)) {
316 FML_LOG(ERROR) << "Current monitor not found in display list.";
317 }
318 return display_id;
319}
320
322 binding_handler_delegate_->OnHighContrastChanged();
323}
324
328
330 CreateAxFragmentRoot();
331 return alert_delegate_.get();
332}
333
334ui::AXPlatformNodeWin* FlutterWindow::GetAlert() {
335 CreateAxFragmentRoot();
336 return alert_node_.get();
337}
338
340 switch (event) {
342 restored_ = true;
343 break;
345 restored_ = false;
346 focused_ = false;
347 break;
349 focused_ = true;
350 if (binding_handler_delegate_) {
351 binding_handler_delegate_->OnFocus(
354 }
355 break;
357 focused_ = false;
358 if (binding_handler_delegate_) {
359 binding_handler_delegate_->OnFocus(
362 }
363 break;
364 }
365 HWND hwnd = GetWindowHandle();
366 if (hwnd && binding_handler_delegate_) {
367 binding_handler_delegate_->OnWindowStateEvent(hwnd, event);
368 }
369}
370
371void FlutterWindow::TrackMouseLeaveEvent(HWND hwnd) {
372 if (!tracking_mouse_leave_) {
373 TRACKMOUSEEVENT tme;
374 tme.cbSize = sizeof(tme);
375 tme.hwndTrack = hwnd;
376 tme.dwFlags = TME_LEAVE;
377 TrackMouseEvent(&tme);
378 tracking_mouse_leave_ = true;
379 }
380}
381
382void FlutterWindow::HandleResize(UINT width, UINT height) {
383 current_width_ = width;
384 current_height_ = height;
386 direct_manipulation_owner_->ResizeViewport(width, height);
387 }
389}
390
391FlutterWindow* FlutterWindow::GetThisFromHandle(HWND const window) noexcept {
392 return reinterpret_cast<FlutterWindow*>(
393 GetWindowLongPtr(window, GWLP_USERDATA));
394}
395
396void FlutterWindow::UpdateScrollOffsetMultiplier() {
397 UINT lines_per_scroll = kLinesPerScrollWindowsDefault;
398
399 // Get lines per scroll wheel value from Windows
400 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_scroll, 0);
401
402 // This logic is based off Chromium's implementation
403 // https://source.chromium.org/chromium/chromium/src/+/main:ui/events/blink/web_input_event_builders_win.cc;l=319-331
404 scroll_offset_multiplier_ =
405 static_cast<float>(lines_per_scroll) * 100.0 / 3.0;
406}
407
408void FlutterWindow::InitializeChild(const char* title,
409 unsigned int width,
410 unsigned int height) {
411 Destroy();
412 std::wstring converted_title = NarrowToWide(title);
413
414 WNDCLASS window_class = RegisterWindowClass(converted_title);
415
416 auto* result = CreateWindowEx(
417 0, window_class.lpszClassName, converted_title.c_str(),
418 WS_CHILD | WS_VISIBLE, CW_DEFAULT, CW_DEFAULT, width, height,
419 HWND_MESSAGE, nullptr, window_class.hInstance, this);
420
421 if (result == nullptr) {
422 auto error = GetLastError();
423 LPWSTR message = nullptr;
424 size_t size = FormatMessageW(
425 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
426 FORMAT_MESSAGE_IGNORE_INSERTS,
427 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
428 reinterpret_cast<LPWSTR>(&message), 0, NULL);
429 OutputDebugString(message);
430 LocalFree(message);
431 }
432 SetUserObjectInformationA(GetCurrentProcess(),
433 UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
434 // SetTimer is not precise, if a 16 ms interval is requested, it will instead
435 // often fire in an interval of 32 ms. Providing a value of 14 will ensure it
436 // runs every 16 ms, which will allow for 60 Hz trackpad gesture events, which
437 // is the maximal frequency supported by SetTimer.
438 SetTimer(result, kDirectManipulationTimer, 14, nullptr);
439 direct_manipulation_owner_ = std::make_unique<DirectManipulationOwner>(this);
441}
442
444 return window_handle_;
445}
446
448 UINT wMsgFilterMin,
449 UINT wMsgFilterMax,
450 UINT wRemoveMsg) {
451 return ::PeekMessage(lpMsg, window_handle_, wMsgFilterMin, wMsgFilterMax,
452 wRemoveMsg);
453}
454
455uint32_t FlutterWindow::Win32MapVkToChar(uint32_t virtual_key) {
456 return ::MapVirtualKey(virtual_key, MAPVK_VK_TO_CHAR);
457}
458
460 WPARAM wParam,
461 LPARAM lParam) {
462 return ::SendMessage(window_handle_, Msg, wParam, lParam);
463}
464
465std::wstring FlutterWindow::NarrowToWide(const char* source) {
466 size_t length = strlen(source);
467 size_t outlen = 0;
468 std::wstring wideTitle(length, L'#');
469 mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length);
470 return wideTitle;
471}
472
473WNDCLASS FlutterWindow::RegisterWindowClass(std::wstring& title) {
474 window_class_name_ = title;
475
476 WNDCLASS window_class{};
477 window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
478 window_class.lpszClassName = title.c_str();
479 window_class.style = CS_HREDRAW | CS_VREDRAW;
480 window_class.cbClsExtra = 0;
481 window_class.cbWndExtra = 0;
482 window_class.hInstance = GetModuleHandle(nullptr);
483 window_class.hIcon = nullptr;
484 window_class.hbrBackground = 0;
485 window_class.lpszMenuName = nullptr;
486 window_class.lpfnWndProc = WndProc;
487 RegisterClass(&window_class);
488 return window_class;
489}
490
491LRESULT CALLBACK FlutterWindow::WndProc(HWND const window,
492 UINT const message,
493 WPARAM const wparam,
494 LPARAM const lparam) noexcept {
495 if (message == WM_NCCREATE) {
496 auto cs = reinterpret_cast<CREATESTRUCT*>(lparam);
497 SetWindowLongPtr(window, GWLP_USERDATA,
498 reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
499
500 auto that = static_cast<FlutterWindow*>(cs->lpCreateParams);
501 that->window_handle_ = window;
502 that->text_input_manager_->SetWindowHandle(window);
503 RegisterTouchWindow(window, 0);
504 } else if (FlutterWindow* that = GetThisFromHandle(window)) {
505 return that->HandleMessage(message, wparam, lparam);
506 }
507
508 return DefWindowProc(window, message, wparam, lparam);
509}
510
513 WPARAM const wparam,
514 LPARAM const lparam) noexcept {
515 LPARAM result_lparam = lparam;
516 int xPos = 0, yPos = 0;
517 UINT width = 0, height = 0;
518 UINT button_pressed = 0;
519 FlutterPointerDeviceKind device_kind;
520
521 switch (message) {
522 case kWmDpiChangedBeforeParent:
523 current_dpi_ = GetDpiForHWND(window_handle_);
524 OnDpiScale(current_dpi_);
525 return 0;
526 case WM_SIZE:
527 width = LOWORD(lparam);
528 height = HIWORD(lparam);
529
530 current_width_ = width;
531 current_height_ = height;
532 HandleResize(width, height);
533
534 OnWindowStateEvent(width == 0 && height == 0 ? WindowStateEvent::kHide
536 break;
537 case WM_PAINT:
538 OnPaint();
539 break;
540 case WM_TOUCH: {
541 UINT num_points = LOWORD(wparam);
542 touch_points_.resize(num_points);
543 auto touch_input_handle = reinterpret_cast<HTOUCHINPUT>(lparam);
544 if (GetTouchInputInfo(touch_input_handle, num_points,
545 touch_points_.data(), sizeof(TOUCHINPUT))) {
546 for (const auto& touch : touch_points_) {
547 // Generate a mapped ID for the Windows-provided touch ID
548 auto touch_id = touch_id_generator_.GetGeneratedId(touch.dwID);
549
550 POINT pt = {TOUCH_COORD_TO_PIXEL(touch.x),
551 TOUCH_COORD_TO_PIXEL(touch.y)};
552 ScreenToClient(window_handle_, &pt);
553 auto x = static_cast<double>(pt.x);
554 auto y = static_cast<double>(pt.y);
555
556 if (touch.dwFlags & TOUCHEVENTF_DOWN) {
557 OnPointerDown(x, y, kFlutterPointerDeviceKindTouch, touch_id,
558 WM_LBUTTONDOWN);
559 } else if (touch.dwFlags & TOUCHEVENTF_MOVE) {
560 OnPointerMove(x, y, kFlutterPointerDeviceKindTouch, touch_id, 0);
561 } else if (touch.dwFlags & TOUCHEVENTF_UP) {
562 OnPointerUp(x, y, kFlutterPointerDeviceKindTouch, touch_id,
563 WM_LBUTTONDOWN);
564 OnPointerLeave(x, y, kFlutterPointerDeviceKindTouch, touch_id);
565 touch_id_generator_.ReleaseNumber(touch.dwID);
566 }
567 }
568 CloseTouchInputHandle(touch_input_handle);
569 }
570 return 0;
571 }
572 case WM_MOUSEMOVE:
573 device_kind = GetFlutterPointerDeviceKind();
574 if (device_kind == kFlutterPointerDeviceKindMouse) {
575 TrackMouseLeaveEvent(window_handle_);
576
577 xPos = GET_X_LPARAM(lparam);
578 yPos = GET_Y_LPARAM(lparam);
579 mouse_x_ = static_cast<double>(xPos);
580 mouse_y_ = static_cast<double>(yPos);
581
582 int mods = 0;
583 if (wparam & MK_CONTROL) {
584 mods |= kControl;
585 }
586 if (wparam & MK_SHIFT) {
587 mods |= kShift;
588 }
589 OnPointerMove(mouse_x_, mouse_y_, device_kind, kDefaultPointerDeviceId,
590 mods);
591 }
592 break;
593 case WM_MOUSELEAVE:
594 device_kind = GetFlutterPointerDeviceKind();
595 if (device_kind == kFlutterPointerDeviceKindMouse) {
596 OnPointerLeave(mouse_x_, mouse_y_, device_kind,
597 kDefaultPointerDeviceId);
598 }
599
600 // Once the tracked event is received, the TrackMouseEvent function
601 // resets. Set to false to make sure it's called once mouse movement is
602 // detected again.
603 tracking_mouse_leave_ = false;
604 break;
605 case WM_SETCURSOR: {
606 UINT hit_test_result = LOWORD(lparam);
607 if (hit_test_result == HTCLIENT) {
608 // Halt further processing to prevent DefWindowProc from setting the
609 // cursor back to the registered class cursor.
610 return TRUE;
611 }
612 break;
613 }
614 case WM_SETFOCUS:
615 OnWindowStateEvent(WindowStateEvent::kFocus);
616 ::CreateCaret(window_handle_, nullptr, 1, 1);
617 break;
618 case WM_KILLFOCUS:
619 OnWindowStateEvent(WindowStateEvent::kUnfocus);
620 ::DestroyCaret();
621 break;
622 case WM_LBUTTONDOWN:
623 case WM_RBUTTONDOWN:
624 case WM_MBUTTONDOWN:
625 case WM_XBUTTONDOWN:
626 device_kind = GetFlutterPointerDeviceKind();
627 if (device_kind != kFlutterPointerDeviceKindMouse) {
628 break;
629 }
630
631 if (message == WM_LBUTTONDOWN) {
632 // Capture the pointer in case the user drags outside the client area.
633 // In this case, the "mouse leave" event is delayed until the user
634 // releases the button. It's only activated on left click given that
635 // it's more common for apps to handle dragging with only the left
636 // button.
637 SetCapture(window_handle_);
638 }
639 button_pressed = message;
640 if (message == WM_XBUTTONDOWN) {
641 button_pressed = GET_XBUTTON_WPARAM(wparam);
642 }
643 xPos = GET_X_LPARAM(lparam);
644 yPos = GET_Y_LPARAM(lparam);
645 OnPointerDown(static_cast<double>(xPos), static_cast<double>(yPos),
646 device_kind, kDefaultPointerDeviceId, button_pressed);
647 break;
648 case WM_LBUTTONUP:
649 case WM_RBUTTONUP:
650 case WM_MBUTTONUP:
651 case WM_XBUTTONUP:
652 device_kind = GetFlutterPointerDeviceKind();
653 if (device_kind != kFlutterPointerDeviceKindMouse) {
654 break;
655 }
656
657 if (message == WM_LBUTTONUP) {
658 ReleaseCapture();
659 }
660 button_pressed = message;
661 if (message == WM_XBUTTONUP) {
662 button_pressed = GET_XBUTTON_WPARAM(wparam);
663 }
664 xPos = GET_X_LPARAM(lparam);
665 yPos = GET_Y_LPARAM(lparam);
666 OnPointerUp(static_cast<double>(xPos), static_cast<double>(yPos),
667 device_kind, kDefaultPointerDeviceId, button_pressed);
668 break;
669 case WM_MOUSEWHEEL:
670 OnScroll(0.0,
671 -(static_cast<short>(HIWORD(wparam)) /
672 static_cast<double>(WHEEL_DELTA)),
673 kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
674 break;
675 case WM_MOUSEHWHEEL:
676 OnScroll((static_cast<short>(HIWORD(wparam)) /
677 static_cast<double>(WHEEL_DELTA)),
678 0.0, kFlutterPointerDeviceKindMouse, kDefaultPointerDeviceId);
679 break;
680 case WM_GETOBJECT: {
681 LRESULT lresult = OnGetObject(message, wparam, lparam);
682 if (lresult) {
683 return lresult;
684 }
685 break;
686 }
687 case WM_TIMER:
688 if (wparam == kDirectManipulationTimer) {
689 direct_manipulation_owner_->Update();
690 return 0;
691 }
692 break;
693 case DM_POINTERHITTEST: {
694 if (direct_manipulation_owner_) {
695 UINT contact_id = GET_POINTERID_WPARAM(wparam);
696 POINTER_INPUT_TYPE pointer_type;
697 if (windows_proc_table_->GetPointerType(contact_id, &pointer_type) &&
698 pointer_type == PT_TOUCHPAD) {
699 direct_manipulation_owner_->SetContact(contact_id);
700 }
701 }
702 break;
703 }
704 case WM_INPUTLANGCHANGE:
705 // TODO(cbracken): pass this to TextInputManager to aid with
706 // language-specific issues.
707 break;
708 case WM_IME_SETCONTEXT:
709 OnImeSetContext(message, wparam, lparam);
710 // Strip the ISC_SHOWUICOMPOSITIONWINDOW bit from lparam before passing it
711 // to DefWindowProc() so that the composition window is hidden since
712 // Flutter renders the composing string itself.
713 result_lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
714 break;
715 case WM_IME_STARTCOMPOSITION:
716 OnImeStartComposition(message, wparam, lparam);
717 // Suppress further processing by DefWindowProc() so that the default
718 // system IME style isn't used, but rather the one set in the
719 // WM_IME_SETCONTEXT handler.
720 return TRUE;
721 case WM_IME_COMPOSITION:
722 OnImeComposition(message, wparam, lparam);
723 if (lparam & GCS_RESULTSTR || lparam & GCS_COMPSTR) {
724 // Suppress further processing by DefWindowProc() since otherwise it
725 // will emit the result string as WM_CHAR messages on commit. Instead,
726 // committing the composing text to the EditableText string is handled
727 // in TextInputModel::CommitComposing, triggered by
728 // OnImeEndComposition().
729 return TRUE;
730 }
731 break;
732 case WM_IME_ENDCOMPOSITION:
733 OnImeEndComposition(message, wparam, lparam);
734 return TRUE;
735 case WM_IME_REQUEST:
736 OnImeRequest(message, wparam, lparam);
737 break;
738 case WM_UNICHAR: {
739 // Tell third-pary app, we can support Unicode.
740 if (wparam == UNICODE_NOCHAR)
741 return TRUE;
742 // DefWindowProc will send WM_CHAR for this WM_UNICHAR.
743 break;
744 }
745 case WM_THEMECHANGED:
746 OnThemeChange();
747 break;
748 case WM_DEADCHAR:
749 case WM_SYSDEADCHAR:
750 case WM_CHAR:
751 case WM_SYSCHAR:
752 case WM_KEYDOWN:
753 case WM_SYSKEYDOWN:
754 case WM_KEYUP:
755 case WM_SYSKEYUP:
756 if (keyboard_manager_->HandleMessage(message, wparam, lparam)) {
757 return 0;
758 }
759 break;
760 }
761
762 return Win32DefWindowProc(window_handle_, message, wparam, result_lparam);
763}
764
766 WPARAM const wparam,
767 LPARAM const lparam) {
768 LRESULT reference_result = static_cast<LRESULT>(0L);
769
770 // Only the lower 32 bits of lparam are valid when checking the object id
771 // because it sometimes gets sign-extended incorrectly (but not always).
772 DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lparam));
773
774 bool is_uia_request = static_cast<DWORD>(UiaRootObjectId) == obj_id;
775 bool is_msaa_request = static_cast<DWORD>(OBJID_CLIENT) == obj_id;
776
777 if (is_uia_request || is_msaa_request) {
778 // On Windows, we don't get a notification that the screen reader has been
779 // enabled or disabled. There is an API to query for screen reader state,
780 // but that state isn't set by all screen readers, including by Narrator,
781 // the screen reader that ships with Windows:
782 // https://docs.microsoft.com/en-us/windows/win32/winauto/screen-reader-parameter
783 //
784 // Instead, we enable semantics in Flutter if Windows issues queries for
785 // Microsoft Active Accessibility (MSAA) COM objects.
787 }
788
790 // TODO(schectman): UIA is currently disabled by default.
791 // https://github.com/flutter/flutter/issues/114547
792 if (root_view) {
793 CreateAxFragmentRoot();
794 if (is_uia_request) {
795#ifdef FLUTTER_ENGINE_USE_UIA
796 // Retrieve UIA object for the root view.
797 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
798 if (SUCCEEDED(
799 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
800 IID_PPV_ARGS(&root)))) {
801 // Return the UIA object via UiaReturnRawElementProvider(). See:
802 // https://docs.microsoft.com/en-us/windows/win32/winauto/wm-getobject
803 reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
804 lparam, root.Get());
805 } else {
806 FML_LOG(ERROR) << "Failed to query AX fragment root.";
807 }
808#endif // FLUTTER_ENGINE_USE_UIA
809 } else if (is_msaa_request) {
810 // Create the accessibility root if it does not already exist.
811 // Return the IAccessible for the root view.
812 Microsoft::WRL::ComPtr<IAccessible> root;
813 ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
814 IID_PPV_ARGS(&root));
815 reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
816 }
817 }
818 return reference_result;
819}
820
822 WPARAM const wparam,
823 LPARAM const lparam) {
824 if (wparam != 0) {
825 text_input_manager_->CreateImeWindow();
826 }
827}
828
830 WPARAM const wparam,
831 LPARAM const lparam) {
832 text_input_manager_->CreateImeWindow();
834}
835
837 WPARAM const wparam,
838 LPARAM const lparam) {
839 // Update the IME window position.
840 text_input_manager_->UpdateImeWindow();
841
842 if (lparam == 0) {
843 OnComposeChange(u"", 0);
845 }
846
847 // Process GCS_RESULTSTR at fisrt, because Google Japanese Input and ATOK send
848 // both GCS_RESULTSTR and GCS_COMPSTR to commit composed text and send new
849 // composing text.
850 if (lparam & GCS_RESULTSTR) {
851 // Commit but don't end composing.
852 // Read the committed composing string.
853 long pos = text_input_manager_->GetComposingCursorPosition();
854 std::optional<std::u16string> text = text_input_manager_->GetResultString();
855 if (text) {
856 OnComposeChange(text.value(), pos);
858 }
859 }
860 if (lparam & GCS_COMPSTR) {
861 // Read the in-progress composing string.
862 long pos = text_input_manager_->GetComposingCursorPosition();
863 std::optional<std::u16string> text =
864 text_input_manager_->GetComposingString();
865 if (text) {
866 OnComposeChange(text.value(), pos);
867 }
868 }
869}
870
872 WPARAM const wparam,
873 LPARAM const lparam) {
874 text_input_manager_->DestroyImeWindow();
875 OnComposeEnd();
876}
877
879 WPARAM const wparam,
880 LPARAM const lparam) {
881 // TODO(cbracken): Handle IMR_RECONVERTSTRING, IMR_DOCUMENTFEED,
882 // and IMR_QUERYCHARPOSITION messages.
883 // https://github.com/flutter/flutter/issues/74547
884}
885
887 text_input_manager_->AbortComposing();
888}
889
891 text_input_manager_->UpdateCaretRect(rect);
892}
893
895 return current_dpi_;
896}
897
899 return current_width_;
900}
901
903 return current_height_;
904}
905
907 return scroll_offset_multiplier_;
908}
909
911 UINT Msg,
912 WPARAM wParam,
913 LPARAM lParam) {
914 return ::DefWindowProc(hWnd, Msg, wParam, lParam);
915}
916
917void FlutterWindow::Destroy() {
918 if (window_handle_) {
919 text_input_manager_->SetWindowHandle(nullptr);
920 DestroyWindow(window_handle_);
921 window_handle_ = nullptr;
922 }
923
924 UnregisterClass(window_class_name_.c_str(), nullptr);
925}
926
927void FlutterWindow::CreateAxFragmentRoot() {
928 if (ax_fragment_root_) {
929 return;
930 }
931 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
932 window_handle_, GetAxFragmentRootDelegate());
934 std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
935 ui::AXPlatformNode* alert_node =
936 ui::AXPlatformNodeWin::Create(alert_delegate_.get());
937 alert_node_.reset(static_cast<ui::AXPlatformNodeWin*>(alert_node));
938 ax_fragment_root_->SetAlertNode(alert_node_.get());
939}
940
941} // 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 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 OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
virtual void OnComposeCommit()
virtual void UpdateCursorRect(const Rect &rect)
virtual PhysicalWindowBounds GetPhysicalWindowBounds() override
virtual void OnImeSetContext(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual void OnWindowStateEvent(WindowStateEvent event)
virtual void OnKey(int key, int scancode, int action, char32_t character, bool extended, bool was_down, KeyEventCallback callback) override
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state)
std::unique_ptr< ui::AXPlatformNodeWin > alert_node_
virtual PointerLocation GetPrimaryPointerLocation() override
virtual void OnComposeEnd()
virtual void SetView(WindowBindingHandlerDelegate *view) override
LRESULT HandleMessage(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
virtual void OnComposeChange(const std::u16string &text, int cursor_pos)
virtual void OnResetImeComposing() override
virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) override
virtual void OnImeComposition(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual void OnDpiScale(unsigned int dpi)
virtual void AbortImeComposing()
virtual float GetDpiScale() override
virtual void OnUpdateSemanticsEnabled(bool enabled)
virtual LRESULT OnGetObject(UINT const message, WPARAM const wparam, LPARAM const lparam)
virtual gfx::NativeViewAccessible GetNativeViewAccessible()
virtual void OnComposeBegin()
virtual bool OnBitmapSurfaceCleared() override
virtual void OnResize(unsigned int width, unsigned int height)
virtual void OnImeEndComposition(UINT const message, WPARAM const wparam, LPARAM const lparam)
std::function< void(bool)> KeyEventCallback
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 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 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
int32_t x
uint64_t FlutterEngineDisplayId
Definition embedder.h:1043
@ kUnfocused
Specifies that a view does not have platform focus.
Definition embedder.h:1191
@ kFocused
Specifies that a view has platform focus.
Definition embedder.h:1194
@ kUndefined
Definition embedder.h:1175
FlutterPointerMouseButtons
Definition embedder.h:1288
@ kFlutterPointerButtonMousePrimary
Definition embedder.h:1289
@ kFlutterPointerButtonMouseMiddle
Definition embedder.h:1291
@ kFlutterPointerButtonMouseForward
Definition embedder.h:1293
@ kFlutterPointerButtonMouseBack
Definition embedder.h:1292
@ kFlutterPointerButtonMouseSecondary
Definition embedder.h:1290
FlutterPointerDeviceKind
The device type that created a pointer event.
Definition embedder.h:1279
@ kFlutterPointerDeviceKindTouch
Definition embedder.h:1281
@ kFlutterPointerDeviceKindStylus
Definition embedder.h:1282
@ kFlutterPointerDeviceKindMouse
Definition embedder.h:1280
GLFWwindow * window
Definition main.cc:60
return TRUE
G_BEGIN_DECLS GBytes * message
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