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 
6 
7 #include <TlHelp32.h>
8 #include <WinUser.h>
9 #include <Windows.h>
10 #include <tchar.h>
11 
13 
14 namespace flutter {
15 
17  : engine_(engine) {}
18 
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 
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,
60  return true;
61  }
62  return false;
63 }
64 
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) {
87  } else if (wpar == SIZE_MINIMIZED) {
89  }
90  break;
91 
92  case WM_SHOWWINDOW:
93  if (!wpar) {
95  } else {
97  }
98  break;
99 
100  case WM_DESTROY:
102  break;
103  }
104  return false;
105 }
106 
108  public:
110  thread_snapshot_ = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
111  }
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::OnWindowStateEvent(HWND hwnd,
209  WindowStateEvent event) {
210  // Synthesize an unfocus event when a focused window is hidden.
211  if (event == WindowStateEvent::kHide &&
212  focused_windows_.find(hwnd) != focused_windows_.end()) {
213  OnWindowStateEvent(hwnd, WindowStateEvent::kUnfocus);
214  }
215 
216  std::lock_guard guard(state_update_lock_);
217  switch (event) {
218  case WindowStateEvent::kShow: {
219  bool first_shown_window = visible_windows_.empty();
220  auto pair = visible_windows_.insert(hwnd);
221  if (first_shown_window && pair.second &&
222  state_ == AppLifecycleState::kHidden) {
223  SetLifecycleState(AppLifecycleState::kInactive);
224  }
225  break;
226  }
227  case WindowStateEvent::kHide: {
228  bool present = visible_windows_.erase(hwnd);
229  bool empty = visible_windows_.empty();
230  if (present && empty &&
231  (state_ == AppLifecycleState::kResumed ||
232  state_ == AppLifecycleState::kInactive)) {
233  SetLifecycleState(AppLifecycleState::kHidden);
234  }
235  break;
236  }
237  case WindowStateEvent::kFocus: {
238  bool first_focused_window = focused_windows_.empty();
239  auto pair = focused_windows_.insert(hwnd);
240  if (first_focused_window && pair.second &&
241  state_ == AppLifecycleState::kInactive) {
242  SetLifecycleState(AppLifecycleState::kResumed);
243  }
244  break;
245  }
246  case WindowStateEvent::kUnfocus: {
247  if (focused_windows_.erase(hwnd) && focused_windows_.empty() &&
248  state_ == AppLifecycleState::kResumed) {
249  SetLifecycleState(AppLifecycleState::kInactive);
250  }
251  break;
252  }
253  }
254 }
255 
256 std::optional<LRESULT> WindowsLifecycleManager::ExternalWindowMessage(
257  HWND hwnd,
258  UINT message,
259  WPARAM wparam,
260  LPARAM lparam) {
261  std::optional<flutter::WindowStateEvent> event = std::nullopt;
262 
263  // TODO (schectman): Handle WM_CLOSE messages.
264  // https://github.com/flutter/flutter/issues/131497
265  switch (message) {
266  case WM_SHOWWINDOW:
267  event = wparam ? flutter::WindowStateEvent::kShow
269  break;
270  case WM_SIZE:
271  switch (wparam) {
272  case SIZE_MINIMIZED:
274  break;
275  case SIZE_RESTORED:
276  case SIZE_MAXIMIZED:
278  break;
279  }
280  break;
281  case WM_SETFOCUS:
283  break;
284  case WM_KILLFOCUS:
286  break;
287  case WM_DESTROY:
289  break;
290  case WM_CLOSE:
291  if (HandleCloseMessage(hwnd, wparam, lparam)) {
292  return NULL;
293  }
294  break;
295  }
296 
297  if (event.has_value()) {
298  OnWindowStateEvent(hwnd, *event);
299  }
300 
301  return std::nullopt;
302 }
303 
304 } // namespace flutter
flutter::FlutterWindowsEngine::RequestApplicationQuit
void RequestApplicationQuit(HWND hwnd, WPARAM wparam, LPARAM lparam, AppExitType exit_type)
Definition: flutter_windows_engine.cc:939
flutter::WindowStateEvent
WindowStateEvent
An event representing a change in window state that may update the.
Definition: windows_lifecycle_manager.h:24
flutter::ThreadSnapshot::~ThreadSnapshot
~ThreadSnapshot()
Definition: windows_lifecycle_manager.cc:112
flutter::AppExitType::cancelable
@ cancelable
flutter::WindowStateEvent::kHide
@ kHide
flutter::ThreadSnapshot
Definition: windows_lifecycle_manager.cc:107
windows_lifecycle_manager.h
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:90
flutter::NumWindowsForThread
static int64_t NumWindowsForThread(const THREADENTRY32 &thread)
Definition: windows_lifecycle_manager.cc:149
flutter::WindowsLifecycleManager::~WindowsLifecycleManager
virtual ~WindowsLifecycleManager()
Definition: windows_lifecycle_manager.cc:19
flutter::ThreadSnapshot::GetNextThread
bool GetNextThread(THREADENTRY32 &thread)
Definition: windows_lifecycle_manager.cc:138
flutter::WindowsLifecycleManager::DispatchMessage
virtual void DispatchMessage(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
Definition: windows_lifecycle_manager.cc:34
flutter::FlutterWindowsEngine::OnDwmCompositionChanged
void OnDwmCompositionChanged()
Definition: flutter_windows_engine.cc:953
flutter::WindowStateEvent::kFocus
@ kFocus
flutter::WindowStateEvent::kShow
@ kShow
flutter::WindowsLifecycleManager::WindowsLifecycleManager
WindowsLifecycleManager(FlutterWindowsEngine *engine)
Definition: windows_lifecycle_manager.cc:16
flutter::ThreadSnapshot::GetFirstThread
std::optional< THREADENTRY32 > GetFirstThread()
Definition: windows_lifecycle_manager.cc:118
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::WindowsLifecycleManager::OnWindowStateEvent
virtual void OnWindowStateEvent(HWND hwnd, WindowStateEvent event)
Definition: windows_lifecycle_manager.cc:208
flutter::ThreadSnapshot::ThreadSnapshot
ThreadSnapshot()
Definition: windows_lifecycle_manager.cc:109
flutter::WindowsLifecycleManager::IsLastWindowOfProcess
virtual bool IsLastWindowOfProcess()
Definition: windows_lifecycle_manager.cc:164
flutter_windows_engine.h
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::WindowStateEvent::kUnfocus
@ kUnfocus
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter::AppLifecycleState
AppLifecycleState
Definition: app_lifecycle_state.h:32
flutter::AppLifecycleStateToString
constexpr const char * AppLifecycleStateToString(AppLifecycleState state)
Definition: app_lifecycle_state.h:72
key
int key
Definition: keyboard_key_handler_unittests.cc:114
flutter::WindowsLifecycleManager::WindowProc
bool WindowProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l, LRESULT *result)
Definition: windows_lifecycle_manager.cc:65