21 constexpr
wchar_t kWindowClassName[] = L
"FLUTTER_HOST_WINDOW";
25 flutter::Size ClampToVirtualScreen(flutter::Size size) {
26 double const virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
27 double const virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
29 return flutter::Size(std::clamp(size.width(), 0.0, virtual_screen_width),
30 std::clamp(size.height(), 0.0, virtual_screen_height));
33 void EnableTransparentWindowBackground(HWND hwnd,
35 enum ACCENT_STATE { ACCENT_DISABLED = 0 };
37 struct ACCENT_POLICY {
38 ACCENT_STATE AccentState;
45 ACCENT_POLICY accent = {ACCENT_DISABLED, 2,
static_cast<DWORD
>(0), 0};
48 flutter::WindowsProcTable::WINDOWCOMPOSITIONATTRIB::WCA_ACCENT_POLICY,
50 .cbData =
sizeof(accent)};
55 MARGINS
const margins = {-1};
64 std::string GetLastErrorAsString() {
65 LPWSTR message_buffer =
nullptr;
67 if (DWORD
const size = FormatMessage(
68 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
69 FORMAT_MESSAGE_IGNORE_INSERTS,
70 nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
71 reinterpret_cast<LPTSTR
>(&message_buffer), 0,
nullptr)) {
72 std::wstring
const wide_message(message_buffer, size);
73 LocalFree(message_buffer);
74 message_buffer =
nullptr;
76 if (
int const buffer_size =
77 WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1,
nullptr,
78 0,
nullptr,
nullptr)) {
79 std::string
message(buffer_size, 0);
80 WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1, &
message[0],
81 buffer_size,
nullptr,
nullptr);
87 LocalFree(message_buffer);
89 std::ostringstream oss;
90 oss <<
"Format message failed with 0x" << std::hex << std::setfill(
'0')
91 << std::setw(8) << GetLastError();
97 bool IsClassRegistered(LPCWSTR class_name) {
98 WNDCLASSEX window_class = {};
99 return GetClassInfoEx(GetModuleHandle(
nullptr), class_name, &window_class) !=
109 #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
110 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
114 void UpdateTheme(HWND window) {
116 const wchar_t kGetPreferredBrightnessRegKey[] =
117 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
118 const wchar_t kGetPreferredBrightnessRegValue[] = L
"AppsUseLightTheme";
123 DWORD light_mode_size =
sizeof(light_mode);
124 LSTATUS
const result =
125 RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
126 kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD,
nullptr,
127 &light_mode, &light_mode_size);
129 if (result == ERROR_SUCCESS) {
130 BOOL enable_dark_mode = light_mode == 0;
132 &enable_dark_mode,
sizeof(enable_dark_mode));
137 void SetChildContent(HWND
content, HWND window) {
140 GetClientRect(window, &client_rect);
141 MoveWindow(
content, client_rect.left, client_rect.top,
142 client_rect.right - client_rect.left,
143 client_rect.bottom - client_rect.top,
true);
161 void AdjustAlongAxis(LONG dst_origin, LONG dst_size, LONG* origin, LONG* size) {
162 *size = std::min(dst_size, *size);
163 if (*origin < dst_origin)
164 *origin = dst_origin;
166 *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
169 RECT AdjustToFit(
const RECT& parent,
const RECT& child) {
170 auto new_x = child.left;
171 auto new_y = child.top;
178 result.right = new_x + new_width;
180 result.bottom = new_y + new_height;
184 flutter::BoxConstraints FromWindowConstraints(
186 std::optional<flutter::Size> smallest, biggest;
197 return flutter::BoxConstraints(smallest, biggest);
211 window_manager, engine, preferred_size,
212 FromWindowConstraints(preferred_constraints), title));
222 return std::unique_ptr<HostWindow>(
224 FromWindowConstraints(preferred_constraints), title,
225 parent ? parent : std::optional<HWND>()));
232 DWORD extended_window_style,
233 const BoxConstraints& box_constraints,
234 Rect
const initial_window_rect,
236 std::optional<HWND>
const& owner_window)
237 : window_manager_(window_manager),
239 archetype_(archetype),
240 box_constraints_(box_constraints) {
242 auto view_window = std::make_unique<FlutterWindow>(
243 initial_window_rect.width(), initial_window_rect.height(),
246 std::unique_ptr<FlutterWindowsView> view =
248 FML_CHECK(view !=
nullptr);
251 std::make_unique<FlutterWindowsViewController>(
nullptr, std::move(view));
259 if (!IsClassRegistered(kWindowClassName)) {
260 auto const idi_app_icon = 101;
261 WNDCLASSEX window_class = {};
262 window_class.cbSize =
sizeof(WNDCLASSEX);
263 window_class.style = CS_HREDRAW | CS_VREDRAW;
265 window_class.hInstance = GetModuleHandle(
nullptr);
267 LoadIcon(window_class.hInstance, MAKEINTRESOURCE(idi_app_icon));
268 if (!window_class.hIcon) {
269 window_class.hIcon = LoadIcon(
nullptr, IDI_APPLICATION);
271 window_class.hCursor = LoadCursor(
nullptr, IDC_ARROW);
272 window_class.lpszClassName = kWindowClassName;
274 FML_CHECK(RegisterClassEx(&window_class));
279 extended_window_style, kWindowClassName, title, window_style,
280 initial_window_rect.left(), initial_window_rect.top(),
281 initial_window_rect.width(), initial_window_rect.height(),
282 owner_window ? *owner_window :
nullptr,
nullptr, GetModuleHandle(
nullptr),
291 DwmGetWindowAttribute(
window_handle_, DWMWA_EXTENDED_FRAME_BOUNDS,
292 &frame_rect,
sizeof(frame_rect));
295 LONG
const left_dropshadow_width = frame_rect.left - window_rect.left;
296 LONG
const top_dropshadow_height = window_rect.top - frame_rect.top;
298 window_rect.left - left_dropshadow_width,
299 window_rect.top - top_dropshadow_height, 0, 0,
300 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
313 reinterpret_cast<LONG_PTR
>(
this));
320 if (!UnregisterClass(kWindowClassName, GetModuleHandle(
nullptr))) {
322 SetLastError(ERROR_SUCCESS);
328 wchar_t class_name[256];
329 if (!GetClassName(hwnd, class_name,
sizeof(class_name) /
sizeof(
wchar_t))) {
330 FML_LOG(ERROR) <<
"Failed to get class name for window handle " << hwnd
331 <<
": " << GetLastErrorAsString();
335 if (wcscmp(class_name, kWindowClassName) != 0) {
339 return reinterpret_cast<HostWindow*
>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
348 if (window !=
nullptr && child_content !=
nullptr) {
349 SetFocus(child_content);
358 auto*
const create_struct =
reinterpret_cast<CREATESTRUCT*
>(lparam);
359 auto*
const windows_proc_table =
362 EnableTransparentWindowBackground(hwnd, *windows_proc_table);
364 return window->HandleMessage(hwnd,
message, wparam, lparam);
367 return DefWindowProc(hwnd,
message, wparam, lparam);
385 case WM_NCLBUTTONDOWN: {
395 GetCursorPos(&cursorPos);
398 MAKELPARAM(cursorPos.x, cursorPos.y));
403 case WM_DPICHANGED: {
404 auto*
const new_scaled_window_rect =
reinterpret_cast<RECT*
>(lparam);
406 new_scaled_window_rect->right - new_scaled_window_rect->left;
408 new_scaled_window_rect->bottom - new_scaled_window_rect->top;
409 SetWindowPos(hwnd,
nullptr, new_scaled_window_rect->left,
410 new_scaled_window_rect->top, width, height,
411 SWP_NOZORDER | SWP_NOACTIVATE);
415 case WM_GETMINMAXINFO: {
417 GetWindowRect(hwnd, &window_rect);
419 GetClientRect(hwnd, &client_rect);
420 LONG
const non_client_width = (window_rect.right - window_rect.left) -
421 (client_rect.right - client_rect.left);
422 LONG
const non_client_height = (window_rect.bottom - window_rect.top) -
423 (client_rect.bottom - client_rect.top);
426 double const scale_factor =
427 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
429 MINMAXINFO* info =
reinterpret_cast<MINMAXINFO*
>(lparam);
430 Size
const min_physical_size = ClampToVirtualScreen(Size(
435 info->ptMinTrackSize.x = min_physical_size.width();
436 info->ptMinTrackSize.y = min_physical_size.height();
437 Size
const max_physical_size = ClampToVirtualScreen(Size(
442 info->ptMaxTrackSize.x = max_physical_size.width();
443 info->ptMaxTrackSize.y = max_physical_size.height();
449 if (child_content !=
nullptr) {
452 GetClientRect(hwnd, &client_rect);
453 MoveWindow(child_content, client_rect.left, client_rect.top,
454 client_rect.right - client_rect.left,
455 client_rect.bottom - client_rect.top, TRUE);
464 case WM_DWMCOLORIZATIONCOLORCHANGED:
476 return DefWindowProc(hwnd,
message, wparam, lparam);
502 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
509 window_info.dwStyle, window_info.dwExStyle,
nullptr);
516 window_size->height(),
517 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
541 auto const current_size = Size(client_size.width, client_size.height);
542 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
547 window_info.dwStyle, window_info.dwExStyle,
nullptr);
549 if (window_size && current_size != window_size) {
551 window_size->height(),
552 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
564 std::optional<FlutterEngineDisplayId> display_id) {
570 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
592 if (
auto const display =
594 monitor =
reinterpret_cast<HMONITOR
>(display->display_id);
598 MONITORINFO monitor_info;
599 monitor_info.cbSize =
sizeof(monitor_info);
600 if (!GetMonitorInfo(monitor, &monitor_info)) {
601 FML_LOG(ERROR) <<
"Cannot set window fullscreen because the monitor info "
605 auto const width =
RectWidth(monitor_info.rcMonitor);
606 auto const height =
RectHeight(monitor_info.rcMonitor);
607 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
616 WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
622 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
624 SetWindowPos(
window_handle_,
nullptr, monitor_info.rcMonitor.left,
625 monitor_info.rcMonitor.top, width, height,
626 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
640 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
644 MONITORINFO monitor_info;
645 monitor_info.cbSize =
sizeof(monitor_info);
646 GetMonitorInfo(monitor, &monitor_info);
654 monitor_info.rcWork)) {
655 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
659 SetWindowPos(
window_handle_,
nullptr, window_rect.left, window_rect.top,
661 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
674 auto const width =
static_cast<LONG
>(scale *
RectWidth(window_rect));
675 auto const height =
static_cast<LONG
>(scale *
RectHeight(window_rect));
676 window_rect.right = window_rect.left + width;
677 window_rect.bottom = window_rect.top + height;
678 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
681 SetWindowPos(
window_handle_,
nullptr, window_rect.left, window_rect.top,
683 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
689 ::CoCreateInstance(CLSID_TaskbarList,
nullptr, CLSCTX_INPROC_SERVER,
714 GetClientRect(hwnd, &rect);
716 static_cast<double>(USER_DEFAULT_SCREEN_DPI);
717 double const width = rect.right / dpr;
718 double const height = rect.bottom / dpr;
720 .width = rect.right / dpr,
721 .height = rect.bottom / dpr,
727 Size
const& client_size,
728 std::optional<Size> smallest,
729 std::optional<Size> biggest,
731 DWORD extended_window_style,
732 std::optional<HWND>
const& owner_hwnd) {
733 UINT
const dpi =
GetDpiForHWND(owner_hwnd ? *owner_hwnd :
nullptr);
734 double const scale_factor =
735 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
737 .right =
static_cast<LONG
>(client_size.width() * scale_factor),
738 .bottom =
static_cast<LONG
>(client_size.height() * scale_factor)};
741 extended_window_style, dpi)) {
742 FML_LOG(ERROR) <<
"Failed to run AdjustWindowRectExForDpi: "
743 << GetLastErrorAsString();
747 double width =
static_cast<double>(rect.right - rect.left);
748 double height =
static_cast<double>(rect.bottom - rect.top);
751 double const non_client_width = width - (client_size.width() * scale_factor);
752 double const non_client_height =
753 height - (client_size.height() * scale_factor);
755 flutter::Size min_physical_size = ClampToVirtualScreen(
756 flutter::Size(smallest->width() * scale_factor + non_client_width,
757 smallest->height() * scale_factor + non_client_height));
758 width = std::max(width, min_physical_size.width());
759 height = std::max(height, min_physical_size.height());
762 flutter::Size max_physical_size = ClampToVirtualScreen(
763 flutter::Size(biggest->width() * scale_factor + non_client_width,
764 biggest->height() * scale_factor + non_client_height));
765 width = std::min(width, max_physical_size.width());
766 height = std::min(height, max_physical_size.height());
769 return flutter::Size{width, height};
776 owned->EnableRecursively(enable);
795 std::vector<HostWindow*> owned_windows;
797 HWND owner_window_handle;
798 std::vector<HostWindow*>* owned_windows;
802 [](HWND hwnd, LPARAM lparam) -> BOOL {
803 auto*
const data =
reinterpret_cast<EnumData*
>(lparam);
804 if (GetWindow(hwnd, GW_OWNER) == data->owner_window_handle) {
807 data->owned_windows->push_back(window);
812 reinterpret_cast<LPARAM
>(&data));
814 return owned_windows;
818 if (HWND
const owner_window_handle = GetWindow(
GetWindowHandle(), GW_OWNER)) {
829 owned->DisableRecursively();
835 if (children.empty()) {
844 auto latest_child = *std::max_element(
846 return a->view_controller_->view()->view_id() <
847 b->view_controller_->view()->view_id();
851 if (child == latest_child) {
852 child->UpdateModalStateLayer();
854 child->DisableRecursively();
std::shared_ptr< WindowsProcTable > windows_proc_table()
void UpdateAccessibilityFeatures()
WindowProcDelegateManager * window_proc_delegate_manager()
std::shared_ptr< DisplayManagerWin32 > display_manager()
virtual bool running() const
std::unique_ptr< FlutterWindowsView > CreateView(std::unique_ptr< WindowBindingHandler > window)
Microsoft::WRL::ComPtr< ITaskbarList2 > task_bar_list_
HWND GetWindowHandle() const
BoxConstraints box_constraints_
SavedWindowInfo saved_window_info_
std::unique_ptr< FlutterWindowsViewController > view_controller_
void DisableRecursively()
static LRESULT WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
HostWindow * GetOwnerWindow() const
static ActualWindowSize GetWindowContentSize(HWND hwnd)
void EnableRecursively(bool enable)
void SetContentSize(const WindowSizeRequest &size)
void UpdateModalStateLayer()
static void FocusRootViewOf(HostWindow *window)
HostWindow(WindowManager *window_manager, FlutterWindowsEngine *engine, WindowArchetype archetype, DWORD window_style, DWORD extended_window_style, const BoxConstraints &box_constraints, Rect const initial_window_rect, LPCWSTR title, std::optional< HWND > const &owner_window)
FlutterWindowsEngine * engine_
static HostWindow * GetThisFromHandle(HWND hwnd)
std::vector< HostWindow * > GetOwnedWindows() const
virtual bool GetFullscreen() const
void SetConstraints(const WindowConstraints &constraints)
virtual void SetFullscreen(bool fullscreen, std::optional< FlutterEngineDisplayId > display_id)
virtual LRESULT HandleMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
static std::unique_ptr< HostWindow > CreateDialogWindow(WindowManager *window_manager, FlutterWindowsEngine *engine, const WindowSizeRequest &preferred_size, const WindowConstraints &preferred_constraints, LPCWSTR title, HWND parent)
static std::optional< Size > GetWindowSizeForClientSize(WindowsProcTable const &win32, Size const &client_size, std::optional< Size > smallest, std::optional< Size > biggest, DWORD window_style, DWORD extended_window_style, std::optional< HWND > const &owner_hwnd)
static std::unique_ptr< HostWindow > CreateRegularWindow(WindowManager *window_manager, FlutterWindowsEngine *engine, const WindowSizeRequest &preferred_size, const WindowConstraints &preferred_constraints, LPCWSTR title)
HostWindow * FindFirstEnabledDescendant() const
std::optional< LRESULT > OnTopLevelWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) const
virtual BOOL AdjustWindowRectExForDpi(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi) const
virtual BOOL EnableNonClientDpiScaling(HWND hwnd) const
virtual HRESULT DwmExtendFrameIntoClientArea(HWND hwnd, const MARGINS *pMarInset) const
virtual HRESULT DwmSetWindowAttribute(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute) const
virtual BOOL SetWindowCompositionAttribute(HWND hwnd, WINDOWCOMPOSITIONATTRIBDATA *data) const
UINT FlutterDesktopGetDpiForHWND(HWND hwnd)
#define DWMWA_USE_IMMERSIVE_DARK_MODE
union flutter::testing::@98::KeyboardChange::@0 content
UINT GetDpiForHWND(HWND hwnd)
LONG RectWidth(const RECT &r)
bool AreRectsEqual(const RECT &a, const RECT &b)
LONG RectHeight(const RECT &r)
ActualWindowSize client_size
bool has_view_constraints
double preferred_view_height
double preferred_view_width
bool has_preferred_view_size
WINDOWCOMPOSITIONATTRIB Attrib