diff --git a/README.md b/README.md index b008af414..3e668cd8c 100644 --- a/README.md +++ b/README.md @@ -82,4 +82,3 @@ speed up the debugging process. request, or [write a plugin](plugins/README.md#writing-your-own-plugins)! * The Linux and Windows implementations currently use GLFW. This is not going to be the final implementation for either platform. -* Plugins and text input do not yet work on Windows. diff --git a/example/windows/GLFW Example.vcxproj b/example/windows/GLFW Example.vcxproj index 1da8b14ed..e15a6eda7 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\GLFW\;$(ProjectDir)..\..\library\windows\;$(IncludePath) + $(ProjectDir)..\..\library\windows\dependencies\;$(ProjectDir)..\..\;$(IncludePath);$(ProjectDir)..\..\library\windows\ $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) @@ -84,7 +84,7 @@ $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(ProjectDir)..\..\library\windows\;$(IncludePath) + $(ProjectDir)..\..\library\windows\dependencies\;$(ProjectDir)..\..\;$(IncludePath);$(ProjectDir)..\..\library\windows\ $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) @@ -227,4 +227,4 @@ $(SolutionDir) - + \ No newline at end of file diff --git a/example/windows/flutter_embedder_example.cpp b/example/windows/flutter_embedder_example.cpp index d8485e70f..165bbd55e 100644 --- a/example/windows/flutter_embedder_example.cpp +++ b/example/windows/flutter_embedder_example.cpp @@ -15,10 +15,10 @@ #include #include -#include +#include "embedder.h" int main(int argc, char **argv) { - if (!FlutterInit()) { + if (!flutter_desktop_embedding::FlutterInit()) { std::cout << "Couldn't init GLFW" << std::endl; } // Arguments for the Flutter Engine. @@ -31,15 +31,15 @@ int main(int argc, char **argv) { #endif // Start the engine. // TODO: Make paths relative to the executable so it can be run from anywhere. - auto window = CreateFlutterWindowInSnapshotMode( + auto window = flutter_desktop_embedding::CreateFlutterWindowInSnapshotMode( 640, 480, "..\\..\\example\\flutter_app\\build\\flutter_assets", "..\\..\\library\\windows\\dependencies\\engine\\icudtl.dat", arguments); if (window == nullptr) { - FlutterTerminate(); + flutter_desktop_embedding::FlutterTerminate(); return EXIT_FAILURE; } - FlutterWindowLoop(window); - FlutterTerminate(); + flutter_desktop_embedding::FlutterWindowLoop(window); + flutter_desktop_embedding::FlutterTerminate(); return EXIT_SUCCESS; } diff --git a/library/windows/.gitignore b/library/windows/.gitignore index f431ddc7c..099c5cb6a 100644 --- a/library/windows/.gitignore +++ b/library/windows/.gitignore @@ -1,3 +1,6 @@ +# Ignore third party code fetched as part of build process of the Flutter embedding library +third_party + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## @@ -327,3 +330,4 @@ ASALocalRun/ # MFractors (Xamarin productivity tool) working folder .mfractor/ + diff --git a/library/windows/GLFW Library.vcxproj b/library/windows/GLFW Library.vcxproj index 2df1af2eb..634ea94d9 100644 --- a/library/windows/GLFW Library.vcxproj +++ b/library/windows/GLFW Library.vcxproj @@ -75,8 +75,9 @@ $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ flutter_embedder .dll - $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\GLFW\;$(IncludePath) - $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\GLFW\;$(LibraryPath) + $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies;$(ProjectDir)third_party\jsoncpp\include;$(ProjectDir)..\..\;$(IncludePath) + $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\json\x64\debug;$(ProjectDir)dependencies\GLFW\;$(LibraryPath) + $(ProjectDir)..\..\library\common\internal;$(SourcePath) $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ @@ -91,8 +92,9 @@ $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ flutter_embedder .dll - $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\GLFW\;$(IncludePath) - $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\GLFW\;$(LibraryPath) + $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies;$(ProjectDir)third_party\jsoncpp\include;$(ProjectDir)..\..\;$(IncludePath) + $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\json\x64\release;;$(ProjectDir)dependencies\GLFW\;$(LibraryPath) + $(ProjectDir)..\..\library\common\internal;$(SourcePath) $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ @@ -108,9 +110,10 @@ Disabled true true + MultiThreadedDebugDLL - flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies) + flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies);json_vc71_libmtd.lib exports.def @@ -123,7 +126,7 @@ - $(ProjectDir)scripts\update_flutter_engine && $(ProjectDir)scripts\get_engine_artifacts && $(ProjectDir)scripts\get_GLFW + $(ProjectDir)scripts\update_flutter_engine && $(ProjectDir)scripts\get_engine_artifacts && $(ProjectDir)scripts\get_GLFW && $(ProjectDir)scripts\build_jsonlib Get the flutter engine, engine artifacts and GLFW @@ -186,11 +189,12 @@ true true true + MultiThreadedDLL true true - flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies) + flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies);json_vc71_libmt.lib exports.def $(OutDir)$(TargetName)$(TargetExt) $(OutDir)$(TargetName).lib @@ -201,7 +205,7 @@ - $(ProjectDir)scripts\update_flutter_engine && $(ProjectDir)scripts\get_engine_artifacts && $(ProjectDir)scripts\get_GLFW + $(ProjectDir)scripts\update_flutter_engine && $(ProjectDir)scripts\get_engine_artifacts && $(ProjectDir)scripts\get_GLFW && $(ProjectDir)scripts\build_jsonlib Get the flutter engine, engine artifacts and GLFW @@ -259,6 +263,19 @@ + + + + + + + + + + + + + @@ -270,4 +287,4 @@ - + \ No newline at end of file diff --git a/library/windows/GLFW Library.vcxproj.filters b/library/windows/GLFW Library.vcxproj.filters index 58963352d..02a3922cf 100644 --- a/library/windows/GLFW Library.vcxproj.filters +++ b/library/windows/GLFW Library.vcxproj.filters @@ -1,32 +1,35 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - - - Header Files - - - - - Source Files - - - \ No newline at end of file + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + + + Source Files + + + diff --git a/library/windows/dependencies/.gitignore b/library/windows/dependencies/.gitignore index 443ccd53a..7f1435b47 100644 --- a/library/windows/dependencies/.gitignore +++ b/library/windows/dependencies/.gitignore @@ -3,3 +3,4 @@ flutter_engine.* .last_engine_version icudtl.dat glfw3.* +json diff --git a/library/windows/dependencies/json/.gitkeep b/library/windows/dependencies/json/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/library/windows/embedder.cpp b/library/windows/embedder.cpp index a601c9c73..e76f35f9a 100644 --- a/library/windows/embedder.cpp +++ b/library/windows/embedder.cpp @@ -21,14 +21,33 @@ #include +#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, ""); -static constexpr char kDefaultWindowTitle[] = "Flutter"; +// Struct for storing state within an instance of the GLFW Window. +struct FlutterEmbedderState { + FlutterEngine engine; + std::unique_ptr plugin_handler; -bool FlutterInit() { return glfwInit(); } + // plugin_handler owns these pointers. Destruction happens when this struct is + // deleted from the heap. + std::vector + keyboard_hook_handlers; +}; -void FlutterTerminate() { glfwTerminate(); } +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) { @@ -41,11 +60,11 @@ static void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch()) .count(); - FlutterEngineSendPointerEvent( - reinterpret_cast(glfwGetWindowUserPointer(window)), &event, - 1); + 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); } @@ -65,8 +84,19 @@ static void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, } } +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); } @@ -79,9 +109,41 @@ static void GLFWwindowSizeCallback(GLFWwindow *window, int width, int height) { event.height = height; // TODO: Handle pixel ratio for different DPI monitors. event.pixel_ratio = 1.0; - FlutterEngineSendWindowMetricsEvent( - reinterpret_cast(glfwGetWindowUserPointer(window)), - &event); + 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) { @@ -152,6 +214,7 @@ static FlutterEngine RunFlutterEngine( 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); @@ -161,6 +224,28 @@ static FlutterEngine RunFlutterEngine( 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, @@ -173,35 +258,43 @@ GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, return nullptr; } GLFWClearCanvas(window); - auto flutter_engine_run_result = RunFlutterEngine( - window, main_path, assets_path, packages_path, icu_data_path, arguments); - if (flutter_engine_run_result == nullptr) { + auto engine = RunFlutterEngine(window, main_path, assets_path, packages_path, + icu_data_path, arguments); + if (engine == nullptr) { glfwDestroyWindow(window); return nullptr; } - glfwSetWindowUserPointer(window, flutter_engine_run_result); + + FlutterEmbedderState *state = new FlutterEmbedderState(); + state->plugin_handler = std::make_unique(engine); + state->engine = engine; + 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; } -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); -} - void FlutterWindowLoop(GLFWwindow *flutter_window) { while (!glfwWindowShouldClose(flutter_window)) { glfwWaitEvents(); + // TODO(awdavies): This will be deprecated soon. + __FlutterEngineFlushPendingTasksNow(); } - FlutterEngineShutdown(reinterpret_cast( - glfwGetWindowUserPointer(flutter_window))); + auto state = GetSavedEmbedderState(flutter_window); + FlutterEngineShutdown(state->engine); + delete state; glfwDestroyWindow(flutter_window); } + +} // namespace flutter_desktop_embedding diff --git a/library/windows/embedder.h b/library/windows/embedder.h index 5d52c1c18..028d418fb 100644 --- a/library/windows/embedder.h +++ b/library/windows/embedder.h @@ -18,7 +18,11 @@ #include #include -#include +#include + +#include "library/include/flutter_desktop_embedding/plugin.h" + +namespace flutter_desktop_embedding { // Calls glfwInit() // @@ -65,6 +69,12 @@ GLFWwindow *CreateFlutterWindowInSnapshotMode( 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 @@ -74,4 +84,6 @@ GLFWwindow *CreateFlutterWindowInSnapshotMode( // cleanup. void FlutterWindowLoop(GLFWwindow *flutter_window); +} // namespace flutter_desktop_embedding + #endif // WINDOWS_LIBRARY_EMBEDDER_H_ diff --git a/library/windows/scripts/build_jsonlib.bat b/library/windows/scripts/build_jsonlib.bat new file mode 100644 index 000000000..17f663456 --- /dev/null +++ b/library/windows/scripts/build_jsonlib.bat @@ -0,0 +1,105 @@ +:: 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. +@echo off + +:: Find where VS lives and start a VC command prompt +set pre=Microsoft.VisualStudio.Product. +set ids=%pre%Community %pre%Professional %pre%Enterprise %pre%BuildTools + +pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" +for /f "usebackq tokens=1* delims=: " %%i in (`vswhere -latest -products *`) do (if /i "%%i"=="installationPath" set InstallDir=%%j) +popd + +pushd %InstallDir%\VC\Auxiliary\Build +call vcvarsall.bat x86_amd64 +popd + +set JSONDEBUGLIBEXISTS=true +if not exist %~dp0..\dependencies\json\x64\debug\json_vc71_libmtd.lib set JSONDEBUGLIBEXISTS=false + +set JSONRELEASELIBEXISTS=true +if not exist %~dp0..\dependencies\json\x64\release\json_vc71_libmt.lib set JSONRELEASELIBEXISTS=false + +if %JSONDEBUGLIBEXISTS% == true ( + if %JSONRELEASELIBEXISTS% == true ( + echo jsoncpp found. + goto DONE + ) +) + +set THIRDPARTYDIREXISTS=true +if not exist %~dp0..\third_party set THIRDPARTYDIREXISTS=false + +if %THIRDPARTYDIREXISTS% == false ( + mkdir %~dp0..\third_party +) + +set JSONDIREXISTS=true +if not exist %~dp0..\third_party\jsoncpp set JSONDIREXISTS=false + +if %JSONDIREXISTS% == false ( + mkdir %~dp0..\third_party\jsoncpp +) + +set JSONEXISTS=true +if not exist %~dp0..\third_party\jsoncpp\README.md set JSONEXISTS=false + +:: Clone source +if %JSONEXISTS% == false ( + :: PR opened on json cpp for VS2017 support: https://github.com/open-source-parsers/jsoncpp/pull/853 + echo Cloning via git clone --branch supportvs2017 https://github.com/clarkezone/jsoncpp.git %~dp0..\third_party\jsoncpp + call git clone --branch supportvs2017 https://github.com/clarkezone/jsoncpp.git %~dp0..\third_party\jsoncpp + + pushd %~dp0..\third_party\jsoncpp + + call git checkout 3ae7e8073a425c93329c8577a3c813c206322ca4 + + popd +) + +:: Build debug lib +echo Building debug lib: msbuild %~dp0..\third_party\jsoncpp\makefiles\msvc2017\lib_json.vcxproj +msbuild %~dp0..\third_party\jsoncpp\makefiles\msvc2017\lib_json.vcxproj + +set DEPBINDIREXISTS=true +if not exist %~dp0..\dependencies\json\x64 set DEPBINDIREXISTS=false + +if %DEPBINDIREXISTS% == false ( + mkdir %~dp0..\dependencies\json\x64 +) + +set DEPBINDBGDIREXISTS=true +if not exist %~dp0..\dependencies\json\x64\debug set DEPBINDBGDIREXISTS=false + +if %DEPBINDBGDIREXISTS% == false ( + mkdir %~dp0..\dependencies\json\x64\debug +) + +copy %~dp0..\third_party\jsoncpp\makefiles\msvc2017\x64\debug\json_vc71_libmtd.lib %~dp0..\dependencies\json\x64\debug\. + +:: Build release lib +echo Building release lib: msbuild %~dp0..\third_party\jsoncpp\makefiles\msvc2017\lib_json.vcxproj /p:Configuration=Release +msbuild %~dp0..\third_party\jsoncpp\makefiles\msvc2017\lib_json.vcxproj /p:Configuration=Release + +set DEPBINRELDIREXISTS=true +if not exist %~dp0..\dependencies\json\x64\release set DEPBINRELDIREXISTS=false + +if %DEPBINRELDIREXISTS% == false ( + mkdir %~dp0..\dependencies\json\x64\release +) + +copy %~dp0..\third_party\jsoncpp\makefiles\msvc2017\x64\release\json_vc71_libmt.lib %~dp0..\dependencies\json\x64\release\. + +:DONE +echo jsoncpplib complete. \ No newline at end of file