diff --git a/example/linux/flutter_embedder_example.cc b/example/linux/flutter_embedder_example.cc index 1a030828e..003ecd989 100644 --- a/example/linux/flutter_embedder_example.cc +++ b/example/linux/flutter_embedder_example.cc @@ -26,7 +26,7 @@ #ifdef USE_FLATTENED_INCLUDES #include #else -#include +#include #endif namespace { @@ -53,7 +53,7 @@ std::string GetExecutableDirectory() { } // namespace int main(int argc, char **argv) { - if (!glfwInit()) { + if (!flutter_desktop_embedding::FlutterInit()) { std::cerr << "Couldn't init GLFW" << std::endl; } @@ -78,7 +78,7 @@ int main(int argc, char **argv) { auto window = flutter_desktop_embedding::CreateFlutterWindowInSnapshotMode( 640, 480, assets_path, icu_data_path, arguments); if (window == nullptr) { - glfwTerminate(); + flutter_desktop_embedding::FlutterTerminate(); return EXIT_FAILURE; } diff --git a/example/windows/GLFW Example.vcxproj b/example/windows/GLFW Example.vcxproj index e15a6eda7..c61b451f0 100644 --- a/example/windows/GLFW Example.vcxproj +++ b/example/windows/GLFW Example.vcxproj @@ -72,7 +72,7 @@ $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)..\..\library\windows\dependencies\;$(ProjectDir)..\..\;$(IncludePath);$(ProjectDir)..\..\library\windows\ + $(ProjectDir)..\..\library\windows\dependencies\;$(ProjectDir)..\..\;$(IncludePath);$(ProjectDir)..\..\library\windows\;$(ProjectDir)..\..\library\include\ $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) diff --git a/example/windows/flutter_embedder_example.cpp b/example/windows/flutter_embedder_example.cpp index 165bbd55e..94d4a2d57 100644 --- a/example/windows/flutter_embedder_example.cpp +++ b/example/windows/flutter_embedder_example.cpp @@ -15,7 +15,7 @@ #include #include -#include "embedder.h" +#include "flutter_desktop_embedding/glfw/embedder.h" int main(int argc, char **argv) { if (!flutter_desktop_embedding::FlutterInit()) { diff --git a/library/BUILD.gn b/library/BUILD.gn index 6b31ae428..605deddc5 100644 --- a/library/BUILD.gn +++ b/library/BUILD.gn @@ -17,14 +17,22 @@ import("//build/packaging.gni") import("//library/engine.gni") published_shared_library("flutter_embedder") { - if (is_linux) { - sources = [ - "linux/src/embedder.cc", - ] + # GLFW embedding implementation. + if (is_linux || is_win) { public = [ - "include/flutter_desktop_embedding/linux/embedder.h", + "include/flutter_desktop_embedding/glfw/embedder.h", + ] + sources = [ + "common/glfw/embedder.cc", + "common/glfw/key_event_handler.cc", + "common/glfw/key_event_handler.h", + "common/glfw/keyboard_hook_handler.h", + "common/glfw/text_input_plugin.cc", + "common/glfw/text_input_plugin.h", ] } + + # Embedding-agnostic shared C++. if (is_linux || is_win) { sources += [ "common/internal/engine_method_result.cc", @@ -56,16 +64,6 @@ published_shared_library("flutter_embedder") { "include/flutter_desktop_embedding/plugin.h", ] } - # GLFW-specific code. - if (is_linux || is_win) { - sources += [ - "common/glfw/key_event_handler.cc", - "common/glfw/key_event_handler.h", - "common/glfw/keyboard_hook_handler.h", - "common/glfw/text_input_plugin.cc", - "common/glfw/text_input_plugin.h", - ] - } deps = [ ":fetch_flutter_engine", diff --git a/library/README.md b/library/README.md index e325a41d6..79329f705 100644 --- a/library/README.md +++ b/library/README.md @@ -49,7 +49,7 @@ $ sudo apt-get install libglfw3-dev libepoxy-dev libjsoncpp-dev libgtk-3-dev \ #### Using the Library Run `make` under `linux/`, then link `libflutter_embedder.so` into your -binary. See [embedder.h](linux/include/flutter_desktop_embedding/embedder.h) +binary. See [embedder.h](include/flutter_desktop_embedding/glfw/embedder.h) for details on calling into the library. You will also need to link `libflutter_engine.so` into your binary. diff --git a/library/linux/src/embedder.cc b/library/common/glfw/embedder.cc similarity index 88% rename from library/linux/src/embedder.cc rename to library/common/glfw/embedder.cc index b793464a2..c2a0b6460 100644 --- a/library/linux/src/embedder.cc +++ b/library/common/glfw/embedder.cc @@ -11,18 +11,14 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#include -#include -#include -#include +#include "library/include/flutter_desktop_embedding/glfw/embedder.h" +#include #include #include #include #include -#include -#include #include @@ -31,6 +27,12 @@ #include "library/common/glfw/text_input_plugin.h" #include "library/common/internal/plugin_handler.h" +#ifdef __linux__ +// For plugin-compatible event handling (e.g., modal windows). +#include +#include +#endif + // GLFW_TRUE & GLFW_FALSE are introduced since libglfw-3.3, // add definitions here to compile under the old versions. #ifndef GLFW_TRUE @@ -51,67 +53,22 @@ struct FlutterEmbedderState { // deleted from the heap. std::vector keyboard_hook_handlers; + // Handles raw key interactions from GLFW. - // TODO: Move key_event_handler once - // https://github.com/google/flutter-desktop-embedding/issues/102 is resolved. + // TODO: Revisit ownership model once Issue #102 is resolved. std::unique_ptr key_event_handler; }; static constexpr char kDefaultWindowTitle[] = "Flutter"; -// Callback forward declarations. -static void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, - int action, int mods); -static void GLFWCharCallback(GLFWwindow *window, unsigned int code_point); -static void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, - int mods); - +// Retrieves state bag for the window in question from the GLFWWindow. static FlutterEmbedderState *GetSavedEmbedderState(GLFWwindow *window) { return reinterpret_cast( glfwGetWindowUserPointer(window)); } -// Flushes event queue and then assigns default window callbacks. -static void GLFWAssignEventCallbacks(GLFWwindow *window) { - glfwPollEvents(); - glfwSetKeyCallback(window, GLFWKeyCallback); - glfwSetCharCallback(window, GLFWCharCallback); - glfwSetMouseButtonCallback(window, GLFWmouseButtonCallback); -} - -// Clears default window events. -static void GLFWClearEventCallbacks(GLFWwindow *window) { - glfwSetKeyCallback(window, nullptr); - glfwSetCharCallback(window, nullptr); - glfwSetMouseButtonCallback(window, nullptr); -} - -static void GLFWwindowSizeCallback(GLFWwindow *window, int width, int height) { - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = width; - event.height = height; - event.pixel_ratio = 1.0; - auto state = GetSavedEmbedderState(window); - FlutterEngineSendWindowMetricsEvent(state->engine, &event); -} - -static void GLFWOnFlutterPlatformMessage(const FlutterPlatformMessage *message, - void *user_data) { - if (message->struct_size != sizeof(FlutterPlatformMessage)) { - std::cerr << "Invalid message size received. Expected: " - << sizeof(FlutterPlatformMessage) << " but received " - << message->struct_size << std::endl; - return; - } - - GLFWwindow *window = reinterpret_cast(user_data); - auto state = GetSavedEmbedderState(window); - state->plugin_handler->HandleMethodCallMessage( - message, [window] { GLFWClearEventCallbacks(window); }, - [window] { GLFWAssignEventCallbacks(window); }); -} - +// When GLFW calls back to the window with a cursor position move, forwards to +// FlutterEngine as a pointer event with appropriate phase. static void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, FlutterPointerPhase phase, double x, double y) { @@ -124,14 +81,16 @@ static void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch()) .count(); - auto state = GetSavedEmbedderState(window); - FlutterEngineSendPointerEvent(state->engine, &event, 1); + FlutterEngineSendPointerEvent(GetSavedEmbedderState(window)->engine, &event, + 1); } +// Reports cursor move to the Flutter engine. static void GLFWcursorPositionCallback(GLFWwindow *window, double x, double y) { GLFWcursorPositionCallbackAtPhase(window, FlutterPointerPhase::kMove, x, y); } +// Reports mouse button press to the Flutter engine. static void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, int mods) { double x, y; @@ -147,6 +106,7 @@ static void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, } } +// Passes character input events to registered handlers. static void GLFWCharCallback(GLFWwindow *window, unsigned int code_point) { for (flutter_desktop_embedding::KeyboardHookHandler *handler : GetSavedEmbedderState(window)->keyboard_hook_handlers) { @@ -154,6 +114,7 @@ static void GLFWCharCallback(GLFWwindow *window, unsigned int code_point) { } } +// Passes raw key events to registered handlers. static void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { for (flutter_desktop_embedding::KeyboardHookHandler *handler : @@ -162,6 +123,51 @@ static void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, } } +// Reports window size changes to the Flutter engine. +static void GLFWwindowSizeCallback(GLFWwindow *window, int width, int height) { + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = width; + event.height = height; + // TODO: Handle pixel ratio for different DPI monitors. + event.pixel_ratio = 1.0; + FlutterEngineSendWindowMetricsEvent(GetSavedEmbedderState(window)->engine, + &event); +} + +// Flushes event queue and then assigns default window callbacks. +static void GLFWAssignEventCallbacks(GLFWwindow *window) { + glfwPollEvents(); + glfwSetKeyCallback(window, GLFWKeyCallback); + glfwSetCharCallback(window, GLFWCharCallback); + glfwSetMouseButtonCallback(window, GLFWmouseButtonCallback); +} + +// Clears default window events. +static void GLFWClearEventCallbacks(GLFWwindow *window) { + glfwSetKeyCallback(window, nullptr); + glfwSetCharCallback(window, nullptr); + glfwSetMouseButtonCallback(window, nullptr); +} + +// The Flutter Engine calls out to this function when new platform messages are +// available +static void GLFWOnFlutterPlatformMessage(const FlutterPlatformMessage *message, + void *user_data) { + if (message->struct_size != sizeof(FlutterPlatformMessage)) { + std::cerr << "Invalid message size received. Expected: " + << sizeof(FlutterPlatformMessage) << " but received " + << message->struct_size << std::endl; + return; + } + + GLFWwindow *window = reinterpret_cast(user_data); + auto state = GetSavedEmbedderState(window); + state->plugin_handler->HandleMethodCallMessage( + message, [window] { GLFWClearEventCallbacks(window); }, + [window] { GLFWAssignEventCallbacks(window); }); +} + static bool GLFWMakeContextCurrent(void *user_data) { GLFWwindow *window = reinterpret_cast(user_data); glfwMakeContextCurrent(window); @@ -192,7 +198,7 @@ static uint32_t GLFWGetActiveFbo(void *user_data) { return 0; } static void GLFWClearCanvas(GLFWwindow *window) { glfwMakeContextCurrent(window); // This color is Material Blue Grey. - glClearColor(0.92549, 0.93725, 0.9451, 0); + glClearColor(236.0 / 255.0, 239.0 / 255.0, 241.0 / 255.0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glFlush(); glfwSwapBuffers(window); @@ -242,6 +248,13 @@ static FlutterEngine RunFlutterEngine( namespace flutter_desktop_embedding { +// Initialize glfw +bool FlutterInit() { return glfwInit(); } + +// Tear down glfw +void FlutterTerminate() { glfwTerminate(); } + +// set up embedder state and add the plugin to the plugin_handler bool AddPlugin(GLFWwindow *flutter_window, std::unique_ptr plugin) { auto state = GetSavedEmbedderState(flutter_window); return state->plugin_handler->AddPlugin(std::move(plugin)); @@ -261,7 +274,9 @@ GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, const std::string &packages_path, const std::string &icu_data_path, const std::vector &arguments) { +#ifdef __linux__ gtk_init(0, nullptr); +#endif auto window = glfwCreateWindow(initial_width, initial_height, kDefaultWindowTitle, NULL, NULL); if (window == nullptr) { @@ -274,6 +289,7 @@ GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, glfwDestroyWindow(window); return nullptr; } + FlutterEmbedderState *state = new FlutterEmbedderState(); state->plugin_handler = std::make_unique(engine); state->engine = engine; @@ -297,13 +313,19 @@ GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, } void FlutterWindowLoop(GLFWwindow *flutter_window) { +#ifdef __linux__ // Necessary for GTK thread safety. XInitThreads(); +#endif while (!glfwWindowShouldClose(flutter_window)) { +#ifdef __linux__ glfwPollEvents(); if (gtk_events_pending()) { gtk_main_iteration(); } +#else + glfwWaitEvents(); +#endif // TODO(awdavies): This will be deprecated soon. __FlutterEngineFlushPendingTasksNow(); } diff --git a/library/windows/embedder.h b/library/include/flutter_desktop_embedding/glfw/embedder.h similarity index 88% rename from library/windows/embedder.h rename to library/include/flutter_desktop_embedding/glfw/embedder.h index 028d418fb..669ea9d50 100644 --- a/library/windows/embedder.h +++ b/library/include/flutter_desktop_embedding/glfw/embedder.h @@ -12,15 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef WINDOWS_LIBRARY_EMBEDDER_H_ -#define WINDOWS_LIBRARY_EMBEDDER_H_ +#ifndef LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_GLFW_EMBEDDER_H_ +#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_GLFW_EMBEDDER_H_ +#include #include #include +#ifdef __linux__ +// Epoxy must be included before any graphics-related code. +#include +#endif + #include -#include "library/include/flutter_desktop_embedding/plugin.h" +#ifdef USE_FLATTENED_INCLUDES +#include "plugin.h" +#else +#include "../plugin.h" +#endif namespace flutter_desktop_embedding { @@ -86,4 +96,4 @@ void FlutterWindowLoop(GLFWwindow *flutter_window); } // namespace flutter_desktop_embedding -#endif // WINDOWS_LIBRARY_EMBEDDER_H_ +#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_GLFW_EMBEDDER_H_ diff --git a/library/include/flutter_desktop_embedding/linux/embedder.h b/library/include/flutter_desktop_embedding/linux/embedder.h deleted file mode 100644 index 3fc973595..000000000 --- a/library/include/flutter_desktop_embedding/linux/embedder.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_LINUX_EMBEDDER_H_ -#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_LINUX_EMBEDDER_H_ - -// Epoxy must be included before any graphics-related code. -#include - -#include - -#include -#include - -#ifdef USE_FLATTENED_INCLUDES -#include "plugin.h" -#else -#include "../plugin.h" -#endif - -namespace flutter_desktop_embedding { - -// Creates a GLFW Window running a Flutter Application. -// -// glfwInit() must be called prior to this function. -// -// The arguments are to configure the paths when launching the engine. See: -// https://github.com/flutter/engine/wiki/Custom-Flutter-Engine-Embedders for -// more details on Flutter Engine embedding. -// -// Returns a null pointer in the event of an error. The caller owns the pointer -// when it is non-null. -GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, - const std::string &main_path, - const std::string &assets_path, - const std::string &packages_path, - const std::string &icu_data_path, - const std::vector &arguments); - -// Creates a GLFW Window running a Flutter Application in snapshot mode. -// -// glfwInit() must be called prior to this function. -// -// In snapshot mode the assets directory snapshot is used to run the application -// instead of the sources. -// -// The arguments are to configure the paths when launching the engine. See: -// https://github.com/flutter/engine/wiki/Custom-Flutter-Engine-Embedders for -// more details on Flutter Engine embedding. -// -// Returns a null pointer in the event of an error. The caller owns the pointer -// when it is non-null. -GLFWwindow *CreateFlutterWindowInSnapshotMode( - size_t initial_width, size_t initial_height, const std::string &assets_path, - const std::string &icu_data_path, - const std::vector &arguments); - -// Adds a plugin to the flutter_window. -// -// If a plugin already exists for this plugin's channel, returns false. -// Otherwise returns true. -bool AddPlugin(GLFWwindow *flutter_window, std::unique_ptr plugin); - -// Loops on flutter window events until termination. -// -// Must be used instead of glfwWindowShouldClose as it cleans up engine state -// after termination. -// -// After this function the user must eventually call glfwTerminate() if doing -// cleanup. -void FlutterWindowLoop(GLFWwindow *flutter_window); - -} // namespace flutter_desktop_embedding - -#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_LINUX_EMBEDDER_H_ diff --git a/library/linux/Makefile b/library/linux/Makefile index 7c7f4db47..6a5810892 100644 --- a/library/linux/Makefile +++ b/library/linux/Makefile @@ -36,7 +36,7 @@ LDFLAGS= -L$(CURDIR) \ LIBRARIES=$(FLUTTER_ENGINE_LIB_FILE) HEADERS=$(shell find $(PROJECT_ROOT)/library/include/ -type f -name '*.h') \ $(FLUTTER_ENGINE_HEADER_DIR)/$(FLUTTER_ENGINE_HEADER) -SOURCES=$(shell find src/ $(PROJECT_ROOT)/library/common/ -type f -name '*.cc') +SOURCES=$(shell find $(PROJECT_ROOT)/library/common/ -type f -name '*.cc') .PHONY: all all: $(LIBRARY_OUT) diff --git a/library/windows/GLFW Library.vcxproj b/library/windows/GLFW Library.vcxproj index 8ea8c4899..f1628bc2d 100644 --- a/library/windows/GLFW Library.vcxproj +++ b/library/windows/GLFW Library.vcxproj @@ -263,6 +263,7 @@ + @@ -277,7 +278,6 @@ - @@ -288,4 +288,4 @@ - \ No newline at end of file + diff --git a/library/windows/GLFW Library.vcxproj.filters b/library/windows/GLFW Library.vcxproj.filters index bf7b4fad3..03dd85a33 100644 --- a/library/windows/GLFW Library.vcxproj.filters +++ b/library/windows/GLFW Library.vcxproj.filters @@ -15,7 +15,7 @@ - + Source Files @@ -71,4 +71,4 @@ Source Files - \ No newline at end of file + diff --git a/library/windows/embedder.cpp b/library/windows/embedder.cpp deleted file mode 100644 index f6522ca3f..000000000 --- a/library/windows/embedder.cpp +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "embedder.h" - -#include -#include -#include -#include - -#include - -#include "library/common/glfw/key_event_handler.h" -#include "library/common/glfw/keyboard_hook_handler.h" -#include "library/common/glfw/text_input_plugin.h" -#include "library/common/internal/plugin_handler.h" - -static_assert(FLUTTER_ENGINE_VERSION == 1, ""); - -// Struct for storing state within an instance of the GLFW Window. -struct FlutterEmbedderState { - FlutterEngine engine; - std::unique_ptr plugin_handler; - - // plugin_handler owns these pointers. Destruction happens when this struct is - // deleted from the heap. - std::vector - keyboard_hook_handlers; - - // Handles raw key interactions from GLFW. - // TODO: Revisit ownership model once Issue #102 is resolved. - std::unique_ptr key_event_handler; -}; - -static constexpr char kDefaultWindowTitle[] = "Flutter"; - -// Retreaves state bag for the window in question from the GLFWWindow -static FlutterEmbedderState *GetSavedEmbedderState(GLFWwindow *window) { - return reinterpret_cast( - glfwGetWindowUserPointer(window)); -} - -// When GLFW calls back to the window with a cursor position move, forward to -// FlutterEngine as a pointer event with appropriate phase -static void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, - FlutterPointerPhase phase, - double x, double y) { - FlutterPointerEvent event = {}; - event.struct_size = sizeof(event); - event.phase = phase; - event.x = x; - event.y = y; - event.timestamp = - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - FlutterEngineSendPointerEvent(GetSavedEmbedderState(window)->engine, &event, - 1); -} - -// Report cursor move to engine -static void GLFWcursorPositionCallback(GLFWwindow *window, double x, double y) { - GLFWcursorPositionCallbackAtPhase(window, FlutterPointerPhase::kMove, x, y); -} - -static void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, - int mods) { - double x, y; - if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) { - glfwGetCursorPos(window, &x, &y); - GLFWcursorPositionCallbackAtPhase(window, FlutterPointerPhase::kDown, x, y); - glfwSetCursorPosCallback(window, GLFWcursorPositionCallback); - } - if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_RELEASE) { - glfwGetCursorPos(window, &x, &y); - GLFWcursorPositionCallbackAtPhase(window, FlutterPointerPhase::kUp, x, y); - glfwSetCursorPosCallback(window, nullptr); - } -} - -static void GLFWCharCallback(GLFWwindow *window, unsigned int code_point) { - for (flutter_desktop_embedding::KeyboardHookHandler *handler : - GetSavedEmbedderState(window)->keyboard_hook_handlers) { - handler->CharHook(window, code_point); - } -} - -static void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, - int action, int mods) { - for (flutter_desktop_embedding::KeyboardHookHandler *handler : - GetSavedEmbedderState(window)->keyboard_hook_handlers) { - handler->KeyboardHook(window, key, scancode, action, mods); - } - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - glfwSetWindowShouldClose(window, GLFW_TRUE); - } -} - -static void GLFWwindowSizeCallback(GLFWwindow *window, int width, int height) { - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = width; - event.height = height; - // TODO: Handle pixel ratio for different DPI monitors. - event.pixel_ratio = 1.0; - FlutterEngineSendWindowMetricsEvent(GetSavedEmbedderState(window)->engine, - &event); -} - -// Flushes event queue and then assigns default window callbacks. -static void GLFWAssignEventCallbacks(GLFWwindow *window) { - glfwPollEvents(); - glfwSetKeyCallback(window, GLFWKeyCallback); - glfwSetCharCallback(window, GLFWCharCallback); - glfwSetMouseButtonCallback(window, GLFWmouseButtonCallback); -} - -// Clears default window events. -static void GLFWClearEventCallbacks(GLFWwindow *window) { - glfwSetKeyCallback(window, nullptr); - glfwSetCharCallback(window, nullptr); - glfwSetMouseButtonCallback(window, nullptr); -} - -// The Flutter Engine calls out to this function when new platform messages are -// available -static void GLFWOnFlutterPlatformMessage(const FlutterPlatformMessage *message, - void *user_data) { - if (message->struct_size != sizeof(FlutterPlatformMessage)) { - std::cerr << "Invalid message size received. Expected: " - << sizeof(FlutterPlatformMessage) << " but received " - << message->struct_size << std::endl; - return; - } - - GLFWwindow *window = reinterpret_cast(user_data); - auto state = GetSavedEmbedderState(window); - state->plugin_handler->HandleMethodCallMessage( - message, [window] { GLFWClearEventCallbacks(window); }, - [window] { GLFWAssignEventCallbacks(window); }); -} - -static bool GLFWMakeContextCurrent(void *user_data) { - GLFWwindow *window = reinterpret_cast(user_data); - glfwMakeContextCurrent(window); - return true; -} - -static bool GLFWClearContext(void *user_data) { - glfwMakeContextCurrent(nullptr); - return true; -} - -static bool GLFWPresent(void *user_data) { - GLFWwindow *window = reinterpret_cast(user_data); - glfwSwapBuffers(window); - return true; -} - -static uint32_t GLFWGetActiveFbo(void *user_data) { return 0; } - -// Clears the GLFW window to Material Blue-Grey. -// -// This function is primarily to fix an issue when the Flutter Engine is -// spinning up, wherein artifacts of existing windows are rendered onto the -// canvas for a few moments. -// -// This function isn't necessary, but makes starting the window much easier on -// the eyes. -static void GLFWClearCanvas(GLFWwindow *window) { - glfwMakeContextCurrent(window); - // This color is Material Blue Grey. - glClearColor(236, 239, 241, 0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glFlush(); - glfwSwapBuffers(window); - glfwMakeContextCurrent(nullptr); -} - -// Spins up an instance of the Flutter Engine. -// -// This function launches the Flutter Engine in a background thread, supplying -// the necessary callbacks for rendering within a GLFWwindow. -// -// Returns a caller-owned pointer to the engine. -static FlutterEngine RunFlutterEngine( - GLFWwindow *window, const std::string &main_path, - const std::string &assets_path, const std::string &packages_path, - const std::string &icu_data_path, - const std::vector &arguments) { - std::vector argv; - std::transform( - arguments.begin(), arguments.end(), std::back_inserter(argv), - [](const std::string &arg) -> const char * { return arg.c_str(); }); - - FlutterRendererConfig config = {}; - config.type = kOpenGL; - config.open_gl.struct_size = sizeof(config.open_gl); - config.open_gl.make_current = GLFWMakeContextCurrent; - config.open_gl.clear_current = GLFWClearContext; - config.open_gl.present = GLFWPresent; - config.open_gl.fbo_callback = GLFWGetActiveFbo; - FlutterProjectArgs args = {}; - args.struct_size = sizeof(FlutterProjectArgs); - args.assets_path = assets_path.c_str(); - args.main_path = main_path.c_str(); - args.packages_path = packages_path.c_str(); - args.icu_data_path = icu_data_path.c_str(); - args.command_line_argc = argv.size(); - args.command_line_argv = &argv[0]; - args.platform_message_callback = GLFWOnFlutterPlatformMessage; - FlutterEngine engine = nullptr; - auto result = - FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &args, window, &engine); - if (result != kSuccess || engine == nullptr) { - return nullptr; - } - return engine; -} - -namespace flutter_desktop_embedding { - -// Initialize glfw -bool FlutterInit() { return glfwInit(); } - -// Tear down glfw -void FlutterTerminate() { glfwTerminate(); } - -// set up embedder state and add the plugin to the plugin_handler -bool AddPlugin(GLFWwindow *flutter_window, std::unique_ptr plugin) { - auto state = GetSavedEmbedderState(flutter_window); - return state->plugin_handler->AddPlugin(std::move(plugin)); -} - -GLFWwindow *CreateFlutterWindowInSnapshotMode( - size_t initial_width, size_t initial_height, const std::string &assets_path, - const std::string &icu_data_path, - const std::vector &arguments) { - return CreateFlutterWindow(initial_width, initial_height, "", assets_path, "", - icu_data_path, arguments); -} - -GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, - const std::string &main_path, - const std::string &assets_path, - const std::string &packages_path, - const std::string &icu_data_path, - const std::vector &arguments) { - auto window = glfwCreateWindow(initial_width, initial_height, - kDefaultWindowTitle, NULL, NULL); - if (window == nullptr) { - return nullptr; - } - GLFWClearCanvas(window); - auto engine = RunFlutterEngine(window, main_path, assets_path, packages_path, - icu_data_path, arguments); - if (engine == nullptr) { - glfwDestroyWindow(window); - return nullptr; - } - - FlutterEmbedderState *state = new FlutterEmbedderState(); - state->plugin_handler = std::make_unique(engine); - state->engine = engine; - - state->key_event_handler = - std::make_unique(state->plugin_handler.get()); - state->keyboard_hook_handlers.push_back(state->key_event_handler.get()); - auto input_plugin = std::make_unique(); - state->keyboard_hook_handlers.push_back(input_plugin.get()); - - glfwSetWindowUserPointer(window, state); - - AddPlugin(window, std::move(input_plugin)); - - int width, height; - glfwGetWindowSize(window, &width, &height); - GLFWwindowSizeCallback(window, width, height); - glfwSetKeyCallback(window, GLFWKeyCallback); - glfwSetWindowSizeCallback(window, GLFWwindowSizeCallback); - glfwSetMouseButtonCallback(window, GLFWmouseButtonCallback); - GLFWAssignEventCallbacks(window); - return window; -} - -void FlutterWindowLoop(GLFWwindow *flutter_window) { - while (!glfwWindowShouldClose(flutter_window)) { - glfwWaitEvents(); - // TODO(awdavies): This will be deprecated soon. - __FlutterEngineFlushPendingTasksNow(); - } - auto state = GetSavedEmbedderState(flutter_window); - FlutterEngineShutdown(state->engine); - delete state; - glfwDestroyWindow(flutter_window); -} - -} // namespace flutter_desktop_embedding