Flutter Windows Embedder
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< HostWindowCreateRegularWindow (WindowManager *controller, FlutterWindowsEngine *engine, const WindowSizeRequest &preferred_size, const WindowConstraints &preferred_constraints, LPCWSTR title)
 
static HostWindowGetThisFromHandle (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 }
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

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 }
UINT FlutterDesktopGetDpiForHWND(HWND hwnd)

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 }
std::shared_ptr< WindowsProcTable > windows_proc_table()
static ActualWindowSize GetWindowContentSize(HWND hwnd)
Definition: host_window.cc:752
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 }
std::shared_ptr< DisplayManagerWin32 > display_manager()
UINT GetDpiForHWND(HWND hwnd)
Definition: dpi_utils.cc:128
LONG RectWidth(const RECT &r)
Definition: rect_helper.h:11
bool AreRectsEqual(const RECT &a, const RECT &b)
Definition: rect_helper.h:19
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: