From 8835c7c6f377cb9ca1cd5a48a19b5d27373352d2 Mon Sep 17 00:00:00 2001 From: Callum Iddon Date: Sun, 6 Jan 2019 23:08:32 -0500 Subject: [PATCH 01/12] Remove VS Static Library Configs (#209) Addresses #192. --- example/windows/Example Embedder.sln | 10 -- example/windows/GLFW Example.vcxproj | 101 ---------------- library/windows/Flutter Windows Embedder.sln | 6 - library/windows/GLFW Library.vcxproj | 121 ------------------- 4 files changed, 238 deletions(-) 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..8b37e61c3 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,24 +48,12 @@ $(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\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) - Level3 @@ -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/windows/Flutter Windows Embedder.sln b/library/windows/Flutter Windows Embedder.sln index aadfddeb4..221531a1e 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 diff --git a/library/windows/GLFW Library.vcxproj b/library/windows/GLFW Library.vcxproj index f1628bc2d..d5ae00ed9 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,15 +39,9 @@ - - - - - - $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ @@ -79,14 +52,6 @@ $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\json\x64\debug;$(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)\ @@ -96,14 +61,6 @@ $(ProjectDir)dependencies\engine\;$(ProjectDir)dependencies\json\x64\release;;$(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 @@ -143,44 +100,6 @@ 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 - - - - - - - - - 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 @@ -222,46 +141,6 @@ 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 - - - - - - - - - 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 - - From cf3a7919ba038d01b13882ce4440bdc6e9c50685 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 7 Jan 2019 15:26:47 -0800 Subject: [PATCH 02/12] Add a generic run_dart_tool command (#210) Adds a tool to run anything from tools/dart_tools/bin, rather than needing a custom wrapper for each tool. Changes the update_flutter_engine shell script to match the existing batch script behavior of automatically providing --flutter_root --- library/linux/Makefile | 2 +- .../project.pbxproj | 2 +- tools/run_dart_tool | 32 +++++++++++++++++++ tools/run_dart_tool.bat | 26 +++++++++++++++ tools/update_flutter_engine | 17 +++++----- tools/update_flutter_engine.bat | 10 +++--- 6 files changed, 72 insertions(+), 17 deletions(-) create mode 100755 tools/run_dart_tool create mode 100644 tools/run_dart_tool.bat diff --git a/library/linux/Makefile b/library/linux/Makefile index 6a5810892..03b8ce637 100644 --- a/library/linux/Makefile +++ b/library/linux/Makefile @@ -51,7 +51,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/FlutterEmbedderMac.xcodeproj/project.pbxproj b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj index 0b730ac53..77c7171bd 100644 --- a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj +++ b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj @@ -302,7 +302,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 */ 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% %* From 0e59304fcc8affc7d9bef33a3b02e3631e822407 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 7 Jan 2019 20:09:40 -0800 Subject: [PATCH 03/12] Dart analysis-related fixes (#211) * Dart analysis-related fixes - Fixes the include path for the example app's analysis file, which broke during the repository restructure. - Removes analysis options that are obsolete in recent Dart versions. - Fix some new analysis issues (mostly constructor ordering). --- analysis_options.yaml | 5 ---- example/flutter_app/analysis_options.yaml | 2 +- .../flutter_app/lib/keyboard_test_page.dart | 5 ---- example/flutter_app/lib/main.dart | 8 +++---- plugins/color_panel/lib/color_panel.dart | 4 ++-- .../lib/src/channel_controller.dart | 22 ++++++++--------- plugins/menubar/lib/src/menu_channel.dart | 10 ++++---- plugins/menubar/lib/src/menu_item.dart | 24 +++++++++---------- .../dart_tools/bin/update_flutter_engine.dart | 22 +++++++++-------- 9 files changed, 47 insertions(+), 55 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 966fd5318..a05b5eb79 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: @@ -75,7 +71,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/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/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/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/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/tools/dart_tools/bin/update_flutter_engine.dart b/tools/dart_tools/bin/update_flutter_engine.dart index 96c44ad4b..fab1ab6ac 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. @@ -83,16 +84,16 @@ Future main(List arguments) async { exit(1); } - if (parsedArguments['help'] || parsedArguments.rest.length != 1) { + if (parsedArguments['help'] != null || parsedArguments.rest.length != 1) { printUsage(parser); - exit(parsedArguments['help'] ? 0 : 1); + exit(parsedArguments['help'] != null ? 0 : 1); } 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( From 62d3380fc4f3ae579835f875322e6f37b20ae830 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 7 Jan 2019 21:50:20 -0800 Subject: [PATCH 04/12] Revert incorrect change in update_flutter_engine.dart (#213) update_flutter_engine was broken by an incorrect change to try to fix an analyzer warning that didn't end up getting turned on; the default value is false, not null, for the help flag, so the test always passed. --- tools/dart_tools/bin/update_flutter_engine.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/dart_tools/bin/update_flutter_engine.dart b/tools/dart_tools/bin/update_flutter_engine.dart index fab1ab6ac..965ad5532 100644 --- a/tools/dart_tools/bin/update_flutter_engine.dart +++ b/tools/dart_tools/bin/update_flutter_engine.dart @@ -84,9 +84,9 @@ Future main(List arguments) async { exit(1); } - if (parsedArguments['help'] != null || parsedArguments.rest.length != 1) { + if (parsedArguments['help'] || parsedArguments.rest.length != 1) { printUsage(parser); - exit(parsedArguments['help'] != null ? 0 : 1); + exit(parsedArguments['help'] ? 0 : 1); } try { From ebe0af7c395036ae6cbee61d9a192e363410b4c9 Mon Sep 17 00:00:00 2001 From: Callum Iddon Date: Tue, 8 Jan 2019 09:01:50 -0500 Subject: [PATCH 05/12] [Windows] [GN] Windows GN Builds (#191) Adds support for building the Windows embedding library using GN. Rewrites the batch scripts used by the Windows build as Dart scripts, so they can be used from GN. Part of #114 --- BUILD.gn | 10 +- analysis_options.yaml | 3 +- build/BUILD.gn | 33 ++-- build/BUILDCONFIG.gn | 4 +- build/packaging.gni | 28 ++-- build/win/config/BUILD.gn | 33 ++++ build/win/toolchain/BUILD.gn | 105 +++++++++++++ example/windows/GLFW Example.vcxproj | 2 +- library/BUILD.gn | 52 +++++-- library/GN.md | 47 +++++- library/engine.gni | 10 ++ library/linux/BUILD.gn | 12 +- library/windows/BUILD.gn | 85 +++++++++++ library/windows/Flutter Windows Embedder.sln | 2 +- library/windows/GLFW Library.vcxproj | 18 +-- library/windows/GLFW Library.vcxproj.filters | 151 ++++++++++--------- library/windows/dependencies/.gitignore | 9 +- library/windows/glfw.gni | 22 +++ library/windows/jsoncpp.gni | 23 +++ library/windows/scripts/build_jsonlib.bat | 92 +---------- library/windows/scripts/get_GLFW.bat | 24 +-- tools/dart_tools/bin/build_jsoncpp.dart | 77 ++++++++++ tools/dart_tools/bin/fetch_glfw.dart | 95 ++++++++++++ tools/dart_tools/bin/fetch_jsoncpp.dart | 54 +++++++ tools/dart_tools/lib/runCommand.dart | 39 +++++ tools/gn_dart.bat | 26 ++++ 26 files changed, 792 insertions(+), 264 deletions(-) create mode 100644 build/win/config/BUILD.gn create mode 100644 build/win/toolchain/BUILD.gn create mode 100644 library/windows/BUILD.gn create mode 100644 library/windows/glfw.gni create mode 100644 library/windows/jsoncpp.gni create mode 100644 tools/dart_tools/bin/build_jsoncpp.dart create mode 100644 tools/dart_tools/bin/fetch_glfw.dart create mode 100644 tools/dart_tools/bin/fetch_jsoncpp.dart create mode 100644 tools/dart_tools/lib/runCommand.dart create mode 100644 tools/gn_dart.bat 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 a05b5eb79..8e5e464a1 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -25,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 diff --git a/build/BUILD.gn b/build/BUILD.gn index afb87c94b..2c37ee9fb 100644 --- a/build/BUILD.gn +++ b/build/BUILD.gn @@ -13,20 +13,31 @@ # 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", + ] + } } 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/windows/GLFW Example.vcxproj b/example/windows/GLFW Example.vcxproj index 8b37e61c3..42c828237 100644 --- a/example/windows/GLFW Example.vcxproj +++ b/example/windows/GLFW Example.vcxproj @@ -51,7 +51,7 @@ $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ $(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)..\..\library\windows\dependencies\;$(ProjectDir)..\..\;$(IncludePath);$(ProjectDir)..\..\library\windows\ + $(ProjectDir)..\..\library\windows\dependencies\;$(ProjectDir)..\..\library\include\;$(IncludePath) $(ProjectDir)..\..\library\windows\dependencies\GLFW\;$(SolutionDir)bin\$(Platform)\$(Configuration)\GLFW Library\;$(LibraryPath) diff --git a/library/BUILD.gn b/library/BUILD.gn index 605deddc5..69e14093a 100644 --- a/library/BUILD.gn +++ b/library/BUILD.gn @@ -69,31 +69,61 @@ published_shared_library("flutter_embedder") { ":fetch_flutter_engine", ] - if (is_linux) { - public_header_subdir = "flutter_desktop_embedding" + public_header_subdir = "flutter_desktop_embedding" - deps += [ - "//library/linux:publish_flutter_engine", - ] + 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", + ] + + deps += [ + "//library/windows:publish_flutter_engine", + "//library/windows:fetch_glfw", + "//library/windows:build_jsoncpp", + ] - public_configs = [ - "//library/linux:relative_public_headers", + 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 +140,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..416fb19e2 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` if 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/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/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/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 221531a1e..f5baade1a 100644 --- a/library/windows/Flutter Windows Embedder.sln +++ b/library/windows/Flutter Windows Embedder.sln @@ -22,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 d5ae00ed9..eff668a1d 100644 --- a/library/windows/GLFW Library.vcxproj +++ b/library/windows/GLFW Library.vcxproj @@ -48,8 +48,8 @@ $(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) @@ -57,8 +57,8 @@ $(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) @@ -83,8 +83,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 + $(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 @@ -125,7 +125,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 + Get the flutter engine, engine artifacts, GLFW and jsoncpp @@ -159,7 +159,7 @@ - + @@ -167,4 +167,4 @@ - + \ No newline at end of file diff --git a/library/windows/GLFW Library.vcxproj.filters b/library/windows/GLFW Library.vcxproj.filters index 03dd85a33..c7eff2ff6 100644 --- a/library/windows/GLFW Library.vcxproj.filters +++ b/library/windows/GLFW Library.vcxproj.filters @@ -1,74 +1,77 @@ - - - - - {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 + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file 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/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..78d4e623c 100644 --- a/library/windows/scripts/build_jsonlib.bat +++ b/library/windows/scripts/build_jsonlib.bat @@ -12,94 +12,4 @@ :: See the License for the specific language governing permissions and :: limitations under the License. @echo off - -:: Find where VS lives and start a VC command prompt -set pre=Microsoft.VisualStudio.Product. -set ids=%pre%Community %pre%Professional %pre%Enterprise %pre%BuildTools - -pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" -for /f "usebackq tokens=1* delims=: " %%i in (`vswhere -latest -products *`) do (if /i "%%i"=="installationPath" set InstallDir=%%j) -popd - -pushd %InstallDir%\VC\Auxiliary\Build -call vcvarsall.bat x86_amd64 -popd - -set JSONDEBUGLIBEXISTS=true -if not exist %~dp0..\dependencies\json\x64\debug\json_vc71_libmtd.lib set JSONDEBUGLIBEXISTS=false - -set JSONRELEASELIBEXISTS=true -if not exist %~dp0..\dependencies\json\x64\release\json_vc71_libmt.lib set JSONRELEASELIBEXISTS=false - -if %JSONDEBUGLIBEXISTS% == true ( - if %JSONRELEASELIBEXISTS% == true ( - echo jsoncpp found. - goto DONE - ) -) - -set THIRDPARTYDIREXISTS=true -if not exist %~dp0..\third_party set THIRDPARTYDIREXISTS=false - -if %THIRDPARTYDIREXISTS% == false ( - mkdir %~dp0..\third_party -) - -set JSONDIREXISTS=true -if not exist %~dp0..\third_party\jsoncpp set JSONDIREXISTS=false - -if %JSONDIREXISTS% == false ( - mkdir %~dp0..\third_party\jsoncpp -) - -set JSONEXISTS=true -if not exist %~dp0..\third_party\jsoncpp\README.md set JSONEXISTS=false - -:: Clone source -if %JSONEXISTS% == false ( - :: PR opened on json cpp for VS2017 support: https://github.com/open-source-parsers/jsoncpp/pull/853 - echo Cloning via git clone --branch supportvs2017 https://github.com/clarkezone/jsoncpp.git %~dp0..\third_party\jsoncpp - call git clone --branch supportvs2017 https://github.com/clarkezone/jsoncpp.git %~dp0..\third_party\jsoncpp - - pushd %~dp0..\third_party\jsoncpp - - call git checkout 3ae7e8073a425c93329c8577a3c813c206322ca4 - - popd -) - -:: Build debug lib -echo Building debug lib: msbuild %~dp0..\third_party\jsoncpp\makefiles\msvc2017\lib_json.vcxproj -msbuild %~dp0..\third_party\jsoncpp\makefiles\msvc2017\lib_json.vcxproj - -set DEPBINDIREXISTS=true -if not exist %~dp0..\dependencies\json\x64 set DEPBINDIREXISTS=false - -if %DEPBINDIREXISTS% == false ( - mkdir %~dp0..\dependencies\json\x64 -) - -set DEPBINDBGDIREXISTS=true -if not exist %~dp0..\dependencies\json\x64\debug set DEPBINDBGDIREXISTS=false - -if %DEPBINDBGDIREXISTS% == false ( - mkdir %~dp0..\dependencies\json\x64\debug -) - -copy %~dp0..\third_party\jsoncpp\makefiles\msvc2017\x64\debug\json_vc71_libmtd.lib %~dp0..\dependencies\json\x64\debug\. - -:: Build release lib -echo Building release lib: msbuild %~dp0..\third_party\jsoncpp\makefiles\msvc2017\lib_json.vcxproj /p:Configuration=Release -msbuild %~dp0..\third_party\jsoncpp\makefiles\msvc2017\lib_json.vcxproj /p:Configuration=Release - -set DEPBINRELDIREXISTS=true -if not exist %~dp0..\dependencies\json\x64\release set DEPBINRELDIREXISTS=false - -if %DEPBINRELDIREXISTS% == false ( - mkdir %~dp0..\dependencies\json\x64\release -) - -copy %~dp0..\third_party\jsoncpp\makefiles\msvc2017\x64\release\json_vc71_libmt.lib %~dp0..\dependencies\json\x64\release\. - -:DONE -echo jsoncpplib complete. \ No newline at end of file +%~dp0..\..\..\tools\run_dart_tool.bat build_jsoncpp %~dp0..\third_party\jsoncpp %~dp0..\dependencies\JSON %* \ No newline at end of file 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/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..b5df870d1 --- /dev/null +++ b/tools/dart_tools/bin/fetch_jsoncpp.dart @@ -0,0 +1,54 @@ +// 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]; + + await runCommand('git', [ + 'clone', + gitRepo, + downloadDirectory, + ]); + + await runCommand( + 'git', + [ + 'checkout', + pinnedVersion, + ], + workingDirectory: downloadDirectory); +} 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 %* From 040f84dc8634e26fae6f0818bfb2485be49186e7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 8 Jan 2019 08:54:47 -0800 Subject: [PATCH 06/12] Fix header guard (#214) The header guard for key_event_handler.h didn't actually work due to a typo. --- library/common/glfw/key_event_handler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" From 3c621203bf00abb615462d9fce1b79108cfeba8f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 8 Jan 2019 10:37:42 -0800 Subject: [PATCH 07/12] Fix Windows VS build (#215) * Add checkout of jsoncpp back to the VS build script * Make fetch_jsoncpp handle the case where the source is already present --- library/windows/scripts/build_jsonlib.bat | 8 +++++++- tools/dart_tools/bin/fetch_jsoncpp.dart | 14 +++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/library/windows/scripts/build_jsonlib.bat b/library/windows/scripts/build_jsonlib.bat index 78d4e623c..81253ddf8 100644 --- a/library/windows/scripts/build_jsonlib.bat +++ b/library/windows/scripts/build_jsonlib.bat @@ -12,4 +12,10 @@ :: See the License for the specific language governing permissions and :: limitations under the License. @echo off -%~dp0..\..\..\tools\run_dart_tool.bat build_jsoncpp %~dp0..\third_party\jsoncpp %~dp0..\dependencies\JSON %* \ No newline at end of file + +set RUN_DART_TOOL=%~dp0..\..\..\tools\run_dart_tool.bat +set CHECKOUT_DIR=%~dp0..\third_party\jsoncpp +set ARTIFACT_DIR=%~dp0..\dependencies\JSON + +call %RUN_DART_TOOL% fetch_jsoncpp %CHECKOUT_DIR% +call %RUN_DART_TOOL% build_jsoncpp %CHECKOUT_DIR% %ARTIFACT_DIR% %* diff --git a/tools/dart_tools/bin/fetch_jsoncpp.dart b/tools/dart_tools/bin/fetch_jsoncpp.dart index b5df870d1..05937d543 100644 --- a/tools/dart_tools/bin/fetch_jsoncpp.dart +++ b/tools/dart_tools/bin/fetch_jsoncpp.dart @@ -38,11 +38,15 @@ Future main(List arguments) async { final downloadDirectory = arguments[0]; - await runCommand('git', [ - 'clone', - gitRepo, - downloadDirectory, - ]); + if (Directory(downloadDirectory).existsSync()) { + print('$downloadDirectory already exists; skipping clone'); + } else { + await runCommand('git', [ + 'clone', + gitRepo, + downloadDirectory, + ]); + } await runCommand( 'git', From 7300622dd5d2b5bf86072e62057ca3030fa4c257 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 8 Jan 2019 10:42:38 -0800 Subject: [PATCH 08/12] [linux/windows] Align C++ plugin structure with Flutter (#183) Eliminates Plugin as the primary interface point, and instead moves to a MethodChannel-based model. This eliminates the need for void* types in the interfaces, allowing a move to full type safety (via templates) for all the classes related to channels. Additionally, the plugin creation and registration system now matches the Flutter structure on iOS and Android, using static registration of a plugin class with a PluginRegistrar. The desktop registrar currently has minimal functionality, but it can be extended over time. This is a breaking change for any Plugin implementation, as well as application-level code that was calling AddPlugin. This essentially completes issue #102 for Linux and Windows. --- example/linux/flutter_embedder_example.cc | 13 ++- library/BUILD.gn | 15 +--- .../{internal => }/engine_method_result.cc | 34 +++---- library/common/glfw/embedder.cc | 36 ++++---- library/common/glfw/text_input_plugin.cc | 35 +++++--- library/common/glfw/text_input_plugin.h | 22 ++--- .../common/internal/engine_method_result.h | 57 ------------ library/common/internal/plugin_handler.cc | 36 ++++---- library/common/internal/plugin_handler.h | 47 +++++----- library/common/json_method_call.cc | 30 ------- library/common/json_method_codec.cc | 34 ++++--- library/common/json_plugin.cc | 51 ----------- library/common/method_call.cc | 23 ----- library/common/method_channel.cc | 56 ------------ library/common/method_codec.cc | 41 --------- library/common/method_result.cc | 32 ------- library/common/plugin.cc | 39 -------- .../engine_method_result.h | 90 +++++++++++++++++++ .../flutter_desktop_embedding/glfw/embedder.h | 14 +-- .../json_method_call.h | 53 ----------- .../json_method_codec.h | 16 ++-- .../flutter_desktop_embedding/json_plugin.h | 67 -------------- .../flutter_desktop_embedding/method_call.h | 30 ++++--- .../method_channel.h | 56 ++++++++---- .../flutter_desktop_embedding/method_codec.h | 35 +++++--- .../flutter_desktop_embedding/method_result.h | 23 ++--- .../flutter_desktop_embedding/plugin.h | 87 ------------------ .../plugin_registrar.h | 63 +++++++++++++ library/windows/GLFW Library.vcxproj | 9 +- library/windows/GLFW Library.vcxproj.filters | 28 +----- .../include/color_panel/color_panel_plugin.h | 30 +++++-- .../linux/src/color_panel_plugin.cc | 50 ++++++++--- .../file_chooser/file_chooser_plugin.h | 31 +++++-- .../linux/src/file_chooser_plugin.cc | 52 ++++++++--- .../linux/include/menubar/menubar_plugin.h | 33 +++++-- plugins/menubar/linux/src/menubar_plugin.cc | 54 ++++++++--- 36 files changed, 594 insertions(+), 828 deletions(-) rename library/common/{internal => }/engine_method_result.cc (56%) delete mode 100644 library/common/internal/engine_method_result.h delete mode 100644 library/common/json_method_call.cc delete mode 100644 library/common/json_plugin.cc delete mode 100644 library/common/method_call.cc delete mode 100644 library/common/method_channel.cc delete mode 100644 library/common/method_codec.cc delete mode 100644 library/common/method_result.cc delete mode 100644 library/common/plugin.cc create mode 100644 library/include/flutter_desktop_embedding/engine_method_result.h delete mode 100644 library/include/flutter_desktop_embedding/json_method_call.h delete mode 100644 library/include/flutter_desktop_embedding/json_plugin.h delete mode 100644 library/include/flutter_desktop_embedding/plugin.h create mode 100644 library/include/flutter_desktop_embedding/plugin_registrar.h 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/library/BUILD.gn b/library/BUILD.gn index 69e14093a..af55b2cf3 100644 --- a/library/BUILD.gn +++ b/library/BUILD.gn @@ -35,33 +35,24 @@ 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/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", ] } 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/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/include/flutter_desktop_embedding/engine_method_result.h b/library/include/flutter_desktop_embedding/engine_method_result.h new file mode 100644 index 000000000..9350942b0 --- /dev/null +++ b/library/include/flutter_desktop_embedding/engine_method_result.h @@ -0,0 +1,90 @@ +// 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_LINUX_SRC_INTERNAL_ENGINE_METHOD_RESULT_H_ +#define LIBRARY_LINUX_SRC_INTERNAL_ENGINE_METHOD_RESULT_H_ + +#include +#include +#include + +#include "binary_messenger.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 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 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/glfw/embedder.h b/library/include/flutter_desktop_embedding/glfw/embedder.h index 669ea9d50..c3d8268b5 100644 --- a/library/include/flutter_desktop_embedding/glfw/embedder.h +++ b/library/include/flutter_desktop_embedding/glfw/embedder.h @@ -27,9 +27,9 @@ #include #ifdef USE_FLATTENED_INCLUDES -#include "plugin.h" +#include "plugin_registrar.h" #else -#include "../plugin.h" +#include "../plugin_registrar.h" #endif namespace flutter_desktop_embedding { @@ -79,11 +79,13 @@ GLFWwindow *CreateFlutterWindowInSnapshotMode( 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. +PluginRegistrar *GetRegistrarForPlugin(GLFWwindow *flutter_window, + const std::string &plugin_name); // Loops on flutter window events until termination. // 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..973a2ec73 100644 --- a/library/include/flutter_desktop_embedding/json_method_codec.h +++ b/library/include/flutter_desktop_embedding/json_method_codec.h @@ -14,15 +14,15 @@ #ifndef LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_METHOD_CODEC_H_ #define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_METHOD_CODEC_H_ +#include + +#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 JsonMethodCodec : public MethodCodec { public: // Returns the shared instance of the codec. static const JsonMethodCodec &GetInstance(); @@ -38,15 +38,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..f329532d5 100644 --- a/library/include/flutter_desktop_embedding/method_call.h +++ b/library/include/flutter_desktop_embedding/method_call.h @@ -11,36 +11,40 @@ // 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 namespace flutter_desktop_embedding { -// An object encapsulating a method call from Flutter. +// An object encapsulating a method call from Flutter whose arguments are of +// type T. +template class 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..f5e276400 100644 --- a/library/include/flutter_desktop_embedding/method_channel.h +++ b/library/include/flutter_desktop_embedding/method_channel.h @@ -14,9 +14,11 @@ #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 "method_call.h" #include "method_codec.h" #include "method_result.h" @@ -26,13 +28,14 @@ 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. +template class MethodChannel { public: // Creates an instance that sends and receives method calls on the channel @@ -40,31 +43,54 @@ class MethodChannel { // // 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..7f7213e34 100644 --- a/library/include/flutter_desktop_embedding/method_codec.h +++ b/library/include/flutter_desktop_embedding/method_codec.h @@ -24,43 +24,58 @@ namespace flutter_desktop_embedding { // Translates between a binary message and higher-level method call and // response/error objects. +template class 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..310883e2b 100644 --- a/library/include/flutter_desktop_embedding/method_result.h +++ b/library/include/flutter_desktop_embedding/method_result.h @@ -20,40 +20,41 @@ 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. +template class 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..6930312f3 --- /dev/null +++ b/library/include/flutter_desktop_embedding/plugin_registrar.h @@ -0,0 +1,63 @@ +// 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" + +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 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 Plugin { + public: + virtual ~Plugin() {} +}; + +} // namespace flutter_desktop_embedding + +#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_PLUGIN_REGISTRAR_H_ diff --git a/library/windows/GLFW Library.vcxproj b/library/windows/GLFW Library.vcxproj index eff668a1d..1d5530036 100644 --- a/library/windows/GLFW Library.vcxproj +++ b/library/windows/GLFW Library.vcxproj @@ -142,21 +142,14 @@ + - - - - - - - - diff --git a/library/windows/GLFW Library.vcxproj.filters b/library/windows/GLFW Library.vcxproj.filters index c7eff2ff6..76cf6e330 100644 --- a/library/windows/GLFW Library.vcxproj.filters +++ b/library/windows/GLFW Library.vcxproj.filters @@ -18,13 +18,10 @@ Source Files - - Source Files - Source Files - + Source Files @@ -33,30 +30,9 @@ Source Files - - Source Files - Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files @@ -74,4 +50,4 @@ Header Files - \ No newline at end of file + 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..6835f18cb 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,20 @@ #include -#include +#include + +#include +#include namespace plugins_color_panel { // A plugin for communicating with a native color picker panel. -class ColorPanelPlugin : public flutter_desktop_embedding::JsonPlugin { +class 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 +40,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/file_chooser/linux/include/file_chooser/file_chooser_plugin.h b/plugins/file_chooser/linux/include/file_chooser/file_chooser_plugin.h index 968a2cf0a..fa78a0d5f 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,38 @@ #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 namespace plugins_file_chooser { // Implements a file chooser plugin. -class FileChooserPlugin : public flutter_desktop_embedding::JsonPlugin { +class 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/menubar/linux/include/menubar/menubar_plugin.h b/plugins/menubar/linux/include/menubar/menubar_plugin.h index 394739c8e..64258f1fc 100644 --- a/plugins/menubar/linux/include/menubar/menubar_plugin.h +++ b/plugins/menubar/linux/include/menubar/menubar_plugin.h @@ -14,22 +14,39 @@ #ifndef PLUGINS_MENUBAR_LINUX_INCLUDE_MENUBAR_MENUBAR_PLUGIN_H_ #define PLUGINS_MENUBAR_LINUX_INCLUDE_MENUBAR_MENUBAR_PLUGIN_H_ -#include +#include + +#include + +#include +#include namespace plugins_menubar { // A plugin to control a native menubar. -class MenubarPlugin : public flutter_desktop_embedding::JsonPlugin { +class 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(); } From 123fea23efcf444d560df2424fa7860eab13d45b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 8 Jan 2019 10:43:35 -0800 Subject: [PATCH 09/12] [macOS] Align FLEPlugin with iOS Flutter (#182) Changes FLEPlugin to be a subset of iOS's FlutterPlugin's API, including changing the plugin creation and registration system. Plugins now create and manage their own channels, instead of relying on FLEViewController for high-level message APIs. This is a breaking change for any FLEPlugin implementation, as well as application-level code that was calling addPlugin:. This essentially completes issue #102 for macOS. --- example/macos/ExampleWindow.swift | 9 +- library/macos/FLEKeyEventPlugin.h | 26 ---- library/macos/FLEKeyEventPlugin.m | 61 -------- library/macos/FLEPlugin.h | 44 ++---- library/macos/FLEPluginRegistrar.h | 48 +++++++ library/macos/FLETextInputPlugin.h | 20 ++- library/macos/FLETextInputPlugin.m | 54 +++++--- library/macos/FLEViewController+Internal.h | 6 - library/macos/FLEViewController.h | 31 +---- library/macos/FLEViewController.m | 131 ++++++++---------- library/macos/FlutterEmbedderMac.h | 1 + .../project.pbxproj | 12 +- .../color_panel/macos/FLEColorPanelPlugin.h | 5 +- .../color_panel/macos/FLEColorPanelPlugin.mm | 34 +++-- .../macos/FLEFileChooserPlugin.mm | 26 +++- plugins/menubar/macos/FLEMenubarPlugin.mm | 27 +++- 16 files changed, 250 insertions(+), 285 deletions(-) delete mode 100644 library/macos/FLEKeyEventPlugin.h delete mode 100644 library/macos/FLEKeyEventPlugin.m create mode 100644 library/macos/FLEPluginRegistrar.h 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/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..1bd853ae9 100644 --- a/library/macos/FlutterEmbedderMac.h +++ b/library/macos/FlutterEmbedderMac.h @@ -23,6 +23,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 77c7171bd..abef783c3 100644 --- a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj +++ b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj @@ -43,12 +43,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 */; }; @@ -105,11 +104,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 = ""; }; @@ -136,8 +134,6 @@ 33C0FA1821B845EF008F8959 /* FLEBasicMessageChannel.m */, 33C0FA1C21B84810008F8959 /* FLEJSONMessageCodec.m */, 33A87EB520F6BCDB0086D21D /* FLEJSONMethodCodec.m */, - 64C76C2620D7BDFB00B16256 /* FLEKeyEventPlugin.h */, - 64C76C2720D7BE2E00B16256 /* FLEKeyEventPlugin.m */, 33C0FA2521B84AA4008F8959 /* FLEMethodCall.m */, 33D7B59820A4F54400296EFC /* FLEMethodChannel.m */, 3389A68C215949CB00A27898 /* FLEMethodError.m */, @@ -176,6 +172,7 @@ 3389A68E215949D600A27898 /* FLEMethodError.h */, 1E24922F1FCF50BE00DD3BBB /* FLEOpenGLContextHandling.h */, 1E2492311FCF50BE00DD3BBB /* FLEPlugin.h */, + 33C0FA2921B865D2008F8959 /* FLEPluginRegistrar.h */, 1E2492321FCF50BE00DD3BBB /* FLEReshapeListener.h */, 1EEF8E051FD1F0C300DD563C /* FLEView.h */, 1E2492351FCF50BE00DD3BBB /* FLEViewController.h */, @@ -198,7 +195,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 33E202A6212BC0ED00337F48 /* FLEKeyEventPlugin.h in Headers */, 33C0FA2621B84AA4008F8959 /* FLEMethodCall.h in Headers */, 1E24923B1FCF50BE00DD3BBB /* FLEPlugin.h in Headers */, 3389A6872159359200A27898 /* FLEBinaryMessenger.h in Headers */, @@ -217,6 +213,7 @@ 33C0FA2221B84810008F8959 /* FLEJSONMessageCodec.h in Headers */, 33A87EB620F6BCDB0086D21D /* FLEMethodCodec.h in Headers */, 3389A68F215949D600A27898 /* FLEMethodError.h in Headers */, + 33C0FA2A21B865D2008F8959 /* FLEPluginRegistrar.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -314,7 +311,6 @@ 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 */, 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/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/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 { From 6d06f19c7ee9b02050f242164d7793d997565cd9 Mon Sep 17 00:00:00 2001 From: Callum Iddon Date: Tue, 8 Jan 2019 16:59:51 -0500 Subject: [PATCH 10/12] Fix Spelling Mistake (#217) --- library/GN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/GN.md b/library/GN.md index 416fb19e2..0ff71d173 100644 --- a/library/GN.md +++ b/library/GN.md @@ -38,7 +38,7 @@ 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` if a placeholder that will eventually +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. From 9098be0e40b52b22f5e5c44496bee800767c600e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 8 Jan 2019 14:32:56 -0800 Subject: [PATCH 11/12] Use visibility/export annotations for C++ code (#216) Adds export macros, and annotations for all symbols that should be public. Switches the default for shared library builds on Linux to visibility=hidden so that missing annotations will be caught on both platforms, rather than just Windows. Fixes issue #208 --- build/BUILD.gn | 2 + library/BUILD.gn | 5 +++ .../binary_messenger.h | 4 +- .../engine_method_result.h | 5 ++- .../flutter_desktop_embedding/fde_export.h | 37 +++++++++++++++++++ .../flutter_desktop_embedding/glfw/embedder.h | 25 +++++++------ .../json_method_codec.h | 3 +- .../flutter_desktop_embedding/method_call.h | 4 +- .../method_channel.h | 3 +- .../flutter_desktop_embedding/method_codec.h | 3 +- .../flutter_desktop_embedding/method_result.h | 4 +- .../plugin_registrar.h | 5 ++- library/linux/Makefile | 3 +- library/windows/GLFW Library.vcxproj | 11 +++--- library/windows/GLFW Library.vcxproj.filters | 5 --- library/windows/exports.def | 22 ----------- plugins/color_panel/BUILD.gn | 5 ++- plugins/color_panel/linux/Makefile | 3 +- .../include/color_panel/color_panel_plugin.h | 9 ++++- plugins/file_chooser/BUILD.gn | 5 ++- plugins/file_chooser/linux/Makefile | 3 +- .../file_chooser/file_chooser_plugin.h | 9 ++++- plugins/menubar/BUILD.gn | 5 ++- plugins/menubar/linux/Makefile | 3 +- .../linux/include/menubar/menubar_plugin.h | 9 ++++- 25 files changed, 128 insertions(+), 64 deletions(-) create mode 100644 library/include/flutter_desktop_embedding/fde_export.h delete mode 100644 library/windows/exports.def diff --git a/build/BUILD.gn b/build/BUILD.gn index 2c37ee9fb..276788b17 100644 --- a/build/BUILD.gn +++ b/build/BUILD.gn @@ -38,6 +38,8 @@ config("shared_library_defaults") { cflags = [ "-shared", "-fPIC", + # Default to hidden for consistency with Windows builds. + "-fvisibility=hidden", ] } } diff --git a/library/BUILD.gn b/library/BUILD.gn index af55b2cf3..e013036f5 100644 --- a/library/BUILD.gn +++ b/library/BUILD.gn @@ -47,6 +47,7 @@ published_shared_library("flutter_embedder") { public += [ "include/flutter_desktop_embedding/binary_messenger.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/method_call.h", "include/flutter_desktop_embedding/method_channel.h", @@ -60,6 +61,10 @@ published_shared_library("flutter_embedder") { ":fetch_flutter_engine", ] + defines = [ + "FLUTTER_DESKTOP_EMBEDDING_IMPL", + ] + public_header_subdir = "flutter_desktop_embedding" public_configs = [ 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 "binary_messenger.h" +#include "fde_export.h" #include "method_codec.h" #include "method_result.h" @@ -28,7 +29,7 @@ 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 ReplyManager { +class FDE_EXPORT ReplyManager { public: ReplyManager(BinaryReply reply_handler_); ~ReplyManager(); @@ -50,7 +51,7 @@ class ReplyManager { // Implemention of MethodResult that sends a response to the Flutter engine // exactly once, encoded using a given codec. template -class EngineMethodResult : public MethodResult { +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 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 c3d8268b5..0b37d1114 100644 --- a/library/include/flutter_desktop_embedding/glfw/embedder.h +++ b/library/include/flutter_desktop_embedding/glfw/embedder.h @@ -27,8 +27,10 @@ #include #ifdef USE_FLATTENED_INCLUDES +#include "fde_export.h" #include "plugin_registrar.h" #else +#include "../fde_export.h" #include "../plugin_registrar.h" #endif @@ -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,7 +75,7 @@ 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); @@ -84,8 +85,8 @@ GLFWwindow *CreateFlutterWindowInSnapshotMode( // // The name must be unique across the application, so the recommended approach // is to use the fully namespace-qualified name of the plugin class. -PluginRegistrar *GetRegistrarForPlugin(GLFWwindow *flutter_window, - const std::string &plugin_name); +FDE_EXPORT PluginRegistrar *GetRegistrarForPlugin( + GLFWwindow *flutter_window, const std::string &plugin_name); // Loops on flutter window events until termination. // @@ -94,7 +95,7 @@ PluginRegistrar *GetRegistrarForPlugin(GLFWwindow *flutter_window, // // 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_codec.h b/library/include/flutter_desktop_embedding/json_method_codec.h index 973a2ec73..9b1978830 100644 --- a/library/include/flutter_desktop_embedding/json_method_codec.h +++ b/library/include/flutter_desktop_embedding/json_method_codec.h @@ -16,13 +16,14 @@ #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. -class JsonMethodCodec : public MethodCodec { +class FDE_EXPORT JsonMethodCodec : public MethodCodec { public: // Returns the shared instance of the codec. static const JsonMethodCodec &GetInstance(); diff --git a/library/include/flutter_desktop_embedding/method_call.h b/library/include/flutter_desktop_embedding/method_call.h index f329532d5..64c06ecec 100644 --- a/library/include/flutter_desktop_embedding/method_call.h +++ b/library/include/flutter_desktop_embedding/method_call.h @@ -17,12 +17,14 @@ #include #include +#include "fde_export.h" + namespace flutter_desktop_embedding { // An object encapsulating a method call from Flutter whose arguments are of // type T. template -class MethodCall { +class FDE_EXPORT MethodCall { public: // Creates a MethodCall with the given name and arguments. explicit MethodCall(const std::string &method_name, diff --git a/library/include/flutter_desktop_embedding/method_channel.h b/library/include/flutter_desktop_embedding/method_channel.h index f5e276400..6016e35d9 100644 --- a/library/include/flutter_desktop_embedding/method_channel.h +++ b/library/include/flutter_desktop_embedding/method_channel.h @@ -19,6 +19,7 @@ #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" @@ -36,7 +37,7 @@ using MethodCallHandler = std::function -class MethodChannel { +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|. diff --git a/library/include/flutter_desktop_embedding/method_codec.h b/library/include/flutter_desktop_embedding/method_codec.h index 7f7213e34..cc8ed2c87 100644 --- a/library/include/flutter_desktop_embedding/method_codec.h +++ b/library/include/flutter_desktop_embedding/method_codec.h @@ -18,6 +18,7 @@ #include #include +#include "fde_export.h" #include "method_call.h" namespace flutter_desktop_embedding { @@ -25,7 +26,7 @@ namespace flutter_desktop_embedding { // Translates between a binary message and higher-level method call and // response/error objects. template -class MethodCodec { +class FDE_EXPORT MethodCodec { public: MethodCodec() = default; virtual ~MethodCodec() = default; diff --git a/library/include/flutter_desktop_embedding/method_result.h b/library/include/flutter_desktop_embedding/method_result.h index 310883e2b..f7a8a5c57 100644 --- a/library/include/flutter_desktop_embedding/method_result.h +++ b/library/include/flutter_desktop_embedding/method_result.h @@ -16,12 +16,14 @@ #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. template -class MethodResult { +class FDE_EXPORT MethodResult { public: MethodResult() = default; virtual ~MethodResult() = default; diff --git a/library/include/flutter_desktop_embedding/plugin_registrar.h b/library/include/flutter_desktop_embedding/plugin_registrar.h index 6930312f3..c64d144f0 100644 --- a/library/include/flutter_desktop_embedding/plugin_registrar.h +++ b/library/include/flutter_desktop_embedding/plugin_registrar.h @@ -17,6 +17,7 @@ #include #include "binary_messenger.h" +#include "fde_export.h" namespace flutter_desktop_embedding { @@ -27,7 +28,7 @@ class Plugin; // 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 PluginRegistrar { +class FDE_EXPORT PluginRegistrar { public: virtual ~PluginRegistrar() {} @@ -53,7 +54,7 @@ class PluginRegistrar { }; // A plugin that can be registered for ownership by a PluginRegistrar. -class Plugin { +class FDE_EXPORT Plugin { public: virtual ~Plugin() {} }; diff --git a/library/linux/Makefile b/library/linux/Makefile index 03b8ce637..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) \ diff --git a/library/windows/GLFW Library.vcxproj b/library/windows/GLFW Library.vcxproj index 1d5530036..f0dbd4f95 100644 --- a/library/windows/GLFW Library.vcxproj +++ b/library/windows/GLFW Library.vcxproj @@ -68,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 @@ -109,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 @@ -154,9 +158,6 @@ - - - diff --git a/library/windows/GLFW Library.vcxproj.filters b/library/windows/GLFW Library.vcxproj.filters index 76cf6e330..c0b1920c8 100644 --- a/library/windows/GLFW Library.vcxproj.filters +++ b/library/windows/GLFW Library.vcxproj.filters @@ -40,11 +40,6 @@ Source Files - - - Source Files - - Header Files 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/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/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 6835f18cb..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 @@ -21,10 +21,17 @@ #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::Plugin { +class COLOR_PANEL_PLUGIN_EXPORT ColorPanelPlugin + : public flutter_desktop_embedding::Plugin { public: static void RegisterWithRegistrar( flutter_desktop_embedding::PluginRegistrar *registrar); 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/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 fa78a0d5f..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 @@ -21,10 +21,17 @@ #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::Plugin { +class FILE_CHOOSER_PLUGIN_EXPORT FileChooserPlugin + : public flutter_desktop_embedding::Plugin { public: static void RegisterWithRegistrar( flutter_desktop_embedding::PluginRegistrar *registrar); 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/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 64258f1fc..97e49a22c 100644 --- a/plugins/menubar/linux/include/menubar/menubar_plugin.h +++ b/plugins/menubar/linux/include/menubar/menubar_plugin.h @@ -21,10 +21,17 @@ #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::Plugin { +class MENUBAR_PLUGIN_EXPORT MenubarPlugin + : public flutter_desktop_embedding::Plugin { public: static void RegisterWithRegistrar( flutter_desktop_embedding::PluginRegistrar *registrar); From a7c25b3aa2af0f167953e618565ce8ed219ed034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=96=E5=8C=97=E6=8D=B7=E6=99=BA=E4=BA=91=E6=8A=80?= =?UTF-8?q?=E6=9C=AF=E6=9C=89=E9=99=90=E5=85=AC=E5=8F=B8?= Date: Wed, 9 Jan 2019 07:51:02 +0800 Subject: [PATCH 12/12] [macOS] Add FLEEventChannel (#174) Adds more parity with mobile Flutter plugin APIs. Fixes #173 --- library/macos/FLEEventChannel.h | 107 ++++++++++++++++++ library/macos/FLEEventChannel.m | 102 +++++++++++++++++ library/macos/FlutterEmbedderMac.h | 1 + .../project.pbxproj | 8 ++ 4 files changed, 218 insertions(+) create mode 100644 library/macos/FLEEventChannel.h create mode 100644 library/macos/FLEEventChannel.m 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/FlutterEmbedderMac.h b/library/macos/FlutterEmbedderMac.h index 1bd853ae9..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" diff --git a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj index abef783c3..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, ); }; }; @@ -90,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 = ""; }; @@ -132,6 +136,7 @@ 1E2492371FCF50BE00DD3BBB /* FlutterEmbedderMac.h */, 1E2492451FCF536200DD3BBB /* Public */, 33C0FA1821B845EF008F8959 /* FLEBasicMessageChannel.m */, + 2CCADF9821C74690001B4026 /* FLEEventChannel.m */, 33C0FA1C21B84810008F8959 /* FLEJSONMessageCodec.m */, 33A87EB520F6BCDB0086D21D /* FLEJSONMethodCodec.m */, 33C0FA2521B84AA4008F8959 /* FLEMethodCall.m */, @@ -163,6 +168,7 @@ children = ( 33C0FA1921B845F0008F8959 /* FLEBasicMessageChannel.h */, 3389A6862159359200A27898 /* FLEBinaryMessenger.h */, + 2CCADF9921C74690001B4026 /* FLEEventChannel.h */, 33C0FA1E21B84810008F8959 /* FLEJSONMessageCodec.h */, 33C0FA1D21B84810008F8959 /* FLEJSONMethodCodec.h */, 33C0FA1F21B84810008F8959 /* FLEMessageCodec.h */, @@ -197,6 +203,7 @@ files = ( 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 */, @@ -317,6 +324,7 @@ 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;