Skip to content

Commit 96e92a9

Browse files
authored
Remove GLFW from the API surface (google#254)
Rather than exposing the GLFWwindow that is used, treat it as an implementation detail and use an opaque pointer specific to this library as the context object instead. In practice the use of GLFW can't be entirely internal since the use of GLFW in the library prevents its use by the embedder, but the details of how it is used should not be exposed. This also gives greater control over the API surface, as part of working toward a stable ABI (google#230). This does mean that the embedder cannot manipulate the GLFW window directly, but rather than trust that doing so would be safe across DLL boundaries, any functionality need by the simple embedder use cases this library enables should be exposed via wrappers.
1 parent 899ad65 commit 96e92a9

File tree

3 files changed

+58
-43
lines changed

3 files changed

+58
-43
lines changed

library/common/glfw/embedder.cc

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
#include <cstdlib>
2121
#include <iostream>
2222

23+
#ifdef __linux__
24+
// Epoxy must be included before any graphics-related code.
25+
#include <epoxy/gl.h>
26+
#endif
27+
28+
#include <GLFW/glfw3.h>
29+
2330
#include <flutter_embedder.h>
2431

2532
#include "library/common/glfw/key_event_handler.h"
@@ -48,7 +55,13 @@ static constexpr double kDpPerInch = 160.0;
4855

4956
// Struct for storing state within an instance of the GLFW Window.
5057
struct FlutterEmbedderState {
58+
// The GLFW window that owns this state object.
59+
GLFWwindow *window;
60+
61+
// The handle to the Flutter engine instance.
5162
FlutterEngine engine;
63+
64+
// The helper class managing plugin registration and messaging.
5265
std::unique_ptr<flutter_desktop_embedding::PluginHandler> plugin_handler;
5366

5467
// Handlers for keyboard events from GLFW.
@@ -300,62 +313,69 @@ bool FlutterInit() {
300313

301314
void FlutterTerminate() { glfwTerminate(); }
302315

303-
PluginRegistrar *GetRegistrarForPlugin(GLFWwindow *flutter_window,
316+
PluginRegistrar *GetRegistrarForPlugin(FlutterWindowRef flutter_window,
304317
const std::string &plugin_name) {
305-
auto *state = GetSavedEmbedderState(flutter_window);
306318
// Currently, PluginHandler acts as the registrar for all plugins, so the
307319
// name is ignored. It is part of the API to reduce churn in the future when
308320
// aligning more closely with the Flutter registrar system.
309-
return state->plugin_handler.get();
321+
return flutter_window->plugin_handler.get();
310322
}
311323

312-
GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height,
313-
const std::string &assets_path,
314-
const std::string &icu_data_path,
315-
const std::vector<std::string> &arguments) {
324+
FlutterWindowRef CreateFlutterWindow(
325+
size_t initial_width, size_t initial_height, const std::string &assets_path,
326+
const std::string &icu_data_path,
327+
const std::vector<std::string> &arguments) {
316328
#ifdef __linux__
317329
gtk_init(0, nullptr);
318330
#endif
331+
// Create the window.
319332
auto window = glfwCreateWindow(initial_width, initial_height,
320333
kDefaultWindowTitle, NULL, NULL);
321334
if (window == nullptr) {
322335
return nullptr;
323336
}
324337
GLFWClearCanvas(window);
338+
339+
// Start the engine.
325340
auto engine = RunFlutterEngine(window, assets_path, icu_data_path, arguments);
326341
if (engine == nullptr) {
327342
glfwDestroyWindow(window);
328343
return nullptr;
329344
}
330345

346+
// Create an embedder state object attached to the window.
331347
FlutterEmbedderState *state = new FlutterEmbedderState();
332-
state->plugin_handler = std::make_unique<PluginHandler>(engine);
348+
state->window = window;
349+
glfwSetWindowUserPointer(window, state);
333350
state->engine = engine;
351+
state->plugin_handler = std::make_unique<PluginHandler>(engine);
334352

335353
// Set up the keyboard handlers.
336354
state->keyboard_hook_handlers.push_back(
337355
std::make_unique<KeyEventHandler>(state->plugin_handler.get()));
338356
state->keyboard_hook_handlers.push_back(
339357
std::make_unique<TextInputPlugin>(state->plugin_handler.get()));
340358

341-
glfwSetWindowUserPointer(window, state);
342-
359+
// Trigger an initial size callback to send size information to Flutter.
343360
state->monitor_screen_coordinates_per_inch = GetScreenCoordinatesPerInch();
344361
int width_px, height_px;
345362
glfwGetFramebufferSize(window, &width_px, &height_px);
346-
glfwSetFramebufferSizeCallback(window, GLFWFramebufferSizeCallback);
347363
GLFWFramebufferSizeCallback(window, width_px, height_px);
348364

365+
// Set up GLFW callbacks for the window.
366+
glfwSetFramebufferSizeCallback(window, GLFWFramebufferSizeCallback);
349367
GLFWAssignEventCallbacks(window);
350-
return window;
368+
369+
return state;
351370
}
352371

353-
void FlutterWindowLoop(GLFWwindow *flutter_window) {
372+
void FlutterWindowLoop(FlutterWindowRef flutter_window) {
373+
GLFWwindow *window = flutter_window->window;
354374
#ifdef __linux__
355375
// Necessary for GTK thread safety.
356376
XInitThreads();
357377
#endif
358-
while (!glfwWindowShouldClose(flutter_window)) {
378+
while (!glfwWindowShouldClose(window)) {
359379
#ifdef __linux__
360380
glfwPollEvents();
361381
if (gtk_events_pending()) {
@@ -367,10 +387,9 @@ void FlutterWindowLoop(GLFWwindow *flutter_window) {
367387
// TODO(awdavies): This will be deprecated soon.
368388
__FlutterEngineFlushPendingTasksNow();
369389
}
370-
auto state = GetSavedEmbedderState(flutter_window);
371-
FlutterEngineShutdown(state->engine);
372-
delete state;
373-
glfwDestroyWindow(flutter_window);
390+
FlutterEngineShutdown(flutter_window->engine);
391+
delete flutter_window;
392+
glfwDestroyWindow(window);
374393
}
375394

376395
} // namespace flutter_desktop_embedding

library/include/flutter_desktop_embedding/glfw/embedder.h

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,6 @@
1919
#include <string>
2020
#include <vector>
2121

22-
#ifdef __linux__
23-
// Epoxy must be included before any graphics-related code.
24-
#include <epoxy/gl.h>
25-
#endif
26-
27-
#include <GLFW/glfw3.h>
28-
2922
#ifdef USE_FLATTENED_INCLUDES
3023
#include "fde_export.h"
3124
#include "plugin_registrar.h"
@@ -34,19 +27,23 @@
3427
#include "../plugin_registrar.h"
3528
#endif
3629

30+
// Opaque reference to a Flutter window.
31+
typedef struct FlutterEmbedderState *FlutterWindowRef;
32+
3733
namespace flutter_desktop_embedding {
3834

39-
// Calls glfwInit()
35+
// Sets up the embedder's graphic context. Must be called before any other
36+
// methods.
4037
//
41-
// glfwInit() must be called in the same library as glfwCreateWindow()
38+
// Note: Internally, this library uses GLFW, which does not support multiple
39+
// copies within the same process. Internally this calls glfwInit, which will
40+
// fail if you have called glfwInit elsewhere in the process.
4241
FDE_EXPORT bool FlutterInit();
4342

44-
// Calls glfwTerminate()
45-
//
46-
// glfwTerminate() must be called in the same library as glfwCreateWindow()
43+
// Tears down embedder state. Must be called before the process terminates.
4744
FDE_EXPORT void FlutterTerminate();
4845

49-
// Creates a GLFW Window running a Flutter Application.
46+
// Creates a Window running a Flutter Application.
5047
//
5148
// FlutterInit() must be called prior to this function.
5249
//
@@ -58,9 +55,11 @@ FDE_EXPORT void FlutterTerminate();
5855
// https://github.com/flutter/engine/blob/master/shell/common/switches.h for
5956
// for details. Not all arguments will apply to embedding mode.
6057
//
61-
// Returns a null pointer in the event of an error. The caller owns the pointer
62-
// when it is non-null.
63-
FDE_EXPORT GLFWwindow *CreateFlutterWindow(
58+
// Returns a null pointer in the event of an error. Otherwise, the pointer is
59+
// valid until FlutterWindowLoop has been called and returned. Note that calling
60+
// CreateFlutterWindow without later calling FlutterWindowLoop on that pointer
61+
// is a memory leak.
62+
FDE_EXPORT FlutterWindowRef CreateFlutterWindow(
6463
size_t initial_width, size_t initial_height, const std::string &assets_path,
6564
const std::string &icu_data_path,
6665
const std::vector<std::string> &arguments);
@@ -71,16 +70,13 @@ FDE_EXPORT GLFWwindow *CreateFlutterWindow(
7170
// The name must be unique across the application, so the recommended approach
7271
// is to use the fully namespace-qualified name of the plugin class.
7372
FDE_EXPORT PluginRegistrar *GetRegistrarForPlugin(
74-
GLFWwindow *flutter_window, const std::string &plugin_name);
73+
FlutterWindowRef flutter_window, const std::string &plugin_name);
7574

76-
// Loops on flutter window events until termination.
77-
//
78-
// Must be used instead of glfwWindowShouldClose as it cleans up engine state
79-
// after termination.
75+
// Loops on Flutter window events until the window is closed.
8076
//
81-
// After this function the user must eventually call FlutterTerminate() if doing
82-
// cleanup.
83-
FDE_EXPORT void FlutterWindowLoop(GLFWwindow *flutter_window);
77+
// Once this function returns, FlutterWindowRef is no longer valid, and must
78+
// not be used again.
79+
FDE_EXPORT void FlutterWindowLoop(FlutterWindowRef flutter_window);
8480

8581
} // namespace flutter_desktop_embedding
8682

library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class FDE_EXPORT FlutterWindowController {
8181
bool init_succeeded_ = false;
8282

8383
// The curent Flutter window, if any.
84-
GLFWwindow *window_ = nullptr;
84+
FlutterWindowRef window_ = nullptr;
8585
};
8686

8787
} // namespace flutter_desktop_embedding

0 commit comments

Comments
 (0)