packages: add flutter_elinux package for platform views (#206)
https://github.com/sony/flutter-embedded-linux/issues/41 Signed-off-by: Hidenori Matsubayashi <[email protected]>
This commit is contained in:
@@ -8,7 +8,7 @@ jobs:
|
|||||||
analysis:
|
analysis:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Fetch Flutter SDK
|
- name: Fetch Flutter SDK
|
||||||
run: |
|
run: |
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
run: flutter/bin/flutter pub get
|
run: flutter/bin/flutter pub get
|
||||||
|
|
||||||
- name: Verify formatting
|
- name: Verify formatting
|
||||||
run: flutter/bin/dart format --output=none --set-exit-if-changed lib
|
run: flutter/bin/dart format --output=none --set-exit-if-changed lib packages
|
||||||
|
|
||||||
- name: Analyze project source
|
- name: Analyze project source
|
||||||
run: flutter/bin/dart analyze --fatal-warnings lib
|
run: flutter/bin/dart analyze --fatal-warnings lib
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Take our settings from the repo's main analysis_options.yaml file, and include
|
||||||
|
# additional rules that are specific to production code.
|
||||||
|
|
||||||
|
include: ../analysis_options.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
- public_member_api_docs # see https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#documentation-dartdocs-javadocs-etc
|
||||||
|
- no_runtimeType_toString # use objectRuntimeType from package:foundation
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
* Initial commit
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# native_texture_view
|
||||||
|
|
||||||
|
This is an example flutter-elinux project for native texture view using platform views.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Please replace the flutter_elinux's `path` in `pubspec.yaml`:
|
||||||
|
|
||||||
|
```
|
||||||
|
flutter_elinux:
|
||||||
|
# You need to correctly replace the path below with the path of your flutter-elinux directory.
|
||||||
|
path: /home/hidenori/work/flutter/flutter-elinux/packages/flutter_elinux
|
||||||
|
```
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at https://dart.dev/lints.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
flutter/ephemeral/
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
# stop cmake from taking make from CMAKE_SYSROOT
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
project(runner LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(BINARY_NAME "texture")
|
||||||
|
|
||||||
|
cmake_policy(SET CMP0063 NEW)
|
||||||
|
|
||||||
|
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
|
||||||
|
|
||||||
|
# Basically we use this include when we got the following error:
|
||||||
|
# fatal error: 'bits/c++config.h' file not found
|
||||||
|
include_directories(SYSTEM ${FLUTTER_SYSTEM_INCLUDE_DIRECTORIES})
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
|
||||||
|
# Configure build options.
|
||||||
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
|
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||||
|
STRING "Flutter build mode" FORCE)
|
||||||
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||||
|
"Debug" "Profile" "Release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Configure build option to target backend.
|
||||||
|
if (NOT FLUTTER_TARGET_BACKEND_TYPE)
|
||||||
|
set(FLUTTER_TARGET_BACKEND_TYPE "wayland" CACHE
|
||||||
|
STRING "Flutter target backend type" FORCE)
|
||||||
|
set_property(CACHE FLUTTER_TARGET_BACKEND_TYPE PROPERTY STRINGS
|
||||||
|
"wayland" "gbm" "eglstream" "x11")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Compilation settings that should be applied to most targets.
|
||||||
|
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||||
|
target_compile_features(${TARGET} PUBLIC cxx_std_17)
|
||||||
|
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
|
||||||
|
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
|
||||||
|
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||||
|
|
||||||
|
# Flutter library and tool build rules.
|
||||||
|
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||||
|
|
||||||
|
# Application build
|
||||||
|
add_subdirectory("runner")
|
||||||
|
|
||||||
|
# Generated plugin build rules, which manage building the plugins and adding
|
||||||
|
# them to the application.
|
||||||
|
include(flutter/generated_plugins.cmake)
|
||||||
|
|
||||||
|
# === Installation ===
|
||||||
|
# By default, "installing" just makes a relocatable bundle in the build
|
||||||
|
# directory.
|
||||||
|
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
|
||||||
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Start with a clean build bundle directory every time.
|
||||||
|
install(CODE "
|
||||||
|
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
|
||||||
|
" COMPONENT Runtime)
|
||||||
|
|
||||||
|
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||||
|
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
|
||||||
|
|
||||||
|
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
install(FILES "${FLUTTER_LIBRARY}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
install(FILES "${FLUTTER_EMBEDDER_LIBRARY}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
if(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||||
|
# from a previous install.
|
||||||
|
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||||
|
install(CODE "
|
||||||
|
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||||
|
" COMPONENT Runtime)
|
||||||
|
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||||
|
|
||||||
|
# Install the AOT library on non-Debug builds only.
|
||||||
|
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||||
|
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
endif()
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||||
|
|
||||||
|
# Configuration provided via flutter tool.
|
||||||
|
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||||
|
|
||||||
|
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
|
||||||
|
|
||||||
|
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
|
||||||
|
# which isn't available in 3.10.
|
||||||
|
function(list_prepend LIST_NAME PREFIX)
|
||||||
|
set(NEW_LIST "")
|
||||||
|
foreach(element ${${LIST_NAME}})
|
||||||
|
list(APPEND NEW_LIST "${PREFIX}${element}")
|
||||||
|
endforeach(element)
|
||||||
|
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# === Flutter Library ===
|
||||||
|
# System-level dependencies.
|
||||||
|
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_engine.so")
|
||||||
|
if(FLUTTER_TARGET_BACKEND_TYPE MATCHES "gbm")
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_gbm.so")
|
||||||
|
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "eglstream")
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_eglstream.so")
|
||||||
|
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "x11")
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_x11.so")
|
||||||
|
else()
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_wayland.so")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Published to parent scope for install step.
|
||||||
|
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY ${FLUTTER_EMBEDDER_LIBRARY} PARENT_SCOPE)
|
||||||
|
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||||
|
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/elinux/" PARENT_SCOPE)
|
||||||
|
set(AOT_LIBRARY "${EPHEMERAL_DIR}/libapp.so" PARENT_SCOPE)
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||||
|
"flutter_export.h"
|
||||||
|
"flutter_plugin_registrar.h"
|
||||||
|
"flutter_messenger.h"
|
||||||
|
"flutter_texture_registrar.h"
|
||||||
|
"flutter_elinux.h"
|
||||||
|
"flutter_platform_views.h"
|
||||||
|
)
|
||||||
|
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/")
|
||||||
|
add_library(flutter INTERFACE)
|
||||||
|
target_include_directories(flutter INTERFACE
|
||||||
|
"${EPHEMERAL_DIR}"
|
||||||
|
)
|
||||||
|
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
|
||||||
|
target_link_libraries(flutter INTERFACE "${FLUTTER_EMBEDDER_LIBRARY}")
|
||||||
|
add_dependencies(flutter flutter_assemble)
|
||||||
|
|
||||||
|
# === Wrapper ===
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_CORE
|
||||||
|
"core_implementations.cc"
|
||||||
|
"standard_codec.cc"
|
||||||
|
)
|
||||||
|
list_prepend(CPP_WRAPPER_SOURCES_CORE "${WRAPPER_ROOT}/")
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
|
||||||
|
"plugin_registrar.cc"
|
||||||
|
)
|
||||||
|
list_prepend(CPP_WRAPPER_SOURCES_PLUGIN "${WRAPPER_ROOT}/")
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_APP
|
||||||
|
"flutter_engine.cc"
|
||||||
|
"flutter_view_controller.cc"
|
||||||
|
)
|
||||||
|
list_prepend(CPP_WRAPPER_SOURCES_APP "${WRAPPER_ROOT}/")
|
||||||
|
|
||||||
|
# Wrapper sources needed for a plugin.
|
||||||
|
add_library(flutter_wrapper_plugin STATIC
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||||
|
)
|
||||||
|
apply_standard_settings(flutter_wrapper_plugin)
|
||||||
|
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||||
|
POSITION_INDEPENDENT_CODE ON)
|
||||||
|
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||||
|
CXX_VISIBILITY_PRESET hidden)
|
||||||
|
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
|
||||||
|
target_include_directories(flutter_wrapper_plugin PUBLIC
|
||||||
|
"${WRAPPER_ROOT}/include"
|
||||||
|
)
|
||||||
|
add_dependencies(flutter_wrapper_plugin flutter_assemble)
|
||||||
|
|
||||||
|
# Wrapper sources needed for the runner.
|
||||||
|
add_library(flutter_wrapper_app STATIC
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_APP}
|
||||||
|
)
|
||||||
|
apply_standard_settings(flutter_wrapper_app)
|
||||||
|
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
|
||||||
|
target_include_directories(flutter_wrapper_app PUBLIC
|
||||||
|
"${WRAPPER_ROOT}/include"
|
||||||
|
)
|
||||||
|
add_dependencies(flutter_wrapper_app flutter_assemble)
|
||||||
|
|
||||||
|
add_custom_target(flutter_assemble DEPENDS
|
||||||
|
"${FLUTTER_LIBRARY}"
|
||||||
|
"${FLUTTER_EMBEDDER_LIBRARY}"
|
||||||
|
${FLUTTER_LIBRARY_HEADERS}
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||||
|
${CPP_WRAPPER_SOURCES_APP}
|
||||||
|
)
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <native_texture_view/native_texture_view_plugin.h>
|
||||||
|
|
||||||
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
NativeTextureViewPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("NativeTextureViewPlugin"));
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||||
|
#define GENERATED_PLUGIN_REGISTRANT_
|
||||||
|
|
||||||
|
#include <flutter/plugin_registry.h>
|
||||||
|
|
||||||
|
// Registers Flutter plugins.
|
||||||
|
void RegisterPlugins(flutter::PluginRegistry* registry);
|
||||||
|
|
||||||
|
#endif // GENERATED_PLUGIN_REGISTRANT_
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#
|
||||||
|
# Generated file, do not edit.
|
||||||
|
#
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
native_texture_view
|
||||||
|
)
|
||||||
|
|
||||||
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|
||||||
|
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||||
|
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/elinux plugins/${plugin})
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||||
|
endforeach(plugin)
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
project(runner LANGUAGES CXX)
|
||||||
|
|
||||||
|
if(FLUTTER_TARGET_BACKEND_TYPE MATCHES "gbm")
|
||||||
|
add_definitions(-DFLUTTER_TARGET_BACKEND_GBM)
|
||||||
|
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "eglstream")
|
||||||
|
add_definitions(-DFLUTTER_TARGET_BACKEND_EGLSTREAM)
|
||||||
|
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "x11")
|
||||||
|
add_definitions(-DFLUTTER_TARGET_BACKEND_X11)
|
||||||
|
else()
|
||||||
|
add_definitions(-DFLUTTER_TARGET_BACKEND_WAYLAND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(${BINARY_NAME}
|
||||||
|
"flutter_window.cc"
|
||||||
|
"main.cc"
|
||||||
|
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||||
|
)
|
||||||
|
apply_standard_settings(${BINARY_NAME})
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||||
|
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||||
|
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||||
+402
@@ -0,0 +1,402 @@
|
|||||||
|
// Copyright 2022 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef COMMAND_OPTIONS_
|
||||||
|
#define COMMAND_OPTIONS_
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace commandline {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr char kOptionStyleNormal[] = "--";
|
||||||
|
constexpr char kOptionStyleShort[] = "-";
|
||||||
|
constexpr char kOptionValueForHelpMessage[] = "=<value>";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class Exception : public std::exception {
|
||||||
|
public:
|
||||||
|
Exception(const std::string& msg) : msg_(msg) {}
|
||||||
|
~Exception() throw() {}
|
||||||
|
|
||||||
|
const char* what() const throw() { return msg_.c_str(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string msg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CommandOptions {
|
||||||
|
public:
|
||||||
|
CommandOptions() = default;
|
||||||
|
~CommandOptions() = default;
|
||||||
|
|
||||||
|
void AddWithoutValue(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
bool required) {
|
||||||
|
Add<std::string, ReaderString>(name, short_name, description, "",
|
||||||
|
ReaderString(), required, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddInt(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const int& default_value,
|
||||||
|
bool required) {
|
||||||
|
Add<int, ReaderInt>(name, short_name, description, default_value,
|
||||||
|
ReaderInt(), required, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDouble(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const double& default_value,
|
||||||
|
bool required) {
|
||||||
|
Add<double, ReaderDouble>(name, short_name, description, default_value,
|
||||||
|
ReaderDouble(), required, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddString(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const std::string& default_value,
|
||||||
|
bool required) {
|
||||||
|
Add<std::string, ReaderString>(name, short_name, description, default_value,
|
||||||
|
ReaderString(), required, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F>
|
||||||
|
void Add(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const T default_value,
|
||||||
|
F reader = F(),
|
||||||
|
bool required = true,
|
||||||
|
bool required_value = true) {
|
||||||
|
if (options_.find(name) != options_.end()) {
|
||||||
|
std::cerr << "Already registered option: " << name << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lut_short_options_.find(short_name) != lut_short_options_.end()) {
|
||||||
|
std::cerr << short_name << "is already registered" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lut_short_options_[short_name] = name;
|
||||||
|
|
||||||
|
options_[name] = std::make_unique<OptionValueReader<T, F>>(
|
||||||
|
name, short_name, description, default_value, reader, required,
|
||||||
|
required_value);
|
||||||
|
|
||||||
|
// register to show help message.
|
||||||
|
registration_order_options_.push_back(options_[name].get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exist(const std::string& name) {
|
||||||
|
auto itr = options_.find(name);
|
||||||
|
return itr != options_.end() && itr->second->HasValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& GetValue(const std::string& name) {
|
||||||
|
auto itr = options_.find(name);
|
||||||
|
if (itr == options_.end()) {
|
||||||
|
throw Exception("Not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* option_value = dynamic_cast<const OptionValue<T>*>(itr->second.get());
|
||||||
|
if (!option_value) {
|
||||||
|
throw Exception("Type mismatch: " + name);
|
||||||
|
}
|
||||||
|
return option_value->GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parse(int argc, const char* const* argv) {
|
||||||
|
if (argc < 1) {
|
||||||
|
errors_.push_back("No options");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_name_ = argv[0];
|
||||||
|
for (auto i = 1; i < argc; i++) {
|
||||||
|
const std::string arg(argv[i]);
|
||||||
|
|
||||||
|
// normal options: e.g. --bundle=/data/sample/bundle --fullscreen
|
||||||
|
if (arg.length() > 2 &&
|
||||||
|
arg.substr(0, 2).compare(kOptionStyleNormal) == 0) {
|
||||||
|
const size_t option_value_len = arg.find("=") != std::string::npos
|
||||||
|
? (arg.length() - arg.find("="))
|
||||||
|
: 0;
|
||||||
|
const bool has_value = option_value_len != 0;
|
||||||
|
std::string option_name =
|
||||||
|
arg.substr(2, arg.length() - 2 - option_value_len);
|
||||||
|
|
||||||
|
if (options_.find(option_name) == options_.end()) {
|
||||||
|
errors_.push_back("Not found option: " + option_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_value && options_[option_name]->IsRequiredValue()) {
|
||||||
|
errors_.push_back(option_name + " requres an option value");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_value && !options_[option_name]->IsRequiredValue()) {
|
||||||
|
errors_.push_back(option_name + " doesn't requres an option value");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_value) {
|
||||||
|
SetOptionValue(option_name, arg.substr(arg.find("=") + 1));
|
||||||
|
} else {
|
||||||
|
SetOption(option_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// short options: e.g. -f /foo/file.txt -h 640 -abc
|
||||||
|
else if (arg.length() > 1 &&
|
||||||
|
arg.substr(0, 1).compare(kOptionStyleShort) == 0) {
|
||||||
|
for (size_t j = 1; j < arg.length(); j++) {
|
||||||
|
const std::string option_name{argv[i][j]};
|
||||||
|
|
||||||
|
if (lut_short_options_.find(option_name) ==
|
||||||
|
lut_short_options_.end()) {
|
||||||
|
errors_.push_back("Not found short option: " + option_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j == arg.length() - 1 &&
|
||||||
|
options_[lut_short_options_[option_name]]->IsRequiredValue()) {
|
||||||
|
if (i == argc - 1) {
|
||||||
|
errors_.push_back("Invalid format option: " + option_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SetOptionValue(lut_short_options_[option_name], argv[++i]);
|
||||||
|
} else {
|
||||||
|
SetOption(lut_short_options_[option_name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors_.push_back("Invalid format option: " + arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < registration_order_options_.size(); i++) {
|
||||||
|
if (registration_order_options_[i]->IsRequired() &&
|
||||||
|
!registration_order_options_[i]->HasValue()) {
|
||||||
|
errors_.push_back(
|
||||||
|
std::string(registration_order_options_[i]->GetName()) +
|
||||||
|
" option is mandatory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors_.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetError() { return errors_.size() > 0 ? errors_[0] : ""; }
|
||||||
|
|
||||||
|
std::vector<std::string>& GetErrors() { return errors_; }
|
||||||
|
|
||||||
|
std::string ShowHelp() {
|
||||||
|
std::ostringstream ostream;
|
||||||
|
|
||||||
|
ostream << "Usage: " << command_name_ << " ";
|
||||||
|
for (size_t i = 0; i < registration_order_options_.size(); i++) {
|
||||||
|
if (registration_order_options_[i]->IsRequired()) {
|
||||||
|
ostream << registration_order_options_[i]->GetHelpShortMessage() << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ostream << std::endl;
|
||||||
|
|
||||||
|
ostream << "Global options:" << std::endl;
|
||||||
|
size_t max_name_len = 0;
|
||||||
|
for (size_t i = 0; i < registration_order_options_.size(); i++) {
|
||||||
|
max_name_len = std::max(
|
||||||
|
max_name_len, registration_order_options_[i]->GetName().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < registration_order_options_.size(); i++) {
|
||||||
|
if (!registration_order_options_[i]->GetShortName().empty()) {
|
||||||
|
ostream << kOptionStyleShort
|
||||||
|
<< registration_order_options_[i]->GetShortName() << ", ";
|
||||||
|
} else {
|
||||||
|
ostream << std::string(4, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index_adjust = 0;
|
||||||
|
constexpr int kSpacerNum = 10;
|
||||||
|
auto need_value = registration_order_options_[i]->IsRequiredValue();
|
||||||
|
ostream << kOptionStyleNormal
|
||||||
|
<< registration_order_options_[i]->GetName();
|
||||||
|
if (need_value) {
|
||||||
|
ostream << kOptionValueForHelpMessage;
|
||||||
|
index_adjust += std::string(kOptionValueForHelpMessage).length();
|
||||||
|
}
|
||||||
|
ostream << std::string(
|
||||||
|
max_name_len + kSpacerNum - index_adjust -
|
||||||
|
registration_order_options_[i]->GetName().length(),
|
||||||
|
' ');
|
||||||
|
ostream << registration_order_options_[i]->GetDescription() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ostream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ReaderInt {
|
||||||
|
int operator()(const std::string& value) { return std::stoi(value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReaderString {
|
||||||
|
std::string operator()(const std::string& value) { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReaderDouble {
|
||||||
|
double operator()(const std::string& value) { return std::stod(value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Option {
|
||||||
|
public:
|
||||||
|
Option(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
bool required,
|
||||||
|
bool required_value)
|
||||||
|
: name_(name),
|
||||||
|
short_name_(short_name),
|
||||||
|
description_(description),
|
||||||
|
is_required_(required),
|
||||||
|
is_required_value_(required_value),
|
||||||
|
value_set_(false){};
|
||||||
|
virtual ~Option() = default;
|
||||||
|
|
||||||
|
const std::string& GetName() const { return name_; };
|
||||||
|
|
||||||
|
const std::string& GetShortName() const { return short_name_; };
|
||||||
|
|
||||||
|
const std::string& GetDescription() const { return description_; };
|
||||||
|
|
||||||
|
const std::string GetHelpShortMessage() const {
|
||||||
|
std::string message = kOptionStyleNormal + name_;
|
||||||
|
if (is_required_value_) {
|
||||||
|
message += kOptionValueForHelpMessage;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRequired() const { return is_required_; };
|
||||||
|
|
||||||
|
bool IsRequiredValue() const { return is_required_value_; };
|
||||||
|
|
||||||
|
void Set() { value_set_ = true; };
|
||||||
|
|
||||||
|
virtual bool SetValue(const std::string& value) = 0;
|
||||||
|
|
||||||
|
virtual bool HasValue() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string name_;
|
||||||
|
std::string short_name_;
|
||||||
|
std::string description_;
|
||||||
|
bool is_required_;
|
||||||
|
bool is_required_value_;
|
||||||
|
bool value_set_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class OptionValue : public Option {
|
||||||
|
public:
|
||||||
|
OptionValue(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const T& default_value,
|
||||||
|
bool required,
|
||||||
|
bool required_value)
|
||||||
|
: Option(name, short_name, description, required, required_value),
|
||||||
|
default_value_(default_value),
|
||||||
|
value_(default_value){};
|
||||||
|
virtual ~OptionValue() = default;
|
||||||
|
|
||||||
|
bool SetValue(const std::string& value) {
|
||||||
|
value_ = Read(value);
|
||||||
|
value_set_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasValue() const { return value_set_; }
|
||||||
|
|
||||||
|
const T& GetValue() const { return value_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual T Read(const std::string& s) = 0;
|
||||||
|
|
||||||
|
T default_value_;
|
||||||
|
T value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename F>
|
||||||
|
class OptionValueReader : public OptionValue<T> {
|
||||||
|
public:
|
||||||
|
OptionValueReader(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const T default_value,
|
||||||
|
F reader,
|
||||||
|
bool required,
|
||||||
|
bool required_value)
|
||||||
|
: OptionValue<T>(name,
|
||||||
|
short_name,
|
||||||
|
description,
|
||||||
|
default_value,
|
||||||
|
required,
|
||||||
|
required_value),
|
||||||
|
reader_(reader) {}
|
||||||
|
~OptionValueReader() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
T Read(const std::string& value) { return reader_(value); }
|
||||||
|
|
||||||
|
F reader_;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool SetOption(const std::string& name) {
|
||||||
|
auto itr = options_.find(name);
|
||||||
|
if (itr == options_.end()) {
|
||||||
|
errors_.push_back("Unknown option: " + name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
itr->second->Set();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetOptionValue(const std::string& name, const std::string& value) {
|
||||||
|
auto itr = options_.find(name);
|
||||||
|
if (itr == options_.end()) {
|
||||||
|
errors_.push_back("Unknown option: " + name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!itr->second->SetValue(value)) {
|
||||||
|
errors_.push_back("Invalid option value: " + name + " = " + value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string command_name_;
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<Option>> options_;
|
||||||
|
std::unordered_map<std::string, std::string> lut_short_options_;
|
||||||
|
std::vector<Option*> registration_order_options_;
|
||||||
|
std::vector<std::string> errors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace commandline
|
||||||
|
|
||||||
|
#endif // COMMAND_OPTIONS_
|
||||||
+203
@@ -0,0 +1,203 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_EMBEDDER_OPTIONS_
|
||||||
|
#define FLUTTER_EMBEDDER_OPTIONS_
|
||||||
|
|
||||||
|
#include <flutter/flutter_view_controller.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "command_options.h"
|
||||||
|
|
||||||
|
class FlutterEmbedderOptions {
|
||||||
|
public:
|
||||||
|
FlutterEmbedderOptions() {
|
||||||
|
options_.AddString("bundle", "b", "Path to Flutter project bundle",
|
||||||
|
"./bundle", true);
|
||||||
|
options_.AddWithoutValue("no-cursor", "n", "No mouse cursor/pointer",
|
||||||
|
false);
|
||||||
|
options_.AddInt("rotation", "r",
|
||||||
|
"Window rotation(degree) [0(default)|90|180|270]", 0,
|
||||||
|
false);
|
||||||
|
options_.AddDouble("text-scaling-factor", "x", "Text scaling factor", 1.0,
|
||||||
|
false);
|
||||||
|
options_.AddWithoutValue("enable-high-contrast", "i",
|
||||||
|
"Request that UI be rendered with darker colors.",
|
||||||
|
false);
|
||||||
|
options_.AddDouble("force-scale-factor", "s",
|
||||||
|
"Force a scale factor instead using default value", 1.0,
|
||||||
|
false);
|
||||||
|
options_.AddWithoutValue(
|
||||||
|
"async-vblank", "v",
|
||||||
|
"Don't sync to compositor redraw/vblank (eglSwapInterval 0)", false);
|
||||||
|
|
||||||
|
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
|
||||||
|
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
|
||||||
|
// no more options.
|
||||||
|
#elif defined(FLUTTER_TARGET_BACKEND_X11)
|
||||||
|
options_.AddString("title", "t", "Window title", "Flutter", false);
|
||||||
|
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
|
||||||
|
false);
|
||||||
|
options_.AddInt("width", "w", "Window width", 1280, false);
|
||||||
|
options_.AddInt("height", "h", "Window height", 720, false);
|
||||||
|
#else // FLUTTER_TARGET_BACKEND_WAYLAND
|
||||||
|
options_.AddString("title", "t", "Window title", "Flutter", false);
|
||||||
|
options_.AddString("app-id", "a", "XDG App ID", "dev.flutter.elinux",
|
||||||
|
false);
|
||||||
|
options_.AddWithoutValue("onscreen-keyboard", "k",
|
||||||
|
"Enable on-screen keyboard", false);
|
||||||
|
options_.AddWithoutValue("window-decoration", "d",
|
||||||
|
"Enable window decorations", false);
|
||||||
|
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
|
||||||
|
false);
|
||||||
|
options_.AddInt("width", "w", "Window width", 1280, false);
|
||||||
|
options_.AddInt("height", "h", "Window height", 720, false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
~FlutterEmbedderOptions() = default;
|
||||||
|
|
||||||
|
bool Parse(int argc, char** argv) {
|
||||||
|
if (!options_.Parse(argc, argv)) {
|
||||||
|
std::cerr << options_.GetError() << std::endl;
|
||||||
|
std::cout << options_.ShowHelp();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle_path_ = options_.GetValue<std::string>("bundle");
|
||||||
|
use_mouse_cursor_ = !options_.Exist("no-cursor");
|
||||||
|
if (options_.Exist("rotation")) {
|
||||||
|
switch (options_.GetValue<int>("rotation")) {
|
||||||
|
case 90:
|
||||||
|
window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_90;
|
||||||
|
break;
|
||||||
|
case 180:
|
||||||
|
window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_180;
|
||||||
|
break;
|
||||||
|
case 270:
|
||||||
|
window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_270;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_scale_factor_ = options_.GetValue<double>("text-scaling-factor");
|
||||||
|
enable_high_contrast_ = options_.Exist("enable-high-contrast");
|
||||||
|
|
||||||
|
if (options_.Exist("force-scale-factor")) {
|
||||||
|
is_force_scale_factor_ = true;
|
||||||
|
scale_factor_ = options_.GetValue<double>("force-scale-factor");
|
||||||
|
} else {
|
||||||
|
is_force_scale_factor_ = false;
|
||||||
|
scale_factor_ = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_vsync_ = !options_.Exist("async-vblank");
|
||||||
|
|
||||||
|
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
|
||||||
|
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
|
||||||
|
use_onscreen_keyboard_ = false;
|
||||||
|
use_window_decoration_ = false;
|
||||||
|
window_view_mode_ = flutter::FlutterViewController::ViewMode::kFullscreen;
|
||||||
|
#elif defined(FLUTTER_TARGET_BACKEND_X11)
|
||||||
|
use_onscreen_keyboard_ = false;
|
||||||
|
use_window_decoration_ = false;
|
||||||
|
window_title_ = options_.GetValue<std::string>("title");
|
||||||
|
window_view_mode_ =
|
||||||
|
options_.Exist("fullscreen")
|
||||||
|
? flutter::FlutterViewController::ViewMode::kFullscreen
|
||||||
|
: flutter::FlutterViewController::ViewMode::kNormal;
|
||||||
|
window_width_ = options_.GetValue<int>("width");
|
||||||
|
window_height_ = options_.GetValue<int>("height");
|
||||||
|
#else // FLUTTER_TARGET_BACKEND_WAYLAND
|
||||||
|
window_title_ = options_.GetValue<std::string>("title");
|
||||||
|
window_app_id_ = options_.GetValue<std::string>("app-id");
|
||||||
|
use_onscreen_keyboard_ = options_.Exist("onscreen-keyboard");
|
||||||
|
use_window_decoration_ = options_.Exist("window-decoration");
|
||||||
|
window_view_mode_ =
|
||||||
|
options_.Exist("fullscreen")
|
||||||
|
? flutter::FlutterViewController::ViewMode::kFullscreen
|
||||||
|
: flutter::FlutterViewController::ViewMode::kNormal;
|
||||||
|
window_width_ = options_.GetValue<int>("width");
|
||||||
|
window_height_ = options_.GetValue<int>("height");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BundlePath() const {
|
||||||
|
return bundle_path_;
|
||||||
|
}
|
||||||
|
std::string WindowTitle() const {
|
||||||
|
return window_title_;
|
||||||
|
}
|
||||||
|
std::string WindowAppID() const {
|
||||||
|
return window_app_id_;
|
||||||
|
}
|
||||||
|
bool IsUseMouseCursor() const {
|
||||||
|
return use_mouse_cursor_;
|
||||||
|
}
|
||||||
|
bool IsUseOnscreenKeyboard() const {
|
||||||
|
return use_onscreen_keyboard_;
|
||||||
|
}
|
||||||
|
bool IsUseWindowDecoraation() const {
|
||||||
|
return use_window_decoration_;
|
||||||
|
}
|
||||||
|
flutter::FlutterViewController::ViewMode WindowViewMode() const {
|
||||||
|
return window_view_mode_;
|
||||||
|
}
|
||||||
|
int WindowWidth() const {
|
||||||
|
return window_width_;
|
||||||
|
}
|
||||||
|
int WindowHeight() const {
|
||||||
|
return window_height_;
|
||||||
|
}
|
||||||
|
flutter::FlutterViewController::ViewRotation WindowRotation() const {
|
||||||
|
return window_view_rotation_;
|
||||||
|
}
|
||||||
|
double TextScaleFactor() const {
|
||||||
|
return text_scale_factor_;
|
||||||
|
}
|
||||||
|
bool EnableHighContrast() const {
|
||||||
|
return enable_high_contrast_;
|
||||||
|
}
|
||||||
|
bool IsForceScaleFactor() const {
|
||||||
|
return is_force_scale_factor_;
|
||||||
|
}
|
||||||
|
double ScaleFactor() const {
|
||||||
|
return scale_factor_;
|
||||||
|
}
|
||||||
|
bool EnableVsync() const {
|
||||||
|
return enable_vsync_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
commandline::CommandOptions options_;
|
||||||
|
|
||||||
|
std::string bundle_path_;
|
||||||
|
std::string window_title_;
|
||||||
|
std::string window_app_id_;
|
||||||
|
bool use_mouse_cursor_ = true;
|
||||||
|
bool use_onscreen_keyboard_ = false;
|
||||||
|
bool use_window_decoration_ = false;
|
||||||
|
flutter::FlutterViewController::ViewMode window_view_mode_ =
|
||||||
|
flutter::FlutterViewController::ViewMode::kNormal;
|
||||||
|
int window_width_ = 1280;
|
||||||
|
int window_height_ = 720;
|
||||||
|
flutter::FlutterViewController::ViewRotation window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_0;
|
||||||
|
bool is_force_scale_factor_;
|
||||||
|
double scale_factor_;
|
||||||
|
double text_scale_factor_;
|
||||||
|
bool enable_high_contrast_;
|
||||||
|
bool enable_vsync_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FLUTTER_EMBEDDER_OPTIONS_
|
||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "flutter_window.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "flutter/generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
FlutterWindow::FlutterWindow(
|
||||||
|
const flutter::FlutterViewController::ViewProperties view_properties,
|
||||||
|
const flutter::DartProject project)
|
||||||
|
: view_properties_(view_properties), project_(project) {}
|
||||||
|
|
||||||
|
bool FlutterWindow::OnCreate() {
|
||||||
|
flutter_view_controller_ = std::make_unique<flutter::FlutterViewController>(
|
||||||
|
view_properties_, project_);
|
||||||
|
|
||||||
|
// Ensure that basic setup of the controller was successful.
|
||||||
|
if (!flutter_view_controller_->engine() ||
|
||||||
|
!flutter_view_controller_->view()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register Flutter plugins.
|
||||||
|
RegisterPlugins(flutter_view_controller_->engine());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlutterWindow::OnDestroy() {
|
||||||
|
if (flutter_view_controller_) {
|
||||||
|
flutter_view_controller_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlutterWindow::Run() {
|
||||||
|
// Main loop.
|
||||||
|
auto next_flutter_event_time =
|
||||||
|
std::chrono::steady_clock::time_point::clock::now();
|
||||||
|
while (flutter_view_controller_->view()->DispatchEvent()) {
|
||||||
|
// Wait until the next event.
|
||||||
|
{
|
||||||
|
auto wait_duration =
|
||||||
|
std::max(std::chrono::nanoseconds(0),
|
||||||
|
next_flutter_event_time -
|
||||||
|
std::chrono::steady_clock::time_point::clock::now());
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(wait_duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes any pending events in the Flutter engine, and returns the
|
||||||
|
// number of nanoseconds until the next scheduled event (or max, if none).
|
||||||
|
auto wait_duration = flutter_view_controller_->engine()->ProcessMessages();
|
||||||
|
{
|
||||||
|
auto next_event_time = std::chrono::steady_clock::time_point::max();
|
||||||
|
if (wait_duration != std::chrono::nanoseconds::max()) {
|
||||||
|
next_event_time =
|
||||||
|
std::min(next_event_time,
|
||||||
|
std::chrono::steady_clock::time_point::clock::now() +
|
||||||
|
wait_duration);
|
||||||
|
} else {
|
||||||
|
// Wait for the next frame if no events.
|
||||||
|
auto frame_rate = flutter_view_controller_->view()->GetFrameRate();
|
||||||
|
next_event_time = std::min(
|
||||||
|
next_event_time,
|
||||||
|
std::chrono::steady_clock::time_point::clock::now() +
|
||||||
|
std::chrono::milliseconds(
|
||||||
|
static_cast<int>(std::trunc(1000000.0 / frame_rate))));
|
||||||
|
}
|
||||||
|
next_flutter_event_time =
|
||||||
|
std::max(next_flutter_event_time, next_event_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_WINDOW_
|
||||||
|
#define FLUTTER_WINDOW_
|
||||||
|
|
||||||
|
#include <flutter/dart_project.h>
|
||||||
|
#include <flutter/flutter_view_controller.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class FlutterWindow {
|
||||||
|
public:
|
||||||
|
explicit FlutterWindow(
|
||||||
|
const flutter::FlutterViewController::ViewProperties view_properties,
|
||||||
|
const flutter::DartProject project);
|
||||||
|
~FlutterWindow() = default;
|
||||||
|
|
||||||
|
// Prevent copying.
|
||||||
|
FlutterWindow(FlutterWindow const&) = delete;
|
||||||
|
FlutterWindow& operator=(FlutterWindow const&) = delete;
|
||||||
|
|
||||||
|
bool OnCreate();
|
||||||
|
void OnDestroy();
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
flutter::FlutterViewController::ViewProperties view_properties_;
|
||||||
|
flutter::DartProject project_;
|
||||||
|
std::unique_ptr<flutter::FlutterViewController> flutter_view_controller_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FLUTTER_WINDOW_
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <flutter/dart_project.h>
|
||||||
|
#include <flutter/flutter_view_controller.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "flutter_embedder_options.h"
|
||||||
|
#include "flutter_window.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
FlutterEmbedderOptions options;
|
||||||
|
if (!options.Parse(argc, argv)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the Flutter project.
|
||||||
|
const auto bundle_path = options.BundlePath();
|
||||||
|
const std::wstring fl_path(bundle_path.begin(), bundle_path.end());
|
||||||
|
flutter::DartProject project(fl_path);
|
||||||
|
auto command_line_arguments = std::vector<std::string>();
|
||||||
|
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
|
||||||
|
|
||||||
|
flutter::FlutterViewController::ViewProperties view_properties = {};
|
||||||
|
view_properties.width = options.WindowWidth();
|
||||||
|
view_properties.height = options.WindowHeight();
|
||||||
|
view_properties.view_mode = options.WindowViewMode();
|
||||||
|
view_properties.view_rotation = options.WindowRotation();
|
||||||
|
view_properties.title = options.WindowTitle();
|
||||||
|
view_properties.app_id = options.WindowAppID();
|
||||||
|
view_properties.use_mouse_cursor = options.IsUseMouseCursor();
|
||||||
|
view_properties.use_onscreen_keyboard = options.IsUseOnscreenKeyboard();
|
||||||
|
view_properties.use_window_decoration = options.IsUseWindowDecoraation();
|
||||||
|
view_properties.text_scale_factor = options.TextScaleFactor();
|
||||||
|
view_properties.enable_high_contrast = options.EnableHighContrast();
|
||||||
|
view_properties.force_scale_factor = options.IsForceScaleFactor();
|
||||||
|
view_properties.scale_factor = options.ScaleFactor();
|
||||||
|
view_properties.enable_vsync = options.EnableVsync();
|
||||||
|
|
||||||
|
// The Flutter instance hosted by this window.
|
||||||
|
FlutterWindow window(view_properties, project);
|
||||||
|
if (!window.OnCreate()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
window.Run();
|
||||||
|
window.OnDestroy();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:native_texture_view_example/native_texture_view.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'flutter-elinux platform views',
|
||||||
|
theme: ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
home: const MyHomePage(title: 'Native view example using platform views'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyHomePage extends StatefulWidget {
|
||||||
|
const MyHomePage({super.key, required this.title});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyHomePage> createState() => _MyHomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||||
|
title: Text(widget.title),
|
||||||
|
),
|
||||||
|
body: const Center(child: NativeTextureViewWidget()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_elinux/widgets.dart';
|
||||||
|
|
||||||
|
class NativeTextureViewWidget extends StatelessWidget {
|
||||||
|
const NativeTextureViewWidget({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
const String viewType = 'plugins.flutter.io/native_texture_view';
|
||||||
|
const Map<String, dynamic> creationParams = <String, dynamic>{};
|
||||||
|
|
||||||
|
return PlatformViewLink(
|
||||||
|
viewType: viewType,
|
||||||
|
surfaceFactory: (context, controller) {
|
||||||
|
return ELinuxViewSurface(
|
||||||
|
controller: controller as ELinuxViewController,
|
||||||
|
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
|
||||||
|
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onCreatePlatformView: (params) {
|
||||||
|
return PlatformViewsServiceELinux.initELinuxView(
|
||||||
|
id: params.id,
|
||||||
|
viewType: viewType,
|
||||||
|
layoutDirection: TextDirection.ltr,
|
||||||
|
creationParams: creationParams,
|
||||||
|
creationParamsCodec: const StandardMessageCodec(),
|
||||||
|
onFocus: () {
|
||||||
|
params.onFocusChanged(true);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
|
||||||
|
..create();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
flutter/
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
set(PROJECT_NAME "native_texture_view")
|
||||||
|
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||||
|
|
||||||
|
# This value is used when generating builds using this plugin, so it must
|
||||||
|
# not be changed
|
||||||
|
set(PLUGIN_NAME "native_texture_view_plugin")
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(GLIB REQUIRED glib-2.0)
|
||||||
|
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
|
||||||
|
if(USE_EGL_IMAGE_DMABUF)
|
||||||
|
pkg_check_modules(GSTREAMER_GL REQUIRED gstreamer-gl-1.0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(${PLUGIN_NAME} SHARED
|
||||||
|
"native_texture_view_plugin.cc"
|
||||||
|
"native_texture_view_factory.cc"
|
||||||
|
"native_texture_view.cc"
|
||||||
|
)
|
||||||
|
apply_standard_settings(${PLUGIN_NAME})
|
||||||
|
set_target_properties(${PLUGIN_NAME} PROPERTIES
|
||||||
|
CXX_VISIBILITY_PRESET hidden)
|
||||||
|
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
|
||||||
|
target_include_directories(${PLUGIN_NAME} INTERFACE
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
|
||||||
|
|
||||||
|
target_include_directories(${PLUGIN_NAME}
|
||||||
|
PRIVATE
|
||||||
|
${GLIB_INCLUDE_DIRS}
|
||||||
|
${GSTREAMER_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
if(USE_EGL_IMAGE_DMABUF)
|
||||||
|
target_include_directories(${PLUGIN_NAME}
|
||||||
|
PRIVATE
|
||||||
|
${GSTREAMER_GL_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(${PLUGIN_NAME}
|
||||||
|
PRIVATE
|
||||||
|
${GLIB_LIBRARIES}
|
||||||
|
${GSTREAMER_LIBRARIES}
|
||||||
|
)
|
||||||
|
if(USE_EGL_IMAGE_DMABUF)
|
||||||
|
target_link_libraries(${PLUGIN_NAME}
|
||||||
|
PRIVATE
|
||||||
|
${GSTREAMER_GL_LIBRARIES}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# List of absolute paths to libraries that should be bundled with the plugin
|
||||||
|
set(native_texture_view_bundled_libraries
|
||||||
|
""
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_PLUGIN_H_
|
||||||
|
#define FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_PLUGIN_H_
|
||||||
|
|
||||||
|
#include <flutter_plugin_registrar.h>
|
||||||
|
|
||||||
|
#ifdef FLUTTER_PLUGIN_IMPL
|
||||||
|
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
#define FLUTTER_PLUGIN_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FLUTTER_PLUGIN_EXPORT void NativeTextureViewPluginRegisterWithRegistrar(
|
||||||
|
FlutterDesktopPluginRegistrarRef registrar);
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_PLUGIN_H_
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
#include "native_texture_view.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
ColorBarTexture::ColorBarTexture() : request_count_(0) {
|
||||||
|
constexpr size_t width = 1024;
|
||||||
|
constexpr size_t height = 640;
|
||||||
|
pixels_.reset(new uint8_t[width * height * 4]);
|
||||||
|
|
||||||
|
buffer_ = std::make_unique<FlutterDesktopPixelBuffer>();
|
||||||
|
buffer_->buffer = pixels_.get();
|
||||||
|
buffer_->width = width;
|
||||||
|
buffer_->height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FlutterDesktopPixelBuffer *ColorBarTexture::CopyBuffer(size_t width,
|
||||||
|
size_t height) {
|
||||||
|
PrepareBuffer();
|
||||||
|
request_count_++;
|
||||||
|
return buffer_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorBarTexture::PrepareBuffer() {
|
||||||
|
constexpr uint32_t kColorData[] = {0xFFFFFFFF, 0xFF00C0C0, 0xFFC0C000,
|
||||||
|
0xFF00C000, 0xFFC000C0, 0xFF0000C0,
|
||||||
|
0xFFC00000, 0xFF000000};
|
||||||
|
auto data_num = sizeof(kColorData) / sizeof(uint32_t);
|
||||||
|
|
||||||
|
auto *buffer = buffer_.get();
|
||||||
|
auto pixel = const_cast<uint32_t *>(
|
||||||
|
reinterpret_cast<const uint32_t *>(buffer->buffer));
|
||||||
|
auto width = buffer->width;
|
||||||
|
auto height = buffer->height;
|
||||||
|
auto column_width = width / data_num;
|
||||||
|
auto offset = request_count_ % 8;
|
||||||
|
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
for (int j = 0; j < width; j++) {
|
||||||
|
auto index = (j / column_width) + offset;
|
||||||
|
index -= (index < data_num) ? 0 : data_num;
|
||||||
|
*(pixel++) = kColorData[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
NativeTextureView::NativeTextureView(
|
||||||
|
flutter::PluginRegistrar *registrar, int view_id,
|
||||||
|
flutter::TextureRegistrar *texture_registrar, double width, double height,
|
||||||
|
const std::vector<uint8_t> ¶ms)
|
||||||
|
: FlutterDesktopPlatformView(registrar, view_id),
|
||||||
|
texture_registrar_(texture_registrar), width_(width), height_(height) {
|
||||||
|
color_bar_texture_ = std::make_unique<ColorBarTexture>();
|
||||||
|
texture_ =
|
||||||
|
std::make_unique<flutter::TextureVariant>(flutter::PixelBufferTexture(
|
||||||
|
[this](size_t width,
|
||||||
|
size_t height) -> const FlutterDesktopPixelBuffer * {
|
||||||
|
return color_bar_texture_->CopyBuffer(width, height);
|
||||||
|
}));
|
||||||
|
SetTextureId(texture_registrar_->RegisterTexture(texture_.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeTextureView::~NativeTextureView() { Dispose(); }
|
||||||
|
|
||||||
|
void NativeTextureView::Dispose() {
|
||||||
|
texture_registrar_->UnregisterTexture(GetTextureId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeTextureView::ClearFocus() {}
|
||||||
|
|
||||||
|
void NativeTextureView::Resize(double width, double height) {
|
||||||
|
width_ = width;
|
||||||
|
height_ = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeTextureView::Touch(int device_id, int event_type, double x,
|
||||||
|
double y) {
|
||||||
|
texture_registrar_->MarkTextureFrameAvailable(GetTextureId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeTextureView::Offset(double top, double left) {}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_H_
|
||||||
|
#define FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_H_
|
||||||
|
|
||||||
|
#include <flutter/plugin_registrar.h>
|
||||||
|
#include <flutter/texture_registrar.h>
|
||||||
|
#include <flutter_platform_views.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class ColorBarTexture {
|
||||||
|
public:
|
||||||
|
ColorBarTexture();
|
||||||
|
virtual ~ColorBarTexture() {}
|
||||||
|
const FlutterDesktopPixelBuffer *CopyBuffer(size_t width, size_t height);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void PrepareBuffer();
|
||||||
|
|
||||||
|
std::unique_ptr<FlutterDesktopPixelBuffer> buffer_;
|
||||||
|
std::unique_ptr<uint8_t> pixels_;
|
||||||
|
int32_t request_count_;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class NativeTextureView : public FlutterDesktopPlatformView {
|
||||||
|
public:
|
||||||
|
NativeTextureView(flutter::PluginRegistrar *registrar, int view_id,
|
||||||
|
flutter::TextureRegistrar *texture_registrar, double width,
|
||||||
|
double height, const std::vector<uint8_t> ¶ms);
|
||||||
|
~NativeTextureView();
|
||||||
|
|
||||||
|
virtual void Dispose() override;
|
||||||
|
|
||||||
|
virtual void ClearFocus() override;
|
||||||
|
|
||||||
|
virtual void Resize(double width, double height) override;
|
||||||
|
|
||||||
|
virtual void Touch(int device_id, int event_type, double x,
|
||||||
|
double y) override;
|
||||||
|
|
||||||
|
virtual void Offset(double top, double left) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
flutter::TextureRegistrar *texture_registrar_;
|
||||||
|
std::unique_ptr<flutter::TextureVariant> texture_;
|
||||||
|
std::unique_ptr<ColorBarTexture> color_bar_texture_;
|
||||||
|
double width_;
|
||||||
|
double height_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_H_
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#include "native_texture_view_factory.h"
|
||||||
|
|
||||||
|
#include "native_texture_view.h"
|
||||||
|
|
||||||
|
NativeTextureViewFactory::NativeTextureViewFactory(
|
||||||
|
flutter::PluginRegistrar *registrar)
|
||||||
|
: FlutterDesktopPlatformViewFactory(registrar) {
|
||||||
|
texture_registrar_ = registrar->texture_registrar();
|
||||||
|
}
|
||||||
|
|
||||||
|
FlutterDesktopPlatformView *
|
||||||
|
NativeTextureViewFactory::Create(int view_id, double width, double height,
|
||||||
|
const std::vector<uint8_t> ¶ms) {
|
||||||
|
return new NativeTextureView(GetPluginRegistrar(), view_id,
|
||||||
|
texture_registrar_, width, height, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeTextureViewFactory::Dispose() {}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_FACTORY_H_
|
||||||
|
#define FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_FACTORY_H_
|
||||||
|
|
||||||
|
#include <flutter/plugin_registrar.h>
|
||||||
|
#include <flutter/texture_registrar.h>
|
||||||
|
#include <flutter_platform_views.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class NativeTextureViewFactory : public FlutterDesktopPlatformViewFactory {
|
||||||
|
public:
|
||||||
|
NativeTextureViewFactory(flutter::PluginRegistrar *registrar);
|
||||||
|
|
||||||
|
virtual FlutterDesktopPlatformView *
|
||||||
|
Create(int view_id, double width, double height,
|
||||||
|
const std::vector<uint8_t> ¶ms) override;
|
||||||
|
|
||||||
|
virtual void Dispose() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
flutter::TextureRegistrar *texture_registrar_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FLUTTER_PLUGIN_NATIVE_TEXTURE_VIEW_FACTORY_H_
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#include "include/native_texture_view/native_texture_view_plugin.h"
|
||||||
|
|
||||||
|
#include <flutter/plugin_registrar.h>
|
||||||
|
#include <flutter_elinux.h>
|
||||||
|
#include <flutter_platform_views.h>
|
||||||
|
|
||||||
|
#include "native_texture_view_factory.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class NativeTextureViewPlugin : public flutter::Plugin {
|
||||||
|
public:
|
||||||
|
static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar) {
|
||||||
|
auto plugin = std::make_unique<NativeTextureViewPlugin>();
|
||||||
|
registrar->AddPlugin(std::move(plugin));
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeTextureViewPlugin() {}
|
||||||
|
|
||||||
|
virtual ~NativeTextureViewPlugin() {}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void NativeTextureViewPluginRegisterWithRegistrar(
|
||||||
|
FlutterDesktopPluginRegistrarRef registrar) {
|
||||||
|
flutter::PluginRegistrar *plugin_registrar =
|
||||||
|
flutter::PluginRegistrarManager::GetInstance()
|
||||||
|
->GetRegistrar<flutter::PluginRegistrar>(registrar);
|
||||||
|
|
||||||
|
NativeTextureViewPlugin::RegisterWithRegistrar(plugin_registrar);
|
||||||
|
FlutterDesktopRegisterPlatformViewFactory(
|
||||||
|
registrar, "plugins.flutter.io/native_texture_view",
|
||||||
|
std::make_unique<NativeTextureViewFactory>(plugin_registrar));
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
name: native_texture_view
|
||||||
|
description: Native texture view example plugin for flutter-elinux.
|
||||||
|
version: 0.1.0
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.18.0 <4.0.0"
|
||||||
|
flutter: ">=3.3.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
plugin:
|
||||||
|
platforms:
|
||||||
|
elinux:
|
||||||
|
pluginClass: NativeTextureViewPlugin
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
name: native_texture_view_example
|
||||||
|
description: Native texture view example plugin for flutter-elinux.
|
||||||
|
publish_to: 'none'
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.1.0 <4.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
cupertino_icons: ^1.0.2
|
||||||
|
flutter_elinux:
|
||||||
|
# You need to correctly replace the path below with the path of your flutter-elinux directory.
|
||||||
|
path: /home/hidenori/work/flutter/flutter-elinux/packages/flutter_elinux
|
||||||
|
native_texture_view:
|
||||||
|
path: plugins/native_texture_view
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
// Copyright 2023 Sony Group Corporation. All rights reserved.
|
||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
|
||||||
|
import '../services/platform_views.dart';
|
||||||
|
|
||||||
|
/// See: [_PlatformViewState] in `src/rendering/platform_view.dart`
|
||||||
|
enum _PlatformViewState {
|
||||||
|
uninitialized,
|
||||||
|
resizing,
|
||||||
|
ready,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See: [RenderAndroidView] in `src/rendering/platform_view.dart`
|
||||||
|
class RenderELinuxView extends PlatformViewRenderBox {
|
||||||
|
/// Creates a render object for an ELinux view.
|
||||||
|
RenderELinuxView({
|
||||||
|
required ELinuxViewController viewController,
|
||||||
|
required PlatformViewHitTestBehavior hitTestBehavior,
|
||||||
|
required Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
|
||||||
|
Clip clipBehavior = Clip.hardEdge,
|
||||||
|
}) : _viewController = viewController,
|
||||||
|
_clipBehavior = clipBehavior,
|
||||||
|
super(
|
||||||
|
controller: viewController,
|
||||||
|
hitTestBehavior: hitTestBehavior,
|
||||||
|
gestureRecognizers: gestureRecognizers) {
|
||||||
|
_viewController.pointTransformer = (Offset offset) => globalToLocal(offset);
|
||||||
|
updateGestureRecognizers(gestureRecognizers);
|
||||||
|
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||||
|
this.hitTestBehavior = hitTestBehavior;
|
||||||
|
_setOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
_PlatformViewState _state = _PlatformViewState.uninitialized;
|
||||||
|
|
||||||
|
Size? _currentTextureSize;
|
||||||
|
|
||||||
|
bool _isDisposed = false;
|
||||||
|
|
||||||
|
/// The ELinux view controller for the ELinux view associated with this render object.
|
||||||
|
@override
|
||||||
|
ELinuxViewController get controller => _viewController;
|
||||||
|
|
||||||
|
ELinuxViewController _viewController;
|
||||||
|
|
||||||
|
/// Sets a new ELinux view controller.
|
||||||
|
@override
|
||||||
|
set controller(ELinuxViewController controller) {
|
||||||
|
assert(!_isDisposed);
|
||||||
|
if (_viewController == controller) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||||
|
super.controller = controller;
|
||||||
|
_viewController = controller;
|
||||||
|
_viewController.pointTransformer = (Offset offset) => globalToLocal(offset);
|
||||||
|
_sizePlatformView();
|
||||||
|
if (_viewController.isCreated) {
|
||||||
|
markNeedsSemanticsUpdate();
|
||||||
|
}
|
||||||
|
_viewController.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {@macro flutter.material.Material.clipBehavior}
|
||||||
|
///
|
||||||
|
/// Defaults to [Clip.hardEdge], and must not be null.
|
||||||
|
Clip get clipBehavior => _clipBehavior;
|
||||||
|
Clip _clipBehavior = Clip.hardEdge;
|
||||||
|
set clipBehavior(Clip value) {
|
||||||
|
if (value != _clipBehavior) {
|
||||||
|
_clipBehavior = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
markNeedsSemanticsUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPlatformViewCreated(int id) {
|
||||||
|
assert(!_isDisposed);
|
||||||
|
markNeedsSemanticsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get sizedByParent => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get alwaysNeedsCompositing => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isRepaintBoundary => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size computeDryLayout(BoxConstraints constraints) {
|
||||||
|
return constraints.biggest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performResize() {
|
||||||
|
super.performResize();
|
||||||
|
_sizePlatformView();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _sizePlatformView() async {
|
||||||
|
// ELinux virtual displays cannot have a zero size.
|
||||||
|
// Trying to size it to 0 crashes the app, which was happening when starting the app
|
||||||
|
// with a locked screen (see: https://github.com/flutter/flutter/issues/20456).
|
||||||
|
if (_state == _PlatformViewState.resizing || size.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_state = _PlatformViewState.resizing;
|
||||||
|
markNeedsPaint();
|
||||||
|
|
||||||
|
Size targetSize;
|
||||||
|
do {
|
||||||
|
targetSize = size;
|
||||||
|
_currentTextureSize = await _viewController.setSize(targetSize);
|
||||||
|
if (_isDisposed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We've resized the platform view to targetSize, but it is possible that
|
||||||
|
// while we were resizing the render object's size was changed again.
|
||||||
|
// In that case we will resize the platform view again.
|
||||||
|
} while (size != targetSize);
|
||||||
|
|
||||||
|
_state = _PlatformViewState.ready;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the offset of the underlying platform view on the platform side.
|
||||||
|
//
|
||||||
|
// This allows the ELinux native view to draw the a11y highlights in the same
|
||||||
|
// location on the screen as the platform view widget in the Flutter framework.
|
||||||
|
//
|
||||||
|
// It also allows platform code to obtain the correct position of the ELinux
|
||||||
|
// native view on the screen.
|
||||||
|
void _setOffset() {
|
||||||
|
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
if (!_isDisposed) {
|
||||||
|
if (attached) {
|
||||||
|
await _viewController.setOffset(localToGlobal(Offset.zero));
|
||||||
|
}
|
||||||
|
// Schedule a new post frame callback.
|
||||||
|
_setOffset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(PaintingContext context, Offset offset) {
|
||||||
|
if (_viewController.textureId == null || _currentTextureSize == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As resizing the ELinux view happens asynchronously we don't know exactly when is a
|
||||||
|
// texture frame with the new size is ready for consumption.
|
||||||
|
// TextureLayer is unaware of the texture frame's size and always maps it to the
|
||||||
|
// specified rect. If the rect we provide has a different size from the current texture frame's
|
||||||
|
// size the texture frame will be scaled.
|
||||||
|
// To prevent unwanted scaling artifacts while resizing, clip the texture.
|
||||||
|
// This guarantees that the size of the texture frame we're painting is always
|
||||||
|
// _currentELinuxTextureSize.
|
||||||
|
final bool isTextureLargerThanWidget =
|
||||||
|
_currentTextureSize!.width > size.width ||
|
||||||
|
_currentTextureSize!.height > size.height;
|
||||||
|
if (isTextureLargerThanWidget && clipBehavior != Clip.none) {
|
||||||
|
_clipRectLayer.layer = context.pushClipRect(
|
||||||
|
true,
|
||||||
|
offset,
|
||||||
|
offset & size,
|
||||||
|
_paintTexture,
|
||||||
|
clipBehavior: clipBehavior,
|
||||||
|
oldLayer: _clipRectLayer.layer,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_clipRectLayer.layer = null;
|
||||||
|
_paintTexture(context, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
final LayerHandle<ClipRectLayer> _clipRectLayer =
|
||||||
|
LayerHandle<ClipRectLayer>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_isDisposed = true;
|
||||||
|
_clipRectLayer.layer = null;
|
||||||
|
_viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _paintTexture(PaintingContext context, Offset offset) {
|
||||||
|
if (_currentTextureSize == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.addLayer(TextureLayer(
|
||||||
|
rect: offset & _currentTextureSize!,
|
||||||
|
textureId: _viewController.textureId!,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||||
|
// Don't call the super implementation since `platformViewId` should
|
||||||
|
// be set only when the platform view is created, but the concept of
|
||||||
|
// a "created" platform view belongs to this subclass.
|
||||||
|
config.isSemanticBoundary = true;
|
||||||
|
|
||||||
|
if (_viewController.isCreated) {
|
||||||
|
config.platformViewId = _viewController.viewId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,301 @@
|
|||||||
|
// Copyright 2023 Sony Group Corporation. All rights reserved.
|
||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import '../rendering/platform_view.dart';
|
||||||
|
import '../services/platform_views.dart';
|
||||||
|
|
||||||
|
/// See: [AndroidView] in `src/widgets/platform_view.dart`
|
||||||
|
class ELinuxView extends StatefulWidget {
|
||||||
|
const ELinuxView({
|
||||||
|
super.key,
|
||||||
|
required this.viewType,
|
||||||
|
this.onPlatformViewCreated,
|
||||||
|
this.hitTestBehavior = PlatformViewHitTestBehavior.opaque,
|
||||||
|
this.layoutDirection,
|
||||||
|
this.gestureRecognizers,
|
||||||
|
this.creationParams,
|
||||||
|
this.creationParamsCodec,
|
||||||
|
this.clipBehavior = Clip.hardEdge,
|
||||||
|
}) : assert(creationParams == null || creationParamsCodec != null);
|
||||||
|
|
||||||
|
final String viewType;
|
||||||
|
final PlatformViewCreatedCallback? onPlatformViewCreated;
|
||||||
|
final PlatformViewHitTestBehavior hitTestBehavior;
|
||||||
|
final TextDirection? layoutDirection;
|
||||||
|
final Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers;
|
||||||
|
final dynamic creationParams;
|
||||||
|
final MessageCodec<dynamic>? creationParamsCodec;
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ELinuxView> createState() => _ELinuxViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See: [_AndroidViewState] in `src/widgets/platform_view.dart`
|
||||||
|
class _ELinuxViewState extends State<ELinuxView> {
|
||||||
|
int? _id;
|
||||||
|
late ELinuxViewController _controller;
|
||||||
|
TextDirection? _layoutDirection;
|
||||||
|
bool _initialized = false;
|
||||||
|
FocusNode? _focusNode;
|
||||||
|
|
||||||
|
static final Set<Factory<OneSequenceGestureRecognizer>> _emptyRecognizersSet =
|
||||||
|
<Factory<OneSequenceGestureRecognizer>>{};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Focus(
|
||||||
|
focusNode: _focusNode,
|
||||||
|
onFocusChange: _onFocusChange,
|
||||||
|
child: _ELinuxPlatformView(
|
||||||
|
controller: _controller,
|
||||||
|
hitTestBehavior: widget.hitTestBehavior,
|
||||||
|
gestureRecognizers: widget.gestureRecognizers ?? _emptyRecognizersSet,
|
||||||
|
clipBehavior: widget.clipBehavior,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initializeOnce() {
|
||||||
|
if (_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_initialized = true;
|
||||||
|
_createNewELinuxView();
|
||||||
|
_focusNode = FocusNode(debugLabel: 'ELinuxView(id: $_id)');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
final TextDirection newLayoutDirection = _findLayoutDirection();
|
||||||
|
final bool didChangeLayoutDirection =
|
||||||
|
_layoutDirection != newLayoutDirection;
|
||||||
|
_layoutDirection = newLayoutDirection;
|
||||||
|
|
||||||
|
_initializeOnce();
|
||||||
|
if (didChangeLayoutDirection) {
|
||||||
|
// The native view will update asynchronously, in the meantime we don't want
|
||||||
|
// to block the framework. (so this is intentionally not awaiting).
|
||||||
|
_controller.setLayoutDirection(_layoutDirection!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(ELinuxView oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
final TextDirection newLayoutDirection = _findLayoutDirection();
|
||||||
|
final bool didChangeLayoutDirection =
|
||||||
|
_layoutDirection != newLayoutDirection;
|
||||||
|
_layoutDirection = newLayoutDirection;
|
||||||
|
|
||||||
|
if (widget.viewType != oldWidget.viewType) {
|
||||||
|
//_controller.disposePostFrame();
|
||||||
|
_controller.dispose();
|
||||||
|
_createNewELinuxView();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (didChangeLayoutDirection) {
|
||||||
|
_controller.setLayoutDirection(_layoutDirection!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextDirection _findLayoutDirection() {
|
||||||
|
assert(
|
||||||
|
widget.layoutDirection != null || debugCheckHasDirectionality(context));
|
||||||
|
return widget.layoutDirection ?? Directionality.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
_focusNode?.dispose();
|
||||||
|
_focusNode = null;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _createNewELinuxView() {
|
||||||
|
_id = platformViewsRegistry.getNextPlatformViewId();
|
||||||
|
_controller = PlatformViewsServiceELinux.initELinuxView(
|
||||||
|
id: _id!,
|
||||||
|
viewType: widget.viewType,
|
||||||
|
layoutDirection: _layoutDirection!,
|
||||||
|
creationParams: widget.creationParams,
|
||||||
|
creationParamsCodec: widget.creationParamsCodec,
|
||||||
|
onFocus: () {
|
||||||
|
_focusNode!.requestFocus();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (widget.onPlatformViewCreated != null) {
|
||||||
|
_controller
|
||||||
|
.addOnPlatformViewCreatedListener(widget.onPlatformViewCreated!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFocusChange(bool isFocused) {
|
||||||
|
if (!_controller.isCreated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isFocused) {
|
||||||
|
_controller.clearFocus().catchError((dynamic e) {
|
||||||
|
if (e is MissingPluginException) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SystemChannels.textInput.invokeMethod<void>(
|
||||||
|
'TextInput.setPlatformViewClient',
|
||||||
|
<String, dynamic>{'platformViewId': _id},
|
||||||
|
).catchError((dynamic e) {
|
||||||
|
if (e is MissingPluginException) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See: [_AndroidPlatformView] in `src/widgets/platform_view.dart`
|
||||||
|
class _ELinuxPlatformView extends LeafRenderObjectWidget {
|
||||||
|
const _ELinuxPlatformView({
|
||||||
|
required this.controller,
|
||||||
|
required this.hitTestBehavior,
|
||||||
|
required this.gestureRecognizers,
|
||||||
|
this.clipBehavior = Clip.hardEdge,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ELinuxViewController controller;
|
||||||
|
final PlatformViewHitTestBehavior hitTestBehavior;
|
||||||
|
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderObject createRenderObject(BuildContext context) => RenderELinuxView(
|
||||||
|
viewController: controller,
|
||||||
|
hitTestBehavior: hitTestBehavior,
|
||||||
|
gestureRecognizers: gestureRecognizers,
|
||||||
|
clipBehavior: clipBehavior,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateRenderObject(BuildContext context, RenderELinuxView renderObject) {
|
||||||
|
renderObject.controller = controller;
|
||||||
|
renderObject.hitTestBehavior = hitTestBehavior;
|
||||||
|
renderObject.updateGestureRecognizers(gestureRecognizers);
|
||||||
|
renderObject.clipBehavior = clipBehavior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See: [AndroidViewSurface] in `src/widgets/platform_view.dart`
|
||||||
|
class ELinuxViewSurface extends StatefulWidget {
|
||||||
|
const ELinuxViewSurface({
|
||||||
|
super.key,
|
||||||
|
required this.controller,
|
||||||
|
required this.hitTestBehavior,
|
||||||
|
required this.gestureRecognizers,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ELinuxViewController controller;
|
||||||
|
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;
|
||||||
|
final PlatformViewHitTestBehavior hitTestBehavior;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return _ELinuxViewSurfaceState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See: [AndroidViewSurfaceState] in `src/widgets/platform_view.dart`
|
||||||
|
class _ELinuxViewSurfaceState extends State<ELinuxViewSurface> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (!widget.controller.isCreated) {
|
||||||
|
widget.controller
|
||||||
|
.addOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.controller
|
||||||
|
.removeOnPlatformViewCreatedListener(_onPlatformViewCreated);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (widget.controller.requiresViewComposition) {
|
||||||
|
return _PlatformLayerBasedELinuxViewSurface(
|
||||||
|
controller: widget.controller,
|
||||||
|
hitTestBehavior: widget.hitTestBehavior,
|
||||||
|
gestureRecognizers: widget.gestureRecognizers,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return _TextureBasedELinuxViewSurface(
|
||||||
|
controller: widget.controller,
|
||||||
|
hitTestBehavior: widget.hitTestBehavior,
|
||||||
|
gestureRecognizers: widget.gestureRecognizers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPlatformViewCreated(int _) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See: [_TextureBasedAndroidViewSurface] in `src/widgets/platform_view.dart`
|
||||||
|
class _TextureBasedELinuxViewSurface extends PlatformViewSurface {
|
||||||
|
const _TextureBasedELinuxViewSurface({
|
||||||
|
required ELinuxViewController super.controller,
|
||||||
|
required super.hitTestBehavior,
|
||||||
|
required super.gestureRecognizers,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
|
final ELinuxViewController viewController =
|
||||||
|
controller as ELinuxViewController;
|
||||||
|
// Use GL texture based composition.
|
||||||
|
// App should use GL texture unless they require to embed a SurfaceView.
|
||||||
|
final RenderELinuxView renderBox = RenderELinuxView(
|
||||||
|
viewController: viewController,
|
||||||
|
gestureRecognizers: gestureRecognizers,
|
||||||
|
hitTestBehavior: hitTestBehavior,
|
||||||
|
);
|
||||||
|
viewController.pointTransformer =
|
||||||
|
(Offset position) => renderBox.globalToLocal(position);
|
||||||
|
return renderBox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See: [_PlatformLayerBasedAndroidViewSurface] in `src/widgets/platform_view.dart`
|
||||||
|
class _PlatformLayerBasedELinuxViewSurface extends PlatformViewSurface {
|
||||||
|
const _PlatformLayerBasedELinuxViewSurface({
|
||||||
|
required ELinuxViewController super.controller,
|
||||||
|
required super.hitTestBehavior,
|
||||||
|
required super.gestureRecognizers,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
|
final ELinuxViewController viewController =
|
||||||
|
controller as ELinuxViewController;
|
||||||
|
final PlatformViewRenderBox renderBox =
|
||||||
|
super.createRenderObject(context) as PlatformViewRenderBox;
|
||||||
|
viewController.pointTransformer =
|
||||||
|
(Offset position) => renderBox.globalToLocal(position);
|
||||||
|
return renderBox;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright 2023 Sony Group Corporation. All rights reserved.
|
||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
export 'src/rendering/platform_view.dart';
|
||||||
|
export 'src/services/platform_views.dart';
|
||||||
|
export 'src/widgets/platform_view.dart';
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
name: flutter_elinux
|
||||||
|
description: Flutter packages for flutter-elinux
|
||||||
|
version: 0.1.0
|
||||||
|
homepage: https://github.com/sony/flutter-elinux
|
||||||
|
repository: https://github.com/sony/flutter-elinux/tree/main/packages/flutter_elinux
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.18.0 <4.0.0"
|
||||||
|
flutter: ">=3.3.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
Reference in New Issue
Block a user