From f26ec2e3c67aa83d83738a7410150d86f2d60b89 Mon Sep 17 00:00:00 2001 From: jbg <39903+jbg@users.noreply.github.com> Date: Tue, 22 Jan 2019 03:53:59 +0800 Subject: [PATCH 01/14] [macOS] Add modifier state to raw key events (#239) --- library/macos/FLEViewController.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/macos/FLEViewController.m b/library/macos/FLEViewController.m index 6b8489f03..ec93a6c07 100644 --- a/library/macos/FLEViewController.m +++ b/library/macos/FLEViewController.m @@ -33,6 +33,12 @@ static const int kDefaultWindowFramebuffer = 0; +// Android KeyEvent constants from https://developer.android.com/reference/android/view/KeyEvent +static const int kAndroidMetaStateShift = 1 << 0; +static const int kAndroidMetaStateAlt = 1 << 1; +static const int kAndroidMetaStateCtrl = 1 << 12; +static const int kAndroidMetaStateMeta = 1 << 16; + #pragma mark - Private interface declaration. /** @@ -376,6 +382,12 @@ - (void)dispatchKeyEvent:(NSEvent *)event ofType:(NSString *)type { @"keymap" : @"android", @"type" : type, @"keyCode" : @(event.keyCode), + @"metaState" : @( + ((event.modifierFlags & NSEventModifierFlagShift) ? kAndroidMetaStateShift : 0) | + ((event.modifierFlags & NSEventModifierFlagOption) ? kAndroidMetaStateAlt : 0) | + ((event.modifierFlags & NSEventModifierFlagControl) ? kAndroidMetaStateCtrl : 0) | + ((event.modifierFlags & NSEventModifierFlagCommand) ? kAndroidMetaStateMeta : 0) + ) }]; } From 3c276ec3f4a569076dadbecd5418983c919629a1 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 22 Jan 2019 10:45:33 -0800 Subject: [PATCH 02/14] Restructure the example app (#242) * Re-organize the example app Instead of making the platform implementations peers of the example Flutter application, make them subdirectories of it. This matches the structure of a standard Flutter application's mobile implementations, and provides a better example of how to use the project to add desktop support to an existing Flutter app. Since interest so far has been for that use case rather than adding Flutter to an existing application (and the current Linux and Windows implementations don't really support the hybrid use case anyway), this should be more reflective of how it would actually be used. * Move Linux build output under example/build/ --- .appveyor.yml | 2 +- .travis.yml | 6 ++-- Debugging.md | 4 +-- example/{flutter_app => }/.gitignore | 0 example/{flutter_app => }/.metadata | 0 example/README.md | 31 +++++++++++------- example/analysis_options.yaml | 1 + example/flutter_app/README.md | 5 --- example/flutter_app/analysis_options.yaml | 1 - .../fonts/Roboto/LICENSE.txt | 0 .../fonts/Roboto/Roboto-Black.ttf | Bin .../fonts/Roboto/Roboto-Bold.ttf | Bin .../fonts/Roboto/Roboto-Light.ttf | Bin .../fonts/Roboto/Roboto-Medium.ttf | Bin .../fonts/Roboto/Roboto-Regular.ttf | Bin .../fonts/Roboto/Roboto-Thin.ttf | Bin .../lib/keyboard_test_page.dart | 0 example/{flutter_app => }/lib/main.dart | 0 example/linux/.gitignore | 1 - example/{linux => linux_fde}/Makefile | 9 +++-- .../flutter_embedder_example.cc | 0 example/{macos => macos_fde}/.gitignore | 0 .../{macos => macos_fde}/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../Base.lproj/MainMenu.xib | 0 .../Example Embedder-Bridging-Header.h | 0 .../ExampleEmbedder.xcodeproj/project.pbxproj | 4 +-- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcschemes/ExampleEmbedder.xcscheme | 0 .../{macos => macos_fde}/ExampleWindow.swift | 0 example/{macos => macos_fde}/Info.plist | 0 example/{flutter_app => }/pubspec.yaml | 11 ++++--- .../{flutter_app => }/test/widget_test.dart | 0 example/{windows => windows_fde}/.gitignore | 0 .../Example Embedder.sln | 0 .../GLFW Example.vcxproj | 0 .../GLFW Example.vcxproj.filters | 0 .../flutter_embedder_example.cpp | 2 +- .../scripts/build_example_app.bat | 2 +- library/GN.md | 2 -- plugins/README.md | 2 +- 42 files changed, 43 insertions(+), 40 deletions(-) rename example/{flutter_app => }/.gitignore (100%) rename example/{flutter_app => }/.metadata (100%) create mode 100644 example/analysis_options.yaml delete mode 100644 example/flutter_app/README.md delete mode 100644 example/flutter_app/analysis_options.yaml rename example/{flutter_app => }/fonts/Roboto/LICENSE.txt (100%) rename example/{flutter_app => }/fonts/Roboto/Roboto-Black.ttf (100%) rename example/{flutter_app => }/fonts/Roboto/Roboto-Bold.ttf (100%) rename example/{flutter_app => }/fonts/Roboto/Roboto-Light.ttf (100%) rename example/{flutter_app => }/fonts/Roboto/Roboto-Medium.ttf (100%) rename example/{flutter_app => }/fonts/Roboto/Roboto-Regular.ttf (100%) rename example/{flutter_app => }/fonts/Roboto/Roboto-Thin.ttf (100%) rename example/{flutter_app => }/lib/keyboard_test_page.dart (100%) rename example/{flutter_app => }/lib/main.dart (100%) delete mode 100644 example/linux/.gitignore rename example/{linux => linux_fde}/Makefile (95%) rename example/{linux => linux_fde}/flutter_embedder_example.cc (100%) rename example/{macos => macos_fde}/.gitignore (100%) rename example/{macos => macos_fde}/AppDelegate.swift (100%) rename example/{macos => macos_fde}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename example/{macos => macos_fde}/Base.lproj/MainMenu.xib (100%) rename example/{macos => macos_fde}/Example Embedder-Bridging-Header.h (100%) rename example/{macos => macos_fde}/ExampleEmbedder.xcodeproj/project.pbxproj (99%) rename example/{macos => macos_fde}/ExampleEmbedder.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename example/{macos => macos_fde}/ExampleEmbedder.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename example/{macos => macos_fde}/ExampleEmbedder.xcodeproj/xcshareddata/xcschemes/ExampleEmbedder.xcscheme (100%) rename example/{macos => macos_fde}/ExampleWindow.swift (100%) rename example/{macos => macos_fde}/Info.plist (100%) rename example/{flutter_app => }/pubspec.yaml (70%) rename example/{flutter_app => }/test/widget_test.dart (100%) rename example/{windows => windows_fde}/.gitignore (100%) rename example/{windows => windows_fde}/Example Embedder.sln (100%) rename example/{windows => windows_fde}/GLFW Example.vcxproj (100%) rename example/{windows => windows_fde}/GLFW Example.vcxproj.filters (100%) rename example/{windows => windows_fde}/flutter_embedder_example.cpp (95%) rename example/{windows => windows_fde}/scripts/build_example_app.bat (88%) diff --git a/.appveyor.yml b/.appveyor.yml index c6debbb57..f92ea68fe 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,4 +20,4 @@ install: - ps: build\ci\install_flutter.ps1 $env:APPVEYOR_BUILD_FOLDER\.. build_script: - - msbuild "example\windows\Example Embedder.sln" + - msbuild "example\windows_fde\Example Embedder.sln" diff --git a/.travis.yml b/.travis.yml index ab49723a9..283338a30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: before_script: - export PATH=$PATH:$TRAVIS_BUILD_DIR/../flutter/bin:$TRAVIS_BUILD_DIR/bin script: - - make -C example/linux + - make -C example/linux_fde - os: linux dist: xenial @@ -39,11 +39,11 @@ matrix: before_script: - export PATH=$PATH:$TRAVIS_BUILD_DIR/bin script: - - make -C example/linux USE_GN=1 + - make -C example/linux_fde USE_GN=1 - os: osx language: objective-c - xcode_project: example/macos/ExampleEmbedder.xcodeproj + xcode_project: example/macos_fde/ExampleEmbedder.xcodeproj xcode_scheme: ExampleEmbedder install: - build/ci/install_flutter $TRAVIS_BUILD_DIR/.. diff --git a/Debugging.md b/Debugging.md index 7e27b3b1b..46fc5cb93 100644 --- a/Debugging.md +++ b/Debugging.md @@ -41,7 +41,7 @@ There are two options: The `flutter attach` command can connect to a desktop Flutter application and provide the same interactive command line that `flutter run` would provide. In the directory of the Flutter portion of your application (e.g., -`/example/flutter_app`) run: +`/example/` for this project's example application) run: ``` $ flutter attach --device-id=flutter-tester --debug-port=49494 @@ -52,7 +52,7 @@ necessary to bypass checks for an attached iOS or Android device. ### VS Code -Open the Flutter portion of your application (e.g., `/example/flutter_app`). +Open the Flutter portion of your application (e.g., `/example/`). Add a [launch configuration](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) like the following, substituting your Observatory port: diff --git a/example/flutter_app/.gitignore b/example/.gitignore similarity index 100% rename from example/flutter_app/.gitignore rename to example/.gitignore diff --git a/example/flutter_app/.metadata b/example/.metadata similarity index 100% rename from example/flutter_app/.metadata rename to example/.metadata diff --git a/example/README.md b/example/README.md index 8f1152c63..1d4c56607 100644 --- a/example/README.md +++ b/example/README.md @@ -3,9 +3,18 @@ This application shows an example of how to use the embedding library on each platform including build dependencies, resource bundling, and using plugins. +In this example, the platform-specific code lives in `_fde`. For +instance, the macOS project is in macos\_fde. This follows the pattern of +the `android/` and `ios/` directories in a typical Flutter application (with +`_fde` suffixes to avoid confusion or collisions if desktop support is added +to Flutter itself). There's no requirement to use the same names in your +project, or even to put them in the Flutter application directory. + The example application is intended to be a starting point, rather than an authoritative example. For instance, you might use a different build system, -package resources differently, etc. +package resources differently, etc. If you are are adding Flutter to an +existing desktop application, you might instead put the Flutter application code +inside your existing project structure. It also serves as a simple test environment for the plugins that are part of this project, and built-in event handling, so is a collection of unrelated @@ -13,26 +22,26 @@ functionality rather than a usable application. ## Building and Running the Example -Since the example is meant to show how the library would actually be used, it -deliberately uses platform-specific build systems that are separate from the -rest of the project's build system. +There is currently no tool that abstracts the platform-specific builds the +way `flutter build` or `flutter run` does for iOS and Android, so you will need +to follow the platform-specific build instructions for your platform below. -The examples do build the library from source, so you will need to ensure you +The examples build the library from source, so you will need to ensure you have all the dependencies for [building the library on your platform](../library/README.md) before continuing. ### Linux -Run `make` under `linux/`. The example binary and its resources will be -in `out/`, and can be run from there: +Run `make -C example/linux_fde/`. The example binary and its resources will be +in `example/build/linux_fde`, and can be run from there: ``` -$ ./out/flutter_embedder_example +$ ./example/build/linux_fde/flutter_embedder_example ``` ### macOS -Open the ExampleEmbedder Xcode project under `macos/`, and build and run the +Open the ExampleEmbedder Xcode project under `macos_fde/`, and build and run the example application target. #### Note @@ -50,11 +59,11 @@ to a XIB in your own project: ### Windows -Open the `Example Embedder` Visual Studio solution file under `windows\` and +Open the `Example Embedder` Visual Studio solution file under `windows_fde\` and build the GLFW Example project. The resulting binary will be in `bin\x64\$(Configuration)\GLFW Example\`. It -currently uses relative paths so must be run from the `windows\` directory: +currently uses relative paths so must be run from the `windows_fde\` directory: ``` > ".\bin\x64\$(Configuration)\GLFW Example\GLFW Example.exe" diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 000000000..5e2133eb6 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1 @@ +include: ../analysis_options.yaml diff --git a/example/flutter_app/README.md b/example/flutter_app/README.md deleted file mode 100644 index 725d0d44a..000000000 --- a/example/flutter_app/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Example Flutter app - -A simple example Flutter app to be run by the embedder examples. - -To run it, build and run the embedder example for your platform. diff --git a/example/flutter_app/analysis_options.yaml b/example/flutter_app/analysis_options.yaml deleted file mode 100644 index f04c6cf0f..000000000 --- a/example/flutter_app/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options.yaml diff --git a/example/flutter_app/fonts/Roboto/LICENSE.txt b/example/fonts/Roboto/LICENSE.txt similarity index 100% rename from example/flutter_app/fonts/Roboto/LICENSE.txt rename to example/fonts/Roboto/LICENSE.txt diff --git a/example/flutter_app/fonts/Roboto/Roboto-Black.ttf b/example/fonts/Roboto/Roboto-Black.ttf similarity index 100% rename from example/flutter_app/fonts/Roboto/Roboto-Black.ttf rename to example/fonts/Roboto/Roboto-Black.ttf diff --git a/example/flutter_app/fonts/Roboto/Roboto-Bold.ttf b/example/fonts/Roboto/Roboto-Bold.ttf similarity index 100% rename from example/flutter_app/fonts/Roboto/Roboto-Bold.ttf rename to example/fonts/Roboto/Roboto-Bold.ttf diff --git a/example/flutter_app/fonts/Roboto/Roboto-Light.ttf b/example/fonts/Roboto/Roboto-Light.ttf similarity index 100% rename from example/flutter_app/fonts/Roboto/Roboto-Light.ttf rename to example/fonts/Roboto/Roboto-Light.ttf diff --git a/example/flutter_app/fonts/Roboto/Roboto-Medium.ttf b/example/fonts/Roboto/Roboto-Medium.ttf similarity index 100% rename from example/flutter_app/fonts/Roboto/Roboto-Medium.ttf rename to example/fonts/Roboto/Roboto-Medium.ttf diff --git a/example/flutter_app/fonts/Roboto/Roboto-Regular.ttf b/example/fonts/Roboto/Roboto-Regular.ttf similarity index 100% rename from example/flutter_app/fonts/Roboto/Roboto-Regular.ttf rename to example/fonts/Roboto/Roboto-Regular.ttf diff --git a/example/flutter_app/fonts/Roboto/Roboto-Thin.ttf b/example/fonts/Roboto/Roboto-Thin.ttf similarity index 100% rename from example/flutter_app/fonts/Roboto/Roboto-Thin.ttf rename to example/fonts/Roboto/Roboto-Thin.ttf diff --git a/example/flutter_app/lib/keyboard_test_page.dart b/example/lib/keyboard_test_page.dart similarity index 100% rename from example/flutter_app/lib/keyboard_test_page.dart rename to example/lib/keyboard_test_page.dart diff --git a/example/flutter_app/lib/main.dart b/example/lib/main.dart similarity index 100% rename from example/flutter_app/lib/main.dart rename to example/lib/main.dart diff --git a/example/linux/.gitignore b/example/linux/.gitignore deleted file mode 100644 index 89f9ac04a..000000000 --- a/example/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -out/ diff --git a/example/linux/Makefile b/example/linux_fde/Makefile similarity index 95% rename from example/linux/Makefile rename to example/linux_fde/Makefile index e09be8d80..9582e7c03 100644 --- a/example/linux/Makefile +++ b/example/linux_fde/Makefile @@ -15,7 +15,8 @@ # Dependency locations PROJECT_ROOT=$(CURDIR)/../.. FLUTTER_EMBEDDER_LIB_DIR=$(PROJECT_ROOT)/library/linux -FLUTTER_APP_DIR=$(PROJECT_ROOT)/example/flutter_app +FLUTTER_APP_DIR=$(CURDIR)/.. +FLUTTER_APP_BUILD_DIR=$(FLUTTER_APP_DIR)/build PLUGINS_DIR=$(PROJECT_ROOT)/plugins TOOLS_DIR=$(PROJECT_ROOT)/tools FLUTTER_DIR=$(shell $(TOOLS_DIR)/flutter_location) @@ -47,10 +48,10 @@ FLUTTER_BIN=$(FLUTTER_DIR)/bin/flutter ICU_DATA_NAME=icudtl.dat ICU_DATA_SOURCE=$(FLUTTER_DIR)/bin/cache/artifacts/engine/linux-x64/$(ICU_DATA_NAME) FLUTTER_ASSETS_NAME=flutter_assets -FLUTTER_ASSETS_SOURCE=$(FLUTTER_APP_DIR)/build/$(FLUTTER_ASSETS_NAME) +FLUTTER_ASSETS_SOURCE=$(FLUTTER_APP_BUILD_DIR)/$(FLUTTER_ASSETS_NAME) # Output bundle structure and targets -OUT_DIR=$(CURDIR)/out +OUT_DIR=$(FLUTTER_APP_BUILD_DIR)/linux_fde OUT_DATA_DIR=$(OUT_DIR)/data OUT_LIB_DIR=$(OUT_DIR)/lib @@ -61,8 +62,6 @@ ALL_LIBS_OUT=$(foreach lib,$(ALL_LIBS),$(OUT_LIB_DIR)/$(notdir $(lib))) # Overrides for the optional GN build. ifdef USE_GN GN_OUT_DIR=$(PROJECT_ROOT)/out -# Use GN's out dir even though this isn't a GN build, to group build output. -OUT_DIR=$(GN_OUT_DIR)/example # The GN build places all libraries at the top level of the output directory. FLUTTER_EMBEDDER_LIB=$(GN_OUT_DIR)/lib$(FLUTTER_EMBEDDER_LIB_NAME).so diff --git a/example/linux/flutter_embedder_example.cc b/example/linux_fde/flutter_embedder_example.cc similarity index 100% rename from example/linux/flutter_embedder_example.cc rename to example/linux_fde/flutter_embedder_example.cc diff --git a/example/macos/.gitignore b/example/macos_fde/.gitignore similarity index 100% rename from example/macos/.gitignore rename to example/macos_fde/.gitignore diff --git a/example/macos/AppDelegate.swift b/example/macos_fde/AppDelegate.swift similarity index 100% rename from example/macos/AppDelegate.swift rename to example/macos_fde/AppDelegate.swift diff --git a/example/macos/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/macos_fde/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/macos/Assets.xcassets/AppIcon.appiconset/Contents.json rename to example/macos_fde/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/macos/Base.lproj/MainMenu.xib b/example/macos_fde/Base.lproj/MainMenu.xib similarity index 100% rename from example/macos/Base.lproj/MainMenu.xib rename to example/macos_fde/Base.lproj/MainMenu.xib diff --git a/example/macos/Example Embedder-Bridging-Header.h b/example/macos_fde/Example Embedder-Bridging-Header.h similarity index 100% rename from example/macos/Example Embedder-Bridging-Header.h rename to example/macos_fde/Example Embedder-Bridging-Header.h diff --git a/example/macos/ExampleEmbedder.xcodeproj/project.pbxproj b/example/macos_fde/ExampleEmbedder.xcodeproj/project.pbxproj similarity index 99% rename from example/macos/ExampleEmbedder.xcodeproj/project.pbxproj rename to example/macos_fde/ExampleEmbedder.xcodeproj/project.pbxproj index 125827f21..e9eadc9ee 100644 --- a/example/macos/ExampleEmbedder.xcodeproj/project.pbxproj +++ b/example/macos_fde/ExampleEmbedder.xcodeproj/project.pbxproj @@ -131,7 +131,7 @@ 33CC10FE2044A7620003C045 /* FlutterEmbedderMac.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FlutterEmbedderMac.xcodeproj; path = ../../library/macos/FlutterEmbedderMac.xcodeproj; sourceTree = ""; }; 33CC11122044BFA00003C045 /* ExampleWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleWindow.swift; sourceTree = ""; }; 33CC11162044C3600003C045 /* Example Embedder-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Example Embedder-Bridging-Header.h"; sourceTree = ""; }; - 33CC112C20461AD40003C045 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = ../../example/flutter_app/build/flutter_assets; sourceTree = ""; }; + 33CC112C20461AD40003C045 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = ../build/flutter_assets; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -355,7 +355,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "DEPOT_ROOT=\"$PROJECT_DIR\"/../..\n\"$DEPOT_ROOT\"/tools/build_flutter_assets \"$DEPOT_ROOT\"/example/flutter_app"; + shellScript = "FDE_ROOT=\"$PROJECT_DIR\"/../..\n\"$FDE_ROOT\"/tools/build_flutter_assets \"$PROJECT_DIR\"/.."; }; /* End PBXShellScriptBuildPhase section */ diff --git a/example/macos/ExampleEmbedder.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/macos_fde/ExampleEmbedder.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/macos/ExampleEmbedder.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to example/macos_fde/ExampleEmbedder.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/macos/ExampleEmbedder.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos_fde/ExampleEmbedder.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/macos/ExampleEmbedder.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to example/macos_fde/ExampleEmbedder.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/macos/ExampleEmbedder.xcodeproj/xcshareddata/xcschemes/ExampleEmbedder.xcscheme b/example/macos_fde/ExampleEmbedder.xcodeproj/xcshareddata/xcschemes/ExampleEmbedder.xcscheme similarity index 100% rename from example/macos/ExampleEmbedder.xcodeproj/xcshareddata/xcschemes/ExampleEmbedder.xcscheme rename to example/macos_fde/ExampleEmbedder.xcodeproj/xcshareddata/xcschemes/ExampleEmbedder.xcscheme diff --git a/example/macos/ExampleWindow.swift b/example/macos_fde/ExampleWindow.swift similarity index 100% rename from example/macos/ExampleWindow.swift rename to example/macos_fde/ExampleWindow.swift diff --git a/example/macos/Info.plist b/example/macos_fde/Info.plist similarity index 100% rename from example/macos/Info.plist rename to example/macos_fde/Info.plist diff --git a/example/flutter_app/pubspec.yaml b/example/pubspec.yaml similarity index 70% rename from example/flutter_app/pubspec.yaml rename to example/pubspec.yaml index 50e2c816f..1111bea12 100644 --- a/example/flutter_app/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,5 +1,5 @@ name: example_flutter -description: A new Flutter project. +description: An example project for flutter-desktop-embedding. dependencies: flutter: @@ -8,12 +8,15 @@ dependencies: cupertino_icons: ^0.1.0 # Desktop embedder plugins. + # Note: In an actual project, these paths would include the + # path from your application to the flutter_desktop_embedding + # checkout. color_panel: - path: ../../plugins/color_panel + path: ../plugins/color_panel file_chooser: - path: ../../plugins/file_chooser + path: ../plugins/file_chooser menubar: - path: ../../plugins/menubar + path: ../plugins/menubar dev_dependencies: flutter_test: diff --git a/example/flutter_app/test/widget_test.dart b/example/test/widget_test.dart similarity index 100% rename from example/flutter_app/test/widget_test.dart rename to example/test/widget_test.dart diff --git a/example/windows/.gitignore b/example/windows_fde/.gitignore similarity index 100% rename from example/windows/.gitignore rename to example/windows_fde/.gitignore diff --git a/example/windows/Example Embedder.sln b/example/windows_fde/Example Embedder.sln similarity index 100% rename from example/windows/Example Embedder.sln rename to example/windows_fde/Example Embedder.sln diff --git a/example/windows/GLFW Example.vcxproj b/example/windows_fde/GLFW Example.vcxproj similarity index 100% rename from example/windows/GLFW Example.vcxproj rename to example/windows_fde/GLFW Example.vcxproj diff --git a/example/windows/GLFW Example.vcxproj.filters b/example/windows_fde/GLFW Example.vcxproj.filters similarity index 100% rename from example/windows/GLFW Example.vcxproj.filters rename to example/windows_fde/GLFW Example.vcxproj.filters diff --git a/example/windows/flutter_embedder_example.cpp b/example/windows_fde/flutter_embedder_example.cpp similarity index 95% rename from example/windows/flutter_embedder_example.cpp rename to example/windows_fde/flutter_embedder_example.cpp index 88789f0e6..89eb21fb8 100644 --- a/example/windows/flutter_embedder_example.cpp +++ b/example/windows_fde/flutter_embedder_example.cpp @@ -29,7 +29,7 @@ int main(int argc, char **argv) { // Start the engine. // TODO: Make paths relative to the executable so it can be run from anywhere. auto window = flutter_desktop_embedding::CreateFlutterWindow( - 640, 480, "..\\..\\example\\flutter_app\\build\\flutter_assets", + 640, 480, "..\\build\\flutter_assets", "..\\..\\library\\windows\\dependencies\\engine\\icudtl.dat", arguments); if (window == nullptr) { flutter_desktop_embedding::FlutterTerminate(); diff --git a/example/windows/scripts/build_example_app.bat b/example/windows_fde/scripts/build_example_app.bat similarity index 88% rename from example/windows/scripts/build_example_app.bat rename to example/windows_fde/scripts/build_example_app.bat index 7a72c1930..470fd0b05 100644 --- a/example/windows/scripts/build_example_app.bat +++ b/example/windows_fde/scripts/build_example_app.bat @@ -12,4 +12,4 @@ :: See the License for the specific language governing permissions and :: limitations under the License. @echo off -%~dp0..\..\..\tools\build_flutter_assets %~dp0..\..\..\example\flutter_app +%~dp0..\..\..\tools\build_flutter_assets %~dp0..\.. diff --git a/library/GN.md b/library/GN.md index d5ca4c2d5..11cf785d0 100644 --- a/library/GN.md +++ b/library/GN.md @@ -79,8 +79,6 @@ $ ninja -C out 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 diff --git a/plugins/README.md b/plugins/README.md index 9235d136c..eda11bb0a 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -57,7 +57,7 @@ plugin you want to use. For instance: See the example application under each platform's directory in the `example` directory to see an example of including optional plugins on that platform. -The Flutter application under `example/flutter_app` shows examples of using +The Flutter application under `example/` shows examples of using optional plugins on the Dart side. ## Writing your own plugins From 54fac78ab4ca5c4e7e0f4171890c7d6fde899479 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 22 Jan 2019 11:09:54 -0800 Subject: [PATCH 03/14] Add error logging to GLFW and Flutter setup (#243) Add error logging when starting the Flutter engine fails (on all platforms), and on GLFW errors. Also exit early with error logging for unrecoverable errors in the GLFW implementation. Fixes #241. --- example/linux_fde/flutter_embedder_example.cc | 4 +++- example/windows_fde/flutter_embedder_example.cpp | 4 +++- library/common/glfw/embedder.cc | 14 +++++++++++--- library/macos/FLEViewController.m | 10 +++++++--- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/example/linux_fde/flutter_embedder_example.cc b/example/linux_fde/flutter_embedder_example.cc index d231cb498..cfbd64f3e 100644 --- a/example/linux_fde/flutter_embedder_example.cc +++ b/example/linux_fde/flutter_embedder_example.cc @@ -54,7 +54,8 @@ std::string GetExecutableDirectory() { int main(int argc, char **argv) { if (!flutter_desktop_embedding::FlutterInit()) { - std::cerr << "Couldn't init GLFW" << std::endl; + std::cerr << "Unable to init GLFW; exiting." << std::endl; + return EXIT_FAILURE; } // Resources are located relative to the executable. @@ -76,6 +77,7 @@ int main(int argc, char **argv) { 640, 480, assets_path, icu_data_path, arguments); if (window == nullptr) { flutter_desktop_embedding::FlutterTerminate(); + std::cerr << "Unable to create Flutter window; exiting." << std::endl; return EXIT_FAILURE; } diff --git a/example/windows_fde/flutter_embedder_example.cpp b/example/windows_fde/flutter_embedder_example.cpp index 89eb21fb8..9ea8d70a7 100644 --- a/example/windows_fde/flutter_embedder_example.cpp +++ b/example/windows_fde/flutter_embedder_example.cpp @@ -19,7 +19,8 @@ int main(int argc, char **argv) { if (!flutter_desktop_embedding::FlutterInit()) { - std::cout << "Couldn't init GLFW" << std::endl; + std::cerr << "Unable to init GLFW; exiting." << std::endl; + return EXIT_FAILURE; } // Arguments for the Flutter Engine. std::vector arguments; @@ -33,6 +34,7 @@ int main(int argc, char **argv) { "..\\..\\library\\windows\\dependencies\\engine\\icudtl.dat", arguments); if (window == nullptr) { flutter_desktop_embedding::FlutterTerminate(); + std::cerr << "Unable to create Flutter window; exiting." << std::endl; return EXIT_FAILURE; } diff --git a/library/common/glfw/embedder.cc b/library/common/glfw/embedder.cc index b5d926940..17eada1a0 100644 --- a/library/common/glfw/embedder.cc +++ b/library/common/glfw/embedder.cc @@ -239,6 +239,10 @@ static void *GLFWProcResolver(void *user_data, const char *name) { return reinterpret_cast(glfwGetProcAddress(name)); } +static void GLFWErrorCallback(int error_code, const char *description) { + std::cerr << "GLFW error " << error_code << ": " << description << std::endl; +} + // Spins up an instance of the Flutter Engine. // // This function launches the Flutter Engine in a background thread, supplying @@ -276,6 +280,8 @@ static FlutterEngine RunFlutterEngine( auto result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &args, window, &engine); if (result != kSuccess || engine == nullptr) { + std::cerr << "Failed to start Flutter engine: error " << result + << std::endl; return nullptr; } return engine; @@ -283,10 +289,12 @@ static FlutterEngine RunFlutterEngine( namespace flutter_desktop_embedding { -// Initialize glfw -bool FlutterInit() { return glfwInit(); } +bool FlutterInit() { + // Before making any GLFW calls, set up a logging error handler. + glfwSetErrorCallback(GLFWErrorCallback); + return glfwInit(); +} -// Tear down glfw void FlutterTerminate() { glfwTerminate(); } PluginRegistrar *GetRegistrarForPlugin(GLFWwindow *flutter_window, diff --git a/library/macos/FLEViewController.m b/library/macos/FLEViewController.m index ec93a6c07..7a14ededa 100644 --- a/library/macos/FLEViewController.m +++ b/library/macos/FLEViewController.m @@ -297,10 +297,14 @@ - (BOOL)launchEngineInternalWithAssetsPath:(NSURL *)assets flutterArguments.command_line_argv = argv; flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage; - BOOL result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &flutterArguments, - (__bridge void *)(self), &_engine) == kSuccess; + FlutterResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &flutterArguments, + (__bridge void *)(self), &_engine); free(argv); - return result; + if (result != kSuccess) { + NSLog(@"Failed to start Flutter engine: error %d", result); + return NO; + } + return YES; } + (FlutterRendererConfig)createRenderConfigHeadless:(BOOL)headless { From 255592da731e56c8cb4e9d8b97639ce0036ef0bd Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 22 Jan 2019 11:13:54 -0800 Subject: [PATCH 04/14] [linux] Improve example Makefile (#244) - Clearly separates out and documents the variables that are most likely to need to change if copying the example app. - Better differentiate between paths that are relative to the example and paths that are relative to the FDE repository, since apps using FDE would not be expected to be in the same directory the way the example is. - Add a minimal "release" mode that enables that flag to disable Dart asserts, as on macOS and Windows. - Remove the output directory override that puts the example app in the same output directory as the GN build (when building with GN). Combining them makes it less clear what's specific to the example, and what's part of building the library itself. --- example/README.md | 9 ++++++++- example/linux_fde/Makefile | 38 +++++++++++++++++++++++++++----------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/example/README.md b/example/README.md index 1d4c56607..dc732378a 100644 --- a/example/README.md +++ b/example/README.md @@ -36,7 +36,14 @@ Run `make -C example/linux_fde/`. The example binary and its resources will be in `example/build/linux_fde`, and can be run from there: ``` -$ ./example/build/linux_fde/flutter_embedder_example +$ ./example/build/linux_fde/debug/flutter_embedder_example +``` + +To build a version with Dart asserts disabled (and thus no DEBUG banner), +run `make BUILD=release` instead, then launch it with: + +``` +$ ./example/build/linux_fde/release/flutter_embedder_example ``` ### macOS diff --git a/example/linux_fde/Makefile b/example/linux_fde/Makefile index 9582e7c03..c9073a6e7 100644 --- a/example/linux_fde/Makefile +++ b/example/linux_fde/Makefile @@ -12,13 +12,30 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Example-specific variables. +# To modify this Makefile for a different application, these are the values +# that are mostly likely to need to be changed. + +# The location of the flutter-desktop-embedding repository. +FDE_ROOT=$(CURDIR)/../.. +# The C++ code for the embedder application. +SOURCES=flutter_embedder_example.cc +# Plugins to include (from the flutter-desktop-embedding plugins/ directory). +PLUGIN_NAMES=color_panel file_chooser menubar + + +# Default build type. For a release build, set BUILD=release. +# Currently this only sets NDEBUG, which is used to control the flags passed +# to the Flutter engine in the example shell, and not the complation settings +# (e.g., optimization level) of the C++ code. +BUILD:=debug + # Dependency locations -PROJECT_ROOT=$(CURDIR)/../.. -FLUTTER_EMBEDDER_LIB_DIR=$(PROJECT_ROOT)/library/linux FLUTTER_APP_DIR=$(CURDIR)/.. FLUTTER_APP_BUILD_DIR=$(FLUTTER_APP_DIR)/build -PLUGINS_DIR=$(PROJECT_ROOT)/plugins -TOOLS_DIR=$(PROJECT_ROOT)/tools +FLUTTER_EMBEDDER_LIB_DIR=$(FDE_ROOT)/library/linux +PLUGINS_DIR=$(FDE_ROOT)/plugins +TOOLS_DIR=$(FDE_ROOT)/tools FLUTTER_DIR=$(shell $(TOOLS_DIR)/flutter_location) # Libraries @@ -28,7 +45,6 @@ FLUTTER_EMBEDDER_LIB=$(FLUTTER_EMBEDDER_LIB_DIR)/lib$(FLUTTER_EMBEDDER_LIB_NAME) FLUTTER_ENGINE_LIB_NAME=flutter_engine FLUTTER_ENGINE_LIB=$(FLUTTER_EMBEDDER_LIB_DIR)/lib$(FLUTTER_ENGINE_LIB_NAME).so -PLUGIN_NAMES=color_panel file_chooser menubar PLUGIN_LIB_NAME_PREFIX=flutter_embedder_ PLUGIN_LIBS=$(foreach plugin,$(PLUGIN_NAMES)\ ,$(PLUGINS_DIR)/$(plugin)/linux/lib$(PLUGIN_LIB_NAME_PREFIX)$(plugin).so) @@ -38,7 +54,7 @@ ALL_LIBS=$(FLUTTER_EMBEDDER_LIB) $(FLUTTER_ENGINE_LIB) $(PLUGIN_LIBS) # Headers PLUGIN_DIRS=$(patsubst %,$(PLUGINS_DIR)/%/linux,$(PLUGIN_NAMES)) LIBRARY_DIRS=$(FLUTTER_EMBEDDER_LIB_DIR) $(PLUGIN_DIRS) -INCLUDE_DIRS=$(patsubst %,%/include,$(LIBRARY_DIRS)) $(PROJECT_ROOT)/library/include +INCLUDE_DIRS=$(patsubst %,%/include,$(LIBRARY_DIRS)) $(FDE_ROOT)/library/include # Tools BUILD_ASSETS_BIN=$(TOOLS_DIR)/build_flutter_assets @@ -51,7 +67,7 @@ FLUTTER_ASSETS_NAME=flutter_assets FLUTTER_ASSETS_SOURCE=$(FLUTTER_APP_BUILD_DIR)/$(FLUTTER_ASSETS_NAME) # Output bundle structure and targets -OUT_DIR=$(FLUTTER_APP_BUILD_DIR)/linux_fde +OUT_DIR=$(FLUTTER_APP_BUILD_DIR)/linux_fde/$(BUILD) OUT_DATA_DIR=$(OUT_DIR)/data OUT_LIB_DIR=$(OUT_DIR)/lib @@ -61,7 +77,7 @@ ALL_LIBS_OUT=$(foreach lib,$(ALL_LIBS),$(OUT_LIB_DIR)/$(notdir $(lib))) # Overrides for the optional GN build. ifdef USE_GN -GN_OUT_DIR=$(PROJECT_ROOT)/out +GN_OUT_DIR=$(FDE_ROOT)/out # The GN build places all libraries at the top level of the output directory. FLUTTER_EMBEDDER_LIB=$(GN_OUT_DIR)/lib$(FLUTTER_EMBEDDER_LIB_NAME).so @@ -82,7 +98,9 @@ endif # Build settings CXX=g++ -std=c++14 -CXXFLAGS=-Wall -Werror $(shell pkg-config --cflags jsoncpp glfw3) +CXXFLAGS.release=-DNDEBUG +CXXFLAGS=-Wall -Werror $(shell pkg-config --cflags jsoncpp glfw3) \ + $(CXXFLAGS.$(BUILD)) CPPFLAGS=$(patsubst %,-I%,$(INCLUDE_DIRS)) ifdef USE_GN CPPFLAGS+=-DUSE_FLATTENED_INCLUDES @@ -94,8 +112,6 @@ LDFLAGS=-L$(OUT_LIB_DIR) \ $(patsubst %,-l$(PLUGIN_LIB_NAME_PREFIX)%,$(PLUGIN_NAMES)) \ -Wl,-rpath=\$$ORIGIN/lib -SOURCES=flutter_embedder_example.cc - # Targets .PHONY: all From c97b76ea077f0abfa1737ec28d50988722d9d555 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 22 Jan 2019 11:57:29 -0800 Subject: [PATCH 05/14] Address the 'How do I just use it' FAQ (#246) Adds documentation targetted at someone who has found the project hoping to be able to run a command or two and have desktop support in their existing Flutter application. Attempts to set expectations, but also provide some more information about how someone could potentially get started even without experience in desktop development. --- Flutter-Requirements.md | 10 +++++++++ Quick-Start.md | 48 +++++++++++++++++++++++++++++++++++++++++ README.md | 3 +++ 3 files changed, 61 insertions(+) create mode 100644 Quick-Start.md diff --git a/Flutter-Requirements.md b/Flutter-Requirements.md index cdfcf43b8..8074f8ac0 100644 --- a/Flutter-Requirements.md +++ b/Flutter-Requirements.md @@ -62,3 +62,13 @@ on all platforms. Symptoms of missing fonts can include text failing to display, console logging about failure to load fonts, or in some cases crashes. + +## Plugins + +If your project uses any plugins with platform components, they won't +work, as the native side will be missing. Depending on how the Dart side of the +plugin is written, they may fail gracefully, or may throw errors. + +You may need to make the calls to those plugins conditional based on the host +platform. Alternately, if you have the expertise, you could implement the native +side of the plugin in your desktop project(s). diff --git a/Quick-Start.md b/Quick-Start.md new file mode 100644 index 000000000..8c6c49a00 --- /dev/null +++ b/Quick-Start.md @@ -0,0 +1,48 @@ +# Quick Start + +A common question for people discovering this project is: How do I easily add +desktop support to my existing Flutter application? + +The answer is that at this point, you don't. The project is still in early +stages, and a lot of things are still in flux; if you don't already have +experience doing desktop development on the platform(s) you want to add, +this project is probably not ready for you to use it yet. The focus is currently +on improving core functionality, not on ease of use. Neither the API surface nor +the project structure are stable, and no attempt will be made to provide +supported migration paths as things change. + +However, if you want to try out an existing Flutter application running on the +desktop even with those caveats, and don't have experience with desktop +development, here are two approaches that might work for you. + +With either approach, be sure to follow the [main README](README.md) and +[library README](library/README.md) instructions on setting up prerequisites +and adjusting your Flutter application. + +## Replace the 'example' Flutter Code + +Since `example/` is already configured to run on all the platforms this project +supports, you can swap in your project's Dart code, `pubspec.yaml`, resources, +etc., then follow the [normal directions](example/README.md) for building the +example application on your platform. + +This will be the easiest approach to keep working as the project changes, but +requires that you essentially wrap your whole application in a +flutter-desktop-embedding checkout. + +## Copy the '\*\_fde' Directories + +Starting from the example projects means you don't have to create projects from +scratch, and since they are self-contained they can be added to an existing +project without needing to move it. However, because the projects build +the flutter-desktop-embedding libraries from source, they contain relative paths +to the flutter-desktop-embedding projects and tools they depend on. You will +need to update those paths in order for the projects to work. On Linux, the +variables you will need to change are documented in the Makefile. On macOS and +Windows, you will need some familiarity with Xcode and Visual Studio +respectively to make the changes. + +With this approach, you should expect breakage when you update the +flutter-desktop-embedding reposity; when that happens you will need to look at +what has changed in the exmaple projects and update your copies accordingly, or +start over with fresh copies and adjust the paths again. diff --git a/README.md b/README.md index 36fd1f8ec..d8bd010f5 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ optional plugins to access other native platform functionality. ## How to Use This Code +_If you have an existing Flutter app and just want to get it running, see +the [quick start](Quick-Start.md) page before continuing._ + ### Setting Up The tooling and build infrastructure for this project requires that you have From 972a5fc554cf61cf176ad3330c465a99a945839d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 22 Jan 2019 13:51:16 -0800 Subject: [PATCH 06/14] README typo fix --- Quick-Start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quick-Start.md b/Quick-Start.md index 8c6c49a00..bf23f2df8 100644 --- a/Quick-Start.md +++ b/Quick-Start.md @@ -44,5 +44,5 @@ respectively to make the changes. With this approach, you should expect breakage when you update the flutter-desktop-embedding reposity; when that happens you will need to look at -what has changed in the exmaple projects and update your copies accordingly, or +what has changed in the example projects and update your copies accordingly, or start over with fresh copies and adjust the paths again. From 3c375033322cbe7c2081bb600ea69c74d763cdcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Klebba?= Date: Wed, 23 Jan 2019 00:22:54 +0100 Subject: [PATCH 07/14] Update README.md (#247) Replaced URL to Flutter's repo from http to https. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8bd010f5..b03340051 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ a Flutter tree in the same parent directory as the clone of this project: ``` - ├─ flutter (from http://github.com/flutter/flutter) + ├─ flutter (from https://github.com/flutter/flutter) └─ flutter-desktop-embedding (from https://github.com/google/flutter-desktop-embedding) ``` From 5881f2bf2918a75ef124195b6abf60df4d0807c9 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 22 Jan 2019 16:09:56 -0800 Subject: [PATCH 08/14] Add Flutter tree min version check (#248) * Add Flutter tree min version check To minimize confusion, and reduce unnecessary issue reports, add a check during builds that the Flutter tree being used is not older than the last known point where there was a compatibility break. Instead of a runtime error, or an unexplained build error, this will fail out during the engine update step with a clear message to use a newer version of Flutter. Currently, this will ensure that people who aren't on Flutter master will get an error message that's as clear as possible. * Update CI to use sufficiently new versions of Flutter --- .appveyor.yml | 5 ++- build/ci/install_flutter | 4 +-- .../dart_tools/bin/update_flutter_engine.dart | 20 +++++++++++ tools/dart_tools/lib/flutter_utils.dart | 8 +++++ tools/dart_tools/lib/git_utils.dart | 33 +++++++++++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tools/dart_tools/lib/git_utils.dart diff --git a/.appveyor.yml b/.appveyor.yml index f92ea68fe..52efa1940 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,7 +17,10 @@ image: Visual Studio 2017 platform: x64 install: - - ps: build\ci\install_flutter.ps1 $env:APPVEYOR_BUILD_FOLDER\.. + # None of the packaged channels are new enough for the current state + # of FDE, so for now clone master instead. + #- ps: build\ci\install_flutter.ps1 $env:APPVEYOR_BUILD_FOLDER\.. + - git clone -b master https://github.com/flutter/flutter.git %APPVEYOR_BUILD_FOLDER%\..\flutter build_script: - msbuild "example\windows_fde\Example Embedder.sln" diff --git a/build/ci/install_flutter b/build/ci/install_flutter index 9c569fa22..5116aa53b 100755 --- a/build/ci/install_flutter +++ b/build/ci/install_flutter @@ -16,8 +16,8 @@ set -e -readonly CHANNEL="stable" -readonly VERSION="1.0.0" +readonly CHANNEL="dev" +readonly VERSION="1.1.8" if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then readonly FLUTTER_OS="linux" diff --git a/tools/dart_tools/bin/update_flutter_engine.dart b/tools/dart_tools/bin/update_flutter_engine.dart index 3dd08f056..bdb2189da 100644 --- a/tools/dart_tools/bin/update_flutter_engine.dart +++ b/tools/dart_tools/bin/update_flutter_engine.dart @@ -23,6 +23,7 @@ import 'package:path/path.dart' as path; import 'package:archive/archive.dart'; import '../lib/flutter_utils.dart'; +import '../lib/git_utils.dart'; /// The filename stored next to a downloaded engine library to indicate its /// version. @@ -84,6 +85,9 @@ Future main(List arguments) async { 'is present.\n' 'Defaults to a "flutter" directory next to this repository.', defaultsTo: getDefaultFlutterRoot()) + ..addFlag('skip_min_version_check', + help: 'If set, skips the initial check that the Flutter tree whose ' + 'engine version is being fetched is new enough for the framework.') ..addOption( 'hash', // Note: engine_override takes precedence over this flag so that @@ -112,6 +116,22 @@ Future main(List arguments) async { final String flutterRoot = parsedArguments['flutter_root']; final outputRoot = path.canonicalize(path.absolute(parsedArguments.rest[0])); + // TODO: Consider making a setup script that should be run after any update, + // which checks/fetches dependencies, and moving this check there. For now, + // do it here since it's a hook that's run on every build. + if (!parsedArguments['skip_min_version_check']) { + bool containsRequiredCommit = await gitHeadContainsCommit( + flutterRoot, lastKnownRequiredFlutterCommit); + if (!containsRequiredCommit) { + print('Flutter engine update aborted: Your Flutter tree is too ' + 'old for use with this project. Please update to a newer version of ' + 'Flutter, then try again.\n\n' + 'Note that this may require switching to Flutter master. See:\n' + 'https://github.com/flutter/flutter/wiki/Flutter-build-release-channels'); + exit(1); + } + } + final engineOverrideBuildType = await getEngineOverrideBuildType(); if (engineOverrideBuildType == null) { final String targetHash = diff --git a/tools/dart_tools/lib/flutter_utils.dart b/tools/dart_tools/lib/flutter_utils.dart index 3941980b5..509db89b8 100644 --- a/tools/dart_tools/lib/flutter_utils.dart +++ b/tools/dart_tools/lib/flutter_utils.dart @@ -19,6 +19,14 @@ import 'dart:io'; import 'package:path/path.dart' as path; +/// The last Flutter hash that's known to be required; a branch that doesn't +/// contain this commit will either fail to build, or fail to run. +/// +/// This should be updated whenever a new dependency is introduced (e.g., a +/// required embedder API addition or implementation fix). +const String lastKnownRequiredFlutterCommit = + '390ded9340e529b8475fefd1afdbe59c5b8d4081'; + /// Returns the path to the root of this repository. /// /// Relies on the known location of dart_tools/bin within the repo, and the fact diff --git a/tools/dart_tools/lib/git_utils.dart b/tools/dart_tools/lib/git_utils.dart new file mode 100644 index 000000000..553632a38 --- /dev/null +++ b/tools/dart_tools/lib/git_utils.dart @@ -0,0 +1,33 @@ +// 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. + +// Wrappers for git commands used by the tools. + +import 'run_command.dart'; + +/// Returns true if the current +Future gitHeadContainsCommit( + String repositoryRoot, String commitHash) async { + final exitCode = await runCommand( + 'git', + [ + 'merge-base', + '--is-ancestor', + commitHash, + 'HEAD', + ], + workingDirectory: repositoryRoot, + allowFail: true); + return exitCode == 0; +} From 0adf81e21d67e1e0c1397fff708984ed1287179c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 28 Jan 2019 11:04:10 -0800 Subject: [PATCH 09/14] Add a warning about binaries to library README (#250) Call out the requirement that the library and the Flutter application must be build with the same version, since some people have started distributing binary versions apparently without being aware of this. --- library/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/README.md b/library/README.md index 79329f705..98447350f 100644 --- a/library/README.md +++ b/library/README.md @@ -17,6 +17,12 @@ for now there is no equivalent to `flutter create`. There are currently no binary releases of the libraries. While a more Flutter-like model of using an SDK containing pre-compiled binaries is likely to be supported in the future, for now you must build the library from source. +(**Note:** You may be tempted to pre-build a generic binary that can run any +Flutter app. If you do, keep in mind that the primary reason there are no +binary releases is that you *must* use the same version of Flutter to build +`flutter_assets` as you use to build the library. If you later upgrade Flutter, +or if you distribute the binary version to other people building their +applications with different versions of Flutter, it will break.) Once you build the library for your platform, link it into your build using whatever build system you are using, and add the relevant headers (see From 61d651d8db85cab0bc1ef143ff2d176e1afe5d69 Mon Sep 17 00:00:00 2001 From: Francisco Magdaleno Date: Mon, 28 Jan 2019 15:49:31 -0800 Subject: [PATCH 10/14] [macOS] Use FLEMethodError on color panel plugin bad arguments (#252) --- .../color_panel/macos/FLEColorPanelPlugin.mm | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/plugins/color_panel/macos/FLEColorPanelPlugin.mm b/plugins/color_panel/macos/FLEColorPanelPlugin.mm index 7fd97e3f4..68df89039 100644 --- a/plugins/color_panel/macos/FLEColorPanelPlugin.mm +++ b/plugins/color_panel/macos/FLEColorPanelPlugin.mm @@ -24,15 +24,15 @@ @implementation FLEColorPanelPlugin { } + (void)registerWithRegistrar:(id)registrar { - FLEMethodChannel* channel = [FLEMethodChannel - methodChannelWithName:@(plugins_color_panel::kChannelName) - binaryMessenger:registrar.messenger - codec:[FLEJSONMethodCodec sharedInstance]]; - FLEColorPanelPlugin* instance = [[FLEColorPanelPlugin alloc] initWithChannel:channel]; + 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]; } -- (instancetype)initWithChannel:(FLEMethodChannel*)channel { +- (instancetype)initWithChannel:(FLEMethodChannel *)channel { self = [super init]; if (self) { _channel = channel; @@ -45,26 +45,29 @@ - (instancetype)initWithChannel:(FLEMethodChannel*)channel { * panel channel. */ - (void)handleMethodCall:(FLEMethodCall *)call result:(FLEMethodResult)result { - BOOL handled = YES; + id methodResult = nil; if ([call.methodName isEqualToString:@(plugins_color_panel::kShowColorPanelMethod)]) { if ([call.arguments isKindOfClass:[NSDictionary class]]) { BOOL showAlpha = [[call.arguments valueForKey:@(plugins_color_panel::kColorPanelShowAlpha)] boolValue]; [self showColorPanelWithAlpha:showAlpha]; } else { - NSLog(@"Malformed call for %@. Expected an NSDictionary but got %@", - @(plugins_color_panel::kShowColorPanelMethod), - NSStringFromClass([call.arguments class])); - handled = NO; + NSString *errorString = + [NSString stringWithFormat:@"Malformed call for %@. Expected an NSDictionary but got %@", + @(plugins_color_panel::kShowColorPanelMethod), + NSStringFromClass([call.arguments class])]; + methodResult = [[FLEMethodError alloc] initWithCode:@"Bad arguments" + message:errorString + details:nil]; } } else if ([call.methodName isEqualToString:@(plugins_color_panel::kHideColorPanelMethod)]) { [self hideColorPanel]; } else { - handled = NO; + methodResult = FLEMethodNotImplemented; } - // Send an immediate empty success message for handled messages, since the actual color data - // will be provided in follow-up messages. - result(handled ? nil : FLEMethodNotImplemented); + // If no errors are generated, send an immediate empty success message for handled messages, since + // the actual color data will be provided in follow-up messages. + result(methodResult); } /** @@ -114,7 +117,7 @@ - (void)selectedColorDidChange { NSColor *color = [NSColorPanel sharedColorPanel].color; NSDictionary *colorDictionary = [self dictionaryWithColor:color]; [_channel invokeMethod:@(plugins_color_panel::kColorSelectedCallbackMethod) - arguments:colorDictionary]; + arguments:colorDictionary]; } /** @@ -138,8 +141,7 @@ - (NSDictionary *)dictionaryWithColor:(NSColor *)color { - (void)windowWillClose:(NSNotification *)notification { [self removeColorPanelConnections]; - [_channel invokeMethod:@(plugins_color_panel::kClosedCallbackMethod) - arguments:nil]; + [_channel invokeMethod:@(plugins_color_panel::kClosedCallbackMethod) arguments:nil]; } @end From ec38f43ee533ae608c29693c914c671d32f8b3ae Mon Sep 17 00:00:00 2001 From: Francisco Magdaleno Date: Mon, 28 Jan 2019 15:50:00 -0800 Subject: [PATCH 11/14] [linux/windows] Cap the minimum pixel ratio to 1 (#251) Avoids generating a UI that is too small in high resolution monitors. --- library/common/glfw/embedder.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/common/glfw/embedder.cc b/library/common/glfw/embedder.cc index 17eada1a0..84bf75327 100644 --- a/library/common/glfw/embedder.cc +++ b/library/common/glfw/embedder.cc @@ -98,12 +98,15 @@ static void GLFWFramebufferSizeCallback(GLFWwindow *window, int width_px, double dpi = state->window_pixels_per_screen_coordinate * state->monitor_screen_coordinates_per_inch; + // Limit the ratio to 1 to avoid rendering a smaller UI in standard resolution + // monitors. + double pixel_ratio = std::max(dpi / kDpPerInch, 1.0); FlutterWindowMetricsEvent event = {}; event.struct_size = sizeof(event); event.width = width_px; event.height = height_px; - event.pixel_ratio = dpi / kDpPerInch; + event.pixel_ratio = pixel_ratio; FlutterEngineSendWindowMetricsEvent(state->engine, &event); } From 899ad65fa36dc41da31688010a0f9c21a2cc85de Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 28 Jan 2019 16:36:47 -0800 Subject: [PATCH 12/14] [linux/windows] Add FlutterWindowController (#253) Creates a simple C++ object as the primary interaction point for the embedder calls. This provides a simpler API surface than embedder.h, and folds in some common code (e.g., error logging). This also serves to insulate clients from the embedder.h API layer, so that future incremental changes done for #230 will cause less churn for embedders. --- example/linux_fde/flutter_embedder_example.cc | 35 +++---- .../windows_fde/flutter_embedder_example.cpp | 27 +++-- library/BUILD.gn | 2 + library/README.md | 7 +- .../common/glfw/flutter_window_controller.cc | 68 +++++++++++++ .../glfw/flutter_window_controller.h | 89 +++++++++++++++++ library/windows/GLFW Library.vcxproj | 1 + library/windows/GLFW Library.vcxproj.filters | 99 ++++++++++--------- 8 files changed, 243 insertions(+), 85 deletions(-) create mode 100644 library/common/glfw/flutter_window_controller.cc create mode 100644 library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h diff --git a/example/linux_fde/flutter_embedder_example.cc b/example/linux_fde/flutter_embedder_example.cc index cfbd64f3e..2dc82fc99 100644 --- a/example/linux_fde/flutter_embedder_example.cc +++ b/example/linux_fde/flutter_embedder_example.cc @@ -24,9 +24,9 @@ #include #ifdef USE_FLATTENED_INCLUDES -#include +#include #else -#include +#include #endif namespace { @@ -53,11 +53,6 @@ std::string GetExecutableDirectory() { } // namespace int main(int argc, char **argv) { - if (!flutter_desktop_embedding::FlutterInit()) { - std::cerr << "Unable to init GLFW; exiting." << std::endl; - return EXIT_FAILURE; - } - // Resources are located relative to the executable. std::string base_directory = GetExecutableDirectory(); if (base_directory.empty()) { @@ -72,27 +67,27 @@ int main(int argc, char **argv) { #ifdef NDEBUG arguments.push_back("--disable-dart-asserts"); #endif + + flutter_desktop_embedding::FlutterWindowController flutter_controller( + icu_data_path); + // Start the engine. - auto window = flutter_desktop_embedding::CreateFlutterWindow( - 640, 480, assets_path, icu_data_path, arguments); - if (window == nullptr) { - flutter_desktop_embedding::FlutterTerminate(); - std::cerr << "Unable to create Flutter window; exiting." << std::endl; + if (!flutter_controller.CreateWindow(640, 480, assets_path, arguments)) { return EXIT_FAILURE; } // Register any native plugins. plugins_menubar::MenubarPlugin::RegisterWithRegistrar( - flutter_desktop_embedding::GetRegistrarForPlugin( - window, "plugins_menubar::MenubarPlugin")); + flutter_controller.GetRegistrarForPlugin( + "plugins_menubar::MenubarPlugin")); plugins_color_panel::ColorPanelPlugin::RegisterWithRegistrar( - flutter_desktop_embedding::GetRegistrarForPlugin( - window, "plugins_color_panel::ColorPanelPlugin")); + flutter_controller.GetRegistrarForPlugin( + "plugins_color_panel::ColorPanelPlugin")); plugins_file_chooser::FileChooserPlugin::RegisterWithRegistrar( - flutter_desktop_embedding::GetRegistrarForPlugin( - window, "plugins_file_chooser::FileChooserPlugin")); + flutter_controller.GetRegistrarForPlugin( + "plugins_file_chooser::FileChooserPlugin")); - flutter_desktop_embedding::FlutterWindowLoop(window); - glfwTerminate(); + // Run until the window is closed. + flutter_controller.RunEventLoop(); return EXIT_SUCCESS; } diff --git a/example/windows_fde/flutter_embedder_example.cpp b/example/windows_fde/flutter_embedder_example.cpp index 9ea8d70a7..34b8530eb 100644 --- a/example/windows_fde/flutter_embedder_example.cpp +++ b/example/windows_fde/flutter_embedder_example.cpp @@ -13,32 +13,31 @@ // limitations under the License. #include +#include #include -#include "flutter_desktop_embedding/glfw/embedder.h" +#include "flutter_desktop_embedding/glfw/flutter_window_controller.h" int main(int argc, char **argv) { - if (!flutter_desktop_embedding::FlutterInit()) { - std::cerr << "Unable to init GLFW; exiting." << std::endl; - return EXIT_FAILURE; - } + // TODO: Make paths relative to the executable so it can be run from anywhere. + std::string assets_path = "..\\build\\flutter_assets"; + std::string icu_data_path = + "..\\..\\library\\windows\\dependencies\\engine\\icudtl.dat"; + // Arguments for the Flutter Engine. std::vector arguments; #ifndef _DEBUG arguments.push_back("--disable-dart-asserts"); #endif + flutter_desktop_embedding::FlutterWindowController flutter_controller( + icu_data_path); + // Start the engine. - // TODO: Make paths relative to the executable so it can be run from anywhere. - auto window = flutter_desktop_embedding::CreateFlutterWindow( - 640, 480, "..\\build\\flutter_assets", - "..\\..\\library\\windows\\dependencies\\engine\\icudtl.dat", arguments); - if (window == nullptr) { - flutter_desktop_embedding::FlutterTerminate(); - std::cerr << "Unable to create Flutter window; exiting." << std::endl; + if (!flutter_controller.CreateWindow(640, 480, assets_path, arguments)) { return EXIT_FAILURE; } - flutter_desktop_embedding::FlutterWindowLoop(window); - flutter_desktop_embedding::FlutterTerminate(); + // Run until the window is closed. + flutter_controller.RunEventLoop(); return EXIT_SUCCESS; } diff --git a/library/BUILD.gn b/library/BUILD.gn index 12aedfb09..e4d786eec 100644 --- a/library/BUILD.gn +++ b/library/BUILD.gn @@ -21,9 +21,11 @@ published_shared_library("flutter_embedder") { if (is_linux || is_win) { public = [ "include/flutter_desktop_embedding/glfw/embedder.h", + "include/flutter_desktop_embedding/glfw/flutter_window_controller.h", ] sources = [ "common/glfw/embedder.cc", + "common/glfw/flutter_window_controller.cc", "common/glfw/key_event_handler.cc", "common/glfw/key_event_handler.h", "common/glfw/keyboard_hook_handler.h", diff --git a/library/README.md b/library/README.md index 98447350f..2cd62a2a9 100644 --- a/library/README.md +++ b/library/README.md @@ -55,7 +55,8 @@ $ sudo apt-get install libglfw3-dev libepoxy-dev libjsoncpp-dev libgtk-3-dev \ #### Using the Library Run `make` under `linux/`, then link `libflutter_embedder.so` into your -binary. See [embedder.h](include/flutter_desktop_embedding/glfw/embedder.h) +binary. See +[flutter_window_controller.h](include/flutter_desktop_embedding/glfw/flutter_window_controller.h) for details on calling into the library. You will also need to link `libflutter_engine.so` into your binary. @@ -96,8 +97,8 @@ You must have a copy of Visual Studio installed. Build the GLFW Library project under `windows/` in Visual Studio into a static or dynamic library, then link `flutter_embedder.lib` into your binary and make -sure `embedder.h` is in your include paths. Also ensure that the -`flutter_engine.dll`, and if using a dynamic library +sure `flutter_window_controller.h` is in your include paths. Also ensure that +the `flutter_engine.dll`, and if using a dynamic library `flutter_embedder.dll`, are in valid DLL include paths. The output files are located in `bin\x64\$(Configuration)\GLFW Library\`. diff --git a/library/common/glfw/flutter_window_controller.cc b/library/common/glfw/flutter_window_controller.cc new file mode 100644 index 000000000..5e686130e --- /dev/null +++ b/library/common/glfw/flutter_window_controller.cc @@ -0,0 +1,68 @@ +// 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. + +#include "library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h" + +#include + +namespace flutter_desktop_embedding { + +FlutterWindowController::FlutterWindowController(std::string &icu_data_path) + : icu_data_path_(icu_data_path) { + init_succeeded_ = FlutterInit(); +} + +FlutterWindowController::~FlutterWindowController() { + if (init_succeeded_) { + FlutterTerminate(); + } +} + +bool FlutterWindowController::CreateWindow( + size_t width, size_t height, const std::string &assets_path, + const std::vector &arguments) { + if (!init_succeeded_) { + std::cerr << "Could not create window; FlutterInit failed." << std::endl; + return false; + } + + if (window_) { + std::cerr << "Only one Flutter window can exist at a time." << std::endl; + return false; + } + + window_ = CreateFlutterWindow(width, height, assets_path, icu_data_path_, + arguments); + if (!window_) { + std::cerr << "Failed to create window." << std::endl; + return false; + } + return true; +} + +PluginRegistrar *FlutterWindowController::GetRegistrarForPlugin( + const std::string &plugin_name) { + if (!window_) { + return nullptr; + } + return flutter_desktop_embedding::GetRegistrarForPlugin(window_, plugin_name); +} + +void FlutterWindowController::RunEventLoop() { + if (window_) { + FlutterWindowLoop(window_); + } +} + +} // namespace flutter_desktop_embedding diff --git a/library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h b/library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h new file mode 100644 index 000000000..01e0092a0 --- /dev/null +++ b/library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h @@ -0,0 +1,89 @@ +// 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_GLFW_FLUTTER_WINDOW_CONTROLLER_H_ +#define LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_GLFW_FLUTTER_WINDOW_CONTROLLER_H_ + +#include +#include + +#include "embedder.h" + +#ifdef USE_FLATTENED_INCLUDES +#include "fde_export.h" +#include "plugin_registrar.h" +#else +#include "../fde_export.h" +#include "../plugin_registrar.h" +#endif + +namespace flutter_desktop_embedding { + +// A controller for a window displaying Flutter content. +// +// This is the primary wrapper class for the desktop embedding C API. +// If you use this class, you should not call any of the setup or teardown +// methods in embedder.h directly, as this class will do that internally. +// +// Note: This is an early implementation (using GLFW internally) which +// requires control of the application's event loop, and is thus useful +// primarily for building a simple one-window shell hosting a Flutter +// application. The final implementation and API will be very different. +class FDE_EXPORT FlutterWindowController { + public: + // There must be only one instance of this class in an application at any + // given time, as Flutter does not support multiple engines in one process, + // or multiple views in one engine. + explicit FlutterWindowController(std::string &icu_data_path); + + ~FlutterWindowController(); + + // Creates and displays a window for displaying Flutter content. + // + // The |assets_path| is the path to the flutter_assets folder for the Flutter + // application to be run. |icu_data_path| is the path to the icudtl.dat file + // for the version of Flutter you are using. + // + // The |arguments| are passed to the Flutter engine. See: + // https://github.com/flutter/engine/blob/master/shell/common/switches.h for + // for details. Not all arguments will apply to embedding mode. + // + // Only one Flutter window can exist at a time; see constructor comment. + bool CreateWindow(size_t width, size_t height, const std::string &assets_path, + const std::vector &arguments); + + // Returns the PluginRegistrar to register a plugin with the given name. + // + // 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(const std::string &plugin_name); + + // Loops on Flutter window events until the window closes. + void RunEventLoop(); + + private: + // The path to the ICU data file. Set at creation time since it is the same + // for any window created. + std::string icu_data_path_; + + // Whether or not FlutterInit succeeded at creation time. + bool init_succeeded_ = false; + + // The curent Flutter window, if any. + GLFWwindow *window_ = nullptr; +}; + +} // namespace flutter_desktop_embedding + +#endif // LIBRARY_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_GLFW_FLUTTER_WINDOW_CONTROLLER_H_ diff --git a/library/windows/GLFW Library.vcxproj b/library/windows/GLFW Library.vcxproj index d7dfa8a2f..c14747a6e 100644 --- a/library/windows/GLFW Library.vcxproj +++ b/library/windows/GLFW Library.vcxproj @@ -148,6 +148,7 @@ + diff --git a/library/windows/GLFW Library.vcxproj.filters b/library/windows/GLFW Library.vcxproj.filters index bf62d4890..ad4017e40 100644 --- a/library/windows/GLFW Library.vcxproj.filters +++ b/library/windows/GLFW Library.vcxproj.filters @@ -1,48 +1,51 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - + + + + + {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 + + + + + Header Files + + + \ No newline at end of file From 96e92a994312d81703b9903506df531b4973e00f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 29 Jan 2019 13:41:52 -0800 Subject: [PATCH 13/14] Remove GLFW from the API surface (#254) Rather than exposing the GLFWwindow that is used, treat it as an implementation detail and use an opaque pointer specific to this library as the context object instead. In practice the use of GLFW can't be entirely internal since the use of GLFW in the library prevents its use by the embedder, but the details of how it is used should not be exposed. This also gives greater control over the API surface, as part of working toward a stable ABI (#230). This does mean that the embedder cannot manipulate the GLFW window directly, but rather than trust that doing so would be safe across DLL boundaries, any functionality need by the simple embedder use cases this library enables should be exposed via wrappers. --- library/common/glfw/embedder.cc | 55 +++++++++++++------ .../flutter_desktop_embedding/glfw/embedder.h | 44 +++++++-------- .../glfw/flutter_window_controller.h | 2 +- 3 files changed, 58 insertions(+), 43 deletions(-) diff --git a/library/common/glfw/embedder.cc b/library/common/glfw/embedder.cc index 84bf75327..7671e4805 100644 --- a/library/common/glfw/embedder.cc +++ b/library/common/glfw/embedder.cc @@ -20,6 +20,13 @@ #include #include +#ifdef __linux__ +// Epoxy must be included before any graphics-related code. +#include +#endif + +#include + #include #include "library/common/glfw/key_event_handler.h" @@ -48,7 +55,13 @@ static constexpr double kDpPerInch = 160.0; // Struct for storing state within an instance of the GLFW Window. struct FlutterEmbedderState { + // The GLFW window that owns this state object. + GLFWwindow *window; + + // The handle to the Flutter engine instance. FlutterEngine engine; + + // The helper class managing plugin registration and messaging. std::unique_ptr plugin_handler; // Handlers for keyboard events from GLFW. @@ -300,37 +313,42 @@ bool FlutterInit() { void FlutterTerminate() { glfwTerminate(); } -PluginRegistrar *GetRegistrarForPlugin(GLFWwindow *flutter_window, +PluginRegistrar *GetRegistrarForPlugin(FlutterWindowRef 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(); + return flutter_window->plugin_handler.get(); } -GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, - const std::string &assets_path, - const std::string &icu_data_path, - const std::vector &arguments) { +FlutterWindowRef CreateFlutterWindow( + size_t initial_width, size_t initial_height, const std::string &assets_path, + const std::string &icu_data_path, + const std::vector &arguments) { #ifdef __linux__ gtk_init(0, nullptr); #endif + // Create the window. auto window = glfwCreateWindow(initial_width, initial_height, kDefaultWindowTitle, NULL, NULL); if (window == nullptr) { return nullptr; } GLFWClearCanvas(window); + + // Start the engine. auto engine = RunFlutterEngine(window, assets_path, icu_data_path, arguments); if (engine == nullptr) { glfwDestroyWindow(window); return nullptr; } + // Create an embedder state object attached to the window. FlutterEmbedderState *state = new FlutterEmbedderState(); - state->plugin_handler = std::make_unique(engine); + state->window = window; + glfwSetWindowUserPointer(window, state); state->engine = engine; + state->plugin_handler = std::make_unique(engine); // Set up the keyboard handlers. state->keyboard_hook_handlers.push_back( @@ -338,24 +356,26 @@ GLFWwindow *CreateFlutterWindow(size_t initial_width, size_t initial_height, state->keyboard_hook_handlers.push_back( std::make_unique(state->plugin_handler.get())); - glfwSetWindowUserPointer(window, state); - + // Trigger an initial size callback to send size information to Flutter. state->monitor_screen_coordinates_per_inch = GetScreenCoordinatesPerInch(); int width_px, height_px; glfwGetFramebufferSize(window, &width_px, &height_px); - glfwSetFramebufferSizeCallback(window, GLFWFramebufferSizeCallback); GLFWFramebufferSizeCallback(window, width_px, height_px); + // Set up GLFW callbacks for the window. + glfwSetFramebufferSizeCallback(window, GLFWFramebufferSizeCallback); GLFWAssignEventCallbacks(window); - return window; + + return state; } -void FlutterWindowLoop(GLFWwindow *flutter_window) { +void FlutterWindowLoop(FlutterWindowRef flutter_window) { + GLFWwindow *window = flutter_window->window; #ifdef __linux__ // Necessary for GTK thread safety. XInitThreads(); #endif - while (!glfwWindowShouldClose(flutter_window)) { + while (!glfwWindowShouldClose(window)) { #ifdef __linux__ glfwPollEvents(); if (gtk_events_pending()) { @@ -367,10 +387,9 @@ void FlutterWindowLoop(GLFWwindow *flutter_window) { // TODO(awdavies): This will be deprecated soon. __FlutterEngineFlushPendingTasksNow(); } - auto state = GetSavedEmbedderState(flutter_window); - FlutterEngineShutdown(state->engine); - delete state; - glfwDestroyWindow(flutter_window); + FlutterEngineShutdown(flutter_window->engine); + delete flutter_window; + glfwDestroyWindow(window); } } // namespace flutter_desktop_embedding diff --git a/library/include/flutter_desktop_embedding/glfw/embedder.h b/library/include/flutter_desktop_embedding/glfw/embedder.h index 4d485aaff..2ce67e94d 100644 --- a/library/include/flutter_desktop_embedding/glfw/embedder.h +++ b/library/include/flutter_desktop_embedding/glfw/embedder.h @@ -19,13 +19,6 @@ #include #include -#ifdef __linux__ -// Epoxy must be included before any graphics-related code. -#include -#endif - -#include - #ifdef USE_FLATTENED_INCLUDES #include "fde_export.h" #include "plugin_registrar.h" @@ -34,19 +27,23 @@ #include "../plugin_registrar.h" #endif +// Opaque reference to a Flutter window. +typedef struct FlutterEmbedderState *FlutterWindowRef; + namespace flutter_desktop_embedding { -// Calls glfwInit() +// Sets up the embedder's graphic context. Must be called before any other +// methods. // -// glfwInit() must be called in the same library as glfwCreateWindow() +// Note: Internally, this library uses GLFW, which does not support multiple +// copies within the same process. Internally this calls glfwInit, which will +// fail if you have called glfwInit elsewhere in the process. FDE_EXPORT bool FlutterInit(); -// Calls glfwTerminate() -// -// glfwTerminate() must be called in the same library as glfwCreateWindow() +// Tears down embedder state. Must be called before the process terminates. FDE_EXPORT void FlutterTerminate(); -// Creates a GLFW Window running a Flutter Application. +// Creates a Window running a Flutter Application. // // FlutterInit() must be called prior to this function. // @@ -58,9 +55,11 @@ FDE_EXPORT void FlutterTerminate(); // https://github.com/flutter/engine/blob/master/shell/common/switches.h for // for details. Not all arguments will apply to embedding mode. // -// Returns a null pointer in the event of an error. The caller owns the pointer -// when it is non-null. -FDE_EXPORT GLFWwindow *CreateFlutterWindow( +// Returns a null pointer in the event of an error. Otherwise, the pointer is +// valid until FlutterWindowLoop has been called and returned. Note that calling +// CreateFlutterWindow without later calling FlutterWindowLoop on that pointer +// is a memory leak. +FDE_EXPORT FlutterWindowRef CreateFlutterWindow( size_t initial_width, size_t initial_height, const std::string &assets_path, const std::string &icu_data_path, const std::vector &arguments); @@ -71,16 +70,13 @@ FDE_EXPORT GLFWwindow *CreateFlutterWindow( // The name must be unique across the application, so the recommended approach // is to use the fully namespace-qualified name of the plugin class. FDE_EXPORT PluginRegistrar *GetRegistrarForPlugin( - GLFWwindow *flutter_window, const std::string &plugin_name); + FlutterWindowRef flutter_window, const std::string &plugin_name); -// Loops on flutter window events until termination. -// -// Must be used instead of glfwWindowShouldClose as it cleans up engine state -// after termination. +// Loops on Flutter window events until the window is closed. // -// After this function the user must eventually call FlutterTerminate() if doing -// cleanup. -FDE_EXPORT void FlutterWindowLoop(GLFWwindow *flutter_window); +// Once this function returns, FlutterWindowRef is no longer valid, and must +// not be used again. +FDE_EXPORT void FlutterWindowLoop(FlutterWindowRef flutter_window); } // namespace flutter_desktop_embedding diff --git a/library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h b/library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h index 01e0092a0..a1f0819c7 100644 --- a/library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h +++ b/library/include/flutter_desktop_embedding/glfw/flutter_window_controller.h @@ -81,7 +81,7 @@ class FDE_EXPORT FlutterWindowController { bool init_succeeded_ = false; // The curent Flutter window, if any. - GLFWwindow *window_ = nullptr; + FlutterWindowRef window_ = nullptr; }; } // namespace flutter_desktop_embedding From 2106e6375d0abd5d9749e47843a88869cddfc553 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 30 Jan 2019 09:35:50 -0800 Subject: [PATCH 14/14] [macos] Adjust for embedding API type name change (#257) https://github.com/flutter/engine/pull/7567 renamed the type of the result for embedding API calls. Rather than force a Flutter upgrade by using the new type name, temporarily use 'auto' for the type (with a TODO to change it later, since auto is arguably less readable here). This requires converting the file to ObjC++, which required a few explicit casts in places that were previously being silently recast. --- ...EViewController.m => FLEViewController.mm} | 28 ++++++++++--------- .../project.pbxproj | 8 +++--- 2 files changed, 19 insertions(+), 17 deletions(-) rename library/macos/{FLEViewController.m => FLEViewController.mm} (93%) diff --git a/library/macos/FLEViewController.m b/library/macos/FLEViewController.mm similarity index 93% rename from library/macos/FLEViewController.m rename to library/macos/FLEViewController.mm index 7a14ededa..254e82294 100644 --- a/library/macos/FLEViewController.m +++ b/library/macos/FLEViewController.mm @@ -297,7 +297,8 @@ - (BOOL)launchEngineInternalWithAssetsPath:(NSURL *)assets flutterArguments.command_line_argv = argv; flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage; - FlutterResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &flutterArguments, + // TODO: Replace auto with FlutterEngineResult after next required Flutter update. + auto result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &flutterArguments, (__bridge void *)(self), &_engine); free(argv); if (result != kSuccess) { @@ -350,7 +351,8 @@ - (void)handlePlatformMessage:(const FlutterPlatformMessage *)message { FLEBinaryReply binaryResponseHandler = ^(NSData *response) { if (responseHandle) { - FlutterEngineSendPlatformMessageResponse(self->_engine, responseHandle, response.bytes, + FlutterEngineSendPlatformMessageResponse(self->_engine, responseHandle, + static_cast(response.bytes), response.length); responseHandle = NULL; } else { @@ -376,7 +378,7 @@ - (void)dispatchMouseEvent:(NSEvent *)event phase:(FlutterPointerPhase)phase { .phase = phase, .x = locationInBackingCoordinates.x, .y = -locationInBackingCoordinates.y, // convertPointToBacking makes this negative. - .timestamp = event.timestamp * NSEC_PER_MSEC, + .timestamp = static_cast(event.timestamp * NSEC_PER_MSEC), }; FlutterEngineSendPointerEvent(_engine, &flutterEvent, 1); } @@ -386,12 +388,11 @@ - (void)dispatchKeyEvent:(NSEvent *)event ofType:(NSString *)type { @"keymap" : @"android", @"type" : type, @"keyCode" : @(event.keyCode), - @"metaState" : @( - ((event.modifierFlags & NSEventModifierFlagShift) ? kAndroidMetaStateShift : 0) | - ((event.modifierFlags & NSEventModifierFlagOption) ? kAndroidMetaStateAlt : 0) | - ((event.modifierFlags & NSEventModifierFlagControl) ? kAndroidMetaStateCtrl : 0) | - ((event.modifierFlags & NSEventModifierFlagCommand) ? kAndroidMetaStateMeta : 0) - ) + @"metaState" : + @(((event.modifierFlags & NSEventModifierFlagShift) ? kAndroidMetaStateShift : 0) | + ((event.modifierFlags & NSEventModifierFlagOption) ? kAndroidMetaStateAlt : 0) | + ((event.modifierFlags & NSEventModifierFlagControl) ? kAndroidMetaStateCtrl : 0) | + ((event.modifierFlags & NSEventModifierFlagCommand) ? kAndroidMetaStateMeta : 0)) }]; } @@ -404,8 +405,8 @@ - (void)viewDidReshape:(NSOpenGLView *)view { CGRect scaledBounds = [view convertRectToBacking:view.bounds]; const FlutterWindowMetricsEvent event = { .struct_size = sizeof(event), - .width = scaledBounds.size.width, - .height = scaledBounds.size.height, + .width = static_cast(scaledBounds.size.width), + .height = static_cast(scaledBounds.size.height), .pixel_ratio = scaledBounds.size.width / view.bounds.size.width, }; FlutterEngineSendWindowMetricsEvent(_engine, &event); @@ -417,11 +418,12 @@ - (void)sendOnChannel:(nonnull NSString *)channel message:(nullable NSData *)mes FlutterPlatformMessage platformMessage = { .struct_size = sizeof(FlutterPlatformMessage), .channel = [channel UTF8String], - .message = message.bytes, + .message = static_cast(message.bytes), .message_size = message.length, }; - FlutterResult result = FlutterEngineSendPlatformMessage(_engine, &platformMessage); + // TODO: Replace auto with FlutterEngineResult after next required Flutter update. + auto result = FlutterEngineSendPlatformMessage(_engine, &platformMessage); if (result != kSuccess) { NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel, result); } diff --git a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj index 2d953f6b7..bbc1a68f4 100644 --- a/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj +++ b/library/macos/FlutterEmbedderMac.xcodeproj/project.pbxproj @@ -26,7 +26,7 @@ 1E24923C1FCF50BE00DD3BBB /* FLEReshapeListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E2492321FCF50BE00DD3BBB /* FLEReshapeListener.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1E24923E1FCF50BE00DD3BBB /* FLETextInputPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E2492341FCF50BE00DD3BBB /* FLETextInputPlugin.m */; }; 1E24923F1FCF50BE00DD3BBB /* FLEViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E2492351FCF50BE00DD3BBB /* FLEViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1E2492401FCF50BE00DD3BBB /* FLEViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E2492361FCF50BE00DD3BBB /* FLEViewController.m */; }; + 1E2492401FCF50BE00DD3BBB /* FLEViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1E2492361FCF50BE00DD3BBB /* FLEViewController.mm */; }; 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 */; }; @@ -87,7 +87,7 @@ 1E2492331FCF50BE00DD3BBB /* FLETextInputPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLETextInputPlugin.h; sourceTree = ""; }; 1E2492341FCF50BE00DD3BBB /* FLETextInputPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLETextInputPlugin.m; sourceTree = ""; }; 1E2492351FCF50BE00DD3BBB /* FLEViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEViewController.h; sourceTree = ""; }; - 1E2492361FCF50BE00DD3BBB /* FLEViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEViewController.m; sourceTree = ""; }; + 1E2492361FCF50BE00DD3BBB /* FLEViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FLEViewController.mm; sourceTree = ""; }; 1E2492371FCF50BE00DD3BBB /* FlutterEmbedderMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlutterEmbedderMac.h; sourceTree = ""; }; 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 = ""; }; @@ -148,7 +148,7 @@ 1E2492341FCF50BE00DD3BBB /* FLETextInputPlugin.m */, 1EEF8E061FD1F0C300DD563C /* FLEView.m */, 6442F82C20EA6C5F00A393AE /* FLEViewController+Internal.h */, - 1E2492361FCF50BE00DD3BBB /* FLEViewController.m */, + 1E2492361FCF50BE00DD3BBB /* FLEViewController.mm */, 1E2492381FCF50BE00DD3BBB /* Info.plist */, 33B1650E201A5F7400732DC9 /* Dependencies */, 1E2492251FCF504200DD3BBB /* Products */, @@ -316,7 +316,7 @@ buildActionMask = 2147483647; files = ( 33D7B59A20A4F54400296EFC /* FLEMethodChannel.m in Sources */, - 1E2492401FCF50BE00DD3BBB /* FLEViewController.m in Sources */, + 1E2492401FCF50BE00DD3BBB /* FLEViewController.mm in Sources */, 3389A68D215949CB00A27898 /* FLEMethodError.m in Sources */, 33C0FA2721B84AA4008F8959 /* FLEMethodCall.m in Sources */, 33C0FA2021B84810008F8959 /* FLEJSONMessageCodec.m in Sources */,