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 "flutter/shell/platform/windows/win32_window.h"
6
7 namespace flutter {
8
Win32Window()9 Win32Window::Win32Window() {
10 // Assume Windows 10 1703 or greater for DPI handling. When running on a
11 // older release of Windows where this context doesn't exist, DPI calls will
12 // fail and Flutter rendering will be impacted until this is fixed.
13 // To handle downlevel correctly, dpi_helper must use the most recent DPI
14 // context available should be used: Windows 1703: Per-Monitor V2, 8.1:
15 // Per-Monitor V1, Windows 7: System See
16 // https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
17 // for more information.
18
19 // TODO the calling applicaiton should participate in setting the DPI.
20 // Currently dpi_helper is asserting per-monitor V2. There are two problems
21 // with this: 1) it is advised that the awareness mode is set using manifest,
22 // not programatically. 2) The calling executable should be responsible for
23 // setting an appropriate scaling mode, not a library. This will be
24 // particularly important once there is a means of hosting Flutter content in
25 // an existing app.
26
27 BOOL result = dpi_helper_->SetProcessDpiAwarenessContext(
28 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
29
30 if (result != TRUE) {
31 OutputDebugString(L"Failed to set PMV2");
32 }
33 }
~Win32Window()34 Win32Window::~Win32Window() {
35 Destroy();
36 }
37
Initialize(const char * title,const unsigned int x,const unsigned int y,const unsigned int width,const unsigned int height)38 void Win32Window::Initialize(const char* title,
39 const unsigned int x,
40 const unsigned int y,
41 const unsigned int width,
42 const unsigned int height) {
43 Destroy();
44 std::wstring converted_title = NarrowToWide(title);
45
46 WNDCLASS window_class = ResgisterWindowClass(converted_title);
47
48 CreateWindow(window_class.lpszClassName, converted_title.c_str(),
49 WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, width, height, nullptr,
50 nullptr, window_class.hInstance, this);
51 }
52
NarrowToWide(const char * source)53 std::wstring Win32Window::NarrowToWide(const char* source) {
54 size_t length = strlen(source);
55 size_t outlen = 0;
56 std::wstring wideTitle(length, L'#');
57 mbstowcs_s(&outlen, &wideTitle[0], length + 1, source, length);
58 return wideTitle;
59 }
60
ResgisterWindowClass(std::wstring & title)61 WNDCLASS Win32Window::ResgisterWindowClass(std::wstring& title) {
62 window_class_name_ = title;
63
64 WNDCLASS window_class{};
65 window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
66 window_class.lpszClassName = title.c_str();
67 window_class.style = CS_HREDRAW | CS_VREDRAW;
68 window_class.cbClsExtra = 0;
69 window_class.cbWndExtra = 0;
70 window_class.hInstance = GetModuleHandle(nullptr);
71 window_class.hIcon = nullptr;
72 window_class.hbrBackground = 0;
73 window_class.lpszMenuName = nullptr;
74 window_class.lpfnWndProc = WndProc;
75 RegisterClass(&window_class);
76 return window_class;
77 }
78
WndProc(HWND const window,UINT const message,WPARAM const wparam,LPARAM const lparam)79 LRESULT CALLBACK Win32Window::WndProc(HWND const window,
80 UINT const message,
81 WPARAM const wparam,
82 LPARAM const lparam) noexcept {
83 if (message == WM_NCCREATE) {
84 auto cs = reinterpret_cast<CREATESTRUCT*>(lparam);
85 SetWindowLongPtr(window, GWLP_USERDATA,
86 reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
87
88 auto that = static_cast<Win32Window*>(cs->lpCreateParams);
89
90 // Since the application is running in Per-monitor V2 mode, turn on
91 // automatic titlebar scaling
92 BOOL result = that->dpi_helper_->EnableNonClientDpiScaling(window);
93 if (result != TRUE) {
94 OutputDebugString(L"Failed to enable non-client area autoscaling");
95 }
96 that->current_dpi_ = that->dpi_helper_->GetDpiForWindow(window);
97 that->window_handle_ = window;
98 } else if (Win32Window* that = GetThisFromHandle(window)) {
99 return that->MessageHandler(window, message, wparam, lparam);
100 }
101
102 return DefWindowProc(window, message, wparam, lparam);
103 }
104
105 LRESULT
MessageHandler(HWND hwnd,UINT const message,WPARAM const wparam,LPARAM const lparam)106 Win32Window::MessageHandler(HWND hwnd,
107 UINT const message,
108 WPARAM const wparam,
109 LPARAM const lparam) noexcept {
110 int xPos = 0, yPos = 0;
111 UINT width = 0, height = 0;
112 auto window =
113 reinterpret_cast<Win32Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
114
115 if (window != nullptr) {
116 switch (message) {
117 case WM_DPICHANGED:
118 return HandleDpiChange(window_handle_, wparam, lparam);
119 break;
120
121 case WM_DESTROY:
122 window->OnClose();
123 return 0;
124 break;
125
126 case WM_SIZE:
127 width = LOWORD(lparam);
128 height = HIWORD(lparam);
129
130 current_width_ = width;
131 current_height_ = height;
132 window->HandleResize(width, height);
133 break;
134
135 case WM_MOUSEMOVE:
136 xPos = GET_X_LPARAM(lparam);
137 yPos = GET_Y_LPARAM(lparam);
138
139 window->OnPointerMove(static_cast<double>(xPos),
140 static_cast<double>(yPos));
141 break;
142 case WM_LBUTTONDOWN:
143 xPos = GET_X_LPARAM(lparam);
144 yPos = GET_Y_LPARAM(lparam);
145 window->OnPointerDown(static_cast<double>(xPos),
146 static_cast<double>(yPos));
147 break;
148 case WM_LBUTTONUP:
149 xPos = GET_X_LPARAM(lparam);
150 yPos = GET_Y_LPARAM(lparam);
151 window->OnPointerUp(static_cast<double>(xPos),
152 static_cast<double>(yPos));
153 break;
154 case WM_MOUSEWHEEL:
155 window->OnScroll(
156 0.0, -(static_cast<short>(HIWORD(wparam)) / (double)WHEEL_DELTA));
157 break;
158 case WM_CHAR:
159 case WM_SYSCHAR:
160 case WM_UNICHAR:
161 if (wparam != VK_BACK) {
162 window->OnChar(static_cast<unsigned int>(wparam));
163 }
164 break;
165 case WM_KEYDOWN:
166 case WM_SYSKEYDOWN:
167 case WM_KEYUP:
168 case WM_SYSKEYUP:
169 unsigned char scancode = ((unsigned char*)&lparam)[2];
170 unsigned int virtualKey = MapVirtualKey(scancode, MAPVK_VSC_TO_VK);
171 const int key = virtualKey;
172 const int action = message == WM_KEYDOWN ? WM_KEYDOWN : WM_KEYUP;
173 window->OnKey(key, scancode, action, 0);
174 break;
175 }
176 return DefWindowProc(hwnd, message, wparam, lparam);
177 }
178
179 return DefWindowProc(window_handle_, message, wparam, lparam);
180 }
181
GetCurrentDPI()182 UINT Win32Window::GetCurrentDPI() {
183 return current_dpi_;
184 }
185
GetCurrentWidth()186 UINT Win32Window::GetCurrentWidth() {
187 return current_width_;
188 }
189
GetCurrentHeight()190 UINT Win32Window::GetCurrentHeight() {
191 return current_height_;
192 }
193
GetWindowHandle()194 HWND Win32Window::GetWindowHandle() {
195 return window_handle_;
196 }
197
Destroy()198 void Win32Window::Destroy() {
199 if (window_handle_) {
200 DestroyWindow(window_handle_);
201 window_handle_ = nullptr;
202 }
203
204 UnregisterClass(window_class_name_.c_str(), nullptr);
205 }
206
207 // DPI Change handler. on WM_DPICHANGE resize the window
208 LRESULT
HandleDpiChange(HWND hwnd,WPARAM wparam,LPARAM lparam)209 Win32Window::HandleDpiChange(HWND hwnd, WPARAM wparam, LPARAM lparam) {
210 if (hwnd != nullptr) {
211 auto window =
212 reinterpret_cast<Win32Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
213
214 UINT uDpi = HIWORD(wparam);
215 current_dpi_ = uDpi;
216 window->OnDpiScale(uDpi);
217
218 // Resize the window
219 auto lprcNewScale = reinterpret_cast<RECT*>(lparam);
220 LONG newWidth = lprcNewScale->right - lprcNewScale->left;
221 LONG newHeight = lprcNewScale->bottom - lprcNewScale->top;
222
223 SetWindowPos(hwnd, nullptr, lprcNewScale->left, lprcNewScale->top, newWidth,
224 newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
225 }
226 return 0;
227 }
228
HandleResize(UINT width,UINT height)229 void Win32Window::HandleResize(UINT width, UINT height) {
230 current_width_ = width;
231 current_height_ = height;
232 OnResize(width, height);
233 }
234
GetThisFromHandle(HWND const window)235 Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
236 return reinterpret_cast<Win32Window*>(
237 GetWindowLongPtr(window, GWLP_USERDATA));
238 }
239
240 } // namespace flutter