16 constexpr
wchar_t kWindowClassName[] = L
"FLUTTER_HOST_WINDOW";
21 double const virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
22 double const virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
25 std::clamp(size.
height(), 0.0, virtual_screen_height));
28 void EnableTransparentWindowBackground(HWND hwnd,
30 enum ACCENT_STATE { ACCENT_DISABLED = 0 };
32 struct ACCENT_POLICY {
33 ACCENT_STATE AccentState;
40 ACCENT_POLICY accent = {ACCENT_DISABLED, 2,
static_cast<DWORD
>(0), 0};
43 flutter::WindowsProcTable::WINDOWCOMPOSITIONATTRIB::WCA_ACCENT_POLICY,
45 .cbData =
sizeof(accent)};
50 MARGINS
const margins = {-1};
59 std::string GetLastErrorAsString() {
60 LPWSTR message_buffer =
nullptr;
62 if (DWORD
const size = FormatMessage(
63 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
64 FORMAT_MESSAGE_IGNORE_INSERTS,
65 nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
66 reinterpret_cast<LPTSTR
>(&message_buffer), 0,
nullptr)) {
67 std::wstring
const wide_message(message_buffer, size);
68 LocalFree(message_buffer);
69 message_buffer =
nullptr;
71 if (
int const buffer_size =
72 WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1,
nullptr,
73 0,
nullptr,
nullptr)) {
74 std::string
message(buffer_size, 0);
75 WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1, &
message[0],
76 buffer_size,
nullptr,
nullptr);
82 LocalFree(message_buffer);
84 std::ostringstream oss;
85 oss <<
"Format message failed with 0x" << std::hex << std::setfill(
'0')
86 << std::setw(8) << GetLastError();
98 std::optional<flutter::Size> GetWindowSizeForClientSize(
101 std::optional<flutter::Size> smallest,
102 std::optional<flutter::Size> biggest,
104 DWORD extended_window_style,
107 double const scale_factor =
108 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
110 .right =
static_cast<LONG
>(client_size.
width() * scale_factor),
111 .bottom =
static_cast<LONG
>(client_size.
height() * scale_factor)};
114 extended_window_style, dpi)) {
115 FML_LOG(ERROR) <<
"Failed to run AdjustWindowRectExForDpi: "
116 << GetLastErrorAsString();
120 double width =
static_cast<double>(rect.right - rect.left);
121 double height =
static_cast<double>(rect.bottom - rect.top);
124 double const non_client_width = width - (client_size.
width() * scale_factor);
125 double const non_client_height =
126 height - (client_size.
height() * scale_factor);
129 flutter::Size(smallest->width() * scale_factor + non_client_width,
130 smallest->height() * scale_factor + non_client_height));
131 width = std::max(width, min_physical_size.
width());
132 height = std::max(height, min_physical_size.
height());
136 flutter::Size(biggest->width() * scale_factor + non_client_width,
137 biggest->height() * scale_factor + non_client_height));
138 width = std::min(width, max_physical_size.
width());
139 height = std::min(height, max_physical_size.
height());
147 bool IsClassRegistered(LPCWSTR class_name) {
148 WNDCLASSEX window_class = {};
149 return GetClassInfoEx(GetModuleHandle(
nullptr), class_name, &window_class) !=
159 #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
160 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
164 void UpdateTheme(HWND window) {
166 const wchar_t kGetPreferredBrightnessRegKey[] =
167 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
168 const wchar_t kGetPreferredBrightnessRegValue[] = L
"AppsUseLightTheme";
173 DWORD light_mode_size =
sizeof(light_mode);
174 LSTATUS
const result =
175 RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
176 kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD,
nullptr,
177 &light_mode, &light_mode_size);
179 if (result == ERROR_SUCCESS) {
180 BOOL enable_dark_mode = light_mode == 0;
182 &enable_dark_mode,
sizeof(enable_dark_mode));
187 void SetChildContent(HWND
content, HWND window) {
190 GetClientRect(window, &client_rect);
191 MoveWindow(
content, client_rect.left, client_rect.top,
192 client_rect.right - client_rect.left,
193 client_rect.bottom - client_rect.top,
true);
204 DWORD window_style = WS_OVERLAPPEDWINDOW;
205 DWORD extended_window_style = 0;
206 std::optional<Size> smallest = std::nullopt;
207 std::optional<Size> biggest = std::nullopt;
222 Rect const initial_window_rect = [&]() ->
Rect {
223 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
227 smallest, biggest, window_style, extended_window_style,
nullptr);
228 return {{CW_USEDEFAULT, CW_USEDEFAULT},
229 window_size ? *window_size :
Size{CW_USEDEFAULT, CW_USEDEFAULT}};
233 auto view_window = std::make_unique<FlutterWindow>(
234 initial_window_rect.
width(), initial_window_rect.
height(),
237 std::unique_ptr<FlutterWindowsView> view =
239 if (view ==
nullptr) {
240 FML_LOG(ERROR) <<
"Failed to create view";
244 std::unique_ptr<FlutterWindowsViewController> view_controller =
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;
258 window_class.lpfnWndProc = HostWindow::WndProc;
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 if (!RegisterClassEx(&window_class)) {
269 FML_LOG(ERROR) <<
"Cannot register window class " << kWindowClassName
270 <<
": " << GetLastErrorAsString();
276 HWND hwnd = CreateWindowEx(
277 extended_window_style, kWindowClassName, L
"", window_style,
278 initial_window_rect.
left(), initial_window_rect.
top(),
279 initial_window_rect.
width(), initial_window_rect.
height(),
nullptr,
282 FML_LOG(ERROR) <<
"Cannot create window: " << GetLastErrorAsString();
291 DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame_rect,
294 GetWindowRect(hwnd, &window_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;
297 SetWindowPos(hwnd,
nullptr, window_rect.left - left_dropshadow_width,
298 window_rect.top - top_dropshadow_height, 0, 0,
299 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
303 SetChildContent(view_controller->view()->GetWindowHandle(), hwnd);
310 ShowWindow(hwnd, SW_SHOWNORMAL);
311 return std::unique_ptr<HostWindow>(
new HostWindow(
313 std::move(view_controller),
BoxConstraints(smallest, biggest), hwnd));
316 HostWindow::HostWindow(
320 std::unique_ptr<FlutterWindowsViewController> view_controller,
323 : window_manager_(window_manager),
325 archetype_(archetype),
326 view_controller_(std::move(view_controller)),
327 window_handle_(hwnd),
328 box_constraints_(box_constraints) {
329 SetWindowLongPtr(hwnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR
>(
this));
333 if (view_controller_) {
336 if (!UnregisterClass(kWindowClassName, GetModuleHandle(
nullptr))) {
338 SetLastError(ERROR_SUCCESS);
344 return reinterpret_cast<HostWindow*
>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
348 return window_handle_;
351 void HostWindow::FocusViewOf(
HostWindow* window) {
352 auto child_content = window->view_controller_->view()->GetWindowHandle();
353 if (window !=
nullptr && child_content !=
nullptr) {
354 SetFocus(child_content);
358 LRESULT HostWindow::WndProc(HWND hwnd,
363 auto*
const create_struct =
reinterpret_cast<CREATESTRUCT*
>(lparam);
364 auto*
const windows_proc_table =
365 static_cast<WindowsProcTable*
>(create_struct->lpCreateParams);
366 windows_proc_table->EnableNonClientDpiScaling(hwnd);
367 EnableTransparentWindowBackground(hwnd, *windows_proc_table);
369 return window->HandleMessage(hwnd,
message, wparam, lparam);
372 return DefWindowProc(hwnd,
message, wparam, lparam);
375 LRESULT HostWindow::HandleMessage(HWND hwnd,
380 window_handle_,
message, wparam, lparam);
386 case WM_DPICHANGED: {
387 auto*
const new_scaled_window_rect =
reinterpret_cast<RECT*
>(lparam);
389 new_scaled_window_rect->right - new_scaled_window_rect->left;
391 new_scaled_window_rect->bottom - new_scaled_window_rect->top;
392 SetWindowPos(hwnd,
nullptr, new_scaled_window_rect->left,
393 new_scaled_window_rect->top, width, height,
394 SWP_NOZORDER | SWP_NOACTIVATE);
398 case WM_GETMINMAXINFO: {
400 GetWindowRect(hwnd, &window_rect);
402 GetClientRect(hwnd, &client_rect);
403 LONG
const non_client_width = (window_rect.right - window_rect.left) -
404 (client_rect.right - client_rect.left);
405 LONG
const non_client_height = (window_rect.bottom - window_rect.top) -
406 (client_rect.bottom - client_rect.top);
409 double const scale_factor =
410 static_cast<double>(dpi) / USER_DEFAULT_SCREEN_DPI;
412 MINMAXINFO* info =
reinterpret_cast<MINMAXINFO*
>(lparam);
413 Size
const min_physical_size = ClampToVirtualScreen(Size(
414 box_constraints_.
smallest().
width() * scale_factor + non_client_width,
418 info->ptMinTrackSize.x = min_physical_size.width();
419 info->ptMinTrackSize.y = min_physical_size.height();
420 Size
const max_physical_size = ClampToVirtualScreen(Size(
421 box_constraints_.
biggest().
width() * scale_factor + non_client_width,
425 info->ptMaxTrackSize.x = max_physical_size.width();
426 info->ptMaxTrackSize.y = max_physical_size.height();
431 auto child_content = view_controller_->view()->GetWindowHandle();
432 if (child_content !=
nullptr) {
435 GetClientRect(hwnd, &client_rect);
436 MoveWindow(child_content, client_rect.left, client_rect.top,
437 client_rect.right - client_rect.left,
438 client_rect.bottom - client_rect.top, TRUE);
447 case WM_MOUSEACTIVATE:
451 case WM_DWMCOLORIZATIONCOLORCHANGED:
459 if (!view_controller_) {
463 return DefWindowProc(hwnd,
message, wparam, lparam);
467 WINDOWINFO window_info = {.cbSize =
sizeof(WINDOWINFO)};
468 GetWindowInfo(window_handle_, &window_info);
470 std::optional<Size> smallest, biggest;
481 std::optional<Size>
const window_size = GetWindowSizeForClientSize(
485 window_info.dwStyle, window_info.dwExStyle,
nullptr);
488 SetWindowPos(window_handle_, NULL, 0, 0, window_size->width(),
489 window_size->height(),
490 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
std::shared_ptr< WindowsProcTable > windows_proc_table()
void UpdateAccessibilityFeatures()
WindowProcDelegateManager * window_proc_delegate_manager()
virtual bool running() const
std::unique_ptr< FlutterWindowsView > CreateView(std::unique_ptr< WindowBindingHandler > window)
HWND GetWindowHandle() const
void SetContentSize(const WindowSizing &size)
static std::unique_ptr< HostWindow > CreateRegularWindow(WindowManager *controller, FlutterWindowsEngine *engine, const WindowSizing &content_size)
static HostWindow * GetThisFromHandle(HWND hwnd)
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 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
#define DWMWA_USE_IMMERSIVE_DARK_MODE
union flutter::testing::@90::KeyboardChange::@0 content
UINT GetDpiForHWND(HWND hwnd)
double preferred_view_width
bool has_view_constraints
bool has_preferred_view_size
double preferred_view_height
WINDOWCOMPOSITIONATTRIB Attrib