win32_window.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #include "win32_window.h"
  2. #include <flutter_windows.h>
  3. #include <shobjidl_core.h>
  4. #include "resource.h"
  5. #include <cstdlib> // for getenv and _putenv
  6. #include <cstring> // for strcmp
  7. namespace {
  8. constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
  9. // The number of Win32Window objects that currently exist.
  10. static int g_active_window_count = 0;
  11. using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
  12. // Scale helper to convert logical scaler values to physical using passed in
  13. // scale factor
  14. int Scale(int source, double scale_factor) {
  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. HMODULE user32_module = LoadLibraryA("User32.dll");
  21. if (!user32_module) {
  22. return;
  23. }
  24. auto enable_non_client_dpi_scaling =
  25. reinterpret_cast<EnableNonClientDpiScaling*>(
  26. GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
  27. if (enable_non_client_dpi_scaling != nullptr) {
  28. enable_non_client_dpi_scaling(hwnd);
  29. FreeLibrary(user32_module);
  30. }
  31. }
  32. } // namespace
  33. // Manages the Win32Window's window class registration.
  34. class WindowClassRegistrar {
  35. public:
  36. ~WindowClassRegistrar() = default;
  37. // Returns the singleton registrar instance.
  38. static WindowClassRegistrar* GetInstance() {
  39. if (!instance_) {
  40. instance_ = new WindowClassRegistrar();
  41. }
  42. return instance_;
  43. }
  44. // Returns the name of the window class, registering the class if it hasn't
  45. // previously been registered.
  46. const wchar_t* GetWindowClass();
  47. // Unregisters the window class. Should only be called if there are no
  48. // instances of the window.
  49. void UnregisterWindowClass();
  50. private:
  51. WindowClassRegistrar() = default;
  52. static WindowClassRegistrar* instance_;
  53. bool class_registered_ = false;
  54. };
  55. WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
  56. const wchar_t* WindowClassRegistrar::GetWindowClass() {
  57. if (!class_registered_) {
  58. WNDCLASS window_class{};
  59. window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
  60. window_class.lpszClassName = kWindowClassName;
  61. window_class.style = CS_HREDRAW | CS_VREDRAW;
  62. window_class.cbClsExtra = 0;
  63. window_class.cbWndExtra = 0;
  64. window_class.hInstance = GetModuleHandle(nullptr);
  65. window_class.hIcon =
  66. LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
  67. window_class.hbrBackground = 0;
  68. window_class.lpszMenuName = nullptr;
  69. window_class.lpfnWndProc = Win32Window::WndProc;
  70. RegisterClass(&window_class);
  71. class_registered_ = true;
  72. }
  73. return kWindowClassName;
  74. }
  75. void WindowClassRegistrar::UnregisterWindowClass() {
  76. UnregisterClass(kWindowClassName, nullptr);
  77. class_registered_ = false;
  78. }
  79. Win32Window::Win32Window() {
  80. ++g_active_window_count;
  81. }
  82. Win32Window::~Win32Window() {
  83. --g_active_window_count;
  84. Destroy();
  85. }
  86. bool Win32Window::CreateAndShow(const std::wstring& title,
  87. const Point& origin,
  88. const Size& size, bool showOnTaskBar) {
  89. Destroy();
  90. const wchar_t* window_class =
  91. WindowClassRegistrar::GetInstance()->GetWindowClass();
  92. const POINT target_point = {static_cast<LONG>(origin.x),
  93. static_cast<LONG>(origin.y)};
  94. HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
  95. UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
  96. double scale_factor = dpi / 96.0;
  97. HWND window = CreateWindow(
  98. window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
  99. Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
  100. Scale(size.width, scale_factor), Scale(size.height, scale_factor),
  101. nullptr, nullptr, GetModuleHandle(nullptr), this);
  102. if (!window) {
  103. return false;
  104. }
  105. if (!showOnTaskBar) {
  106. // hide from taskbar
  107. HRESULT hr;
  108. ITaskbarList* pTaskbarList;
  109. hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,IID_ITaskbarList,(void**)&pTaskbarList);
  110. if (FAILED(hr)) {
  111. return false;
  112. }
  113. hr = pTaskbarList->HrInit();
  114. hr = pTaskbarList->DeleteTab(window);
  115. hr = pTaskbarList->Release();
  116. }
  117. return OnCreate();
  118. }
  119. static void trySetWindowForeground(HWND window) {
  120. char* value = nullptr;
  121. size_t size = 0;
  122. // Use _dupenv_s to safely get the environment variable
  123. _dupenv_s(&value, &size, "SET_FOREGROUND_WINDOW");
  124. if (value != nullptr) {
  125. // Correctly compare the value with "1"
  126. if (strcmp(value, "1") == 0) {
  127. // Clear the environment variable
  128. _putenv("SET_FOREGROUND_WINDOW=");
  129. // Set the window to foreground
  130. SetForegroundWindow(window);
  131. }
  132. // Free the duplicated string
  133. free(value);
  134. }
  135. }
  136. // static
  137. LRESULT CALLBACK Win32Window::WndProc(HWND const window,
  138. UINT const message,
  139. WPARAM const wparam,
  140. LPARAM const lparam) noexcept {
  141. if (message == WM_NCCREATE) {
  142. auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
  143. SetWindowLongPtr(window, GWLP_USERDATA,
  144. reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
  145. auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
  146. EnableFullDpiSupportIfAvailable(window);
  147. that->window_handle_ = window;
  148. trySetWindowForeground(window);
  149. } else if (Win32Window* that = GetThisFromHandle(window)) {
  150. return that->MessageHandler(window, message, wparam, lparam);
  151. }
  152. return DefWindowProc(window, message, wparam, lparam);
  153. }
  154. LRESULT
  155. Win32Window::MessageHandler(HWND hwnd,
  156. UINT const message,
  157. WPARAM const wparam,
  158. LPARAM const lparam) noexcept {
  159. switch (message) {
  160. case WM_DESTROY:
  161. window_handle_ = nullptr;
  162. Destroy();
  163. if (quit_on_close_) {
  164. PostQuitMessage(0);
  165. }
  166. return 0;
  167. case WM_DPICHANGED: {
  168. auto newRectSize = reinterpret_cast<RECT*>(lparam);
  169. LONG newWidth = newRectSize->right - newRectSize->left;
  170. LONG newHeight = newRectSize->bottom - newRectSize->top;
  171. SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
  172. newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
  173. return 0;
  174. }
  175. case WM_SIZE: {
  176. RECT rect = GetClientArea();
  177. if (child_content_ != nullptr) {
  178. // Size and position the child window.
  179. MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
  180. rect.bottom - rect.top, TRUE);
  181. }
  182. return 0;
  183. }
  184. case WM_ACTIVATE:
  185. if (child_content_ != nullptr) {
  186. SetFocus(child_content_);
  187. }
  188. return 0;
  189. }
  190. return DefWindowProc(window_handle_, message, wparam, lparam);
  191. }
  192. void Win32Window::Destroy() {
  193. OnDestroy();
  194. if (window_handle_) {
  195. DestroyWindow(window_handle_);
  196. window_handle_ = nullptr;
  197. }
  198. if (g_active_window_count == 0) {
  199. WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
  200. }
  201. }
  202. Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
  203. return reinterpret_cast<Win32Window*>(
  204. GetWindowLongPtr(window, GWLP_USERDATA));
  205. }
  206. void Win32Window::SetChildContent(HWND content) {
  207. child_content_ = content;
  208. SetParent(content, window_handle_);
  209. RECT frame = GetClientArea();
  210. MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
  211. frame.bottom - frame.top, true);
  212. SetFocus(child_content_);
  213. }
  214. RECT Win32Window::GetClientArea() {
  215. RECT frame;
  216. GetClientRect(window_handle_, &frame);
  217. return frame;
  218. }
  219. HWND Win32Window::GetHandle() {
  220. return window_handle_;
  221. }
  222. void Win32Window::SetQuitOnClose(bool quit_on_close) {
  223. quit_on_close_ = quit_on_close;
  224. }
  225. bool Win32Window::OnCreate() {
  226. // No-op; provided for subclasses.
  227. return true;
  228. }
  229. void Win32Window::OnDestroy() {
  230. // No-op; provided for subclasses.
  231. }
  232. const wchar_t* getWindowClassName() {
  233. return kWindowClassName;
  234. }