1 #include "flutter/shell/platform/windows/win32_flutter_window.h"
2
3 #include <chrono>
4
5 namespace flutter {
6
7 // the Windows DPI system is based on this
8 // constant for machines running at 100% scaling.
9 constexpr int base_dpi = 96;
10
Win32FlutterWindow()11 Win32FlutterWindow::Win32FlutterWindow() {
12 surface_manager = std::make_unique<AngleSurfaceManager>();
13 }
14
Win32FlutterWindow(const char * title,const int x,const int y,const int width,const int height)15 Win32FlutterWindow::Win32FlutterWindow(const char* title,
16 const int x,
17 const int y,
18 const int width,
19 const int height) noexcept
20 : Win32FlutterWindow() {
21 Win32Window::Initialize(title, x, y, width, height);
22 }
23
~Win32FlutterWindow()24 Win32FlutterWindow::~Win32FlutterWindow() {
25 DestroyRenderSurface();
26 }
27
CreateWin32FlutterWindow(const char * title,const int x,const int y,const int width,const int height)28 FlutterDesktopWindowControllerRef Win32FlutterWindow::CreateWin32FlutterWindow(
29 const char* title,
30 const int x,
31 const int y,
32 const int width,
33 const int height) {
34 auto state = std::make_unique<FlutterDesktopWindowControllerState>();
35 state->window = std::make_unique<flutter::Win32FlutterWindow>(title, 10, 10,
36 width, height);
37
38 // a window wrapper for the state block, distinct from the
39 // window_wrapper handed to plugin_registrar.
40 state->window_wrapper = std::make_unique<FlutterDesktopWindow>();
41 state->window_wrapper->window = state->window.get();
42 return state.release();
43 }
44
SetState(FLUTTER_API_SYMBOL (FlutterEngine)eng)45 void Win32FlutterWindow::SetState(FLUTTER_API_SYMBOL(FlutterEngine) eng) {
46 engine_ = eng;
47
48 auto messenger = std::make_unique<FlutterDesktopMessenger>();
49 message_dispatcher_ =
50 std::make_unique<flutter::IncomingMessageDispatcher>(messenger.get());
51 messenger->engine = engine_;
52 messenger->dispatcher = message_dispatcher_.get();
53
54 window_wrapper_ = std::make_unique<FlutterDesktopWindow>();
55 window_wrapper_->window = this;
56
57 plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
58 plugin_registrar_->messenger = std::move(messenger);
59 plugin_registrar_->window = window_wrapper_.get();
60
61 internal_plugin_registrar_ =
62 std::make_unique<flutter::PluginRegistrar>(plugin_registrar_.get());
63
64 // Set up the keyboard handlers.
65 auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
66 keyboard_hook_handlers_.push_back(
67 std::make_unique<flutter::KeyEventHandler>(internal_plugin_messenger));
68 keyboard_hook_handlers_.push_back(
69 std::make_unique<flutter::TextInputPlugin>(internal_plugin_messenger));
70 platform_handler_ = std::make_unique<flutter::PlatformHandler>(
71 internal_plugin_messenger, this);
72
73 auto state = std::make_unique<FlutterDesktopWindowControllerState>();
74 state->engine = engine_;
75
76 process_events_ = true;
77 }
78
GetRegistrar()79 FlutterDesktopPluginRegistrarRef Win32FlutterWindow::GetRegistrar() {
80 return plugin_registrar_.get();
81 }
82
83 // Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage.
ConvertToDesktopMessage(const FlutterPlatformMessage & engine_message)84 static FlutterDesktopMessage ConvertToDesktopMessage(
85 const FlutterPlatformMessage& engine_message) {
86 FlutterDesktopMessage message = {};
87 message.struct_size = sizeof(message);
88 message.channel = engine_message.channel;
89 message.message = engine_message.message;
90 message.message_size = engine_message.message_size;
91 message.response_handle = engine_message.response_handle;
92 return message;
93 }
94
95 // The Flutter Engine calls out to this function when new platform messages
96 // are available.
HandlePlatformMessage(const FlutterPlatformMessage * engine_message)97 void Win32FlutterWindow::HandlePlatformMessage(
98 const FlutterPlatformMessage* engine_message) {
99 if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
100 std::cerr << "Invalid message size received. Expected: "
101 << sizeof(FlutterPlatformMessage) << " but received "
102 << engine_message->struct_size << std::endl;
103 return;
104 }
105
106 auto message = ConvertToDesktopMessage(*engine_message);
107
108 message_dispatcher_->HandleMessage(
109 message, [this] { this->process_events_ = false; },
110 [this] { this->process_events_ = true; });
111 }
112
OnDpiScale(unsigned int dpi)113 void Win32FlutterWindow::OnDpiScale(unsigned int dpi){};
114
115 // When DesktopWindow notifies that a WM_Size message has come in
116 // lets FlutterEngine know about the new size.
OnResize(unsigned int width,unsigned int height)117 void Win32FlutterWindow::OnResize(unsigned int width, unsigned int height) {
118 SendWindowMetrics();
119 }
120
OnPointerMove(double x,double y)121 void Win32FlutterWindow::OnPointerMove(double x, double y) {
122 if (process_events_) {
123 SendPointerMove(x, y);
124 }
125 }
126
OnPointerDown(double x,double y)127 void Win32FlutterWindow::OnPointerDown(double x, double y) {
128 if (process_events_) {
129 SendPointerDown(x, y);
130 }
131 }
132
OnPointerUp(double x,double y)133 void Win32FlutterWindow::OnPointerUp(double x, double y) {
134 if (process_events_) {
135 SendPointerUp(x, y);
136 }
137 }
138
OnChar(unsigned int code_point)139 void Win32FlutterWindow::OnChar(unsigned int code_point) {
140 if (process_events_) {
141 SendChar(code_point);
142 }
143 }
144
OnKey(int key,int scancode,int action,int mods)145 void Win32FlutterWindow::OnKey(int key, int scancode, int action, int mods) {
146 if (process_events_) {
147 SendKey(key, scancode, action, 0);
148 }
149 }
150
OnScroll(double delta_x,double delta_y)151 void Win32FlutterWindow::OnScroll(double delta_x, double delta_y) {
152 if (process_events_) {
153 SendScroll(delta_x, delta_y);
154 }
155 }
156
OnClose()157 void Win32FlutterWindow::OnClose() {
158 messageloop_running_ = false;
159 }
160
FlutterMessageLoop()161 void Win32FlutterWindow::FlutterMessageLoop() {
162 MSG message;
163
164 messageloop_running_ = true;
165
166 // TODO: need either non-blocking meesage loop or custom dispatch
167 // implementation per https://github.com/flutter/flutter/issues/36420
168 while (GetMessage(&message, nullptr, 0, 0) && messageloop_running_) {
169 TranslateMessage(&message);
170 DispatchMessage(&message);
171 __FlutterEngineFlushPendingTasksNow();
172 }
173 }
174
175 // Sends new size information to FlutterEngine.
SendWindowMetrics()176 void Win32FlutterWindow::SendWindowMetrics() {
177 if (engine_ == nullptr) {
178 return;
179 }
180
181 FlutterWindowMetricsEvent event = {};
182 event.struct_size = sizeof(event);
183 event.width = GetCurrentWidth();
184 event.height = GetCurrentHeight();
185 event.pixel_ratio = static_cast<double>(GetCurrentDPI()) / base_dpi;
186 auto result = FlutterEngineSendWindowMetricsEvent(engine_, &event);
187 }
188
189 // Updates |event_data| with the current location of the mouse cursor.
SetEventLocationFromCursorPosition(FlutterPointerEvent * event_data)190 void Win32FlutterWindow::SetEventLocationFromCursorPosition(
191 FlutterPointerEvent* event_data) {
192 POINT point;
193 GetCursorPos(&point);
194
195 ScreenToClient(GetWindowHandle(), &point);
196
197 event_data->x = point.x;
198 event_data->y = point.y;
199 }
200
201 // Set's |event_data|'s phase to either kMove or kHover depending on the current
202 // primary mouse button state.
SetEventPhaseFromCursorButtonState(FlutterPointerEvent * event_data)203 void Win32FlutterWindow::SetEventPhaseFromCursorButtonState(
204 FlutterPointerEvent* event_data) {
205 event_data->phase = pointer_is_down_ ? FlutterPointerPhase::kMove
206 : FlutterPointerPhase::kHover;
207 }
208
SendPointerMove(double x,double y)209 void Win32FlutterWindow::SendPointerMove(double x, double y) {
210 FlutterPointerEvent event = {};
211 event.x = x;
212 event.y = y;
213 SetEventPhaseFromCursorButtonState(&event);
214 SendPointerEventWithData(event);
215 }
216
SendPointerDown(double x,double y)217 void Win32FlutterWindow::SendPointerDown(double x, double y) {
218 pointer_is_down_ = true;
219 FlutterPointerEvent event = {};
220 event.phase = FlutterPointerPhase::kDown;
221 event.x = x;
222 event.y = y;
223 SendPointerEventWithData(event);
224 }
225
SendPointerUp(double x,double y)226 void Win32FlutterWindow::SendPointerUp(double x, double y) {
227 pointer_is_down_ = false;
228 FlutterPointerEvent event = {};
229 event.phase = FlutterPointerPhase::kUp;
230 event.x = x;
231 event.y = y;
232 SendPointerEventWithData(event);
233 }
234
SendChar(unsigned int code_point)235 void Win32FlutterWindow::SendChar(unsigned int code_point) {
236 for (const auto& handler : keyboard_hook_handlers_) {
237 handler->CharHook(this, code_point);
238 }
239 }
240
SendKey(int key,int scancode,int action,int mods)241 void Win32FlutterWindow::SendKey(int key, int scancode, int action, int mods) {
242 for (const auto& handler : keyboard_hook_handlers_) {
243 handler->KeyboardHook(this, key, scancode, action, mods);
244 }
245 }
246
SendScroll(double delta_x,double delta_y)247 void Win32FlutterWindow::SendScroll(double delta_x, double delta_y) {
248 FlutterPointerEvent event = {};
249 SetEventLocationFromCursorPosition(&event);
250 SetEventPhaseFromCursorButtonState(&event);
251 event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
252 // TODO: See if this can be queried from the OS; this value is chosen
253 // arbitrarily to get something that feels reasonable.
254 const int kScrollOffsetMultiplier = 20;
255 event.scroll_delta_x = delta_x * kScrollOffsetMultiplier;
256 event.scroll_delta_y = delta_y * kScrollOffsetMultiplier;
257 SendPointerEventWithData(event);
258 }
259
SendPointerEventWithData(const FlutterPointerEvent & event_data)260 void Win32FlutterWindow::SendPointerEventWithData(
261 const FlutterPointerEvent& event_data) {
262 // If sending anything other than an add, and the pointer isn't already added,
263 // synthesize an add to satisfy Flutter's expectations about events.
264 if (!pointer_currently_added_ &&
265 event_data.phase != FlutterPointerPhase::kAdd) {
266 FlutterPointerEvent event = {};
267 event.phase = FlutterPointerPhase::kAdd;
268 event.x = event_data.x;
269 event.y = event_data.y;
270 SendPointerEventWithData(event);
271 }
272 // Don't double-add (e.g., if events are delivered out of order, so an add has
273 // already been synthesized).
274 if (pointer_currently_added_ &&
275 event_data.phase == FlutterPointerPhase::kAdd) {
276 return;
277 }
278
279 FlutterPointerEvent event = event_data;
280 // Set metadata that's always the same regardless of the event.
281 event.struct_size = sizeof(event);
282 event.timestamp =
283 std::chrono::duration_cast<std::chrono::microseconds>(
284 std::chrono::high_resolution_clock::now().time_since_epoch())
285 .count();
286
287 // Windows passes all input in either physical pixels (Per-monitor, System
288 // DPI) or pre-scaled to match bitmap scaling of output where process is
289 // running in DPI unaware more. In either case, no need to manually scale
290 // input here. For more information see DPIHelper.
291 event.scroll_delta_x;
292 event.scroll_delta_y;
293
294 FlutterEngineSendPointerEvent(engine_, &event, 1);
295
296 if (event_data.phase == FlutterPointerPhase::kAdd) {
297 pointer_currently_added_ = true;
298 } else if (event_data.phase == FlutterPointerPhase::kRemove) {
299 pointer_currently_added_ = false;
300 }
301 }
302
MakeCurrent()303 bool Win32FlutterWindow::MakeCurrent() {
304 return surface_manager->MakeCurrent(render_surface);
305 }
306
ClearContext()307 bool Win32FlutterWindow::ClearContext() {
308 return surface_manager->MakeCurrent(nullptr);
309 }
310
SwapBuffers()311 bool Win32FlutterWindow::SwapBuffers() {
312 return surface_manager->SwapBuffers(render_surface);
313 }
314
CreateRenderSurface()315 void Win32FlutterWindow::CreateRenderSurface() {
316 if (surface_manager && render_surface == EGL_NO_SURFACE) {
317 render_surface = surface_manager->CreateSurface(GetWindowHandle());
318 }
319 }
320
DestroyRenderSurface()321 void Win32FlutterWindow::DestroyRenderSurface() {
322 if (surface_manager) {
323 surface_manager->DestroySurface(render_surface);
324 }
325 render_surface = EGL_NO_SURFACE;
326 }
327
328 } // namespace flutter