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;
183 bool sized_to_content,
185 return std::unique_ptr<HostWindow>(
187 FromWindowConstraints(preferred_constraints), title,
188 sized_to_content, resizable));
198 bool sized_to_content,
201 window_manager,
engine, preferred_size,
202 FromWindowConstraints(preferred_constraints), title,
203 parent ? parent : std::optional<HWND>(), sized_to_content, resizable));
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);
361 auto*
const create_struct =
reinterpret_cast<CREATESTRUCT*
>(lparam);
362 auto*
const windows_proc_table =
369 return DefWindowProc(hwnd,
message, wparam, lparam);
376 auto result = engine_->window_proc_delegate_manager()->OnTopLevelWindowProc(
377 window_handle_,
message, wparam, lparam);
384 is_being_destroyed_ =
true;
387 case WM_NCLBUTTONDOWN: {
391 if (
SendMessage(window_handle_, WM_NCHITTEST, wparam, lparam) ==
397 GetCursorPos(&cursorPos);
398 ScreenToClient(window_handle_, &cursorPos);
400 MAKELPARAM(cursorPos.x, cursorPos.y));
405 case WM_DPICHANGED: {
406 auto*
const new_scaled_window_rect =
reinterpret_cast<RECT*
>(lparam);
408 new_scaled_window_rect->right - new_scaled_window_rect->left;
410 new_scaled_window_rect->bottom - new_scaled_window_rect->top;
411 SetWindowPos(hwnd,
nullptr, new_scaled_window_rect->left,
413 SWP_NOZORDER | SWP_NOACTIVATE);
417 case WM_GETMINMAXINFO: {
419 GetWindowRect(hwnd, &window_rect);
421 GetClientRect(hwnd, &client_rect);
422 LONG const non_client_width = (window_rect.right - window_rect.left) -
423 (client_rect.right - client_rect.left);
424 LONG const non_client_height = (window_rect.bottom - window_rect.top) -
425 (client_rect.bottom - client_rect.top);
428 double const scale_factor =
429 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
431 MINMAXINFO* info =
reinterpret_cast<MINMAXINFO*
>(lparam);
432 Size const min_physical_size = ClampToVirtualScreen(
Size(
433 box_constraints_.smallest().width() * scale_factor + non_client_width,
434 box_constraints_.smallest().height() * scale_factor +
437 info->ptMinTrackSize.x = min_physical_size.
width();
438 info->ptMinTrackSize.y = min_physical_size.
height();
439 Size const max_physical_size = ClampToVirtualScreen(
Size(
440 box_constraints_.biggest().width() * scale_factor + non_client_width,
441 box_constraints_.biggest().height() * scale_factor +
444 info->ptMaxTrackSize.x = max_physical_size.
width();
445 info->ptMaxTrackSize.y = max_physical_size.
height();
450 auto child_content = view_controller_->view()->GetWindowHandle();
451 if (child_content !=
nullptr) {
454 GetClientRect(hwnd, &client_rect);
455 MoveWindow(child_content, client_rect.left, client_rect.top,
456 client_rect.right - client_rect.left,
457 client_rect.bottom - client_rect.top,
TRUE);
463 FocusRootViewOf(
this);
466 case WM_DWMCOLORIZATIONCOLORCHANGED:
474 if (!view_controller_) {
478 return DefWindowProc(hwnd,
message, wparam, lparam);
482 if (!
size.has_preferred_view_size) {
486 if (GetFullscreen()) {
487 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
488 *engine_->windows_proc_table(),
489 Size(
size.preferred_view_width,
size.preferred_view_height),
490 box_constraints_.smallest(), box_constraints_.biggest(),
491 saved_window_info_.style, saved_window_info_.ex_style,
nullptr);
496 saved_window_info_.client_size =
498 .height =
size.preferred_view_height};
499 saved_window_info_.rect.right =
500 saved_window_info_.rect.left +
static_cast<LONG>(window_size->width());
501 saved_window_info_.rect.bottom =
502 saved_window_info_.rect.top +
static_cast<LONG>(window_size->height());
504 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
505 GetWindowInfo(window_handle_, &window_info);
507 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
508 *engine_->windows_proc_table(),
509 Size(
size.preferred_view_width,
size.preferred_view_height),
510 box_constraints_.smallest(), box_constraints_.biggest(),
511 window_info.dwStyle, window_info.dwExStyle,
nullptr);
517 SetWindowPos(window_handle_, NULL, 0, 0, window_size->width(),
518 window_size->height(),
519 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
524 box_constraints_ = FromWindowConstraints(constraints);
526 if (GetFullscreen()) {
527 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
528 *engine_->windows_proc_table(),
529 Size(saved_window_info_.client_size.width,
530 saved_window_info_.client_size.height),
531 box_constraints_.smallest(), box_constraints_.biggest(),
532 saved_window_info_.style, saved_window_info_.ex_style,
nullptr);
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());
542 auto const client_size = GetWindowContentSize(window_handle_);
543 auto const current_size =
Size(client_size.width, client_size.height);
544 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
545 GetWindowInfo(window_handle_, &window_info);
546 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
547 *engine_->windows_proc_table(), current_size,
548 box_constraints_.smallest(), box_constraints_.biggest(),
549 window_info.dwStyle, window_info.dwExStyle,
nullptr);
551 if (window_size && current_size != window_size) {
552 SetWindowPos(window_handle_, NULL, 0, 0, window_size->width(),
553 window_size->height(),
554 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
564void HostWindow::SetFullscreen(
566 std::optional<FlutterEngineDisplayId> display_id) {
567 if (fullscreen == GetFullscreen()) {
572 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
573 GetWindowInfo(window_handle_, &window_info);
574 saved_window_info_.style = window_info.dwStyle;
575 saved_window_info_.ex_style = window_info.dwExStyle;
578 ::GetWindowRect(window_handle_, &saved_window_info_.rect);
579 saved_window_info_.client_size = GetWindowContentSize(window_handle_);
581 saved_window_info_.monitor =
582 MonitorFromWindow(window_handle_, MONITOR_DEFAULTTONEAREST);
583 saved_window_info_.monitor_info.cbSize =
584 sizeof(saved_window_info_.monitor_info);
585 GetMonitorInfo(saved_window_info_.monitor,
586 &saved_window_info_.monitor_info);
592 MonitorFromWindow(window_handle_, MONITOR_DEFAULTTONEAREST);
594 if (
auto const display =
595 engine_->display_manager()->FindById(display_id.value())) {
596 monitor =
reinterpret_cast<HMONITOR
>(display->display_id);
600 MONITORINFO monitor_info;
601 monitor_info.cbSize =
sizeof(monitor_info);
602 if (!GetMonitorInfo(monitor, &monitor_info)) {
603 FML_LOG(ERROR) <<
"Cannot set window fullscreen because the monitor info "
609 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
610 GetWindowInfo(window_handle_, &window_info);
613 SetWindowLong(window_handle_, GWL_STYLE,
614 saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME));
616 window_handle_, GWL_EXSTYLE,
617 saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
618 WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
623 SetWindowPos(window_handle_, NULL, 0, 0, 0, 0,
624 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
626 SetWindowPos(window_handle_,
nullptr, monitor_info.rcMonitor.left,
628 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
634 SetWindowLong(window_handle_, GWL_STYLE,
635 saved_window_info_.style | WS_VISIBLE);
636 SetWindowLong(window_handle_, GWL_EXSTYLE, saved_window_info_.ex_style);
641 SetWindowPos(window_handle_, NULL, 0, 0, 0, 0,
642 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
645 MonitorFromRect(&saved_window_info_.rect, MONITOR_DEFAULTTONEAREST);
646 MONITORINFO monitor_info;
647 monitor_info.cbSize =
sizeof(monitor_info);
648 GetMonitorInfo(monitor, &monitor_info);
650 auto window_rect = saved_window_info_.rect;
654 if (monitor != saved_window_info_.monitor ||
656 monitor_info.rcWork)) {
657 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
661 SetWindowPos(window_handle_,
nullptr, window_rect.left, window_rect.top,
663 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
665 if (final_dpi != saved_window_info_.dpi || final_dpi != fullscreen_dpi) {
673 if (final_dpi != saved_window_info_.dpi) {
675 final_dpi /
static_cast<float>(saved_window_info_.dpi);
678 window_rect.right = window_rect.left +
width;
679 window_rect.bottom = window_rect.top +
height;
680 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
683 SetWindowPos(window_handle_,
nullptr, window_rect.left, window_rect.top,
685 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
689 if (!task_bar_list_) {
691 ::CoCreateInstance(CLSID_TaskbarList,
nullptr, CLSCTX_INPROC_SERVER,
692 IID_PPV_ARGS(&task_bar_list_));
694 task_bar_list_ =
nullptr;
703 if (task_bar_list_) {
704 task_bar_list_->MarkFullscreenWindow(window_handle_, !!fullscreen);
707 is_fullscreen_ = fullscreen;
710bool HostWindow::GetFullscreen()
const {
711 return is_fullscreen_;
716 GetClientRect(hwnd, &rect);
718 static_cast<double>(USER_DEFAULT_SCREEN_DPI);
719 double const width = rect.right / dpr;
720 double const height = rect.bottom / dpr;
722 .width = rect.right / dpr,
723 .height = rect.bottom / dpr,
727std::optional<Size> HostWindow::GetWindowSizeForClientSize(
729 Size const& client_size,
730 std::optional<Size> smallest,
731 std::optional<Size> biggest,
733 DWORD extended_window_style,
734 std::optional<HWND>
const& owner_hwnd) {
736 double const scale_factor =
737 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
739 .right =
static_cast<LONG>(client_size.
width() * scale_factor),
740 .bottom =
static_cast<LONG>(client_size.
height() * scale_factor)};
743 extended_window_style, dpi)) {
744 FML_LOG(ERROR) <<
"Failed to run AdjustWindowRectExForDpi: "
745 << GetLastErrorAsString();
749 double width =
static_cast<double>(rect.right - rect.left);
750 double height =
static_cast<double>(rect.bottom - rect.top);
753 double const non_client_width =
width - (client_size.
width() * scale_factor);
754 double const non_client_height =
758 flutter::Size(smallest->width() * scale_factor + non_client_width,
759 smallest->height() * scale_factor + non_client_height));
765 flutter::Size(biggest->width() * scale_factor + non_client_width,
766 biggest->height() * scale_factor + non_client_height));
774void HostWindow::EnableRecursively(
bool enable) {
775 EnableWindow(window_handle_, enable);
777 for (
HostWindow*
const owned : GetOwnedWindows()) {
778 owned->EnableRecursively(enable);
783 if (IsWindowEnabled(window_handle_)) {
787 for (
HostWindow*
const owned : GetOwnedWindows()) {
796std::vector<HostWindow*> HostWindow::GetOwnedWindows()
const {
797 std::vector<HostWindow*> owned_windows;
799 HWND owner_window_handle;
800 std::vector<HostWindow*>* owned_windows;
801 }
data{window_handle_, &owned_windows};
805 auto*
const data =
reinterpret_cast<EnumData*
>(lparam);
806 if (GetWindow(hwnd, GW_OWNER) ==
data->owner_window_handle) {
816 return owned_windows;
820 if (HWND
const owner_window_handle = GetWindow(GetWindowHandle(), GW_OWNER)) {
821 return GetThisFromHandle(owner_window_handle);
826void HostWindow::DisableRecursively() {
828 EnableWindow(window_handle_,
false);
830 for (
HostWindow*
const owned : GetOwnedWindows()) {
831 owned->DisableRecursively();
835void HostWindow::UpdateModalStateLayer() {
836 auto children = GetOwnedWindows();
838 if (children.empty()) {
840 EnableWindow(window_handle_,
true);
843 EnableWindow(window_handle_,
false);
847 auto latest_child = *std::max_element(
849 return a->view_controller_->view()->view_id() <
850 b->view_controller_->view()->view_id();
854 if (child == latest_child) {
855 child->UpdateModalStateLayer();
857 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
UINT FlutterDesktopGetDpiForHWND(HWND hwnd)
#define FML_LOG(severity)
#define FML_CHECK(condition)
#define DWMWA_USE_IMMERSIVE_DARK_MODE
union flutter::testing::@2888::KeyboardChange::@77 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)