22 constexpr
wchar_t kWindowClassName[] = L
"FLUTTER_HOST_WINDOW";
26 flutter::Size ClampToVirtualScreen(flutter::Size size) {
27 double const virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
28 double const virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
30 return flutter::Size(std::clamp(size.width(), 0.0, virtual_screen_width),
31 std::clamp(size.height(), 0.0, virtual_screen_height));
34 void EnableTransparentWindowBackground(HWND hwnd,
36 enum ACCENT_STATE { ACCENT_DISABLED = 0 };
38 struct ACCENT_POLICY {
39 ACCENT_STATE AccentState;
46 ACCENT_POLICY accent = {ACCENT_DISABLED, 2,
static_cast<DWORD
>(0), 0};
49 flutter::WindowsProcTable::WINDOWCOMPOSITIONATTRIB::WCA_ACCENT_POLICY,
51 .cbData =
sizeof(accent)};
56 MARGINS
const margins = {-1};
65 std::string GetLastErrorAsString() {
66 LPWSTR message_buffer =
nullptr;
68 if (DWORD
const size = FormatMessage(
69 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
70 FORMAT_MESSAGE_IGNORE_INSERTS,
71 nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
72 reinterpret_cast<LPTSTR
>(&message_buffer), 0,
nullptr)) {
73 std::wstring
const wide_message(message_buffer, size);
74 LocalFree(message_buffer);
75 message_buffer =
nullptr;
77 if (
int const buffer_size =
78 WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1,
nullptr,
79 0,
nullptr,
nullptr)) {
80 std::string
message(buffer_size, 0);
81 WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1, &
message[0],
82 buffer_size,
nullptr,
nullptr);
88 LocalFree(message_buffer);
90 std::ostringstream oss;
91 oss <<
"Format message failed with 0x" << std::hex << std::setfill(
'0')
92 << std::setw(8) << GetLastError();
98 bool IsClassRegistered(LPCWSTR class_name) {
99 WNDCLASSEX window_class = {};
100 return GetClassInfoEx(GetModuleHandle(
nullptr), class_name, &window_class) !=
110 #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
111 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
115 void UpdateTheme(HWND window) {
117 const wchar_t kGetPreferredBrightnessRegKey[] =
118 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
119 const wchar_t kGetPreferredBrightnessRegValue[] = L
"AppsUseLightTheme";
124 DWORD light_mode_size =
sizeof(light_mode);
125 LSTATUS
const result =
126 RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
127 kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD,
nullptr,
128 &light_mode, &light_mode_size);
130 if (result == ERROR_SUCCESS) {
131 BOOL enable_dark_mode = light_mode == 0;
133 &enable_dark_mode,
sizeof(enable_dark_mode));
138 void SetChildContent(HWND
content, HWND window) {
141 GetClientRect(window, &client_rect);
142 MoveWindow(
content, client_rect.left, client_rect.top,
143 client_rect.right - client_rect.left,
144 client_rect.bottom - client_rect.top,
true);
162 void AdjustAlongAxis(LONG dst_origin, LONG dst_size, LONG* origin, LONG* size) {
163 *size = std::min(dst_size, *size);
164 if (*origin < dst_origin)
165 *origin = dst_origin;
167 *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
170 RECT AdjustToFit(
const RECT& parent,
const RECT& child) {
171 auto new_x = child.left;
172 auto new_y = child.top;
179 result.right = new_x + new_width;
181 result.bottom = new_y + new_height;
185 flutter::BoxConstraints FromWindowConstraints(
187 std::optional<flutter::Size> smallest, biggest;
198 return flutter::BoxConstraints(smallest, biggest);
212 window_manager, engine, preferred_size,
213 FromWindowConstraints(preferred_constraints), title));
223 return std::unique_ptr<HostWindow>(
225 FromWindowConstraints(preferred_constraints), title,
226 parent ? parent : std::optional<HWND>()));
233 bool is_sized_to_content,
237 window_manager, engine, FromWindowConstraints(preferred_constraints),
238 is_sized_to_content, get_position_callback, parent));
243 : window_manager_(window_manager), engine_(engine) {}
248 auto view_window = std::make_unique<FlutterWindow>(
252 std::unique_ptr<FlutterWindowsView> view =
255 FML_CHECK(view !=
nullptr);
258 std::make_unique<FlutterWindowsViewController>(
nullptr, std::move(view));
266 if (!IsClassRegistered(kWindowClassName)) {
267 auto const idi_app_icon = 101;
268 WNDCLASSEX window_class = {};
269 window_class.cbSize =
sizeof(WNDCLASSEX);
270 window_class.style = CS_HREDRAW | CS_VREDRAW;
272 window_class.hInstance = GetModuleHandle(
nullptr);
274 LoadIcon(window_class.hInstance, MAKEINTRESOURCE(idi_app_icon));
275 if (!window_class.hIcon) {
276 window_class.hIcon = LoadIcon(
nullptr, IDI_APPLICATION);
278 window_class.hCursor = LoadCursor(
nullptr, IDC_ARROW);
279 window_class.lpszClassName = kWindowClassName;
281 FML_CHECK(RegisterClassEx(&window_class));
299 DwmGetWindowAttribute(
window_handle_, DWMWA_EXTENDED_FRAME_BOUNDS,
300 &frame_rect,
sizeof(frame_rect));
303 LONG
const left_dropshadow_width = frame_rect.left - window_rect.left;
304 LONG
const top_dropshadow_height = window_rect.top - frame_rect.top;
306 window_rect.left - left_dropshadow_width,
307 window_rect.top - top_dropshadow_height, 0, 0,
308 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
321 reinterpret_cast<LONG_PTR
>(
this));
328 if (!UnregisterClass(kWindowClassName, GetModuleHandle(
nullptr))) {
330 SetLastError(ERROR_SUCCESS);
336 wchar_t class_name[256];
337 if (!GetClassName(hwnd, class_name,
sizeof(class_name) /
sizeof(
wchar_t))) {
338 FML_LOG(ERROR) <<
"Failed to get class name for window handle " << hwnd
339 <<
": " << GetLastErrorAsString();
343 if (wcscmp(class_name, kWindowClassName) != 0) {
347 return reinterpret_cast<HostWindow*
>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
360 if (window !=
nullptr && child_content !=
nullptr) {
361 SetFocus(child_content);
370 auto*
const create_struct =
reinterpret_cast<CREATESTRUCT*
>(lparam);
371 auto*
const windows_proc_table =
374 EnableTransparentWindowBackground(hwnd, *windows_proc_table);
376 return window->HandleMessage(hwnd,
message, wparam, lparam);
379 return DefWindowProc(hwnd,
message, wparam, lparam);
397 case WM_NCLBUTTONDOWN: {
407 GetCursorPos(&cursorPos);
410 MAKELPARAM(cursorPos.x, cursorPos.y));
415 case WM_DPICHANGED: {
416 auto*
const new_scaled_window_rect =
reinterpret_cast<RECT*
>(lparam);
418 new_scaled_window_rect->right - new_scaled_window_rect->left;
420 new_scaled_window_rect->bottom - new_scaled_window_rect->top;
421 SetWindowPos(hwnd,
nullptr, new_scaled_window_rect->left,
422 new_scaled_window_rect->top, width, height,
423 SWP_NOZORDER | SWP_NOACTIVATE);
427 case WM_GETMINMAXINFO: {
429 GetWindowRect(hwnd, &window_rect);
431 GetClientRect(hwnd, &client_rect);
432 LONG
const non_client_width = (window_rect.right - window_rect.left) -
433 (client_rect.right - client_rect.left);
434 LONG
const non_client_height = (window_rect.bottom - window_rect.top) -
435 (client_rect.bottom - client_rect.top);
438 double const scale_factor =
439 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
441 MINMAXINFO* info =
reinterpret_cast<MINMAXINFO*
>(lparam);
442 Size
const min_physical_size = ClampToVirtualScreen(Size(
447 info->ptMinTrackSize.x = min_physical_size.width();
448 info->ptMinTrackSize.y = min_physical_size.height();
449 Size
const max_physical_size = ClampToVirtualScreen(Size(
454 info->ptMaxTrackSize.x = max_physical_size.width();
455 info->ptMaxTrackSize.y = max_physical_size.height();
461 if (child_content !=
nullptr) {
464 GetClientRect(hwnd, &client_rect);
465 MoveWindow(child_content, client_rect.left, client_rect.top,
466 client_rect.right - client_rect.left,
467 client_rect.bottom - client_rect.top, TRUE);
476 case WM_DWMCOLORIZATIONCOLORCHANGED:
488 return DefWindowProc(hwnd,
message, wparam, lparam);
514 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
521 window_info.dwStyle, window_info.dwExStyle,
nullptr);
528 window_size->height(),
529 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
553 auto const current_size = Size(client_size.width, client_size.height);
554 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
559 window_info.dwStyle, window_info.dwExStyle,
nullptr);
561 if (window_size && current_size != window_size) {
563 window_size->height(),
564 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
576 std::optional<FlutterEngineDisplayId> display_id) {
582 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
604 if (
auto const display =
606 monitor =
reinterpret_cast<HMONITOR
>(display->display_id);
610 MONITORINFO monitor_info;
611 monitor_info.cbSize =
sizeof(monitor_info);
612 if (!GetMonitorInfo(monitor, &monitor_info)) {
613 FML_LOG(ERROR) <<
"Cannot set window fullscreen because the monitor info "
617 auto const width =
RectWidth(monitor_info.rcMonitor);
618 auto const height =
RectHeight(monitor_info.rcMonitor);
619 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
628 WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
634 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
636 SetWindowPos(
window_handle_,
nullptr, monitor_info.rcMonitor.left,
637 monitor_info.rcMonitor.top, width, height,
638 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
652 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
656 MONITORINFO monitor_info;
657 monitor_info.cbSize =
sizeof(monitor_info);
658 GetMonitorInfo(monitor, &monitor_info);
666 monitor_info.rcWork)) {
667 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
671 SetWindowPos(
window_handle_,
nullptr, window_rect.left, window_rect.top,
673 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
686 auto const width =
static_cast<LONG
>(scale *
RectWidth(window_rect));
687 auto const height =
static_cast<LONG
>(scale *
RectHeight(window_rect));
688 window_rect.right = window_rect.left + width;
689 window_rect.bottom = window_rect.top + height;
690 window_rect = AdjustToFit(monitor_info.rcWork, window_rect);
693 SetWindowPos(
window_handle_,
nullptr, window_rect.left, window_rect.top,
695 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
701 ::CoCreateInstance(CLSID_TaskbarList,
nullptr, CLSCTX_INPROC_SERVER,
726 GetClientRect(hwnd, &rect);
728 static_cast<double>(USER_DEFAULT_SCREEN_DPI);
729 double const width = rect.right / dpr;
730 double const height = rect.bottom / dpr;
732 .width = rect.right / dpr,
733 .height = rect.bottom / dpr,
739 Size
const& client_size,
740 std::optional<Size> smallest,
741 std::optional<Size> biggest,
743 DWORD extended_window_style,
744 std::optional<HWND>
const& owner_hwnd) {
745 UINT
const dpi =
GetDpiForHWND(owner_hwnd ? *owner_hwnd :
nullptr);
746 double const scale_factor =
747 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
749 .right =
static_cast<LONG
>(client_size.width() * scale_factor),
750 .bottom =
static_cast<LONG
>(client_size.height() * scale_factor)};
753 extended_window_style, dpi)) {
754 FML_LOG(ERROR) <<
"Failed to run AdjustWindowRectExForDpi: "
755 << GetLastErrorAsString();
759 double width =
static_cast<double>(rect.right - rect.left);
760 double height =
static_cast<double>(rect.bottom - rect.top);
763 double const non_client_width = width - (client_size.width() * scale_factor);
764 double const non_client_height =
765 height - (client_size.height() * scale_factor);
767 flutter::Size min_physical_size = ClampToVirtualScreen(
768 flutter::Size(smallest->width() * scale_factor + non_client_width,
769 smallest->height() * scale_factor + non_client_height));
770 width = std::max(width, min_physical_size.width());
771 height = std::max(height, min_physical_size.height());
774 flutter::Size max_physical_size = ClampToVirtualScreen(
775 flutter::Size(biggest->width() * scale_factor + non_client_width,
776 biggest->height() * scale_factor + non_client_height));
777 width = std::min(width, max_physical_size.width());
778 height = std::min(height, max_physical_size.height());
781 return flutter::Size{width, height};
788 owned->EnableRecursively(enable);
807 std::vector<HostWindow*> owned_windows;
809 HWND owner_window_handle;
810 std::vector<HostWindow*>* owned_windows;
814 [](HWND hwnd, LPARAM lparam) -> BOOL {
815 auto*
const data =
reinterpret_cast<EnumData*
>(lparam);
816 if (GetWindow(hwnd, GW_OWNER) == data->owner_window_handle) {
819 data->owned_windows->push_back(window);
824 reinterpret_cast<LPARAM
>(&data));
826 return owned_windows;
830 if (HWND
const owner_window_handle = GetWindow(
GetWindowHandle(), GW_OWNER)) {
841 owned->DisableRecursively();
847 if (children.empty()) {
856 auto latest_child = *std::max_element(
858 return a->view_controller_->view()->view_id() <
859 b->view_controller_->view()->view_id();
863 if (child == latest_child) {
864 child->UpdateModalStateLayer();
866 child->DisableRecursively();
std::shared_ptr< WindowsProcTable > windows_proc_table()
void UpdateAccessibilityFeatures()
WindowProcDelegateManager * window_proc_delegate_manager()
std::shared_ptr< DisplayManagerWin32 > display_manager()
std::unique_ptr< FlutterWindowsView > CreateView(std::unique_ptr< WindowBindingHandler > window, bool is_sized_to_content, const BoxConstraints &box_constraints, FlutterWindowsViewSizingDelegate *sizing_delegate=nullptr)
virtual bool running() const
Microsoft::WRL::ComPtr< ITaskbarList2 > task_bar_list_
HWND GetWindowHandle() const
BoxConstraints box_constraints_
void InitializeFlutterView(HostWindowInitializationParams const ¶ms)
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)
HWND GetFlutterViewWindowHandle() const
FlutterWindowsEngine * engine_
static HostWindow * GetThisFromHandle(HWND hwnd)
HostWindow(WindowManager *window_manager, FlutterWindowsEngine *engine)
std::vector< HostWindow * > GetOwnedWindows() const
virtual bool GetFullscreen() const
void SetConstraints(const WindowConstraints &constraints)
static std::unique_ptr< HostWindow > CreateTooltipWindow(WindowManager *window_manager, FlutterWindowsEngine *engine, const WindowConstraints &preferred_constraints, bool is_sized_to_content, GetWindowPositionCallback get_position_callback, HWND parent)
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::@100::KeyboardChange::@0 content
WindowRect *(* GetWindowPositionCallback)(const WindowSize &child_size, const WindowRect &parent_rect, const WindowRect &output_rect)
UINT GetDpiForHWND(HWND hwnd)
LONG RectWidth(const RECT &r)
bool AreRectsEqual(const RECT &a, const RECT &b)
LONG RectHeight(const RECT &r)
DWORD extended_window_style
std::optional< HWND > const & owner_window
Rect const initial_window_rect
FlutterWindowsViewSizingDelegate * sizing_delegate
const BoxConstraints & box_constraints
ActualWindowSize client_size
bool has_view_constraints
double preferred_view_height
double preferred_view_width
bool has_preferred_view_size
WINDOWCOMPOSITIONATTRIB Attrib