| Flutter Windows Embedder |

Classes | Public Member Functions | Static Public Member Functions | List of all members
flutter::HostWindow Class Reference

`#include <host_window.h>`

## Public Member Functions
virtual | ~HostWindow ()
HWND | GetWindowHandle () const
void | SetContentSize (const WindowSizeRequest &size)
void | SetConstraints (const WindowConstraints &constraints)
void | SetFullscreen (bool fullscreen, std::optional< FlutterEngineDisplayId > display_id)
bool | GetFullscreen () const

## Static Public Member Functions
static std::unique_ptr< HostWindow > | CreateRegularWindow (WindowManager *controller, FlutterWindowsEngine *engine, const WindowSizeRequest &preferred_size, const WindowConstraints &preferred_constraints, LPCWSTR title)
static HostWindow * | GetThisFromHandle (HWND hwnd)
static ActualWindowSize | GetWindowContentSize (HWND hwnd)

## Detailed Description

Definition at line 26 of file host_window.h.

## Constructor & Destructor Documentation

## ◆ ~HostWindow()

flutter::HostWindow::~HostWindow | ( |  | ) |
| virtual

Definition at line 385 of file host_window.cc.

385 {
386 if (view_controller_) {
387 // Unregister the window class. Fail silently if other windows are still
388 // using the class, as only the last window can successfully unregister it.
389 if (!UnregisterClass(kWindowClassName, GetModuleHandle(nullptr))) {
390 // Clear the error state after the failed unregistration.
391 SetLastError(ERROR_SUCCESS);
392 }
393 }
394 }

## Member Function Documentation

## ◆ CreateRegularWindow()

std::unique_ptr< HostWindow > flutter::HostWindow::CreateRegularWindow | ( | WindowManager * | controller,
|  | FlutterWindowsEngine * | engine,
|  | const WindowSizeRequest & | preferred_size,
|  | const WindowConstraints & | preferred_constraints,
|  | LPCWSTR | title
| ) |  |
| static

Definition at line 257 of file host_window.cc.

262 {
263 DWORD window_style = WS_OVERLAPPEDWINDOW;
264 DWORD extended_window_style = 0;
265 auto const box_constraints = FromWindowConstraints(preferred_constraints);
266
267 // TODO(knopp): What about windows sized to content?
268 FML_CHECK(preferred_size.has_preferred_view_size);
269
270 // Calculate the screen space window rectangle for the new window.
271 // Default positioning values (CW_USEDEFAULT) are used
272 // if the window has no owner.
273 Rect const initial_window_rect = [&]() -> Rect {
274 std::optional<Size> const window_size = GetWindowSizeForClientSize(
275 *engine->windows_proc_table(),
276 Size(preferred_size.preferred_view_width,
277 preferred_size.preferred_view_height),
278 box_constraints.smallest(), box_constraints.biggest(), window_style,
279 extended_window_style, nullptr);
280 return {{CW_USEDEFAULT, CW_USEDEFAULT},
281 window_size ? *window_size : Size{CW_USEDEFAULT, CW_USEDEFAULT}};
282 }();
283
284 // Set up the view.
285 auto view_window = std::make_unique<FlutterWindow>(
286 initial_window_rect.width(), initial_window_rect.height(),
287 engine->display_manager(), engine->windows_proc_table());
288
289 std::unique_ptr<FlutterWindowsView> view =
290 engine->CreateView(std::move(view_window));
291 if (view == nullptr) {
292 FML_LOG(ERROR) << "Failed to create view";
293 return nullptr;
294 }
295
296 std::unique_ptr<FlutterWindowsViewController> view_controller =
297 std::make_unique<FlutterWindowsViewController>(nullptr, std::move(view));
298 FML_CHECK(engine->running());
299 // The Windows embedder listens to accessibility updates using the
300 // view's HWND. The embedder's accessibility features may be stale if
301 // the app was in headless mode.
302 engine->UpdateAccessibilityFeatures();
303
304 // Register the window class.
305 if (!IsClassRegistered(kWindowClassName)) {
306 auto const idi_app_icon = 101;
307 WNDCLASSEX window_class = {};
308 window_class.cbSize = sizeof(WNDCLASSEX);
309 window_class.style = CS_HREDRAW | CS_VREDRAW;
310 window_class.lpfnWndProc = HostWindow::WndProc;
311 window_class.hInstance = GetModuleHandle(nullptr);
312 window_class.hIcon =
313 LoadIcon(window_class.hInstance, MAKEINTRESOURCE(idi_app_icon));
314 if (!window_class.hIcon) {
315 window_class.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
316 }
317 window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
318 window_class.lpszClassName = kWindowClassName;
319
320 if (!RegisterClassEx(&window_class)) {
321 FML_LOG(ERROR) << "Cannot register window class "
322 << WCharBufferToString(kWindowClassName) << ": "
323 << GetLastErrorAsString();
324 return nullptr;
325 }
326 }
327
328 // Create the native window.
329 HWND hwnd = CreateWindowEx(
330 extended_window_style, kWindowClassName, title, window_style,
331 initial_window_rect.left(), initial_window_rect.top(),
332 initial_window_rect.width(), initial_window_rect.height(), nullptr,
333 nullptr, GetModuleHandle(nullptr), engine->windows_proc_table().get());
334 if (!hwnd) {
335 FML_LOG(ERROR) << "Cannot create window: " << GetLastErrorAsString();
336 return nullptr;
337 }
338
339 // Adjust the window position so its origin aligns with the top-left corner
340 // of the window frame, not the window rectangle (which includes the
341 // drop-shadow). This adjustment must be done post-creation since the frame
342 // rectangle is only available after the window has been created.
343 RECT frame_rect;
344 DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame_rect,
345 sizeof(frame_rect));
346 RECT window_rect;
347 GetWindowRect(hwnd, &window_rect);
348 LONG const left_dropshadow_width = frame_rect.left - window_rect.left;
349 LONG const top_dropshadow_height = window_rect.top - frame_rect.top;
350 SetWindowPos(hwnd, nullptr, window_rect.left - left_dropshadow_width,
351 window_rect.top - top_dropshadow_height, 0, 0,
352 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
353
354 UpdateTheme(hwnd);
355
356 SetChildContent(view_controller->view()->GetWindowHandle(), hwnd);
357
358 // TODO(loicsharma): Hide the window until the first frame is rendered.
359 // Single window apps use the engine's next frame callback to show the
360 // window. This doesn't work for multi window apps as the engine cannot have
361 // multiple next frame callbacks. If multiple windows are created, only the
362 // last one will be shown.
363 ShowWindow(hwnd, SW_SHOWNORMAL);
364 return std::unique_ptr<HostWindow>(
365 new HostWindow(window_manager, engine, WindowArchetype::kRegular,
366 std::move(view_controller), box_constraints, hwnd));
367 }
flutter::WCharBufferToString
std::string WCharBufferToString(const wchar_t *wstr)
Convert a null terminated wchar_t buffer to a std::string using Windows utilities.
Definition: wchar_util.cc:11
flutter::WindowArchetype::kRegular
@ kRegular

References flutter::FlutterWindowsEngine::CreateView(), flutter::FlutterWindowsEngine::display_manager(), flutter::WindowSizeRequest::has_preferred_view_size, flutter::kRegular, flutter::WindowSizeRequest::preferred_view_height, flutter::WindowSizeRequest::preferred_view_width, flutter::FlutterWindowsEngine::running(), flutter::FlutterWindowsEngine::UpdateAccessibilityFeatures(), flutter::WCharBufferToString(), and flutter::FlutterWindowsEngine::windows_proc_table().

Referenced by flutter::WindowManager::CreateRegularWindow().

## ◆ GetFullscreen()

bool flutter::HostWindow::GetFullscreen | ( |  | ) | const

Definition at line 748 of file host_window.cc.

748 {
749 return is_fullscreen_;
750 }

Referenced by InternalFlutterWindows_WindowManager_GetFullscreen(), SetConstraints(), SetContentSize(), and SetFullscreen().

## ◆ GetThisFromHandle()

HostWindow * flutter::HostWindow::GetThisFromHandle | ( | HWND | hwnd | ) |
| static

Definition at line 396 of file host_window.cc.

396 {
397 return reinterpret_cast<HostWindow*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
398 }

Referenced by InternalFlutterWindows_WindowManager_GetFullscreen(), InternalFlutterWindows_WindowManager_SetFullscreen(), InternalFlutterWindows_WindowManager_SetWindowConstraints(), and InternalFlutterWindows_WindowManager_SetWindowSize().

## ◆ GetWindowContentSize()

ActualWindowSize flutter::HostWindow::GetWindowContentSize | ( | HWND | hwnd | ) |
| static

Definition at line 752 of file host_window.cc.

752 {
753 RECT rect;
754 GetClientRect(hwnd, &rect);
755 double const dpr = FlutterDesktopGetDpiForHWND(hwnd) /
756 static_cast<double>(USER_DEFAULT_SCREEN_DPI);
757 double const width = rect.right / dpr;
758 double const height = rect.bottom / dpr;
759 return {
760 .width = rect.right / dpr,
761 .height = rect.bottom / dpr,
762 };
763 }
FlutterDesktopGetDpiForHWND
UINT FlutterDesktopGetDpiForHWND(HWND hwnd)
Definition: flutter_windows.cc:323

References FlutterDesktopGetDpiForHWND().

Referenced by InternalFlutterWindows_WindowManager_GetWindowContentSize(), SetConstraints(), and SetFullscreen().

## ◆ GetWindowHandle()

HWND flutter::HostWindow::GetWindowHandle | ( |  | ) | const

Definition at line 400 of file host_window.cc.

400 {
401 return window_handle_;
402 }

## ◆ SetConstraints()

void flutter::HostWindow::SetConstraints | ( | const WindowConstraints & | constraints | ) |

Definition at line 561 of file host_window.cc.

561 {
562 box_constraints_ = FromWindowConstraints(constraints);
563
564 if (GetFullscreen()) {
565 std::optional<Size> const window_size = GetWindowSizeForClientSize(
566 *engine_->windows_proc_table(),
567 Size(saved_window_info_.client_size.width,
568 saved_window_info_.client_size.height),
569 box_constraints_.smallest(), box_constraints_.biggest(),
570 saved_window_info_.style, saved_window_info_.ex_style, nullptr);
571 if (!window_size) {
572 return;
573 }
574
575 saved_window_info_.rect.right =
576 saved_window_info_.rect.left + static_cast<LONG>(window_size->width());
577 saved_window_info_.rect.bottom =
578 saved_window_info_.rect.top + static_cast<LONG>(window_size->height());
579 } else {
580 auto const client_size = GetWindowContentSize(window_handle_);
581 auto const current_size = Size(client_size.width, client_size.height);
582 WINDOWINFO window_info = {.cbSize = sizeof(WINDOWINFO)};
583 GetWindowInfo(window_handle_, &window_info);
584 std::optional<Size> const window_size = GetWindowSizeForClientSize(
585 *engine_->windows_proc_table(), current_size,
586 box_constraints_.smallest(), box_constraints_.biggest(),
587 window_info.dwStyle, window_info.dwExStyle, nullptr);
588
589 if (window_size && current_size != window_size) {
590 SetWindowPos(window_handle_, NULL, 0, 0, window_size->width(),
591 window_size->height(),
592 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
593 }
594 }
595 }
flutter::FlutterWindowsEngine::windows_proc_table
std::shared_ptr< WindowsProcTable > windows_proc_table()
Definition: flutter_windows_engine.h:316
flutter::HostWindow::GetWindowContentSize
static ActualWindowSize GetWindowContentSize(HWND hwnd)
Definition: host_window.cc:752
flutter::HostWindow::GetFullscreen
bool GetFullscreen() const
Definition: host_window.cc:748

References GetFullscreen(), GetWindowContentSize(), and flutter::FlutterWindowsEngine::windows_proc_table().

Referenced by InternalFlutterWindows_WindowManager_SetWindowConstraints().

## ◆ SetContentSize()

void flutter::HostWindow::SetContentSize | ( | const WindowSizeRequest & | size | ) |

Definition at line 519 of file host_window.cc.

519 {
520 if (!size.has_preferred_view_size) {
521 return;
522 }
523
524 if (GetFullscreen()) {
525 std::optional<Size> const window_size = GetWindowSizeForClientSize(
526 *engine_->windows_proc_table(),
527 Size(size.preferred_view_width, size.preferred_view_height),
528 box_constraints_.smallest(), box_constraints_.biggest(),
529 saved_window_info_.style, saved_window_info_.ex_style, nullptr);
530 if (!window_size) {
531 return;
532 }
533
534 saved_window_info_.client_size =
535 ActualWindowSize{.width = size.preferred_view_width,
536 .height = size.preferred_view_height};
537 saved_window_info_.rect.right =
538 saved_window_info_.rect.left + static_cast<LONG>(window_size->width());
539 saved_window_info_.rect.bottom =
540 saved_window_info_.rect.top + static_cast<LONG>(window_size->height());
541 } else {
542 WINDOWINFO window_info = {.cbSize = sizeof(WINDOWINFO)};
543 GetWindowInfo(window_handle_, &window_info);
544
545 std::optional<Size> const window_size = GetWindowSizeForClientSize(
546 *engine_->windows_proc_table(),
547 Size(size.preferred_view_width, size.preferred_view_height),
548 box_constraints_.smallest(), box_constraints_.biggest(),
549 window_info.dwStyle, window_info.dwExStyle, nullptr);
550
551 if (!window_size) {
552 return;
553 }
554
555 SetWindowPos(window_handle_, NULL, 0, 0, window_size->width(),
556 window_size->height(),
557 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
558 }
559 }

References GetFullscreen(), flutter::WindowSizeRequest::has_preferred_view_size, flutter::WindowSizeRequest::preferred_view_height, flutter::WindowSizeRequest::preferred_view_width, flutter::ActualWindowSize::width, and flutter::FlutterWindowsEngine::windows_proc_table().

Referenced by InternalFlutterWindows_WindowManager_SetWindowSize().

## ◆ SetFullscreen()

void flutter::HostWindow::SetFullscreen | ( | bool | fullscreen,
|  | std::optional< FlutterEngineDisplayId > | display_id
| ) |  |

Definition at line 602 of file host_window.cc.

604 {
605 if (fullscreen == GetFullscreen()) {
606 return;
607 }
608
609 if (fullscreen) {
610 WINDOWINFO window_info = {.cbSize = sizeof(WINDOWINFO)};
611 GetWindowInfo(window_handle_, &window_info);
612 saved_window_info_.style = window_info.dwStyle;
613 saved_window_info_.ex_style = window_info.dwExStyle;
614 // Store the original window rect, DPI, and monitor info to detect changes
615 // and more accurately restore window placements when exiting fullscreen.
616 ::GetWindowRect(window_handle_, &saved_window_info_.rect);
617 saved_window_info_.client_size = GetWindowContentSize(window_handle_);
618 saved_window_info_.dpi = GetDpiForHWND(window_handle_);
619 saved_window_info_.monitor =
620 MonitorFromWindow(window_handle_, MONITOR_DEFAULTTONEAREST);
621 saved_window_info_.monitor_info.cbSize =
622 sizeof(saved_window_info_.monitor_info);
623 GetMonitorInfo(saved_window_info_.monitor,
624 &saved_window_info_.monitor_info);
625 }
626
627 if (fullscreen) {
628 // Next, get the raw HMONITOR that we want to be fullscreened on
629 HMONITOR monitor =
630 MonitorFromWindow(window_handle_, MONITOR_DEFAULTTONEAREST);
631 if (display_id) {
632 if (auto const display =
633 engine_->display_manager()->FindById(display_id.value())) {
634 monitor = reinterpret_cast<HMONITOR>(display->display_id);
635 }
636 }
637
638 MONITORINFO monitor_info;
639 monitor_info.cbSize = sizeof(monitor_info);
640 if (!GetMonitorInfo(monitor, &monitor_info)) {
641 FML_LOG(ERROR) << "Cannot set window fullscreen because the monitor info "
642 "was not found";
643 }
644
645 auto const width = RectWidth(monitor_info.rcMonitor);
646 auto const height = RectHeight(monitor_info.rcMonitor);
647 WINDOWINFO window_info = {.cbSize = sizeof(WINDOWINFO)};
648 GetWindowInfo(window_handle_, &window_info);
649
650 // Set new window style and size.
651 SetWindowLong(window_handle_, GWL_STYLE,
652 saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME));
653 SetWindowLong(
654 window_handle_, GWL_EXSTYLE,
655 saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
656 WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
657
658 // We call SetWindowPos first to set the window flags immediately. This
659 // makes it so that the WM_GETMINMAXINFO gets called with the correct window
660 // and content sizes.
661 SetWindowPos(window_handle_, NULL, 0, 0, 0, 0,
662 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
663
664 SetWindowPos(window_handle_, nullptr, monitor_info.rcMonitor.left,
665 monitor_info.rcMonitor.top, width, height,
666 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
667 } else {
668 // Restore the window style and bounds saved prior to entering fullscreen.
669 // Use WS_VISIBLE for windows shown after SetFullscreen: crbug.com/1062251.
670 // Making multiple window adjustments here is ugly, but if SetWindowPos()
671 // doesn't redraw, the taskbar won't be repainted.
672 SetWindowLong(window_handle_, GWL_STYLE,
673 saved_window_info_.style | WS_VISIBLE);
674 SetWindowLong(window_handle_, GWL_EXSTYLE, saved_window_info_.ex_style);
675
676 // We call SetWindowPos first to set the window flags immediately. This
677 // makes it so that the WM_GETMINMAXINFO gets called with the correct window
678 // and content sizes.
679 SetWindowPos(window_handle_, NULL, 0, 0, 0, 0,
680 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
681
682 HMONITOR monitor =
683 MonitorFromRect(&saved_window_info_.rect, MONITOR_DEFAULTTONEAREST);
684 MONITORINFO monitor_info;
685 monitor_info.cbSize = sizeof(monitor_info);
686 GetMonitorInfo(monitor, &monitor_info);
687
688 auto window_rect = saved_window_info_.rect;
689
690 // Adjust the window bounds to restore, if displays were disconnected,
691 // virtually rearranged, or otherwise changed metrics during fullscreen.
692 if (monitor != saved_window_info_.monitor ||
693 !AreRectsEqual(saved_window_info_.monitor_info.rcWork,
694 monitor_info.rcWork)) {
695 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
696 }
697
698 auto const fullscreen_dpi = GetDpiForHWND(window_handle_);
699 SetWindowPos(window_handle_, nullptr, window_rect.left, window_rect.top,
700 RectWidth(window_rect), RectHeight(window_rect),
701 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
702 auto const final_dpi = GetDpiForHWND(window_handle_);
703 if (final_dpi != saved_window_info_.dpi || final_dpi != fullscreen_dpi) {
704 // Reissue SetWindowPos if the DPI changed from saved or fullscreen DPIs.
705 // The first call may misinterpret bounds spanning displays, if the
706 // fullscreen display's DPI does not match the target display's DPI.
707 //
708 // Scale and clamp the bounds if the final DPI changed from the saved DPI.
709 // This more accurately matches the original placement, while avoiding
710 // unexpected offscreen placement in a recongifured multi-screen space.
711 if (final_dpi != saved_window_info_.dpi) {
712 auto const scale =
713 final_dpi / static_cast<float>(saved_window_info_.dpi);
714 auto const width = static_cast<LONG>(scale * RectWidth(window_rect));
715 auto const height = static_cast<LONG>(scale * RectHeight(window_rect));
716 window_rect.right = window_rect.left + width;
717 window_rect.bottom = window_rect.top + height;
718 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
719 }
720
721 SetWindowPos(window_handle_, nullptr, window_rect.left, window_rect.top,
722 RectWidth(window_rect), RectHeight(window_rect),
723 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
724 }
725 }
726
727 if (!task_bar_list_) {
728 HRESULT hr =
729 ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER,
730 IID_PPV_ARGS(&task_bar_list_));
731 if (SUCCEEDED(hr) && FAILED(task_bar_list_->HrInit())) {
732 task_bar_list_ = nullptr;
733 }
734 }
735
736 // As per MSDN marking the window as fullscreen should ensure that the
737 // taskbar is moved to the bottom of the Z-order when the fullscreen window
738 // is activated. If the window is not fullscreen, the Shell falls back to
739 // heuristics to determine how the window should be treated, which means
740 // that it could still consider the window as fullscreen. :(
741 if (task_bar_list_) {
742 task_bar_list_->MarkFullscreenWindow(window_handle_, !!fullscreen);
743 }
744
745 is_fullscreen_ = fullscreen;
746 }
flutter::FlutterWindowsEngine::display_manager
std::shared_ptr< DisplayManagerWin32 > display_manager()
Definition: flutter_windows_engine.h:163
flutter::GetDpiForHWND
UINT GetDpiForHWND(HWND hwnd)
Definition: dpi_utils.cc:128
flutter::RectWidth
LONG RectWidth(const RECT &r)
Definition: rect_helper.h:11
flutter::AreRectsEqual
bool AreRectsEqual(const RECT &a, const RECT &b)
Definition: rect_helper.h:19
flutter::RectHeight
LONG RectHeight(const RECT &r)
Definition: rect_helper.h:15

References flutter::AreRectsEqual(), flutter::FlutterWindowsEngine::display_manager(), flutter::GetDpiForHWND(), GetFullscreen(), GetWindowContentSize(), flutter::RectHeight(), and flutter::RectWidth().

Referenced by InternalFlutterWindows_WindowManager_SetFullscreen().

The documentation for this class was generated from the following files:

- shell/platform/windows/host_window.h
- shell/platform/windows/host_window.cc