Skip to content

Commit fe9fa99

Browse files
committed
Add support for windows created in the runner
1 parent b902fa5 commit fe9fa99

8 files changed

+203
-118
lines changed

engine/src/flutter/shell/platform/windows/flutter_host_window.cc

Lines changed: 87 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ namespace {
1515

1616
constexpr wchar_t kWindowClassName[] = L"FLUTTER_HOST_WINDOW";
1717

18+
// RAII wrapper for global Win32 ATOMs.
19+
struct AtomRAII {
20+
AtomRAII(wchar_t const* name) : atom(GlobalAddAtom(name)) {}
21+
~AtomRAII() { GlobalDeleteAtom(atom); }
22+
ATOM const atom;
23+
};
24+
25+
// Atom representing a window property that stores a pointer to this host
26+
// window. This property serves as an alternative way to access the window in
27+
// |FlutterHostWindow::GetThisFromHandle| for windows created from existing
28+
// views, since the `GWLP_USERDATA` of such windows may point to something other
29+
// than a |FlutterHostWindow|.
30+
AtomRAII const kWindowPropAtom(kWindowClassName);
31+
1832
// Clamps |size| to the size of the virtual screen. Both the parameter and
1933
// return size are in physical coordinates.
2034
flutter::Size ClampToVirtualScreen(flutter::Size size) {
@@ -303,6 +317,40 @@ FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller,
303317
window_size ? *window_size : Size{CW_USEDEFAULT, CW_USEDEFAULT}};
304318
}();
305319

320+
// Set up the view.
321+
FlutterWindowsEngine* const engine = window_controller_->engine();
322+
auto view_window = std::make_unique<FlutterWindow>(
323+
initial_window_rect.width(), initial_window_rect.height(),
324+
engine->windows_proc_table());
325+
326+
std::unique_ptr<FlutterWindowsView> view =
327+
engine->CreateView(std::move(view_window));
328+
if (!view) {
329+
FML_LOG(ERROR) << "Failed to create view";
330+
return;
331+
}
332+
333+
view_controller_ =
334+
std::make_unique<FlutterWindowsViewController>(nullptr, std::move(view));
335+
336+
// Launch the engine if it is not running already.
337+
if (!engine->running() && !engine->Run()) {
338+
FML_LOG(ERROR) << "Failed to launch engine";
339+
return;
340+
}
341+
// Must happen after engine is running.
342+
view_controller_->view()->SendInitialBounds();
343+
// The Windows embedder listens to accessibility updates using the
344+
// view's HWND. The embedder's accessibility features may be stale if
345+
// the app was in headless mode.
346+
view_controller_->engine()->UpdateAccessibilityFeatures();
347+
348+
// Ensure that basic setup of the view controller was successful.
349+
if (!view_controller_->view()) {
350+
FML_LOG(ERROR) << "Failed to set up the view controller";
351+
return;
352+
}
353+
306354
// Register the window class.
307355
if (!IsClassRegistered(kWindowClassName)) {
308356
auto const idi_app_icon = 101;
@@ -354,44 +402,6 @@ FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller,
354402
window_rect.top - top_dropshadow_height, 0, 0,
355403
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
356404

357-
// Set up the view.
358-
RECT client_rect;
359-
GetClientRect(hwnd, &client_rect);
360-
int const width = client_rect.right - client_rect.left;
361-
int const height = client_rect.bottom - client_rect.top;
362-
363-
FlutterWindowsEngine* const engine = window_controller_->engine();
364-
auto view_window = std::make_unique<FlutterWindow>(
365-
width, height, engine->windows_proc_table());
366-
367-
std::unique_ptr<FlutterWindowsView> view =
368-
engine->CreateView(std::move(view_window));
369-
if (!view) {
370-
FML_LOG(ERROR) << "Failed to create view";
371-
return;
372-
}
373-
374-
view_controller_ =
375-
std::make_unique<FlutterWindowsViewController>(nullptr, std::move(view));
376-
377-
// Launch the engine if it is not running already.
378-
if (!engine->running() && !engine->Run()) {
379-
FML_LOG(ERROR) << "Failed to launch engine";
380-
return;
381-
}
382-
// Must happen after engine is running.
383-
view_controller_->view()->SendInitialBounds();
384-
// The Windows embedder listens to accessibility updates using the
385-
// view's HWND. The embedder's accessibility features may be stale if
386-
// the app was in headless mode.
387-
view_controller_->engine()->UpdateAccessibilityFeatures();
388-
389-
// Ensure that basic setup of the view controller was successful.
390-
if (!view_controller_->view()) {
391-
FML_LOG(ERROR) << "Failed to set up the view controller";
392-
return;
393-
}
394-
395405
UpdateTheme(hwnd);
396406

397407
SetChildContent(view_controller_->view()->GetWindowHandle());
@@ -426,26 +436,46 @@ FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller,
426436
window_handle_ = hwnd;
427437
}
428438

439+
FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller,
440+
HWND hwnd,
441+
FlutterWindowsView* view)
442+
: window_controller_(controller), window_handle_(hwnd) {
443+
if (!SetProp(hwnd, MAKEINTATOM(kWindowPropAtom.atom), this)) {
444+
FML_LOG(ERROR) << "Failed to set up entry in the window property list";
445+
return;
446+
}
447+
child_content_ = view->GetWindowHandle();
448+
}
449+
429450
FlutterHostWindow::~FlutterHostWindow() {
430-
if (HWND const hwnd = window_handle_) {
431-
window_handle_ = nullptr;
451+
HWND const hwnd = window_handle_;
452+
window_handle_ = nullptr;
453+
if (view_controller_) {
432454
DestroyWindow(hwnd);
433-
434-
// Unregisters the window class. It will fail silently if there are
435-
// other windows using the class, as only the last window can
436-
// successfully unregister the class.
455+
// Unregister the window class. Fail silently if other windows are still
456+
// using the class, as only the last window can successfully unregister it.
437457
if (!UnregisterClass(kWindowClassName, GetModuleHandle(nullptr))) {
438-
// Clears the error information after the failed unregistering.
458+
// Clear the error state after the failed unregistration.
439459
SetLastError(ERROR_SUCCESS);
440460
}
441461
}
442462
}
443463

444464
FlutterHostWindow* FlutterHostWindow::GetThisFromHandle(HWND hwnd) {
465+
// For native windows created by the runner, retrieve the instance pointer
466+
// from a window property.
467+
if (HANDLE const data = GetProp(hwnd, MAKEINTATOM(kWindowPropAtom.atom))) {
468+
return reinterpret_cast<FlutterHostWindow*>(data);
469+
}
470+
// Otherwise, retrieve the instance pointer from the window's user data.
445471
return reinterpret_cast<FlutterHostWindow*>(
446472
GetWindowLongPtr(hwnd, GWLP_USERDATA));
447473
}
448474

475+
bool FlutterHostWindow::HasThisAsProperty(HWND hwnd) {
476+
return GetProp(hwnd, MAKEINTATOM(kWindowPropAtom.atom)) != nullptr;
477+
}
478+
449479
HWND FlutterHostWindow::GetWindowHandle() const {
450480
return window_handle_;
451481
}
@@ -487,12 +517,6 @@ LRESULT FlutterHostWindow::HandleMessage(HWND hwnd,
487517
WPARAM wparam,
488518
LPARAM lparam) {
489519
switch (message) {
490-
case WM_DESTROY:
491-
if (window_handle_ && quit_on_close_) {
492-
PostQuitMessage(0);
493-
}
494-
return 0;
495-
496520
case WM_DPICHANGED: {
497521
auto* const new_scaled_window_rect = reinterpret_cast<RECT*>(lparam);
498522
LONG const width =
@@ -567,6 +591,18 @@ LRESULT FlutterHostWindow::HandleMessage(HWND hwnd,
567591
break;
568592
}
569593

594+
if (!view_controller_) {
595+
return 0;
596+
}
597+
598+
if (window_handle_) {
599+
LRESULT* result;
600+
if (view_controller_->engine()->lifecycle_manager()->WindowProc(
601+
hwnd, message, wparam, lparam, result)) {
602+
return 0;
603+
}
604+
}
605+
570606
return DefWindowProc(hwnd, message, wparam, lparam);
571607
}
572608

engine/src/flutter/shell/platform/windows/flutter_host_window.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
namespace flutter {
1818

1919
class FlutterHostWindowController;
20+
class FlutterWindowsView;
2021
class FlutterWindowsViewController;
2122

2223
// A Win32 window that hosts a |FlutterWindow| in its client area.
@@ -28,11 +29,21 @@ class FlutterHostWindow {
2829
// |FlutterHostWindow::GetWindowHandle|.
2930
FlutterHostWindow(FlutterHostWindowController* controller,
3031
WindowCreationSettings const& settings);
32+
// Creates a |FlutterHostWindow| from an existing |view| associated with a
33+
// top-level |hwnd|. Used when the native window is created by the runner.
34+
FlutterHostWindow(FlutterHostWindowController* controller,
35+
HWND hwnd,
36+
FlutterWindowsView* view);
3137
virtual ~FlutterHostWindow();
3238

3339
// Returns the instance pointer for |hwnd| or nulllptr if invalid.
3440
static FlutterHostWindow* GetThisFromHandle(HWND hwnd);
3541

42+
// Returns true if |hwnd| has an instance pointer set as a window property.
43+
// This is the case only if |hwnd| was created by the runner with an
44+
// associated |FlutterHostWindow|.
45+
static bool HasThisAsProperty(HWND hwnd);
46+
3647
// Returns the current window state.
3748
WindowState GetState() const;
3849

@@ -43,7 +54,7 @@ class FlutterHostWindow {
4354
private:
4455
friend FlutterHostWindowController;
4556

46-
// Set the focus to the child view window of |window|.
57+
// Sets the focus to the child view window of |window|.
4758
static void FocusViewOf(FlutterHostWindow* window);
4859

4960
// OS callback called by message pump. Handles the WM_NCCREATE message which
@@ -70,9 +81,11 @@ class FlutterHostWindow {
7081
void SetTitle(std::string_view title) const;
7182

7283
// Controller for this window.
73-
FlutterHostWindowController* const window_controller_;
84+
FlutterHostWindowController* const window_controller_ = nullptr;
7485

75-
// Controller for the view hosted by this window.
86+
// Controller for the view hosted in this window. Value-initialized if the
87+
// window is created from an existing top-level native window created by the
88+
// runner.
7689
std::unique_ptr<FlutterWindowsViewController> view_controller_;
7790

7891
// The window archetype.

engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc

Lines changed: 49 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ FlutterHostWindowController::FlutterHostWindowController(
3232
FlutterWindowsEngine* engine)
3333
: engine_(engine) {}
3434

35-
FlutterHostWindowController::~FlutterHostWindowController() {
36-
DestroyAllWindows();
37-
}
38-
3935
std::optional<WindowMetadata> FlutterHostWindowController::CreateHostWindow(
4036
WindowCreationSettings const& settings) {
4137
auto window = std::make_unique<FlutterHostWindow>(this, settings);
@@ -63,6 +59,15 @@ std::optional<WindowMetadata> FlutterHostWindowController::CreateHostWindow(
6359
return result;
6460
}
6561

62+
void FlutterHostWindowController::CreateHostWindowFromExisting(
63+
HWND hwnd,
64+
FlutterWindowsView* view) {
65+
auto window = std::make_unique<FlutterHostWindow>(this, hwnd, view);
66+
67+
FlutterViewId const view_id = view->view_id();
68+
windows_[view_id] = std::move(window);
69+
}
70+
6671
bool FlutterHostWindowController::ModifyHostWindow(
6772
FlutterViewId view_id,
6873
WindowModificationSettings const& settings) const {
@@ -123,49 +128,49 @@ LRESULT FlutterHostWindowController::HandleMessage(HWND hwnd,
123128
UINT message,
124129
WPARAM wparam,
125130
LPARAM lparam) {
126-
switch (message) {
127-
case WM_NCDESTROY: {
128-
auto const it = std::find_if(
129-
windows_.begin(), windows_.end(), [hwnd](auto const& window) {
130-
return window.second->GetWindowHandle() == hwnd;
131-
});
132-
if (it != windows_.end()) {
133-
FlutterViewId const view_id = it->first;
134-
bool const quit_on_close = it->second->quit_on_close_;
135-
136-
windows_.erase(it);
137-
138-
SendOnWindowDestroyed(view_id);
139-
140-
if (quit_on_close) {
141-
DestroyAllWindows();
131+
if (engine_->running()) {
132+
switch (message) {
133+
case WM_NCDESTROY:
134+
if (engine_->running()) {
135+
auto const it = std::find_if(
136+
windows_.begin(), windows_.end(), [hwnd](auto const& pair) {
137+
return pair.second->GetWindowHandle() == hwnd;
138+
});
139+
if (it != windows_.end()) {
140+
FlutterViewId const view_id = it->first;
141+
bool const quit_on_close = it->second->quit_on_close_;
142+
windows_.erase(it);
143+
SendOnWindowDestroyed(view_id);
144+
if (quit_on_close) {
145+
PostQuitMessage(0);
146+
}
147+
}
142148
}
143-
}
144-
}
145-
return 0;
146-
case WM_SIZE: {
147-
auto const it = std::find_if(
148-
windows_.begin(), windows_.end(), [hwnd](auto const& window) {
149-
return window.second->GetWindowHandle() == hwnd;
150-
});
151-
if (it != windows_.end()) {
152-
auto& [view_id, window] = *it;
153-
if (window->archetype_ == WindowArchetype::kRegular) {
154-
window->state_ = (wparam == SIZE_MAXIMIZED) ? WindowState::kMaximized
155-
: (wparam == SIZE_MINIMIZED)
156-
? WindowState::kMinimized
157-
: WindowState::kRestored;
149+
return 0;
150+
case WM_SIZE: {
151+
auto const it = std::find_if(
152+
windows_.begin(), windows_.end(), [hwnd](auto const& pair) {
153+
return pair.second->GetWindowHandle() == hwnd;
154+
});
155+
if (it != windows_.end()) {
156+
auto& [view_id, window] = *it;
157+
if (window->archetype_ == WindowArchetype::kRegular) {
158+
window->state_ =
159+
(wparam == SIZE_MAXIMIZED) ? WindowState::kMaximized
160+
: (wparam == SIZE_MINIMIZED) ? WindowState::kMinimized
161+
: WindowState::kRestored;
162+
}
163+
SendOnWindowChanged(view_id, GetViewSize(view_id), std::nullopt);
158164
}
159-
SendOnWindowChanged(view_id, GetViewSize(view_id), std::nullopt);
160-
}
161-
} break;
162-
default:
163-
break;
164-
}
165+
} break;
166+
default:
167+
break;
168+
}
165169

166-
if (FlutterHostWindow* const window =
167-
FlutterHostWindow::GetThisFromHandle(hwnd)) {
168-
return window->HandleMessage(hwnd, message, wparam, lparam);
170+
if (FlutterHostWindow* const window =
171+
FlutterHostWindow::GetThisFromHandle(hwnd)) {
172+
return window->HandleMessage(hwnd, message, wparam, lparam);
173+
}
169174
}
170175
return DefWindowProc(hwnd, message, wparam, lparam);
171176
}
@@ -179,20 +184,6 @@ FlutterWindowsEngine* FlutterHostWindowController::engine() const {
179184
return engine_;
180185
}
181186

182-
void FlutterHostWindowController::DestroyAllWindows() {
183-
if (!windows_.empty()) {
184-
// Destroy windows in reverse order of creation.
185-
for (auto it = std::prev(windows_.end());
186-
it != std::prev(windows_.begin());) {
187-
auto const current = it--;
188-
auto const& [view_id, window] = *current;
189-
if (window->GetWindowHandle()) {
190-
DestroyHostWindow(view_id);
191-
}
192-
}
193-
}
194-
}
195-
196187
Size FlutterHostWindowController::GetViewSize(FlutterViewId view_id) const {
197188
HWND const window_handle = GetHostWindow(view_id)->GetWindowHandle();
198189
RECT rect;

0 commit comments

Comments
 (0)