23constexpr wchar_t kWindowClassName[] = L
"FLUTTER_HOST_WINDOW";
28 double const virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
29 double const virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
32 std::clamp(
size.height(), 0.0, virtual_screen_height));
37std::string GetLastErrorAsString() {
38 LPWSTR message_buffer =
nullptr;
40 if (
DWORD const size = FormatMessage(
41 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
42 FORMAT_MESSAGE_IGNORE_INSERTS,
43 nullptr,
GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
44 reinterpret_cast<LPTSTR
>(&message_buffer), 0,
nullptr)) {
45 std::wstring
const wide_message(message_buffer, size);
46 LocalFree(message_buffer);
47 message_buffer =
nullptr;
49 if (
int const buffer_size =
50 WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1,
nullptr,
51 0,
nullptr,
nullptr)) {
52 std::string
message(buffer_size, 0);
53 WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1, &
message[0],
54 buffer_size,
nullptr,
nullptr);
60 LocalFree(message_buffer);
62 std::ostringstream oss;
63 oss <<
"Format message failed with 0x" << std::hex << std::setfill(
'0')
70bool IsClassRegistered(LPCWSTR class_name) {
71 WNDCLASSEX window_class = {};
72 return GetClassInfoEx(GetModuleHandle(
nullptr), class_name, &window_class) !=
82#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
83#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
87void UpdateTheme(HWND
window) {
89 const wchar_t kGetPreferredBrightnessRegKey[] =
90 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
91 const wchar_t kGetPreferredBrightnessRegValue[] = L
"AppsUseLightTheme";
96 DWORD light_mode_size =
sizeof(light_mode);
97 LSTATUS
const result =
99 kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD,
nullptr,
100 &light_mode, &light_mode_size);
103 BOOL enable_dark_mode = light_mode == 0;
105 &enable_dark_mode,
sizeof(enable_dark_mode));
113 GetClientRect(
window, &client_rect);
114 MoveWindow(
content, client_rect.left, client_rect.top,
115 client_rect.right - client_rect.left,
116 client_rect.bottom - client_rect.top,
true);
135 *
size = std::min(dst_size, *size);
136 if (*origin < dst_origin)
137 *origin = dst_origin;
139 *origin = std::min(dst_origin + dst_size, *origin + *size) - *
size;
142RECT AdjustToFit(
const RECT& parent,
const RECT& child) {
143 auto new_x = child.left;
144 auto new_y = child.top;
151 result.right = new_x + new_width;
153 result.bottom = new_y + new_height;
159 std::optional<flutter::Size> smallest, biggest;
185 return std::unique_ptr<HostWindow>(
187 FromWindowConstraints(preferred_constraints), title,
201 window_manager,
engine, preferred_size,
202 FromWindowConstraints(preferred_constraints), title,
213 window_manager,
engine, FromWindowConstraints(preferred_constraints),
214 get_position_callback, parent));
224 window_manager,
engine, FromWindowConstraints(preferred_constraints),
225 get_position_callback, parent));
230 : window_manager_(window_manager), engine_(
engine) {}
235 auto view_window = std::make_unique<FlutterWindow>(
236 params.initial_window_rect.width(),
params.initial_window_rect.height(),
239 std::unique_ptr<FlutterWindowsView>
view =
245 std::make_unique<FlutterWindowsViewController>(
nullptr, std::move(
view));
253 if (!IsClassRegistered(kWindowClassName)) {
254 auto const idi_app_icon = 101;
255 WNDCLASSEX window_class = {};
256 window_class.cbSize =
sizeof(WNDCLASSEX);
257 window_class.style = CS_HREDRAW | CS_VREDRAW;
259 window_class.hInstance = GetModuleHandle(
nullptr);
261 LoadIcon(window_class.hInstance, MAKEINTRESOURCE(idi_app_icon));
262 if (!window_class.hIcon) {
263 window_class.hIcon =
LoadIcon(
nullptr, IDI_APPLICATION);
265 window_class.hCursor = LoadCursor(
nullptr, IDC_ARROW);
266 window_class.lpszClassName = kWindowClassName;
268 FML_CHECK(RegisterClassEx(&window_class));
273 params.extended_window_style, kWindowClassName,
params.title,
275 params.initial_window_rect.top(),
params.initial_window_rect.width(),
276 params.initial_window_rect.height(),
277 params.owner_window ? *
params.owner_window :
nullptr,
nullptr,
286 DwmGetWindowAttribute(
window_handle_, DWMWA_EXTENDED_FRAME_BOUNDS,
287 &frame_rect,
sizeof(frame_rect));
290 LONG const left_dropshadow_width = frame_rect.left - window_rect.left;
291 LONG const top_dropshadow_height = window_rect.top - frame_rect.top;
293 window_rect.left - left_dropshadow_width,
294 window_rect.top - top_dropshadow_height, 0, 0,
295 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
306 if (::IsWindow(hwnd)) {
307 ShowWindow(hwnd, cmd_show);
310 archetype_ =
params.archetype;
311 SetWindowLongPtr(window_handle_, GWLP_USERDATA,
315HostWindow::~HostWindow() {
316 if (view_controller_) {
319 if (!UnregisterClass(kWindowClassName, GetModuleHandle(
nullptr))) {
327 wchar_t class_name[256];
328 if (!GetClassName(hwnd, class_name,
sizeof(class_name) /
sizeof(
wchar_t))) {
329 FML_LOG(ERROR) <<
"Failed to get class name for window handle " << hwnd
330 <<
": " << GetLastErrorAsString();
334 if (wcscmp(class_name, kWindowClassName) != 0) {
338 return reinterpret_cast<HostWindow*
>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
341HWND HostWindow::GetWindowHandle()
const {
342 return window_handle_;
345HWND HostWindow::GetFlutterViewWindowHandle()
const {
350 auto child_content =
window->view_controller_->view()->GetWindowHandle();
351 if (
window !=
nullptr && child_content !=
nullptr) {
352 SetFocus(child_content);
356void HostWindow::HandleWindowActivation(HWND hwnd,
WPARAM wparam) {
357 if (LOWORD(wparam) == WA_INACTIVE) {
366 if (!IsWindowEnabled(hwnd)) {
370 if (
HostWindow*
const enabled_descendant = FindFirstEnabledDescendant()) {
371 SetActiveWindow(enabled_descendant->GetWindowHandle());
372 FocusRootViewOf(
this);
377 FocusRootViewOf(
this);
385 auto*
const create_struct =
reinterpret_cast<CREATESTRUCT*
>(lparam);
386 auto*
const windows_proc_table =
393 return DefWindowProc(hwnd,
message, wparam, lparam);
400 auto result = engine_->window_proc_delegate_manager()->OnTopLevelWindowProc(
401 window_handle_,
message, wparam, lparam);
408 is_being_destroyed_ =
true;
411 case WM_NCLBUTTONDOWN: {
415 if (
SendMessage(window_handle_, WM_NCHITTEST, wparam, lparam) ==
421 GetCursorPos(&cursorPos);
422 ScreenToClient(window_handle_, &cursorPos);
424 MAKELPARAM(cursorPos.x, cursorPos.y));
429 case WM_DPICHANGED: {
430 auto*
const new_scaled_window_rect =
reinterpret_cast<RECT*
>(lparam);
432 new_scaled_window_rect->right - new_scaled_window_rect->left;
434 new_scaled_window_rect->bottom - new_scaled_window_rect->top;
435 SetWindowPos(hwnd,
nullptr, new_scaled_window_rect->left,
437 SWP_NOZORDER | SWP_NOACTIVATE);
441 case WM_GETMINMAXINFO: {
443 GetWindowRect(hwnd, &window_rect);
445 GetClientRect(hwnd, &client_rect);
446 LONG const non_client_width = (window_rect.right - window_rect.left) -
447 (client_rect.right - client_rect.left);
448 LONG const non_client_height = (window_rect.bottom - window_rect.top) -
449 (client_rect.bottom - client_rect.top);
452 double const scale_factor =
453 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
455 MINMAXINFO* info =
reinterpret_cast<MINMAXINFO*
>(lparam);
456 Size const min_physical_size = ClampToVirtualScreen(
Size(
457 box_constraints_.smallest().width() * scale_factor + non_client_width,
458 box_constraints_.smallest().height() * scale_factor +
461 info->ptMinTrackSize.x = min_physical_size.
width();
462 info->ptMinTrackSize.y = min_physical_size.
height();
463 Size const max_physical_size = ClampToVirtualScreen(
Size(
464 box_constraints_.biggest().width() * scale_factor + non_client_width,
465 box_constraints_.biggest().height() * scale_factor +
468 info->ptMaxTrackSize.x = max_physical_size.
width();
469 info->ptMaxTrackSize.y = max_physical_size.
height();
474 auto child_content = view_controller_->view()->GetWindowHandle();
475 if (child_content !=
nullptr) {
478 GetClientRect(hwnd, &client_rect);
479 MoveWindow(child_content, client_rect.left, client_rect.top,
480 client_rect.right - client_rect.left,
481 client_rect.bottom - client_rect.top,
TRUE);
487 HandleWindowActivation(hwnd, wparam);
490 case WM_DWMCOLORIZATIONCOLORCHANGED:
498 if (!view_controller_) {
502 return DefWindowProc(hwnd,
message, wparam, lparam);
506 if (!
size.has_preferred_view_size) {
510 if (GetFullscreen()) {
511 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
512 *engine_->windows_proc_table(),
513 Size(
size.preferred_view_width,
size.preferred_view_height),
514 box_constraints_.smallest(), box_constraints_.biggest(),
515 saved_window_info_.style, saved_window_info_.ex_style,
nullptr);
520 saved_window_info_.client_size =
522 .height =
size.preferred_view_height};
523 saved_window_info_.rect.right =
524 saved_window_info_.rect.left +
static_cast<LONG>(window_size->width());
525 saved_window_info_.rect.bottom =
526 saved_window_info_.rect.top +
static_cast<LONG>(window_size->height());
528 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
529 GetWindowInfo(window_handle_, &window_info);
531 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
532 *engine_->windows_proc_table(),
533 Size(
size.preferred_view_width,
size.preferred_view_height),
534 box_constraints_.smallest(), box_constraints_.biggest(),
535 window_info.dwStyle, window_info.dwExStyle,
nullptr);
541 SetWindowPos(window_handle_, NULL, 0, 0, window_size->width(),
542 window_size->height(),
543 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
548 box_constraints_ = FromWindowConstraints(constraints);
550 if (GetFullscreen()) {
551 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
552 *engine_->windows_proc_table(),
553 Size(saved_window_info_.client_size.width,
554 saved_window_info_.client_size.height),
555 box_constraints_.smallest(), box_constraints_.biggest(),
556 saved_window_info_.style, saved_window_info_.ex_style,
nullptr);
561 saved_window_info_.rect.right =
562 saved_window_info_.rect.left +
static_cast<LONG>(window_size->width());
563 saved_window_info_.rect.bottom =
564 saved_window_info_.rect.top +
static_cast<LONG>(window_size->height());
566 auto const client_size = GetWindowContentSize(window_handle_);
567 auto const current_size =
Size(client_size.width, client_size.height);
568 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
569 GetWindowInfo(window_handle_, &window_info);
570 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
571 *engine_->windows_proc_table(), current_size,
572 box_constraints_.smallest(), box_constraints_.biggest(),
573 window_info.dwStyle, window_info.dwExStyle,
nullptr);
575 if (window_size && current_size != window_size) {
576 SetWindowPos(window_handle_, NULL, 0, 0, window_size->width(),
577 window_size->height(),
578 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
588void HostWindow::SetFullscreen(
590 std::optional<FlutterEngineDisplayId> display_id) {
591 if (fullscreen == GetFullscreen()) {
596 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
597 GetWindowInfo(window_handle_, &window_info);
598 saved_window_info_.style = window_info.dwStyle;
599 saved_window_info_.ex_style = window_info.dwExStyle;
602 ::GetWindowRect(window_handle_, &saved_window_info_.rect);
603 saved_window_info_.client_size = GetWindowContentSize(window_handle_);
605 saved_window_info_.monitor =
606 MonitorFromWindow(window_handle_, MONITOR_DEFAULTTONEAREST);
607 saved_window_info_.monitor_info.cbSize =
608 sizeof(saved_window_info_.monitor_info);
609 GetMonitorInfo(saved_window_info_.monitor,
610 &saved_window_info_.monitor_info);
616 MonitorFromWindow(window_handle_, MONITOR_DEFAULTTONEAREST);
618 if (
auto const display =
619 engine_->display_manager()->FindById(display_id.value())) {
620 monitor =
reinterpret_cast<HMONITOR
>(display->display_id);
624 MONITORINFO monitor_info;
625 monitor_info.cbSize =
sizeof(monitor_info);
626 if (!GetMonitorInfo(monitor, &monitor_info)) {
627 FML_LOG(ERROR) <<
"Cannot set window fullscreen because the monitor info "
633 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
634 GetWindowInfo(window_handle_, &window_info);
637 SetWindowLong(window_handle_, GWL_STYLE,
638 saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME));
640 window_handle_, GWL_EXSTYLE,
641 saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
642 WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
647 SetWindowPos(window_handle_, NULL, 0, 0, 0, 0,
648 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
650 SetWindowPos(window_handle_,
nullptr, monitor_info.rcMonitor.left,
652 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
658 SetWindowLong(window_handle_, GWL_STYLE,
659 saved_window_info_.style | WS_VISIBLE);
660 SetWindowLong(window_handle_, GWL_EXSTYLE, saved_window_info_.ex_style);
665 SetWindowPos(window_handle_, NULL, 0, 0, 0, 0,
666 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
669 MonitorFromRect(&saved_window_info_.rect, MONITOR_DEFAULTTONEAREST);
670 MONITORINFO monitor_info;
671 monitor_info.cbSize =
sizeof(monitor_info);
672 GetMonitorInfo(monitor, &monitor_info);
674 auto window_rect = saved_window_info_.rect;
678 if (monitor != saved_window_info_.monitor ||
680 monitor_info.rcWork)) {
681 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
685 SetWindowPos(window_handle_,
nullptr, window_rect.left, window_rect.top,
687 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
689 if (final_dpi != saved_window_info_.dpi || final_dpi != fullscreen_dpi) {
697 if (final_dpi != saved_window_info_.dpi) {
699 final_dpi /
static_cast<float>(saved_window_info_.dpi);
702 window_rect.right = window_rect.left +
width;
703 window_rect.bottom = window_rect.top +
height;
704 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
707 SetWindowPos(window_handle_,
nullptr, window_rect.left, window_rect.top,
709 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
713 if (!task_bar_list_) {
715 ::CoCreateInstance(CLSID_TaskbarList,
nullptr, CLSCTX_INPROC_SERVER,
716 IID_PPV_ARGS(&task_bar_list_));
718 task_bar_list_ =
nullptr;
727 if (task_bar_list_) {
728 task_bar_list_->MarkFullscreenWindow(window_handle_, !!fullscreen);
731 is_fullscreen_ = fullscreen;
734bool HostWindow::GetFullscreen()
const {
735 return is_fullscreen_;
740 GetClientRect(hwnd, &rect);
742 static_cast<double>(USER_DEFAULT_SCREEN_DPI);
743 double const width = rect.right / dpr;
744 double const height = rect.bottom / dpr;
746 .width = rect.right / dpr,
747 .height = rect.bottom / dpr,
751std::optional<Size> HostWindow::GetWindowSizeForClientSize(
753 Size const& client_size,
754 std::optional<Size> smallest,
755 std::optional<Size> biggest,
757 DWORD extended_window_style,
758 std::optional<HWND>
const& owner_hwnd) {
760 double const scale_factor =
761 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
763 .right =
static_cast<LONG>(client_size.
width() * scale_factor),
764 .bottom =
static_cast<LONG>(client_size.
height() * scale_factor)};
767 extended_window_style, dpi)) {
768 FML_LOG(ERROR) <<
"Failed to run AdjustWindowRectExForDpi: "
769 << GetLastErrorAsString();
773 double width =
static_cast<double>(rect.right - rect.left);
774 double height =
static_cast<double>(rect.bottom - rect.top);
777 double const non_client_width =
width - (client_size.
width() * scale_factor);
778 double const non_client_height =
782 flutter::Size(smallest->width() * scale_factor + non_client_width,
783 smallest->height() * scale_factor + non_client_height));
789 flutter::Size(biggest->width() * scale_factor + non_client_width,
790 biggest->height() * scale_factor + non_client_height));
798void HostWindow::EnableRecursively(
bool enable) {
799 EnableWindow(window_handle_, enable);
801 for (
HostWindow*
const owned : GetOwnedWindows()) {
802 owned->EnableRecursively(enable);
807 if (IsWindowEnabled(window_handle_)) {
811 for (
HostWindow*
const owned : GetOwnedWindows()) {
820std::vector<HostWindow*> HostWindow::GetOwnedWindows()
const {
821 std::vector<HostWindow*> owned_windows;
823 HWND owner_window_handle;
824 std::vector<HostWindow*>* owned_windows;
825 }
data{window_handle_, &owned_windows};
829 auto*
const data =
reinterpret_cast<EnumData*
>(lparam);
830 if (GetWindow(hwnd, GW_OWNER) ==
data->owner_window_handle) {
840 return owned_windows;
844 if (HWND
const owner_window_handle = GetWindow(GetWindowHandle(), GW_OWNER)) {
845 return GetThisFromHandle(owner_window_handle);
850void HostWindow::DisableRecursively() {
852 EnableWindow(window_handle_,
false);
854 for (
HostWindow*
const owned : GetOwnedWindows()) {
855 owned->DisableRecursively();
859void HostWindow::UpdateModalStateLayer() {
860 auto children = GetOwnedWindows();
862 if (children.empty()) {
864 EnableWindow(window_handle_,
true);
867 EnableWindow(window_handle_,
false);
871 auto latest_child = *std::max_element(
873 return a->view_controller_->view()->view_id() <
874 b->view_controller_->view()->view_id();
878 if (child == latest_child) {
879 child->UpdateModalStateLayer();
881 child->DisableRecursively();
void UpdateAccessibilityFeatures()
std::shared_ptr< WindowsProcTable > windows_proc_table()
std::unique_ptr< FlutterWindowsView > CreateView(std::unique_ptr< WindowBindingHandler > window, bool is_sized_to_content, const BoxConstraints &box_constraints, FlutterWindowsViewSizingDelegate *sizing_delegate=nullptr)
std::shared_ptr< DisplayManagerWin32 > display_manager()
virtual bool running() const
HWND GetWindowHandle() const
static std::unique_ptr< HostWindow > CreatePopupWindow(WindowManager *window_manager, FlutterWindowsEngine *engine, const WindowConstraints &preferred_constraints, GetWindowPositionCallback get_position_callback, HWND parent)
static std::unique_ptr< HostWindow > CreateDialogWindow(WindowManager *window_manager, FlutterWindowsEngine *engine, const WindowSizeRequest &preferred_size, const WindowConstraints &preferred_constraints, LPCWSTR title, HWND parent, bool sized_to_content, bool resizable)
void InitializeFlutterView(HostWindowInitializationParams const ¶ms)
std::unique_ptr< FlutterWindowsViewController > view_controller_
static LRESULT WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
static std::unique_ptr< HostWindow > CreateTooltipWindow(WindowManager *window_manager, FlutterWindowsEngine *engine, const WindowConstraints &preferred_constraints, GetWindowPositionCallback get_position_callback, HWND parent)
static std::unique_ptr< HostWindow > CreateRegularWindow(WindowManager *window_manager, FlutterWindowsEngine *engine, const WindowSizeRequest &preferred_size, const WindowConstraints &preferred_constraints, LPCWSTR title, bool sized_to_content, bool resizable)
FlutterWindowsEngine * engine_
HostWindow(WindowManager *window_manager, FlutterWindowsEngine *engine)
HostWindow * FindFirstEnabledDescendant() const
virtual BOOL AdjustWindowRectExForDpi(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi) const
virtual BOOL EnableNonClientDpiScaling(HWND hwnd) const
const EmbeddedViewParams * params
G_BEGIN_DECLS gboolean sized_to_content
UINT FlutterDesktopGetDpiForHWND(HWND hwnd)
#define FML_LOG(severity)
#define FML_CHECK(condition)
#define DWMWA_USE_IMMERSIVE_DARK_MODE
union flutter::testing::@2903::KeyboardChange::@78 content
UINT GetDpiForHWND(HWND hwnd)
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
WindowRect *(* GetWindowPositionCallback)(const WindowSize &child_size, const WindowRect &parent_rect, const WindowRect &output_rect)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
LONG RectWidth(const RECT &r)
bool AreRectsEqual(const RECT &a, const RECT &b)
LONG RectHeight(const RECT &r)
void AdjustAlongAxis(int dst_origin, int dst_size, int *origin, int *size)
bool has_view_constraints
#define HKEY_CURRENT_USER
WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode)
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)