diff --git a/BUILD.gn b/BUILD.gn index 380d3007b..e8c5ef6b5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -15,8 +15,12 @@ group("gn_all") { deps = [ "//library:flutter_embedder", - "//plugins/color_panel:color_panel", - "//plugins/file_chooser:file_chooser", - "//plugins/menubar:menubar", ] + if (is_linux) { + deps += [ + "//plugins/color_panel:color_panel", + "//plugins/file_chooser:file_chooser", + "//plugins/menubar:menubar", + ] + } } diff --git a/analysis_options.yaml b/analysis_options.yaml index 966fd5318..8e5e464a1 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,10 +4,6 @@ # Root analysis options shared among all Dart code in the respository. Based # on the Fuchsia standard analysis options, with some changes. -analyzer: - strong-mode: true - language: - enableSuperMixins: true linter: # Full list available at http://dart-lang.github.io/linter/lints/options/options.html. rules: @@ -29,7 +25,8 @@ linter: - avoid_null_checks_in_equality_operators - avoid_positional_boolean_parameters - avoid_private_typedef_functions - - avoid_relative_lib_imports + # TODO: Change relative imports for package imports + # - avoid_relative_lib_imports # This puts an unnecessary burden on API clients. # - avoid_renaming_method_parameters - avoid_return_types_on_setters @@ -75,7 +72,6 @@ linter: - parameter_assignments - prefer_adjacent_string_concatenation - prefer_asserts_in_initializer_lists - - prefer_bool_in_asserts - prefer_collection_literals - prefer_conditional_assignment # Disabled until bug is fixed diff --git a/build/BUILD.gn b/build/BUILD.gn index afb87c94b..276788b17 100644 --- a/build/BUILD.gn +++ b/build/BUILD.gn @@ -13,20 +13,33 @@ # limitations under the License. config("defaults") { - cflags = [ - "-std=c++14", - "-Wall", - "-Werror", - "-pthread", - ] + if (is_linux) { + cflags = [ + "-std=c++14", + "-Wall", + "-Werror", + "-pthread", + ] + } + if (is_win) { + cflags = [ + "/EHsc", + "/W3", + "/Od", + ] + } include_dirs = [ - "//" + "//", ] } config("shared_library_defaults") { - cflags = [ - "-shared", - "-fPIC", - ] + if (is_linux) { + cflags = [ + "-shared", + "-fPIC", + # Default to hidden for consistency with Windows builds. + "-fvisibility=hidden", + ] + } } diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 09cbb879a..a87e2dff4 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -set_default_toolchain("//third_party/chromium/build/toolchain:gcc") - set_defaults("executable") { configs = [ "//build:defaults" ] } @@ -28,6 +26,7 @@ set_defaults("published_shared_library") { } if (host_os == "linux") { + set_default_toolchain("//third_party/chromium/build/toolchain:gcc") is_linux = true is_mac = false is_win = false @@ -36,6 +35,7 @@ if (host_os == "linux") { is_mac = true is_win = false } else if (host_os == "win") { + set_default_toolchain("//build/win/toolchain:msvc") is_linux = false is_mac = false is_win = true diff --git a/build/packaging.gni b/build/packaging.gni index 9c06f5407..ffb3984cf 100644 --- a/build/packaging.gni +++ b/build/packaging.gni @@ -16,31 +16,29 @@ # under the top-level include/ directory in the build output. # # This is intended to make consuming public headers of all the libraries built -# by GN easy for outside build systems on Linux, by requiring only a single -# include directory. -if (is_linux) { - template("copy_includes") { - copy(target_name) { - assert(defined(invoker.sources), - "|sources| must be provided for copy_includes.") - forward_variables_from(invoker, [ "deps", "sources", "subdir" ]) - output_dir = "$root_out_dir/include" - if (defined(subdir)) { - output_dir = "$output_dir/$subdir" - } - outputs = [ "$output_dir/{{source_file_part}}"] +# by GN easy for outside build systems, by requiring only a single include +# directory. +template("copy_includes") { + copy(target_name) { + assert(defined(invoker.sources), + "|sources| must be provided for copy_includes.") + forward_variables_from(invoker, [ "deps", "sources", "subdir" ]) + output_dir = "$root_out_dir/include" + if (defined(subdir)) { + output_dir = "$output_dir/$subdir" } + outputs = [ "$output_dir/{{source_file_part}}"] } } # An abstraction for a shared library with associated headers that is intended # to be consumed from the build output. # -# On Linux, this performs a copy_includes in addition to building the library. +# This performs a copy_includes in addition to building the library. template("published_shared_library") { template_target_name = target_name - if (is_linux) { + if (is_linux || is_win) { copy_includes("_publish_${template_target_name}_headers") { sources = invoker.public subdir = invoker.public_header_subdir diff --git a/build/win/config/BUILD.gn b/build/win/config/BUILD.gn new file mode 100644 index 000000000..546e4d9a4 --- /dev/null +++ b/build/win/config/BUILD.gn @@ -0,0 +1,33 @@ +# 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. + +import("//library/windows/glfw.gni") +import("//library/windows/jsoncpp.gni") + +config("glfw3") { + libs = [ + "$glfw_lib_name", + "opengl32.lib", + + "user32.lib", + "gdi32.lib", + "shell32.lib", + ] +} + +config("jsoncpp") { + libs = [ + "$jsoncpp_lib_name", + ] +} \ No newline at end of file diff --git a/build/win/toolchain/BUILD.gn b/build/win/toolchain/BUILD.gn new file mode 100644 index 000000000..c8ccd960a --- /dev/null +++ b/build/win/toolchain/BUILD.gn @@ -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. + +toolchain("msvc") { + env_setup = "vcvars64.bat 1> nul &&" + + tool("cc") { + pdbfile = "{{target_out_dir}}/{{label_name}}_c.pdb" + command = "$env_setup cl /nologo /showIncludes /MDd /FC {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /c {{source}} /Fo{{output}} /Fd$pdbfile" + depsformat = "msvc" + description = "CC {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj", + ] + } + + tool("cxx") { + pdbfile = "{{target_out_dir}}/{{label_name}}_c.pdb" + command = "$env_setup cl /nologo /showIncludes /MDd /FC {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /c {{source}} /Fo{{output}} /Fd$pdbfile" + depsformat = "msvc" + description = "CXX {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj", + ] + } + + tool("alink") { + rspfile = "{{output}}.rsp" + command = "$env_setup lib /nologo /OUT:{{output}} @$rspfile" + description = "AR {{target_output_name}}{{output_extension}}" + rspfile_content = "{{inputs}}" + outputs = [ + "{{target_out_dir}}/{{target_output_name}}{{output_extension}}", + ] + default_output_extension = ".lib" + } + + tool("solink") { + dllname = "{{target_output_name}}{{output_extension}}" # e.g. foo.dll + dllfile = "{{output_dir}}/$dllname" + libfile = dllname + ".lib" # e.g. foo.dll.lib + pdbfile = dllname + ".pdb" + rspfile = dllname + ".rsp" + + command = "$env_setup link /nologo /DLL {{ldflags}} /IMPLIB:$libfile /OUT:$dllfile /DEBUG /PDB:$pdbfile @$rspfile" + rspfile_content = "{{inputs}} {{solibs}} {{libs}}" + + description = "SOLINK $dllfile" + + # Use this for {{output_extension}} expansions unless a target manually + # overrides it (in which case {{output_extension}} will be what the target + # specifies). + default_output_extension = ".dll" + + # Use this for {{output_dir}} expansions unless a target manually overrides + # it (in which case {{output_dir}} will be what the target specifies). + default_output_dir = "{{root_out_dir}}" + + lib_dir_switch = "/LIBPATH:" + + outputs = [ + dllfile, + libfile, + pdbfile, + ] + + link_output = dllfile + depend_output = dllfile + } + + tool("link") { + outfile = "{{target_output_name}}{{output_extension}}" + rspfile = "$outfile.rsp" + pdbfile = "$outfile.pdb" + command = "$env_setup link {{ldflags}} /nologo /OUT:$outfile /PDB:$pdbfile @$rspfile" + description = "LINK $outfile" + default_output_dir = "{{root_out_dir}}" + rspfile_content = "{{inputs}} {{libs}} {{solibs}}" + lib_dir_switch = "/LIBPATH:" + outputs = [ + outfile, + ] + } + + tool("stamp") { + command = "cmd /c echo > {{output}}" + description = "STAMP {{output}}" + } + + tool("copy") { + command = "powershell -Command Copy-Item {{source}} {{output}}" + description = "COPY {{source}} {{output}}" + } +} \ No newline at end of file diff --git a/example/flutter_app/analysis_options.yaml b/example/flutter_app/analysis_options.yaml index 5e2133eb6..f04c6cf0f 100644 --- a/example/flutter_app/analysis_options.yaml +++ b/example/flutter_app/analysis_options.yaml @@ -1 +1 @@ -include: ../analysis_options.yaml +include: ../../analysis_options.yaml diff --git a/example/flutter_app/lib/keyboard_test_page.dart b/example/flutter_app/lib/keyboard_test_page.dart index d0e8f29e6..65fb12fee 100644 --- a/example/flutter_app/lib/keyboard_test_page.dart +++ b/example/flutter_app/lib/keyboard_test_page.dart @@ -29,11 +29,6 @@ class _KeyboardTestPageState extends State { final FocusNode _focusNode = FocusNode(); final ScrollController _scrollController = new ScrollController(); - @override - void initState() { - super.initState(); - } - @override void didChangeDependencies() { super.didChangeDependencies(); diff --git a/example/flutter_app/lib/main.dart b/example/flutter_app/lib/main.dart index b5d8ef7ea..f70d89ca1 100644 --- a/example/flutter_app/lib/main.dart +++ b/example/flutter_app/lib/main.dart @@ -140,18 +140,18 @@ class _AppState extends State { } class _MyHomePage extends StatelessWidget { + const _MyHomePage({this.title, this.counter = 0}); + final String title; final int counter; - const _MyHomePage({this.title, this.counter = 0}); - void _changePrimaryThemeColor(BuildContext context) { final colorPanel = ColorPanel.instance; if (!colorPanel.showing) { colorPanel.show((color) { _AppState.of(context).setPrimaryColor(color); - // Setting the primary color to a non-opaque color raises an exception. - }, showAlpha: false); + // Setting the primary color to a non-opaque color raises an exception. + }, showAlpha: false); } } diff --git a/example/linux/flutter_embedder_example.cc b/example/linux/flutter_embedder_example.cc index 5cfad92a7..7685166cc 100644 --- a/example/linux/flutter_embedder_example.cc +++ b/example/linux/flutter_embedder_example.cc @@ -83,10 +83,15 @@ int main(int argc, char **argv) { } // Register any native plugins. - AddPlugin(window, std::make_unique()); - AddPlugin(window, std::make_unique()); - AddPlugin(window, - std::make_unique()); + plugins_menubar::MenubarPlugin::RegisterWithRegistrar( + flutter_desktop_embedding::GetRegistrarForPlugin( + window, "plugins_menubar::MenubarPlugin")); + plugins_color_panel::ColorPanelPlugin::RegisterWithRegistrar( + flutter_desktop_embedding::GetRegistrarForPlugin( + window, "plugins_color_panel::ColorPanelPlugin")); + plugins_file_chooser::FileChooserPlugin::RegisterWithRegistrar( + flutter_desktop_embedding::GetRegistrarForPlugin( + window, "plugins_file_chooser::FileChooserPlugin")); flutter_desktop_embedding::FlutterWindowLoop(window); glfwTerminate(); diff --git a/example/macos/ExampleWindow.swift b/example/macos/ExampleWindow.swift index c3d88a134..5fe496f85 100644 --- a/example/macos/ExampleWindow.swift +++ b/example/macos/ExampleWindow.swift @@ -18,9 +18,12 @@ class ExampleWindow: NSWindow { @IBOutlet weak var flutterViewController: FLEViewController! override func awakeFromNib() { - flutterViewController.add(FLEColorPanelPlugin()) - flutterViewController.add(FLEFileChooserPlugin()) - flutterViewController.add(FLEMenubarPlugin()) + FLEColorPanelPlugin.register( + with: flutterViewController.registrar(forPlugin: "FLEColorPanelPlugin")) + FLEFileChooserPlugin.register( + with: flutterViewController.registrar(forPlugin: "FLEFileChooserPlugin")) + FLEMenubarPlugin.register( + with: flutterViewController.registrar(forPlugin: "FLEMenubarPlugin")) let assets = NSURL.fileURL(withPath: "flutter_assets", relativeTo: Bundle.main.resourceURL) // Pass through argument zero, since the Flutter engine expects to be processing a full diff --git a/example/windows/Example Embedder.sln b/example/windows/Example Embedder.sln index b42884e01..f69934fe9 100644 --- a/example/windows/Example Embedder.sln +++ b/example/windows/Example Embedder.sln @@ -13,27 +13,17 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug Dynamic Library|x64 = Debug Dynamic Library|x64 - Debug Static Library|x64 = Debug Static Library|x64 Release Dynamic Library|x64 = Release Dynamic Library|x64 - Release Static Library|x64 = Release Static Library|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Debug Dynamic Library|x64.ActiveCfg = Debug Dynamic Library|x64 {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Debug Dynamic Library|x64.Build.0 = Debug Dynamic Library|x64 - {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Debug Static Library|x64.ActiveCfg = Debug Static Library|x64 - {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Debug Static Library|x64.Build.0 = Debug Static Library|x64 {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Release Dynamic Library|x64.ActiveCfg = Release Dynamic Library|x64 {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Release Dynamic Library|x64.Build.0 = Release Dynamic Library|x64 - {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Release Static Library|x64.ActiveCfg = Release Static Library|x64 - {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Release Static Library|x64.Build.0 = Release Static Library|x64 {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Debug Dynamic Library|x64.ActiveCfg = Debug Dynamic Library|x64 {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Debug Dynamic Library|x64.Build.0 = Debug Dynamic Library|x64 - {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Debug Static Library|x64.ActiveCfg = Debug Static Library|x64 - {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Debug Static Library|x64.Build.0 = Debug Static Library|x64 {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Release Dynamic Library|x64.ActiveCfg = Release Dynamic Library|x64 {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Release Dynamic Library|x64.Build.0 = Release Dynamic Library|x64 - {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Release Static Library|x64.ActiveCfg = Release Static Library|x64 - {5A827760-CF8B-408A-99A3-B6C0AD2271E7}.Release Static Library|x64.Build.0 = Release Static Library|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/example/windows/GLFW Example.vcxproj b/example/windows/GLFW Example.vcxproj index c61b451f0..42c828237 100644 --- a/example/windows/GLFW Example.vcxproj +++ b/example/windows/GLFW Example.vcxproj @@ -1,18 +1,10 @@ - - Debug Static Library - x64 - Debug Dynamic Library x64 - - Release Static Library - x64 - Release Dynamic Library x64 @@ -31,12 +23,6 @@ v141 MultiByte - - Application - true - v141 - MultiByte - Application false @@ -44,13 +30,6 @@ true MultiByte - - Application - false - v141 - true - MultiByte - @@ -59,15 +38,9 @@ - - - - - - $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ @@ -75,22 +48,10 @@ $(ProjectDir)..\..\library\windows\dependencies\;$(ProjectDir)..\..\;$(IncludePath);$(ProjectDir)..\..\library\windows\;$(ProjectDir)..\..\library\include\ $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) - - $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ - $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(ProjectDir)..\..\library\windows\;$(IncludePath) - $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) - $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)..\..\library\windows\dependencies\;$(ProjectDir)..\..\;$(IncludePath);$(ProjectDir)..\..\library\windows\ - $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) - - - $(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)..\..\library\include\;$(IncludePath) $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) @@ -120,35 +81,6 @@ Copy flutter_embedder.dll from library out to example out dir - - - Level3 - Disabled - true - true - - - - - flutter_embedder.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies) - - - $(ProjectDir)scripts\build_example_app - Build the example app - - - xcopy /y /d /q "$(OutputPath)..\GLFW Library\flutter_engine.dll" "$(OutputPath)" - Get the flutter_engine.dll file from GLFW Library bin folder - - - - - - - - - - Level3 @@ -180,39 +112,6 @@ Copy flutter_embedder.dll from library out to example out dir - - - Level3 - MaxSpeed - true - true - true - true - - - - - true - true - flutter_embedder.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies) - - - $(ProjectDir)scripts\build_example_app - Build the example app - - - xcopy /y /d /q "$(OutputPath)..\GLFW Library\flutter_engine.dll" "$(OutputPath)" - Get the flutter_engine.dll file from GLFW Library bin folder - - - - - - - - - - diff --git a/library/BUILD.gn b/library/BUILD.gn index 605deddc5..e013036f5 100644 --- a/library/BUILD.gn +++ b/library/BUILD.gn @@ -35,33 +35,25 @@ published_shared_library("flutter_embedder") { # Embedding-agnostic shared C++. if (is_linux || is_win) { sources += [ - "common/internal/engine_method_result.cc", - "common/internal/engine_method_result.h", + "common/engine_method_result.cc", "common/internal/json_message_codec.cc", "common/internal/json_message_codec.h", "common/internal/plugin_handler.cc", "common/internal/plugin_handler.h", "common/internal/text_input_model.cc", "common/internal/text_input_model.h", - "common/json_method_call.cc", "common/json_method_codec.cc", - "common/json_plugin.cc", - "common/method_call.cc", - "common/method_channel.cc", - "common/method_codec.cc", - "common/method_result.cc", - "common/plugin.cc", ] public += [ "include/flutter_desktop_embedding/binary_messenger.h", - "include/flutter_desktop_embedding/json_method_call.h", + "include/flutter_desktop_embedding/engine_method_result.h", + "include/flutter_desktop_embedding/fde_export.h", "include/flutter_desktop_embedding/json_method_codec.h", - "include/flutter_desktop_embedding/json_plugin.h", "include/flutter_desktop_embedding/method_call.h", "include/flutter_desktop_embedding/method_channel.h", "include/flutter_desktop_embedding/method_codec.h", "include/flutter_desktop_embedding/method_result.h", - "include/flutter_desktop_embedding/plugin.h", + "include/flutter_desktop_embedding/plugin_registrar.h", ] } @@ -69,31 +61,65 @@ published_shared_library("flutter_embedder") { ":fetch_flutter_engine", ] - if (is_linux) { - public_header_subdir = "flutter_desktop_embedding" + defines = [ + "FLUTTER_DESKTOP_EMBEDDING_IMPL", + ] - deps += [ - "//library/linux:publish_flutter_engine", - ] + public_header_subdir = "flutter_desktop_embedding" + + public_configs = [ + ":relative_public_headers", + ] + if (is_linux) { libs = [ "glfw", "GL", ] + deps += [ + "//library/linux:publish_flutter_engine", + ] + configs += [ "//build/linux/config:epoxy", "//build/linux/config:gtk3", "//build/linux/config:jsoncpp", "//build/linux/config:x11", ] + } + + if (is_win) { + dll_exports = rebase_path("windows/exports.def") + ldflags = [ + "/DEF:$dll_exports", + ] - public_configs = [ - "//library/linux:relative_public_headers", + deps += [ + "//library/windows:publish_flutter_engine", + "//library/windows:fetch_glfw", + "//library/windows:build_jsoncpp", + ] + + libs = [ + engine_files[2], + ] + + public_configs += [ + "//library/windows:relative_glfw_dependencies", + "//library/windows:relative_jsoncpp_dependencies", ] } } +# Allows targets depending on this library to use library-style +# inculdes for its header rather than project-relative. +config("relative_public_headers") { + include_dirs = [ + "include", + ] +} + # Allows targets depending on the engine library to use library-style # inculdes for its header rather than project-relative. config("relative_engine_headers") { @@ -110,9 +136,7 @@ action("fetch_flutter_engine") { "--flutter_root=$flutter_tree_path", rebase_path(engine_download_dir, root_build_dir), ] - if (is_linux) { - public_configs = [ - ":relative_engine_headers", - ] - } + public_configs = [ + ":relative_engine_headers", + ] } diff --git a/library/GN.md b/library/GN.md index 2288ee985..0ff71d173 100644 --- a/library/GN.md +++ b/library/GN.md @@ -1,6 +1,7 @@ # Using GN -If you are building on Linux, you can use GN instead of Make. +If you are building on Linux or Windows, you can use GN instead of Make or +Visual Studio. This is currently optional and is under evaluation, but in the future it may become the build system used on all platforms. @@ -9,10 +10,38 @@ become the build system used on all platforms. In addition to the normal dependencies, you will need to install: * [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages) -* [gn](https://gn.googlesource.com/gn/) +* [gn](https://gn.googlesource.com/gn/) Ensure that both binaries are in your path. +### Windows + +Windows also requires the 64 bit compiler, linker and setup scripts to be in +your path. They are found under: + +``` +> Visual Studio Install Path\2017\Version\VC\Auxiliary\Build +``` + +e.g.: + +``` +> C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build +``` + +Windows requires jsoncpp to be downloaded to +`library/windows/third_party/jsoncpp`. Use +`tools/dart_tools/bin/fetch_jsoncpp.dart` to automatically download `jsoncpp` +with Visual Studio 2017 support as shown below: + +``` +> tools\run_dart_tool.bat fetch_jsoncpp library\windows\third_party\jsoncpp +``` + +Currently the GN build rule for `jsoncpp` is a placeholder that will eventually +be replaced with full GN build capabilities. Currently if you modify the source +of `jsoncpp`, `out/gen/JSON` will need to be deleted for GN to rebuild it. + ## Building ### Library @@ -45,13 +74,21 @@ $ ninja -C out ### Example +#### Linux + To use the GN build for the depedencies of the example application, when running `make` for the example add `USE_GN=1` to the end of the command. The resulting binary will be in `out/example/` rather than `example/linux/out/`. +#### Windows + +Building the example with GN is not currently supported. Follow the [Visual +Studio example build instructions](../example/README.md) to build the example +app. + ## Feedback -If you encounter issues with the GN build, please test with Make before filing -a bug so that the report can include whether the issue is specific to GN, or -a general build issue. +If you encounter issues with the GN build, please test with Make or Visual +Studio before filing a bug so that the report can include whether the issue is +specific to GN, or a general build issue. diff --git a/library/common/internal/engine_method_result.cc b/library/common/engine_method_result.cc similarity index 56% rename from library/common/internal/engine_method_result.cc rename to library/common/engine_method_result.cc index 91a7d73d1..dd83babf3 100644 --- a/library/common/internal/engine_method_result.cc +++ b/library/common/engine_method_result.cc @@ -11,22 +11,22 @@ // 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 "library/common/internal/engine_method_result.h" +#include "library/include/flutter_desktop_embedding/engine_method_result.h" #include namespace flutter_desktop_embedding { +namespace internal { -EngineMethodResult::EngineMethodResult(BinaryReply reply_handler, - const MethodCodec *codec) - : reply_handler_(std::move(reply_handler)), codec_(codec) { +ReplyManager::ReplyManager(BinaryReply reply_handler) + : reply_handler_(std::move(reply_handler)) { if (!reply_handler_) { std::cerr << "Error: Reply handler must be provided for a response." << std::endl; } } -EngineMethodResult::~EngineMethodResult() { +ReplyManager::~ReplyManager() { if (reply_handler_) { // Warn, rather than send a not-implemented response, since the engine may // no longer be valid at this point. @@ -36,27 +36,12 @@ EngineMethodResult::~EngineMethodResult() { } } -void EngineMethodResult::SuccessInternal(const void *result) { - std::unique_ptr> data = - codec_->EncodeSuccessEnvelope(result); - SendResponseData(data.get()); -} - -void EngineMethodResult::ErrorInternal(const std::string &error_code, - const std::string &error_message, - const void *error_details) { - std::unique_ptr> data = - codec_->EncodeErrorEnvelope(error_code, error_message, error_details); - SendResponseData(data.get()); -} - -void EngineMethodResult::NotImplementedInternal() { SendResponseData(nullptr); } - -void EngineMethodResult::SendResponseData(const std::vector *data) { +void ReplyManager::SendResponseData(const std::vector *data) { if (!reply_handler_) { std::cerr - << "Error: Only one of Success, Error, or NotImplemented can be called," - << " and it can be called exactyl once. Ignoring duplicate result." + << "Error: Only one of Success, Error, or NotImplemented can be " + "called," + << " and it can be called exactly once. Ignoring duplicate result." << std::endl; return; } @@ -67,4 +52,5 @@ void EngineMethodResult::SendResponseData(const std::vector *data) { reply_handler_ = nullptr; } +} // namespace internal } // namespace flutter_desktop_embedding diff --git a/library/common/glfw/embedder.cc b/library/common/glfw/embedder.cc index 31abb1f52..733e5a123 100644 --- a/library/common/glfw/embedder.cc +++ b/library/common/glfw/embedder.cc @@ -51,15 +51,10 @@ 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 + // Handlers for keyboard events from GLFW. + 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; - // The screen coordinates per inch on the primary monitor. Defaults to a sane // value based on pixel_ratio 1.0. double monitor_screen_coordinates_per_inch = kDpPerInch; @@ -153,7 +148,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 : + for (const auto &handler : GetSavedEmbedderState(window)->keyboard_hook_handlers) { handler->CharHook(window, code_point); } @@ -162,7 +157,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 : + for (const auto &handler : GetSavedEmbedderState(window)->keyboard_hook_handlers) { handler->KeyboardHook(window, key, scancode, action, mods); } @@ -294,10 +289,13 @@ 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)); +PluginRegistrar *GetRegistrarForPlugin(GLFWwindow *flutter_window, + const std::string &plugin_name) { + auto *state = GetSavedEmbedderState(flutter_window); + // Currently, PluginHandler acts as the registrar for all plugins, so the + // name is ignored. It is part of the API to reduce churn in the future when + // aligning more closely with the Flutter registrar system. + return state->plugin_handler.get(); } GLFWwindow *CreateFlutterWindowInSnapshotMode( @@ -334,16 +332,14 @@ GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, 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()); + // Set up the keyboard handlers. + state->keyboard_hook_handlers.push_back( + std::make_unique(state->plugin_handler.get())); + state->keyboard_hook_handlers.push_back( + std::make_unique(state->plugin_handler.get())); glfwSetWindowUserPointer(window, state); - AddPlugin(window, std::move(input_plugin)); - state->monitor_screen_coordinates_per_inch = GetScreenCoordinatesPerInch(); int width_px, height_px; glfwGetFramebufferSize(window, &width_px, &height_px); diff --git a/library/common/glfw/key_event_handler.h b/library/common/glfw/key_event_handler.h index 1b1793feb..ccf86b20d 100644 --- a/library/common/glfw/key_event_handler.h +++ b/library/common/glfw/key_event_handler.h @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. #ifndef LIBRARY_COMMON_GLFW_KEY_EVENT_HANDLER_H_ -#define LIBRARY_COMMON_GLFW_kEY_EVENT_HANDLER_H_ +#define LIBRARY_COMMON_GLFW_KEY_EVENT_HANDLER_H_ #include "library/common/glfw/keyboard_hook_handler.h" #include "library/include/flutter_desktop_embedding/binary_messenger.h" diff --git a/library/common/glfw/text_input_plugin.cc b/library/common/glfw/text_input_plugin.cc index d9890d13f..dfb98d396 100644 --- a/library/common/glfw/text_input_plugin.cc +++ b/library/common/glfw/text_input_plugin.cc @@ -16,6 +16,8 @@ #include #include +#include "library/include/flutter_desktop_embedding/json_method_codec.h" + static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState"; static constexpr char kClearClientMethod[] = "TextInput.clearClient"; static constexpr char kSetClientMethod[] = "TextInput.setClient"; @@ -97,13 +99,23 @@ void TextInputPlugin::KeyboardHook(GLFWwindow *window, int key, int scancode, } } -TextInputPlugin::TextInputPlugin() - : JsonPlugin(kChannelName, false), active_model_(nullptr) {} +TextInputPlugin::TextInputPlugin(PluginRegistrar *registrar) + : channel_(std::make_unique>( + registrar->messenger(), kChannelName, + &JsonMethodCodec::GetInstance())), + active_model_(nullptr) { + channel_->SetMethodCallHandler( + [this](const MethodCall &call, + std::unique_ptr> result) { + HandleMethodCall(call, std::move(result)); + }); +} TextInputPlugin::~TextInputPlugin() {} -void TextInputPlugin::HandleJsonMethodCall( - const JsonMethodCall &method_call, std::unique_ptr result) { +void TextInputPlugin::HandleMethodCall( + const MethodCall &method_call, + std::unique_ptr> result) { const std::string &method = method_call.method_name(); if (method.compare(kShowMethod) == 0 || method.compare(kHideMethod) == 0) { @@ -112,11 +124,11 @@ void TextInputPlugin::HandleJsonMethodCall( active_model_ = nullptr; } else { // Every following method requires args. - const Json::Value &args = method_call.GetArgumentsAsJson(); - if (args.isNull()) { + if (!method_call.arguments() || method_call.arguments()->isNull()) { result->Error(kBadArgumentError, "Method invoked without args"); return; } + const Json::Value &args = *method_call.arguments(); if (method.compare(kSetClientMethod) == 0) { // TODO(awdavies): There's quite a wealth of arguments supplied with this @@ -173,15 +185,16 @@ void TextInputPlugin::HandleJsonMethodCall( } void TextInputPlugin::SendStateUpdate(const TextInputModel &model) { - InvokeMethod(kUpdateEditingStateMethod, model.GetState()); + channel_->InvokeMethod(kUpdateEditingStateMethod, + std::make_unique(model.GetState())); } void TextInputPlugin::EnterPressed(const TextInputModel &model) { - Json::Value args = Json::arrayValue; - args.append(model.client_id()); - args.append(kDoneAction); + auto args = std::make_unique(Json::arrayValue); + args->append(model.client_id()); + args->append(kDoneAction); - InvokeMethod(kPerformActionMethod, args); + channel_->InvokeMethod(kPerformActionMethod, std::move(args)); } } // namespace flutter_desktop_embedding diff --git a/library/common/glfw/text_input_plugin.h b/library/common/glfw/text_input_plugin.h index d5016cb64..6a0ce36cd 100644 --- a/library/common/glfw/text_input_plugin.h +++ b/library/common/glfw/text_input_plugin.h @@ -19,27 +19,22 @@ #include "library/common/glfw/keyboard_hook_handler.h" #include "library/common/internal/text_input_model.h" -#include "library/include/flutter_desktop_embedding/json_plugin.h" +#include "library/include/flutter_desktop_embedding/method_channel.h" +#include "library/include/flutter_desktop_embedding/plugin_registrar.h" namespace flutter_desktop_embedding { // Implements a text input plugin. // // Specifically handles window events within GLFW. -class TextInputPlugin : public KeyboardHookHandler, public JsonPlugin { +class TextInputPlugin : public KeyboardHookHandler { public: - TextInputPlugin(); + TextInputPlugin(PluginRegistrar *registrar); virtual ~TextInputPlugin(); - // Plugin. - void HandleJsonMethodCall(const JsonMethodCall &method_call, - std::unique_ptr result) override; - - // KeyboardHookHandler. + // KeyboardHookHandler: void KeyboardHook(GLFWwindow *window, int key, int scancode, int action, int mods) override; - - // KeyboardHookHandler. void CharHook(GLFWwindow *window, unsigned int code_point) override; private: @@ -49,6 +44,13 @@ class TextInputPlugin : public KeyboardHookHandler, public JsonPlugin { // Sends an action triggered by the Enter key to the Flutter engine. void EnterPressed(const TextInputModel &model); + // Called when a method is called on |channel_|; + void HandleMethodCall(const MethodCall &method_call, + std::unique_ptr> result); + + // The MethodChannel used for communication with the Flutter engine. + std::unique_ptr> channel_; + // Mapping of client IDs to text input models. std::map> input_models_; diff --git a/library/common/internal/engine_method_result.h b/library/common/internal/engine_method_result.h deleted file mode 100644 index e97c0dedf..000000000 --- a/library/common/internal/engine_method_result.h +++ /dev/null @@ -1,57 +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_COMMON_INTERNAL_ENGINE_METHOD_RESULT_H_ -#define LIBRARY_COMMON_INTERNAL_ENGINE_METHOD_RESULT_H_ - -#include -#include - -#include "library/include/flutter_desktop_embedding/binary_messenger.h" -#include "library/include/flutter_desktop_embedding/method_codec.h" -#include "library/include/flutter_desktop_embedding/method_result.h" - -namespace flutter_desktop_embedding { - -// Implemention of MethodResult that sends responses to the Flutter egnine. -class EngineMethodResult : public MethodResult { - public: - // Creates a result object that will send results to |reply_handler|, encoded - // using |codec|. The |codec| pointer must remain valid for as long as this - // object exists. - // - // If the codec is null, only NotImplemented() may be called. - EngineMethodResult(BinaryReply reply_handler, const MethodCodec *codec); - ~EngineMethodResult(); - - protected: - // MethodResult: - void SuccessInternal(const void *result) override; - void ErrorInternal(const std::string &error_code, - const std::string &error_message, - const void *error_details) override; - void NotImplementedInternal() override; - - private: - // Sends the given response data (which must either be nullptr, which - // indicates an unhandled method, or a response serialized with |codec_|) to - // the engine. - void SendResponseData(const std::vector *data); - - BinaryReply reply_handler_; - const MethodCodec *codec_; -}; - -} // namespace flutter_desktop_embedding - -#endif // LIBRARY_COMMON_INTERNAL_ENGINE_METHOD_RESULT_H_ diff --git a/library/common/internal/plugin_handler.cc b/library/common/internal/plugin_handler.cc index 9454d84b9..92161d583 100644 --- a/library/common/internal/plugin_handler.cc +++ b/library/common/internal/plugin_handler.cc @@ -13,7 +13,7 @@ // limitations under the License. #include "library/common/internal/plugin_handler.h" -#include "library/common/internal/engine_method_result.h" +#include "library/include/flutter_desktop_embedding/engine_method_result.h" #include "library/include/flutter_desktop_embedding/method_channel.h" #include @@ -24,15 +24,6 @@ PluginHandler::PluginHandler(FlutterEngine engine) : engine_(engine) {} PluginHandler::~PluginHandler() {} -bool PluginHandler::AddPlugin(std::unique_ptr plugin) { - if (plugins_.find(plugin->channel()) != plugins_.end()) { - return false; - } - plugin->SetBinaryMessenger(this); - plugins_.insert(std::make_pair(plugin->channel(), std::move(plugin))); - return true; -} - void PluginHandler::HandleMethodCallMessage( const FlutterPlatformMessage *message, std::function input_block_cb, @@ -58,26 +49,25 @@ void PluginHandler::HandleMethodCallMessage( // Find the handler for the channel; if there isn't one, report the failure. if (handlers_.find(channel) == handlers_.end()) { - auto result = - std::make_unique( - std::move(reply_handler), nullptr); - result->NotImplemented(); + reply_handler(nullptr, 0); return; } const BinaryMessageHandler &message_handler = handlers_[channel]; - const std::unique_ptr &plugin = plugins_[channel]; - // Process the call, handling input blocking if requested by the plugin. - if (plugin && plugin->input_blocking()) { + // Process the call, handling input blocking if requested. + bool block_input = input_blocking_channels_.count(channel) > 0; + if (block_input) { input_block_cb(); } message_handler(message->message, message->message_size, std::move(reply_handler)); - if (plugin && plugin->input_blocking()) { + if (block_input) { input_unblock_cb(); } } +// BinaryMessenger: + void PluginHandler::Send(const std::string &channel, const uint8_t *message, const size_t message_size) const { FlutterPlatformMessage platform_message = { @@ -94,4 +84,14 @@ void PluginHandler::SetMessageHandler(const std::string &channel, handlers_[channel] = std::move(handler); } +// PluginRegistrar: + +void PluginHandler::AddPlugin(std::unique_ptr plugin) { + plugins_.insert(std::move(plugin)); +} + +void PluginHandler::EnableInputBlockingForChannel(const std::string &channel) { + input_blocking_channels_.insert(channel); +} + } // namespace flutter_desktop_embedding diff --git a/library/common/internal/plugin_handler.h b/library/common/internal/plugin_handler.h index 65dfc1bbd..91b359bcc 100644 --- a/library/common/internal/plugin_handler.h +++ b/library/common/internal/plugin_handler.h @@ -16,19 +16,20 @@ #include #include +#include #include #include #include "library/include/flutter_desktop_embedding/binary_messenger.h" -#include "library/include/flutter_desktop_embedding/plugin.h" +#include "library/include/flutter_desktop_embedding/plugin_registrar.h" namespace flutter_desktop_embedding { // A class for managing a set of plugins. // // The plugins all map from a unique channel name to an actual plugin. -class PluginHandler : public BinaryMessenger { +class PluginHandler : public BinaryMessenger, public PluginRegistrar { public: // Creates a new PluginHandler. |engine| must remain valid as long as this // object exists. @@ -39,26 +40,18 @@ class PluginHandler : public BinaryMessenger { PluginHandler(PluginHandler const &) = delete; PluginHandler &operator=(PluginHandler const &) = delete; - // Attempts to add the given plugin. + // Decodes the method call in |message| and routes it to to the registered + // handler for |message|'s channel, if any. // - // Returns true if the plugin could be registered, false if there is already - // a plugin registered under the same channel. - bool AddPlugin(std::unique_ptr plugin); - - // Decodes the method call in |message| and routes it to to the plugin - // registered for |message|'s channel, if any. - // - // In the event that the plugin on the message's channel is input blocking, - // calls the caller-defined callbacks to block and then unblock input. + // If input blocking has been enabled on that channel, wraps the call to the + // handler with calls to the given callbacks to block and then unblock input. // - // If no plugin is registered for the message's channel, sends a + // If no handler is registered for the message's channel, sends a // NotImplemented response to the engine. - // - // TODO: Move to an API matching Flutter's MethodChannel. - void HandleMethodCallMessage(const FlutterPlatformMessage *message, - std::function input_block_cb = [] {}, - std::function input_unblock_cb = - [] {}); + void HandleMethodCallMessage( + const FlutterPlatformMessage *message, + std::function input_block_cb = [] {}, + std::function input_unblock_cb = [] {}); // BinaryMessenger implementation: void Send(const std::string &channel, const uint8_t *message, @@ -66,10 +59,24 @@ class PluginHandler : public BinaryMessenger { void SetMessageHandler(const std::string &channel, BinaryMessageHandler handler) override; + // PluginRegistrar implementation: + BinaryMessenger *messenger() override { return this; } + void AddPlugin(std::unique_ptr plugin) override; + void EnableInputBlockingForChannel(const std::string &channel) override; + private: FlutterEngine engine_; - std::map> plugins_; + + // Plugins registered for ownership via PluginRegistrar. + std::set> plugins_; + + // A map from channel names to the BinaryMessageHandler that should be called + // for incoming messages on that channel. std::map handlers_; + + // Channel names for which input blocking should be enabled during the call to + // that channel's handler. + std::set input_blocking_channels_; }; } // namespace flutter_desktop_embedding diff --git a/library/common/json_method_call.cc b/library/common/json_method_call.cc deleted file mode 100644 index db2d847ff..000000000 --- a/library/common/json_method_call.cc +++ /dev/null @@ -1,30 +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 "library/include/flutter_desktop_embedding/json_method_call.h" - -namespace flutter_desktop_embedding { - -JsonMethodCall::JsonMethodCall(const std::string &method_name, - const Json::Value &arguments) - : MethodCall(method_name), arguments_(arguments) {} - -JsonMethodCall::~JsonMethodCall() {} - -const void *JsonMethodCall::arguments() const { return &arguments_; } - -const Json::Value &JsonMethodCall::GetArgumentsAsJson() const { - return arguments_; -} - -} // namespace flutter_desktop_embedding diff --git a/library/common/json_method_codec.cc b/library/common/json_method_codec.cc index 69c3d4f16..2538860c4 100644 --- a/library/common/json_method_codec.cc +++ b/library/common/json_method_codec.cc @@ -14,7 +14,6 @@ #include "library/include/flutter_desktop_embedding/json_method_codec.h" #include "library/common/internal/json_message_codec.h" -#include "library/include/flutter_desktop_embedding/json_method_call.h" namespace flutter_desktop_embedding { @@ -30,8 +29,9 @@ const JsonMethodCodec &JsonMethodCodec::GetInstance() { return sInstance; } -std::unique_ptr JsonMethodCodec::DecodeMethodCallInternal( - const uint8_t *message, const size_t message_size) const { +std::unique_ptr> +JsonMethodCodec::DecodeMethodCallInternal(const uint8_t *message, + const size_t message_size) const { std::unique_ptr json_message = JsonMessageCodec::GetInstance().DecodeMessage(message, message_size); if (!json_message) { @@ -42,39 +42,37 @@ std::unique_ptr JsonMethodCodec::DecodeMethodCallInternal( if (method.isNull()) { return nullptr; } - Json::Value arguments = (*json_message)[kMessageArgumentsKey]; - return std::make_unique(method.asString(), arguments); + return std::make_unique>( + method.asString(), + std::make_unique((*json_message)[kMessageArgumentsKey])); } std::unique_ptr> JsonMethodCodec::EncodeMethodCallInternal( - const MethodCall &method_call) const { + const MethodCall &method_call) const { Json::Value message(Json::objectValue); message[kMessageMethodKey] = method_call.method_name(); - message[kMessageArgumentsKey] = - *static_cast(method_call.arguments()); + const Json::Value *arguments = method_call.arguments(); + message[kMessageArgumentsKey] = arguments ? *arguments : Json::Value(); return JsonMessageCodec::GetInstance().EncodeMessage(message); } std::unique_ptr> -JsonMethodCodec::EncodeSuccessEnvelopeInternal(const void *result) const { +JsonMethodCodec::EncodeSuccessEnvelopeInternal( + const Json::Value *result) const { Json::Value envelope(Json::arrayValue); - envelope.append(result == nullptr - ? Json::Value() - : *static_cast(result)); + envelope.append(result == nullptr ? Json::Value() : *result); return JsonMessageCodec::GetInstance().EncodeMessage(envelope); } std::unique_ptr> -JsonMethodCodec::EncodeErrorEnvelopeInternal(const std::string &error_code, - const std::string &error_message, - const void *error_details) const { +JsonMethodCodec::EncodeErrorEnvelopeInternal( + const std::string &error_code, const std::string &error_message, + const Json::Value *error_details) const { Json::Value envelope(Json::arrayValue); envelope.append(error_code); envelope.append(error_message.empty() ? Json::Value() : error_message); - envelope.append(error_details == nullptr - ? Json::Value() - : *static_cast(error_details)); + envelope.append(error_details == nullptr ? Json::Value() : *error_details); return JsonMessageCodec::GetInstance().EncodeMessage(envelope); } diff --git a/library/common/json_plugin.cc b/library/common/json_plugin.cc deleted file mode 100644 index 5f4b12ef9..000000000 --- a/library/common/json_plugin.cc +++ /dev/null @@ -1,51 +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 "library/include/flutter_desktop_embedding/json_plugin.h" - -#include "library/include/flutter_desktop_embedding/json_method_codec.h" - -namespace flutter_desktop_embedding { - -JsonPlugin::JsonPlugin(const std::string &channel, bool input_blocking) - : Plugin(channel, input_blocking) {} - -JsonPlugin::~JsonPlugin() {} - -const MethodCodec &JsonPlugin::GetCodec() const { - return JsonMethodCodec::GetInstance(); -} - -void JsonPlugin::HandleMethodCall(const MethodCall &method_call, - std::unique_ptr result) { - HandleJsonMethodCall(dynamic_cast(method_call), - std::move(result)); -} - -void JsonPlugin::RegisterMethodChannels(BinaryMessenger *messenger) { - method_channel_ = - std::make_unique(messenger, channel(), &GetCodec()); - - MethodCallHandler handler = [this](const MethodCall &call, - std::unique_ptr result) { - HandleMethodCall(call, std::move(result)); - }; - method_channel_->SetMethodCallHandler(std::move(handler)); -} - -void JsonPlugin::InvokeMethod(const std::string &method, - const Json::Value &arguments) { - method_channel_->InvokeMethodCall(JsonMethodCall(method, arguments)); -} - -} // namespace flutter_desktop_embedding diff --git a/library/common/method_call.cc b/library/common/method_call.cc deleted file mode 100644 index 4dcd4c1cb..000000000 --- a/library/common/method_call.cc +++ /dev/null @@ -1,23 +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 "library/include/flutter_desktop_embedding/method_call.h" - -namespace flutter_desktop_embedding { - -MethodCall::MethodCall(const std::string &method_name) - : method_name_(method_name) {} - -MethodCall::~MethodCall() {} - -} // namespace flutter_desktop_embedding diff --git a/library/common/method_channel.cc b/library/common/method_channel.cc deleted file mode 100644 index 8ba9b406b..000000000 --- a/library/common/method_channel.cc +++ /dev/null @@ -1,56 +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 "library/include/flutter_desktop_embedding/method_channel.h" - -#include - -#include "library/common/internal/engine_method_result.h" - -namespace flutter_desktop_embedding { - -MethodChannel::MethodChannel(BinaryMessenger *messenger, - const std::string &name, const MethodCodec *codec) - : messenger_(messenger), name_(name), codec_(codec) {} - -MethodChannel::~MethodChannel() {} - -void MethodChannel::InvokeMethodCall(const MethodCall &method_call) const { - std::unique_ptr> message = - codec_->EncodeMethodCall(method_call); - messenger_->Send(name_, message->data(), message->size()); -} - -void MethodChannel::SetMethodCallHandler(MethodCallHandler handler) const { - const auto *codec = codec_; - std::string channel_name = name_; - BinaryMessageHandler binary_handler = [handler, codec, channel_name]( - const uint8_t *message, - const size_t message_size, - BinaryReply reply) { - // Use this channel's codec to decode the call and build a result handler. - auto result = std::make_unique(std::move(reply), codec); - std::unique_ptr method_call = - codec->DecodeMethodCall(message, message_size); - if (!method_call) { - std::cerr << "Unable to construct method call from message on channel " - << channel_name << std::endl; - result->NotImplemented(); - return; - } - handler(*method_call, std::move(result)); - }; - messenger_->SetMessageHandler(name_, std::move(binary_handler)); -} - -} // namespace flutter_desktop_embedding diff --git a/library/common/method_codec.cc b/library/common/method_codec.cc deleted file mode 100644 index 9899041d5..000000000 --- a/library/common/method_codec.cc +++ /dev/null @@ -1,41 +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 "library/include/flutter_desktop_embedding/method_codec.h" - -namespace flutter_desktop_embedding { - -MethodCodec::~MethodCodec() {} - -std::unique_ptr MethodCodec::DecodeMethodCall( - const uint8_t *message, const size_t message_size) const { - return DecodeMethodCallInternal(message, message_size); -} - -std::unique_ptr> MethodCodec::EncodeMethodCall( - const MethodCall &method_call) const { - return EncodeMethodCallInternal(method_call); -} - -std::unique_ptr> MethodCodec::EncodeSuccessEnvelope( - const void *result) const { - return EncodeSuccessEnvelopeInternal(result); -} - -std::unique_ptr> MethodCodec::EncodeErrorEnvelope( - const std::string &error_code, const std::string &error_message, - const void *error_details) const { - return EncodeErrorEnvelopeInternal(error_code, error_message, error_details); -} - -} // namespace flutter_desktop_embedding diff --git a/library/common/method_result.cc b/library/common/method_result.cc deleted file mode 100644 index c81c28fbd..000000000 --- a/library/common/method_result.cc +++ /dev/null @@ -1,32 +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 "library/include/flutter_desktop_embedding/method_result.h" - -namespace flutter_desktop_embedding { - -MethodResult::MethodResult() {} - -MethodResult::~MethodResult() {} - -void MethodResult::Success(const void *result) { SuccessInternal(result); } - -void MethodResult::Error(const std::string &error_code, - const std::string &error_message, - const void *error_details) { - ErrorInternal(error_code, error_message, error_details); -} - -void MethodResult::NotImplemented() { NotImplementedInternal(); } - -} // namespace flutter_desktop_embedding diff --git a/library/common/plugin.cc b/library/common/plugin.cc deleted file mode 100644 index d6587f08f..000000000 --- a/library/common/plugin.cc +++ /dev/null @@ -1,39 +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 "library/include/flutter_desktop_embedding/plugin.h" - -#include "library/include/flutter_desktop_embedding/json_method_codec.h" - -namespace flutter_desktop_embedding { - -Plugin::Plugin(const std::string &channel, bool input_blocking) - : channel_(channel), messenger_(nullptr), input_blocking_(input_blocking) {} - -Plugin::~Plugin() {} - -void Plugin::SetBinaryMessenger(BinaryMessenger *messenger) { - messenger_ = messenger; - RegisterMethodChannels(messenger); -} - -void Plugin::InvokeMethodCall(const MethodCall &method_call) { - if (!messenger_) { - return; - } - std::unique_ptr> message = - GetCodec().EncodeMethodCall(method_call); - messenger_->Send(channel_, message->data(), message->size()); -} - -} // namespace flutter_desktop_embedding diff --git a/library/engine.gni b/library/engine.gni index 92018bf21..8db14add8 100644 --- a/library/engine.gni +++ b/library/engine.gni @@ -18,7 +18,17 @@ engine_download_dir = "$root_gen_dir/flutter_engine" # The files that make up the downloaded engine library on Linux. if (is_linux) { engine_files = [ + "$engine_download_dir/flutter_embedder.h", "$engine_download_dir/libflutter_engine.so", + ] +} + +if (is_win) { + engine_files = [ "$engine_download_dir/flutter_embedder.h", + "$engine_download_dir/flutter_engine.dll", + "$engine_download_dir/flutter_engine.dll.lib", + "$engine_download_dir/flutter_engine.dll.exp", + "$engine_download_dir/flutter_engine.dll.pdb", ] } diff --git a/library/include/flutter_desktop_embedding/binary_messenger.h b/library/include/flutter_desktop_embedding/binary_messenger.h index 4e6f73b13..13f18ac33 100644 --- a/library/include/flutter_desktop_embedding/binary_messenger.h +++ b/library/include/flutter_desktop_embedding/binary_messenger.h @@ -17,6 +17,8 @@ #include #include +#include "fde_export.h" + // TODO: Consider adding absl as a dependency and using absl::Span for all of // the message/message_size pairs. namespace flutter_desktop_embedding { @@ -36,7 +38,7 @@ typedef std::function +#include +#include + +#include "binary_messenger.h" +#include "fde_export.h" +#include "method_codec.h" +#include "method_result.h" + +namespace flutter_desktop_embedding { + +namespace internal { +// Manages the one-time sending of response data. This is an internal helper +// class for EngineMethodResult, separated out since the implementation doesn't +// vary based on the template type. +class FDE_EXPORT ReplyManager { + public: + ReplyManager(BinaryReply reply_handler_); + ~ReplyManager(); + + // Prevent copying. + ReplyManager(ReplyManager const &) = delete; + ReplyManager &operator=(ReplyManager const &) = delete; + + // Sends the given response data (which must either be nullptr, which + // indicates an unhandled method, or a response serialized with |codec_|) to + // the engine. + void SendResponseData(const std::vector *data); + + private: + BinaryReply reply_handler_; +}; +} // namespace internal + +// Implemention of MethodResult that sends a response to the Flutter engine +// exactly once, encoded using a given codec. +template +class FDE_EXPORT EngineMethodResult : public MethodResult { + public: + // Creates a result object that will send results to |reply_handler|, encoded + // using |codec|. The |codec| pointer must remain valid for as long as this + // object exists. + EngineMethodResult(BinaryReply reply_handler, const MethodCodec *codec) + : reply_manager_( + std::make_unique(std::move(reply_handler))), + codec_(codec) {} + ~EngineMethodResult() = default; + + protected: + // MethodResult: + void SuccessInternal(const T *result) override { + std::unique_ptr> data = + codec_->EncodeSuccessEnvelope(result); + reply_manager_->SendResponseData(data.get()); + } + void ErrorInternal(const std::string &error_code, + const std::string &error_message, + const T *error_details) override { + std::unique_ptr> data = + codec_->EncodeErrorEnvelope(error_code, error_message, error_details); + reply_manager_->SendResponseData(data.get()); + } + void NotImplementedInternal() override { + reply_manager_->SendResponseData(nullptr); + } + + private: + std::unique_ptr reply_manager_; + + const MethodCodec *codec_; +}; + +} // namespace flutter_desktop_embedding + +#endif // LIBRARY_LINUX_SRC_INTERNAL_ENGINE_METHOD_RESULT_H_ diff --git a/library/include/flutter_desktop_embedding/fde_export.h b/library/include/flutter_desktop_embedding/fde_export.h new file mode 100644 index 000000000..06b83fb97 --- /dev/null +++ b/library/include/flutter_desktop_embedding/fde_export.h @@ -0,0 +1,37 @@ +// Copyright 2019 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_FDE_EXPORT_H_ +#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_FDE_EXPORT_H_ + +#ifdef FLUTTER_DESKTOP_EMBEDDING_IMPL +// Add visibiilty/export annotations when building the library. + +#ifdef _WIN32 +#define FDE_EXPORT __declspec(dllexport) +#else +#define FDE_EXPORT __attribute__((visibility("default"))) +#endif + +#else + +// Add import annotations when consuming the library. +#ifdef _WIN32 +#define FDE_EXPORT __declspec(dllimport) +#else +#define FDE_EXPORT +#endif + +#endif + +#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_FDE_EXPORT_H_ diff --git a/library/include/flutter_desktop_embedding/glfw/embedder.h b/library/include/flutter_desktop_embedding/glfw/embedder.h index 669ea9d50..0b37d1114 100644 --- a/library/include/flutter_desktop_embedding/glfw/embedder.h +++ b/library/include/flutter_desktop_embedding/glfw/embedder.h @@ -27,9 +27,11 @@ #include #ifdef USE_FLATTENED_INCLUDES -#include "plugin.h" +#include "fde_export.h" +#include "plugin_registrar.h" #else -#include "../plugin.h" +#include "../fde_export.h" +#include "../plugin_registrar.h" #endif namespace flutter_desktop_embedding { @@ -37,12 +39,12 @@ namespace flutter_desktop_embedding { // Calls glfwInit() // // glfwInit() must be called in the same library as glfwCreateWindow() -bool FlutterInit(); +FDE_EXPORT bool FlutterInit(); // Calls glfwTerminate() // // glfwTerminate() must be called in the same library as glfwCreateWindow() -void FlutterTerminate(); +FDE_EXPORT void FlutterTerminate(); // Creates a GLFW Window running a Flutter Application. // @@ -54,12 +56,11 @@ void FlutterTerminate(); // // 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); +FDE_EXPORT 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. // @@ -74,16 +75,18 @@ GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, // // Returns a null pointer in the event of an error. The caller owns the pointer // when it is non-null. -GLFWwindow *CreateFlutterWindowInSnapshotMode( +FDE_EXPORT 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. +// Returns the PluginRegistrar to register a plugin with the given name with +// 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); +// The name must be unique across the application, so the recommended approach +// is to use the fully namespace-qualified name of the plugin class. +FDE_EXPORT PluginRegistrar *GetRegistrarForPlugin( + GLFWwindow *flutter_window, const std::string &plugin_name); // Loops on flutter window events until termination. // @@ -92,7 +95,7 @@ bool AddPlugin(GLFWwindow *flutter_window, std::unique_ptr plugin); // // After this function the user must eventually call FlutterTerminate() if doing // cleanup. -void FlutterWindowLoop(GLFWwindow *flutter_window); +FDE_EXPORT void FlutterWindowLoop(GLFWwindow *flutter_window); } // namespace flutter_desktop_embedding diff --git a/library/include/flutter_desktop_embedding/json_method_call.h b/library/include/flutter_desktop_embedding/json_method_call.h deleted file mode 100644 index bfc552468..000000000 --- a/library/include/flutter_desktop_embedding/json_method_call.h +++ /dev/null @@ -1,53 +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_JSON_METHOD_CALL_H_ -#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_METHOD_CALL_H_ - -#include - -#include - -#include "method_call.h" - -namespace flutter_desktop_embedding { - -// A concrete implmentation of MethodCall for use with the JSON codec. -class JsonMethodCall : public MethodCall { - public: - // Creates a MethodCall with the given name and, optionally, arguments. - explicit JsonMethodCall(const std::string &method_name, - const Json::Value &arguments = Json::Value()); - ~JsonMethodCall(); - - // Prevent copying. - JsonMethodCall(JsonMethodCall const &) = delete; - JsonMethodCall &operator=(JsonMethodCall const &) = delete; - - // MethodCall: - // Returns a pointer to a Json::Value object. This will never return null; - // 'no arguments' is represented with a JSON::nullValue. - const void *arguments() const override; - - // Returns a reference to the object pointed to by arguments(). - // This is a convience method for code that is interacting directly with a - // JsonMethodCall to avoid casting and dereferencing. - const Json::Value &GetArgumentsAsJson() const; - - private: - Json::Value arguments_; -}; - -} // namespace flutter_desktop_embedding - -#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_METHOD_CALL_H_ diff --git a/library/include/flutter_desktop_embedding/json_method_codec.h b/library/include/flutter_desktop_embedding/json_method_codec.h index a34da1b65..9b1978830 100644 --- a/library/include/flutter_desktop_embedding/json_method_codec.h +++ b/library/include/flutter_desktop_embedding/json_method_codec.h @@ -14,15 +14,16 @@ #ifndef LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_METHOD_CODEC_H_ #define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_METHOD_CODEC_H_ +#include + +#include "fde_export.h" +#include "method_call.h" #include "method_codec.h" namespace flutter_desktop_embedding { // An implementation of MethodCodec that uses JSON strings as the serialization. -// -// void* types in this implementation must always be Json::Value* types (from -// the jsoncpp library). -class JsonMethodCodec : public MethodCodec { +class FDE_EXPORT JsonMethodCodec : public MethodCodec { public: // Returns the shared instance of the codec. static const JsonMethodCodec &GetInstance(); @@ -38,15 +39,15 @@ class JsonMethodCodec : public MethodCodec { JsonMethodCodec() = default; // MethodCodec: - std::unique_ptr DecodeMethodCallInternal( + std::unique_ptr> DecodeMethodCallInternal( const uint8_t *message, const size_t message_size) const override; std::unique_ptr> EncodeMethodCallInternal( - const MethodCall &method_call) const override; + const MethodCall &method_call) const override; std::unique_ptr> EncodeSuccessEnvelopeInternal( - const void *result) const override; + const Json::Value *result) const override; std::unique_ptr> EncodeErrorEnvelopeInternal( const std::string &error_code, const std::string &error_message, - const void *error_details) const override; + const Json::Value *error_details) const override; }; } // namespace flutter_desktop_embedding diff --git a/library/include/flutter_desktop_embedding/json_plugin.h b/library/include/flutter_desktop_embedding/json_plugin.h deleted file mode 100644 index 59d4a85b4..000000000 --- a/library/include/flutter_desktop_embedding/json_plugin.h +++ /dev/null @@ -1,67 +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_JSON_PLUGIN_H_ -#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_PLUGIN_H_ - -#include -#include - -#include "json_method_call.h" -#include "method_channel.h" -#include "plugin.h" - -namespace flutter_desktop_embedding { - -// A base class for plugins using the JSON method codec. -// -// Provides a few utility shims from the type-agnostic Plugin class. -class JsonPlugin : public Plugin { - public: - // See Plugin for constructor details. - explicit JsonPlugin(const std::string &channel, bool input_blocking = false); - virtual ~JsonPlugin(); - - // Prevent copying. - JsonPlugin(JsonPlugin const &) = delete; - JsonPlugin &operator=(JsonPlugin const &) = delete; - - // Plugin implementation: - const MethodCodec &GetCodec() const override; - void HandleMethodCall(const MethodCall &method_call, - std::unique_ptr result) override; - - protected: - // Plugin implementation: - void RegisterMethodChannels(BinaryMessenger *messenger) override; - - // Identical to HandleMethodCall, except that the call has been cast to the - // more specific type. Subclasses must implement this instead of - // HandleMethodCall. - virtual void HandleJsonMethodCall(const JsonMethodCall &method_call, - std::unique_ptr result) = 0; - - // Calls InvokeMethodCall on |method_channel_| with a JsonMethodCall - // constructed from the given values. - void InvokeMethod(const std::string &method, - const Json::Value &arguments = Json::Value()); - - private: - // The MethodChannel used by this plugin for communication with the Flutter - // engine. - std::unique_ptr method_channel_; -}; - -} // namespace flutter_desktop_embedding - -#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_PLUGIN_H_ diff --git a/library/include/flutter_desktop_embedding/method_call.h b/library/include/flutter_desktop_embedding/method_call.h index 1e801718b..64c06ecec 100644 --- a/library/include/flutter_desktop_embedding/method_call.h +++ b/library/include/flutter_desktop_embedding/method_call.h @@ -11,36 +11,42 @@ // 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_METHOD_CALL_H_ -#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_METHOD_CALL_H_ +#ifndef LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_TYPED_METHOD_CALL_H_ +#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_TYPED_METHOD_CALL_H_ +#include #include +#include "fde_export.h" + namespace flutter_desktop_embedding { -// An object encapsulating a method call from Flutter. -class MethodCall { +// An object encapsulating a method call from Flutter whose arguments are of +// type T. +template +class FDE_EXPORT MethodCall { public: - // Creates a MethodCall with the given name. Used only as a superclass - // constructor for subclasses, which should also take the arguments. - explicit MethodCall(const std::string &method_name); - virtual ~MethodCall(); + // Creates a MethodCall with the given name and arguments. + explicit MethodCall(const std::string &method_name, + std::unique_ptr arguments) + : method_name_(method_name), arguments_(std::move(arguments)) {} + virtual ~MethodCall() = default; // Prevent copying. - MethodCall(MethodCall const &) = delete; - MethodCall &operator=(MethodCall const &) = delete; + MethodCall(MethodCall const &) = delete; + MethodCall &operator=(MethodCall const &) = delete; // The name of the method being called. const std::string &method_name() const { return method_name_; } - // The arguments to the method call, or NULL if there are none. The type of - // the object being pointed to is determined by the concrete subclasses. - virtual const void *arguments() const = 0; + // The arguments to the method call, or NULL if there are none. + const T *arguments() const { return arguments_.get(); } private: std::string method_name_; + std::unique_ptr arguments_; }; } // namespace flutter_desktop_embedding -#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_METHOD_CALL_H_ +#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_TYPED_METHOD_CALL_H_ diff --git a/library/include/flutter_desktop_embedding/method_channel.h b/library/include/flutter_desktop_embedding/method_channel.h index 22b5cf4cd..6016e35d9 100644 --- a/library/include/flutter_desktop_embedding/method_channel.h +++ b/library/include/flutter_desktop_embedding/method_channel.h @@ -14,9 +14,12 @@ #ifndef LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_METHOD_CHANNEL_H_ #define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_METHOD_CHANNEL_H_ +#include #include #include "binary_messenger.h" +#include "engine_method_result.h" +#include "fde_export.h" #include "method_call.h" #include "method_codec.h" #include "method_result.h" @@ -26,45 +29,69 @@ namespace flutter_desktop_embedding { // A handler for receiving a method call from the Flutter engine. // // Implementations must asynchronously call exactly one of the methods on -// |result| to indicate the resust of the method call. -typedef std::function result)> - MethodCallHandler; +// |result| to indicate the result of the method call. +template +using MethodCallHandler = std::function &call, std::unique_ptr> result)>; // A channel for communicating with the Flutter engine using invocation of // asynchronous methods. -class MethodChannel { +template +class FDE_EXPORT MethodChannel { public: // Creates an instance that sends and receives method calls on the channel // named |name|, encoded with |codec| and dispatched via |messenger|. // // TODO: Make codec optional once the standard codec is supported (Issue #67). MethodChannel(BinaryMessenger *messenger, const std::string &name, - const MethodCodec *codec); - ~MethodChannel(); + const MethodCodec *codec) + : messenger_(messenger), name_(name), codec_(codec) {} + ~MethodChannel() {} // Prevent copying. MethodChannel(MethodChannel const &) = delete; MethodChannel &operator=(MethodChannel const &) = delete; - // Sends |method_call| to the Flutter engine on this channel. - // - // TODO: Implement InovkeMethod and remove this. This is a temporary - // implementation, since supporting InvokeMethod involves significant changes - // to other classes. - void InvokeMethodCall(const MethodCall &method_call) const; + // Sends a message to the Flutter engine on this channel. + void InvokeMethod(const std::string &method, std::unique_ptr arguments) { + MethodCall method_call(method, std::move(arguments)); + std::unique_ptr> message = + codec_->EncodeMethodCall(method_call); + messenger_->Send(name_, message->data(), message->size()); + } - // TODO: Add support for a version expecting a reply once + // TODO: Add support for a version of InvokeMethod expecting a reply once // https://github.com/flutter/flutter/issues/18852 is fixed. // Registers a handler that should be called any time a method call is // received on this channel. - void SetMethodCallHandler(MethodCallHandler handler) const; + void SetMethodCallHandler(MethodCallHandler handler) const { + const auto *codec = codec_; + std::string channel_name = name_; + BinaryMessageHandler binary_handler = [handler, codec, channel_name]( + const uint8_t *message, + const size_t message_size, + BinaryReply reply) { + // Use this channel's codec to decode the call and build a result handler. + auto result = + std::make_unique>(std::move(reply), codec); + std::unique_ptr> method_call = + codec->DecodeMethodCall(message, message_size); + if (!method_call) { + std::cerr << "Unable to construct method call from message on channel " + << channel_name << std::endl; + result->NotImplemented(); + return; + } + handler(*method_call, std::move(result)); + }; + messenger_->SetMessageHandler(name_, std::move(binary_handler)); + } private: BinaryMessenger *messenger_; std::string name_; - const MethodCodec *codec_; + const MethodCodec *codec_; }; } // namespace flutter_desktop_embedding diff --git a/library/include/flutter_desktop_embedding/method_codec.h b/library/include/flutter_desktop_embedding/method_codec.h index fc34f4d82..cc8ed2c87 100644 --- a/library/include/flutter_desktop_embedding/method_codec.h +++ b/library/include/flutter_desktop_embedding/method_codec.h @@ -18,49 +18,65 @@ #include #include +#include "fde_export.h" #include "method_call.h" namespace flutter_desktop_embedding { // Translates between a binary message and higher-level method call and // response/error objects. -class MethodCodec { +template +class FDE_EXPORT MethodCodec { public: - virtual ~MethodCodec(); + MethodCodec() = default; + virtual ~MethodCodec() = default; + + // Prevent copying. + MethodCodec(MethodCodec const &) = delete; + MethodCodec &operator=(MethodCodec const &) = delete; // Returns the MethodCall encoded in |message|, or nullptr if it cannot be // decoded. // TODO: Consider adding absl as a dependency and using absl::Span. - std::unique_ptr DecodeMethodCall(const uint8_t *message, - const size_t message_size) const; + std::unique_ptr> DecodeMethodCall( + const uint8_t *message, const size_t message_size) const { + return std::move(DecodeMethodCallInternal(message, message_size)); + } // Returns a binary encoding of the given |method_call|, or nullptr if the // method call cannot be serialized by this codec. std::unique_ptr> EncodeMethodCall( - const MethodCall &method_call) const; + const MethodCall &method_call) const { + return std::move(EncodeMethodCallInternal(method_call)); + } // Returns a binary encoding of |result|. |result| must be a type supported // by the codec. std::unique_ptr> EncodeSuccessEnvelope( - const void *result = nullptr) const; + const T *result = nullptr) const { + return std::move(EncodeSuccessEnvelopeInternal(result)); + } // Returns a binary encoding of |error|. The |error_details| must be a type // supported by the codec. std::unique_ptr> EncodeErrorEnvelope( const std::string &error_code, const std::string &error_message = "", - const void *error_details = nullptr) const; + const T *error_details = nullptr) const { + return std::move( + EncodeErrorEnvelopeInternal(error_code, error_message, error_details)); + } protected: // Implementations of the public interface, to be provided by subclasses. - virtual std::unique_ptr DecodeMethodCallInternal( + virtual std::unique_ptr> DecodeMethodCallInternal( const uint8_t *message, const size_t message_size) const = 0; virtual std::unique_ptr> EncodeMethodCallInternal( - const MethodCall &method_call) const = 0; + const MethodCall &method_call) const = 0; virtual std::unique_ptr> EncodeSuccessEnvelopeInternal( - const void *result) const = 0; + const T *result) const = 0; virtual std::unique_ptr> EncodeErrorEnvelopeInternal( const std::string &error_code, const std::string &error_message, - const void *error_details) const = 0; + const T *error_details) const = 0; }; } // namespace flutter_desktop_embedding diff --git a/library/include/flutter_desktop_embedding/method_result.h b/library/include/flutter_desktop_embedding/method_result.h index 205ca1f51..f7a8a5c57 100644 --- a/library/include/flutter_desktop_embedding/method_result.h +++ b/library/include/flutter_desktop_embedding/method_result.h @@ -16,44 +16,47 @@ #include +#include "fde_export.h" + namespace flutter_desktop_embedding { // Encapsulates a result sent back to the Flutter engine in response to a // MethodCall. Only one method should be called on any given instance. -class MethodResult { +template +class FDE_EXPORT MethodResult { public: - MethodResult(); - virtual ~MethodResult(); + MethodResult() = default; + virtual ~MethodResult() = default; // Prevent copying. MethodResult(MethodResult const &) = delete; MethodResult &operator=(MethodResult const &) = delete; // Sends a success response, indicating that the call completed successfully. - // An optional value can be provided as part of the success message. The value - // must be a type handled by the channel's codec. - void Success(const void *result = nullptr); + // An optional value can be provided as part of the success message. + void Success(const T *result = nullptr) { SuccessInternal(result); } // Sends an error response, indicating that the call was understood but // handling failed in some way. A string error code must be provided, and in // addition an optional user-readable error_message and/or details object can - // be included. The details, if provided, must be a type handled by the - // channel's codec. + // be included. void Error(const std::string &error_code, const std::string &error_message = "", - const void *error_details = nullptr); + const T *error_details = nullptr) { + ErrorInternal(error_code, error_message, error_details); + } // Sends a not-implemented response, indicating that the method either was not // recognized, or has not been implemented. - void NotImplemented(); + void NotImplemented() { NotImplementedInternal(); } protected: // Internal implementation of the interface methods above, to be implemented // in subclasses. - virtual void SuccessInternal(const void *result) = 0; + virtual void SuccessInternal(const T *result) = 0; virtual void ErrorInternal(const std::string &error_code, const std::string &error_message, - const void *error_details) = 0; + const T *error_details) = 0; virtual void NotImplementedInternal() = 0; }; diff --git a/library/include/flutter_desktop_embedding/plugin.h b/library/include/flutter_desktop_embedding/plugin.h deleted file mode 100644 index c69483739..000000000 --- a/library/include/flutter_desktop_embedding/plugin.h +++ /dev/null @@ -1,87 +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_PLUGIN_H_ -#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_PLUGIN_H_ - -#include -#include -#include - -#include "binary_messenger.h" -#include "method_call.h" -#include "method_codec.h" -#include "method_result.h" - -namespace flutter_desktop_embedding { - -// Represents a plugin that can be registered with the Flutter Embedder. -// -// A plugin listens on a platform channel and processes requests that come -// in on said channel. See https://flutter.io/platform-channels/ for more -// details on what these channels look like. -class Plugin { - public: - // Constructs a plugin for a given channel. - // - // |input_blocking| Determines whether user input should be blocked during the - // duration of this plugin's platform callback handler (in most cases this - // can be set to false). - explicit Plugin(const std::string &channel, bool input_blocking = false); - virtual ~Plugin(); - - // Returns the codec to use for this plugin. - virtual const MethodCodec &GetCodec() const = 0; - - // Handles a method call from Flutter on this platform's channel. - // - // Implementations must call exactly one of the methods on |result|, - // exactly once. Failure to indicate a |result| is a memory leak. - virtual void HandleMethodCall(const MethodCall &method_call, - std::unique_ptr result) = 0; - - // Returns the channel on which this plugin listens. - virtual std::string channel() const { return channel_; } - - // Determines whether this plugin blocks on input while it is running. - // - // If this is true, then the parent window should disable input callbacks - // while waiting for this plugin to handle its platform message. - virtual bool input_blocking() const { return input_blocking_; } - - // Binds this plugin to the given caller-owned binary messenger. It must - // remain valid for the life of the plugin. - // - // The embedder typically sets this pointer rather than the client. - void SetBinaryMessenger(BinaryMessenger *messenger); - - protected: - // Implementers should register any MethodChannels that should receive - // messages from Flutter with |messenger| when this is called. - virtual void RegisterMethodChannels(BinaryMessenger *messenger) = 0; - - // Calls a method in the Flutter engine on this Plugin's channel. - // - // Deprecated. Use MethodChannel's InvokeMethodCall instead. - void InvokeMethodCall(const MethodCall &method_call); - - private: - std::string channel_; - // Caller-owned instance of the binary messenger used to message the engine. - const BinaryMessenger *messenger_; - bool input_blocking_; -}; - -} // namespace flutter_desktop_embedding - -#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_PLUGIN_H_ diff --git a/library/include/flutter_desktop_embedding/plugin_registrar.h b/library/include/flutter_desktop_embedding/plugin_registrar.h new file mode 100644 index 000000000..c64d144f0 --- /dev/null +++ b/library/include/flutter_desktop_embedding/plugin_registrar.h @@ -0,0 +1,64 @@ +// 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_PLUGIN_REGISTRAR_H_ +#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_PLUGIN_REGISTRAR_H_ + +#include + +#include "binary_messenger.h" +#include "fde_export.h" + +namespace flutter_desktop_embedding { + +class Plugin; + +// A object managing the registration of a plugin for various events. +// +// Currently this class has very limited functionality, but is expected to +// expand over time to more closely match the functionality of +// the Flutter mobile plugin APIs' plugin registrars. +class FDE_EXPORT PluginRegistrar { + public: + virtual ~PluginRegistrar() {} + + // Returns the messenger to use for creating channels to communicate with the + // Flutter engine. + // + // This pointer must remain valid for the life of the plugin that this + // registrar is used for. + virtual BinaryMessenger *messenger() = 0; + + // Takes ownership of |plugin|. + // + // Plugins are not required to call this method if they have other lifetime + // management, but this is a convient place for plugins to be owned to ensure + // that they stay valid for any registered callbacks. + virtual void AddPlugin(std::unique_ptr plugin) = 0; + + // Enables input blocking on the given channel name. + // + // If set, then the parent window should disable input callbacks + // while waiting for the handler for messages on that channel to run. + virtual void EnableInputBlockingForChannel(const std::string &channel) = 0; +}; + +// A plugin that can be registered for ownership by a PluginRegistrar. +class FDE_EXPORT Plugin { + public: + virtual ~Plugin() {} +}; + +} // namespace flutter_desktop_embedding + +#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_PLUGIN_REGISTRAR_H_ diff --git a/library/linux/BUILD.gn b/library/linux/BUILD.gn index 6b049638c..ee88df121 100644 --- a/library/linux/BUILD.gn +++ b/library/linux/BUILD.gn @@ -15,16 +15,8 @@ import("//build/packaging.gni") import("//library/engine.gni") -# Allows targets depending on this library to use library-style -# inculdes for its header rather than project-relative. -config("relative_public_headers") { - include_dirs = [ - "../include" - ] -} - copy_includes("_publish_engine_headers") { - sources = [ engine_files[1] ] + sources = [ engine_files[0] ] deps = [ "//library:fetch_flutter_engine" ] } @@ -32,7 +24,7 @@ copy_includes("_publish_engine_headers") { # output directory where built libraries go, so that it doesn't require # special link handling, and publishes its header. copy("publish_flutter_engine") { - sources = [ engine_files[0] ] + sources = [ engine_files[1] ] outputs = [ "$root_out_dir/{{source_file_part}}" ] deps = [ ":_publish_engine_headers", diff --git a/library/linux/Makefile b/library/linux/Makefile index 6a5810892..df7b62ebe 100644 --- a/library/linux/Makefile +++ b/library/linux/Makefile @@ -22,10 +22,11 @@ ENGINE_UPDATER=$(TOOLS_DIR)/update_flutter_engine FLUTTER_DIR=$(shell "$(TOOLS_DIR)/flutter_location") LIBRARY_OUT=libflutter_embedder.so CXX=g++ -std=c++14 -CXXFLAGS= -Wall -Werror -shared -fPIC \ +CXXFLAGS= -Wall -Werror -shared -fPIC -fvisibility=hidden \ -I$(PROJECT_ROOT) \ -I$(PROJECT_ROOT)/library/include \ -I$(FLUTTER_ENGINE_HEADER_DIR) \ + -DFLUTTER_DESKTOP_EMBEDDING_IMPL \ $(shell pkg-config --cflags gtk+-3.0 epoxy x11 jsoncpp) LDFLAGS= -L$(CURDIR) \ $(shell pkg-config --libs gtk+-3.0 epoxy x11 jsoncpp) \ @@ -51,7 +52,7 @@ $(LIBRARY_OUT): $(SOURCES) $(HEADERS) $(LIBRARIES) # avoiding unnecessary relinks. $(FLUTTER_ENGINE_LIB_FILE) $(FLUTTER_ENGINE_HEADER_DIR)/$(FLUTTER_ENGINE_HEADER): \ $(FLUTTER_DIR)/bin/internal/engine.version - $(ENGINE_UPDATER) --flutter_root=$(FLUTTER_DIR) ./ + $(ENGINE_UPDATER) ./ if [ -f $(FLUTTER_ENGINE_HEADER) ]; then \ mkdir -p $(FLUTTER_ENGINE_HEADER_DIR) && \ mv $(FLUTTER_ENGINE_HEADER) $(FLUTTER_ENGINE_HEADER_DIR)/; \ diff --git a/library/macos/FLEEventChannel.h b/library/macos/FLEEventChannel.h new file mode 100644 index 000000000..7dac85821 --- /dev/null +++ b/library/macos/FLEEventChannel.h @@ -0,0 +1,107 @@ +// 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. + +#import + +#import "FLEBinaryMessenger.h" +#import "FLEMethodCodec.h" + +/** + * An event sink callback. + * + * @param event The event. + */ +typedef void (^FLEEventSink)(id _Nullable event); + +/** + * A constant used with `FLEEventChannel` to indicate end of stream. + */ +extern NSString const *_Nonnull FLEEndOfEventStream; + +/** + * A strategy for exposing an event stream to the Flutter side. + */ +@protocol FLEStreamHandler + +/** + * Sets up an event stream and begins emitting events. + * + * Invoked when the first listener is registered with the Stream associated to + * this channel on the Flutter side. + * + * @param arguments Arguments for the stream. + * @param events A callback to asynchronously emit events. Invoke the + * callback with a `FLEMethodError` to emit an error event. Invoke the + * callback with `FLEEndOfEventStream` to indicate that no more + * events will be emitted. Any other value, including `nil` are emitted as + * successful events. + * @return A FLEMethodError instance, if setup fails. + */ +- (nullable FLEMethodError *)onListenWithArguments:(nullable id)arguments + eventSink:(FLEEventSink _Nonnull)events; + +/** + * Tears down an event stream. + * + * Invoked when the last listener is deregistered from the Stream associated to + * this channel on the Flutter side. + * + * The channel implementation may call this method with `nil` arguments + * to separate a pair of two consecutive set up requests. Such request pairs + * may occur during Flutter hot restart. + * + * @param arguments Arguments for the stream. + * @return A FLEMethodError instance, if teardown fails. + */ +- (nullable FLEMethodError *)onCancelWithArguments:(nullable id)arguments; + +@end + +/** + * A channel for communicating with the Flutter side using event streams. + */ +@interface FLEEventChannel : NSObject + +/** + * Creates a `FLEEventChannel` with the specified name and binary messenger. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. + * + * The channel uses `FLEMethodCodec` to decode stream setup and + * teardown requests, and to encode event envelopes. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param codec The method codec. + * @return A new channel. + */ ++ (nonnull instancetype)eventChannelWithName:(nonnull NSString *)name + binaryMessenger:(nonnull id)messenger + codec:(nonnull id)codec; + +/** + * Registers a handler for stream setup requests from the Flutter side. + * + * Replaces any existing handler. Use a `nil` handler for unregistering the + * existing handler. + * + * @param handler The stream handler. + */ +- (void)setStreamHandler:(nullable id)handler; + +@end diff --git a/library/macos/FLEEventChannel.m b/library/macos/FLEEventChannel.m new file mode 100644 index 000000000..931e4929e --- /dev/null +++ b/library/macos/FLEEventChannel.m @@ -0,0 +1,102 @@ +// 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. + +#import "FLEEventChannel.h" + +NSString const *FLEEndOfEventStream = @"EndOfEventStream"; + +@implementation FLEEventChannel { + NSString *_name; + __weak id _messenger; + id _codec; +} + ++ (instancetype)eventChannelWithName:(NSString *)name + binaryMessenger:(id)messenger + codec:(id)codec { + return [[[self class] alloc] initWithName:name binaryMessenger:messenger codec:codec]; +} + +- (instancetype)initWithName:(NSString *)name + binaryMessenger:(id)messenger + codec:(id)codec { + self = [super init]; + if (self) { + _name = [name copy]; + _messenger = messenger; + _codec = codec; + } + return self; +} + +- (void)setStreamHandler:(NSObject *)handler { + if (!handler) { + [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil]; + return; + } + + id codec = _codec; + __weak id messenger = _messenger; + NSString *name = _name; + FLEEventSink eventSink = ^(id event) { + if (event == FLEEndOfEventStream) { + [messenger sendOnChannel:name message:nil]; + } else if ([event isKindOfClass:[FLEMethodError class]]) { + [messenger sendOnChannel:name message:[codec encodeErrorEnvelope:(FLEMethodError *)event]]; + } else { + [messenger sendOnChannel:name message:[codec encodeSuccessEnvelope:event]]; + } + }; + + __block FLEEventSink currentSink = nil; + FLEBinaryMessageHandler messageHandler = ^(NSData *message, FLEBinaryReply callback) { + FLEMethodCall *call = [codec decodeMethodCall:message]; + if ([call.methodName isEqual:@"listen"]) { + if (currentSink) { + FLEMethodError *error = [handler onCancelWithArguments:nil]; + if (error) { + NSLog(@"Failed to cancel existing stream: %@. %@ (%@)", error.code, error.message, + error.details); + } + } + currentSink = eventSink; + FLEMethodError *error = [handler onListenWithArguments:call.arguments eventSink:currentSink]; + if (error) { + callback([codec encodeErrorEnvelope:error]); + } else { + callback([codec encodeSuccessEnvelope:nil]); + } + } else if ([call.methodName isEqual:@"cancel"]) { + if (!currentSink) { + callback([codec + encodeErrorEnvelope:[[FLEMethodError alloc] initWithCode:@"error" + message:@"No active stream to cancel" + details:nil]]); + return; + } + currentSink = nil; + FLEMethodError *error = [handler onCancelWithArguments:call.arguments]; + if (error) { + callback([codec encodeErrorEnvelope:error]); + } else { + callback([codec encodeSuccessEnvelope:nil]); + } + } else { + callback(nil); + } + }; + [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler]; +} + +@end diff --git a/library/macos/FLEKeyEventPlugin.h b/library/macos/FLEKeyEventPlugin.h deleted file mode 100644 index 3c5ce6523..000000000 --- a/library/macos/FLEKeyEventPlugin.h +++ /dev/null @@ -1,26 +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. - -#import - -#import "FLEPlugin.h" - -/** - * A FlutterPlugin to handle raw keyboard events. Owned by the FlutterViewController. - * Responsible for bridging the native macOS keyboard event system with the - * Flutter framework raw keyboard event classes, via system channels. - */ -@interface FLEKeyEventPlugin : NSResponder - -@end diff --git a/library/macos/FLEKeyEventPlugin.m b/library/macos/FLEKeyEventPlugin.m deleted file mode 100644 index 549522233..000000000 --- a/library/macos/FLEKeyEventPlugin.m +++ /dev/null @@ -1,61 +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. - -#import "FLEKeyEventPlugin.h" - -#import "FLEViewController+Internal.h" -#import "FLEViewController.h" - -static NSString *const kKeyEventChannel = @"flutter/keyevent"; - -@implementation FLEKeyEventPlugin - -@synthesize controller = _controller; - -- (NSString *)channel { - return kKeyEventChannel; -} - -- (void)handleMethodCall:(FLEMethodCall *)call result:(FLEMethodResult)result { - result(FLEMethodNotImplemented); -} - -/** - * Sends a key event to flutter. - * @param event NSEvent received from the system. - * @param type Type of this key event. Can be either "keydown" or "keyup". - */ -- (void)dispatchKeyEvent:(NSEvent *)event ofType:(NSString *)type { - // TODO: Do not use 'android'. Reconsider when - // https://github.com/google/flutter-desktop-embedding/issues/47 is solved. - NSDictionary *message = @{ - @"keymap" : @"android", - @"type" : type, - @"keyCode" : @(event.keyCode), - }; - - [self.controller dispatchMessage:message onChannel:kKeyEventChannel]; -} - -#pragma mark - NSResponder - -- (void)keyDown:(NSEvent *)event { - [self dispatchKeyEvent:event ofType:@"keydown"]; -} - -- (void)keyUp:(NSEvent *)event { - [self dispatchKeyEvent:event ofType:@"keyup"]; -} - -@end diff --git a/library/macos/FLEPlugin.h b/library/macos/FLEPlugin.h index cad50efa4..f8264ab6b 100644 --- a/library/macos/FLEPlugin.h +++ b/library/macos/FLEPlugin.h @@ -18,52 +18,38 @@ #import "FLEMethodChannel.h" #import "FLEMethodCodec.h" -@class FLEViewController; +@protocol FLEPluginRegistrar; /** - * A plugin is an object that can respond appropriately to Flutter platform messages over a specific - * named system channel. See https://flutter.io/platform-channels/. + * Implemented by the platform side of a Flutter plugin. * - * Note: This interface will be changing in the future to more closely match the iOS Flutter - * platform message API. It will be a one-time breaking change. + * Defines a set of optional callback methods and a method to set up the plugin + * and register it to be called by other application components. + * + * Currently FLEPlugin has very limited functionality, but is expected to expand over time to + * more closely match the functionality of FlutterPlugin. */ @protocol FLEPlugin /** - * A weak reference to the owning controller. May be used to send messages to the Flutter - * framework. + * Creates an instance of the plugin to register with |registrar| using the desired + * FLEPluginRegistrar methods. */ -@property(nullable, weak) FLEViewController *controller; ++ (void)registerWithRegistrar:(nonnull id)registrar; -/** - * The name of the system channel via which this plugin communicates. - */ -@property(nonnull, readonly) NSString *channel; +@optional /** - * Called when a message is sent from Flutter on this plugin's channel. - * The result callback must be called exactly once, with one of: + * Called when a message is sent from Flutter on a channel that a plugin instance has subscribed + * to via -[FLEPluginRegistrar addMethodCallDelegate:channel:]. + * + * The |result| callback must be called exactly once, with one of: * - FLEMethodNotImplemented, if the method call is unknown. * - An FLEMethodError, if the method call was understood but there was a * problem handling it. * - Any other value (including nil) to indicate success. The value will * be returned to the Flutter caller, and must be serializable to JSON. - * - * If handling the method involves multiple responses to Flutter, follow-up - * messages can be sent by calling the other direction using - * -[FLEViewController invokeMethod:arguments:onChannel:]. */ - (void)handleMethodCall:(nonnull FLEMethodCall *)call result:(nonnull FLEMethodResult)result; -@optional - -/** - * If implemented, returns the codec to use for method calls to this plugin. - * - * If not implemented, the codec is assumed to be FLEJSONMethodCodec. Note that this is different - * from existing Flutter platforms, which default to the standard codec; this is to preserve - * backwards compatibility for FLEPlugin until the breaking change for the platform messages API. - */ -@property(nonnull, readonly) id codec; - @end diff --git a/library/macos/FLEPluginRegistrar.h b/library/macos/FLEPluginRegistrar.h new file mode 100644 index 000000000..20401863e --- /dev/null +++ b/library/macos/FLEPluginRegistrar.h @@ -0,0 +1,48 @@ +// 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. + +#import + +#import "FLEBinaryMessenger.h" +#import "FLEMethodChannel.h" + +/** + * The protocol for an object managing registration for a plugin. It provides access to application + * context, as as allowing registering for callbacks for handling various conditions. + * + * Currently FLEPluginRegistrar has very limited functionality, but is expected to expand over time + * to more closely match the functionality of FlutterPluginRegistrar. + */ +@protocol FLEPluginRegistrar + +/** + * The binary messenger used for creating channels to communicate with the Flutter engine. + */ +@property(nonnull, readonly) id messenger; + +/** + * The view displaying Flutter content. + * + * WARNING: If/when multiple Flutter views within the same application are supported (#98), this + * API will change. + */ +@property(nullable, readonly) NSView *view; + +/** + * Registers |delegate| to receive handleMethodCall:result: callbacks for the given |channel|. + */ +- (void)addMethodCallDelegate:(nonnull id)delegate + channel:(nonnull FLEMethodChannel *)channel; + +@end diff --git a/library/macos/FLETextInputPlugin.h b/library/macos/FLETextInputPlugin.h index 913d6e0f1..f173a7cdc 100644 --- a/library/macos/FLETextInputPlugin.h +++ b/library/macos/FLETextInputPlugin.h @@ -14,13 +14,23 @@ #import -#import "FLEPlugin.h" +#import "FLEBinaryMessenger.h" +#import "FLEViewController.h" /** - * A FlutterPlugin to handle text input. Owned by the FlutterViewController. - * Responsible for bridging the native macOS text input system with the - * Flutter framework text editing classes, via system channels. + * A plugin to handle text input. + * + * Responsible for bridging the native macOS text input system with the Flutter framework text + * editing classes, via system channels. + * + * This is not an FLEPlugin since it needs access to FLEViewController internals, so needs to be + * managed differently. */ -@interface FLETextInputPlugin : NSResponder +@interface FLETextInputPlugin : NSResponder + +/** + * Initializes a text input plugin that coordinates key event handling with |viewController|. + */ +- (instancetype)initWithViewController:(FLEViewController*)viewController; @end diff --git a/library/macos/FLETextInputPlugin.m b/library/macos/FLETextInputPlugin.m index 6f8daba2f..a8fdd2816 100644 --- a/library/macos/FLETextInputPlugin.m +++ b/library/macos/FLETextInputPlugin.m @@ -16,6 +16,7 @@ #import +#import "FLEJSONMethodCodec.h" #import "FLETextInputModel.h" #import "FLEViewController+Internal.h" @@ -33,7 +34,6 @@ static NSString *const kNewLine = @"TextInputAction.newline"; static NSString *const kDone = @"TextInputAction.done"; - /** * Private properties of FlutterTextInputPlugin. */ @@ -42,42 +42,61 @@ @interface FLETextInputPlugin () /** * A text input context, representing a connection to the Cocoa text input system. */ -@property NSTextInputContext *textInputContext; +@property(nonatomic) NSTextInputContext *textInputContext; /** * A dictionary of text input models, one per client connection, keyed * by the client connection ID. */ -@property NSMutableDictionary *textInputModels; +@property(nonatomic) NSMutableDictionary *textInputModels; /** * The currently active client connection ID. */ -@property(nullable) NSNumber *activeClientID; +@property(nonatomic, nullable) NSNumber *activeClientID; /** * The currently active text input model. */ -@property(readonly, nullable) FLETextInputModel *activeModel; +@property(nonatomic, readonly, nullable) FLETextInputModel *activeModel; + +/** + * The channel used to communicate with Flutter. + */ +@property(nonatomic) FLEMethodChannel *channel; + +/** + * The FLEViewController to manage input for. + */ +@property(nonatomic, weak) FLEViewController *flutterViewController; + +/** + * Handles a Flutter system message on the text input channel. + */ +- (void)handleMethodCall:(FLEMethodCall *)call result:(FLEMethodResult)result; @end @implementation FLETextInputPlugin -@synthesize controller = _controller; - -- (instancetype)init { +- (instancetype)initWithViewController:(FLEViewController *)viewController { self = [super init]; if (self != nil) { + _flutterViewController = viewController; + _channel = [FLEMethodChannel methodChannelWithName:kTextInputChannel + binaryMessenger:viewController + codec:[FLEJSONMethodCodec sharedInstance]]; + __weak FLETextInputPlugin *weakSelf = self; + [_channel setMethodCallHandler:^(FLEMethodCall *call, FLEMethodResult result) { + [weakSelf handleMethodCall:call result:result]; + }]; _textInputModels = [[NSMutableDictionary alloc] init]; _textInputContext = [[NSTextInputContext alloc] initWithClient:self]; } return self; } -- (NSString *)channel { - return kTextInputChannel; -} +#pragma mark - Private - (FLETextInputModel *)activeModel { return (_activeClientID == nil) ? nil : _textInputModels[_activeClientID]; @@ -95,10 +114,10 @@ - (void)handleMethodCall:(FLEMethodCall *)call result:(FLEMethodResult)result { _textInputModels[_activeClientID] = [[FLETextInputModel alloc] init]; } } else if ([method isEqualToString:kShowMethod]) { - [self.controller addKeyResponder:self]; + [self.flutterViewController addKeyResponder:self]; [_textInputContext activate]; } else if ([method isEqualToString:kHideMethod]) { - [self.controller removeKeyResponder:self]; + [self.flutterViewController removeKeyResponder:self]; [_textInputContext deactivate]; } else if ([method isEqualToString:kClearClientMethod]) { _activeClientID = nil; @@ -120,9 +139,8 @@ - (void)updateEditState { return; } - [_controller invokeMethod:kUpdateEditStateResponseMethod - arguments:@[ _activeClientID, _textInputModels[_activeClientID].state ] - onChannel:kTextInputChannel]; + [_channel invokeMethod:kUpdateEditStateResponseMethod + arguments:@[ _activeClientID, _textInputModels[_activeClientID].state ]]; } #pragma mark - @@ -226,9 +244,7 @@ - (void)insertNewline:(id)sender { // There is a PR in Flutter to identify if the widget is multiline, and act accordingly // (https://github.com/flutter/flutter/pull/23015). Once merged, this action should be changed // to kNewLine. - [_controller invokeMethod:kPerformAction - arguments:@[ _activeClientID, kDone ] - onChannel:kTextInputChannel]; + [_channel invokeMethod:kPerformAction arguments:@[ _activeClientID, kDone ]]; } - (void)setMarkedText:(id)string diff --git a/library/macos/FLEViewController+Internal.h b/library/macos/FLEViewController+Internal.h index 107932912..7336e7c47 100644 --- a/library/macos/FLEViewController+Internal.h +++ b/library/macos/FLEViewController+Internal.h @@ -27,10 +27,4 @@ */ - (void)removeKeyResponder:(nonnull NSResponder *)responder; -/** - * Sends a platform message to the Flutter engine on the given channel. - * @param message The raw message. - */ -- (void)dispatchMessage:(nonnull NSDictionary *)message onChannel:(nonnull NSString *)channel; - @end diff --git a/library/macos/FLEViewController.h b/library/macos/FLEViewController.h index 51b6ac12e..6fb980dba 100644 --- a/library/macos/FLEViewController.h +++ b/library/macos/FLEViewController.h @@ -14,9 +14,11 @@ #import +#import "FLEBinaryMessenger.h" #import "FLEMethodCodec.h" #import "FLEOpenGLContextHandling.h" #import "FLEPlugin.h" +#import "FLEPluginRegistrar.h" #import "FLEReshapeListener.h" /** @@ -26,7 +28,8 @@ * Can be launched headless (no managed view), at which point a Dart executable will be run on the * Flutter engine in non-interactive mode, or with a drawable Flutter canvas. */ -@interface FLEViewController : NSViewController +@interface FLEViewController + : NSViewController /** * The view this controller manages when launched in interactive mode (headless set to false). Must @@ -65,30 +68,8 @@ commandLineArguments:(nonnull NSArray *)arguments; /** - * Adds a plugin to the view controller to handle domain-specific system messages. The plugin's - * channel property will determine which system channel the plugin operates on. Only one plugin - * can operate on a given named channel; if two plugins declare the same channel, the second - * add will return NO. + * Returns the FLEPluginRegistrar that should be used to register the plugin with the given name. */ -- (BOOL)addPlugin:(nonnull id)plugin; - -/** - * Sends a platform message to the Flutter engine on |channel|, encoded using |codec|. - * - * // TODO: Move to an API that mirrors the FlutterMethodChannel API. - * // TODO: Support responses. - */ -- (void)invokeMethod:(nonnull NSString *)method - arguments:(nullable id)arguments - onChannel:(nonnull NSString *)channelName - withCodec:(nonnull id)codec; - -/** - * Calls invokeMethod:arguments:onChannel:withCodec: using FLEJSONCodec. See the note in - * FLEPlugin.h for why JSON is the default. - */ -- (void)invokeMethod:(nonnull NSString *)method - arguments:(nullable id)arguments - onChannel:(nonnull NSString *)channelName; +- (nonnull id)registrarForPlugin:(nonnull NSString *)pluginName; @end diff --git a/library/macos/FLEViewController.m b/library/macos/FLEViewController.m index a8964876f..1711f0b7f 100644 --- a/library/macos/FLEViewController.m +++ b/library/macos/FLEViewController.m @@ -16,7 +16,9 @@ #import "FLEViewController+Internal.h" #import -#import "FLEKeyEventPlugin.h" + +#import "FLEBasicMessageChannel.h" +#import "FLEJSONMessageCodec.h" #import "FLEReshapeListener.h" #import "FLETextInputPlugin.h" #import "FLEView.h" @@ -34,12 +36,7 @@ /** * Private interface declaration for FLEViewController. */ -@interface FLEViewController () { - NSOpenGLContext *_resourceContext; - - // A mapping of channel names to the registered handlers for those channels. - NSMutableDictionary *_messageHandlers; -} +@interface FLEViewController () /** * A list of additional responders to keyboard events. Keybord events are forwarded to all of them. @@ -92,6 +89,11 @@ - (void)handlePlatformMessage:(const FlutterPlatformMessage *)message; */ - (void)dispatchMouseEvent:(nonnull NSEvent *)event phase:(FlutterPointerPhase)phase; +/** + * Converts |event| to a key event channel message, and sends it to the engine. + */ +- (void)dispatchKeyEvent:(NSEvent *)event ofType:(NSString *)type; + @end #pragma mark - Static methods provided to engine configuration @@ -157,6 +159,19 @@ static bool OnMakeResourceCurrent(FLEViewController *controller) { @implementation FLEViewController { FlutterEngine _engine; + + // The additional context provided to the Flutter engine for resource loading. + NSOpenGLContext *_resourceContext; + + // A mapping of channel names to the registered handlers for those channels. + NSMutableDictionary *_messageHandlers; + + // The plugin used to handle text input. This is not an FLEPlugin, so must be owned separately. + FLETextInputPlugin *_textInputPlugin; + + // A message channel for passing key events to the Flutter engine. This should be replaced with + // an embedding API; see Issue #47. + FLEBasicMessageChannel *_keyEventChannel; } @dynamic view; @@ -219,53 +234,11 @@ - (BOOL)launchEngineWithMainPath:(NSURL *)main commandLineArguments:arguments]; } -- (BOOL)addPlugin:(id)plugin { - NSString *channelName = plugin.channel; - if (_messageHandlers[channelName] != nil) { - NSLog(@"Warning: channel %@ already has an associated plugin", channelName); - return NO; - } - - plugin.controller = self; - // See the note on the |codec| property of FLEPlugin for the reason this defaults to JSON. - id codec = [FLEJSONMethodCodec sharedInstance]; - if ([plugin respondsToSelector:@selector(codec)]) { - codec = plugin.codec; - } - - // TODO: Move the channel into the plugin implementations, matching Flutter's iOS plugin - // structure. This is is a temporary shim to add FLEMethodChannel without coupling it to the - // breaking API change. - FLEMethodChannel *channel = [FLEMethodChannel methodChannelWithName:channelName - binaryMessenger:self - codec:codec]; - // This will end up calling back to setMessageHandlerOnChannel:binaryMessageHandler: below, which - // retains the handler, so the strong reference to |plugin| here will keep the plugin alive. - [channel setMethodCallHandler:^(FLEMethodCall *call, FLEMethodResult result) { - [plugin handleMethodCall:call result:result]; - }]; - - return YES; -} - -- (void)invokeMethod:(NSString *)method - arguments:(id)arguments - onChannel:(NSString *)channelName - withCodec:(id)codec { - // TODO: Move the channel into the plugin implementations, matching Flutter's iOS plugin - // structure. This is is a temporary shim to add FLEMethodChannel without coupling it to the - // breaking API change. - FLEMethodChannel *channel = [FLEMethodChannel methodChannelWithName:channelName - binaryMessenger:self - codec:codec]; - [channel invokeMethod:method arguments:arguments]; -} - -- (void)invokeMethod:(NSString *)method arguments:(id)arguments onChannel:(NSString *)channel { - [self invokeMethod:method - arguments:arguments - onChannel:channel - withCodec:[FLEJSONMethodCodec sharedInstance]]; +- (id)registrarForPlugin:(NSString *)pluginName { + // Currently, the view controller acts as the registrar for all plugins, so the + // name is ignored. It is part of the API to reduce churn in the future when + // aligning more closely with the Flutter registrar system. + return self; } #pragma mark - Framework-internal methods @@ -278,31 +251,14 @@ - (void)removeKeyResponder:(NSResponder *)responder { [self.additionalKeyResponders removeObject:responder]; } -- (void)dispatchMessage:(NSDictionary *)message onChannel:(NSString *)channel { - if (![NSJSONSerialization isValidJSONObject:message]) { - NSLog(@"Error: Unable to construct a valid JSON object from %@", message); - return; - } - - NSError *error = nil; - NSData *messageData = [NSJSONSerialization dataWithJSONObject:message options:0 error:&error]; - if (!messageData) { - NSLog(@"Error: Failed to create JSON message data for %@: %@", message, error.debugDescription); - return; - } - - [self sendOnChannel:channel message:messageData]; -} - #pragma mark - Private methods - (void)addInternalPlugins { - FLETextInputPlugin *textPlugin = [[FLETextInputPlugin alloc] init]; - [self addPlugin:textPlugin]; - - FLEKeyEventPlugin *keyEventPlugin = [[FLEKeyEventPlugin alloc] init]; - [self addPlugin:keyEventPlugin]; - [_additionalKeyResponders addObject:keyEventPlugin]; + _textInputPlugin = [[FLETextInputPlugin alloc] initWithViewController:self]; + _keyEventChannel = + [FLEBasicMessageChannel messageChannelWithName:@"flutter/keyevent" + binaryMessenger:self + codec:[FLEJSONMessageCodec sharedInstance]]; } - (BOOL)launchEngineInternalWithAssetsPath:(NSURL *)assets @@ -433,6 +389,14 @@ - (void)dispatchMouseEvent:(NSEvent *)event phase:(FlutterPointerPhase)phase { FlutterEngineSendPointerEvent(_engine, &flutterEvent, 1); } +- (void)dispatchKeyEvent:(NSEvent *)event ofType:(NSString *)type { + [_keyEventChannel sendMessage:@{ + @"keymap" : @"android", + @"type" : type, + @"keyCode" : @(event.keyCode), + }]; +} + #pragma mark - FLEReshapeListener /** @@ -470,6 +434,19 @@ - (void)setMessageHandlerOnChannel:(nonnull NSString *)channel _messageHandlers[channel] = [handler copy]; } +#pragma mark - FLEPluginRegistrar + +- (id)messenger { + return self; +} + +- (void)addMethodCallDelegate:(nonnull id)delegate + channel:(nonnull FLEMethodChannel *)channel { + [channel setMethodCallHandler:^(FLEMethodCall *call, FLEMethodResult result) { + [delegate handleMethodCall:call result:result]; + }]; +} + #pragma mark - NSResponder - (BOOL)acceptsFirstResponder { @@ -477,6 +454,7 @@ - (BOOL)acceptsFirstResponder { } - (void)keyDown:(NSEvent *)event { + [self dispatchKeyEvent:event ofType:@"keydown"]; for (NSResponder *responder in self.additionalKeyResponders) { if ([responder respondsToSelector:@selector(keyDown:)]) { [responder keyDown:event]; @@ -485,6 +463,7 @@ - (void)keyDown:(NSEvent *)event { } - (void)keyUp:(NSEvent *)event { + [self dispatchKeyEvent:event ofType:@"keyup"]; for (NSResponder *responder in self.additionalKeyResponders) { if ([responder respondsToSelector:@selector(keyUp:)]) { [responder keyUp:event]; diff --git a/library/macos/FlutterEmbedderMac.h b/library/macos/FlutterEmbedderMac.h index 18ccefa39..8d5ed477b 100644 --- a/library/macos/FlutterEmbedderMac.h +++ b/library/macos/FlutterEmbedderMac.h @@ -14,6 +14,7 @@ #import "FLEBasicMessageChannel.h" #import "FLEBinaryMessenger.h" +#import "FLEEventChannel.h" #import "FLEJSONMessageCodec.h" #import "FLEJSONMethodCodec.h" #import "FLEMessageCodec.h" @@ -23,6 +24,7 @@ #import "FLEMethodError.h" #import "FLEOpenGLContextHandling.h" #import "FLEPlugin.h" +#import "FLEPluginRegistrar.h" #import "FLEReshapeListener.h" #import "FLEView.h" #import "FLEViewController.h" diff --git a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj index 0b730ac53..2d953f6b7 100644 --- a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj +++ b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj @@ -30,6 +30,8 @@ 1E2492411FCF50BE00DD3BBB /* FlutterEmbedderMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E2492371FCF50BE00DD3BBB /* FlutterEmbedderMac.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EEF8E071FD1F0C300DD563C /* FLEView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEF8E051FD1F0C300DD563C /* FLEView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1EEF8E081FD1F0C300DD563C /* FLEView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EEF8E061FD1F0C300DD563C /* FLEView.m */; }; + 2CCADF9A21C74690001B4026 /* FLEEventChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CCADF9821C74690001B4026 /* FLEEventChannel.m */; }; + 2CCADF9B21C74690001B4026 /* FLEEventChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CCADF9921C74690001B4026 /* FLEEventChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3389A6872159359200A27898 /* FLEBinaryMessenger.h in Headers */ = {isa = PBXBuildFile; fileRef = 3389A6862159359200A27898 /* FLEBinaryMessenger.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3389A68D215949CB00A27898 /* FLEMethodError.m in Sources */ = {isa = PBXBuildFile; fileRef = 3389A68C215949CB00A27898 /* FLEMethodError.m */; }; 3389A68F215949D600A27898 /* FLEMethodError.h in Headers */ = {isa = PBXBuildFile; fileRef = 3389A68E215949D600A27898 /* FLEMethodError.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -43,12 +45,11 @@ 33C0FA2321B84810008F8959 /* FLEMessageCodec.h in Headers */ = {isa = PBXBuildFile; fileRef = 33C0FA1F21B84810008F8959 /* FLEMessageCodec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33C0FA2621B84AA4008F8959 /* FLEMethodCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 33C0FA2421B84AA4008F8959 /* FLEMethodCall.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33C0FA2721B84AA4008F8959 /* FLEMethodCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 33C0FA2521B84AA4008F8959 /* FLEMethodCall.m */; }; + 33C0FA2A21B865D2008F8959 /* FLEPluginRegistrar.h in Headers */ = {isa = PBXBuildFile; fileRef = 33C0FA2921B865D2008F8959 /* FLEPluginRegistrar.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33D7B59920A4F54400296EFC /* FLEMethodChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 33D7B59720A4F54400296EFC /* FLEMethodChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33D7B59A20A4F54400296EFC /* FLEMethodChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = 33D7B59820A4F54400296EFC /* FLEMethodChannel.m */; }; 33E202A5212BC0A800337F48 /* FLETextInputPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E2492331FCF50BE00DD3BBB /* FLETextInputPlugin.h */; }; - 33E202A6212BC0ED00337F48 /* FLEKeyEventPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 64C76C2620D7BDFB00B16256 /* FLEKeyEventPlugin.h */; }; 33E202A7212BC0F100337F48 /* FLEViewController+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 6442F82C20EA6C5F00A393AE /* FLEViewController+Internal.h */; }; - 64C76C2820D7BE2E00B16256 /* FLEKeyEventPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 64C76C2720D7BE2E00B16256 /* FLEKeyEventPlugin.m */; }; AA8AE8B81FD948AC00B6FB31 /* FLETextInputModel.h in Headers */ = {isa = PBXBuildFile; fileRef = AA8AE8B71FD948AC00B6FB31 /* FLETextInputModel.h */; }; AA8AE8BA1FD948BA00B6FB31 /* FLETextInputModel.m in Sources */ = {isa = PBXBuildFile; fileRef = AA8AE8B91FD948BA00B6FB31 /* FLETextInputModel.m */; }; C4C1FE441FD74F6400691968 /* FlutterEmbedder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4C1FE431FD74F6400691968 /* FlutterEmbedder.framework */; }; @@ -91,6 +92,8 @@ 1E2492381FCF50BE00DD3BBB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 1EEF8E051FD1F0C300DD563C /* FLEView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEView.h; sourceTree = ""; }; 1EEF8E061FD1F0C300DD563C /* FLEView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEView.m; sourceTree = ""; }; + 2CCADF9821C74690001B4026 /* FLEEventChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEEventChannel.m; sourceTree = ""; }; + 2CCADF9921C74690001B4026 /* FLEEventChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEEventChannel.h; sourceTree = ""; }; 3389A6862159359200A27898 /* FLEBinaryMessenger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEBinaryMessenger.h; sourceTree = ""; }; 3389A68C215949CB00A27898 /* FLEMethodError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEMethodError.m; sourceTree = ""; }; 3389A68E215949D600A27898 /* FLEMethodError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEMethodError.h; sourceTree = ""; }; @@ -105,11 +108,10 @@ 33C0FA1F21B84810008F8959 /* FLEMessageCodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEMessageCodec.h; sourceTree = ""; }; 33C0FA2421B84AA4008F8959 /* FLEMethodCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEMethodCall.h; sourceTree = ""; }; 33C0FA2521B84AA4008F8959 /* FLEMethodCall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEMethodCall.m; sourceTree = ""; }; + 33C0FA2921B865D2008F8959 /* FLEPluginRegistrar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEPluginRegistrar.h; sourceTree = ""; }; 33D7B59720A4F54400296EFC /* FLEMethodChannel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEMethodChannel.h; sourceTree = ""; }; 33D7B59820A4F54400296EFC /* FLEMethodChannel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEMethodChannel.m; sourceTree = ""; }; 6442F82C20EA6C5F00A393AE /* FLEViewController+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FLEViewController+Internal.h"; sourceTree = ""; }; - 64C76C2620D7BDFB00B16256 /* FLEKeyEventPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEKeyEventPlugin.h; sourceTree = ""; }; - 64C76C2720D7BE2E00B16256 /* FLEKeyEventPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEKeyEventPlugin.m; sourceTree = ""; }; AA8AE8B71FD948AC00B6FB31 /* FLETextInputModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLETextInputModel.h; sourceTree = ""; }; AA8AE8B91FD948BA00B6FB31 /* FLETextInputModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLETextInputModel.m; sourceTree = ""; }; C4C1FE431FD74F6400691968 /* FlutterEmbedder.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterEmbedder.framework; path = ../../../flutter_engine_framework/macos/FlutterEmbedder.framework; sourceTree = ""; }; @@ -134,10 +136,9 @@ 1E2492371FCF50BE00DD3BBB /* FlutterEmbedderMac.h */, 1E2492451FCF536200DD3BBB /* Public */, 33C0FA1821B845EF008F8959 /* FLEBasicMessageChannel.m */, + 2CCADF9821C74690001B4026 /* FLEEventChannel.m */, 33C0FA1C21B84810008F8959 /* FLEJSONMessageCodec.m */, 33A87EB520F6BCDB0086D21D /* FLEJSONMethodCodec.m */, - 64C76C2620D7BDFB00B16256 /* FLEKeyEventPlugin.h */, - 64C76C2720D7BE2E00B16256 /* FLEKeyEventPlugin.m */, 33C0FA2521B84AA4008F8959 /* FLEMethodCall.m */, 33D7B59820A4F54400296EFC /* FLEMethodChannel.m */, 3389A68C215949CB00A27898 /* FLEMethodError.m */, @@ -167,6 +168,7 @@ children = ( 33C0FA1921B845F0008F8959 /* FLEBasicMessageChannel.h */, 3389A6862159359200A27898 /* FLEBinaryMessenger.h */, + 2CCADF9921C74690001B4026 /* FLEEventChannel.h */, 33C0FA1E21B84810008F8959 /* FLEJSONMessageCodec.h */, 33C0FA1D21B84810008F8959 /* FLEJSONMethodCodec.h */, 33C0FA1F21B84810008F8959 /* FLEMessageCodec.h */, @@ -176,6 +178,7 @@ 3389A68E215949D600A27898 /* FLEMethodError.h */, 1E24922F1FCF50BE00DD3BBB /* FLEOpenGLContextHandling.h */, 1E2492311FCF50BE00DD3BBB /* FLEPlugin.h */, + 33C0FA2921B865D2008F8959 /* FLEPluginRegistrar.h */, 1E2492321FCF50BE00DD3BBB /* FLEReshapeListener.h */, 1EEF8E051FD1F0C300DD563C /* FLEView.h */, 1E2492351FCF50BE00DD3BBB /* FLEViewController.h */, @@ -198,9 +201,9 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 33E202A6212BC0ED00337F48 /* FLEKeyEventPlugin.h in Headers */, 33C0FA2621B84AA4008F8959 /* FLEMethodCall.h in Headers */, 1E24923B1FCF50BE00DD3BBB /* FLEPlugin.h in Headers */, + 2CCADF9B21C74690001B4026 /* FLEEventChannel.h in Headers */, 3389A6872159359200A27898 /* FLEBinaryMessenger.h in Headers */, 33D7B59920A4F54400296EFC /* FLEMethodChannel.h in Headers */, 1E24923F1FCF50BE00DD3BBB /* FLEViewController.h in Headers */, @@ -217,6 +220,7 @@ 33C0FA2221B84810008F8959 /* FLEJSONMessageCodec.h in Headers */, 33A87EB620F6BCDB0086D21D /* FLEMethodCodec.h in Headers */, 3389A68F215949D600A27898 /* FLEMethodError.h in Headers */, + 33C0FA2A21B865D2008F8959 /* FLEPluginRegistrar.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -302,7 +306,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/bash; - shellScript = "readonly TOOLS_DIR=\"$PROJECT_DIR\"/../../tools\n\"$TOOLS_DIR\"/update_flutter_engine --flutter_root=\"$(\"$TOOLS_DIR\"/flutter_location)\" \"$PROJECT_DIR\"/../../../flutter_engine_framework/macos"; + shellScript = "readonly TOOLS_DIR=\"$PROJECT_DIR\"/../../tools\n\"$TOOLS_DIR\"/update_flutter_engine \"$PROJECT_DIR\"/../../../flutter_engine_framework/macos"; }; /* End PBXShellScriptBuildPhase section */ @@ -314,13 +318,13 @@ 33D7B59A20A4F54400296EFC /* FLEMethodChannel.m in Sources */, 1E2492401FCF50BE00DD3BBB /* FLEViewController.m in Sources */, 3389A68D215949CB00A27898 /* FLEMethodError.m in Sources */, - 64C76C2820D7BE2E00B16256 /* FLEKeyEventPlugin.m in Sources */, 33C0FA2721B84AA4008F8959 /* FLEMethodCall.m in Sources */, 33C0FA2021B84810008F8959 /* FLEJSONMessageCodec.m in Sources */, 33C0FA1A21B845F0008F8959 /* FLEBasicMessageChannel.m in Sources */, 1E24923E1FCF50BE00DD3BBB /* FLETextInputPlugin.m in Sources */, 1EEF8E081FD1F0C300DD563C /* FLEView.m in Sources */, 33A87EB720F6BCDB0086D21D /* FLEJSONMethodCodec.m in Sources */, + 2CCADF9A21C74690001B4026 /* FLEEventChannel.m in Sources */, AA8AE8BA1FD948BA00B6FB31 /* FLETextInputModel.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/library/windows/BUILD.gn b/library/windows/BUILD.gn new file mode 100644 index 000000000..c688977d1 --- /dev/null +++ b/library/windows/BUILD.gn @@ -0,0 +1,85 @@ +# 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. + +import("//build/packaging.gni") +import("//library/engine.gni") +import("//library/windows/glfw.gni") +import("//library/windows/jsoncpp.gni") + +action("fetch_glfw") { + script = "//tools/dart_tools/bin/fetch_glfw.dart" + outputs = glfw_files + args = [ + rebase_path(glfw_download_dir, root_build_dir), + ] + public_configs = [ + "//build/win/config:glfw3", + ] +} + +config("relative_glfw_dependencies") { + include_dirs = [ + glfw_include_dir, + ] + lib_dirs = [ + glfw_download_dir, + ] +} + +# TODO: Replace this with actual GN build rules for jsoncpp. +# This target is a placeholder, and will not actually rebuild jsoncpp if any of its +# source changes. +action("build_jsoncpp") { + script = "//tools/dart_tools/bin/build_jsoncpp.dart" + outputs = [ jsoncpp_files[1] ] + args = [ + rebase_path(jsoncpp_download_dir, root_build_dir), + rebase_path(jsoncpp_lib_dir, root_build_dir), + "--debug", + ] + public_configs = [ + "//build/win/config:jsoncpp", + ] +} + +config("relative_jsoncpp_dependencies") { + include_dirs = [ + jsoncpp_include_dir, + ] + lib_dirs = [ + jsoncpp_lib_dir, + ] +} + +copy_includes("_publish_engine_headers") { + sources = [ engine_files[0] ] + deps = [ "//library:fetch_flutter_engine" ] +} + +# Places the downloaded Flutter engine library at the top level of the +# output directory where built libraries go, so that it doesn't require +# special link handling, and publishes its header. +copy("publish_flutter_engine") { + sources = [ + engine_files[1], + engine_files[2], + engine_files[3], + engine_files[4], + ] + outputs = [ "$root_out_dir/{{source_file_part}}" ] + deps = [ + ":_publish_engine_headers", + "//library:fetch_flutter_engine", + ] +} diff --git a/library/windows/Flutter Windows Embedder.sln b/library/windows/Flutter Windows Embedder.sln index aadfddeb4..f5baade1a 100644 --- a/library/windows/Flutter Windows Embedder.sln +++ b/library/windows/Flutter Windows Embedder.sln @@ -8,19 +8,13 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug Dynamic Library|x64 = Debug Dynamic Library|x64 - Debug Static Library|x64 = Debug Static Library|x64 Release Dynamic Library|x64 = Release Dynamic Library|x64 - Release Static Library|x64 = Release Static Library|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Debug Dynamic Library|x64.ActiveCfg = Debug Dynamic Library|x64 {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Debug Dynamic Library|x64.Build.0 = Debug Dynamic Library|x64 - {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Debug Static Library|x64.ActiveCfg = Debug Static Library|x64 - {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Debug Static Library|x64.Build.0 = Debug Static Library|x64 {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Release Dynamic Library|x64.ActiveCfg = Release Dynamic Library|x64 {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Release Dynamic Library|x64.Build.0 = Release Dynamic Library|x64 - {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Release Static Library|x64.ActiveCfg = Release Static Library|x64 - {90057FD8-9460-43A6-8CDF-3AAC1C4255E5}.Release Static Library|x64.Build.0 = Release Static Library|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -28,4 +22,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9DA7C025-192F-4D21-8553-E795B4B60A1F} EndGlobalSection -EndGlobal +EndGlobal \ No newline at end of file diff --git a/library/windows/GLFW Library.vcxproj b/library/windows/GLFW Library.vcxproj index f1628bc2d..f0dbd4f95 100644 --- a/library/windows/GLFW Library.vcxproj +++ b/library/windows/GLFW Library.vcxproj @@ -1,18 +1,10 @@ - - Debug Static Library - x64 - Debug Dynamic Library x64 - - Release Static Library - x64 - Release Dynamic Library x64 @@ -32,12 +24,6 @@ v141 MultiByte - - StaticLibrary - true - v141 - MultiByte - DynamicLibrary false @@ -45,13 +31,6 @@ true MultiByte - - StaticLibrary - false - v141 - true - MultiByte - @@ -60,50 +39,28 @@ - - - - - - $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ flutter_embedder .dll - $(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)..\include\;$(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\;$(ProjectDir)third_party\jsoncpp\include\;$(ProjectDir)..\..\;$(IncludePath) + $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\JSON\;$(ProjectDir)dependencies\GLFW\;$(LibraryPath) $(ProjectDir)..\..\library\common\internal;$(SourcePath) - - $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ - $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ - flutter_embedder - .lib - $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\GLFW\;$(IncludePath) - $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\GLFW\;$(LibraryPath) - $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ flutter_embedder .dll - $(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)..\include\;$(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\;$(ProjectDir)third_party\jsoncpp\include\;$(ProjectDir)..\..\;$(IncludePath) + $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\JSON\;$(ProjectDir)dependencies\GLFW\;$(LibraryPath) $(ProjectDir)..\..\library\common\internal;$(SourcePath) - - $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ - $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ - flutter_embedder - .lib - $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\GLFW\;$(IncludePath) - $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\GLFW\;$(LibraryPath) - Level3 @@ -111,12 +68,14 @@ true true MultiThreadedDebugDLL + _WINDLL;FLUTTER_DESKTOP_EMBEDDING_IMPL;%(PreprocessorDefinitions) flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies);json_vc71_libmtd.lib - exports.def + + $(OutDir)$(TargetName)$(TargetExt) $(OutDir)$(TargetName).lib $(OutDir)$(TargetName).pdb @@ -126,46 +85,8 @@ - $(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 - - - - - - - - - flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies) - - - xcopy /y /d /q "$(ProjectDir)dependencies\engine\flutter_engine.dll" "$(OutputPath)" - Add flutter_engine.dll to the out dir - - - - - Level3 - Disabled - true - true - - - flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies) - - - exports.def - $(OutDir)$(TargetName)$(TargetExt) - $(OutDir)$(TargetName).dll.lib - $(OutDir)$(TargetName).dll.pdb - - - - - - - $(ProjectDir)scripts\update_flutter_engine && $(ProjectDir)scripts\get_engine_artifacts && $(ProjectDir)scripts\get_GLFW - Get the flutter engine, engine artifacts and GLFW + $(ProjectDir)scripts\update_flutter_engine && $(ProjectDir)scripts\get_engine_artifacts && $(ProjectDir)scripts\get_GLFW && $(ProjectDir)scripts\build_jsonlib --debug + Get the flutter engine, engine artifacts, GLFW and jsoncpp @@ -190,12 +111,14 @@ true true MultiThreadedDLL + _WINDLL;FLUTTER_DESKTOP_EMBEDDING_IMPL;%(PreprocessorDefinitions) true true flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies);json_vc71_libmt.lib - exports.def + + $(OutDir)$(TargetName)$(TargetExt) $(OutDir)$(TargetName).lib $(OutDir)$(TargetName).pdb @@ -206,47 +129,7 @@ $(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 - - - - - - - - - flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies) - - - xcopy /y /d /q "$(ProjectDir)dependencies\engine\flutter_engine.dll" "$(OutputPath)" - Add flutter_engine.dll to the out dir - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - flutter_engine.dll.lib;glfw3.lib;opengl32.lib;%(AdditionalDependencies) - exports.def - $(OutDir)$(TargetName)$(TargetExt) - $(OutDir)$(TargetName).dll.lib - $(OutDir)$(TargetName).dll.pdb - - - - - - - $(ProjectDir)scripts\update_flutter_engine && $(ProjectDir)scripts\get_engine_artifacts && $(ProjectDir)scripts\get_GLFW - Get the flutter engine, engine artifacts and GLFW + Get the flutter engine, engine artifacts, GLFW and jsoncpp @@ -263,29 +146,19 @@ + - - - - - - - - - - - - + - + \ No newline at end of file diff --git a/library/windows/GLFW Library.vcxproj.filters b/library/windows/GLFW Library.vcxproj.filters index 03dd85a33..c0b1920c8 100644 --- a/library/windows/GLFW Library.vcxproj.filters +++ b/library/windows/GLFW Library.vcxproj.filters @@ -1,74 +1,48 @@ - - - - - {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 - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - - - Source Files - - - + + + + + {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 + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + diff --git a/library/windows/dependencies/.gitignore b/library/windows/dependencies/.gitignore index 7f1435b47..eb7a86f0c 100644 --- a/library/windows/dependencies/.gitignore +++ b/library/windows/dependencies/.gitignore @@ -1,6 +1,3 @@ -flutter_embedder.h -flutter_engine.* -.last_engine_version -icudtl.dat -glfw3.* -json +engine +GLFW +JSON diff --git a/library/windows/exports.def b/library/windows/exports.def deleted file mode 100644 index 4ba7cf9d3..000000000 --- a/library/windows/exports.def +++ /dev/null @@ -1,22 +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. - -LIBRARY flutter_embedder - -EXPORTS - FlutterInit - FlutterTerminate - CreateFlutterWindow - CreateFlutterWindowInSnapshotMode - FlutterWindowLoop \ No newline at end of file diff --git a/library/windows/glfw.gni b/library/windows/glfw.gni new file mode 100644 index 000000000..0a4f60f18 --- /dev/null +++ b/library/windows/glfw.gni @@ -0,0 +1,22 @@ +# 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. + +glfw_include_dir = "$root_gen_dir/glfw" +glfw_download_dir = "$glfw_include_dir/GLFW" +glfw_lib_name = "glfw3.lib" + +glfw_files = [ + "$glfw_download_dir/glfw3.h", + "$glfw_download_dir/$glfw_lib_name", +] \ No newline at end of file diff --git a/library/windows/jsoncpp.gni b/library/windows/jsoncpp.gni new file mode 100644 index 000000000..c45d9389c --- /dev/null +++ b/library/windows/jsoncpp.gni @@ -0,0 +1,23 @@ +# 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. + +jsoncpp_download_dir = rebase_path("third_party/jsoncpp") +jsoncpp_include_dir = "$jsoncpp_download_dir/include" +jsoncpp_lib_dir = "$root_gen_dir/JSON" +jsoncpp_lib_name = "json_vc71_libmtd.lib" + +jsoncpp_files = [ + "$jsoncpp_include_dir/json/json.h", + "$jsoncpp_lib_dir/$jsoncpp_lib_name", +] \ No newline at end of file diff --git a/library/windows/scripts/build_jsonlib.bat b/library/windows/scripts/build_jsonlib.bat index 17f663456..81253ddf8 100644 --- a/library/windows/scripts/build_jsonlib.bat +++ b/library/windows/scripts/build_jsonlib.bat @@ -13,93 +13,9 @@ :: 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 +set RUN_DART_TOOL=%~dp0..\..\..\tools\run_dart_tool.bat +set CHECKOUT_DIR=%~dp0..\third_party\jsoncpp +set ARTIFACT_DIR=%~dp0..\dependencies\JSON -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 +call %RUN_DART_TOOL% fetch_jsoncpp %CHECKOUT_DIR% +call %RUN_DART_TOOL% build_jsoncpp %CHECKOUT_DIR% %ARTIFACT_DIR% %* diff --git a/library/windows/scripts/get_GLFW.bat b/library/windows/scripts/get_GLFW.bat index 0ac0f8925..48be07526 100644 --- a/library/windows/scripts/get_GLFW.bat +++ b/library/windows/scripts/get_GLFW.bat @@ -12,26 +12,4 @@ :: See the License for the specific language governing permissions and :: limitations under the License. @echo off - -cd %~dp0..\dependencies\GLFW\ - -:: Check that GLFW isn't already setup. -set EXISTS=true -if not exist glfw3.h set EXISTS=false -if not exist glfw3.lib set EXISTS=false - -if %EXISTS%==false ( - :: Download zip folder with correct TLS version. - PowerShell -NoProfile -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri http://github.com/glfw/glfw/releases/download/3.2.1/glfw-3.2.1.bin.WIN64.zip -OutFile GLFW.zip" - - :: Expand folder. - PowerShell -NoProfile -ExecutionPolicy Bypass -Command "Expand-Archive GLFW.zip -DestinationPath GLFW" - - :: Copy required files into GLFW directory. - COPY GLFW\glfw-3.2.1.bin.WIN64\include\GLFW\glfw3.h >NUL - COPY GLFW\glfw-3.2.1.bin.WIN64\lib-vc2015\glfw3.lib >NUL - - :: Cleanup. - DEL GLFW.zip - RD /S /Q GLFW -) \ No newline at end of file +%~dp0..\..\..\tools\run_dart_tool.bat fetch_glfw %~dp0..\dependencies\GLFW \ No newline at end of file diff --git a/plugins/color_panel/BUILD.gn b/plugins/color_panel/BUILD.gn index 0b6bf3c8e..0926adc48 100644 --- a/plugins/color_panel/BUILD.gn +++ b/plugins/color_panel/BUILD.gn @@ -30,7 +30,10 @@ published_shared_library("color_panel") { ] } - defines = ["USE_FLATTENED_INCLUDES"] + defines = [ + "COLOR_PANEL_PLUGIN_IMPL", + "USE_FLATTENED_INCLUDES", + ] deps = [ "//library:flutter_embedder", diff --git a/plugins/color_panel/lib/color_panel.dart b/plugins/color_panel/lib/color_panel.dart index c22b6e0d2..1692de8ca 100644 --- a/plugins/color_panel/lib/color_panel.dart +++ b/plugins/color_panel/lib/color_panel.dart @@ -40,13 +40,13 @@ typedef ColorPanelCallback = void Function(Color color); /// This class is a singleton, since the OS may not allow multiple color panels /// simultaneously. class ColorPanel { - ColorPanelCallback _callback; - /// Private constructor. ColorPanel._() { _platformChannel.setMethodCallHandler(_wrappedColorPanelCallback); } + ColorPanelCallback _callback; + /// The static instance of the panel. static ColorPanel instance = new ColorPanel._(); diff --git a/plugins/color_panel/linux/Makefile b/plugins/color_panel/linux/Makefile index 4f8e167a8..14a2eb8d4 100644 --- a/plugins/color_panel/linux/Makefile +++ b/plugins/color_panel/linux/Makefile @@ -24,9 +24,10 @@ EMBEDDER_LIB_FILE=$(EMBEDDER_LIBRARY_OUT_DIR)/lib$(EMBEDDER_LIB_NAME).so COMMON_DIR=$(CURDIR)/../common CXX=g++ -std=c++14 -CXXFLAGS= -Wall -Werror -shared -fPIC \ +CXXFLAGS= -Wall -Werror -shared -fPIC -fvisibility=hidden \ -I$(PROJECT_ROOT) \ -I$(EMBEDDER_LIBRARY_HEADER_DIR) \ + -DCOLOR_PANEL_PLUGIN_IMPL \ $(shell pkg-config --cflags gtk+-3.0 jsoncpp) LDFLAGS= -L$(EMBEDDER_LIBRARY_OUT_DIR) \ $(shell pkg-config --libs gtk+-3.0 jsoncpp) \ diff --git a/plugins/color_panel/linux/include/color_panel/color_panel_plugin.h b/plugins/color_panel/linux/include/color_panel/color_panel_plugin.h index 4797ee1b2..17ea3fd50 100644 --- a/plugins/color_panel/linux/include/color_panel/color_panel_plugin.h +++ b/plugins/color_panel/linux/include/color_panel/color_panel_plugin.h @@ -16,19 +16,27 @@ #include -#include +#include + +#include +#include + +#ifdef COLOR_PANEL_PLUGIN_IMPL +#define COLOR_PANEL_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define COLOR_PANEL_PLUGIN_EXPORT +#endif namespace plugins_color_panel { // A plugin for communicating with a native color picker panel. -class ColorPanelPlugin : public flutter_desktop_embedding::JsonPlugin { +class COLOR_PANEL_PLUGIN_EXPORT ColorPanelPlugin + : public flutter_desktop_embedding::Plugin { public: - ColorPanelPlugin(); - virtual ~ColorPanelPlugin(); + static void RegisterWithRegistrar( + flutter_desktop_embedding::PluginRegistrar *registrar); - void HandleJsonMethodCall( - const flutter_desktop_embedding::JsonMethodCall &method_call, - std::unique_ptr result) override; + virtual ~ColorPanelPlugin(); protected: // The source of a request to hide the panel, either a user action or @@ -39,6 +47,21 @@ class ColorPanelPlugin : public flutter_desktop_embedding::JsonPlugin { void HidePanel(CloseRequestSource source); private: + // Creates a plugin that communicates on the given channel. + ColorPanelPlugin( + std::unique_ptr> + channel); + + // Called when a method is called on |channel_|; + void HandleMethodCall( + const flutter_desktop_embedding::MethodCall &method_call, + std::unique_ptr> + result); + + // The MethodChannel used for communication with the Flutter engine. + std::unique_ptr> + channel_; + // Private implementation. class ColorPanel; std::unique_ptr color_panel_; diff --git a/plugins/color_panel/linux/src/color_panel_plugin.cc b/plugins/color_panel/linux/src/color_panel_plugin.cc index a51e106aa..98dd15079 100644 --- a/plugins/color_panel/linux/src/color_panel_plugin.cc +++ b/plugins/color_panel/linux/src/color_panel_plugin.cc @@ -16,13 +16,13 @@ #include #include +#include + #include "plugins/color_panel/common/channel_constants.h" static constexpr char kWindowTitle[] = "Flutter Color Picker"; namespace plugins_color_panel { -using flutter_desktop_embedding::JsonMethodCall; -using flutter_desktop_embedding::MethodResult; // Private implementation class containing the color picker widget. // @@ -30,11 +30,11 @@ using flutter_desktop_embedding::MethodResult; class ColorPanelPlugin::ColorPanel { public: explicit ColorPanel(ColorPanelPlugin *parent, - const Json::Value &method_args) { + const Json::Value *method_args) { gtk_widget_ = gtk_color_chooser_dialog_new(kWindowTitle, nullptr); gtk_color_chooser_set_use_alpha( reinterpret_cast(gtk_widget_), - method_args[kColorPanelShowAlpha].asBool()); + method_args && (*method_args)[kColorPanelShowAlpha].asBool()); gtk_widget_show_all(gtk_widget_); g_signal_connect(gtk_widget_, "close", G_CALLBACK(CloseCallback), parent); g_signal_connect(gtk_widget_, "response", G_CALLBACK(ResponseCallback), @@ -78,8 +78,9 @@ class ColorPanelPlugin::ColorPanel { if (response_id == GTK_RESPONSE_OK) { GdkRGBA color; gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color); - plugin->InvokeMethod(kColorSelectedCallbackMethod, - GdkColorToArgs(&color)); + plugin->channel_->InvokeMethod( + kColorSelectedCallbackMethod, + std::make_unique(GdkColorToArgs(&color))); } // Need this to close the color handler. plugin->HidePanel(CloseRequestSource::kUserAction); @@ -89,13 +90,38 @@ class ColorPanelPlugin::ColorPanel { GtkWidget *gtk_widget_; }; -ColorPanelPlugin::ColorPanelPlugin() - : JsonPlugin(kChannelName), color_panel_(nullptr) {} +// static +void ColorPanelPlugin::RegisterWithRegistrar( + flutter_desktop_embedding::PluginRegistrar *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), kChannelName, + &flutter_desktop_embedding::JsonMethodCodec::GetInstance()); + auto *channel_pointer = channel.get(); + + // Uses new instead of make_unique due to private constructor. + std::unique_ptr plugin( + new ColorPanelPlugin(std::move(channel))); + + channel_pointer->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +ColorPanelPlugin::ColorPanelPlugin( + std::unique_ptr> + channel) + : channel_(std::move(channel)), color_panel_(nullptr) {} ColorPanelPlugin::~ColorPanelPlugin() {} -void ColorPanelPlugin::HandleJsonMethodCall( - const JsonMethodCall &method_call, std::unique_ptr result) { +void ColorPanelPlugin::HandleMethodCall( + const flutter_desktop_embedding::MethodCall &method_call, + std::unique_ptr> + result) { if (method_call.method_name().compare(kShowColorPanelMethod) == 0) { result->Success(); // There is only one color panel that can be displayed at once. @@ -104,7 +130,7 @@ void ColorPanelPlugin::HandleJsonMethodCall( return; } color_panel_ = std::make_unique( - this, method_call.GetArgumentsAsJson()); + this, method_call.arguments()); } else if (method_call.method_name().compare(kHideColorPanelMethod) == 0) { result->Success(); if (color_panel_ == nullptr) { @@ -119,7 +145,7 @@ void ColorPanelPlugin::HandleJsonMethodCall( void ColorPanelPlugin::HidePanel(CloseRequestSource source) { color_panel_.reset(); if (source == CloseRequestSource::kUserAction) { - InvokeMethod(kClosedCallbackMethod); + channel_->InvokeMethod(kClosedCallbackMethod, nullptr); } } diff --git a/plugins/color_panel/macos/FLEColorPanelPlugin.h b/plugins/color_panel/macos/FLEColorPanelPlugin.h index 0357501de..3c3b32c1c 100644 --- a/plugins/color_panel/macos/FLEColorPanelPlugin.h +++ b/plugins/color_panel/macos/FLEColorPanelPlugin.h @@ -17,9 +17,8 @@ #import /** - * A FlutterPlugin to manage macOS's shared NSColorPanel singleton. Owned by - * the FlutterViewController. Responsible for managing the panel's display state - * and sending selected color data to Flutter, via system channels. + * A FlutterPlugin to manage macOS's shared NSColorPanel singleton. + * Responsible for managing the panel's display state and sending selected color data to Flutter. */ @interface FLEColorPanelPlugin : NSObject diff --git a/plugins/color_panel/macos/FLEColorPanelPlugin.mm b/plugins/color_panel/macos/FLEColorPanelPlugin.mm index b253902e8..7fd97e3f4 100644 --- a/plugins/color_panel/macos/FLEColorPanelPlugin.mm +++ b/plugins/color_panel/macos/FLEColorPanelPlugin.mm @@ -18,12 +18,26 @@ #include "plugins/color_panel/common/channel_constants.h" -@implementation FLEColorPanelPlugin +@implementation FLEColorPanelPlugin { + // The channel used to communicate with Flutter. + FLEMethodChannel *_channel; +} -@synthesize controller = _controller; ++ (void)registerWithRegistrar:(id)registrar { + FLEMethodChannel* channel = [FLEMethodChannel + methodChannelWithName:@(plugins_color_panel::kChannelName) + binaryMessenger:registrar.messenger + codec:[FLEJSONMethodCodec sharedInstance]]; + FLEColorPanelPlugin* instance = [[FLEColorPanelPlugin alloc] initWithChannel:channel]; + [registrar addMethodCallDelegate:instance channel:channel]; +} -- (NSString *)channel { - return @(plugins_color_panel::kChannelName); +- (instancetype)initWithChannel:(FLEMethodChannel*)channel { + self = [super init]; + if (self) { + _channel = channel; + } + return self; } /** @@ -94,14 +108,13 @@ - (void)removeColorPanelConnections { /** * Called when the user selects a color in the color panel. Grabs the selected color from the - * panel and sends it to Flutter via the '_controller'. + * panel and sends it to Flutter via the '_channel'. */ - (void)selectedColorDidChange { NSColor *color = [NSColorPanel sharedColorPanel].color; NSDictionary *colorDictionary = [self dictionaryWithColor:color]; - [_controller invokeMethod:@(plugins_color_panel::kColorSelectedCallbackMethod) - arguments:colorDictionary - onChannel:@(plugins_color_panel::kChannelName)]; + [_channel invokeMethod:@(plugins_color_panel::kColorSelectedCallbackMethod) + arguments:colorDictionary]; } /** @@ -125,9 +138,8 @@ - (NSDictionary *)dictionaryWithColor:(NSColor *)color { - (void)windowWillClose:(NSNotification *)notification { [self removeColorPanelConnections]; - [_controller invokeMethod:@(plugins_color_panel::kClosedCallbackMethod) - arguments:nil - onChannel:@(plugins_color_panel::kChannelName)]; + [_channel invokeMethod:@(plugins_color_panel::kClosedCallbackMethod) + arguments:nil]; } @end diff --git a/plugins/file_chooser/BUILD.gn b/plugins/file_chooser/BUILD.gn index 86b3e8837..1b9f3ec1c 100644 --- a/plugins/file_chooser/BUILD.gn +++ b/plugins/file_chooser/BUILD.gn @@ -30,7 +30,10 @@ published_shared_library("file_chooser") { ] } - defines = ["USE_FLATTENED_INCLUDES"] + defines = [ + "FILE_CHOOSER_PLUGIN_IMPL", + "USE_FLATTENED_INCLUDES", + ] deps = [ "//library:flutter_embedder", diff --git a/plugins/file_chooser/lib/src/channel_controller.dart b/plugins/file_chooser/lib/src/channel_controller.dart index c498e7407..f5121fb5b 100644 --- a/plugins/file_chooser/lib/src/channel_controller.dart +++ b/plugins/file_chooser/lib/src/channel_controller.dart @@ -37,15 +37,6 @@ enum FileChooserType { /// A set of configuration options for a file chooser. class FileChooserConfigurationOptions { - // See channel_constants.h for documentation; these correspond exactly to - // the configuration parameters defined in the channel protocol. - final String initialDirectory; // ignore: public_member_api_docs - final String initialFileName; // ignore: public_member_api_docs - final List allowedFileTypes; // ignore: public_member_api_docs - final bool allowsMultipleSelection; // ignore: public_member_api_docs - final bool canSelectDirectories; // ignore: public_member_api_docs - final String confirmButtonText; // ignore: public_member_api_docs - /// Creates a new configuration options object with the given settings. const FileChooserConfigurationOptions( {this.initialDirectory, @@ -55,6 +46,15 @@ class FileChooserConfigurationOptions { this.canSelectDirectories, this.confirmButtonText}); + // See channel_constants.h for documentation; these correspond exactly to + // the configuration parameters defined in the channel protocol. + final String initialDirectory; // ignore: public_member_api_docs + final String initialFileName; // ignore: public_member_api_docs + final List allowedFileTypes; // ignore: public_member_api_docs + final bool allowsMultipleSelection; // ignore: public_member_api_docs + final bool canSelectDirectories; // ignore: public_member_api_docs + final String confirmButtonText; // ignore: public_member_api_docs + /// Returns the configuration as a map that can be passed as the /// arguments to invokeMethod for [_kShowOpenPanelMethod] or /// [_kShowSavePanelMethod]. @@ -87,11 +87,11 @@ class FileChooserConfigurationOptions { /// A singleton object that controls file-choosing interactions with macOS. class FileChooserChannelController { + FileChooserChannelController._(); + /// The platform channel used to manage native file chooser affordances. final _channel = new MethodChannel(_kChannelName, new JSONMethodCodec()); - FileChooserChannelController._(); - /// A reference to the singleton instance of the class. static final FileChooserChannelController instance = new FileChooserChannelController._(); diff --git a/plugins/file_chooser/linux/Makefile b/plugins/file_chooser/linux/Makefile index bb2bf3f87..0cfc2b300 100644 --- a/plugins/file_chooser/linux/Makefile +++ b/plugins/file_chooser/linux/Makefile @@ -24,9 +24,10 @@ EMBEDDER_LIB_FILE=$(EMBEDDER_LIBRARY_OUT_DIR)/lib$(EMBEDDER_LIB_NAME).so COMMON_DIR=$(CURDIR)/../common CXX=g++ -std=c++14 -CXXFLAGS= -Wall -Werror -shared -fPIC \ +CXXFLAGS= -Wall -Werror -shared -fPIC -fvisibility=hidden \ -I$(PROJECT_ROOT) \ -I$(EMBEDDER_LIBRARY_HEADER_DIR) \ + -DFILE_CHOOSER_PLUGIN_IMPL \ $(shell pkg-config --cflags gtk+-3.0 jsoncpp) LDFLAGS= -L$(EMBEDDER_LIBRARY_OUT_DIR) \ $(shell pkg-config --libs gtk+-3.0 jsoncpp) \ diff --git a/plugins/file_chooser/linux/include/file_chooser/file_chooser_plugin.h b/plugins/file_chooser/linux/include/file_chooser/file_chooser_plugin.h index 968a2cf0a..ae97be36e 100644 --- a/plugins/file_chooser/linux/include/file_chooser/file_chooser_plugin.h +++ b/plugins/file_chooser/linux/include/file_chooser/file_chooser_plugin.h @@ -14,19 +14,45 @@ #ifndef PLUGINS_FILE_CHOOSER_LINUX_INCLUDE_FILE_CHOOSER_FILE_CHOOSER_PLUGIN_H_ #define PLUGINS_FILE_CHOOSER_LINUX_INCLUDE_FILE_CHOOSER_FILE_CHOOSER_PLUGIN_H_ -#include +#include + +#include + +#include +#include + +#ifdef FILE_CHOOSER_PLUGIN_IMPL +#define FILE_CHOOSER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FILE_CHOOSER_PLUGIN_EXPORT +#endif namespace plugins_file_chooser { // Implements a file chooser plugin. -class FileChooserPlugin : public flutter_desktop_embedding::JsonPlugin { +class FILE_CHOOSER_PLUGIN_EXPORT FileChooserPlugin + : public flutter_desktop_embedding::Plugin { public: - FileChooserPlugin(); + static void RegisterWithRegistrar( + flutter_desktop_embedding::PluginRegistrar *registrar); + virtual ~FileChooserPlugin(); - void HandleJsonMethodCall( - const flutter_desktop_embedding::JsonMethodCall &method_call, - std::unique_ptr result) override; + private: + // Creates a plugin that communicates on the given channel. + FileChooserPlugin( + std::unique_ptr> + channel); + + // Called when a method is called on |channel_|; + void HandleMethodCall( + const flutter_desktop_embedding::MethodCall &method_call, + std::unique_ptr> + result); + + // The MethodChannel used for communication with the Flutter engine. + std::unique_ptr> + channel_; }; } // namespace plugins_file_chooser diff --git a/plugins/file_chooser/linux/src/file_chooser_plugin.cc b/plugins/file_chooser/linux/src/file_chooser_plugin.cc index 3d6e446cd..1d68ff309 100644 --- a/plugins/file_chooser/linux/src/file_chooser_plugin.cc +++ b/plugins/file_chooser/linux/src/file_chooser_plugin.cc @@ -17,6 +17,8 @@ #include #include +#include + #include "plugins/file_chooser/common/channel_constants.h" // File chooser callback results. @@ -24,8 +26,6 @@ static constexpr int kCancelResultValue = 0; static constexpr int kOkResultValue = 1; namespace plugins_file_chooser { -using flutter_desktop_embedding::JsonMethodCall; -using flutter_desktop_embedding::MethodResult; // Applies filters to the file chooser. // @@ -86,8 +86,8 @@ static void ProcessAttributes(const Json::Value &method_args, // string, then this returns a file saver dialog. // // If the method is not recognized as one of those above, will return a nullptr. -static GtkWidget *CreateFileChooserFromMethod( - const std::string &method, const std::string &ok_button) { +static GtkWidget *CreateFileChooserFromMethod(const std::string &method, + const std::string &ok_button) { GtkWidget *chooser = nullptr; if (method == kShowOpenPanelMethod) { GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; @@ -110,14 +110,13 @@ static GtkWidget *CreateFileChooserFromMethod( // The JSON args determine the modifications to the file chooser, like filters, // being able to choose multiple files, etc. static GtkWidget *CreateFileChooser(const std::string &method, - const Json::Value &args) { + const Json::Value &args) { Json::Value ok_button_value = args[kConfirmButtonTextKey]; std::string ok_button_str; if (!ok_button_value.isNull()) { ok_button_str = ok_button_value.asString(); } - GtkWidget *chooser = - CreateFileChooserFromMethod(method, ok_button_str); + GtkWidget *chooser = CreateFileChooserFromMethod(method, ok_button_str); if (chooser == nullptr) { std::cerr << "Could not determine method for file chooser from: " << method << std::endl; @@ -143,19 +142,46 @@ static Json::Value CreateResponseObject( return response; } -FileChooserPlugin::FileChooserPlugin() : JsonPlugin(kChannelName, true) {} +// static +void FileChooserPlugin::RegisterWithRegistrar( + flutter_desktop_embedding::PluginRegistrar *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), kChannelName, + &flutter_desktop_embedding::JsonMethodCodec::GetInstance()); + auto *channel_pointer = channel.get(); + + // Uses new instead of make_unique due to private constructor. + std::unique_ptr plugin( + new FileChooserPlugin(std::move(channel))); + + channel_pointer->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + registrar->EnableInputBlockingForChannel(kChannelName); + + registrar->AddPlugin(std::move(plugin)); +} + +FileChooserPlugin::FileChooserPlugin( + std::unique_ptr> + channel) + : channel_(std::move(channel)) {} FileChooserPlugin::~FileChooserPlugin() {} -void FileChooserPlugin::HandleJsonMethodCall( - const JsonMethodCall &method_call, std::unique_ptr result) { - if (method_call.GetArgumentsAsJson().isNull()) { +void FileChooserPlugin::HandleMethodCall( + const flutter_desktop_embedding::MethodCall &method_call, + std::unique_ptr> + result) { + if (!method_call.arguments() || method_call.arguments()->isNull()) { result->Error("Bad Arguments", "Null file chooser method args received"); return; } - auto chooser = CreateFileChooser(method_call.method_name(), - method_call.GetArgumentsAsJson()); + auto chooser = + CreateFileChooser(method_call.method_name(), *method_call.arguments()); if (chooser == nullptr) { result->NotImplemented(); return; diff --git a/plugins/file_chooser/macos/FLEFileChooserPlugin.mm b/plugins/file_chooser/macos/FLEFileChooserPlugin.mm index e37577bf0..cb7ff7b8d 100644 --- a/plugins/file_chooser/macos/FLEFileChooserPlugin.mm +++ b/plugins/file_chooser/macos/FLEFileChooserPlugin.mm @@ -18,9 +18,18 @@ #include "plugins/file_chooser/common/channel_constants.h" -@implementation FLEFileChooserPlugin +@implementation FLEFileChooserPlugin { + // The view displaying Flutter content. + NSView* _flutterView; +} -@synthesize controller = _controller; +- (instancetype)initWithView:(NSView*)view { + self = [super init]; + if (self != nil) { + _flutterView = view; + } + return self; +} /** * Configures an NSSavePanel instance on behalf of a flutter client. @@ -69,8 +78,13 @@ - (void)configureOpenPanel:(nonnull NSOpenPanel *)panel #pragma FLEPlugin implementation -- (NSString *)channel { - return @(plugins_file_chooser::kChannelName); ++ (void)registerWithRegistrar:(id)registrar { + FLEMethodChannel* channel = [FLEMethodChannel + methodChannelWithName:@(plugins_file_chooser::kChannelName) + binaryMessenger:registrar.messenger +codec:[FLEJSONMethodCodec sharedInstance]]; + FLEFileChooserPlugin* instance = [[FLEFileChooserPlugin alloc] initWithView:registrar.view]; + [registrar addMethodCallDelegate:instance channel:channel]; } - (void)handleMethodCall:(FLEMethodCall *)call result:(FLEMethodResult)result { @@ -80,7 +94,7 @@ - (void)handleMethodCall:(FLEMethodCall *)call result:(FLEMethodResult)result { NSSavePanel *savePanel = [NSSavePanel savePanel]; savePanel.canCreateDirectories = YES; [self configureSavePanel:savePanel withArguments:arguments]; - [savePanel beginSheetModalForWindow:_controller.view.window + [savePanel beginSheetModalForWindow:_flutterView.window completionHandler:^(NSModalResponse panelResult) { NSArray *URLs = (panelResult == NSModalResponseOK) ? @[ savePanel.URL ] : nil; @@ -91,7 +105,7 @@ - (void)handleMethodCall:(FLEMethodCall *)call result:(FLEMethodResult)result { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; [self configureSavePanel:openPanel withArguments:arguments]; [self configureOpenPanel:openPanel withArguments:arguments]; - [openPanel beginSheetModalForWindow:_controller.view.window + [openPanel beginSheetModalForWindow:_flutterView.window completionHandler:^(NSModalResponse panelResult) { NSArray *URLs = (panelResult == NSModalResponseOK) ? openPanel.URLs : nil; diff --git a/plugins/menubar/BUILD.gn b/plugins/menubar/BUILD.gn index ab431f8ef..3d081058a 100644 --- a/plugins/menubar/BUILD.gn +++ b/plugins/menubar/BUILD.gn @@ -30,7 +30,10 @@ published_shared_library("menubar") { ] } - defines = ["USE_FLATTENED_INCLUDES"] + defines = [ + "MENUBAR_PLUGIN_IMPL", + "USE_FLATTENED_INCLUDES", + ] deps = [ "//library:flutter_embedder", diff --git a/plugins/menubar/lib/src/menu_channel.dart b/plugins/menubar/lib/src/menu_channel.dart index 830126e84..0d665e9c3 100644 --- a/plugins/menubar/lib/src/menu_channel.dart +++ b/plugins/menubar/lib/src/menu_channel.dart @@ -30,6 +30,11 @@ const String _kDividerKey = 'isDivider'; /// A singleton object that handles the interaction with the menu bar platform /// channel. class MenuChannel { + /// Private constructor. + MenuChannel._() { + _platformChannel.setMethodCallHandler(_callbackHandler); + } + final MethodChannel _platformChannel = const MethodChannel(_kMenuChannelName, const JSONMethodCodec()); @@ -47,11 +52,6 @@ class MenuChannel { /// stale callbacks. bool _updateInProgress; - /// Private constructor. - MenuChannel._() { - _platformChannel.setMethodCallHandler(_callbackHandler); - } - /// The static instance of the menu channel. static final MenuChannel instance = new MenuChannel._(); diff --git a/plugins/menubar/lib/src/menu_item.dart b/plugins/menubar/lib/src/menu_item.dart index d3d3be28b..6bfc1cba3 100644 --- a/plugins/menubar/lib/src/menu_item.dart +++ b/plugins/menubar/lib/src/menu_item.dart @@ -18,21 +18,15 @@ typedef MenuSelectedCallback = void Function(); /// The base type for an individual menu item that can be shown in a menu. abstract class AbstractMenuItem { - /// The displayed label for the menu item. - final String label; - /// Creates a new menu item with the give label. const AbstractMenuItem(this.label); + + /// The displayed label for the menu item. + final String label; } /// A standard menu item, with no submenus. class MenuItem extends AbstractMenuItem { - /// The callback to call whenever the menu item is selected. - final MenuSelectedCallback onClicked; - - /// Whether or not the menu item is enabled. - final bool enabled; - /// Creates a new menu item with the given [label] and options. /// /// Note that onClicked should generally be set unless [enabled] is false, @@ -42,18 +36,24 @@ class MenuItem extends AbstractMenuItem { this.enabled = true, this.onClicked, }) : super(label); + + /// The callback to call whenever the menu item is selected. + final MenuSelectedCallback onClicked; + + /// Whether or not the menu item is enabled. + final bool enabled; } /// A menu item continaing a submenu. /// /// The item itself can't be selected, it just displays the submenu. class Submenu extends AbstractMenuItem { - /// The menu items contained in the submenu. - final List children; - /// Creates a new submenu with the given [label] and [children]. const Submenu({@required String label, @required this.children}) : super(label); + + /// The menu items contained in the submenu. + final List children; } /// A menu item that serves as a divider, generally drawn as a line. diff --git a/plugins/menubar/linux/Makefile b/plugins/menubar/linux/Makefile index 0ee350ef1..87928cb71 100644 --- a/plugins/menubar/linux/Makefile +++ b/plugins/menubar/linux/Makefile @@ -24,9 +24,10 @@ EMBEDDER_LIB_FILE=$(EMBEDDER_LIBRARY_OUT_DIR)/lib$(EMBEDDER_LIB_NAME).so COMMON_DIR=$(CURDIR)/../common CXX=g++ -std=c++14 -CXXFLAGS= -Wall -Werror -shared -fPIC \ +CXXFLAGS= -Wall -Werror -shared -fPIC -fvisibility=hidden \ -I$(PROJECT_ROOT) \ -I$(EMBEDDER_LIBRARY_HEADER_DIR) \ + -DMENUBAR_PLUGIN_IMPL \ $(shell pkg-config --cflags gtk+-3.0 jsoncpp) LDFLAGS= -L$(EMBEDDER_LIBRARY_OUT_DIR) \ $(shell pkg-config --libs gtk+-3.0 jsoncpp) \ diff --git a/plugins/menubar/linux/include/menubar/menubar_plugin.h b/plugins/menubar/linux/include/menubar/menubar_plugin.h index 394739c8e..97e49a22c 100644 --- a/plugins/menubar/linux/include/menubar/menubar_plugin.h +++ b/plugins/menubar/linux/include/menubar/menubar_plugin.h @@ -14,22 +14,46 @@ #ifndef PLUGINS_MENUBAR_LINUX_INCLUDE_MENUBAR_MENUBAR_PLUGIN_H_ #define PLUGINS_MENUBAR_LINUX_INCLUDE_MENUBAR_MENUBAR_PLUGIN_H_ -#include +#include + +#include + +#include +#include + +#ifdef MENUBAR_PLUGIN_IMPL +#define MENUBAR_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define MENUBAR_PLUGIN_EXPORT +#endif namespace plugins_menubar { // A plugin to control a native menubar. -class MenubarPlugin : public flutter_desktop_embedding::JsonPlugin { +class MENUBAR_PLUGIN_EXPORT MenubarPlugin + : public flutter_desktop_embedding::Plugin { public: - // See Plugin for constructor details. - MenubarPlugin(); - virtual ~MenubarPlugin(); + static void RegisterWithRegistrar( + flutter_desktop_embedding::PluginRegistrar *registrar); - void HandleJsonMethodCall( - const flutter_desktop_embedding::JsonMethodCall &method_call, - std::unique_ptr result) override; + virtual ~MenubarPlugin(); private: + // Creates a plugin that communicates on the given channel. + MenubarPlugin( + std::unique_ptr> + channel); + + // Called when a method is called on |channel_|; + void HandleMethodCall( + const flutter_desktop_embedding::MethodCall &method_call, + std::unique_ptr> + result); + + // The MethodChannel used for communication with the Flutter engine. + std::unique_ptr> + channel_; + class Menubar; std::unique_ptr menubar_; }; diff --git a/plugins/menubar/linux/src/menubar_plugin.cc b/plugins/menubar/linux/src/menubar_plugin.cc index 149e8bc8e..8aa9c6617 100644 --- a/plugins/menubar/linux/src/menubar_plugin.cc +++ b/plugins/menubar/linux/src/menubar_plugin.cc @@ -17,16 +17,38 @@ #include #include +#include + #include "plugins/menubar/common/channel_constants.h" static constexpr char kWindowTitle[] = "Flutter Menubar"; namespace plugins_menubar { -using flutter_desktop_embedding::JsonMethodCall; -using flutter_desktop_embedding::MethodResult; -MenubarPlugin::MenubarPlugin() - : JsonPlugin(kChannelName, false) {} +// static +void MenubarPlugin::RegisterWithRegistrar( + flutter_desktop_embedding::PluginRegistrar *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), kChannelName, + &flutter_desktop_embedding::JsonMethodCodec::GetInstance()); + auto *channel_pointer = channel.get(); + + // Uses new instead of make_unique due to private constructor. + std::unique_ptr plugin(new MenubarPlugin(std::move(channel))); + + channel_pointer->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +MenubarPlugin::MenubarPlugin( + std::unique_ptr> + channel) + : channel_(std::move(channel)) {} MenubarPlugin::~MenubarPlugin() {} @@ -61,8 +83,9 @@ class MenubarPlugin::Menubar { static void MenuItemSelected(GtkWidget *menuItem, gpointer *data) { auto plugin = reinterpret_cast(data); - plugin->InvokeMethod(kMenuItemSelectedCallbackMethod, - std::stoi(gtk_widget_get_name(menuItem))); + plugin->channel_->InvokeMethod(kMenuItemSelectedCallbackMethod, + std::make_unique(std::stoi( + gtk_widget_get_name(menuItem)))); } // Creates the menu items heirarchy from a given Json object. @@ -88,8 +111,8 @@ class MenubarPlugin::Menubar { std::string label = root[kLabelKey].asString(); if (root[kChildrenKey].isArray()) { - // A parent menu item. Creates a widget with its label and then build the - // children. + // A parent menu item. Creates a widget with its label and then build + // the children. auto array = root[kChildrenKey]; auto menu = gtk_menu_new(); auto menuItem = gtk_menu_item_new_with_label(label.c_str()); @@ -140,18 +163,25 @@ class MenubarPlugin::Menubar { GtkWidget *menubar_; }; -void MenubarPlugin::HandleJsonMethodCall(const JsonMethodCall &method_call, - std::unique_ptr result) { +void MenubarPlugin::HandleMethodCall( + const flutter_desktop_embedding::MethodCall &method_call, + std::unique_ptr> + result) { if (method_call.method_name().compare(kMenuSetMethod) == 0) { - result->Success(); + if (!method_call.arguments()) { + result->Error("Bad Arguments", "Null menu bar arguments received"); + return; + } + if (menubar_ == nullptr) { menubar_ = std::make_unique(this); } // The menubar will be redrawn after every interaction. Clear items to avoid // duplication. menubar_->ClearMenuItems(); - menubar_->SetMenuItems(method_call.GetArgumentsAsJson(), this, + menubar_->SetMenuItems(*method_call.arguments(), this, menubar_->GetRootMenuBar()); + result->Success(); } else { result->NotImplemented(); } diff --git a/plugins/menubar/macos/FLEMenubarPlugin.mm b/plugins/menubar/macos/FLEMenubarPlugin.mm index 0a09651fd..faabdaba1 100644 --- a/plugins/menubar/macos/FLEMenubarPlugin.mm +++ b/plugins/menubar/macos/FLEMenubarPlugin.mm @@ -16,9 +16,18 @@ #include "plugins/menubar/common/channel_constants.h" -@implementation FLEMenubarPlugin +@implementation FLEMenubarPlugin { +// The channel used to communicate with Flutter. +FLEMethodChannel *_channel; +} -@synthesize controller = _controller; +- (instancetype)initWithChannel:(FLEMethodChannel*)channel { + self = [super init]; + if (self) { + _channel = channel; + } + return self; +} /** * Removes any top-level menus added by this plugin. @@ -102,15 +111,19 @@ - (NSMenuItem *)menuItemFromJSONRepresentation:(NSDictionary *)representation { */ - (void)flutterMenuItemSelected:(id)sender { NSMenuItem *item = sender; - [_controller invokeMethod:@(plugins_menubar::kMenuItemSelectedCallbackMethod) - arguments:@(item.tag) - onChannel:@(plugins_menubar::kChannelName)]; + [_channel invokeMethod:@(plugins_menubar::kMenuItemSelectedCallbackMethod) + arguments:@(item.tag)]; } #pragma FLEPlugin implementation -- (NSString *)channel { - return @(plugins_menubar::kChannelName); ++ (void)registerWithRegistrar:(id)registrar { + FLEMethodChannel* channel = [FLEMethodChannel + methodChannelWithName:@(plugins_menubar::kChannelName) + binaryMessenger:registrar.messenger + codec:[FLEJSONMethodCodec sharedInstance]]; + FLEMenubarPlugin* instance = [[FLEMenubarPlugin alloc] initWithChannel:channel]; + [registrar addMethodCallDelegate:instance channel:channel]; } - (void)handleMethodCall:(FLEMethodCall *)call result:(FLEMethodResult)result { diff --git a/tools/dart_tools/bin/build_jsoncpp.dart b/tools/dart_tools/bin/build_jsoncpp.dart new file mode 100644 index 000000000..0efb7b8d4 --- /dev/null +++ b/tools/dart_tools/bin/build_jsoncpp.dart @@ -0,0 +1,77 @@ +// 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. + +// This script builds jsoncpp using Visual Studio 2017 in a provided directory. +// An additional directory can be provided which will have to built library +// copied to it. Optionally using the --debug flag will build in debug mode. + +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:path/path.dart' as path; + +import '../lib/runCommand.dart'; + +Future main(List arguments) async { + if (!Platform.isWindows) { + throw new Exception('Building jsoncpp libraries is only available on ' + 'windows.'); + } + + final parser = new ArgParser()..addFlag('debug', abbr: 'd', negatable: false); + final args = parser.parse(arguments); + + if (args.rest.isEmpty) { + throw new Exception('One argument must be provided, the directory where ' + 'jsoncpp is downloaded.'); + } + + final downloadDirectory = args.rest[0]; + final debug = args['debug']; + + final buildDirectory = '$downloadDirectory/makefiles/msvc2017'; + await buildLibrary(buildDirectory, debug: debug); + + if (args.rest.length != 2) { + print('Copy directory not provided.'); + exit(0); + } + + final copyDirectory = args.rest[1]; + await copyLibraryToOutputDirectory(buildDirectory, copyDirectory, + debug: debug); +} + +Future buildLibrary(String buildDirectory, {bool debug}) async { + final arguments = [ + 'msbuild', + 'lib_json.vcxproj', + ]; + if (!debug) { + arguments.add('/p:Configuration=Release'); + } + await runCommand('vcvars64.bat 1> nul &&', arguments, + workingDirectory: buildDirectory); +} + +Future copyLibraryToOutputDirectory( + String buildDirectory, String copyDirectory, + {bool debug}) async { + final outputDirectory = "$buildDirectory/x64/${debug ? "Debug" : "Release"}"; + final outputLibrary = + "$outputDirectory/json_vc71_libmt${debug ? "d" : ""}.lib"; + + await File(outputLibrary) + .copy(path.join(copyDirectory, path.basename(outputLibrary))); +} diff --git a/tools/dart_tools/bin/fetch_glfw.dart b/tools/dart_tools/bin/fetch_glfw.dart new file mode 100644 index 000000000..3dee53783 --- /dev/null +++ b/tools/dart_tools/bin/fetch_glfw.dart @@ -0,0 +1,95 @@ +// 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. + +// This script downloads a prebuilt glfw library into a provided directory. + +import 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:archive/archive.dart'; + +const String glfwArchiveUrlString = + 'http://github.com/glfw/glfw/releases/download/3.2.1/glfw-3.2.1.bin.WIN64.zip'; + +const List requiredFiles = [ + 'glfw-3.2.1.bin.WIN64/include/GLFW/glfw3.h', + 'glfw-3.2.1.bin.WIN64/lib-vc2015/glfw3.lib', +]; + +Future main(List arguments) async { + if (!Platform.isWindows) { + throw new Exception('Prebuilt glfw3 libraries are only available on ' + 'windows.'); + } + + if (arguments.length != 1) { + throw new Exception('Only one argument should be passed, the directory to ' + 'download glfw to.'); + } + + final outputDirectory = arguments[0]; + + if (await downloadExists(outputDirectory)) { + print('GLFW files already exist.'); + exit(0); + } + + final archiveData = await downloadLibrary(); + + await new Directory(outputDirectory).create(recursive: true); + + await extractRequiredFiles(archiveData, outputDirectory); +} + +Future downloadExists(String outputDirectory) async { + var existingFiles = 0; + for (final file in requiredFiles) { + if (File('$outputDirectory/${path.basename(file)}').existsSync()) { + existingFiles++; + } + } + + if (existingFiles == requiredFiles.length) { + return true; + } + return false; +} + +Future> downloadLibrary() async { + final archiveUri = Uri.parse(glfwArchiveUrlString); + + final httpClient = new HttpClient(); + final response = + await httpClient.getUrl(archiveUri).then((request) => request.close()); + final archiveData = []; + await for (final data in response) { + archiveData.addAll(data); + } + httpClient.close(); + return archiveData; +} + +Future extractRequiredFiles( + List archiveData, String outputDirectory) async { + final archive = new ZipDecoder().decodeBytes(archiveData); + for (final file in archive) { + if (!requiredFiles.contains(file.name)) { + continue; + } + + final extractedFile = + new File(path.join(outputDirectory, path.basename(file.name))); + await extractedFile.writeAsBytes(file.content); + } +} diff --git a/tools/dart_tools/bin/fetch_jsoncpp.dart b/tools/dart_tools/bin/fetch_jsoncpp.dart new file mode 100644 index 000000000..05937d543 --- /dev/null +++ b/tools/dart_tools/bin/fetch_jsoncpp.dart @@ -0,0 +1,58 @@ +// 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. + +// This script downloads a specific version of jsoncpp with Visual Studio 2017 +// support into a provided directory. + +import 'dart:io'; + +import '../lib/runCommand.dart'; + +// For the fork containing V2017 support. Once +// https://github.com/open-source-parsers/jsoncpp/pull/853 +// has landed, this should use the jsoncpp repository. +const String gitRepo = 'https://github.com/clarkezone/jsoncpp.git'; +const String pinnedVersion = '3ae7e8073a425c93329c8577a3c813c206322ca4'; + +Future main(List arguments) async { + if (!Platform.isWindows) { + throw new Exception('Fetching jsoncpp libraries is only available on ' + 'windows.'); + } + + if (arguments.length != 1) { + throw new Exception('One argument should be passed, the directory to ' + 'download jsoncpp to.'); + } + + final downloadDirectory = arguments[0]; + + if (Directory(downloadDirectory).existsSync()) { + print('$downloadDirectory already exists; skipping clone'); + } else { + await runCommand('git', [ + 'clone', + gitRepo, + downloadDirectory, + ]); + } + + await runCommand( + 'git', + [ + 'checkout', + pinnedVersion, + ], + workingDirectory: downloadDirectory); +} diff --git a/tools/dart_tools/bin/update_flutter_engine.dart b/tools/dart_tools/bin/update_flutter_engine.dart index 96c44ad4b..965ad5532 100644 --- a/tools/dart_tools/bin/update_flutter_engine.dart +++ b/tools/dart_tools/bin/update_flutter_engine.dart @@ -32,18 +32,19 @@ const String engineArchiveBaseUrlString = /// Simple container for platform-specific information. class PlatformInfo { + PlatformInfo(this.archiveSubpath, this.libraryFile); + // The subpath on storage.googleapis.com for a platform's engine archive. final String archiveSubpath; // The extracted engine library filename for a platform. final String libraryFile; - - PlatformInfo(this.archiveSubpath, this.libraryFile); } /// Exceptions for known error cases in updating the engine. class EngineUpdateException implements Exception { - final String message; EngineUpdateException(this.message); + + final String message; } /// PlatformInfo for each supported platform. @@ -89,10 +90,10 @@ Future main(List arguments) async { } try { - final platform = parsedArguments['platform']; + final String platform = parsedArguments['platform']; final outputRoot = path.canonicalize(path.absolute(parsedArguments.rest[0])); - final targetHash = parsedArguments['hash'] ?? + final String targetHash = parsedArguments['hash'] ?? await engineHashForFlutterTree(parsedArguments['flutter_root']); final libraryFile = platformInfo[platform].libraryFile; @@ -213,7 +214,8 @@ Future extractEngineArchive( // Unwrap the outer zip via Archive to avoid starting an extra process. final isDoubleZipped = archive.numberOfFiles() == 1 && archive[0].name.endsWith('.zip'); - final innermostZipData = isDoubleZipped ? archive[0].content : archiveData; + final List innermostZipData = + isDoubleZipped ? archive[0].content : archiveData; await unzipMacOSEngineFramework(innermostZipData, outputDirectory); } else { // Windows and Linux have flat archives, so can be easily extracted via @@ -249,8 +251,8 @@ Future unzipMacOSEngineFramework( // Temporarily write the data to a file, since unzip doesn't accept piped // input, then delete the file. await temporaryArchiveFile.writeAsBytes(archiveData); - final result = await Process - .run('/usr/bin/unzip', [temporaryArchiveFile.path, '-d', targetPath]); + final result = await Process.run( + '/usr/bin/unzip', [temporaryArchiveFile.path, '-d', targetPath]); await temporaryArchiveFile.delete(); if (result.exitCode != 0) { throw new EngineUpdateException( diff --git a/tools/dart_tools/lib/runCommand.dart b/tools/dart_tools/lib/runCommand.dart new file mode 100644 index 000000000..5e6dd074f --- /dev/null +++ b/tools/dart_tools/lib/runCommand.dart @@ -0,0 +1,39 @@ +// 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. + +import 'dart:io'; + +/// Runs a [command] on the command line with some logging and error handling +/// +/// Takes a [command] and [arguments] to pass to the [command] that will be run +/// on the command line. Stdout and stderr will be printed and an exception +/// thrown if the exit code is not 0 and [allowFail] is true. An optional +/// [workingDirectory] can be passed for the directory of the [command] to be +/// executed in. +Future runCommand(String command, List arguments, + {String workingDirectory, bool allowFail = false}) async { + final fullCommand = '$command ${arguments.join(" ")}'; + print('Running $fullCommand'); + + final process = await Process.start(command, arguments, + workingDirectory: workingDirectory, runInShell: true); + await stdout.addStream(process.stdout); + await stderr.addStream(process.stderr); + + final exitCode = await process.exitCode; + if (!allowFail && exitCode != 0) { + throw new Exception('$fullCommand failed with exit code $exitCode'); + } + return exitCode; +} diff --git a/tools/gn_dart.bat b/tools/gn_dart.bat new file mode 100644 index 000000000..e6323d56e --- /dev/null +++ b/tools/gn_dart.bat @@ -0,0 +1,26 @@ +:: 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 + +:: This script wraps GN with a --script-executable pointing at the Dart binary +:: packaged with the Flutter tree. +set BASE_DIR=%~dp0 +for /f "delims=" %%i in ('%~dp0flutter_location') do set FLUTTER_DIR=%%i +set FLUTTER_BIN_DIR=%FLUTTER_DIR%\bin +set DART_BIN_DIR=%FLUTTER_BIN_DIR%\cache\dart-sdk\bin + +:: Ensure that the Dart SDK has been downloaded. +if not exist %DART_BIN_DIR%\ call %FLUTTER_BIN_DIR%\flutter precache + +call gn --script-executable=%DART_BIN_DIR%\dart %* diff --git a/tools/run_dart_tool b/tools/run_dart_tool new file mode 100755 index 000000000..0d645b1a6 --- /dev/null +++ b/tools/run_dart_tool @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# +# Copyright 2019 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. + +# Takes the name of a tool in dart_tools/bin (without the .dart extension), +# as well as any parameters to that tool, then runs it with those parameters +# using the version of Dart from the Flutter tree. + +readonly base_dir="$(dirname "$0")" +readonly flutter_dir="$("${base_dir}/flutter_location")" +readonly flutter_bin_dir="${flutter_dir}/bin" +readonly dart_bin_dir="${flutter_bin_dir}/cache/dart-sdk/bin" + +# Ensure that the Dart SDK has been downloaded. +if [[ ! -e ${dart_bin_dir} ]]; then + "${flutter_bin_dir}/flutter" precache +fi + +readonly tool_script="${base_dir}/dart_tools/bin/$1.dart" +exec "${dart_bin_dir}/dart" "${tool_script}" "${@:2}" diff --git a/tools/run_dart_tool.bat b/tools/run_dart_tool.bat new file mode 100644 index 000000000..1cc4b4d39 --- /dev/null +++ b/tools/run_dart_tool.bat @@ -0,0 +1,26 @@ +:: Copyright 2019 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 + +for /f "delims=" %%i in ('%~dp0flutter_location') do set FLUTTER_DIR=%%i +set FLUTTER_BIN_DIR=%FLUTTER_DIR%\bin +set DART_BIN_DIR=%FLUTTER_BIN_DIR%\cache\dart-sdk\bin + +:: Ensure that the Dart SDK has been downloaded. +if not exist %DART_BIN_DIR%\ call %FLUTTER_BIN_DIR%\flutter precache + +set DART_TOOL=%1 +for /f "tokens=1,*" %%a in ("%*") do set TOOL_PARAMS=%%b + +call %DART_BIN_DIR%\dart %~dp0.\dart_tools\bin\%DART_TOOL%.dart %TOOL_PARAMS% diff --git a/tools/update_flutter_engine b/tools/update_flutter_engine index e6e655549..913935b3f 100755 --- a/tools/update_flutter_engine +++ b/tools/update_flutter_engine @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# +# # Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,12 +13,11 @@ # 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. + +# Runs update_flutter_engine.dart, using the output of flutter_location as +# --flutter_root + readonly base_dir="$(dirname "$0")" -readonly flutter_dir="$("$base_dir/flutter_location")" -readonly flutter_bin_dir="$flutter_dir/bin" -readonly dart_bin_dir="$flutter_bin_dir/cache/dart-sdk/bin" -# Ensure that the Dart SDK has been downloaded. -if [[ ! -e $dart_bin_dir ]]; then - "$flutter_bin_dir/flutter" precache -fi -exec "$dart_bin_dir/dart" "$base_dir/dart_tools/bin/update_flutter_engine.dart" "$@" +readonly flutter_dir="$("${base_dir}/flutter_location")" +exec "${base_dir}/run_dart_tool" update_flutter_engine \ + --flutter_root="${flutter_dir}" "$@" diff --git a/tools/update_flutter_engine.bat b/tools/update_flutter_engine.bat index 612eb68ee..4ae0dc0ad 100644 --- a/tools/update_flutter_engine.bat +++ b/tools/update_flutter_engine.bat @@ -11,13 +11,11 @@ :: 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. + +:: Runs update_flutter_engine.dart, using the output of flutter_location.bat as +:: --flutter_root @echo off for /f "delims=" %%i in ('%~dp0flutter_location') do set FLUTTER_DIR=%%i -set FLUTTER_BIN_DIR=%FLUTTER_DIR%\bin -set DART_BIN_DIR=%FLUTTER_BIN_DIR%\cache\dart-sdk\bin - -:: Ensure that the Dart SDK has been downloaded. -if not exist %DART_BIN_DIR%\ call %FLUTTER_BIN_DIR%\flutter precache -call %DART_BIN_DIR%\dart %~dp0.\dart_tools\bin\update_flutter_engine.dart --flutter_root %FLUTTER_DIR% %* +call %~dp0.\run_dart_tool update_flutter_engine --flutter_root %FLUTTER_DIR% %*