win32_window.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. #include "win32_window.h"
  2. #include <flutter_windows.h>
  3. #include "resource.h"
  4. #include "app_links/app_links_plugin_c_api.h"
  5. namespace
  6. {
  7. constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
  8. // The number of Win32Window objects that currently exist.
  9. static int g_active_window_count = 0;
  10. using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
  11. // Scale helper to convert logical scaler values to physical using passed in
  12. // scale factor
  13. int Scale(int source, double scale_factor)
  14. {
  15. return static_cast<int>(source * scale_factor);
  16. }
  17. // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
  18. // This API is only needed for PerMonitor V1 awareness mode.
  19. void EnableFullDpiSupportIfAvailable(HWND hwnd)
  20. {
  21. HMODULE user32_module = LoadLibraryA("User32.dll");
  22. if (!user32_module)
  23. {
  24. return;
  25. }
  26. auto enable_non_client_dpi_scaling =
  27. reinterpret_cast<EnableNonClientDpiScaling *>(
  28. GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
  29. if (enable_non_client_dpi_scaling != nullptr)
  30. {
  31. enable_non_client_dpi_scaling(hwnd);
  32. FreeLibrary(user32_module);
  33. }
  34. }
  35. } // namespace
  36. // Manages the Win32Window's window class registration.
  37. class WindowClassRegistrar
  38. {
  39. public:
  40. ~WindowClassRegistrar() = default;
  41. // Returns the singleton registar instance.
  42. static WindowClassRegistrar *GetInstance()
  43. {
  44. if (!instance_)
  45. {
  46. instance_ = new WindowClassRegistrar();
  47. }
  48. return instance_;
  49. }
  50. // Returns the name of the window class, registering the class if it hasn't
  51. // previously been registered.
  52. const wchar_t *GetWindowClass();
  53. // Unregisters the window class. Should only be called if there are no
  54. // instances of the window.
  55. void UnregisterWindowClass();
  56. private:
  57. WindowClassRegistrar() = default;
  58. static WindowClassRegistrar *instance_;
  59. bool class_registered_ = false;
  60. };
  61. WindowClassRegistrar *WindowClassRegistrar::instance_ = nullptr;
  62. const wchar_t *WindowClassRegistrar::GetWindowClass()
  63. {
  64. if (!class_registered_)
  65. {
  66. WNDCLASS window_class{};
  67. window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
  68. window_class.lpszClassName = kWindowClassName;
  69. window_class.style = CS_HREDRAW | CS_VREDRAW;
  70. window_class.cbClsExtra = 0;
  71. window_class.cbWndExtra = 0;
  72. window_class.hInstance = GetModuleHandle(nullptr);
  73. window_class.hIcon =
  74. LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
  75. window_class.hbrBackground = 0;
  76. window_class.lpszMenuName = nullptr;
  77. window_class.lpfnWndProc = Win32Window::WndProc;
  78. RegisterClass(&window_class);
  79. class_registered_ = true;
  80. }
  81. return kWindowClassName;
  82. }
  83. void WindowClassRegistrar::UnregisterWindowClass()
  84. {
  85. UnregisterClass(kWindowClassName, nullptr);
  86. class_registered_ = false;
  87. }
  88. Win32Window::Win32Window()
  89. {
  90. ++g_active_window_count;
  91. }
  92. Win32Window::~Win32Window()
  93. {
  94. --g_active_window_count;
  95. Destroy();
  96. }
  97. bool Win32Window::CreateAndShow(const std::wstring &title,
  98. const Point &origin,
  99. const Size &size)
  100. {
  101. if (SendAppLinkToInstance(title))
  102. {
  103. return false;
  104. }
  105. Destroy();
  106. const wchar_t *window_class =
  107. WindowClassRegistrar::GetInstance()->GetWindowClass();
  108. const POINT target_point = {static_cast<LONG>(origin.x),
  109. static_cast<LONG>(origin.y)};
  110. HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
  111. UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
  112. double scale_factor = dpi / 96.0;
  113. HWND window = CreateWindow(
  114. window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  115. Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
  116. Scale(size.width, scale_factor), Scale(size.height, scale_factor),
  117. nullptr, nullptr, GetModuleHandle(nullptr), this);
  118. if (!window)
  119. {
  120. return false;
  121. }
  122. return OnCreate();
  123. }
  124. bool Win32Window::SendAppLinkToInstance(const std::wstring &title)
  125. {
  126. // Find our exact window
  127. HWND hwnd = ::FindWindow(kWindowClassName, title.c_str());
  128. if (hwnd)
  129. {
  130. // Dispatch new link to current window
  131. SendAppLink(hwnd);
  132. // (Optional) Restore our window to front in same state
  133. WINDOWPLACEMENT place = {sizeof(WINDOWPLACEMENT)};
  134. GetWindowPlacement(hwnd, &place);
  135. switch (place.showCmd)
  136. {
  137. case SW_SHOWMAXIMIZED:
  138. ShowWindow(hwnd, SW_SHOWMAXIMIZED);
  139. break;
  140. case SW_SHOWMINIMIZED:
  141. ShowWindow(hwnd, SW_RESTORE);
  142. break;
  143. default:
  144. ShowWindow(hwnd, SW_NORMAL);
  145. break;
  146. }
  147. SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
  148. SetForegroundWindow(hwnd);
  149. // Window has been found, don't create another one.
  150. return true;
  151. }
  152. return false;
  153. }
  154. // static
  155. LRESULT CALLBACK Win32Window::WndProc(HWND const window,
  156. UINT const message,
  157. WPARAM const wparam,
  158. LPARAM const lparam) noexcept
  159. {
  160. if (message == WM_NCCREATE)
  161. {
  162. auto window_struct = reinterpret_cast<CREATESTRUCT *>(lparam);
  163. SetWindowLongPtr(window, GWLP_USERDATA,
  164. reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
  165. auto that = static_cast<Win32Window *>(window_struct->lpCreateParams);
  166. EnableFullDpiSupportIfAvailable(window);
  167. that->window_handle_ = window;
  168. }
  169. else if (Win32Window *that = GetThisFromHandle(window))
  170. {
  171. return that->MessageHandler(window, message, wparam, lparam);
  172. }
  173. return DefWindowProc(window, message, wparam, lparam);
  174. }
  175. LRESULT
  176. Win32Window::MessageHandler(HWND hwnd,
  177. UINT const message,
  178. WPARAM const wparam,
  179. LPARAM const lparam) noexcept
  180. {
  181. switch (message)
  182. {
  183. case WM_DESTROY:
  184. window_handle_ = nullptr;
  185. Destroy();
  186. if (quit_on_close_)
  187. {
  188. PostQuitMessage(0);
  189. }
  190. return 0;
  191. case WM_DPICHANGED:
  192. {
  193. auto newRectSize = reinterpret_cast<RECT *>(lparam);
  194. LONG newWidth = newRectSize->right - newRectSize->left;
  195. LONG newHeight = newRectSize->bottom - newRectSize->top;
  196. SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
  197. newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
  198. return 0;
  199. }
  200. case WM_SIZE:
  201. {
  202. RECT rect = GetClientArea();
  203. if (child_content_ != nullptr)
  204. {
  205. // Size and position the child window.
  206. MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
  207. rect.bottom - rect.top, TRUE);
  208. }
  209. return 0;
  210. }
  211. case WM_ACTIVATE:
  212. if (child_content_ != nullptr)
  213. {
  214. SetFocus(child_content_);
  215. }
  216. return 0;
  217. }
  218. return DefWindowProc(window_handle_, message, wparam, lparam);
  219. }
  220. void Win32Window::Destroy()
  221. {
  222. OnDestroy();
  223. if (window_handle_)
  224. {
  225. DestroyWindow(window_handle_);
  226. window_handle_ = nullptr;
  227. }
  228. if (g_active_window_count == 0)
  229. {
  230. WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
  231. }
  232. }
  233. Win32Window *Win32Window::GetThisFromHandle(HWND const window) noexcept
  234. {
  235. return reinterpret_cast<Win32Window *>(
  236. GetWindowLongPtr(window, GWLP_USERDATA));
  237. }
  238. void Win32Window::SetChildContent(HWND content)
  239. {
  240. child_content_ = content;
  241. SetParent(content, window_handle_);
  242. RECT frame = GetClientArea();
  243. MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
  244. frame.bottom - frame.top, true);
  245. SetFocus(child_content_);
  246. }
  247. RECT Win32Window::GetClientArea()
  248. {
  249. RECT frame;
  250. GetClientRect(window_handle_, &frame);
  251. return frame;
  252. }
  253. HWND Win32Window::GetHandle()
  254. {
  255. return window_handle_;
  256. }
  257. void Win32Window::SetQuitOnClose(bool quit_on_close)
  258. {
  259. quit_on_close_ = quit_on_close;
  260. }
  261. bool Win32Window::OnCreate()
  262. {
  263. // No-op; provided for subclasses.
  264. return true;
  265. }
  266. void Win32Window::OnDestroy()
  267. {
  268. // No-op; provided for subclasses.
  269. }