• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/glfw/public/flutter_glfw.h"
6 
7 #include <GLFW/glfw3.h>
8 #include <assert.h>
9 
10 #include <algorithm>
11 #include <chrono>
12 #include <cstdlib>
13 #include <iostream>
14 
15 #include "flutter/shell/platform/embedder/embedder.h"
16 #include "flutter/shell/platform/glfw/glfw_event_loop.h"
17 #include "flutter/shell/platform/glfw/keyboard_hook_handler.h"
18 
19 #ifndef WINDOWS_PLATFORM
20 #define WINDOWS_PLATFORM
21 #endif
22 
23 // GLFW_TRUE & GLFW_FALSE are introduced since libglfw-3.3,
24 // add definitions here to compile under the old versions.
25 #ifndef GLFW_TRUE
26 #define GLFW_TRUE 1
27 #endif
28 #ifndef GLFW_FALSE
29 #define GLFW_FALSE 0
30 #endif
31 
32 using UniqueGLFWwindowPtr = std::unique_ptr<GLFWwindow, void (*)(GLFWwindow*)>;
33 
34 static constexpr double kDpPerInch = 160.0;
35 
36 // Struct for storing state within an instance of the GLFW Window.
37 struct FlutterDesktopWindowControllerState {
38   // The GLFW window that is bound to this state object.
39   UniqueGLFWwindowPtr window = UniqueGLFWwindowPtr(nullptr, glfwDestroyWindow);
40 
41   // The invisible GLFW window used to upload resources in the background.
42   UniqueGLFWwindowPtr resource_window =
43       UniqueGLFWwindowPtr(nullptr, glfwDestroyWindow);
44 
45   // The handle to the Flutter engine instance.
46   FLUTTER_API_SYMBOL(FlutterEngine) engine;
47 
48   // The window handle given to API clients.
49   std::unique_ptr<FlutterDesktopWindow> window_wrapper;
50 
51   // Handlers for keyboard events from GLFW.
52   std::vector<std::unique_ptr<flutter::KeyboardHookHandler>>
53       keyboard_hook_handlers;
54 
55   // The event loop for the main thread that allows for delayed task execution.
56   std::unique_ptr<flutter::GLFWEventLoop> event_loop;
57 
58   // Whether or not the pointer has been added (or if tracking is enabled,
59   // has been added since it was last removed).
60   bool pointer_currently_added = false;
61 
62   // The screen coordinates per inch on the primary monitor. Defaults to a sane
63   // value based on pixel_ratio 1.0.
64   double monitor_screen_coordinates_per_inch = kDpPerInch;
65 };
66 
67 // Opaque reference for the GLFW window itself. This is separate from the
68 // controller so that it can be provided to plugins without giving them access
69 // to all of the controller-based functionality.
70 struct FlutterDesktopWindow {
71   // The GLFW window that (indirectly) owns this state object.
72   GLFWwindow* window;
73 
74   // Whether or not to track mouse movements to send kHover events.
75   bool hover_tracking_enabled = true;
76 
77   // The ratio of pixels per screen coordinate for the window.
78   double pixels_per_screen_coordinate = 1.0;
79 
80   // Resizing triggers a window refresh, but the resize already updates Flutter.
81   // To avoid double messages, the refresh after each resize is skipped.
82   bool skip_next_window_refresh = false;
83 };
84 
85 // Struct for storing state of a Flutter engine instance.
86 struct FlutterDesktopEngineState {
87   // The handle to the Flutter engine instance.
88   FLUTTER_API_SYMBOL(FlutterEngine) engine;
89 };
90 
91 // Retrieves state bag for the window in question from the GLFWWindow.
GetSavedWindowState(GLFWwindow * window)92 static FlutterDesktopWindowControllerState* GetSavedWindowState(
93     GLFWwindow* window) {
94   return reinterpret_cast<FlutterDesktopWindowControllerState*>(
95       glfwGetWindowUserPointer(window));
96 }
97 
98 // Creates and returns an invisible GLFW window that shares |window|'s resource
99 // context.
CreateShareWindowForWindow(GLFWwindow * window)100 static UniqueGLFWwindowPtr CreateShareWindowForWindow(GLFWwindow* window) {
101   glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
102   glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
103   GLFWwindow* share_window = glfwCreateWindow(1, 1, "", NULL, window);
104   glfwDefaultWindowHints();
105   return UniqueGLFWwindowPtr(share_window, glfwDestroyWindow);
106 }
107 
108 // Returns the number of screen coordinates per inch for the main monitor.
109 // If the information is unavailable, returns a default value that assumes
110 // that a screen coordinate is one dp.
GetScreenCoordinatesPerInch()111 static double GetScreenCoordinatesPerInch() {
112   auto* primary_monitor = glfwGetPrimaryMonitor();
113   auto* primary_monitor_mode = glfwGetVideoMode(primary_monitor);
114   int primary_monitor_width_mm;
115   glfwGetMonitorPhysicalSize(primary_monitor, &primary_monitor_width_mm,
116                              nullptr);
117   if (primary_monitor_width_mm == 0) {
118     return kDpPerInch;
119   }
120   return primary_monitor_mode->width / (primary_monitor_width_mm / 25.4);
121 }
122 
123 // Sends a window metrics update to the Flutter engine using the given
124 // framebuffer size and the current window information in |state|.
SendWindowMetrics(FlutterDesktopWindowControllerState * state,int width,int height)125 static void SendWindowMetrics(FlutterDesktopWindowControllerState* state,
126                               int width,
127                               int height) {
128   double dpi = state->window_wrapper->pixels_per_screen_coordinate *
129                state->monitor_screen_coordinates_per_inch;
130 
131   FlutterWindowMetricsEvent event = {};
132   event.struct_size = sizeof(event);
133   event.width = width;
134   event.height = height;
135   // The Flutter pixel_ratio is defined as DPI/dp. Limit the ratio to a minimum
136   // of 1 to avoid rendering a smaller UI on standard resolution monitors.
137   event.pixel_ratio = std::max(dpi / kDpPerInch, 1.0);
138   FlutterEngineSendWindowMetricsEvent(state->engine, &event);
139 }
140 
141 // When GLFW calls back to the window with a framebuffer size change, notify
142 // FlutterEngine about the new window metrics.
GLFWFramebufferSizeCallback(GLFWwindow * window,int width_px,int height_px)143 static void GLFWFramebufferSizeCallback(GLFWwindow* window,
144                                         int width_px,
145                                         int height_px) {
146   int width;
147   glfwGetWindowSize(window, &width, nullptr);
148   auto* state = GetSavedWindowState(window);
149   state->window_wrapper->pixels_per_screen_coordinate =
150       width > 0 ? width_px / width : 1;
151 
152   SendWindowMetrics(state, width_px, height_px);
153   state->window_wrapper->skip_next_window_refresh = true;
154 }
155 
156 // Indicates that the window needs to be redrawn.
GLFWWindowRefreshCallback(GLFWwindow * window)157 void GLFWWindowRefreshCallback(GLFWwindow* window) {
158   auto* state = GetSavedWindowState(window);
159   if (state->window_wrapper->skip_next_window_refresh) {
160     state->window_wrapper->skip_next_window_refresh = false;
161     return;
162   }
163   // There's no engine API to request a redraw explicitly, so instead send a
164   // window metrics event with the current size to trigger it.
165   int width_px, height_px;
166   glfwGetFramebufferSize(window, &width_px, &height_px);
167   if (width_px > 0 && height_px > 0) {
168     SendWindowMetrics(state, width_px, height_px);
169   }
170 }
171 
172 // Sends a pointer event to the Flutter engine based on the given data.
173 //
174 // Any coordinate/distance values in |event_data| should be in screen
175 // coordinates; they will be adjusted to pixel values before being sent.
SendPointerEventWithData(GLFWwindow * window,const FlutterPointerEvent & event_data)176 static void SendPointerEventWithData(GLFWwindow* window,
177                                      const FlutterPointerEvent& event_data) {
178   auto* state = GetSavedWindowState(window);
179   // If sending anything other than an add, and the pointer isn't already added,
180   // synthesize an add to satisfy Flutter's expectations about events.
181   if (!state->pointer_currently_added &&
182       event_data.phase != FlutterPointerPhase::kAdd) {
183     FlutterPointerEvent event = {};
184     event.phase = FlutterPointerPhase::kAdd;
185     event.x = event_data.x;
186     event.y = event_data.y;
187     SendPointerEventWithData(window, event);
188   }
189   // Don't double-add (e.g., if events are delivered out of order, so an add has
190   // already been synthesized).
191   if (state->pointer_currently_added &&
192       event_data.phase == FlutterPointerPhase::kAdd) {
193     return;
194   }
195 
196   FlutterPointerEvent event = event_data;
197   // Set metadata that's always the same regardless of the event.
198   event.struct_size = sizeof(event);
199   event.timestamp =
200       std::chrono::duration_cast<std::chrono::microseconds>(
201           std::chrono::high_resolution_clock::now().time_since_epoch())
202           .count();
203   // Convert all screen coordinates to pixel coordinates.
204   double pixels_per_coordinate =
205       state->window_wrapper->pixels_per_screen_coordinate;
206   event.x *= pixels_per_coordinate;
207   event.y *= pixels_per_coordinate;
208   event.scroll_delta_x *= pixels_per_coordinate;
209   event.scroll_delta_y *= pixels_per_coordinate;
210 
211   FlutterEngineSendPointerEvent(state->engine, &event, 1);
212 
213   if (event_data.phase == FlutterPointerPhase::kAdd) {
214     state->pointer_currently_added = true;
215   } else if (event_data.phase == FlutterPointerPhase::kRemove) {
216     state->pointer_currently_added = false;
217   }
218 }
219 
220 // Updates |event_data| with the current location of the mouse cursor.
SetEventLocationFromCursorPosition(GLFWwindow * window,FlutterPointerEvent * event_data)221 static void SetEventLocationFromCursorPosition(
222     GLFWwindow* window,
223     FlutterPointerEvent* event_data) {
224   glfwGetCursorPos(window, &event_data->x, &event_data->y);
225 }
226 
227 // Set's |event_data|'s phase to either kMove or kHover depending on the current
228 // primary mouse button state.
SetEventPhaseFromCursorButtonState(GLFWwindow * window,FlutterPointerEvent * event_data)229 static void SetEventPhaseFromCursorButtonState(
230     GLFWwindow* window,
231     FlutterPointerEvent* event_data) {
232   event_data->phase =
233       glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS
234           ? FlutterPointerPhase::kMove
235           : FlutterPointerPhase::kHover;
236 }
237 
238 // Reports the mouse entering or leaving the Flutter view.
GLFWCursorEnterCallback(GLFWwindow * window,int entered)239 static void GLFWCursorEnterCallback(GLFWwindow* window, int entered) {
240   FlutterPointerEvent event = {};
241   event.phase =
242       entered ? FlutterPointerPhase::kAdd : FlutterPointerPhase::kRemove;
243   SetEventLocationFromCursorPosition(window, &event);
244   SendPointerEventWithData(window, event);
245 }
246 
247 // Reports mouse movement to the Flutter engine.
GLFWCursorPositionCallback(GLFWwindow * window,double x,double y)248 static void GLFWCursorPositionCallback(GLFWwindow* window, double x, double y) {
249   FlutterPointerEvent event = {};
250   event.x = x;
251   event.y = y;
252   SetEventPhaseFromCursorButtonState(window, &event);
253   SendPointerEventWithData(window, event);
254 }
255 
256 // Reports mouse button press to the Flutter engine.
GLFWMouseButtonCallback(GLFWwindow * window,int key,int action,int mods)257 static void GLFWMouseButtonCallback(GLFWwindow* window,
258                                     int key,
259                                     int action,
260                                     int mods) {
261   // Flutter currently doesn't understand other buttons, so ignore anything
262   // other than left.
263   if (key != GLFW_MOUSE_BUTTON_LEFT) {
264     return;
265   }
266   FlutterPointerEvent event = {};
267   event.phase = (action == GLFW_PRESS) ? FlutterPointerPhase::kDown
268                                        : FlutterPointerPhase::kUp;
269   SetEventLocationFromCursorPosition(window, &event);
270   SendPointerEventWithData(window, event);
271 
272   // If mouse tracking isn't already enabled, turn it on for the duration of
273   // the drag to generate kMove events.
274   bool hover_enabled =
275       GetSavedWindowState(window)->window_wrapper->hover_tracking_enabled;
276   if (!hover_enabled) {
277     glfwSetCursorPosCallback(
278         window, (action == GLFW_PRESS) ? GLFWCursorPositionCallback : nullptr);
279   }
280   // Disable enter/exit events while the mouse button is down; GLFW will send
281   // an exit event when the mouse button is released, and the pointer should
282   // stay valid until then.
283   if (hover_enabled) {
284     glfwSetCursorEnterCallback(
285         window, (action == GLFW_PRESS) ? nullptr : GLFWCursorEnterCallback);
286   }
287 }
288 
289 // Reports scroll wheel events to the Flutter engine.
GLFWScrollCallback(GLFWwindow * window,double delta_x,double delta_y)290 static void GLFWScrollCallback(GLFWwindow* window,
291                                double delta_x,
292                                double delta_y) {
293   FlutterPointerEvent event = {};
294   SetEventLocationFromCursorPosition(window, &event);
295   SetEventPhaseFromCursorButtonState(window, &event);
296   event.signal_kind = FlutterPointerSignalKind::kFlutterPointerSignalKindScroll;
297   // TODO: See if this can be queried from the OS; this value is chosen
298   // arbitrarily to get something that feels reasonable.
299   const int kScrollOffsetMultiplier = 20;
300   event.scroll_delta_x = delta_x * kScrollOffsetMultiplier;
301   event.scroll_delta_y = -delta_y * kScrollOffsetMultiplier;
302   SendPointerEventWithData(window, event);
303 }
304 
305 // Passes character input events to registered handlers.
GLFWCharCallback(GLFWwindow * window,unsigned int code_point)306 static void GLFWCharCallback(GLFWwindow* window, unsigned int code_point) {
307   for (const auto& handler :
308        GetSavedWindowState(window)->keyboard_hook_handlers) {
309     handler->CharHook(window, code_point);
310   }
311 }
312 
313 // Passes raw key events to registered handlers.
GLFWKeyCallback(GLFWwindow * window,int key,int scancode,int action,int mods)314 static void GLFWKeyCallback(GLFWwindow* window,
315                             int key,
316                             int scancode,
317                             int action,
318                             int mods) {
319   for (const auto& handler :
320        GetSavedWindowState(window)->keyboard_hook_handlers) {
321     handler->KeyboardHook(window, key, scancode, action, mods);
322   }
323 }
324 
325 // Enables/disables the callbacks related to mouse tracking.
SetHoverCallbacksEnabled(GLFWwindow * window,bool enabled)326 static void SetHoverCallbacksEnabled(GLFWwindow* window, bool enabled) {
327   glfwSetCursorEnterCallback(window,
328                              enabled ? GLFWCursorEnterCallback : nullptr);
329   glfwSetCursorPosCallback(window,
330                            enabled ? GLFWCursorPositionCallback : nullptr);
331 }
332 
333 // Flushes event queue and then assigns default window callbacks.
GLFWAssignEventCallbacks(GLFWwindow * window)334 static void GLFWAssignEventCallbacks(GLFWwindow* window) {
335   glfwPollEvents();
336   glfwSetKeyCallback(window, GLFWKeyCallback);
337   glfwSetCharCallback(window, GLFWCharCallback);
338   glfwSetMouseButtonCallback(window, GLFWMouseButtonCallback);
339   glfwSetScrollCallback(window, GLFWScrollCallback);
340   if (GetSavedWindowState(window)->window_wrapper->hover_tracking_enabled) {
341     SetHoverCallbacksEnabled(window, true);
342   }
343 }
344 
GLFWMakeContextCurrent(void * user_data)345 static bool GLFWMakeContextCurrent(void* user_data) {
346   GLFWwindow* window = reinterpret_cast<GLFWwindow*>(user_data);
347   glfwMakeContextCurrent(window);
348   return true;
349 }
350 
GLFWMakeResourceContextCurrent(void * user_data)351 static bool GLFWMakeResourceContextCurrent(void* user_data) {
352   GLFWwindow* window = reinterpret_cast<GLFWwindow*>(user_data);
353   glfwMakeContextCurrent(GetSavedWindowState(window)->resource_window.get());
354   return true;
355 }
356 
GLFWClearContext(void * user_data)357 static bool GLFWClearContext(void* user_data) {
358   glfwMakeContextCurrent(nullptr);
359   return true;
360 }
361 
GLFWPresent(void * user_data)362 static bool GLFWPresent(void* user_data) {
363   GLFWwindow* window = reinterpret_cast<GLFWwindow*>(user_data);
364   glfwSwapBuffers(window);
365   return true;
366 }
367 
GLFWGetActiveFbo(void * user_data)368 static uint32_t GLFWGetActiveFbo(void* user_data) {
369   return 0;
370 }
371 
372 // Clears the GLFW window to Material Blue-Grey.
373 //
374 // This function is primarily to fix an issue when the Flutter Engine is
375 // spinning up, wherein artifacts of existing windows are rendered onto the
376 // canvas for a few moments.
377 //
378 // This function isn't necessary, but makes starting the window much easier on
379 // the eyes.
GLFWClearCanvas(GLFWwindow * window)380 static void GLFWClearCanvas(GLFWwindow* window) {
381   glfwMakeContextCurrent(window);
382   // This color is Material Blue Grey.
383   glClearColor(236.0f / 255.0f, 239.0f / 255.0f, 241.0f / 255.0f, 0.0f);
384   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
385   glFlush();
386   glfwSwapBuffers(window);
387   glfwMakeContextCurrent(nullptr);
388 }
389 
390 // Resolves the address of the specified OpenGL or OpenGL ES
391 // core or extension function, if it is supported by the current context.
GLFWProcResolver(void * user_data,const char * name)392 static void* GLFWProcResolver(void* user_data, const char* name) {
393   return reinterpret_cast<void*>(glfwGetProcAddress(name));
394 }
395 
GLFWErrorCallback(int error_code,const char * description)396 static void GLFWErrorCallback(int error_code, const char* description) {
397   std::cerr << "GLFW error " << error_code << ": " << description << std::endl;
398 }
399 
400 // Spins up an instance of the Flutter Engine.
401 //
402 // This function launches the Flutter Engine in a background thread, supplying
403 // the necessary callbacks for rendering within a GLFWwindow (if one is
404 // provided).
405 //
406 // Returns a caller-owned pointer to the engine.
407 static FLUTTER_API_SYMBOL(FlutterEngine)
RunFlutterEngine(GLFWwindow * window,const SurfacePresentCallback & sendSurface,const FlutterCustomTaskRunners * custom_task_runners)408     RunFlutterEngine(GLFWwindow* window,
409                      const SurfacePresentCallback& sendSurface,
410                      const FlutterCustomTaskRunners* custom_task_runners) {
411   // FlutterProjectArgs is expecting a full argv, so when processing it for
412   // flags the first item is treated as the executable and ignored. Add a dummy
413   // value so that all provided arguments are used.
414   std::vector<const char*> argv = {"placeholder"};
415 
416   FlutterRendererConfig config = {};
417   if (window == nullptr) {
418     config.type = kOpenGL;
419     config.open_gl.struct_size = sizeof(config.open_gl);
420     config.open_gl.make_current = [](void* data) -> bool { return false; };
421     config.open_gl.clear_current = [](void* data) -> bool { return false; };
422     config.open_gl.present = [](void* data) -> bool { return false; };
423     config.open_gl.fbo_callback = [](void* data) -> uint32_t { return 0; };
424   } else {
425     // Provide the necessary callbacks for rendering within a GLFWwindow.
426     config.type = kOpenGL;
427     config.open_gl.struct_size = sizeof(config.open_gl);
428     config.open_gl.make_current = GLFWMakeContextCurrent;
429     config.open_gl.clear_current = GLFWClearContext;
430     config.open_gl.present = GLFWPresent;
431     config.open_gl.send_current_surface = sendSurface;
432     config.open_gl.fbo_callback = GLFWGetActiveFbo;
433     config.open_gl.make_resource_current = GLFWMakeResourceContextCurrent;
434     config.open_gl.gl_proc_resolver = GLFWProcResolver;
435   }
436   FlutterProjectArgs args = {};
437   args.struct_size = sizeof(FlutterProjectArgs);
438   args.custom_task_runners = custom_task_runners;
439   FLUTTER_API_SYMBOL(FlutterEngine) engine = nullptr;
440   auto result =
441       FlutterEngineRun(&config, &args, window, &engine);
442   if (result != kSuccess || engine == nullptr) {
443     std::cerr << "Failed to start Flutter engine: error " << result
444               << std::endl;
445     return nullptr;
446   }
447   return engine;
448 }
449 
FlutterDesktopInit()450 bool FlutterDesktopInit() {
451   // Before making any GLFW calls, set up a logging error handler.
452   glfwSetErrorCallback(GLFWErrorCallback);
453   return glfwInit();
454 }
455 
FlutterDesktopTerminate()456 void FlutterDesktopTerminate() {
457   glfwTerminate();
458 }
459 
FlutterDesktopCreateWindow(int & initial_width,int & initial_height,const char * title,const SurfacePresentCallback & sendSurface)460 FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow(
461     int &initial_width,
462     int &initial_height,
463     const char* title,
464     const SurfacePresentCallback& sendSurface) {
465   auto state = std::make_unique<FlutterDesktopWindowControllerState>();
466 
467 #ifndef USE_GLFW_WINDOW
468   glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
469   glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
470 #endif
471 
472   // Create the window, and set the state as its user data.
473   state->window = UniqueGLFWwindowPtr(
474       glfwCreateWindow(initial_width, initial_height, title, NULL, NULL),
475       glfwDestroyWindow);
476   GLFWwindow* window = state->window.get();
477   if (window == nullptr) {
478     return nullptr;
479   }
480   GLFWClearCanvas(window);
481   glfwSetWindowUserPointer(window, state.get());
482 
483   // Create the share window before starting the engine, since it may call
484   // GLFWMakeResourceContextCurrent immediately.
485   state->resource_window = CreateShareWindowForWindow(window);
486 
487   // Create an event loop for the window. It is not running yet.
488   state->event_loop = std::make_unique<flutter::GLFWEventLoop>(
489       std::this_thread::get_id(),  // main GLFW thread
490       [state = state.get()](const auto* task) {
491         if (FlutterEngineRunTask(state->engine, task) != kSuccess) {
492           std::cerr << "Could not post an engine task." << std::endl;
493         }
494       });
495 
496   // Configure task runner interop.
497   FlutterTaskRunnerDescription platform_task_runner = {};
498   platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
499   platform_task_runner.user_data = state.get();
500   platform_task_runner.runs_task_on_current_thread_callback =
501       [](void* state) -> bool {
502     return reinterpret_cast<FlutterDesktopWindowControllerState*>(state)
503         ->event_loop->RunsTasksOnCurrentThread();
504   };
505   platform_task_runner.post_task_callback =
506       [](FlutterTask task, uint64_t target_time_nanos, void* state) -> void {
507     reinterpret_cast<FlutterDesktopWindowControllerState*>(state)
508         ->event_loop->PostTask(task, target_time_nanos);
509   };
510 
511   FlutterCustomTaskRunners custom_task_runners = {};
512   custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
513   custom_task_runners.platform_task_runner = &platform_task_runner;
514   custom_task_runners.render_task_runner = &platform_task_runner;
515 
516   // Start the engine.
517   state->engine =
518       RunFlutterEngine(window, sendSurface, &custom_task_runners);
519   if (state->engine == nullptr) {
520     return nullptr;
521   }
522 
523   state->window_wrapper = std::make_unique<FlutterDesktopWindow>();
524   state->window_wrapper->window = window;
525 
526   // Trigger an initial size callback to send size information to Flutter.
527   state->monitor_screen_coordinates_per_inch = GetScreenCoordinatesPerInch();
528   int width_px, height_px;
529 #ifdef USE_GLFW_WINDOW
530   glfwGetFramebufferSize(window, &width_px, &height_px);
531   initial_width = width_px;
532   initial_height = height_px;
533 #else
534   glfwGetWindowSize(window, &width_px, &height_px);
535 #endif
536   GLFWFramebufferSizeCallback(window, width_px, height_px);
537 
538   // Set up GLFW callbacks for the window.
539   glfwSetFramebufferSizeCallback(window, GLFWFramebufferSizeCallback);
540   glfwSetWindowRefreshCallback(window, GLFWWindowRefreshCallback);
541   GLFWAssignEventCallbacks(window);
542 
543   return state.release();
544 }
545 
FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller)546 void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) {
547   FlutterEngineShutdown(controller->engine);
548   delete controller;
549 }
550 
FlutterDesktopSetIdleCallback(FlutterDesktopWindowControllerRef controller,const IdleCallback & idleCallback)551 void FlutterDesktopSetIdleCallback(FlutterDesktopWindowControllerRef controller,
552                                    const IdleCallback& idleCallback) {
553   FlutterEngineSetIdleNotificationCallback(controller->engine, idleCallback);
554 }
555 
FlutterDesktopAddKeyboardHookHandler(FlutterDesktopWindowControllerRef controller,std::unique_ptr<flutter::KeyboardHookHandler> keyboardHookHandler)556 void FlutterDesktopAddKeyboardHookHandler(FlutterDesktopWindowControllerRef controller,
557                                           std::unique_ptr<flutter::KeyboardHookHandler> keyboardHookHandler) {
558   controller->keyboard_hook_handlers.push_back(std::move(keyboardHookHandler));
559 }
560 
FlutterDesktopSetClipboardData(FlutterDesktopWindowControllerRef controller,const char * data)561 FLUTTER_EXPORT void FlutterDesktopSetClipboardData(
562     FlutterDesktopWindowControllerRef controller, const char* data) {
563   GLFWwindow* window = FlutterDesktopGetWindow(controller)->window;
564   glfwSetClipboardString(window, data);
565 }
566 
FlutterDesktopGetClipboardData(FlutterDesktopWindowControllerRef controller)567 FLUTTER_EXPORT const char* FlutterDesktopGetClipboardData(
568     FlutterDesktopWindowControllerRef controller) {
569   GLFWwindow* window = FlutterDesktopGetWindow(controller)->window;
570   auto result = glfwGetClipboardString(window);
571   return result == NULL ? "" : result;
572 }
573 
FlutterDesktopGetFramebufferSize(FlutterDesktopWindowRef flutter_window,int * width,int * height)574 void FlutterDesktopGetFramebufferSize(FlutterDesktopWindowRef flutter_window,
575                                   int* width,
576                                   int* height) {
577   glfwGetFramebufferSize(flutter_window->window, width, height);
578 }
579 
FlutterDesktopWindowSetHoverEnabled(FlutterDesktopWindowRef flutter_window,bool enabled)580 void FlutterDesktopWindowSetHoverEnabled(FlutterDesktopWindowRef flutter_window,
581                                          bool enabled) {
582   flutter_window->hover_tracking_enabled = enabled;
583   SetHoverCallbacksEnabled(flutter_window->window, enabled);
584 }
585 
FlutterDesktopWindowSetTitle(FlutterDesktopWindowRef flutter_window,const char * title)586 void FlutterDesktopWindowSetTitle(FlutterDesktopWindowRef flutter_window,
587                                   const char* title) {
588   GLFWwindow* window = flutter_window->window;
589   glfwSetWindowTitle(window, title);
590 }
591 
FlutterDesktopWindowSetIcon(FlutterDesktopWindowRef flutter_window,uint8_t * pixel_data,int width,int height)592 void FlutterDesktopWindowSetIcon(FlutterDesktopWindowRef flutter_window,
593                                  uint8_t* pixel_data,
594                                  int width,
595                                  int height) {
596   GLFWimage image = {width, height, static_cast<unsigned char*>(pixel_data)};
597   glfwSetWindowIcon(flutter_window->window, pixel_data ? 1 : 0, &image);
598 }
599 
FlutterDesktopWindowGetFrame(FlutterDesktopWindowRef flutter_window,int * x,int * y,int * width,int * height)600 void FlutterDesktopWindowGetFrame(FlutterDesktopWindowRef flutter_window,
601                                   int* x,
602                                   int* y,
603                                   int* width,
604                                   int* height) {
605   glfwGetWindowPos(flutter_window->window, x, y);
606   glfwGetWindowSize(flutter_window->window, width, height);
607   // The above gives content area size and position; adjust for the window
608   // decoration to give actual window frame.
609   int frame_left, frame_top, frame_right, frame_bottom;
610   glfwGetWindowFrameSize(flutter_window->window, &frame_left, &frame_top,
611                          &frame_right, &frame_bottom);
612   if (x) {
613     *x -= frame_left;
614   }
615   if (y) {
616     *y -= frame_top;
617   }
618   if (width) {
619     *width += frame_left + frame_right;
620   }
621   if (height) {
622     *height += frame_top + frame_bottom;
623   }
624 }
625 
FlutterDesktopWindowSetFrame(FlutterDesktopWindowRef flutter_window,int x,int y,int width,int height)626 void FlutterDesktopWindowSetFrame(FlutterDesktopWindowRef flutter_window,
627                                   int x,
628                                   int y,
629                                   int width,
630                                   int height) {
631   // Get the window decoration sizes to adjust, since the GLFW setters take
632   // content position and size.
633   int frame_left, frame_top, frame_right, frame_bottom;
634   glfwGetWindowFrameSize(flutter_window->window, &frame_left, &frame_top,
635                          &frame_right, &frame_bottom);
636   glfwSetWindowPos(flutter_window->window, x + frame_left, y + frame_top);
637   glfwSetWindowSize(flutter_window->window, width - frame_left - frame_right,
638                     height - frame_top - frame_bottom);
639 }
640 
FlutterDesktopGetWindowSize(FlutterDesktopWindowRef flutter_window,int * width,int * height)641 void FlutterDesktopGetWindowSize(FlutterDesktopWindowRef flutter_window,
642                                  int* width,
643                                  int* height) {
644   glfwGetWindowSize(flutter_window->window, width, height);
645 }
646 
FlutterDesktopSetWindowSize(FlutterDesktopWindowRef flutter_window,int & width,int & height)647 void FlutterDesktopSetWindowSize(FlutterDesktopWindowRef flutter_window,
648                                  int& width,
649                                  int& height) {
650   glfwSetWindowSize(flutter_window->window, width, height);
651 #ifdef USE_GLFW_WINDOW
652   int width_px, height_px;
653   glfwGetFramebufferSize(flutter_window->window, &width_px, &height_px);
654   width = width_px;
655   height = height_px;
656 #endif
657   GLFWFramebufferSizeCallback(flutter_window->window, width, height);
658 
659   // Set up GLFW callbacks for the window.
660   glfwSetFramebufferSizeCallback(flutter_window->window, GLFWFramebufferSizeCallback);
661   glfwSetWindowRefreshCallback(flutter_window->window, GLFWWindowRefreshCallback);
662   GLFWAssignEventCallbacks(flutter_window->window);
663 }
664 
FlutterDesktopWindowGetScaleFactor(FlutterDesktopWindowRef flutter_window)665 double FlutterDesktopWindowGetScaleFactor(
666     FlutterDesktopWindowRef flutter_window) {
667   return flutter_window->pixels_per_screen_coordinate;
668 }
669 
FlutterDesktopRunWindowLoop(FlutterDesktopWindowControllerRef controller)670 void FlutterDesktopRunWindowLoop(FlutterDesktopWindowControllerRef controller) {
671   GLFWwindow* window = controller->window.get();
672   while (!glfwWindowShouldClose(window)) {
673     auto wait_duration = std::chrono::milliseconds::max();
674     controller->event_loop->WaitForEvents(wait_duration);
675   }
676   FlutterDesktopDestroyWindow(controller);
677 }
678 
FlutterDesktopWaitForEvents(FlutterDesktopWindowControllerRef controller)679 void FlutterDesktopWaitForEvents(FlutterDesktopWindowControllerRef controller) {
680   auto wait_duration = std::chrono::milliseconds::max();
681   controller->event_loop->WaitForEvents(wait_duration);
682 }
683 
FlutterDesktopWindowShouldClose(FlutterDesktopWindowControllerRef controller)684 bool FlutterDesktopWindowShouldClose(FlutterDesktopWindowControllerRef controller) {
685   GLFWwindow* window = controller->window.get();
686   return glfwWindowShouldClose(window);
687 }
688 
FlutterDesktopGetWindow(FlutterDesktopWindowControllerRef controller)689 FlutterDesktopWindowRef FlutterDesktopGetWindow(
690     FlutterDesktopWindowControllerRef controller) {
691   // Currently, one registrar acts as the registrar for all plugins, so the
692   // name is ignored. It is part of the API to reduce churn in the future when
693   // aligning more closely with the Flutter registrar system.
694   return controller->window_wrapper.get();
695 }
696 
FlutterDesktopRunEngine()697 FlutterDesktopEngineRef FlutterDesktopRunEngine() {
698   auto engine =
699       RunFlutterEngine(nullptr, nullptr, nullptr /* custom task runners */);
700   if (engine == nullptr) {
701     return nullptr;
702   }
703   auto engine_state = new FlutterDesktopEngineState();
704   engine_state->engine = engine;
705   return engine_state;
706 }
707 
FlutterDesktopShutDownEngine(FlutterDesktopEngineRef engine_ref)708 bool FlutterDesktopShutDownEngine(FlutterDesktopEngineRef engine_ref) {
709   std::cout << "Shutting down flutter engine process." << std::endl;
710   auto result = FlutterEngineShutdown(engine_ref->engine);
711   delete engine_ref;
712   return (result == kSuccess);
713 }
714