| Flutter Windows Embedder |

windows_lifecycle_manager.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "windows_lifecycle_manager.h"
6
7 #include <TlHelp32.h>
8 #include <WinUser.h>
9 #include <Windows.h>
10 #include <tchar.h>
11
12 #include "flutter/shell/platform/windows/flutter_windows_engine.h"
13
14 namespace flutter {
15
16 WindowsLifecycleManager::WindowsLifecycleManager(FlutterWindowsEngine* engine)
17 : engine_(engine) {}
18
19 WindowsLifecycleManager::~WindowsLifecycleManager() {}
20
21 void WindowsLifecycleManager::Quit(std::optional<HWND> hwnd,
22 std::optional<WPARAM> wparam,
23 std::optional<LPARAM> lparam,
24 UINT exit_code) {
25 if (!hwnd.has_value()) {
26 ::PostQuitMessage(exit_code);
27 } else {
28 BASE_CHECK(wparam.has_value() && lparam.has_value());
29 sent_close_messages_[std::make_tuple(*hwnd, *wparam, *lparam)]++;
30 DispatchMessage(*hwnd, WM_CLOSE, *wparam, *lparam);
31 }
32 }
33
34 void WindowsLifecycleManager::DispatchMessage(HWND hwnd,
35 UINT message,
36 WPARAM wparam,
37 LPARAM lparam) {
38 PostMessage(hwnd, message, wparam, lparam);
39 }
40
41 bool WindowsLifecycleManager::HandleCloseMessage(HWND hwnd,
42 WPARAM wparam,
43 LPARAM lparam) {
44 if (!process_exit_) {
45 return false;
46 }
47 auto key = std::make_tuple(hwnd, wparam, lparam);
48 auto itr = sent_close_messages_.find(key);
49 if (itr != sent_close_messages_.end()) {
50 if (itr->second == 1) {
51 sent_close_messages_.erase(itr);
52 } else {
53 sent_close_messages_[key]--;
54 }
55 return false;
56 }
57 if (IsLastWindowOfProcess()) {
58 engine_->RequestApplicationQuit(hwnd, wparam, lparam,
59 AppExitType::cancelable);
60 return true;
61 }
62 return false;
63 }
64
65 bool WindowsLifecycleManager::WindowProc(HWND hwnd,
66 UINT msg,
67 WPARAM wpar,
68 LPARAM lpar,
69 LRESULT* result) {
70 switch (msg) {
71 // When WM_CLOSE is received from the final window of an application, we
72 // send a request to the framework to see if the app should exit. If it
73 // is, we re-dispatch a new WM_CLOSE message. In order to allow the new
74 // message to reach other delegates, we ignore it here.
75 case WM_CLOSE:
76 return HandleCloseMessage(hwnd, wpar, lpar);
77
78 // DWM composition can be disabled on Windows 7.
79 // Notify the engine as this can result in screen tearing.
80 case WM_DWMCOMPOSITIONCHANGED:
81 engine_->OnDwmCompositionChanged();
82 break;
83
84 case WM_SIZE:
85 if (wpar == SIZE_MAXIMIZED || wpar == SIZE_RESTORED) {
86 OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
87 } else if (wpar == SIZE_MINIMIZED) {
88 OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
89 }
90 break;
91
92 case WM_SHOWWINDOW:
93 if (!wpar) {
94 OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
95 } else {
96 OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
97 }
98 break;
99
100 case WM_DESTROY:
101 OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
102 break;
103 }
104 return false;
105 }
106
107 class ThreadSnapshot {
108 public:
109 ThreadSnapshot() {
110 thread_snapshot_ = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
111 }
112 ~ThreadSnapshot() {
113 if (thread_snapshot_ != INVALID_HANDLE_VALUE) {
114 ::CloseHandle(thread_snapshot_);
115 }
116 }
117
118 std::optional<THREADENTRY32> GetFirstThread() {
119 if (thread_snapshot_ == INVALID_HANDLE_VALUE) {
120 FML_LOG(ERROR) << "Failed to get thread snapshot";
121 return std::nullopt;
122 }
123 THREADENTRY32 thread;
124 thread.dwSize = sizeof(thread);
125 if (!::Thread32First(thread_snapshot_, &thread)) {
126 DWORD error_num = ::GetLastError();
127 char msg[256];
128 ::FormatMessageA(
129 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
130 error_num, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msg, 256,
131 nullptr);
132 FML_LOG(ERROR) << "Failed to get thread(" << error_num << "): " << msg;
133 return std::nullopt;
134 }
135 return thread;
136 }
137
138 bool GetNextThread(THREADENTRY32& thread) {
139 if (thread_snapshot_ == INVALID_HANDLE_VALUE) {
140 return false;
141 }
142 return ::Thread32Next(thread_snapshot_, &thread);
143 }
144
145 private:
146 HANDLE thread_snapshot_;
147 };
148
149 static int64_t NumWindowsForThread(const THREADENTRY32& thread) {
150 int64_t num_windows = 0;
151 ::EnumThreadWindows(
152 thread.th32ThreadID,
153 [](HWND hwnd, LPARAM lparam) {
154 int64_t* windows_ptr = reinterpret_cast<int64_t*>(lparam);
155 if (::GetParent(hwnd) == nullptr) {
156 (*windows_ptr)++;
157 }
158 return *windows_ptr <= 1 ? TRUE : FALSE;
159 },
160 reinterpret_cast<LPARAM>(&num_windows));
161 return num_windows;
162 }
163
164 bool WindowsLifecycleManager::IsLastWindowOfProcess() {
165 DWORD pid = ::GetCurrentProcessId();
166 ThreadSnapshot thread_snapshot;
167 std::optional<THREADENTRY32> first_thread = thread_snapshot.GetFirstThread();
168 if (!first_thread.has_value()) {
169 FML_LOG(ERROR) << "No first thread found";
170 return true;
171 }
172
173 int num_windows = 0;
174 THREADENTRY32 thread = *first_thread;
175 do {
176 if (thread.th32OwnerProcessID == pid) {
177 num_windows += NumWindowsForThread(thread);
178 if (num_windows > 1) {
179 return false;
180 }
181 }
182 } while (thread_snapshot.GetNextThread(thread));
183
184 return num_windows <= 1;
185 }
186
187 void WindowsLifecycleManager::BeginProcessingLifecycle() {
188 process_lifecycle_ = true;
189 }
190
191 void WindowsLifecycleManager::BeginProcessingExit() {
192 process_exit_ = true;
193 }
194
195 void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) {
196 if (state_ == state) {
197 return;
198 }
199 state_ = state;
200 if (engine_ && process_lifecycle_) {
201 const char* state_name = AppLifecycleStateToString(state);
202 engine_->SendPlatformMessage("flutter/lifecycle",
203 reinterpret_cast<const uint8_t*>(state_name),
204 strlen(state_name), nullptr, nullptr);
205 }
206 }
207
208 void WindowsLifecycleManager::UpdateState() {
209 AppLifecycleState new_state = AppLifecycleState::kResumed;
210 if (visible_windows_.empty()) {
211 new_state = AppLifecycleState::kHidden;
212 } else if (focused_windows_.empty()) {
213 new_state = AppLifecycleState::kInactive;
214 }
215 SetLifecycleState(new_state);
216 }
217
218 void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd,
219 WindowStateEvent event) {
220 // Instead of updating the state immediately, remember all
221 // changes to individual window and then update the state in next run loop
222 // turn. Otherwise the application would be temporarily deactivated when
223 // switching focus between windows for example.
224 if (!update_state_scheduled_) {
225 update_state_scheduled_ = true;
226 // Task runner will be destroyed together with engine so it is safe
227 // to keep reference to it.
228 engine_->task_runner()->PostTask([this]() {
229 update_state_scheduled_ = false;
230 UpdateState();
231 });
232 }
233
234 switch (event) {
235 case WindowStateEvent::kShow: {
236 visible_windows_.insert(hwnd);
237 break;
238 }
239 case WindowStateEvent::kHide: {
240 visible_windows_.erase(hwnd);
241 focused_windows_.erase(hwnd);
242 break;
243 }
244 case WindowStateEvent::kFocus: {
245 focused_windows_.insert(hwnd);
246 break;
247 }
248 case WindowStateEvent::kUnfocus: {
249 focused_windows_.erase(hwnd);
250 break;
251 }
252 }
253 }
254
255 std::optional<LRESULT> WindowsLifecycleManager::ExternalWindowMessage(
256 HWND hwnd,
257 UINT message,
258 WPARAM wparam,
259 LPARAM lparam) {
260 std::optional<flutter::WindowStateEvent> event = std::nullopt;
261
262 // TODO (schectman): Handle WM_CLOSE messages.
263 // https://github.com/flutter/flutter/issues/131497
264 switch (message) {
265 case WM_SHOWWINDOW:
266 event = wparam ? flutter::WindowStateEvent::kShow
267 : flutter::WindowStateEvent::kHide;
268 break;
269 case WM_SIZE:
270 switch (wparam) {
271 case SIZE_MINIMIZED:
272 event = flutter::WindowStateEvent::kHide;
273 break;
274 case SIZE_RESTORED:
275 case SIZE_MAXIMIZED:
276 event = flutter::WindowStateEvent::kShow;
277 break;
278 }
279 break;
280 case WM_SETFOCUS:
281 event = flutter::WindowStateEvent::kFocus;
282 break;
283 case WM_KILLFOCUS:
284 event = flutter::WindowStateEvent::kUnfocus;
285 break;
286 case WM_DESTROY:
287 event = flutter::WindowStateEvent::kHide;
288 break;
289 case WM_CLOSE:
290 if (HandleCloseMessage(hwnd, wparam, lparam)) {
291 return NULL;
292 }
293 break;
294 }
295
296 if (event.has_value()) {
297 OnWindowStateEvent(hwnd, *event);
298 }
299
300 return std::nullopt;
301 }
302
303 } // namespace flutter
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:92
flutter::FlutterWindowsEngine::OnDwmCompositionChanged
void OnDwmCompositionChanged()
Definition: flutter_windows_engine.cc:1058
flutter::FlutterWindowsEngine::RequestApplicationQuit
void RequestApplicationQuit(HWND hwnd, WPARAM wparam, LPARAM lparam, AppExitType exit_type)
Definition: flutter_windows_engine.cc:1044
flutter::ThreadSnapshot
Definition: windows_lifecycle_manager.cc:107
flutter::ThreadSnapshot::~ThreadSnapshot
~ThreadSnapshot()
Definition: windows_lifecycle_manager.cc:112
flutter::ThreadSnapshot::ThreadSnapshot
ThreadSnapshot()
Definition: windows_lifecycle_manager.cc:109
flutter::ThreadSnapshot::GetNextThread
bool GetNextThread(THREADENTRY32 &thread)
Definition: windows_lifecycle_manager.cc:138
flutter::ThreadSnapshot::GetFirstThread
std::optional< THREADENTRY32 > GetFirstThread()
Definition: windows_lifecycle_manager.cc:118
flutter::WindowsLifecycleManager::OnWindowStateEvent
virtual void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)
Definition: windows_lifecycle_manager.cc:218
flutter::WindowsLifecycleManager::WindowsLifecycleManager
WindowsLifecycleManager(FlutterWindowsEngine *engine)
Definition: windows_lifecycle_manager.cc:16
flutter::WindowsLifecycleManager::~WindowsLifecycleManager
virtual ~WindowsLifecycleManager()
Definition: windows_lifecycle_manager.cc:19
flutter::WindowsLifecycleManager::WindowProc
bool WindowProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l, LRESULT *result)
Definition: windows_lifecycle_manager.cc:65
flutter::WindowsLifecycleManager::Quit
virtual void Quit(std::optional< HWND > window, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
Definition: windows_lifecycle_manager.cc:21
flutter::WindowsLifecycleManager::DispatchMessage
virtual void DispatchMessage(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
Definition: windows_lifecycle_manager.cc:34
flutter::WindowsLifecycleManager::IsLastWindowOfProcess
virtual bool IsLastWindowOfProcess()
Definition: windows_lifecycle_manager.cc:164
flutter_windows_engine.h
key
int key
Definition: keyboard_key_handler_unittests.cc:114
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::AppLifecycleStateToString
constexpr const char * AppLifecycleStateToString(AppLifecycleState state)
Definition: app_lifecycle_state.h:72
flutter::NumWindowsForThread
static int64_t NumWindowsForThread(const THREADENTRY32 &thread)
Definition: windows_lifecycle_manager.cc:149
flutter::AppExitType::cancelable
@ cancelable
flutter::WindowStateEvent
WindowStateEvent
An event representing a change in window state that may update the.
Definition: windows_lifecycle_manager.h:24
flutter::WindowStateEvent::kHide
@ kHide
flutter::WindowStateEvent::kUnfocus
@ kUnfocus
flutter::WindowStateEvent::kShow
@ kShow
flutter::WindowStateEvent::kFocus
@ kFocus
flutter::AppLifecycleState
AppLifecycleState
Definition: app_lifecycle_state.h:32
windows_lifecycle_manager.h