Skip to content

Commit e1b8497

Browse files
authored
Merge pull request #32 from canonical/use-runner-windows
Add support for windows created in the runner
2 parents 72d71d6 + 3ca6fc5 commit e1b8497

File tree

7 files changed

+176
-91
lines changed

7 files changed

+176
-91
lines changed

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

Lines changed: 96 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ namespace {
1515

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

18+
// RAII wrapper for global Win32 ATOMs.
19+
struct AtomRAII {
20+
explicit AtomRAII(wchar_t const* name) : atom(GlobalAddAtom(name)) {}
21+
~AtomRAII() { GlobalDeleteAtom(atom); }
22+
ATOM const atom;
23+
};
24+
25+
// Atom used as the identifier for a window property that stores a pointer to a
26+
// |FlutterHostWindow| instance.
27+
AtomRAII const kWindowPropAtom(kWindowClassName);
28+
1829
// Clamps |size| to the size of the virtual screen. Both the parameter and
1930
// return size are in physical coordinates.
2031
flutter::Size ClampToVirtualScreen(flutter::Size size) {
@@ -212,7 +223,7 @@ bool IsClassRegistered(LPCWSTR class_name) {
212223
0;
213224
}
214225

215-
// Convert std::string to std::wstring.
226+
// Converts std::string to std::wstring.
216227
std::wstring StringToWstring(std::string_view str) {
217228
if (str.empty()) {
218229
return {};
@@ -238,7 +249,7 @@ std::wstring StringToWstring(std::string_view str) {
238249
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
239250
#endif
240251

241-
// Update the window frame's theme to match the system theme.
252+
// Updates the window frame's theme to match the system theme.
242253
void UpdateTheme(HWND window) {
243254
// Registry key for app theme preference.
244255
const wchar_t kGetPreferredBrightnessRegKey[] =
@@ -261,6 +272,31 @@ void UpdateTheme(HWND window) {
261272
}
262273
}
263274

275+
// Associates |instance| with the window |hwnd| as a window property.
276+
// Can be retrieved later using GetInstanceProperty.
277+
// Logs an error if setting the property fails.
278+
void SetInstanceProperty(HWND hwnd, flutter::FlutterHostWindow* instance) {
279+
if (!SetProp(hwnd, MAKEINTATOM(kWindowPropAtom.atom), instance)) {
280+
FML_LOG(ERROR) << "Failed to set up instance entry in the property list: "
281+
<< GetLastErrorAsString();
282+
}
283+
}
284+
285+
// Retrieves the instance pointer set with SetInstanceProperty, or returns
286+
// nullptr if the property was not set.
287+
flutter::FlutterHostWindow* GetInstanceProperty(HWND hwnd) {
288+
return reinterpret_cast<flutter::FlutterHostWindow*>(
289+
GetProp(hwnd, MAKEINTATOM(kWindowPropAtom.atom)));
290+
}
291+
292+
// Removes the instance property associated with |hwnd| previously set with
293+
// SetInstanceProperty. Logs an error if the property is not found.
294+
void RemoveInstanceProperty(HWND hwnd) {
295+
if (!RemoveProp(hwnd, MAKEINTATOM(kWindowPropAtom.atom))) {
296+
FML_LOG(ERROR) << "Failed to locate instance entry in the property list";
297+
}
298+
}
299+
264300
} // namespace
265301

266302
namespace flutter {
@@ -303,6 +339,35 @@ FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller,
303339
window_size ? *window_size : Size{CW_USEDEFAULT, CW_USEDEFAULT}};
304340
}();
305341

342+
// Set up the view.
343+
FlutterWindowsEngine* const engine = window_controller_->engine();
344+
auto view_window = std::make_unique<FlutterWindow>(
345+
initial_window_rect.width(), initial_window_rect.height(),
346+
engine->windows_proc_table());
347+
348+
std::unique_ptr<FlutterWindowsView> view =
349+
engine->CreateView(std::move(view_window));
350+
if (!view) {
351+
FML_LOG(ERROR) << "Failed to create view";
352+
return;
353+
}
354+
355+
view_controller_ =
356+
std::make_unique<FlutterWindowsViewController>(nullptr, std::move(view));
357+
FML_CHECK(engine->running());
358+
// Must happen after engine is running.
359+
view_controller_->view()->SendInitialBounds();
360+
// The Windows embedder listens to accessibility updates using the
361+
// view's HWND. The embedder's accessibility features may be stale if
362+
// the app was in headless mode.
363+
view_controller_->engine()->UpdateAccessibilityFeatures();
364+
365+
// Ensure that basic setup of the view controller was successful.
366+
if (!view_controller_->view()) {
367+
FML_LOG(ERROR) << "Failed to set up the view controller";
368+
return;
369+
}
370+
306371
// Register the window class.
307372
if (!IsClassRegistered(kWindowClassName)) {
308373
auto const idi_app_icon = 101;
@@ -354,44 +419,6 @@ FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller,
354419
window_rect.top - top_dropshadow_height, 0, 0,
355420
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
356421

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-
395422
UpdateTheme(hwnd);
396423

397424
SetChildContent(view_controller_->view()->GetWindowHandle());
@@ -426,24 +453,32 @@ FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller,
426453
window_handle_ = hwnd;
427454
}
428455

456+
FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller,
457+
HWND hwnd,
458+
FlutterWindowsView* view)
459+
: window_controller_(controller), window_handle_(hwnd) {
460+
SetInstanceProperty(hwnd, this);
461+
FML_CHECK(view != nullptr);
462+
child_content_ = view->GetWindowHandle();
463+
}
464+
429465
FlutterHostWindow::~FlutterHostWindow() {
430-
if (HWND const hwnd = window_handle_) {
431-
window_handle_ = nullptr;
432-
DestroyWindow(hwnd);
466+
RemoveInstanceProperty(window_handle_);
467+
HWND const hwnd = std::exchange(window_handle_, nullptr);
433468

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.
469+
if (view_controller_) {
470+
DestroyWindow(hwnd);
471+
// Unregister the window class. Fail silently if other windows are still
472+
// using the class, as only the last window can successfully unregister it.
437473
if (!UnregisterClass(kWindowClassName, GetModuleHandle(nullptr))) {
438-
// Clears the error information after the failed unregistering.
474+
// Clear the error state after the failed unregistration.
439475
SetLastError(ERROR_SUCCESS);
440476
}
441477
}
442478
}
443479

444480
FlutterHostWindow* FlutterHostWindow::GetThisFromHandle(HWND hwnd) {
445-
return reinterpret_cast<FlutterHostWindow*>(
446-
GetWindowLongPtr(hwnd, GWLP_USERDATA));
481+
return GetInstanceProperty(hwnd);
447482
}
448483

449484
HWND FlutterHostWindow::GetWindowHandle() const {
@@ -466,10 +501,9 @@ LRESULT FlutterHostWindow::WndProc(HWND hwnd,
466501
LPARAM lparam) {
467502
if (message == WM_NCCREATE) {
468503
auto* const create_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
469-
SetWindowLongPtr(hwnd, GWLP_USERDATA,
470-
reinterpret_cast<LONG_PTR>(create_struct->lpCreateParams));
471504
auto* const window =
472505
static_cast<FlutterHostWindow*>(create_struct->lpCreateParams);
506+
SetInstanceProperty(hwnd, window);
473507
window->window_handle_ = hwnd;
474508

475509
EnableFullDpiSupportIfAvailable(hwnd);
@@ -486,13 +520,15 @@ LRESULT FlutterHostWindow::HandleMessage(HWND hwnd,
486520
UINT message,
487521
WPARAM wparam,
488522
LPARAM lparam) {
489-
switch (message) {
490-
case WM_DESTROY:
491-
if (window_handle_ && quit_on_close_) {
492-
PostQuitMessage(0);
493-
}
523+
if (window_handle_ && view_controller_) {
524+
LRESULT* result;
525+
if (view_controller_->engine()->lifecycle_manager()->WindowProc(
526+
hwnd, message, wparam, lparam, result)) {
494527
return 0;
528+
}
529+
}
495530

531+
switch (message) {
496532
case WM_DPICHANGED: {
497533
auto* const new_scaled_window_rect = reinterpret_cast<RECT*>(lparam);
498534
LONG const width =
@@ -567,6 +603,10 @@ LRESULT FlutterHostWindow::HandleMessage(HWND hwnd,
567603
break;
568604
}
569605

606+
if (!view_controller_) {
607+
return 0;
608+
}
609+
570610
return DefWindowProc(hwnd, message, wparam, lparam);
571611
}
572612

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

Lines changed: 12 additions & 4 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,9 +29,14 @@ 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

33-
// Returns the instance pointer for |hwnd| or nulllptr if invalid.
39+
// Returns the instance pointer for |hwnd| or nullptr if invalid.
3440
static FlutterHostWindow* GetThisFromHandle(HWND hwnd);
3541

3642
// Returns the current window state.
@@ -43,7 +49,7 @@ class FlutterHostWindow {
4349
private:
4450
friend FlutterHostWindowController;
4551

46-
// Set the focus to the child view window of |window|.
52+
// Sets the focus to the child view window of |window|.
4753
static void FocusViewOf(FlutterHostWindow* window);
4854

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

7278
// Controller for this window.
73-
FlutterHostWindowController* const window_controller_;
79+
FlutterHostWindowController* const window_controller_ = nullptr;
7480

75-
// Controller for the view hosted by this window.
81+
// Controller for the view hosted in this window. Value-initialized if the
82+
// window is created from an existing top-level native window created by the
83+
// runner.
7684
std::unique_ptr<FlutterWindowsViewController> view_controller_;
7785

7886
// The window archetype.

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

Lines changed: 15 additions & 27 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+
FML_CHECK(view != nullptr);
66+
auto window = std::make_unique<FlutterHostWindow>(this, hwnd, view);
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 {
@@ -126,27 +131,24 @@ LRESULT FlutterHostWindowController::HandleMessage(HWND hwnd,
126131
switch (message) {
127132
case WM_NCDESTROY: {
128133
auto const it = std::find_if(
129-
windows_.begin(), windows_.end(), [hwnd](auto const& window) {
130-
return window.second->GetWindowHandle() == hwnd;
134+
windows_.begin(), windows_.end(), [hwnd](auto const& pair) {
135+
return pair.second->GetWindowHandle() == hwnd;
131136
});
132137
if (it != windows_.end()) {
133138
FlutterViewId const view_id = it->first;
134139
bool const quit_on_close = it->second->quit_on_close_;
135-
136140
windows_.erase(it);
137-
138141
SendOnWindowDestroyed(view_id);
139-
140142
if (quit_on_close) {
141-
DestroyAllWindows();
143+
PostQuitMessage(0);
142144
}
143145
}
144-
}
145146
return 0;
147+
}
146148
case WM_SIZE: {
147149
auto const it = std::find_if(
148-
windows_.begin(), windows_.end(), [hwnd](auto const& window) {
149-
return window.second->GetWindowHandle() == hwnd;
150+
windows_.begin(), windows_.end(), [hwnd](auto const& pair) {
151+
return pair.second->GetWindowHandle() == hwnd;
150152
});
151153
if (it != windows_.end()) {
152154
auto& [view_id, window] = *it;
@@ -179,20 +181,6 @@ FlutterWindowsEngine* FlutterHostWindowController::engine() const {
179181
return engine_;
180182
}
181183

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-
196184
Size FlutterHostWindowController::GetViewSize(FlutterViewId view_id) const {
197185
HWND const window_handle = GetHostWindow(view_id)->GetWindowHandle();
198186
RECT rect;

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class FlutterWindowsEngine;
2222
class FlutterHostWindowController {
2323
public:
2424
explicit FlutterHostWindowController(FlutterWindowsEngine* engine);
25-
virtual ~FlutterHostWindowController();
25+
virtual ~FlutterHostWindowController() = default;
2626

2727
// Creates a |FlutterHostWindow|, i.e., a native Win32 window with a
2828
// |FlutterWindow| parented to it. The child |FlutterWindow| implements a
@@ -34,6 +34,10 @@ class FlutterHostWindowController {
3434
virtual std::optional<WindowMetadata> CreateHostWindow(
3535
WindowCreationSettings const& settings);
3636

37+
// Creates a |FlutterHostWindow| from an existing top-level |hwnd| and |view|.
38+
virtual void CreateHostWindowFromExisting(HWND hwnd,
39+
FlutterWindowsView* view);
40+
3741
// Modifies the attributes of the window hosting the view with ID |view_id|
3842
// according to the given |settings|. A "onWindowChanged" message is sent if
3943
// at least one attribute is modified.
@@ -69,9 +73,6 @@ class FlutterHostWindowController {
6973
FlutterWindowsEngine* engine() const;
7074

7175
private:
72-
// Destroys all windows managed by this controller.
73-
void DestroyAllWindows();
74-
7576
// Retrieves the size of the view with ID |view_id|, in logical coordinates.
7677
Size GetViewSize(FlutterViewId view_id) const;
7778

0 commit comments

Comments
 (0)